14. Przykład 11 – Konwersja i walidacja dat
Nowa aplikacja umożliwia wprowadzanie dat:
![]() |
- w [1], formularz wprowadzania danych
- na [2], zwracana odpowiedź
Aplikacja działa podobnie jak w przypadku wprowadzania liczb całkowitych, dlatego omówimy tylko te różnice.
14.1. Projekt NetBeans
Projekt NetBeans wygląda następująco:
![]() |
- w [1], widoki aplikacji
- [Accueil.JSP]: strona główna
- [FormDate.JSP]: formularz wprowadzania danych
- [ConfirmationFormDate.JSP]: strona potwierdzenia
- w [2], plik komunikatów [messages.properties] oraz główny plik konfiguracyjny Struts
- w [3]:
- [FormDate.java]: akcja wyświetlająca i przetwarzająca formularz
- [FormDate-validation.xml]: reguły walidacji akcji [FormDate]. Plik ten przekazuje te walidacje do modelu.
- [FormDateModel]: model akcji [FormDate]
- [ FormDateModel-validation.xml]: reguły walidacji modelu
- [FormDateModel.properties]: plik komunikatów modelu
- [example.xml]: dodatkowy plik konfiguracyjny Struts
14.2. Konfiguracja projektu
Projekt jest konfigurowany głównie za pomocą następującego pliku [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>
Jest on analogiczny do tego, który został omówiony w przypadku wprowadzania liczb całkowitych.
14.3. Pliki komunikatów
Plik [messages.properties] ma następującą postać:
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}".
Plik [FormDateModel.properties] ma następującą treść:
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
Ważną rolę odgrywa wiersz 1. Wrócimy do niego później.
14.4. Formularz wprowadzania danych
Widok [FormDate.JSP] wygląda następująco:
<%@ 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>
Wiersze od 13 do 18 to sześć pól do wprowadzania dat. Pola od date2 do date5 mają złożony atrybut value, taki sam jak w przypadku wprowadzania liczb rzeczywistych. Ponieważ napotkano te same trudności, rozwiązano je w ten sam sposób.
14.5. Ekran potwierdzenia
Widok potwierdzenia [ConfirmationFormDate.JSP] wygląda następująco:
![]() |
Jej kod jest następujący:
<%@ 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>
Widok [ConfirmationFormDate.JSP] ogranicza się do wyświetlania szablonu [FormDateModel]. Wiersze 47–49 pokazują wyświetlanie daty. Chcemy, aby wyświetlanie to uwzględniało format daty. W tym celu wykorzystujemy znacznik <s:text>, którego używaliśmy dotychczas do internacjonalizacji aplikacji.
W tym przypadku używany jest klucz komunikatu date.format. Klucz ten znajduje się w pliku [FormDateModel.properties]:
date.format={0,date,dd/MM/yyyy}
Wartość powiązana z tym kluczem nie jest tutaj komunikatem, lecz formatem wyświetlania:
- 0 to parametr reprezentujący wartość, która ma zostać wyświetlona. W tym przypadku będzie to pole date5 w szablonie.
- data oznacza datę. Wcześniej jako liczbę podano number.
- dd/MM/yyyy oznacza format daty. W tym przypadku chcemy uzyskać format dd/mm/rrrr. Odpowiedni format w Javie to dd/MM/yyyy (d: dzień, M: miesiąc, y: rok)
Tag
<s:text name="date.format">
<s:param value="date5"/>
</s:text>
powoduje wyświetlenie komunikatu {0, data, dd/MM/yyyy}, gdzie parametr date5 (wiersz 2) zastąpi parametr 0 w formacie. W ten sposób szablon date5 zostanie wyświetlony w formacie dd/mm/rrrr.
14.6. Szablon [FormDateModel]
Pola od date1 do date6 z formularza [FormDate.JSP] są wstawiane do następującego szablonu [FormDateModel]:
package example;
import java.util.Date;
public class FormDateModel {
// konstruktor bez parametrów
public FormDateModel() {
}
// pola
private String date1;
private Date date2 = new Date();
private Date date3 = new Date();
private Date date4;
private Date date5;
private String date6;
// szablon
public void clearModel() {
date1 = null;
date2 = null;
date3 = null;
date4 = null;
date5 = null;
date6 = null;
}
// metody pobierające i ustawiające
...
}
- Pola date1 i date6 są typu String
- pozostałe pola są typu Date
14.7. Walidacja modelu
Walidacja modelu jest kontrolowana przez dwa pliki: [FormDate-validation.xml] i [FormDateModel-validation.xml].
Plik [FormDate-validation.xml] przekazuje zadania walidacji do pliku [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/przykład-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>
Ten plik już wcześniej spotkaliśmy.
Plik [FormDateModel-validation.xml] zawiera następujące reguły walidacji:
<!--
<!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/przykład-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>
- reguła w wierszach 11–20 sprawdza, czy pole wprowadzania danych date1 jest zgodne ze wzorcem wyrażenia regularnego reprezentującego datę w formacie dd/mm/rrrr. Należy zauważyć, że sprawdzane jest, czy ciąg znaków ma postać daty, ale nie jest sprawdzane, czy jest to data prawidłowa. Tak więc 29/02/2011 ma postać daty, ale nie jest datą prawidłową.
- Reguła w wierszach 22–29 sprawdza, czy pole wprowadzania danych date2 zawiera prawidłową datę.
- Reguła w wierszach 31–42 sprawdza, czy pole wprowadzania danych date3 jest prawidłową datą >= 18/05/2000
- reguła w wierszach 44–55 sprawdza, czy pole wprowadzania danych date4 zawiera prawidłową datę <= 20.06.2001.
- Reguła dotycząca wierszy 57–69 sprawdza, czy pole wprowadzania danych date5 zawiera prawidłową datę <=20.06.2001 i >=18.05.2000.
- Reguła dotycząca wierszy 71–80 sprawdza, czy pole date6 ma postać daty dd/mm/rrrr.
Po przetworzeniu pliku [FormDateModel-validation.xml] przez moduł sprawdzający poprawność, moduł ten uruchamia metodę validate akcji [FormDate], o ile taka metoda istnieje. Przedstawimy ją wraz z całą akcją.
14.8. Akcja [FormDate]
Akcja [FormDate] wygląda następująco:
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 {
// konstruktor bez parametrów
public FormDate() {
}
// szablon akcji
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormDateModel());
}
return session.get("model");
}
@SkipValidation
public String clearModel() {
// dane modelu
((FormDateModel) getModel()).clearModel();
// wynik
return INPUT;
}
public String cancel() {
// oczyszczamy model
((FormDateModel) getModel()).clearModel();
// wynik
return "cancel";
}
// SessionAware
Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
// walidacja
@Override
public void validate() {
// formatowanie dat
SimpleDateFormat formateurDate = new SimpleDateFormat("dd/MM/yyyy");
formateurDate.setLenient(false);
// czy wprowadzona data1 jest prawidłowa?
if (getFieldErrors().get("date1") == null) {
// sprawdzenie poprawności daty
try {
formateurDate.parse(((FormDateModel) getModel()).getDate1());
} catch (Exception e) {
addFieldError("date1", getText("date1.error"));
}
}
// czy wprowadzona data6 jest prawidłowa?
if (getFieldErrors().get("date6") == null) {
Date d = null;
try {
// weryfikacja poprawności daty
d = formateurDate.parse(((FormDateModel) getModel()).getDate6());
// sprawdzanie granic
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;
}
}
}
}
Akcja [FormDate] jest zbudowana na tym samym wzorze co akcja [FormInt]. Omówimy jedynie metodę validate. Przypominamy, że metoda validate jest wykonywana po przetworzeniu pliku walidacyjnego [FormDateModel-validation.xml] i przed wykonaniem metody execute.
- Metoda validate uzupełnia walidację dat przeprowadzoną przez metody date1 i date6. Plik walidacyjny [FormDateModel-validation.xml] sprawdził, czy wprowadzone ciągi znaków miały postać daty. Teraz sprawdzamy, czy są to rzeczywiście prawidłowe daty. Przypominamy również, że date1 i date6 były jedynymi dwoma polami w szablonie, które miały typ String.
- wiersz 50: tworzymy format daty dd/mm/rrrr, aby sprawdzić, czy ciągi tego typu są rzeczywiście prawidłowymi datami.
- wiersz 51: setLenient(false) pozwala uniknąć sytuacji, w której nieprawidłowa data 32/01/2012 zostałaby zinterpretowana jako prawidłowa data 01/02/2012.
- wiersz 53: date1 jest sprawdzane tylko wtedy, gdy poprzednie weryfikacje nie wykazały błędów w tym polu.
- wiersz 56: sprawdzamy, czy date1 jest prawidłową datą w formacie dd/mm/rrrr.
- wiersz 58: jeśli tak nie jest, do pola date1 przypisuje się komunikat o błędzie.
- wiersz 62: date6 jest zatwierdzane tylko wtedy, gdy pole to przeszło poprzednie weryfikacje.
- wiersz 66: sprawdzamy, czy date6 jest prawidłową datą
- wiersz 68: jeśli date6 jest <18.05.2000 lub date6>20.06.2001, to date6 jest nieprawidłowe.
14.9. Ostatnie szczegóły
Poprzedni formularz zawiera błędy, co ilustruje poniższy przykład:
![]() |
- w [1] wprowadzono nieprawidłową datę
- w [2] została ona zaakceptowana. Uwzględniono jedynie pierwsze znaki dd/mm/rrrr.
Pola od date2 do date5, które są powiązane z szablonami typu Date, mają ten sam problem. Być może ponownie przeoczyłem coś w dokumentacji. Pola date1 i date6, powiązane z szablonami typu String, nie mają tego problemu.



