Skip to content

14. Esempio 11 – Conversione e convalida della data

La nuova applicazione prevede l'inserimento della data:

  • in [1], il modulo di immissione
  • in [2], la risposta restituita

L'applicazione funziona in modo simile all'input di numeri interi, quindi commenteremo solo i punti che differiscono.

14.1. Il progetto NetBeans

Il progetto NetBeans è il seguente:

  • in [1], le viste dell'applicazione
  • [Home.jsp]: la pagina iniziale
  • [FormDate.jsp]: il modulo di inserimento
  • [ConfirmationFormDate.jsp]: la pagina di conferma
  • in [2], il file dei messaggi [messages.properties] e il file di configurazione principale di Struts
  • in [3]:
  • [FormDate.java]: l'azione che visualizza ed elabora il modulo
  • [FormDate-validation.xml]: le regole di convalida per l'azione [FormDate]. Questo file delega tali convalide al modello.
  • [FormDateModel]: il modello per l'azione [FormDate]
  • [FormDateModel-validation.xml]: le regole di validazione per il modello
  • [FormDateModel.properties]: il file dei messaggi del modello
  • [example.xml]: file di configurazione secondario di Struts

14.2. Configurazione del progetto

Il progetto è configurato principalmente dal seguente file [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>

È simile a quello discusso per l'inserimento di numeri interi.

14.3. File dei messaggi

Il file [messages.properties] è il seguente:


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}".

Il file [FormDateModel.properties] è il seguente:


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

La riga 1 svolge un ruolo importante. Torneremo su questo punto.

14.4. Il modulo di inserimento dati

La vista [FormDate.jsp] è la seguente:


<%@ 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>

Le righe da 13 a 18 sono i sei campi di immissione della data. I campi da date2 a date5 hanno un attributo value complesso, lo stesso utilizzato per l'immissione di numeri reali. Poiché si sono riscontrate le stesse difficoltà, sono state risolte allo stesso modo.

14.5. La vista di conferma

La vista di conferma [ConfirmationFormDate.jsp] è la seguente:

Image

Il codice è il seguente:


<%@ 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>

La vista [ConfirmationFormDate.jsp] visualizza semplicemente il [FormDateModel]. Le righe 47–49 mostrano come viene visualizzata una data. Vogliamo che questa visualizzazione segua un formato di data specifico. Per farlo, utilizziamo il tag <s:text>, che abbiamo già utilizzato in precedenza per l'internazionalizzazione dell'applicazione.

Qui, la chiave del messaggio utilizzata è *date.format*. Questa chiave si trova nel file FormDateModel.properties:


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

Il valore associato alla chiave non è un messaggio, ma un formato di visualizzazione:

  • 0 è un parametro che rappresenta il valore da visualizzare. In questo caso, sarà il campo date5 del modello.
  • date rappresenta una data. In precedenza, abbiamo usato number per un numero.
  • dd/MM/yyyy rappresenta il formato della data. In questo caso, lo vogliamo nella forma dd/mm/yyyy. Il formato Java appropriato è dd/MM/yyyy (d: giorno, M: mese, y: anno)

Il tag


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

visualizza il messaggio {0, data, gg/MM/aaaa}, dove il parametro date5 (riga 2) sostituisce il parametro 0 nel formato. Pertanto, il modello date5 verrà visualizzato nel formato gg/mm/aaaa.

14.6. Il modello [FormDateModel]

I campi da date1 a date6 del modulo [FormDate.jsp] vengono inseriti nel seguente modello [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
...
}
  • I campi date1 e date6 sono di tipo String
  • gli altri campi sono di tipo Date

14.7. Convalida del modello

La convalida del modello è gestita da due file: [FormDate-validation.xml] e [FormDateModel-validation.xml].

Il file [FormDate-validation.xml] delega le validazioni a [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>

Abbiamo già incontrato questo file.

Il file [FormDateModel-validation.xml] contiene le seguenti regole di convalida:


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

  • La regola nelle righe 11–20 verifica che il campo di input date1 segua lo schema di un'espressione regolare che rappresenta una data nel formato dd/mm/yyyy. Si noti che verifichiamo che la stringa abbia la forma di una data, ma non verifichiamo che sia una data valida. Pertanto, 29/02/2011 ha la forma di una data ma non è una data valida.
  • La regola nelle righe 22–29 verifica che il campo di immissione date2 sia una data valida.
  • La regola nelle righe 31–42 verifica che il campo di immissione date3 sia una data valida >= 18/05/2000
  • La regola nelle righe 44–55 verifica che il campo di immissione date4 sia una data valida pari o precedente al 20/06/2001.
  • La regola nelle righe 57-69 verifica che il campo di immissione date5 sia una data valida <= 20 giugno 2001 e >= 18 maggio 2000.
  • La regola nelle righe 71–80 verifica che il campo date6 sia nel formato gg/mm/aaaa.

Una volta che il file [FormDateModel-validation.xml] è stato elaborato dall'intercettatore di validazione, quest'ultimo esegue il metodo validate dell'azione [FormDate], se esiste. Lo presenteremo insieme all'intera azione.

14.8. L'azione [FormDate]

L'azione [FormDate] è la seguente:


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;
      }
    }
  }
}

L'azione [FormDate] è basata sullo stesso modello dell'azione [FormInt]. Ci limiteremo a commentare il metodo validate. Ricordiamo che il metodo validate viene eseguito dopo l'elaborazione del file di validazione [FormDateModel-validation.xml] e prima dell'esecuzione del metodo execute.

  • Il metodo validate completa la convalida dei campi date1 e date6. Il file di convalida [FormDateModel-validation.xml] aveva già verificato che le stringhe inserite fossero in formato data. Ora verifichiamo che siano effettivamente date valide. Si noti inoltre che date1 e date6 erano gli unici due campi nel modello di tipo String.
  • Riga 50: creiamo un formato data dd/mm/yyyy per verificare che le stringhe di questo tipo siano effettivamente date valide.
  • Riga 51: setLenient(false) impedisce che la data non valida 32/01/2012 venga interpretata come la data valida 01/02/2012.
  • Riga 53: Convalidiamo date1 solo se le precedenti convalide non hanno riscontrato errori in questo campo.
  • Riga 56: Verifichiamo che date1 sia una data valida nel formato gg/mm/aaaa.
  • Riga 58: Se così non fosse, viene generato un messaggio di errore associato al campo date1.
  • Riga 62: date6 viene convalidata solo se questo campo ha superato le precedenti convalide.
  • Riga 66: Verifichiamo che date6 sia una data valida
  • Riga 68: Se date6 è <18/05/2000 o date6 > 20/06/2001, allora date6 non è corretta.

14.9. Dettagli finali

Il modulo precedente presenta dei difetti, come mostrato nell'esempio seguente:

  • In [1] è stata inserita una data non valida
  • in [2] è stata accettata. Sono stati presi in considerazione solo i primi caratteri gg/mm/aaaa.

I campi da date2 a date5, collegati a modelli di tipo Date, presentano tutti questo problema. Ancora una volta, forse mi è sfuggito qualcosa nella documentazione. I campi date1 e date6, collegati a modelli di tipo String, non presentano questo problema.