Skip to content

14. مثال ۱۱ – تبدیل و اعتبارسنجی تاریخ

برنامه جدید دارای قابلیت وارد کردن تاریخ است:

  • در [1]، فرم ورودی
  • به [2]، پاسخ بازگردانده‌شده

این برنامه به روشی مشابه قابلیت ورود عدد صحیح عمل می‌کند، بنابراین تنها به نکاتی که متفاوت هستند اشاره خواهیم کرد.

14.1. پروژه NetBeans

پروژه NetBeans به شرح زیر است:

  • در [1]، نماهای برنامه
    • [Accueil.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

خط ۱ نقش مهمی ایفا می‌کند. بعداً به این موضوع باز خواهیم گشت.

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>

خطوط ۱۳ تا ۱۸ مربوط به شش فیلد ورودی تاریخ هستند. فیلدهای date2 تا date5 دارای ویژگی complex هستند، مشابه همان ویژگی‌ای که برای وارد کردن اعداد حقیقی استفاده می‌شود. از آنجا که با همان مشکلات مواجه شدیم، آن‌ها را به همان روش حل کردیم.

14.5. نمای تأیید

نمای تأیید [ConfirmationFormDate.JSP] به شرح زیر است:

 

کد آن به شرح زیر است:


<%@ 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] را نمایش می‌دهد. خطوط ۴۷–۴۹ نشان می‌دهند که تاریخ چگونه نمایش داده می‌شود. ما می‌خواهیم این نمایش فرمت تاریخ را در نظر بگیرد. برای این کار از تگ <s:text> استفاده می‌کنیم که قبلاً برای بین‌المللی‌سازی برنامه به کار برده بودیم.

در اینجا کلید پیام مورد استفاده date.format است. این کلید را می‌توان در فایل [FormDateModel.properties] یافت:


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

مقدار مرتبط با کلید در اینجا یک پیام نیست، بلکه یک قالب نمایش است:

  • ۰ پارامتری است که مقدار قابل نمایش را نشان می‌دهد. در این مورد، این مقدار فیلد date5 در قالب خواهد بود.
  • date نمایانگر یک تاریخ است. قبلاً برای یک عدد از number استفاده می‌کردیم.
  • dd/MM/yyyy نمایانگر قالب تاریخ است. در اینجا می‌خواهیم آن را به شکل dd/mm/yyyy داشته باشیم. قالب مناسب جاوا dd/MM/yyyy است (d: روز، M: ماه، y: سال)

برچسب


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

پیام {0, تاریخ, dd/MM/yyyy} را نمایش می‌دهد، که در آن پارامتر date5 (خط ۲) جایگزین پارامتر 0 در قالب می‌شود. بنابراین، قالب date5 به صورت dd/mm/yyyy نمایش داده خواهد شد.

14.6. قالب [FormDateModel]

میدان‌های date1 تا date6 از فرم [FormDate.JSP] در قالب زیر [FormDateModel] درج می‌شوند:


package example;

import java.util.Date;

public class FormDateModel {

  // سازنده بدون پارامتر
  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;

  //پاک کردن قالب
  public void clearModel() {
    date1 = null;
    date2 = null;
    date3 = null;
    date4 = null;
    date5 = null;
    date6 = null;
  }

