Skip to content

14. المثال 11 – تحويل التاريخ والتحقق من صحته

يتميز التطبيق الجديد بإدخال التاريخ:

  • في [1]، نموذج الإدخال
  • في [2]، الاستجابة المعادة

يعمل التطبيق بشكل مشابه لإدخال الأعداد الصحيحة، لذا سنعلق فقط على النقاط التي تختلف.

14.1. مشروع NetBeans

مشروع NetBeans هو كما يلي:

  • في [1]، تعرض التطبيق
  • [Home.jsp]: الصفحة الرئيسية
  • [FormDate.jsp]: نموذج الإدخال
  • [ConfirmationFormDate.jsp]: صفحة التأكيد
  • في [2]، ملف الرسائل [messages.properties] وملف التكوين الرئيسي لـ Struts
  • في [3]:
  • [FormDate.java]: الإجراء الذي يعرض النموذج ويعالجه
  • [FormDate-validation.xml]: قواعد التحقق من صحة الإجراء [FormDate]. يفوض هذا الملف عمليات التحقق هذه إلى النموذج.
  • [FormDateModel]: النموذج الخاص بالإجراء [FormDate]
  • [FormDateModel-validation.xml]: قواعد التحقق من صحة النموذج
  • [FormDateModel.properties]: ملف رسائل النموذج
  • [example.xml]: ملف التكوين الثانوي لـ Struts

14.2. تكوين المشروع

يتم تكوين المشروع بشكل أساسي بواسطة ملف [example.xml] التالي:


<?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="FormDate" class="example.FormDate">
      <result name="input">/example/FormDate.jsp</result>
      <result name="cancel" type="redirect">/example/Accueil.jsp</result>
      <result name="success">/example/ConfirmationFormDate.jsp</result>
    </action>
  </package>
</struts>

وهو مشابه لما تمت مناقشته بشأن إدخال الأعداد الصحيحة.

14.3. ملفات الرسائل

ملف [messages.properties] هو كما يلي:


Accueil.titre=Accueil
Accueil.message=Struts 2 - Conversions et validations
Accueil.FormDate=Saisie de dates
Form.titre=Conversions et validations
FormDate.message=Struts 2 - Conversion et validation de dates
FormDate.conseil=Tapez les dates au format JJ/MM/AAAA comme dans 12/10/2008
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}".

ملف [FormDateModel.properties] كما يلي:


date.format={0,date,dd/MM/yyyy}
date1.prompt=1-Tapez une date au format JJ/MM/AAAA
date1.error=Date erron\u00E9e
date2.prompt=2-Tapez une date au format JJ/MM/AAAA
date2.error=Date erron\u00E9e
date3.prompt=3-Tapez une date >=18/05/2000
date3.error=Date erron\u00E9e
date4.prompt=4-Tapez une date <=20/06/2001
date4.error=Date erron\u00E9e
date5.prompt=5-Tapez une date dans l''intervalle [18/05/2000,20/06/2001]
date5.error=Date errron\u00E9e
date6.prompt=6-Tapez une date dans l''intervalle [18/05/2000,20/06/2001]
date6.error=Date errron\u00E9e

السطر 1 يلعب دورًا مهمًا. سنعود إلى هذا لاحقًا.

14.4. نموذج الإدخال

تبدو طريقة العرض [FormDate.jsp] كما يلي:


<%@ 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="FormDate.message"/></h2>
    <h4><s:text name="FormDate.conseil"/></h4>
    <s:form name="formulaire" action="FormDate">
      <s:textfield name="date1" key="date1.prompt"/>
      <s:textfield name="date2" key="date2.prompt" value="%{#parameters['date2']!=null ? #parameters['date2'] : date2==null ? '' :getText('date.format',{date2})}"/>
      <s:textfield name="date3" key="date3.prompt" value="%{#parameters['date3']!=null ? #parameters['date3'] : date3==null ? '' :getText('date.format',{date3})}"/>
      <s:textfield name="date4" key="date4.prompt" value="%{#parameters['date4']!=null ? #parameters['date4'] : date4==null ? '' :getText('date.format',{date4})}"/>
      <s:textfield name="date5" key="date5.prompt" value="%{#parameters['date5']!=null ? #parameters['date5'] : date5==null ? '' :getText('date.format',{date5})}"/>
      <s:textfield name="date6" key="date6.prompt"/>
      <s:submit key="Form.submitText" method="execute"/>
    </s:form>
    <br/>
    <s:url id="url" action="FormDate" method="cancel"/>
    <s:a href="%{url}"><s:text name="Form.cancelText"/></s:a>
      <br/>
    <s:url id="url" action="FormDate" method="clearModel"/>
    <s:a href="%{url}"><s:text name="Form.clearModel"/></s:a>
  </body>
