Hi, we actually made it the hard way and currently we have our own BigDecimalFieldTranslatorImpl, which takes care of the actual validation, as seen here:
public class BigDecimalFieldTranslatorImpl extends FieldTranslatorImpl<BigDecimal> { private final Locale locale; private final int numberOfDecimals; private final Field field; private final MessageFormatter messageFormatter; private final Messages messages; private final boolean useGrouping; public BigDecimalFieldTranslatorImpl(int numberOfDecimals, boolean useGrouping, Locale locale, Field field, Translator<BigDecimal> translator, MessageFormatter formatter, FormSupport formSupport, Messages messages) { super(field, translator, formatter, formSupport); this.locale = locale; this.numberOfDecimals = numberOfDecimals; this.field = field; this.messageFormatter = formatter; this.messages = messages; this.useGrouping = useGrouping; } @Override public String toClient(BigDecimal value) { if(useGrouping){ return NumberFormatUtility.formatWithGrouping(value, numberOfDecimals, locale); } return NumberFormatUtility.format(value, numberOfDecimals, locale); } @Override public BigDecimal parse(String input) throws ValidationException { if(input != null) { input = input.replaceAll("\\s", ""); } BigDecimal result = super.parse(input); if(NumberFormatUtility.hasTooManyDecimals(input, numberOfDecimals, locale)) { throw new ValidationException(formatMessage()); } return result; } private String formatMessage() { if(numberOfDecimals == 0) { return messages.format("integer-format-exception", field.getLabel()); } else { return messages.format("fi.cerion.common.too-many-decimal-places", field.getLabel(), numberOfDecimals); } } } However, to supply per field validation data to the validator we opted to make our own service that is used to get the field translator: public class CerionFieldTranslatorSourceImpl implements CerionFieldTranslatorSource { private final PersistentLocale persistentLocale; private final TranslatorSource translatorSource; private final FieldTranslatorSource fieldTranslatorSource; private final ValidationMessagesSource validationMessagesSource; private final FormSupport formSupport; public CerionFieldTranslatorSourceImpl(PersistentLocale persistentLocale, TranslatorSource translatorSource, FieldTranslatorSource fieldTranslatorSource, ValidationMessagesSource validationMessagesSource, FormSupport formSupport) { this.persistentLocale = persistentLocale; this.translatorSource = translatorSource; this.fieldTranslatorSource = fieldTranslatorSource; this.validationMessagesSource = validationMessagesSource; this.formSupport = formSupport; } @Override public FieldTranslator<BigDecimal> createBigDecimalTranslator(Field field, int numberOfDecimals) { ComponentResources resources = ((ComponentResourcesAware)field).getComponentResources(); Translator translator = translatorSource.getByType(BigDecimal.class); return createTranslator(field, resources.getId(), resources.getContainerMessages(), resources.getLocale(), translator, numberOfDecimals, false); } @Override public FieldTranslator<BigDecimal> createBigDecimalTranslator(Field field, int numberOfDecimals, boolean useGrouping) { ComponentResources resources = ((ComponentResourcesAware)field).getComponentResources(); Translator translator = translatorSource.getByType(BigDecimal.class); return createTranslator(field, resources.getId(), resources.getContainerMessages(), resources.getLocale(), translator, numberOfDecimals, useGrouping); } private FieldTranslator createTranslator(Field field, String overrideId, Messages overrideMessages, Locale locale, Translator translator, int numberOfDecimals, boolean useGrouping) { MessageFormatter formatter = findFormatter(overrideId, overrideMessages, locale, translator); return new BigDecimalFieldTranslatorImpl(numberOfDecimals, useGrouping, locale, field, translator, formatter, formSupport, overrideMessages); } private MessageFormatter findFormatter(String overrideId, Messages overrideMessages, Locale locale, Translator translator) { // TAP5-228: Try to distinguish message overrides by form id and overrideId (i.e., property name) first. String translatorName = translator.getName(); String overrideKey = formSupport.getFormValidationId() + "-" + overrideId + "-" + translatorName + "-message"; if (overrideMessages.contains(overrideKey)) return overrideMessages.getFormatter(overrideKey); // Ok, look for a simpler name that omits the formId prefix. overrideKey = overrideId + "-" + translatorName + "-message"; if (overrideMessages.contains(overrideKey)) return overrideMessages.getFormatter(overrideKey); // Otherwise, use the built-in validation message appropriate to this validator. Messages validationMessages = validationMessagesSource.getValidationMessages(locale); return validationMessages.getFormatter(translator.getMessageKey()); } } Then we just use it to get the desired translator. Our system (form templating engine) is constructed so that every field requiring this is added in a single location, so this code is good enough for our use case. Anyway, I really would like to see your solutions: How to supply arbitrary per field validation data to custom validators when you need to also use the per field validation data to construct the validation error message? One example of this is how to tell the user in error message how many characters is the max limit in the field and how many characters she/he has typed. E.g. "Product description is 3072 characters long but the maximum length is 3000 characters. Please edit your text accordingly." - Ville Bryan Lewis-5 wrote: > > Thanks, that's what I was missing!! A translator can be supplied as a > page > property for per-field use! > One doesn't need to contribute a custom translator in AppModule unless one > wants it to apply universally to a custom type and wants to avoid > repeatedly > specifying the "translate". > > It would be good to clarify this in the docs. I don't see it described > anywhere; I see only examples of the contributed-custom-type case. (And > it's different from the way Tapestry 3 and 4 did it.) > > > Details: In the tml: > > <t:textfield t:id="annualSales" value="company.annualSales" > translate="prop:currency"/> > > In the java (actually in my Page super-class): > > public FieldTranslator<BigDecimal> getCurrency() > { > return new CurrencyTranslator(); > } > > Where CurrencyTranslator implements FieldTranslator<BigDecimal>. > > > > On Sun, Dec 27, 2009 at 3:37 PM, Thiago H. de Paula Figueiredo < > thiag...@gmail.com> wrote: > >> Em Sun, 27 Dec 2009 17:58:07 -0200, Bryan Lewis <jbryanle...@gmail.com> >> escreveu: >> >> Really? I wish that was my experience. Here's what I'm seeing; maybe >> you >>> can point out what I've done wrong. >>> <t:textfield t:id="annualSales" value="company.annualSales" >>> t:translate="bigdecimal"/> >>> >> >> Have you tried t:translate="prop:salesTranslator" and a Translator >> getSalesTranslator() in your class? >> >> TapestryModule already creates a translator for BigDecimal, so I guess >> yours is ignored. I agree that the source of translators should be >> improved. >> >> >> -- >> Thiago H. de Paula Figueiredo >> Independent Java, Apache Tapestry 5 and Hibernate consultant, developer, >> and instructor >> Owner, software architect and developer, Ars Machina Tecnologia da >> Informação Ltda. >> http://www.arsmachina.com.br >> >> --------------------------------------------------------------------- >> To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org >> For additional commands, e-mail: users-h...@tapestry.apache.org >> >> > > -- View this message in context: http://old.nabble.com/Number-Formatting-in-TextField-%28T5%29-tp26398708p27009963.html Sent from the Tapestry - User mailing list archive at Nabble.com. --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org For additional commands, e-mail: users-h...@tapestry.apache.org