14. Exemplo 11 – Conversão e validação de datas
A nova aplicação apresenta a introdução de datas:
![]() |
- em [1], o formulário de introdução
- em [2], a resposta devolvida
A aplicação funciona de forma semelhante à introdução de números inteiros, pelo que apenas comentaremos os pontos que diferem.
14.1. O projeto NetBeans
O projeto NetBeans é o seguinte:
![]() |
- em [1], as vistas da aplicação
- [Accueil.JSP]: a página inicial
- [FormDate.JSP]: o formulário de introdução de dados
- [ConfirmationFormDate.JSP]: a página de confirmação
- em [2], o ficheiro de mensagens [messages.properties] e o ficheiro de configuração principal do Struts
- em [3]:
- [FormDate.java]: a ação que apresenta e processa o formulário
- [FormDate-validation.xml]: as regras de validação da ação [FormDate]. Este ficheiro delega essas validações ao modelo.
- [FormDateModel]: o modelo da ação [FormDate]
- [ FormDateModel-validation.xml]: as regras de validação do modelo
- [FormDateModel.properties]: o ficheiro de mensagens do modelo
- [example.xml]: ficheiro de configuração secundário do Struts
14.2. A configuração do projeto
O projeto é configurado principalmente pelo seguinte ficheiro [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>
É semelhante ao que foi analisado para a introdução de números inteiros.
14.3. Os ficheiros de mensagens
O ficheiro [messages.properties] é o seguinte:
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}".
O ficheiro [FormDateModel.properties] é o seguinte:
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
A linha 1 desempenha um papel importante. Voltaremos a este assunto mais tarde.
14.4. O formulário de introdução de dados
A vista [FormDate.JSP] é a seguinte:
<%@ 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>
As linhas 13 a 18 correspondem aos seis campos de introdução de datas. Os campos date2 a date5 têm um atributo complexo value, o mesmo utilizado para a introdução de números reais. Uma vez que se depararam com as mesmas dificuldades, estas foram resolvidas da mesma forma.
14.5. A vista de confirmação
A vista de confirmação [ConfirmationFormDate.JSP] é a seguinte:
![]() |
O seu código é o seguinte:
<%@ 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>
A vista [ConfirmationFormDate.JSP] limita-se a apresentar o modelo [FormDateModel]. As linhas 47-49 mostram a apresentação de uma data. Pretende-se que esta exibição tenha em conta um formato de data. Para tal, utiliza-se a baliza <s:text>, que já tinha sido utilizada até então para a internacionalização da aplicação.
Aqui, a chave de mensagem utilizada é date.format. Esta chave encontra-se no ficheiro [FormDateModel.properties]:
date.format={0,date,dd/MM/yyyy}
O valor associado à chave não é, neste caso, uma mensagem, mas sim um formato de exibição:
- 0 é um parâmetro que representa o valor a apresentar. Neste caso, será o campo date5 do modelo.
- date representa uma data. Anteriormente, tínhamos number para um número.
- dd/MM/yyyy representa o formato da data. Neste caso, queremos que seja na forma dd/mm/aaaa. O formato Java adequado é dd/MM/yyyy (d: dia, M: mês, y: ano)
A baliza
<s:text name="date.format">
<s:param value="date5"/>
</s:text>
faz com que seja exibida a mensagem {0, data, dd/MM/yyyy}, em que o parâmetro date5 (linha 2) substituirá o parâmetro 0 do formato. Assim, o modelo date5 será apresentado no formato dd/mm/aaaa.
14.6. O modelo [FormDateModel]
Os campos date1 a date6 do formulário [FormDate.JSP] são inseridos no seguinte modelo [FormDateModel]:
package example;
import java.util.Date;
public class FormDateModel {
// construtor sem parâmetros
public FormDateModel() {
}
// campos
private String date1;
private Date date2 = new Date();
private Date date3 = new Date();
private Date date4;
private Date date5;
private String date6;
// inicialização do modelo
public void clearModel() {
date1 = null;
date2 = null;
date3 = null;
date4 = null;
date5 = null;
date6 = null;
}
// getters e setters
...
}
- os campos date1 e date6 são do tipo String
- os restantes campos são do tipo Date
14.7. A validação do modelo
A validação do modelo é controlada por dois ficheiros: [FormDate-validation.xml] e [FormDateModel-validation.xml].
O ficheiro [FormDate-validation.xml] delega as validações ao ficheiro [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/exemplo-10/exemplo/xwork-validator-1.0.2.dtd">
<validators>
<field name="model" >
<field-validator type="visitor">
<param name="appendPrefix">false</param>
<message/>
</field-validator>
</field>
</validators>
Já nos deparámos com este ficheiro.
O ficheiro [FormDateModel-validation.xml] contém as seguintes regras de validação:
<!--
<!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/exemplo-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>
- a regra das linhas 11-20 verifica se o campo de introdução de dados date1 segue o modelo de uma expressão regular que representa uma data no formato dd/mm/aaaa. Note-se que se verifica se a cadeia de caracteres tem o formato de uma data, mas não se verifica se é uma data válida. Assim, 29/02/2011 tem o formato de uma data, mas não é uma data válida.
- A regra das linhas 22-29 verifica se o campo de introdução date2 é uma data válida.
- A regra das linhas 31-42 verifica se o campo de introdução date3 é uma data válida >= 18/05/2000
- A regra das linhas 44-55 verifica se o campo de introdução date4 é uma data válida <= 20/06/2001.
- A regra das linhas 57-69 verifica se o campo de introdução date5 é uma data válida <= 20/06/2001 e >= 18/05/2000.
- A regra das linhas 71-80 verifica se o campo date6 tem o formato de data dd/mm/aaaa.
Assim que o ficheiro [FormDateModel-validation.xml] for processado pelo interceptor de validação, este executa o método validate da ação [FormDate], caso este exista. Iremos apresentá-lo em simultâneo com a ação na sua totalidade.
14.8. A ação [FormDate]
A ação [FormDate] é a seguinte:
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 {
// construtor sem parâmetros
public FormDate() {
}
// modelo da ação
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormDateModel());
}
return session.get("model");
}
@SkipValidation
public String clearModel() {
// raz do modelo
((FormDateModel) getModel()).clearModel();
// resultado
return INPUT;
}
public String cancel() {
// limpa-se o modelo
((FormDateModel) getModel()).clearModel();
// resultado
return "cancel";
}
// SessionAware
Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
// validação
@Override
public void validate() {
// formatação das datas
SimpleDateFormat formateurDate = new SimpleDateFormat("dd/MM/yyyy");
formateurDate.setLenient(false);
// a data1 introduzida é válida?
if (getFieldErrors().get("date1") == null) {
// verificação da validade da data
try {
formateurDate.parse(((FormDateModel) getModel()).getDate1());
} catch (Exception e) {
addFieldError("date1", getText("date1.error"));
}
}
// A data 6 introduzida é válida?
if (getFieldErrors().get("date6") == null) {
Date d = null;
try {
// verificação da validade da data
d = formateurDate.parse(((FormDateModel) getModel()).getDate6());
// verificação dos limites
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;
}
}
}
}
A ação [FormDate] segue o mesmo modelo da ação [FormInt]. Iremos comentar apenas o método validate. Recorde-se que o método validate é executado após o processamento do ficheiro de validação [FormDateModel-validation.xml] e antes da execução do método execute.
- O método validate completa a validação das datas date1 e date6. O ficheiro de validação [FormDateModel-validation.xml] tinha verificado se as cadeias introduzidas tinham o formato de uma data. Verifica-se agora se se trata efetivamente de datas válidas. Recorde-se também que date1 e date6 eram os únicos dois campos do modelo a serem do tipo String.
- linha 50: cria-se um formato de data dd/mm/aaaa para verificar se as cadeias deste tipo são, de facto, datas válidas.
- linha 51: setLenient(false) permite evitar que a data inválida 32/01/2012 seja interpretada como a data válida 01/02/2012.
- linha 53: só se valida date1 se as validações anteriores não tiverem apresentado erros neste campo.
- linha 56: verifica-se se date1 é uma data válida no formato dd/mm/aaaa.
- linha 58: se não for o caso, atribui-se uma mensagem de erro ao campo date1.
- linha 62: só se valida date6 se este campo tiver sido aprovado nas validações anteriores.
- linha 66: verifica-se se date6 é uma data válida
- linha 68: se date6 for <18/05/2000 ou date6>20/06/2001, então date6 está incorreto.
14.9. Últimos detalhes
O formulário anterior apresenta falhas, como demonstra o exemplo seguinte:
![]() |
- em [1], introduz-se uma data inválida
- em [2], esta foi aceite. Apenas os primeiros caracteres dd/mm/aaaa foram tidos em conta.
Os campos date2 a date5, que estão associados a modelos do tipo Date, apresentam todos este problema. Mais uma vez, talvez me tenha escapado alguma informação na documentação. Os campos date1 e date6, que estão associados a modelos do tipo String, não apresentam este problema.



