diff --git a/teavm-classlib/pom.xml b/teavm-classlib/pom.xml
index 57d85caa8..f6fc23a7a 100644
--- a/teavm-classlib/pom.xml
+++ b/teavm-classlib/pom.xml
@@ -34,7 +34,6 @@
org.teavm
teavm-platform
${project.version}
- test
org.teavm
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java
index 43a7c6e78..cb3702e93 100644
--- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java
@@ -16,6 +16,7 @@
package org.teavm.classlib.impl;
import org.teavm.classlib.impl.unicode.CLDRHelper;
+import org.teavm.classlib.impl.unicode.CLDRReader;
import org.teavm.classlib.java.util.LocaleSettingsNativeGenerator;
import org.teavm.javascript.ni.Generator;
import org.teavm.model.MethodDescriptor;
@@ -44,8 +45,8 @@ public class JCLPlugin implements TeaVMPlugin {
JavacSupport javacSupport = new JavacSupport();
host.add(javacSupport);
+ host.registerService(CLDRReader.class, new CLDRReader(host.getProperties(), host.getClassLoader()));
Generator localeGen = new LocaleSettingsNativeGenerator(host.getClassLoader(), host.getProperties());
- host.add(new MethodReference("java.util.Locale", "readLanguagesFromCLDR", ValueType.VOID), localeGen);
host.add(new MethodReference("java.util.Locale", "readCountriesFromCLDR", ValueType.VOID), localeGen);
host.add(new MethodReference("java.util.Calendar", "readWeeksFromCDLR", ValueType.VOID), localeGen);
host.add(new MethodReference(CLDRHelper.class.getName(), "readLikelySubtagsFromCLDR", ValueType.VOID),
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 1d6fef75a..259a76b40 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
@@ -17,6 +17,9 @@ package org.teavm.classlib.impl.unicode;
import org.teavm.dependency.PluggableDependency;
import org.teavm.javascript.ni.GeneratedBy;
+import org.teavm.platform.metadata.MetadataProvider;
+import org.teavm.platform.metadata.ResourceMap;
+import org.teavm.platform.metadata.StringResource;
/**
*
@@ -55,4 +58,7 @@ public class CLDRHelper {
@GeneratedBy(CLDRHelperNativeGenerator.class)
@PluggableDependency(CLDRHelperNativeGenerator.class)
private static native String[] getEras(String localeCode);
+
+ @MetadataProvider(LanguageMetadataGenerator.class)
+ public static native ResourceMap> getLanguagesMap();
}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRLocale.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRLocale.java
new file mode 100644
index 000000000..575df5c71
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRLocale.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 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.unicode;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class CLDRLocale {
+ final Map languages = new LinkedHashMap<>();
+ final Map territories = new LinkedHashMap<>();
+ String[] eras;
+
+ public Map getLanguages() {
+ return Collections.unmodifiableMap(languages);
+ }
+
+ public Map getTerritories() {
+ return Collections.unmodifiableMap(territories);
+ }
+
+ public String[] getEras() {
+ return Arrays.copyOf(eras, eras.length);
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRReader.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRReader.java
new file mode 100644
index 000000000..4755e0698
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRReader.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2014 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.unicode;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class CLDRReader {
+ private Map knownLocales = new LinkedHashMap<>();
+ private Map minDaysMap = new LinkedHashMap<>();
+ private Map firstDayMap = new LinkedHashMap<>();
+ private Map likelySubtags = new LinkedHashMap<>();
+ private Set availableLocales = new LinkedHashSet<>();
+ private Set availableLanguages = new LinkedHashSet<>();
+ private Set availableCountries = new LinkedHashSet<>();
+
+ public CLDRReader(Properties properties, ClassLoader classLoader) {
+ findAvailableLocales(properties);
+ readCLDR(classLoader);
+ }
+
+ private void findAvailableLocales(Properties properties) {
+ String availableLocalesString = properties.getProperty("java.util.Locale.available", "en_EN").trim();
+ for (String locale : Arrays.asList(availableLocalesString.split(" *, *"))) {
+ int countryIndex = locale.indexOf('_');
+ if (countryIndex > 0) {
+ String language = locale.substring(0, countryIndex);
+ String country = locale.substring(countryIndex + 1);
+ availableLocales.add(language + "-" + country);
+ availableLocales.add(language);
+ availableLanguages.add(language);
+ availableCountries.add(country);
+ } else {
+ availableLocales.add(locale);
+ availableLanguages.add(locale);
+ }
+ }
+ }
+
+ private void readCLDR(ClassLoader classLoader) {
+ try (ZipInputStream input = new ZipInputStream(classLoader.getResourceAsStream(
+ "org/teavm/classlib/impl/unicode/cldr-json.zip"))) {
+ while (true) {
+ ZipEntry entry = input.getNextEntry();
+ if (entry == null) {
+ break;
+ }
+ if (!entry.getName().endsWith(".json")) {
+ continue;
+ }
+ if (entry.getName().equals("supplemental/weekData.json")) {
+ readWeekData(input);
+ continue;
+ } else if (entry.getName().equals("supplemental/likelySubtags.json")) {
+ readLikelySubtags(input);
+ }
+ int objectIndex = entry.getName().lastIndexOf('/');
+ String objectName = entry.getName().substring(objectIndex + 1);
+ String localeName = entry.getName().substring(0, objectIndex);
+ if (localeName.startsWith("/")) {
+ localeName = localeName.substring(1);
+ }
+ if (!localeName.equals("root") && !availableLocales.contains(localeName)) {
+ continue;
+ }
+ CLDRLocale localeInfo = knownLocales.get(localeName);
+ if (localeInfo == null) {
+ localeInfo = new CLDRLocale();
+ knownLocales.put(localeName, localeInfo);
+ }
+ switch (objectName) {
+ case "languages.json":
+ readLanguages(localeName, localeInfo, input);
+ break;
+ case "territories.json":
+ readCountries(localeName, localeInfo, input);
+ break;
+ case "ca-gregorian.json":
+ readEras(localeName, localeInfo, input);
+ break;
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Error reading CLDR file", e);
+ }
+ }
+
+ private void readLanguages(String localeCode, CLDRLocale locale, InputStream input) {
+ JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
+ JsonObject languagesJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
+ .get("localeDisplayNames").getAsJsonObject().get("languages").getAsJsonObject();
+ for (Map.Entry property : languagesJson.entrySet()) {
+ String language = property.getKey();
+ if (availableLanguages.contains(language)) {
+ locale.languages.put(language, property.getValue().getAsString());
+ }
+ }
+ }
+
+ private void readCountries(String localeCode, CLDRLocale locale, InputStream input) {
+ JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
+ JsonObject countriesJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
+ .get("localeDisplayNames").getAsJsonObject().get("territories").getAsJsonObject();
+ for (Map.Entry property : countriesJson.entrySet()) {
+ String country = property.getKey();
+ if (availableCountries.contains(country)) {
+ locale.territories.put(country, property.getValue().getAsString());
+ }
+ }
+ }
+
+ private void readEras(String localeCode, CLDRLocale locale, InputStream input) {
+ JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
+ JsonObject erasJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
+ .get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
+ .get("gregorian").getAsJsonObject().get("eras").getAsJsonObject().get("eraNames").getAsJsonObject();
+ String am = erasJson.get("0").getAsString();
+ String pm = erasJson.get("1").getAsString();
+ locale.eras = new String[] { am, pm };
+ }
+
+ private void readWeekData(InputStream input) {
+ JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
+ JsonObject weekJson = root.get("supplemental").getAsJsonObject().get("weekData").getAsJsonObject();
+ JsonObject minDaysJson = weekJson.get("minDays").getAsJsonObject();
+ for (Map.Entry property : minDaysJson.entrySet()) {
+ minDaysMap.put(property.getKey(), property.getValue().getAsInt());
+ }
+ JsonObject firstDayJson = weekJson.get("firstDay").getAsJsonObject();
+ for (Map.Entry property : firstDayJson.entrySet()) {
+ firstDayMap.put(property.getKey(), getNumericDay(property.getValue().getAsString()));
+ }
+ }
+
+ private void readLikelySubtags(InputStream input) {
+ JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
+ JsonObject likelySubtagsJson = root.get("supplemental").getAsJsonObject().get("likelySubtags")
+ .getAsJsonObject();
+ for (Map.Entry property : likelySubtagsJson.entrySet()) {
+ likelySubtags.put(property.getKey(), property.getValue().getAsString());
+ }
+ }
+
+ private int getNumericDay(String day) {
+ switch (day) {
+ case "sun":
+ return 1;
+ case "mon":
+ return 2;
+ case "tue":
+ return 3;
+ case "wed":
+ return 4;
+ case "thu":
+ return 5;
+ case "fri":
+ return 6;
+ case "sat":
+ return 7;
+ default:
+ throw new IllegalArgumentException("Can't recognize day name: " + day);
+ }
+ }
+
+ public Map getKnownLocales() {
+ return Collections.unmodifiableMap(knownLocales);
+ }
+
+ public Set getAvailableLocales() {
+ return Collections.unmodifiableSet(availableLocales);
+ }
+
+ public Set getAvailableLanguages() {
+ return Collections.unmodifiableSet(availableLanguages);
+ }
+
+ public Set getAvailableCountries() {
+ return Collections.unmodifiableSet(availableCountries);
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/LanguageMetadataGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/LanguageMetadataGenerator.java
new file mode 100644
index 000000000..fd5f20c96
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/LanguageMetadataGenerator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 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.unicode;
+
+import java.util.Map;
+import org.teavm.model.MethodReference;
+import org.teavm.platform.metadata.*;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class LanguageMetadataGenerator implements MetadataGenerator {
+ @Override
+ public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
+ ResourceMap> languages = context.createResourceMap();
+ CLDRReader reader = context.getService(CLDRReader.class);
+ for (Map.Entry entry : reader.getKnownLocales().entrySet()) {
+ CLDRLocale locale = entry.getValue();
+ ResourceMap languageNames = context.createResourceMap();
+ languages.put(entry.getKey(), languageNames);
+ for (Map.Entry language : locale.getLanguages().entrySet()) {
+ StringResource languageName = context.createResource(StringResource.class);
+ languageName.setValue(language.getValue());
+ languageNames.put(language.getKey(), languageName);
+ }
+ }
+ return languages;
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java
index bce04cef7..14f756e49 100644
--- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java
@@ -115,9 +115,6 @@ public class LocaleSettingsNativeGenerator implements Generator {
case "territories.json":
readCountries(localeName, localeInfo, input);
break;
- case "ca-gregorian.json":
- readEras(localeName, localeInfo, input);
- break;
}
}
} catch (IOException e) {
@@ -149,16 +146,6 @@ public class LocaleSettingsNativeGenerator implements Generator {
}
}
- private void readEras(String localeCode, LocaleInfo locale, InputStream input) {
- JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
- JsonObject erasJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
- .get("calendars").getAsJsonObject().get("gregorian").getAsJsonObject()
- .get("eras").getAsJsonObject().get("eraNames").getAsJsonObject();
- String am = erasJson.get("0").getAsString();
- String pm = erasJson.get("1").getAsString();
- locale.eras = new String[] { am, pm };
- }
-
private void readWeekData(InputStream input) {
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
JsonObject weekJson = root.get("supplemental").getAsJsonObject().get("weekData").getAsJsonObject();
@@ -206,9 +193,6 @@ public class LocaleSettingsNativeGenerator implements Generator {
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
init();
switch (methodRef.getName()) {
- case "readLanguagesFromCLDR":
- generateReadLanguagesFromCLDR(writer);
- break;
case "readCountriesFromCLDR":
generateReadCountriesFromCLDR(writer);
break;
@@ -248,32 +232,6 @@ public class LocaleSettingsNativeGenerator implements Generator {
writer.append("];").softNewLine();
}
- private void generateReadLanguagesFromCLDR(SourceWriter writer) throws IOException {
- generateDefender(writer, "languages");
- writer.appendClass("java.util.Locale").append(".$CLDR.languages = {").indent().softNewLine();
- boolean firstLocale = true;
- for (Map.Entry entry : knownLocales.entrySet()) {
- if (!firstLocale) {
- writer.append(",").softNewLine();
- }
- firstLocale = false;
- writer.append('"').append(Renderer.escapeString(entry.getKey())).append('"').ws().append(":").ws()
- .append('{').indent().softNewLine();
-
- boolean first = true;
- for (Map.Entry langEntry : entry.getValue().languages.entrySet()) {
- if (!first) {
- writer.append(',').softNewLine();
- }
- first = false;
- writer.append('"').append(Renderer.escapeString(langEntry.getKey())).append('"').ws().append(':')
- .ws().append('"').append(Renderer.escapeString(langEntry.getValue())).append('"');
- }
- writer.outdent().append('}');
- }
- writer.outdent().append("};").softNewLine();
- }
-
private void generateReadCountriesFromCLDR(SourceWriter writer) throws IOException {
generateDefender(writer, "territories");
writer.appendClass("java.util.Locale").append(".$CLDR.territories = {").indent().softNewLine();
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java
index 07c5365ac..5fc084da3 100644
--- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java
@@ -33,10 +33,13 @@
package org.teavm.classlib.java.util;
import java.util.Arrays;
+import org.teavm.classlib.impl.unicode.CLDRHelper;
import org.teavm.classlib.java.io.TSerializable;
import org.teavm.classlib.java.lang.TCloneable;
import org.teavm.dependency.PluggableDependency;
import org.teavm.javascript.ni.GeneratedBy;
+import org.teavm.platform.metadata.ResourceMap;
+import org.teavm.platform.metadata.StringResource;
public final class TLocale implements TCloneable, TSerializable {
private static TLocale defaultLocale;
@@ -85,9 +88,6 @@ public final class TLocale implements TCloneable, TSerializable {
// Redefined by JCLPlugin
private static native void readCountriesFromCLDR();
- // Redefined by JCLPlugin
- private static native void readLanguagesFromCLDR();
-
// Redefined by JCLPlugin
private static native void readAvailableLocales();
@@ -189,7 +189,6 @@ public final class TLocale implements TCloneable, TSerializable {
}
public String getDisplayLanguage(TLocale locale) {
- readLanguagesFromCLDR();
String result = getDisplayLanguage(locale.getLanguage() + "-" + locale.getCountry(), languageCode);
if (result == null) {
result = getDisplayLanguage(locale.getLanguage(), languageCode);
@@ -197,9 +196,16 @@ public final class TLocale implements TCloneable, TSerializable {
return result != null ? result : languageCode;
}
- @GeneratedBy(LocaleNativeGenerator.class)
- @PluggableDependency(LocaleNativeGenerator.class)
- private static native String getDisplayLanguage(String localeName, String country);
+ private static String getDisplayLanguage(String localeName, String language) {
+ if (!CLDRHelper.getLanguagesMap().has(localeName)) {
+ return null;
+ }
+ ResourceMap languages = CLDRHelper.getLanguagesMap().get(localeName);
+ if (!languages.has(language)) {
+ return null;
+ }
+ return languages.get(language).getValue();
+ }
public final String getDisplayName() {
return getDisplayName(getDefault());
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java
index 5002ec399..22cbff38e 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java
@@ -22,7 +22,9 @@ import org.teavm.platform.metadata.Resource;
* @author Alexey Andreev
*/
final class ResourceAccessor {
- public static native Object get(Object obj, String propertyName);
+ public static native Object getProperty(Object obj, String propertyName);
+
+ public static native Resource get(Object obj, String propertyName);
public static native void put(Object obj, String propertyName, Object elem);
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java
index 1eaa46a52..62c267ad5 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java
@@ -32,6 +32,7 @@ class ResourceAccessorGenerator implements Injector {
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) {
case "get":
+ case "getProperty":
if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) {
context.writeExpr(context.getArgument(0));
context.getWriter().append('[');
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java
index 56e51343b..0b80ebbe6 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java
@@ -166,7 +166,7 @@ class ResourceProgramTransformer {
instructions.add(nameInsn);
InvokeInstruction accessorInvoke = new InvokeInstruction();
accessorInvoke.setType(InvocationType.SPECIAL);
- accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "get",
+ accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "getProperty",
Object.class, String.class, Object.class));
accessorInvoke.getArguments().add(insn.getInstance());
accessorInvoke.getArguments().add(nameVar);