JDK-8252383 : NumberFormat.parse fails with some locale providers
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.text
  • Affected Version: 11,16
  • Priority: P4
  • Status: Resolved
  • Resolution: Not an Issue
  • Submitted: 2020-08-26
  • Updated: 2020-08-26
  • Resolved: 2020-08-26
Related Reports
Relates :  
Description
Differences between JRE and CLDR locale providers cause an application to fail.

With the application below the behavior is different on JDK 8 and JDK 11+.

The full report is:
https://mail.openjdk.java.net/pipermail/core-libs-dev/2020-August/068355.html

public class Main {

	public static void main(String[] args) throws IOException {
		// System.setProperty("java.locale.providers", "JRE");
		System.out.println(getPriceInCents(Locale.GERMANY, "9,99 ���"));
	}

	static int getPriceInCents(Locale locale, String price) {
		try {
			DecimalFormat format = (DecimalFormat) NumberFormat.getCurrencyInstance(locale);
			Number number = format.parse(price);
			return (int) (number.doubleValue() * 100);
		} catch (ParseException e) {
                                           // This should be thrown on JDK 9+
			System.out.println(e);
		}
		return 0;
	}

}
	
After some digging I think this is caused by the changes done for JDK-8008577[1].
When I change the java.locale.providers property to "JRE" for example, it works again.

My investigations so far revealed that apparently the CLDR number pattern for the currency slightly differs.

I created breakpoints in sun.util.locale.provider.NumberFormatProviderImpl::getInstance() to display some things:

        LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type);
        String[] numberPatterns = adapter.getLocaleResources(override).getNumberPatterns();
        DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(override);
        int entry = (choice == INTEGERSTYLE) ? NUMBERSTYLE : choice;
        DecimalFormat format = new DecimalFormat(numberPatterns[entry], symbols);

	// CLDR (type) 
	// #,##0.00 �� (numberPatterns[entry])
	// [35,44,35,35,48,46,48,48,-62,-96,-62,-92] (numberPatterns[entry] in bytes)

	//
	// JRE type
	// #,##0.00 ��;-#,##0.00 �� (numberPatterns[entry])
	// [35,44,35,35,48,46,48,48,32,-62,-92,59,45,35,44,35,35,48,46,48,48,32,-62,-92] (numberPatterns[entry] in bytes)

The JRE one includes the negative pattern, but the more interesting bit is that apparently the spacing differs here.
For JRE it seems to be a normal space (the 32), but for CLDR it's showing [-62, -96] which seems to be a non breaking space aka nbsp.

Ultimately this leads to a check failing in DecimalFormat when parsing the string "9,99 ���" that obviously includes a normal space.

            if (gotPositive) {
                // the regionMatches will return false because nbsp != space
                gotPositive = text.regionMatches(position,positiveSuffix,0,
                                                 positiveSuffix.length());
            }

Which itself leads to the following in our case:

        // fail if neither or both
        if (gotPositive == gotNegative) {
            parsePosition.errorIndex = position;
            // We hit this part here which causes the parsing to fail
            return false;
        }


There are workarounds - e.g. by setting java.locale.providers as already mentioned or setting format.setPositiveSuffix(" ���"); to fix this particular case.

Is this a bug or a feature or are we missing something?

In case this is an actual bug we would appreciate a "reported-by" mentioning in an eventual fix.

Thanks in advance. I do hope you can follow my thoughts in this email.

[1] https://bugs.openjdk.java.net/browse/JDK-8008577




Comments
As replied to the core-libs ml: --- The behavior you are observing is what is expected with the CLDR provider, as the currency format is exactly the one that CLDR has for the de locale: https://unicode-org.github.io/cldr-staging/charts/37/by_type/numbers.number_formatting_patterns.html#53687a25c19b6481 I'd recommend that either the code uses the "COMPAT" (same as "JRE") provider which keeps compatibility with JDK8 (which you have mentioned already), or replace the normal space with non-breaking space in the input string.
26-08-2020