diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CountriesGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CountriesGenerator.java new file mode 100644 index 000000000..2f2d58dfe --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CountriesGenerator.java @@ -0,0 +1,108 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.impl.currency; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import org.teavm.model.MethodReference; +import org.teavm.platform.metadata.*; + +/** + * + * @author Alexey Andreev + */ +public class CountriesGenerator implements MetadataGenerator { + @Override + public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) { + try (InputStream input = context.getClassLoader().getResourceAsStream( + "org/teavm/classlib/impl/currency/iso3166.csv")) { + if (input == null) { + throw new AssertionError("ISO 3166 table was not found"); + } + try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"))) { + return readIso3166(context, reader); + } + } catch (IOException e) { + throw new RuntimeException("Error reading ISO 3166 table", e); + } + } + + private ResourceMap readIso3166(MetadataGeneratorContext context, BufferedReader reader) + throws IOException { + ResourceMap result = context.createResourceMap(); + int index = 0; + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + if (index++ == 0 || line.trim().isEmpty()) { + continue; + } + String[] cells = readCsvRow(index - 1, line); + StringResource currency = context.createResource(StringResource.class); + currency.setValue(cells[7]); + result.put(cells[10], currency); + } + return result; + } + + private String[] readCsvRow(int rowIndex, String row) { + List values = new ArrayList<>(); + int index = 0; + while (index < row.length()) { + char c = row.charAt(index); + int next = index; + if (c == '"') { + ++index; + StringBuilder sb = new StringBuilder(); + while (index < row.length()) { + next = row.indexOf('"', index); + if (next == -1) { + throw new IllegalStateException("Syntax error at row " + rowIndex + + ": closing quote not found"); + } + if (next + 1 == row.length() || row.charAt(next + 1) != '"') { + sb.append(row.substring(index, next)); + index = next + 1; + break; + } + index = next + 2; + } + if (index < row.length() && row.charAt(index) != ',') { + throw new IllegalStateException("Syntax error at row " + rowIndex + ": closing quote must be " + + "followed by either line separator or comma"); + } + values.add(sb.toString()); + } else { + next = row.indexOf(',', index); + if (next == -1) { + values.add(row.substring(index)); + index = row.length(); + } else { + values.add(row.substring(index, next)); + ++next; + index = next; + } + } + } + return values.toArray(new String[values.size()]); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyHelper.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyHelper.java index be945647e..7cb7cc824 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyHelper.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyHelper.java @@ -17,6 +17,8 @@ package org.teavm.classlib.impl.currency; import org.teavm.platform.metadata.MetadataProvider; import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; +import org.teavm.platform.metadata.StringResource; /** * @@ -25,4 +27,7 @@ import org.teavm.platform.metadata.ResourceArray; public final class CurrencyHelper { @MetadataProvider(CurrenciesGenerator.class) public static native ResourceArray getCurrencies(); + + @MetadataProvider(CountriesGenerator.class) + public static native ResourceMap getCountryToCurrencyMap(); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java index 7c2c9a395..8ad5a1969 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java @@ -33,6 +33,15 @@ public class CLDRHelper { return map.has(localeCode) ? map.get(localeCode).getValue() : localeCode; } + public static String resolveCountry(String language, String country) { + if (country.isEmpty()) { + String subtags = getLikelySubtags(language); + int index = subtags.lastIndexOf('_'); + country = index > 0 ? subtags.substring(index + 1) : ""; + } + return country; + } + @MetadataProvider(LikelySubtagsMetadataGenerator.class) private static native ResourceMap getLikelySubtagsMap(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCalendar.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCalendar.java index 2c835c7d3..f54c9901b 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCalendar.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCalendar.java @@ -175,21 +175,11 @@ public abstract class TCalendar implements TSerializable, TCloneable, TComparabl cacheFor = locale; } - private static String resolveCountry(TLocale locale) { - String country = locale.getCountry(); - if (country.isEmpty()) { - String subtags = CLDRHelper.getLikelySubtags(locale.getLanguage()); - int index = subtags.lastIndexOf('_'); - country = index > 0 ? subtags.substring(index + 1) : ""; - } - return country; - } - private static int resolveFirstDayOfWeek(TLocale locale) { if (locale == cacheFor && firstDayOfWeekCache >= 0) { return firstDayOfWeekCache; } - String country = resolveCountry(locale); + String country = CLDRHelper.resolveCountry(locale.getLanguage(), locale.getCountry()); ResourceMap dayMap = CLDRHelper.getFirstDayOfWeek(); firstDayOfWeekCache = dayMap.has(country) ? dayMap.get(country).getValue() : dayMap.get("001").getValue(); return firstDayOfWeekCache; @@ -199,7 +189,7 @@ public abstract class TCalendar implements TSerializable, TCloneable, TComparabl if (locale == cacheFor && minimalDaysInFirstWeekCache >= 0) { return minimalDaysInFirstWeekCache; } - String country = resolveCountry(locale); + String country = CLDRHelper.resolveCountry(locale.getLanguage(), locale.getCountry()); ResourceMap dayMap = CLDRHelper.getMinimalDaysInFirstWeek(); minimalDaysInFirstWeekCache = dayMap.has(country) ? dayMap.get(country).getValue() : dayMap.get("001").getValue(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCurrency.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCurrency.java index 526ee1b6e..53ac00dea 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCurrency.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCurrency.java @@ -21,8 +21,11 @@ import java.util.Map; import java.util.Set; import org.teavm.classlib.impl.currency.CurrencyHelper; import org.teavm.classlib.impl.currency.CurrencyResource; +import org.teavm.classlib.impl.unicode.CLDRHelper; import org.teavm.classlib.java.io.TSerializable; import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; +import org.teavm.platform.metadata.StringResource; /** * @@ -60,6 +63,18 @@ public final class TCurrency implements TSerializable { return currency; } + public static TCurrency getInstance(TLocale locale) { + if (locale == null) { + throw new NullPointerException(); + } + String coutry = CLDRHelper.resolveCountry(locale.getLanguage(), locale.getCountry()); + ResourceMap countryMap = CurrencyHelper.getCountryToCurrencyMap(); + if (!countryMap.has(coutry)) { + return null; + } + return getInstance(countryMap.get(coutry).getValue()); + } + public static Set getAvailableCurrencies() { initCurrencies(); return new HashSet<>(currencies.values()); diff --git a/teavm-tests/src/test/java/org/teavm/classlib/java/util/CurrencyTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/CurrencyTest.java index ef2c06be1..3d4ee4eca 100644 --- a/teavm-tests/src/test/java/org/teavm/classlib/java/util/CurrencyTest.java +++ b/teavm-tests/src/test/java/org/teavm/classlib/java/util/CurrencyTest.java @@ -17,6 +17,7 @@ package org.teavm.classlib.java.util; import static org.junit.Assert.*; import java.util.Currency; +import java.util.Locale; import org.junit.Test; /** @@ -32,6 +33,18 @@ public class CurrencyTest { assertEquals(643, currency.getNumericCode()); } + @Test + public void findsByLocale() { + Currency currency = Currency.getInstance(new Locale("ru", "RU")); + assertEquals("RUB", currency.getCurrencyCode()); + + currency = Currency.getInstance(new Locale("en", "US")); + assertEquals("USD", currency.getCurrencyCode()); + + currency = Currency.getInstance(new Locale("en", "GB")); + assertEquals("GBP", currency.getCurrencyCode()); + } + @Test(expected = IllegalArgumentException.class) public void rejectsWrongCode() { Currency.getInstance("WWW");