Uploaded image for project: 'Commons Validator'
  1. Commons Validator
  2. VALIDATOR-433

BigDecimalValidator does not validate "()" negative currency uniformly for all locales

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Critical
    • Resolution: Not A Bug
    • Affects Version/s: 1.6
    • Fix Version/s: None
    • Component/s: Routines
    • Labels:
      None
    • Environment:

      Windows 10, Tomcat 6, Java 8

      Description

      Hi,

      The following tests fail:

      final String JAPAN_YEN = (new DecimalFormatSymbols(Locale.JAPAN)).getCurrencySymbol();
      final String CHINA_YEN = (new DecimalFormatSymbols(Locale.CHINA)).getCurrencySymbol();

      BigDecimal negative = new BigDecimal("-1234.56");

      assertEquals("Japan negative", negative, validator.validate("(" + JAPAN_YEN + "1,234.56)", Locale.CHINA));
      assertEquals("China negative", negative, validator.validate("(" + CHINA_YEN + "1,234.56)", Locale.CHINA));

      Please not that this test do not fail for other locales (US AND UK, for example).

      Please advise on whether this is an issue, if there is a workaround, etc...

      Regards,
      John DiPirro

      1. CurrencyValidatorTest.java
        8 kB
        John DiPirro
      2. junitResults.jpg
        85 kB
        John DiPirro
      3. screenshot-1.png
        33 kB
        John DiPirro

        Activity

        Hide
        jjdipirro John DiPirro added a comment -

        Issue addressed, custom formatting solution will be needed to compensate for non-uniformity of how JVM interprets "()' negative prefix/suffix.

        Show
        jjdipirro John DiPirro added a comment - Issue addressed, custom formatting solution will be needed to compensate for non-uniformity of how JVM interprets "()' negative prefix/suffix.
        Hide
        kinow Bruno P. Kinoshita added a comment -

        So, accordingly, your test would have to be:

        assertEquals("US negative", negative, validator.validate("(" + US_DOLLAR + "1,234.56)", Locale.US));
        assertEquals("UK negative", negative, validator.validate("-" + UK_POUND + "1,234.56", Locale.UK));
        assertEquals("China negative", negative, validator.validate("-" + CHINA_RMB + "1,234.56", Locale.CHINA));
        assertEquals("Japan negative", negative2, validator.validate("-" + JAPAN_YEN + "1,234", Locale.JAPAN));
        

        To pass, and it would be following what the Locale defines as the negative prefix and the negative suffix. You can either change the logic of your application to take into consideration how local handles these prefix/suffix pairs, or try using a more generic like ¤#,###.##;-¤#,###.##.

        Hope that helps,
        Bruno

        Show
        kinow Bruno P. Kinoshita added a comment - So, accordingly, your test would have to be: assertEquals( "US negative" , negative, validator.validate( "(" + US_DOLLAR + "1,234.56)" , Locale.US)); assertEquals( "UK negative" , negative, validator.validate( "-" + UK_POUND + "1,234.56" , Locale.UK)); assertEquals( "China negative" , negative, validator.validate( "-" + CHINA_RMB + "1,234.56" , Locale.CHINA)); assertEquals( "Japan negative" , negative2, validator.validate( "-" + JAPAN_YEN + "1,234" , Locale.JAPAN)); To pass, and it would be following what the Locale defines as the negative prefix and the negative suffix. You can either change the logic of your application to take into consideration how local handles these prefix/suffix pairs, or try using a more generic like ¤#,###.##;-¤#,###.##. Hope that helps, Bruno
        Hide
        kinow Bruno P. Kinoshita added a comment -

        Here's the output:

        -¤ ---	
        د.إ.‏ ----	Arabic (United Arab Emirates)
        د.أ.‏ ----	Arabic (Jordan)
        ل.س.‏ ----	Arabic (Syria)
        -Kn ---	Croatian (Croatia)
        ---- €	French (Belgium)
        (B---)	Spanish (Panama)
        -€---	Maltese (Malta)
        Bs.F. ----	Spanish (Venezuela)
        -¤ ---	Bulgarian
        -NT$---	Chinese (Taiwan)
        -¤ ---	Italian
        -¤ ---	Korean
        -¤ ---	Ukrainian
        -¤ ---	Latvian
        kr ----	Danish (Denmark)
        ($---)	Spanish (Puerto Rico)
        ---- đ	Vietnamese (Vietnam)
        ($---)	English (United States)
        -€ ---	Serbian (Montenegro)
        ---- kr	Swedish (Sweden)
        (B$---)	Spanish (Bolivia)
        -$---	English (Singapore)
        د.ب.‏ ----	Arabic (Bahrain)
        -¤ ---	Portuguese
        ر.س.‏ ----	Arabic (Saudi Arabia)
        -¤ ---	Slovak
        ر.ي.‏ ----	Arabic (Yemen)
        -रू ---	Hindi (India)
        -¤ ---	Irish
        -€---	English (Malta)
        ---- €	Finnish (Finland)
        -¤ ---	Estonian
        -¤ ---	Swedish
        -¤ ---	Czech
        -KM ---	Serbian (Latin,Bosnia and Herzegovina)
        -¤ ---	Greek
        ---- грн.	Ukrainian (Ukraine)
        -¤ ---	Hungarian
        SFr.----	French (Switzerland)
        -¤---	Indonesian
        $----	Spanish (Argentina)
        ج.م.‏ ----	Arabic (Egypt)
        -¥---	Japanese (Japan,JP)
        (C---)	Spanish (El Salvador)
        -R$ ---	Portuguese (Brazil)
        -¤ ---	Belarusian
        ---- kr.	Icelandic (Iceland)
        ---- Kč	Czech (Czech Republic)
        (¤---)	Spanish
        ---- zł	Polish (Poland)
        ---- ¤	Turkish
        -€ ---	Catalan (Spain)
        -CSD ---	Serbian (Serbia and Montenegro)
        (RM---)	Malay (Malaysia)
        -¤ ---	Croatian
        -¤ ---	Lithuanian
        ---- €	Spanish (Spain)
        ($---)	Spanish (Colombia)
        -лв.---	Bulgarian (Bulgaria)
        -¤ ---	Albanian
        ---- ¤	French
        -¤ ---	Japanese
        -КМ. ---	Serbian (Bosnia and Herzegovina)
        -¤ ---	Icelandic
        (G---)	Spanish (Paraguay)
        -¤ ---	German
        $----	Spanish (Ecuador)
        (US$---)	Spanish (United States)
        ج.س.‏ ----	Arabic (Sudan)
        -¤---	English
        ---- LEI	Romanian (Romania)
        (Php---)	English (Philippines)
        -¤ ---	Catalan
        د.ت.‏ ----	Arabic (Tunisia)
        -€ ---	Serbian (Latin,Montenegro)
        (Q---)	Spanish (Guatemala)
        -¤ ---	Slovenian
        -₩---	Korean (South Korea)
        -€---	Greek (Cyprus)
        -$---	Spanish (Mexico)
        ---- руб.	Russian (Russia)
        (L---)	Spanish (Honduras)
        (HK$---)	Chinese (Hong Kong)
        kr ----	Norwegian (Norway,Nynorsk)
        ---- Ft	Hungarian (Hungary)
        ฿----	Thai (Thailand)
        د.ع.‏ ----	Arabic (Iraq)
        Ch$----	Spanish (Chile)
        -¤ ---	Finnish
        د.م.‏ ----	Arabic (Morocco)
        -€---	Irish (Ireland)
        -¤ ---	Macedonian
        ---- TL	Turkish (Turkey)
        ---- €	Estonian (Estonia)
        ر.ق.‏ ----	Arabic (Qatar)
        -¤ ---	Serbian (Latin)
        ---- €	Portuguese (Portugal)
        ---- €	French (Luxembourg)
        ر.ع.‏ ----	Arabic (Oman)
        -¤ ---	Thai
        -Lek---	Albanian (Albania)
        (RD$---)	Spanish (Dominican Republic)
        (CU$---)	Spanish (Cuba)
        ¤ ----	Arabic
        -¤ ---	Russian
        -$---	English (New Zealand)
        -дин. ---	Serbian (Serbia)
        SFr.----	German (Switzerland)
        (NU$---)	Spanish (Uruguay)
        -¤ ---	Malay
        ---- €	Greek (Greece)
        ---- ש"ח	Hebrew (Israel)
        R----	English (South Africa)
        ฿----	Thai (Thailand,TH)
        -¤ ---	Hindi
        ---- €	French (France)
        -€ ---	German (Austria)
        -¤ ---	Dutch
        kr ----	Norwegian (Norway)
        -$---	English (Australia)
        -¤ ---	Vietnamese
        € ----	Dutch (Netherlands)
        (---$)	French (Canada)
        ---- €	Latvian (Latvia)
        ---- €	German (Luxembourg)
        (C---)	Spanish (Costa Rica)
        د.ك.‏ ----	Arabic (Kuwait)
        -¤ ---	Serbian
        د.ل.‏ ----	Arabic (Libya)
        -¤ ---	Maltese
        SFr.----	Italian (Switzerland)
        -¤ ---	Danish
        ---- €	German (Germany)
        د.ج.‏ ----	Arabic (Algeria)
        ---- €	Slovak (Slovakia)
        ---- €	Lithuanian (Lithuania)
        -€ ---	Italian (Italy)
        -€---	English (Ireland)
        -S$---	Chinese (Singapore)
        -¤ ---	Romanian
        -$---	English (Canada)
        ---- €	Dutch (Belgium)
        -¤ ---	Norwegian
        -¤ ---	Polish
        -¥---	Chinese (China)
        -¥---	Japanese (Japan)
        -€ ---	German (Greece)
        -din. ---	Serbian (Latin,Serbia)
        -¤ ---	Hebrew
        -Rs.---	English (India)
        ل.ل.‏ ----	Arabic (Lebanon)
        ($C---)	Spanish (Nicaragua)
        -¤ ---	Chinese
        -Den ---	Macedonian (Macedonia)
        -Руб---	Belarusian (Belarus)
        -€ ---	Slovenian (Slovenia)
        S/.----	Spanish (Peru)
        -Rp---	Indonesian (Indonesia)
        -£---	English (United Kingdom)
        
        
        Show
        kinow Bruno P. Kinoshita added a comment - Here's the output: -¤ --- د.إ.‏ ---- Arabic (United Arab Emirates) د.أ.‏ ---- Arabic (Jordan) ل.س.‏ ---- Arabic (Syria) -Kn --- Croatian (Croatia) ---- € French (Belgium) (B---) Spanish (Panama) -€--- Maltese (Malta) Bs.F. ---- Spanish (Venezuela) -¤ --- Bulgarian -NT$--- Chinese (Taiwan) -¤ --- Italian -¤ --- Korean -¤ --- Ukrainian -¤ --- Latvian kr ---- Danish (Denmark) ($---) Spanish (Puerto Rico) ---- đ Vietnamese (Vietnam) ($---) English (United States) -€ --- Serbian (Montenegro) ---- kr Swedish (Sweden) (B$---) Spanish (Bolivia) -$--- English (Singapore) د.ب.‏ ---- Arabic (Bahrain) -¤ --- Portuguese ر.س.‏ ---- Arabic (Saudi Arabia) -¤ --- Slovak ر.ي.‏ ---- Arabic (Yemen) -रू --- Hindi (India) -¤ --- Irish -€--- English (Malta) ---- € Finnish (Finland) -¤ --- Estonian -¤ --- Swedish -¤ --- Czech -KM --- Serbian (Latin,Bosnia and Herzegovina) -¤ --- Greek ---- грн. Ukrainian (Ukraine) -¤ --- Hungarian SFr.---- French (Switzerland) -¤--- Indonesian $---- Spanish (Argentina) ج.م.‏ ---- Arabic (Egypt) -¥--- Japanese (Japan,JP) (C---) Spanish (El Salvador) -R$ --- Portuguese (Brazil) -¤ --- Belarusian ---- kr. Icelandic (Iceland) ---- Kč Czech (Czech Republic) (¤---) Spanish ---- zł Polish (Poland) ---- ¤ Turkish -€ --- Catalan (Spain) -CSD --- Serbian (Serbia and Montenegro) (RM---) Malay (Malaysia) -¤ --- Croatian -¤ --- Lithuanian ---- € Spanish (Spain) ($---) Spanish (Colombia) -лв.--- Bulgarian (Bulgaria) -¤ --- Albanian ---- ¤ French -¤ --- Japanese -КМ. --- Serbian (Bosnia and Herzegovina) -¤ --- Icelandic (G---) Spanish (Paraguay) -¤ --- German $---- Spanish (Ecuador) (US$---) Spanish (United States) ج.س.‏ ---- Arabic (Sudan) -¤--- English ---- LEI Romanian (Romania) (Php---) English (Philippines) -¤ --- Catalan د.ت.‏ ---- Arabic (Tunisia) -€ --- Serbian (Latin,Montenegro) (Q---) Spanish (Guatemala) -¤ --- Slovenian -₩--- Korean (South Korea) -€--- Greek (Cyprus) -$--- Spanish (Mexico) ---- руб. Russian (Russia) (L---) Spanish (Honduras) (HK$---) Chinese (Hong Kong) kr ---- Norwegian (Norway,Nynorsk) ---- Ft Hungarian (Hungary) ฿---- Thai (Thailand) د.ع.‏ ---- Arabic (Iraq) Ch$---- Spanish (Chile) -¤ --- Finnish د.م.‏ ---- Arabic (Morocco) -€--- Irish (Ireland) -¤ --- Macedonian ---- TL Turkish (Turkey) ---- € Estonian (Estonia) ر.ق.‏ ---- Arabic (Qatar) -¤ --- Serbian (Latin) ---- € Portuguese (Portugal) ---- € French (Luxembourg) ر.ع.‏ ---- Arabic (Oman) -¤ --- Thai -Lek--- Albanian (Albania) (RD$---) Spanish (Dominican Republic) (CU$---) Spanish (Cuba) ¤ ---- Arabic -¤ --- Russian -$--- English (New Zealand) -дин. --- Serbian (Serbia) SFr.---- German (Switzerland) (NU$---) Spanish (Uruguay) -¤ --- Malay ---- € Greek (Greece) ---- ש"ח Hebrew (Israel) R---- English (South Africa) ฿---- Thai (Thailand,TH) -¤ --- Hindi ---- € French (France) -€ --- German (Austria) -¤ --- Dutch kr ---- Norwegian (Norway) -$--- English (Australia) -¤ --- Vietnamese € ---- Dutch (Netherlands) (---$) French (Canada) ---- € Latvian (Latvia) ---- € German (Luxembourg) (C---) Spanish (Costa Rica) د.ك.‏ ---- Arabic (Kuwait) -¤ --- Serbian د.ل.‏ ---- Arabic (Libya) -¤ --- Maltese SFr.---- Italian (Switzerland) -¤ --- Danish ---- € German (Germany) د.ج.‏ ---- Arabic (Algeria) ---- € Slovak (Slovakia) ---- € Lithuanian (Lithuania) -€ --- Italian (Italy) -€--- English (Ireland) -S$--- Chinese (Singapore) -¤ --- Romanian -$--- English (Canada) ---- € Dutch (Belgium) -¤ --- Norwegian -¤ --- Polish -¥--- Chinese (China) -¥--- Japanese (Japan) -€ --- German (Greece) -din. --- Serbian (Latin,Serbia) -¤ --- Hebrew -Rs.--- English (India) ل.ل.‏ ---- Arabic (Lebanon) ($C---) Spanish (Nicaragua) -¤ --- Chinese -Den --- Macedonian (Macedonia) -Руб--- Belarusian (Belarus) -€ --- Slovenian (Slovenia) S/.---- Spanish (Peru) -Rp--- Indonesian (Indonesia) -£--- English (United Kingdom)
        Hide
        kinow Bruno P. Kinoshita added a comment -

        Hi John, thanks for the test case, that helped.

        There are different negative prefixes and negative suffixes, depending on the locale you use. This is controlled by the JVM, not by Commons Validator, so not much can be done here.

        The UK locale expects you to use "-£1,234.56". Here's a code that generates the list of locales and their prefix/suffix pairs.

        Locale[] locales = Locale.getAvailableLocales();
        for (Locale locale : locales) {
            DecimalFormat nf = (DecimalFormat) NumberFormat.getCurrencyInstance(locale);
            String negativePrefix = nf.getNegativePrefix();
            String negativeSuffix = nf.getNegativeSuffix();
            System.out.println(String.format("%s---%s\t%s", negativePrefix, negativeSuffix, locale.getDisplayName()));
        }
        
        Show
        kinow Bruno P. Kinoshita added a comment - Hi John, thanks for the test case, that helped. There are different negative prefixes and negative suffixes, depending on the locale you use. This is controlled by the JVM, not by Commons Validator, so not much can be done here. The UK locale expects you to use "-£1,234.56". Here's a code that generates the list of locales and their prefix/suffix pairs. Locale[] locales = Locale.getAvailableLocales(); for (Locale locale : locales) { DecimalFormat nf = (DecimalFormat) NumberFormat.getCurrencyInstance(locale); String negativePrefix = nf.getNegativePrefix(); String negativeSuffix = nf.getNegativeSuffix(); System .out.println( String .format( "%s---%s\t%s" , negativePrefix, negativeSuffix, locale.getDisplayName())); }
        Hide
        jjdipirro John DiPirro added a comment - - edited

        Attaching test file: CurrencyValidatorTest.java

        and image from Eclipse: junitResults.jpg showing the test result (failure on UK).

        Overall, US passes, UK/China/Japan fail. I had thought UK passed in a previous test. Please advise.

        Show
        jjdipirro John DiPirro added a comment - - edited Attaching test file: CurrencyValidatorTest.java and image from Eclipse: junitResults.jpg showing the test result (failure on UK). Overall, US passes, UK/China/Japan fail. I had thought UK passed in a previous test. Please advise.
        Hide
        kinow Bruno P. Kinoshita added a comment -

        Can you share a complete test case for that? The following code fails with US too, but not sure if you have similar code.

            @Test
            public void bigDecimalValidatorTest() {
                BigDecimalValidator validator = new BigDecimalValidator();
                final String US_DOLLAR = (new DecimalFormatSymbols(Locale.US)).getCurrencySymbol();
                final String JAPAN_YEN = (new DecimalFormatSymbols(Locale.JAPAN)).getCurrencySymbol();
                final String CHINA_YEN = (new DecimalFormatSymbols(Locale.CHINA)).getCurrencySymbol();
        
                BigDecimal negative = new BigDecimal("-1234.56");
        
                assertEquals("US negative", negative, validator.validate("(" + US_DOLLAR + "1,234.56)", Locale.US)); // fails
                assertEquals("Japan negative", negative, validator.validate("(" + JAPAN_YEN + "1,234.56)", Locale.CHINA)); // fails
                assertEquals("China negative", negative, validator.validate("(" + CHINA_YEN + "1,234.56)", Locale.CHINA)); // fails
            }
        
        Show
        kinow Bruno P. Kinoshita added a comment - Can you share a complete test case for that? The following code fails with US too, but not sure if you have similar code. @Test public void bigDecimalValidatorTest() { BigDecimalValidator validator = new BigDecimalValidator(); final String US_DOLLAR = ( new DecimalFormatSymbols(Locale.US)).getCurrencySymbol(); final String JAPAN_YEN = ( new DecimalFormatSymbols(Locale.JAPAN)).getCurrencySymbol(); final String CHINA_YEN = ( new DecimalFormatSymbols(Locale.CHINA)).getCurrencySymbol(); BigDecimal negative = new BigDecimal( "-1234.56" ); assertEquals( "US negative" , negative, validator.validate( "(" + US_DOLLAR + "1,234.56)" , Locale.US)); // fails assertEquals( "Japan negative" , negative, validator.validate( "(" + JAPAN_YEN + "1,234.56)" , Locale.CHINA)); // fails assertEquals( "China negative" , negative, validator.validate( "(" + CHINA_YEN + "1,234.56)" , Locale.CHINA)); // fails }

          People

          • Assignee:
            kinow Bruno P. Kinoshita
            Reporter:
            jjdipirro John DiPirro
          • Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development