</html>

الأسطر من 13 إلى 18 هي حقول إدخال التاريخ الستة. تحتوي الحقول من date2 إلى date5 على سمة قيمة معقدة، وهي نفس السمة المستخدمة في إدخال الأرقام الحقيقية. ونظرًا لظهور نفس الصعوبات، فقد تم حلها بنفس الطريقة.

14.5. عرض التأكيد

تبدو طريقة عرض التأكيد [ConfirmationFormDate.jsp] كما يلي:

Image

وإليك شفرة البرنامج:


<%@ 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="date1.prompt"/></td>
        <td><s:text name="date1"/></td>
      </tr>
      <tr>
        <td><s:text name="date2.prompt"/></td>
        <td>
          <s:text name="date.format">
            <s:param value="date2"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="date3.prompt"/></td>
        <td>
          <s:text name="date.format">
            <s:param value="date3"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="date4.prompt"/></td>
        <td>
          <s:text name="date.format">
            <s:param value="date4"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="date5.prompt"/></td>
        <td>
          <s:text name="date.format">
            <s:param value="date5"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="date6.prompt"/></td>
        <td><s:text name="date6"/></td>
      </tr>
    </table>
    <br/>
    <s:url id="url" action="FormDate!input"/>
    <s:a href="%{url}"><s:text name="Confirmation.lien"/></s:a>
  </body>
</html>

تعرض طريقة العرض [ConfirmationFormDate.jsp] ببساطة [FormDateModel]. توضح الأسطر 47-49 كيفية عرض التاريخ. نريد أن يتبع هذا العرض تنسيقًا محددًا للتاريخ. للقيام بذلك، نستخدم العلامة <s:text>، التي استخدمناها سابقًا لتدويل التطبيق.

هنا، مفتاح الرسالة المستخدم هو *date.format*. يوجد هذا المفتاح في ملف FormDateModel.properties:


date.format={0,date,dd/MM/yyyy}

القيمة المرتبطة بالمفتاح ليست رسالة هنا، بل تنسيق عرض:

  • 0 هو معلمة تمثل القيمة المراد عرضها. هنا، سيكون حقل date5 في النموذج.
  • date يمثل تاريخًا. في السابق، كنا نستخدم number للرقم.
  • dd/MM/yyyy يمثل تنسيق التاريخ. هنا، نريده في صيغة dd/mm/yyyy. تنسيق Java المناسب هو dd/MM/yyyy (d: اليوم، M: الشهر، y: السنة)

العلامة


<s:text name="date.format">
            <s:param value="date5"/>
</s:text>

يعرض الرسالة {0, date, dd/MM/yyyy}، حيث يحل المعلمة date5 (السطر 2) محل المعلمة 0 في التنسيق. وبالتالي، سيتم عرض النموذج date5 بالتنسيق dd/mm/yyyy.

14.6. قالب [FormDateModel]

يتم إدراج الحقول من date1 إلى date6 من النموذج [FormDate.jsp] في القالب [FormDateModel] التالي:


package example;
 
import java.util.Date;
 
public class FormDateModel {
 
  // constructor without parameters
  public FormDateModel() {
  }
  // fields
  private String date1;
  private Date date2 = new Date();
  private Date date3 = new Date();
  private Date date4;
  private Date date5;
  private String date6;
 
  // raz model
  public void clearModel() {
    date1 = null;
    date2 = null;
    date3 = null;
    date4 = null;
    date5 = null;
    date6 = null;
  }
 
  // getters and setters
...
}
  • الحقول date1 و date6 من النوع String
  • أما الحقول الأخرى فهي من النوع Date

