Skip to content

13. Örnek 10 – Reel sayıların dönüştürülmesi ve doğrulanması

Yeni uygulama, gerçek sayıların girilmesini göstermektedir:

  • [1]'e, giriş formu
  • [2], geri gönderilen yanıt

Uygulama, tamsayı girişine benzer şekilde çalışır; bu nedenle sadece farklı olan noktalara değineceğiz.

13.1. NetBeans projesi

NetBeans projesi şu şekildedir:

  • [1]'te, uygulamanın görünümleri
    • [Accueil.JSP]: ana sayfa
    • [FormDouble.JSP]: giriş formu
    • [ConfirmationDouble.JSP]: onay sayfası
  • [2]'te, mesaj dosyası [messages.properties] ve Struts'un ana yapılandırma dosyası
    • [3]'te:
    • [FormDouble.java]: Formu görüntüleyen ve işleyen eylem
    • [FormDouble-validation.xml]: [FormDouble] eyleminin doğrulama kuralları. Bu dosya, az önce görülen yönteme göre bu doğrulamaları modele devreder.
    • [FormDoubleModel]: [FormDouble] eyleminin modeli
    • [FormDoubleModel-validation.xml]: modelin doğrulama kuralları
    • [FormDoubleModel.properties]: modelin mesaj dosyası
    • [example.xml]: Struts'un ikincil yapılandırma dosyası

13.2. Projenin yapılandırılması

Proje, esas olarak aşağıdaki [example.xml] dosyası ile yapılandırılır:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
  <package name="example" namespace="/example" extends="struts-default">
    <action name="Accueil">
      <result name="success">/example/Accueil.JSP</result>
    </action>
    <action name="FormDouble" class="example.FormDouble">
      <result name="input">/example/FormDouble.JSP</result>
      <result name="cancel" type="redirect">/example/Accueil.JSP</result>
      <result name="success">/example/ConfirmationFormDouble.JSP</result>
    </action>
  </package>
</struts>

Bu dosya, tamsayı girişi için incelenen dosyaya benzerdir.

13.3. Mesaj dosyaları

[messages.properties] dosyası şöyledir:


Accueil.titre=Accueil
Accueil.message=Struts 2 - Conversions et validations
Accueil.FormDouble=Saisie de nombres r\u00e9els
Form.titre=Conversions et validations
FormDouble.message=Struts 2 - Conversion et validation de nombres r\u00e9els
FormDouble.conseil=Tapez les nombres r\u00e9els avec une virgule comme 10,7
Form.submitText=Valider
Form.cancelText=Annuler
Form.clearModel=Raz mod\u00e8le
Confirmation.titre=Confirmation
Confirmation.message=Confirmation des valeurs saisies
Confirmation.champ=champ
Confirmation.valeur=valeur
Confirmation.lien=Formulaire de test
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".

[FormDoubleModel.properties] dosyası şöyledir:


double.format={0,number}
double1.prompt=1-Nombre r\u00E9el
double1.error=Tapez un nombre r\u00E9el
double2.prompt=2-Nombre r\u00E9el
double2.error=Tapez un nombre r\u00E9el
double3.prompt=3-Nombre r\u00E9el >=2.64
double3.error=Tapez un nombre r\u00E9el >=2.64
double4.prompt=4-Nombre r\u00E9el <8.32
double4.error=Tapez un nombre r\u00E9el <8.32
double5.prompt=5-Nombre r\u00E9el dans l''intervalle [2.64,8.32[
double5.error=Tapez un nombre r\u00E9el dans l''intervalle [2.64,8.32[
double6.prompt=6-Nombre r\u00E9el dans l''intervalle [2.64,8.32]
double6.error=Tapez un nombre r\u00E9el dans l''intervalle [2.64,8.32]
  1. satır önemli bir rol oynar. Buna daha sonra tekrar döneceğiz.

13.4. Giriş formu

[FormDouble.JSP] görünümü şu şekildedir:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
  <head>
    <title><s:text name="Form.titre"/></title>
    <s:head/>
  </head>

  <body background="<s:url value="/ressources/standard.jpg"/>">
    <h2><s:text name="FormDouble.message"/></h2>
    <h4><s:text name="FormDouble.conseil"/></h4>
    <s:form name="formulaire" action="FormDouble">
      <s:textfield name="double1" key="double1.prompt"/>
      <s:textfield name="double2" key="double2.prompt" value="%{#parameters['double2']!=null ? #parameters['double2'] : double2==null ? '' :getText('double.format',{double2})}"/>
      <s:textfield name="double3" key="double3.prompt" value="%{#parameters['double3']!=null ? #parameters['double3'] : double3==null ? '' :getText('double.format',{double3})}"/>
      <s:textfield name="double4" key="double4.prompt" value="%{#parameters['double4']!=null ? #parameters['double4'] : double4==null ? '' :getText('double.format',{double4})}"/>
      <s:textfield name="double5" key="double5.prompt" value="%{#parameters['double5']!=null ? #parameters['double5'] : double5==null ? '' :getText('double.format',{double5})}"/>
      <s:textfield name="double6" key="double6.prompt"/>
      <s:submit key="Form.submitText" method="execute"/>
    </s:form>
    <br/>
    <s:url id="URL" action="FormDouble" method="cancel"/>
    <s:a href="%{URL}"><s:text name="Form.cancelText"/></s:a>
    <br/>
    <s:url id="URL" action="FormDouble" method="clearModel"/>
    <s:a href="%{URL}"><s:text name="Form.clearModel"/></s:a>
  </body>
</html>
  1. ile 18. satırlar, gerçek sayıların girildiği altı alandır. double2 ile double5 arasındaki alanlar, karmaşık bir value özniteliğine sahiptir. Normalde altı giriş alanı şu şekilde olmalıdır:

      <s:textfield name="double1" key="double1.prompt"/>
      <s:textfield name="double2" key="double2.prompt"/>
      <s:textfield name="double3" key="double3.prompt"/>
      <s:textfield name="double4" key="double4.prompt"/>
      <s:textfield name="double5" key="double5.prompt"/>
<s:textfield name="double6" key="double6.prompt"/>

Testler sırasında karşılaşılan bazı sorunları çözmek için işleri biraz karmaşıklaştırmak zorunda kaldık. Şimdilik okuyucu bu karmaşıklığı göz ardı edebilir. Bunu daha sonra açıklayacağız.

13.5. Onay ekranı

[ConfirmationFormDouble.JSP] onay ekranı şu şekildedir:

 

Kodu şöyledir:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
  <head>
    <title><s:text name="Confirmation.titre"/></title>
    <s:head/>
  </head>

  <body background="<s:url value="/ressources/standard.jpg"/>">
    <h2><s:text name="Confirmation.message"/></h2>
    <table border="1">
      <tr>
        <th><s:text name="Confirmation.champ"/></th>
        <th><s:text name="Confirmation.valeur"/></th>
      </tr>
      <tr>
        <td><s:text name="double1.prompt"/></td>
        <td><s:text name="double1"/></td>
      </tr>
      <tr>
        <td><s:text name="double2.prompt"/></td>
        <td>
          <s:text name="double.format">
            <s:param value="double2"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="double3.prompt"/></td>
        <td>
          <s:text name="double.format">
            <s:param value="double3"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="double4.prompt"/></td>
        <td>
          <s:text name="double.format">
            <s:param value="double4"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="double5.prompt"/></td>
        <td>
          <s:text name="double.format">
            <s:param value="double5"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="double6.prompt"/></td>
        <td><s:text name="double6"/></td>
      </tr>
    </table>
    <br/>
    <s:url id="URL" action="FormDouble!input"/>
    <s:a href="%{URL}"><s:text name="Confirmation.lien"/></s:a>
  </body>
</html>

[ConfirmationFormDouble.JSP] görünümü, yalnızca [FormDoubleModel] şablonunu görüntüler. 47-49. satırlar, bir gerçek sayının görüntülenmesini gösterir. Bu görüntülemenin, uygulamanın localisation şablonunu dikkate almasını istiyoruz. Buna göre, bir gerçek sayı Fransa’da (10,7) ve Büyük Britanya’da (10.7) aynı şekilde görüntülenmeyecektir. Bunun için, o ana kadar uygulamanın uluslararasılaştırılması amacıyla kullandığımız <s:text> etiketini kullanıyoruz. Dolayısıyla bu etiket, yerelleştirme için de kullanılır.

Burada kullanılan mesaj anahtarı double.format’tir. Bu anahtar, [FormDoubleModel.properties] dosyasında bulunur:


double.format={0,number}

Anahtara atanan değer burada bir mesaj değil, bir görüntüleme formatıdır:

  • 0, görüntülenecek değeri temsil eden bir parametredir. Burada bu, şablondaki double5 alanı olacaktır.
  • number, sayısal biçimi temsil eder. Bu biçim her ülkeye göre uyarlanacaktır. Bu biçim olmadan, 10,7 sayısı hangi ülkede olursa olsun her zaman 10,7 olarak görüntülenecektir.

Etiket


         <s:text name="double.format">
            <s:param value="double5"/>
</s:text>

{0, number} mesajını görüntüler; burada double5 parametresi (2. satır), formattaki 0 parametresinin yerini alır. Böylece, double5 şablonu, yerel sayısal formatta görüntülenir.

13.6. [FormDoubleModel] şablonu

[FormDouble.JSP] formundaki double1 ile double6 arasındaki alanlar, aşağıdaki [FormDoubleModel] şablonuna eklenir:


package example;

public class FormDoubleModel {

  // parametresiz oluşturucu
  public FormDoubleModel() {
  }
  // alanlar
  private String double1;
  private Double double2;
  private Double double3;
  private Double double4;
  private Double double5;
  private String double6;

  // model sıfırlama
  public void clearModel() {
    double1 = null;
    double2 = null;
    double3 = null;
    double4 = null;
    double5 = null;
    double6 = null;
  }

  // alıcı ve ayarlayıcılar
   ...
}
  • double1 ve double6 alanları String türündedir
  • diğer alanlar Double türündedir

13.7. Şablonun doğrulanması

Modelin doğrulanması iki dosya tarafından kontrol edilir: [FormDouble-validation.xml] ve [FormDoubleModel-validation.xml].

[FormDouble-validation.xml] dosyası, doğrulama işlemlerini [FormDoubleModel-validation.xml] dosyasına devreder:


<!--
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
-->
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://localhost:8084/örnek-10/example/xwork-validator-1.0.2.dtd">

<validators>
  <field name="model" >
    <field-validator type="visitor">
      <param name="appendPrefix">false</param>
      <message/>
    </field-validator>
  </field>
</validators>

Bu dosyayla daha önce karşılaşmıştık.

[FormDoubleModel-validation.xml] dosyası aşağıdaki doğrulama kurallarını içerir:


<!--
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
-->

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://localhost:8084/örnek-10/example/xwork-validator-1.0.2.dtd">


<validators>

  <field name="double1" >
    <field-validator type="requiredstring" short-circuit="true">
      <message key="double1.error"/>
    </field-validator>
    <field-validator type="regex" short-circuit="true">
      <param name="expression">^[+|-]*\s*\d+(,\d+)*$</param>
      <param name="trim">true</param>
      <message key="double1.error"/>
    </field-validator>
  </field>

  <field name="double2" >
    <field-validator type="required" short-circuit="true">
      <message key="double2.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="double2.error"/>
    </field-validator>
  </field>

  <field name="double3" >
    <field-validator type="required" short-circuit="true">
      <message key="double3.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="double3.error"/>
    </field-validator>
    <field-validator type="double" short-circuit="true">
      <param name="minInclusive">2.64</param>
      <message key="double3.error"/>
    </field-validator>
  </field>

  <field name="double4" >
    <field-validator type="required" short-circuit="true">
      <message key="double4.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="double4.error"/>
    </field-validator>
    <field-validator type="double" short-circuit="true">
      <param name="maxExclusive">8.32</param>
      <message key="double4.error"/>
    </field-validator>
  </field>

  <field name="double5" >
    <field-validator type="required" short-circuit="true">
      <message key="double5.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="double5.error"/>
    </field-validator>
    <field-validator type="double" short-circuit="true">
      <param name="minInclusive">2.64</param>
      <param name="maxExclusive">8.32</param>
      <message key="double5.error"/>
    </field-validator>
  </field>

  <field name="double6" >
    <field-validator type="requiredstring" short-circuit="true">
      <message key="double6.error"/>
    </field-validator>
    <field-validator type="regex" short-circuit="true">
      <param name="expression">^[+|-]*\s*\d+(,\d+)*$</param>
      <param name="trim">true</param>
      <message key="double6.error"/>
    </field-validator>
  </field>
</validators>
  • 12-21. satırlardaki kural, double1 giriş alanının, bir gerçek sayıyı temsil eden düzenli ifade şablonuna uygun olup olmadığını kontrol eder.
  • 23-30. satırlardaki kural, double2 giriş alanının çift hassasiyetli bir gerçek sayıya dönüştürülebilir olup olmadığını kontrol eder.
  • 32-43. satırlardaki kural, double3 giriş alanının >=2,64 değerinde bir çift hassasiyetli gerçek sayıya dönüştürülebilir olup olmadığını kontrol eder. Burada, gerçek sayılar için İngilizce notasyonunun kullanılması gerektiğine dikkat edilmelidir.
  • 45-56. satırlardaki kural, double4 giriş alanının <8,32 değerinde bir çift hassasiyetli gerçek sayıya dönüştürülebilir olup olmadığını kontrol eder.
  • 58-70. satırlardaki kural, double5 giriş alanının [2,64; 8,32] aralığında bir çift hassasiyetli gerçek sayıya dönüştürülebilir olup olmadığını kontrol eder.
  • 72-80. satırlardaki kural, double6 alanının bir gerçek sayıyı temsil eden düzenli ifade şablonuna uygun olup olmadığını kontrol eder.

[FormDoubleModel-validation.xml] dosyası doğrulama yakalayıcısı tarafından işlendiğinde, yakalayıcı, varsa [FormDouble] eyleminin validate yöntemini çalıştırır. Bu yöntemi, eylemin tamamıyla birlikte sunacağız.

13.8. [FormDouble] eylemi

[FormDouble] eylemi şu şekildedir:


package example;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.interceptor.validation.SkipValidation;

public class FormDouble extends ActionSupport implements ModelDriven, SessionAware {

  // parametresiz oluşturucu
  public FormDouble() {
  }

  // eylem modeli
  public Object getModel() {
    if (session.get("model") == null) {
      session.put("model", new FormDoubleModel());
    }
    return session.get("model");
  }

  @SkipValidation
  public String clearModel() {
    // model sıfırlama
    ((FormDoubleModel) getModel()).clearModel();
    // sonuç
    return INPUT;
  }

  public String cancel() {
    // modeli temizliyoruz
    ((FormDoubleModel) getModel()).clearModel();
    // sonuç
    return "cancel";
  }
  // SessionAware
  Map<String, Object> session;

  public void setSession(Map<String, Object> session) {
    this.session = session;
  }

  // doğrulama
  @Override
  public void validate() {
    // çift giriş6 geçerli mi?
    if (getFieldErrors().get("double6") == null) {
      // double6 dizesindeki virgül nokta ile değiştiriliyor
      String strDouble6 = (((FormDoubleModel) getModel()).getDouble6()).replace(',', '.');
      // Dize --> double
      double double6 = Double.parseDouble(strDouble6);
      // doğrulama
      if (double6 < 2.64 || double6 > 8.32) {
        addFieldError("double6", getText("double6.error"));
      }
    }
  }
}

[FormDouble] eylemi, [FormInt] eylemi ile aynı model üzerine kurulmuştur. Yalnızca validate yöntemini ele alacağız. validate yönteminin, [FormDoubleModel-validation.xml] doğrulama dosyasının işlenmesinden sonra ve execute yönteminin yürütülmesinden önce yürütüldüğünü hatırlatırız.

  • 48. satır: double6 alanında zaten hatalar varsa, başka bir işlem yapılmaz.
  • 50. satır: 45,67 biçiminde bir dize elde edildi. Bu dize, şablonun double6 alanında depolandı. 45.67 elde etmek için virgül nokta ile değiştirilir.
  • 52. satır: 45.67 dizesi çift hassasiyetli gerçek sayıya dönüştürülür. double6 dizesi bir gerçek sayının biçimine uygun olduğu için bu işlem başarılı olmalıdır.
  • 54. satır: Elde edilen çift hassasiyetli gerçek sayının [2.64, 8.32] aralığında olup olmadığı kontrol edilir.
  • 55. satır: Aksi takdirde, double6.error anahtarındaki hata mesajı, double6 alanına eklenir. Bu mesaj, [FormDoubleModel.properties] dosyasında bulunur. Hatalı form yeniden görüntülendiğinde bu mesaj gösterilir.

13.9. Son ayrıntılar

Şimdi, [FormDouble.JSP] formundaki giriş alanlarının karmaşıklığına geri dönelim. double2 alanını ele alalım. Bu mantık, Double tipi şablona sahip olan double3 ile double5 arasındaki alanlar için de geçerlidir. String türünde bir şablona sahip olan double1 ve double6 alanları için herhangi bir sorun yoktur.

double2 giriş alanı şu şekildedir:

<s:textfield name="double2" key="double2.prompt" value="%{#parameters['double2']!=null ? #parameters['double2'] : double2==null ? '' :getText('double.format',{double2})}"/>

En basit etiketle başlayalım:

<s:textfield name="double2" key="double2.prompt"/>

ve ne olduğunu inceleyelim:

  • [1]'te, doğru sayı olan double2 onaylanıyor. Ekran görüntüsünde çok net görünmüyor ama sayıyı virgülle yazmıştık.
  • [2]'te, onay ekranı. double2 girişi doğrulama testlerini geçti. Sayı virgülle birlikte görüntüleniyor.
  • [3]'te, forma geri dönüyoruz
  • [4]'te, form. Ekran görüntüsünde net olarak görülmeyen şey, başlangıçta 4,32 olan double2 sayısının, ondalık nokta ile 4.32 haline gelmiş olmasıdır.
  • [5]'te, hiçbir değişiklik yapmadan formu yeniden onaylıyoruz
  • [6]'e geldiğimizde, double2 alanında bir hata bildiriliyor.

Sorun şudur:

  • başlangıçta [1]'te, "4,32" giriş dizesi başarıyla 4.32 tamsayısına dönüştürülmüştür. Bu, String --> Double işleminin başarılı olduğu ve dolayısıyla Struts'un bu bağlamda yerel ayarı (burada Fransa) dikkate aldığı anlamına gelir.
  • Yeni form [4], double2 alanında 4.32 sayısını görüntülüyor. Görüntüleme yerelleştirilmediğinden, varsayılan olarak İngilizce biçiminde, yani ondalık nokta ile birlikte c.a.d olarak görüntülenir. Dolayısıyla Double --> String dönüşümünde Struts artık yerel ayarı dikkate almaz; aksi takdirde 4,32'yi virgül ile birlikte gösterirdi.

Bu, en azından tutarsız bir durumdur. Ancak sorun değil, 4,32 sayısının görüntülenmesini yerelleştireceğiz. Giriş etiketi şu şekilde olur:


<s:textfield name="double2" key="double2.prompt" value="%{getText('double.format',{double2})}"/>

value özniteliği, double2 alanında görüntülenecek değeri belirtir. Bu değer, OGNL ifadesinden elde edilir. [FormDoubleModel.properties] dosyasındaki double.format anahtarının tanımını hatırlayalım:


double.format={0,number}

getText('anahtar') yöntemi, bir anahtara ait mesajı almayı sağlar. Bu mesaj, geçerli yerel ayara karşılık gelen dosyada aranır. Örneğin, yerel ayar es (İspanya) olsaydı, double.format anahtarı [FormDoubleModel_es.properties] dosyasında aranırdı.

getText('anahtar', {param0, param1, ...}) yöntemi, parametreli bir mesajı bulmaya yarar. Mesaj

{0, number}

mesajı, 0 numaralı parametre ile parametrelenmiş bir mesajdır. Bu, konumsal bir parametredir. getText('double.format', {double2}) yöntemi, 0 numaralı parametreye double2 sayısını atayacaktır. Sonuç olarak, double2 değerinin yerelleştirilmiş sayısal biçimde döndürülmesi istenir. Fransa'da 4,56 sayısı "4,56" dizesi olarak yerelleştirilir.

Bu dönüşümden sonra testler tekrarlanır.

Form ilk görüntülendiğinde, [1]'te bir hata ortaya çıkıyor. Etiketine geri dönelim:


<s:textfield name="double2" key="double2.prompt" value="%{getText('double.format',{double2})}"/>

İlk görüntülemede, double2 şablonunun değeri, sayısal olmayan bir değer olan null'tir. Etiketi şu şekilde değiştiriyoruz:


<s:textfield name="double2" key="double2.prompt" value="%{double2==null ? '' : getText('double.format',{double2})}"/>

Bu sefer, double2==null olup olmadığını test ediyoruz. Eğer öyleyse, boş bir dize görüntülüyoruz.

Bu değişiklik yapıldıktan sonra testlere devam ediyoruz:

[1] ile [4] arasındaki ekranlar, çözmeye çalıştığımız sorunun çözüldüğünü gösteriyor:

  • [1] ekranında 4,67 girildiğinde
  • [2] ekranında bu sayı kabul edildi
  • [3]'te, virgül ile birlikte 4,67 olarak yeniden görüntülenir ve bu durum [4] tarafından da teyit edilir.

Ne yazık ki sorunlar bununla bitmiyor. Şu kod parçasına bir bakalım:

  • [5]'te bir karakter eklenerek double2
  • [6]'te, doğrulama testleri işini yaptı ve hata bildirildi. Ancak [6] olarak görüntülenen dize hatalı dize değil, double2 şablonunun mevcut değeridir. Girilen değer kayboldu.

[5]'ten [6]'e geçildiğinde istek sonuna kadar tamamlanmıyor. Doğrulama engelleyicisi tarafından durduruluyor. [6]'te, bu durdurma nedeniyle yeni bir değer almamış olan double2 modelinin değeri görüntülenmiştir. Dolayısıyla, girilen dizenin görüntülenmesi gerekirken, modelin önceki değeri görüntülenmiştir. Bir isteğin parametreleri, #parameters['param'] notasyonu aracılığıyla görüntülenebilir. double2 giriş alanını şu şekilde değiştiriyoruz:


      <s:textfield name="double2" key="double2.prompt" value="%{#parameters['double2']!=null ? #parameters['double2'] : double2==null ? '' : getText('double.format',{double2})}"/>

double2 alanının görüntülenen değeri şu şekilde hesaplanır: 'double2' parametresi varsa bu parametre görüntülenir, yoksa double2 şablonu görüntülenir. Okuyucunun, bu yeni etiket sürümünün karşılaşılan sorunları çözüp çözmediğini test etmesi önerilir.

13.10. Conclusion

Geçerlilik denetimi ile gerçek sayıları girmek bu kadar karmaşık olması şaşırtıcı... Belki de belgelerde bir şeyi gözden kaçırmışımdır?