Skip to content

14. Esempio 11 – Conversione e convalida delle date

La nuova applicazione prevede l’inserimento di date:

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

Il funzionamento dell’applicazione è simile a quello dell’inserimento dei numeri interi, pertanto commenteremo solo i punti che differiscono.

14.1. Il progetto NetBeans

Il progetto NetBeans è il seguente:

  • in [1], le viste dell'applicazione
    • [Accueil.JSP]: la pagina iniziale
    • [FormDate.JSP]: il modulo di inserimento dati
    • [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 dell'azione [FormDate]. Questo file delega tali convalide al modello.
    • [FormDateModel]: il modello dell'azione [FormDate]
    • [ FormDateModel-validation.xml]: le regole di convalida del 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>

È analogo a quello esaminato per l’inserimento dei numeri interi.

14.3. I 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 riveste un ruolo importante. Ci torneremo più avanti.

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 delle date. I campi da date2 a date5 presentano un attributo complesso value, lo stesso utilizzato per l’immissione dei numeri reali. Poiché si sono presentate le stesse difficoltà, le abbiamo risolte allo stesso modo.

14.5. La vista di conferma

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

 

Il suo 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] si limita a visualizzare il modello [FormDateModel]. Le righe 47-49 mostrano la visualizzazione di una data. Si desidera che questa visualizzazione tenga conto di un formato di data. A tal fine, si utilizza il tag <s:text> che era stato utilizzato fino a quel momento per l’internazionalizzazione dell’applicazione.

In questo caso, 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 è in questo caso un messaggio, ma un formato di visualizzazione:

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

Il tag


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

fa visualizzare il messaggio {0, data, dd/MM/yyyy}, dove il parametro date5 (riga 2) sostituirà il parametro 0 del 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 {

  // costruttore senza parametri
  public FormDateModel() {
  }
  // campi
  private String date1;
  private Date date2 = new Date();
  private Date date3 = new Date();
  private Date date4;
  private Date date5;
  private String date6;

  // azzeramento modello
  public void clearModel() {
    date1 = null;
    date2 = null;
    date3 = null;
    date4 = null;
    date5 = null;
    date6 = null;
  }

  // getter e setter
...
}
  • i campi date1 e date6 sono di tipo String
  • gli altri campi sono di tipo Date

14.7. La convalida del modello

La convalida del modello è controllata 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/esempio-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/esempio-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 delle righe 11-20 verifica che il campo di immissione date1 segua il modello di un'espressione regolare che rappresenta una data nel formato gg/mm/aaaa. Si noti che viene verificato che la stringa abbia la forma di una data, ma non che si tratti di una data valida. Pertanto, 29/02/2011 ha la forma di una data ma non è una data valida.
  • La regola delle righe 22-29 verifica che il campo di immissione date2 sia una data valida.
  • La regola delle righe 31-42 verifica che il campo di immissione date3 sia una data valida >=18/05/2000
  • la regola delle righe 44-55 verifica che il campo di immissione date4 sia una data valida <=20/06/2001.
  • la regola delle righe 57-69 verifica che il campo di immissione date5 sia una data valida <=20/06/2001 e >=18/05/2000.
  • La regola delle righe 71-80 verifica che il campo date6 abbia il formato di una data gg/mm/aaaa.

Una volta che il file [FormDateModel-validation.xml] è stato elaborato dall’intercettatore di convalida, quest’ultimo fa eseguire il metodo validate dell’azione [FormDate], se presente. 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 {

  // costruttore senza parametri
  public FormDate() {
  }

  // modello dell'azione
  public Object getModel() {
    if (session.get("model") == null) {
      session.put("model", new FormDateModel());
    }
    return session.get("model");
  }

  @SkipValidation
  public String clearModel() {
    // campo vuoto del modello
    ((FormDateModel) getModel()).clearModel();
    // risultato
    return INPUT;
  }

  public String cancel() {
    // si ripulisce il modello
    ((FormDateModel) getModel()).clearModel();
    // risultato
    return "cancel";
  }
  // SessionAware
  Map<String, Object> session;

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

  // convalida
  @Override
  public void validate() {
    // formattazione delle date
    SimpleDateFormat formateurDate = new SimpleDateFormat("dd/MM/yyyy");
    formateurDate.setLenient(false);
    // data1 inserita valida?
    if (getFieldErrors().get("date1") == null) {
      // verifica validità data
      try {
        formateurDate.parse(((FormDateModel) getModel()).getDate1());
      } catch (Exception e) {
        addFieldError("date1", getText("date1.error"));
      }
    }
    // data6 inserita valida?
    if (getFieldErrors().get("date6") == null) {
      Date d = null;
      try {
        // verifica validità data
        d = formateurDate.parse(((FormDateModel) getModel()).getDate6());
        // verifica dei limiti
        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] è costruita sullo stesso modello dell'azione [FormInt]. Ci limiteremo a commentare il metodo validate. Si ricorda che il metodo validate viene eseguito dopo l’elaborazione del file di convalida [FormDateModel-validation.xml] e prima dell’esecuzione del metodo execute.

  • Il metodo validate completa la convalida delle date effettuata dai metodi date1 e date6. Il file di convalida [FormDateModel-validation.xml] aveva verificato che le stringhe inserite avessero la forma di una data. Ora si verifica che si tratti effettivamente di date valide. Si ricorda inoltre che date1 e date6 erano gli unici due campi del modello ad essere di tipo String.
  • riga 50: si crea un formato di data gg/mm/aaaa 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: si convalida date1 solo se le precedenti convalide non hanno rilevato errori su questo campo.
  • riga 56: si verifica che date1 sia una data valida nel formato gg/mm/aaaa.
  • riga 58: se non è così, si associa un messaggio di errore al campo date1.
  • riga 62: si convalida date6 solo se questo campo ha superato le precedenti convalide.
  • riga 66: si verifica che date6 sia una data valida
  • riga 68: se date6 è <18/05/2000 o >20/06/2001, allora date6 non è corretto.

14.9. Ultimi dettagli

Il modulo precedente presenta delle lacune, come mostra il seguente esempio:

  • in [1], inserendo 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. Anche in questo caso, forse mi è sfuggito qualcosa nella documentazione. I campi date1 e date6, collegati a modelli di tipo String, non presentano questo problema.