14.7. التحقق من صحة النموذج

يتم التحكم في التحقق من صحة النموذج بواسطة ملفين: [FormDate-validation.xml] و [FormDateModel-validation.xml].

يقوم ملف [FormDate-validation.xml] بتفويض عمليات التحقق من الصحة إلى ملف [FormDateModel-validation.xml]:


<!--
<!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/exemple-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>

لقد صادفنا هذا الملف من قبل.

يحتوي ملف [FormDateModel-validation.xml] على قواعد التحقق من الصحة التالية:


<!--
<!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/exemple-11/example/xwork-validator-1.0.2.dtd">
 
<validators>
 
  <field name="date1" >
    <field-validator type="requiredstring" short-circuit="true">
      <message key="date1.error"/>
    </field-validator>
    <field-validator type="regex" short-circuit="true">
      <param name="expression">^\d{2}/\d{2}/\d{4}$</param>
      <param name="trim">true</param>
      <message key="date1.error"/>
    </field-validator>
  </field>
 
  <field name="date2" >
    <field-validator type="required" short-circuit="true">
      <message key="date2.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="date2.error"/>
    </field-validator>
   </field>
 
  <field name="date3" >
    <field-validator type="required" short-circuit="true">
      <message key="date2.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="date3.error"/>
    </field-validator>
    <field-validator type="date" short-circuit="true">
      <param name="min">18/05/2000</param>
      <message key="date3.error"/>
    </field-validator>
  </field>
 
  <field name="date4" >
    <field-validator type="required" short-circuit="true">
      <message key="date2.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="date4.error"/>
    </field-validator>
    <field-validator type="date" short-circuit="true">
      <param name="max">20/06/2001</param>
      <message key="date4.error"/>
    </field-validator>
  </field>
 
 <field name="date5" >
    <field-validator type="required" short-circuit="true">
      <message key="date2.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="date5.error"/>
    </field-validator>
    <field-validator type="date" short-circuit="true">
      <param name="min">18/05/2000</param>
      <param name="max">20/06/2001</param>
      <message key="date5.error"/>
    </field-validator>
  </field>
 
  <field name="date6" >
    <field-validator type="requiredstring" short-circuit="true">
      <message key="date6.error"/>
    </field-validator>
    <field-validator type="regex" short-circuit="true">
      <param name="expression">^\d{2}/\d{2}/\d{4}$</param>
      <param name="trim">true</param>
      <message key="date6.error"/>
    </field-validator>
  </field>
 
</validators>

  • تتحقق القاعدة في الأسطر 11–20 من أن حقل الإدخال date1 يتبع نمط تعبير عادي يمثل تاريخًا بتنسيق dd/mm/yyyy. لاحظ أننا نتحقق من أن السلسلة لها شكل تاريخ ولكننا لا نتحقق من أنها تاريخ صالح. وبالتالي، فإن 02/29/2011 له شكل تاريخ ولكنه ليس تاريخًا صالحًا.
  • تتحقق القاعدة في الأسطر 22-29 من أن حقل الإدخال date2 هو تاريخ صالح.
  • تتحقق القاعدة في الأسطر 31-42 من أن حقل الإدخال date3 هو تاريخ صالح >= 05/18/2000
  • تتحقق القاعدة في الأسطر 44-55 من أن حقل الإدخال date4 هو تاريخ صالح في أو قبل 20/06/2001.
  • تتحقق القاعدة في الأسطر 57-69 من أن حقل الإدخال date5 هو تاريخ صالح <= 20 يونيو 2001 و >= 18 مايو 2000.
  • تتحقق القاعدة في الأسطر 71–80 من أن حقل date6 بتنسيق dd/mm/yyyy.

بمجرد معالجة ملف [FormDateModel-validation.xml] بواسطة مانع التحقق من الصحة، يقوم هذا الأخير بتنفيذ طريقة validate الخاصة بعملية [FormDate] إذا كانت موجودة. سنقدمها مع العملية بأكملها.

14.8. الإجراء [FormDate]

الإجراء [FormDate] هو كما يلي:


package example;
 
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.interceptor.validation.SkipValidation;
 
public class FormDate extends ActionSupport implements ModelDriven, SessionAware {
 
