14. Voorbeeld 11 – Omzetting en validatie van datums
De nieuwe applicatie bevat een functie voor het invoeren van datums:
![]() |
- in [1], het invoerformulier
- in [2], het teruggestuurde antwoord
De applicatie werkt op dezelfde manier als bij de invoer van gehele getallen, dus zullen we alleen de punten bespreken die verschillen.
14.1. Het NetBeans-project
Het NetBeans-project ziet er als volgt uit:
![]() |
- in [1], de weergaven van de applicatie
- [Accueil.JSP]: de startpagina
- [FormDate.JSP]: het invoerformulier
- [ConfirmationFormDate.JSP]: de bevestigingspagina
- in [2], het berichtenbestand [messages.properties] en het hoofdconfiguratiebestand van Struts
- in [3]:
- [FormDate.java]: de actie die het formulier weergeeft en verwerkt
- [FormDate-validation.xml]: de validatieregels voor de actie [FormDate]. Dit bestand delegeert deze validaties aan het model.
- [FormDateModel]: het model van de actie [FormDate]
- [ FormDateModel-validation.xml]: de validatieregels van het model
- [FormDateModel.properties]: het berichtenbestand van het model
- [example.xml]: secundair configuratiebestand van Struts
14.2. De configuratie van het project
Het project wordt voornamelijk geconfigureerd via het volgende bestand [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>
Dit is vergelijkbaar met het bestand dat is besproken voor de invoer van gehele getallen.
14.3. De berichtenbestanden
Het bestand [messages.properties] ziet er als volgt uit:
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}".
Het bestand [FormDateModel.properties] ziet er als volgt uit:
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
Regel 1 speelt een belangrijke rol. We komen hier later op terug.
14.4. Het invoerformulier
De weergave [FormDate.JSP] ziet er als volgt uit:
<%@ 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>
De regels 13 tot en met 18 zijn de zes velden voor het invoeren van datums. De velden date2 tot en met date5 hebben een complex attribuut value, hetzelfde als voor de invoer van reële getallen. Aangezien dezelfde problemen zich voordeden, zijn deze op dezelfde manier opgelost.
14.5. Het bevestigingsscherm
Het bevestigingsscherm [ConfirmationFormDate.JSP] ziet er als volgt uit:
![]() |
De code ervan is als volgt:
<%@ 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>
Het scherm [ConfirmationFormDate.JSP] geeft alleen het sjabloon [FormDateModel] weer. De regels 47-49 tonen de weergave van een datum. We willen dat bij deze weergave rekening wordt gehouden met een datumformaat. Hiervoor gebruiken we de tag <s:text>, die we tot nu toe hadden gebruikt voor de internationalisering van de applicatie.
Hier is de gebruikte berichtcode date.format. Deze code is te vinden in het bestand [FormDateModel.properties]:
date.format={0,date,dd/MM/yyyy}
De waarde die aan de sleutel is gekoppeld, is hier geen bericht, maar een weergaveformaat:
- 0 is een parameter die de weer te geven waarde vertegenwoordigt. In dit geval is dat het veld date5 van het model.
- date staat voor een datum. Eerder hadden we number voor een getal.
- dd/MM/yyyy staat voor het datumformaat. Hier willen we de vorm dd/mm/jjjj. Het juiste Java-formaat is dd/MM/yyyy (d: dag, M: maand, y: jaar)
De tag
<s:text name="date.format">
<s:param value="date5"/>
</s:text>
zorgt ervoor dat het bericht {0, date, dd/MM/yyyy} wordt weergegeven, waarbij de parameter date5 (regel 2) de parameter 0 van het formaat vervangt. Het sjabloon date5 wordt dus weergegeven in de notatie dd/mm/jjjj.
14.6. Het sjabloon [FormDateModel]
De velden date1 tot en met date6 van het formulier [FormDate.JSP] worden ingevoegd in het volgende sjabloon [FormDateModel]:
package example;
import java.util.Date;
public class FormDateModel {
// constructor zonder parameters
public FormDateModel() {
}
// velden
private String date1;
private Date date2 = new Date();
private Date date3 = new Date();
private Date date4;
private Date date5;
private String date6;
// model-raz
public void clearModel() {
date1 = null;
date2 = null;
date3 = null;
date4 = null;
date5 = null;
date6 = null;
}
// getters en setters
...
}
- de velden date1 en date6 zijn van het type String
- de overige velden zijn van het type Date
14.7. De validatie van het model
De validatie van het model wordt gecontroleerd door twee bestanden: [FormDate-validation.xml] en [FormDateModel-validation.xml].
Het bestand [FormDate-validation.xml] delegeert de validaties aan [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/voorbeeld-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>
We zijn dit bestand al eerder tegengekomen.
Het bestand [FormDateModel-validation.xml] bevat de volgende validatieregels:
<!--
<!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/voorbeeld-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>
- de regel in de regels 11-20 controleert of het invoerveld date1 voldoet aan het patroon van een reguliere expressie die een datum in de vorm dd/mm/jjjj weergeeft. Let wel: er wordt gecontroleerd of de tekenreeks de vorm van een datum heeft, maar er wordt niet gecontroleerd of het een geldige datum is. Zo heeft 29/02/2011 wel de vorm van een datum, maar is het geen geldige datum.
- De regel op de regels 22-29 controleert of het invoerveld date2 een geldige datum is.
- De regel in de regels 31-42 controleert of het invoerveld date3 een geldige datum is >= 18/05/2000
- De regel in de regels 44-55 controleert of het invoerveld date4 een geldige datum is <= 20/06/2001.
- De regel voor de regels 57-69 controleert of het invoerveld date5 een geldige datum is <= 20/06/2001 en >= 18/05/2000.
- De regel voor de regels 71-80 controleert of het veld date6 de datumnotatie dd/mm/jjjj heeft.
Zodra het bestand [FormDateModel-validation.xml] door de validatie-interceptor is verwerkt, laat deze de methode validate van de actie [FormDate] uitvoeren, indien deze bestaat. We zullen deze samen met de volledige actie presenteren.
14.8. De actie [FormDate]
De actie [FormDate] is als volgt:
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 zonder parameters
public FormDate() {
}
// actiemodel
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormDateModel());
}
return session.get("model");
}
@SkipValidation
public String clearModel() {
// model-raz
((FormDateModel) getModel()).clearModel();
// resultaat
return INPUT;
}
public String cancel() {
// het model wordt opgeschoond
((FormDateModel) getModel()).clearModel();
// resultaat
return "cancel";
}
// SessionAware
Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
// validatie
@Override
public void validate() {
// opmaak van datums
SimpleDateFormat formateurDate = new SimpleDateFormat("dd/MM/yyyy");
formateurDate.setLenient(false);
// is datum1 geldig?
if (getFieldErrors().get("date1") == null) {
// controle geldigheid datum
try {
formateurDate.parse(((FormDateModel) getModel()).getDate1());
} catch (Exception e) {
addFieldError("date1", getText("date1.error"));
}
}
// is datum 6 geldig?
if (getFieldErrors().get("date6") == null) {
Date d = null;
try {
// controle geldigheid datum
d = formateurDate.parse(((FormDateModel) getModel()).getDate6());
// controle van de grenzen
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;
}
}
}
}
De actie [FormDate] is opgebouwd volgens hetzelfde model als de actie [FormInt]. We zullen alleen de methode validate toelichten. Ter herinnering: de methode validate wordt uitgevoerd na verwerking van het validatiebestand [FormDateModel-validation.xml] en vóór de uitvoering van de methode execute.
- De methode validate voltooit de validatie van de datums uit de methoden date1 en date6. Het validatiebestand [FormDateModel-validation.xml] had gecontroleerd of de ingevoerde tekenreeksen de vorm van een datum hadden. Nu wordt gecontroleerd of het inderdaad om geldige datums gaat. Ter herinnering: date1 en date6 waren de enige twee velden in het model die van het type String waren.
- regel 50: er wordt een datumformaat dd/mm/jjjj aangemaakt om te controleren of tekenreeksen van dit type inderdaad geldige datums zijn.
- regel 51: setLenient(false) voorkomt dat de ongeldige datum 32/01/2012 wordt geïnterpreteerd als de geldige datum 01/02/2012.
- regel 53: date1 wordt alleen gevalideerd als de voorgaande validaties geen fouten hebben opgeleverd voor dit veld.
- regel 56: er wordt gecontroleerd of date1 een geldige datum is in de notatie dd/mm/jjjj.
- regel 58: als dat niet het geval is, wordt er een foutmelding gekoppeld aan het veld date1.
- regel 62: date6 wordt alleen gevalideerd als dit veld de voorgaande validaties heeft doorstaan.
- regel 66: er wordt gecontroleerd of date6 een geldige datum is
- regel 68: als date6 <18/05/2000 of date6>20/06/2001 is, dan is date6 onjuist.
14.9. Laatste details
Het vorige formulier vertoont fouten, zoals blijkt uit het volgende voorbeeld:
![]() |
- in [1] wordt een ongeldige datum ingevoerd
- in [2] werd deze geaccepteerd. Alleen de eerste tekens dd/mm/jjjj werden in aanmerking genomen.
De velden date2 tot en met date5, die gekoppeld zijn aan sjablonen van het type Date, hebben allemaal dit probleem. Ook hier geldt: misschien heb ik iets over het hoofd gezien in de documentatie. De velden date1 en date6, die gekoppeld zijn aan sjablonen van het type String, hebben dit probleem niet.



