3

I have a monetary amount that is entered by a user and want to validate it. I have written a JSF validator but am having trouble getting this to work in all circumstances. Here is my scenario:
I have users in different Locales and therefore I need to cope with the various methods of input and want to allow the following

English  
1234  
1,234  
1234.56  
1,234.5  

German & Spanish  
1234  
1.234  
1234,56  
1.234,5

French  
1234  
1 234  
1234,56  
1 234,5  

My problem is with French as options 2 & 4 are seen as invalid using this code as the parsing stops at the space.

public void validate(final FacesContext pContext,
                     final UIComponent pComponent,
                     final Object pValue) {

  boolean isValid = true;
  final Locale locale = (Locale)pComponent.getAttributes().get(USERS_LOCALE);
  final Currency currency = (Currency)pComponent.getAttributes().get(CURRENCY);

  final NumberFormat formatter = NumberFormat.getNumberInstance(locale);
  formatter.setGroupingUsed(true);
  formatter.setMinimumFractionDigits(currency.getDefaultFractionDigits());
  formatter.setMaximumFractionDigits(currency.getDefaultFractionDigits());
  final ParsePosition pos = new ParsePosition(0);
  final String stringValue = (String)pValue;

  if (pos.getIndex() != stringValue.length() || pos.getErrorIndex() != -1) {
    isValid = false;
  }
  ...

I also want to ensure that the following are treated as invalid but they all parse successfully (except for French of course)

1,234,9.56 (Invalid grouping)
1,234.567 (too many decimal places for the currency)

Any help will be much appreciated
Ian

Sam DeHaan
  • 10,246
  • 2
  • 40
  • 48
bluesky
  • 33
  • 2
  • 4

1 Answers1

6

The French thousands' separator is actually a non-breaking space, \u00a0. If your input uses a regular space you can change the input:

input = input.replace(' ', '\u00a0');

Another thing you can do is change the grouping symbol to a regular space:

DecimalFormat decimalFormatter = (DecimalFormat) formatter;
DecimalFormatSymbols symbols = decimalFormatter.getDecimalFormatSymbols();
symbols.setGroupingSeparator(' ');
decimalFormatter.setDecimalFormatSymbols(symbols);

Can't recommend this, though. The new formatter won't accept numbers that use a non-breaking space as the grouping character.

Joni
  • 108,737
  • 14
  • 143
  • 193
  • Thanks, learned something new. Some more discussion about this can be found on oracle's bug tracker: http://bugs.sun.com/view_bug.do?bug_id=4510618 – Jörn Horstmann Mar 08 '12 at 18:23
  • Thanks for the responses, the first did work although rather than hard code I used stringValue.replace(' ', symbols.getGroupingSeparator()) – bluesky Mar 09 '12 at 10:03
  • Anyone help with the second part regarding invalid grouping and decimal places? (should probably have made this a separate question!) – bluesky Mar 09 '12 at 10:06
  • For the second part you probably need regular expression, NumberFormat doesn't enforce the format when parsing. E.g. for amounts > 1, two decimal places required, `[1-9]\d{0,2}(?:,\d{3})*\.\d{2}` should work. – Joni Mar 09 '12 at 11:48