13. Exemplo 10 – Conversão e validação de números reais
A nova aplicação permite a introdução de números reais:
![]() |
- em [1], o formulário de entrada
- em [2], a resposta devolvida
A aplicação funciona de forma semelhante à entrada de números inteiros, pelo que apenas comentaremos os pontos que diferem.
13.1. O projeto NetBeans
O projeto NetBeans é o seguinte:
![]() |
- em [1], as vistas da aplicação
- [Accueil.jsp]: a página inicial
- [FormDouble.jsp]: o formulário de entrada
- [ConfirmationDouble.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]:
- [FormDouble.java]: a ação que apresenta e processa o formulário
- [FormDouble-validation.xml]: as regras de validação para a ação [FormDouble]. Este ficheiro delega estas validações ao modelo utilizando o método que acabámos de discutir.
- [FormDoubleModel]: o modelo para a ação [FormDouble]
- [FormDoubleModel-validation.xml]: as regras de validação do modelo
- [FormDoubleModel.properties]: o ficheiro de mensagens do modelo
- [example.xml]: ficheiro de configuração secundário do Struts
13.2. 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="FormDouble" class="example.FormDouble">
<result name="input">/example/FormDouble.jsp</result>
<result name="cancel" type="redirect">/example/Accueil.jsp</result>
<result name="success">/example/ConfirmationFormDouble.jsp</result>
</action>
</package>
</struts>
É semelhante ao discutido para a introdução de números inteiros.
13.3. Ficheiros de mensagens
O ficheiro [messages.properties] é o seguinte:
Accueil.titre=Accueil
Accueil.message=Struts 2 - Conversions et validations
Accueil.FormDouble=Saisie de nombres r\u00e9els
Form.titre=Conversions et validations
FormDouble.message=Struts 2 - Conversion et validation de nombres r\u00e9els
FormDouble.conseil=Tapez les nombres r\u00e9els avec une virgule comme 10,7
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 [FormDoubleModel.properties] é o seguinte:
double.format={0,number}
double1.prompt=1-Nombre r\u00E9el
double1.error=Tapez un nombre r\u00E9el
double2.prompt=2-Nombre r\u00E9el
double2.error=Tapez un nombre r\u00E9el
double3.prompt=3-Nombre r\u00E9el >=2.64
double3.error=Tapez un nombre r\u00E9el >=2.64
double4.prompt=4-Nombre r\u00E9el <8.32
double4.error=Tapez un nombre r\u00E9el <8.32
double5.prompt=5-Nombre r\u00E9el dans l''intervalle [2.64,8.32[
double5.error=Tapez un nombre r\u00E9el dans l''intervalle [2.64,8.32[
double6.prompt=6-Nombre r\u00E9el dans l''intervalle [2.64,8.32]
double6.error=Tapez un nombre r\u00E9el dans l''intervalle [2.64,8.32]
A linha 1 desempenha um papel importante. Voltaremos a ela mais tarde.
13.4. O formulário de entrada
A vista [FormDouble.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="FormDouble.message"/></h2>
<h4><s:text name="FormDouble.conseil"/></h4>
<s:form name="formulaire" action="FormDouble">
<s:textfield name="double1" key="double1.prompt"/>
<s:textfield name="double2" key="double2.prompt" value="%{#parameters['double2']!=null ? #parameters['double2'] : double2==null ? '' :getText('double.format',{double2})}"/>
<s:textfield name="double3" key="double3.prompt" value="%{#parameters['double3']!=null ? #parameters['double3'] : double3==null ? '' :getText('double.format',{double3})}"/>
<s:textfield name="double4" key="double4.prompt" value="%{#parameters['double4']!=null ? #parameters['double4'] : double4==null ? '' :getText('double.format',{double4})}"/>
<s:textfield name="double5" key="double5.prompt" value="%{#parameters['double5']!=null ? #parameters['double5'] : double5==null ? '' :getText('double.format',{double5})}"/>
<s:textfield name="double6" key="double6.prompt"/>
<s:submit key="Form.submitText" method="execute"/>
</s:form>
<br/>
<s:url id="url" action="FormDouble" method="cancel"/>
<s:a href="%{url}"><s:text name="Form.cancelText"/></s:a>
<br/>
<s:url id="url" action="FormDouble" 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 entrada para números reais. Os campos double2 a double5 possuem um atributo de valor complexo. Normalmente, os seis campos de entrada devem ser os seguintes:
<s:textfield name="double1" key="double1.prompt"/>
<s:textfield name="double2" key="double2.prompt"/>
<s:textfield name="double3" key="double3.prompt"/>
<s:textfield name="double4" key="double4.prompt"/>
<s:textfield name="double5" key="double5.prompt"/>
<s:textfield name="double6" key="double6.prompt"/>
Para resolver certos problemas encontrados durante os testes, tivemos de tornar as coisas mais complexas. Por enquanto, o leitor pode ignorar esta complexidade. Explicaremos isso mais tarde.
13.5. A vista de confirmação
A vista de confirmação [ConfirmationFormDouble.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="double1.prompt"/></td>
<td><s:text name="double1"/></td>
</tr>
<tr>
<td><s:text name="double2.prompt"/></td>
<td>
<s:text name="double.format">
<s:param value="double2"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="double3.prompt"/></td>
<td>
<s:text name="double.format">
<s:param value="double3"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="double4.prompt"/></td>
<td>
<s:text name="double.format">
<s:param value="double4"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="double5.prompt"/></td>
<td>
<s:text name="double.format">
<s:param value="double5"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="double6.prompt"/></td>
<td><s:text name="double6"/></td>
</tr>
</table>
<br/>
<s:url id="url" action="FormDouble!input"/>
<s:a href="%{url}"><s:text name="Confirmation.lien"/></s:a>
</body>
</html>
A página [ConfirmationFormDouble.jsp] simplesmente apresenta o [FormDoubleModel]. As linhas 47–49 mostram como um número real é apresentado. Queremos que esta exibição tenha em conta a localização da aplicação. Dependendo da localização, um número real não será exibido da mesma forma em França (10,7) como no Reino Unido (10.7). Para o conseguir, utilizamos a tag <s:text>, que já tínhamos utilizado anteriormente para a internacionalização da aplicação. Esta tag é, portanto, também utilizada para a localização.
Aqui, a chave de mensagem utilizada é double.format. Esta chave encontra-se no ficheiro [FormDoubleModel.properties]:
double.format={0,number}
O valor associado à chave não é uma mensagem, mas sim um formato de exibição:
- 0 é um parâmetro que representa o valor a ser exibido. Aqui, será o campo double5 do modelo.
- number representa o formato numérico. Será adaptado a cada país. Sem este formato, o número 10,7 será sempre apresentado como 10,7, independentemente do país.
A tag
<s:text name="double.format">
<s:param value="double5"/>
</s:text>
Exibe a mensagem {0, número}, em que o parâmetro double5 (linha 2) substitui o parâmetro 0 no formato. Assim, o valor double5 será exibido no formato numérico localizado.
13.6. O modelo [FormDoubleModel]
Os campos double1 a double6 do formulário [FormDouble.jsp] são inseridos no seguinte modelo [FormDoubleModel]:
package example;
public class FormDoubleModel {
// constructor without parameters
public FormDoubleModel() {
}
// fields
private String double1;
private Double double2;
private Double double3;
private Double double4;
private Double double5;
private String double6;
// raz model
public void clearModel() {
double1 = null;
double2 = null;
double3 = null;
double4 = null;
double5 = null;
double6 = null;
}
// getters and setters
...
}
- Os campos double1 e double6 são do tipo String
- os outros campos são do tipo Double
13.7. Validação do modelo
A validação do modelo é controlada por dois ficheiros: [FormDouble-validation.xml] e [FormDoubleModel-validation.xml].
O ficheiro [FormDouble-validation.xml] delega as validações ao ficheiro [FormDoubleModel-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>
Já encontrámos este ficheiro.
O ficheiro [FormDoubleModel-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/exemple-10/example/xwork-validator-1.0.2.dtd">
<validators>
<field name="double1" >
<field-validator type="requiredstring" short-circuit="true">
<message key="double1.error"/>
</field-validator>
<field-validator type="regex" short-circuit="true">
<param name="expression">^[+|-]*\s*\d+(,\d+)*$</param>
<param name="trim">true</param>
<message key="double1.error"/>
</field-validator>
</field>
<field name="double2" >
<field-validator type="required" short-circuit="true">
<message key="double2.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="double2.error"/>
</field-validator>
</field>
<field name="double3" >
<field-validator type="required" short-circuit="true">
<message key="double3.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="double3.error"/>
</field-validator>
<field-validator type="double" short-circuit="true">
<param name="minInclusive">2.64</param>
<message key="double3.error"/>
</field-validator>
</field>
<field name="double4" >
<field-validator type="required" short-circuit="true">
<message key="double4.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="double4.error"/>
</field-validator>
<field-validator type="double" short-circuit="true">
<param name="maxExclusive">8.32</param>
<message key="double4.error"/>
</field-validator>
</field>
<field name="double5" >
<field-validator type="required" short-circuit="true">
<message key="double5.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="double5.error"/>
</field-validator>
<field-validator type="double" short-circuit="true">
<param name="minInclusive">2.64</param>
<param name="maxExclusive">8.32</param>
<message key="double5.error"/>
</field-validator>
</field>
<field name="double6" >
<field-validator type="requiredstring" short-circuit="true">
<message key="double6.error"/>
</field-validator>
<field-validator type="regex" short-circuit="true">
<param name="expression">^[+|-]*\s*\d+(,\d+)*$</param>
<param name="trim">true</param>
<message key="double6.error"/>
</field-validator>
</field>
</validators>
- A regra nas linhas 12–21 verifica se o campo de entrada double1 corresponde a um padrão de expressão regular que representa um número real.
- A regra nas linhas 23–30 verifica se o campo de entrada double2 pode ser convertido num número real de precisão dupla.
- A regra nas linhas 32–43 verifica se o campo de entrada `double3` pode ser convertido num número real de dupla precisão maior ou igual a 2,64. Note-se que deve ser utilizada a notação anglo-saxónica para números reais.
- A regra nas linhas 45–56 verifica se o campo de entrada double4 pode ser convertido num número real de dupla precisão < 8,32.
- A regra nas linhas 58–70 verifica se o campo de entrada `double5` pode ser convertido num número real de dupla precisão no intervalo [2,64, 8,32[.
- A regra nas linhas 72–80 verifica se o campo double6 segue o padrão de uma expressão regular que representa um número real.
Assim que o ficheiro [FormDoubleModel-validation.xml] for processado pelo interceptor de validação, este executa o método validate da ação [FormDouble], caso exista. Apresentá-lo-emos juntamente com toda a ação.
13.8. A ação [FormDouble]
A ação [FormDouble] é a seguinte:
package example;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.interceptor.validation.SkipValidation;
public class FormDouble extends ActionSupport implements ModelDriven, SessionAware {
// constructor without parameters
public FormDouble() {
}
// action model
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormDoubleModel());
}
return session.get("model");
}
@SkipValidation
public String clearModel() {
// close to the model
((FormDoubleModel) getModel()).clearModel();
// result
return INPUT;
}
public String cancel() {
// cleaning the model
((FormDoubleModel) 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() {
// valid double6 entry?
if (getFieldErrors().get("double6") == null) {
// replace the comma with a period in the double6 string
String strDouble6 = (((FormDoubleModel) getModel()).getDouble6()).replace(',', '.');
// String --> double
double double6 = Double.parseDouble(strDouble6);
// check
if (double6 < 2.64 || double6 > 8.32) {
addFieldError("double6", getText("double6.error"));
}
}
}
}
A ação [FormDouble] é construída com base no mesmo modelo que a ação [FormInt]. Iremos apenas comentar o método validate. Note-se que o método validate é executado após o ficheiro de validação [FormDoubleModel-validation.xml] ser processado e antes de o método execute ser chamado.
- Linha 48: Se já existirem erros no campo double6, não é tomada nenhuma outra medida.
- linha 50: recebemos uma cadeia de caracteres no formato 45.67. Esta foi armazenada no campo double6 do modelo. Substituímos a vírgula por um ponto para obter 45.67.
- linha 52: a cadeia de caracteres 45.67 é convertida num número de ponto flutuante de dupla precisão. Isto deve funcionar, uma vez que a cadeia de caracteres no campo double6 segue o formato de um número real.
- Linha 54: Verificamos se o número duplo resultante está no intervalo [2,64; 8,32].
- Linha 55: Se não for esse o caso, a mensagem de erro da chave double6.error é anexada ao campo double6. Esta mensagem pode ser encontrada no ficheiro [FormDoubleModel.properties]. Será apresentada quando o formulário com o erro for novamente apresentado.
13.9. Detalhes finais
Agora, voltemos à complexidade dos campos de entrada no formulário [FormDouble.jsp]. Vamos concentrar-nos no campo double2. O raciocínio aplica-se aos campos double3 a double5, que têm um modelo Double. Para os campos double1 e double6, que têm um modelo String, não há qualquer problema.
O campo de entrada double2 é o seguinte:
<s:textfield name="double2" key="double2.prompt" value="%{#parameters['double2']!=null ? #parameters['double2'] : double2==null ? '' :getText('double.format',{double2})}"/>
Vamos começar com a tag mais simples:
<s:textfield name="double2" key="double2.prompt"/>
e vamos ver o que acontece:
![]() |
- Em [1], validamos o número double2 correto. Não é muito claro na captura de ecrã, mas introduzimos o número com uma vírgula decimal.
- Em [2], a vista de confirmação. A entrada double2 passou nas verificações de validação. O número é apresentado com uma vírgula decimal.
![]() |
- Em [3], voltamos ao formulário
- em [4], o formulário. O que a captura de ecrã não mostra claramente é que o número double2, que inicialmente era 4,32, passou a ser 4,32 com uma vírgula decimal.
![]() |
- Em [5], reenviamos o formulário sem alterar nada
- Em [6], é reportado um erro no campo double2.
O problema é o seguinte:
- Inicialmente, em [1], a cadeia de caracteres de entrada «4,32» foi convertida com sucesso para o número de ponto flutuante 4,32. Isto significa que a conversão de String para Double foi bem-sucedida e que, neste contexto, o Struts tem em conta a localização — neste caso, a França.
- O novo formulário [4] apresenta o número real 4,32 no campo double2. Uma vez que a apresentação não foi localizada, assume por predefinição o formato anglo-saxónico, ou seja, com um ponto decimal. Assim, na direção Double --> String, o Struts já não tem em conta a localização; caso contrário, teria apresentado 4,32 com uma vírgula.
Isto é, no mínimo, inconsistente. Mas não importa, vamos localizar a exibição do número 4,32. A tag de entrada passa a ser a seguinte:
<s:textfield name="double2" key="double2.prompt" value="%{getText('double.format',{double2})}"/>
O atributo value especifica o valor a ser exibido no campo double2. Trata-se de uma expressão OGNL. Recorde a definição da chave double.format no ficheiro [FormDoubleModel.properties]:
double.format={0,number}
O método getText('key') recupera a mensagem associada a uma chave. Esta mensagem é procurada no ficheiro correspondente à localização atual. Assim, se a localização fosse es (Espanha), a chave double.format teria sido procurada no ficheiro [FormDoubleModel_es.properties].
O método getText('key', {param0, param1, ...}) recupera uma mensagem parametrizada. A mensagem
é uma mensagem parametrizada pelo parâmetro 0. Trata-se de um parâmetro posicional. O método getText('double.format', {double2}) atribuirá o número double2 ao parâmetro 0. Por fim, solicitamos o valor de double2 no formato numérico localizado. Em França, o número 4,56 será localizado como a cadeia de caracteres «4,56».
Após esta alteração, executamos os testes novamente.
![]() |
Assim que o formulário é exibido pela primeira vez, verifica-se uma anomalia em [1]. Voltemos à tag:
<s:textfield name="double2" key="double2.prompt" value="%{getText('double.format',{double2})}"/>
Na exibição inicial, o valor do modelo double2 é nulo, um valor não numérico. Modificamos a tag da seguinte forma:
<s:textfield name="double2" key="double2.prompt" value="%{double2==null ? '' : getText('double.format',{double2})}"/>
Desta vez, verificamos se double2==null. Se for o caso, exibimos uma string vazia.
Com esta alteração feita, retomamos os testes:
![]() |
![]() |
As imagens [1] a [4] mostram que o problema que estávamos a tentar resolver foi resolvido:
- em [1], introduzimos 4,67
- em [2], este número foi aceite
- em [3], é exibido novamente como 4,67 com a vírgula decimal, o que é confirmado por [4].
Infelizmente, os problemas não terminam aqui. Vejamos a seguinte sequência:
![]() |
- em [5], é adicionado um caractere para invalidar double2
- Em [6], as verificações de validação cumpriram a sua função e o erro é reportado. No entanto, a cadeia de caracteres apresentada em [6] não é a cadeia de caracteres errada, mas sim o valor atual do modelo double2. O valor introduzido foi perdido.
Ao passar de [5] para [6], o pedido não é concluído. É interrompido pelo interceptor de validação. Em [6], apresentámos o valor do modelo double2, que não recebeu um novo valor devido a esta interrupção. Por conseguinte, é exibido o seu valor anterior, quando deveria ter sido exibida a string introduzida. Os parâmetros de um pedido são acessíveis através da notação #parameters['param']. Atualizamos o campo de entrada double2 da seguinte forma:
<s:textfield name="double2" key="double2.prompt" value="%{#parameters['double2']!=null ? #parameters['double2'] : double2==null ? '' : getText('double.format',{double2})}"/>
O valor exibido do campo double2 é calculado da seguinte forma: se o parâmetro «double2» existir, é exibido; caso contrário, é exibido o modelo double2. O leitor é convidado a testar se esta nova versão da tag resolve os problemas encontrados.
13.10. Conclusão
É surpreendente que a introdução de números reais com verificações de validade seja tão complicada... Será que me escapou alguma coisa na documentação?