  // گیرنده‌ها و تنظیم‌کننده‌ها
...
}
  • میدان‌های 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/example-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/example-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>
  • قاعده در خطوط ۱۱–۲۰ بررسی می‌کند که فیلد ورودی date1 با الگوی عبارت منظم (regular expression) که نمایانگر تاریخ به فرمت dd/mm/yyyy است، مطابقت دارد. توجه کنید که این بررسی تضمین می‌کند رشته در قالب تاریخ باشد، اما صحت تاریخ را تأیید نمی‌کند. بنابراین، 29/02/2011 در قالب تاریخ است اما تاریخ معتبری نیست.
  • قاعده در خطوط ۲۲–۲۹ بررسی می‌کند که فیلد ورودی date2 یک تاریخ معتبر باشد.
  • قاعده در خطوط ۳۱–۴۲ بررسی می‌کند که فیلد ورودی date3 یک تاریخ معتبر در تاریخ یا بعد از ۱۸/۰۵/۲۰۰۰ باشد
  • قاعده در خطوط 44–55 بررسی می‌کند که فیلد ورودی date4 یک تاریخ معتبر در تاریخ یا قبل از 20 ژوئن 2001 باشد.
  • قاعده برای خطوط ۵۷–۶۹ بررسی می‌کند که فیلد ورودی date5 یک تاریخ معتبر باشد که ≤ 20/06/2001 و ≥ 18/05/2000 باشد.
  • قاعده برای خطوط ۷۱–۸۰ بررسی می‌کند که فیلد 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 {

  // سازنده بدون هیچ پارامتری
  public FormDate() {
  }

  // مدل اقدام
  public Object getModel() {
    if (session.get("model") == null) {
      session.put("model", new FormDateModel());
    }
    return session.get("model");
  }

  @SkipValidation
  public String clearModel() {
    // ریست مدل
    ((FormDateModel) getModel()).clearModel();
    // نتیجه
    return INPUT;
  }

  public String cancel() {
    //پاکسازی مدل
    ((FormDateModel) getModel()).clearModel();
    // نتیجه
    return "cancel";
  }
  // SessionAware
  Map<String, Object> session;

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

  //اعتبارسنجی
  @Override
  public void validate() {
    //قالب‌بندی تاریخ
    SimpleDateFormat formateurDate = new SimpleDateFormat("dd/MM/yyyy");
    formateurDate.setLenient(false);
    //آیا تاریخ1 معتبر است؟
    if (getFieldErrors().get("date1") == null) {
      //بررسی اعتبار تاریخ
      try {
        formateurDate.parse(((FormDateModel) getModel()).getDate1());
      } catch (Exception e) {
        addFieldError("date1", getText("date1.error"));
      }
    }
    //آیا تاریخ6 معتبر است؟
    if (getFieldErrors().get("date6") == null) {
      Date d = null;
      try {
        //بررسی اعتبار تاریخ
        d = formateurDate.parse(((FormDateModel) getModel()).getDate6());
        //بررسی بازه زمانی
        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 بودند.
  • خط ۵۰: قالب تاریخ dd/mm/yyyy ایجاد شده است تا بررسی شود که رشته‌های از این نوع واقعاً تاریخ‌های معتبر هستند.
  • خط ۵۱: setLenient(false) از تفسیر تاریخ نامعتبر 32/01/2012 به عنوان تاریخ معتبر 01/02/2012 جلوگیری می‌کند.
  • خط ۵۳: date1 تنها در صورتی اعتبارسنجی می‌شود که اعتبارسنجی‌های قبلی برای این فیلد هیچ خطایی برنگردانده باشند.
  • خط ۵۶: بررسی می‌کنیم که date1 یک تاریخ معتبر در قالب dd/mm/yyyy است.
  • خط ۵۸: اگر اینطور نباشد، یک پیام خطا به فیلد date1 اختصاص داده می‌شود.
  • خط ۶۲: date6 تنها در صورتی اعتبارسنجی می‌شود که این فیلد اعتبارسنجی‌های قبلی را گذرانده باشد.
  • خط ۶۶: بررسی می‌کنیم که date6 یک تاریخ معتبر است
  • خط ۶۸: اگر date6 کمتر از ۱۸/۰۵/۲۰۰۰ یا بزرگتر از ۲۰/۰۶/۲۰۰۱ باشد، آنگاه date6 نادرست است.

14.9. جزئیات نهایی

فرم قبلی دارای نواقصی است، همان‌طور که در مثال زیر نشان داده شده است:

  • در [1]، یک تاریخ نامعتبر وارد شده است
  • در [2]، پذیرفته شد. فقط حروف اول dd/mm/yyyy در نظر گرفته شدند.

میدان‌های date2 تا date5 که به قالب‌هایی از نوع Date متصل هستند، همگی این مشکل را دارند. باز هم، شاید من نکته‌ای را در مستندات از قلم انداخته‌ام. فیلدهای date1 و date6 که به قالب‌هایی از نوع String متصل هستند، این مشکل را ندارند.