Skip to content

8353713: Improve Currency.getInstance exception handling #3754

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions src/java.base/share/classes/java/util/Currency.java
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,8 @@ private static Currency getInstance(String currencyCode, int defaultFractionDigi
// or in the list of other currencies.
boolean found = false;
if (currencyCode.length() != 3) {
throw new IllegalArgumentException("The input currency code must " +
"have a length of 3 characters");
throw new IllegalArgumentException(
"The input currency code: \"%s\" must have a length of 3 characters".formatted(currencyCode));
}
char char1 = currencyCode.charAt(0);
char char2 = currencyCode.charAt(1);
Expand All @@ -338,8 +338,8 @@ private static Currency getInstance(String currencyCode, int defaultFractionDigi
if (!found) {
OtherCurrencyEntry ocEntry = OtherCurrencyEntry.findEntry(currencyCode);
if (ocEntry == null) {
throw new IllegalArgumentException("The input currency code" +
" is not a valid ISO 4217 code");
throw new IllegalArgumentException(
"The input currency code: \"%s\" is not a valid ISO 4217 code".formatted(currencyCode));
}
defaultFractionDigits = ocEntry.fraction;
numericCode = ocEntry.numericCode;
Expand Down Expand Up @@ -394,8 +394,8 @@ public static Currency getInstance(Locale locale) {
String country = CalendarDataUtility.findRegionOverride(locale).getCountry();

if (country == null || !country.matches("^[a-zA-Z]{2}$")) {
throw new IllegalArgumentException("The country of the input locale" +
" is not a valid ISO 3166 country code");
throw new IllegalArgumentException(
"The country of the input locale: \"%s\" is not a valid ISO 3166 country code".formatted(locale));
}

char char1 = country.charAt(0);
Expand All @@ -412,8 +412,8 @@ public static Currency getInstance(Locale locale) {
} else {
// special cases
if (tableEntry == INVALID_COUNTRY_ENTRY) {
throw new IllegalArgumentException("The country of the input locale" +
" is not a valid ISO 3166 country code");
throw new IllegalArgumentException(
"The country of the input locale: \"%s\" is not a valid ISO 3166 country code".formatted(locale));
}
if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) {
return null;
Expand Down Expand Up @@ -678,8 +678,8 @@ private Object readResolve() {
*/
private static int getMainTableEntry(char char1, char char2) {
if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
throw new IllegalArgumentException("The country code is not a " +
"valid ISO 3166 code");
throw new IllegalArgumentException(
"The country code: \"%c%c\" is not a valid ISO 3166 code".formatted(char1, char2));
}
return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')];
}
Expand All @@ -690,8 +690,8 @@ private static int getMainTableEntry(char char1, char char2) {
*/
private static void setMainTableEntry(char char1, char char2, int entry) {
if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
throw new IllegalArgumentException("The country code is not a " +
"valid ISO 3166 code");
throw new IllegalArgumentException(
"The country code: \"%c%c\" is not a valid ISO 3166 code".formatted(char1, char2));
}
mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')] = entry;
}
Expand Down
38 changes: 25 additions & 13 deletions test/jdk/java/util/Currency/CurrencyTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -25,7 +25,7 @@
* @test
* @bug 4290801 4692419 4693631 5101540 5104960 6296410 6336600 6371531
* 6488442 7036905 8008577 8039317 8074350 8074351 8150324 8167143
* 8264792 8334653
* 8264792 8334653 8353713
* @summary Basic tests for Currency class.
* @modules java.base/java.util:open
* jdk.localedata
Expand Down Expand Up @@ -84,23 +84,33 @@ private static Stream<String> validCurrencies() {
public void invalidCurrencyTest(String currencyCode) {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () ->
Currency.getInstance(currencyCode), "getInstance() did not throw IAE");
assertEquals("The input currency code is not a" +
" valid ISO 4217 code", ex.getMessage());
assertEquals("The input currency code: \"%s\" is not a valid ISO 4217 code"
.formatted(currencyCode), ex.getMessage());
}

private static Stream<String> non4217Currencies() {
return Stream.of("AQD", "US$");
}

// Provide 3 length code, but first 2 chars should not be able to index
// the main table, thus resulting as incorrect country code
@Test
void invalidCountryInCodeTest() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () ->
Currency.getInstance("..A"), "getInstance() did not throw IAE");
assertEquals("The country code: \"%s\" is not a valid ISO 3166 code"
.formatted(".."), ex.getMessage());
}

// Calling getInstance() with a currency code not 3 characters long should throw
// an IAE
@ParameterizedTest
@MethodSource("invalidLengthCurrencies")
public void invalidCurrencyLengthTest(String currencyCode) {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () ->
Currency.getInstance(currencyCode), "getInstance() did not throw IAE");
assertEquals("The input currency code must have a length of 3" +
" characters", ex.getMessage());
assertEquals("The input currency code: \"%s\" must have a length of 3 characters"
.formatted(currencyCode), ex.getMessage());
}

private static Stream<String> invalidLengthCurrencies() {
Expand Down Expand Up @@ -163,8 +173,8 @@ public void localeMappingTest() {
"AC|CP|DG|EA|EU|FX|IC|SU|TA|UK")) { // exceptional reservation codes
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> Currency.getInstance(locale), "Did not throw IAE");
assertEquals("The country of the input locale is not a" +
" valid ISO 3166 country code", ex.getMessage());
assertEquals("The country of the input locale: \"%s\" is not a valid ISO 3166 country code"
.formatted(locale), ex.getMessage());
} else {
goodCountries++;
Currency currency = Currency.getInstance(locale);
Expand All @@ -180,14 +190,16 @@ public void localeMappingTest() {
}
}

// Check an invalid country code
// Check an invalid country code supplied via the region override
@Test
public void invalidCountryTest() {
public void invalidCountryRegionOverrideTest() {
// Override US with nonsensical country
var loc = Locale.forLanguageTag("en-US-u-rg-XXzzzz");
Locale l = new Locale("", "EU");
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
()-> Currency.getInstance(l), "Did not throw IAE");
assertEquals("The country of the input locale is not a valid" +
" ISO 3166 country code", ex.getMessage());
()-> Currency.getInstance(loc), "Did not throw IAE");
assertEquals("The country of the input locale: \"%s\" is not a valid ISO 3166 country code"
.formatted(loc), ex.getMessage());
}

// Ensure a selection of countries have the expected currency
Expand Down