  // constructor without parameters
  public FormDate() {
  }
 
  // action model
  public Object getModel() {
    if (session.get("model") == null) {
      session.put("model", new FormDateModel());
    }
    return session.get("model");
  }
 
  @SkipValidation
  public String clearModel() {
    // close to the model
    ((FormDateModel) getModel()).clearModel();
    // result
    return INPUT;
  }
 
  public String cancel() {
    // cleaning the model
    ((FormDateModel) getModel()).clearModel();
    // result
    return "cancel";
  }
  // SessionAware
  Map<String, Object> session;
 
  public void setSession(Map<String, Object> session) {
    this.session = session;
  }
 
  // validation
  @Override
  public void validate() {
    // date formatting
    SimpleDateFormat formateurDate = new SimpleDateFormat("dd/MM/yyyy");
    formateurDate.setLenient(false);
    // enter valid date1 ?
    if (getFieldErrors().get("date1") == null) {
      // check validity date
      try {
        formateurDate.parse(((FormDateModel) getModel()).getDate1());
      } catch (Exception e) {
        addFieldError("date1", getText("date1.error"));
      }
    }
    // valid date6 entry?
    if (getFieldErrors().get("date6") == null) {
      Date d = null;
      try {
        // check validity date
        d = formateurDate.parse(((FormDateModel) getModel()).getDate6());
        // terminal check
        if (d.after(formateurDate.parse("20/06/2001")) || d.before(formateurDate.parse("18/05/2000"))) {
          addFieldError("date6", getText("date6.error"));
          return;
        }
      } catch (Exception e) {
        addFieldError("date6", getText("date6.error"));
        return;
      }
    }
  }
}

تم بناء الإجراء [FormDate] على نفس نموذج الإجراء [FormInt]. سنكتفي بالتعليق على طريقة validate. تذكر أن طريقة validate يتم تنفيذها بعد معالجة ملف التحقق من الصحة [FormDateModel-validation.xml] وقبل تنفيذ طريقة execute.

  • تُكمل طريقة validate التحقق من صحة الحقلين date1 و date6. كان ملف التحقق من الصحة [FormDateModel-validation.xml] قد تحقق بالفعل من أن السلاسل المدخلة كانت بتنسيق التاريخ. نحن الآن نتحقق من أنها تواريخ صالحة بالفعل. لاحظ أيضًا أن date1 و date6 كانا الحقلين الوحيدين في النموذج من النوع String.
  • السطر 50: نقوم بإنشاء تنسيق تاريخ dd/mm/yyyy للتحقق من أن السلاسل من هذا النوع هي بالفعل تواريخ صالحة.
  • السطر 51: تمنع setLenient(false) تفسير التاريخ غير الصحيح 32/01/2012 على أنه التاريخ الصحيح 01/02/2012.
  • السطر 53: نقوم بالتحقق من صحة date1 فقط إذا لم تواجه عمليات التحقق السابقة أي أخطاء في هذا الحقل.
  • السطر 56: نتحقق من أن date1 هو تاريخ صالح بتنسيق يوم/شهر/سنة.
  • السطر 58: إذا لم يكن الأمر كذلك، تظهر رسالة خطأ مرتبطة بحقل date1.
  • السطر 62: يتم التحقق من صحة date6 فقط إذا اجتاز هذا الحقل عمليات التحقق السابقة.
  • السطر 66: نتحقق من أن date6 هو تاريخ صالح
  • السطر 68: إذا كان date6 <18/05/2000 أو date6 > 20/06/2001، فإن date6 غير صحيح.

14.9. التفاصيل النهائية

يحتوي النموذج السابق على عيوب، كما هو موضح في المثال التالي:

  • في [1]، تم إدخال تاريخ غير صالح
  • في [2]، تم قبوله. تم أخذ الأحرف الأولى فقط dd/mm/yyyy في الاعتبار.

تواجه الحقول من date2 إلى date5، المرتبطة بنماذج من نوع Date، هذه المشكلة جميعها. مرة أخرى، ربما فاتني شيء ما في الوثائق. أما الحقول date1 و date6، المرتبطة بنماذج من نوع String، فلا تواجه هذه المشكلة.