Instroduces usage of a new metadata provider to implement locale methods

This commit is contained in:
konsoletyper 2014-06-10 16:47:04 +04:00
parent c4d32fce85
commit 5063091126
11 changed files with 316 additions and 53 deletions

View File

@ -34,7 +34,6 @@
<groupId>org.teavm</groupId> <groupId>org.teavm</groupId>
<artifactId>teavm-platform</artifactId> <artifactId>teavm-platform</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.teavm</groupId> <groupId>org.teavm</groupId>

View File

@ -16,6 +16,7 @@
package org.teavm.classlib.impl; package org.teavm.classlib.impl;
import org.teavm.classlib.impl.unicode.CLDRHelper; import org.teavm.classlib.impl.unicode.CLDRHelper;
import org.teavm.classlib.impl.unicode.CLDRReader;
import org.teavm.classlib.java.util.LocaleSettingsNativeGenerator; import org.teavm.classlib.java.util.LocaleSettingsNativeGenerator;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
@ -44,8 +45,8 @@ public class JCLPlugin implements TeaVMPlugin {
JavacSupport javacSupport = new JavacSupport(); JavacSupport javacSupport = new JavacSupport();
host.add(javacSupport); host.add(javacSupport);
host.registerService(CLDRReader.class, new CLDRReader(host.getProperties(), host.getClassLoader()));
Generator localeGen = new LocaleSettingsNativeGenerator(host.getClassLoader(), host.getProperties()); 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.Locale", "readCountriesFromCLDR", ValueType.VOID), localeGen);
host.add(new MethodReference("java.util.Calendar", "readWeeksFromCDLR", ValueType.VOID), localeGen); host.add(new MethodReference("java.util.Calendar", "readWeeksFromCDLR", ValueType.VOID), localeGen);
host.add(new MethodReference(CLDRHelper.class.getName(), "readLikelySubtagsFromCLDR", ValueType.VOID), host.add(new MethodReference(CLDRHelper.class.getName(), "readLikelySubtagsFromCLDR", ValueType.VOID),

View File

@ -17,6 +17,9 @@ package org.teavm.classlib.impl.unicode;
import org.teavm.dependency.PluggableDependency; import org.teavm.dependency.PluggableDependency;
import org.teavm.javascript.ni.GeneratedBy; 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) @GeneratedBy(CLDRHelperNativeGenerator.class)
@PluggableDependency(CLDRHelperNativeGenerator.class) @PluggableDependency(CLDRHelperNativeGenerator.class)
private static native String[] getEras(String localeCode); private static native String[] getEras(String localeCode);
@MetadataProvider(LanguageMetadataGenerator.class)
public static native ResourceMap<ResourceMap<StringResource>> getLanguagesMap();
} }

View File

@ -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<String, String> languages = new LinkedHashMap<>();
final Map<String, String> territories = new LinkedHashMap<>();
String[] eras;
public Map<String, String> getLanguages() {
return Collections.unmodifiableMap(languages);
}
public Map<String, String> getTerritories() {
return Collections.unmodifiableMap(territories);
}
public String[] getEras() {
return Arrays.copyOf(eras, eras.length);
}
}

View File

@ -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<String, CLDRLocale> knownLocales = new LinkedHashMap<>();
private Map<String, Integer> minDaysMap = new LinkedHashMap<>();
private Map<String, Integer> firstDayMap = new LinkedHashMap<>();
private Map<String, String> likelySubtags = new LinkedHashMap<>();
private Set<String> availableLocales = new LinkedHashSet<>();
private Set<String> availableLanguages = new LinkedHashSet<>();
private Set<String> 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<String, JsonElement> 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<String, JsonElement> 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<String, JsonElement> property : minDaysJson.entrySet()) {
minDaysMap.put(property.getKey(), property.getValue().getAsInt());
}
JsonObject firstDayJson = weekJson.get("firstDay").getAsJsonObject();
for (Map.Entry<String, JsonElement> 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<String, JsonElement> 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<String, CLDRLocale> getKnownLocales() {
return Collections.unmodifiableMap(knownLocales);
}
public Set<String> getAvailableLocales() {
return Collections.unmodifiableSet(availableLocales);
}
public Set<String> getAvailableLanguages() {
return Collections.unmodifiableSet(availableLanguages);
}
public Set<String> getAvailableCountries() {
return Collections.unmodifiableSet(availableCountries);
}
}

View File

@ -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<ResourceMap<StringResource>> languages = context.createResourceMap();
CLDRReader reader = context.getService(CLDRReader.class);
for (Map.Entry<String, CLDRLocale> entry : reader.getKnownLocales().entrySet()) {
CLDRLocale locale = entry.getValue();
ResourceMap<StringResource> languageNames = context.createResourceMap();
languages.put(entry.getKey(), languageNames);
for (Map.Entry<String, String> language : locale.getLanguages().entrySet()) {
StringResource languageName = context.createResource(StringResource.class);
languageName.setValue(language.getValue());
languageNames.put(language.getKey(), languageName);
}
}
return languages;
}
}

View File

@ -115,9 +115,6 @@ public class LocaleSettingsNativeGenerator implements Generator {
case "territories.json": case "territories.json":
readCountries(localeName, localeInfo, input); readCountries(localeName, localeInfo, input);
break; break;
case "ca-gregorian.json":
readEras(localeName, localeInfo, input);
break;
} }
} }
} catch (IOException e) { } 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) { private void readWeekData(InputStream input) {
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input)); JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
JsonObject weekJson = root.get("supplemental").getAsJsonObject().get("weekData").getAsJsonObject(); 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 { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
init(); init();
switch (methodRef.getName()) { switch (methodRef.getName()) {
case "readLanguagesFromCLDR":
generateReadLanguagesFromCLDR(writer);
break;
case "readCountriesFromCLDR": case "readCountriesFromCLDR":
generateReadCountriesFromCLDR(writer); generateReadCountriesFromCLDR(writer);
break; break;
@ -248,32 +232,6 @@ public class LocaleSettingsNativeGenerator implements Generator {
writer.append("];").softNewLine(); 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<String, LocaleInfo> 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<String, String> 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 { private void generateReadCountriesFromCLDR(SourceWriter writer) throws IOException {
generateDefender(writer, "territories"); generateDefender(writer, "territories");
writer.appendClass("java.util.Locale").append(".$CLDR.territories = {").indent().softNewLine(); writer.appendClass("java.util.Locale").append(".$CLDR.territories = {").indent().softNewLine();

View File

@ -33,10 +33,13 @@
package org.teavm.classlib.java.util; package org.teavm.classlib.java.util;
import java.util.Arrays; import java.util.Arrays;
import org.teavm.classlib.impl.unicode.CLDRHelper;
import org.teavm.classlib.java.io.TSerializable; import org.teavm.classlib.java.io.TSerializable;
import org.teavm.classlib.java.lang.TCloneable; import org.teavm.classlib.java.lang.TCloneable;
import org.teavm.dependency.PluggableDependency; import org.teavm.dependency.PluggableDependency;
import org.teavm.javascript.ni.GeneratedBy; 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 { public final class TLocale implements TCloneable, TSerializable {
private static TLocale defaultLocale; private static TLocale defaultLocale;
@ -85,9 +88,6 @@ public final class TLocale implements TCloneable, TSerializable {
// Redefined by JCLPlugin // Redefined by JCLPlugin
private static native void readCountriesFromCLDR(); private static native void readCountriesFromCLDR();
// Redefined by JCLPlugin
private static native void readLanguagesFromCLDR();
// Redefined by JCLPlugin // Redefined by JCLPlugin
private static native void readAvailableLocales(); private static native void readAvailableLocales();
@ -189,7 +189,6 @@ public final class TLocale implements TCloneable, TSerializable {
} }
public String getDisplayLanguage(TLocale locale) { public String getDisplayLanguage(TLocale locale) {
readLanguagesFromCLDR();
String result = getDisplayLanguage(locale.getLanguage() + "-" + locale.getCountry(), languageCode); String result = getDisplayLanguage(locale.getLanguage() + "-" + locale.getCountry(), languageCode);
if (result == null) { if (result == null) {
result = getDisplayLanguage(locale.getLanguage(), languageCode); result = getDisplayLanguage(locale.getLanguage(), languageCode);
@ -197,9 +196,16 @@ public final class TLocale implements TCloneable, TSerializable {
return result != null ? result : languageCode; return result != null ? result : languageCode;
} }
@GeneratedBy(LocaleNativeGenerator.class) private static String getDisplayLanguage(String localeName, String language) {
@PluggableDependency(LocaleNativeGenerator.class) if (!CLDRHelper.getLanguagesMap().has(localeName)) {
private static native String getDisplayLanguage(String localeName, String country); return null;
}
ResourceMap<StringResource> languages = CLDRHelper.getLanguagesMap().get(localeName);
if (!languages.has(language)) {
return null;
}
return languages.get(language).getValue();
}
public final String getDisplayName() { public final String getDisplayName() {
return getDisplayName(getDefault()); return getDisplayName(getDefault());

View File

@ -22,7 +22,9 @@ import org.teavm.platform.metadata.Resource;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
final class ResourceAccessor { 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); public static native void put(Object obj, String propertyName, Object elem);

View File

@ -32,6 +32,7 @@ class ResourceAccessorGenerator implements Injector {
public void generate(InjectorContext context, MethodReference methodRef) throws IOException { public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) { switch (methodRef.getName()) {
case "get": case "get":
case "getProperty":
if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) { if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) {
context.writeExpr(context.getArgument(0)); context.writeExpr(context.getArgument(0));
context.getWriter().append('['); context.getWriter().append('[');

View File

@ -166,7 +166,7 @@ class ResourceProgramTransformer {
instructions.add(nameInsn); instructions.add(nameInsn);
InvokeInstruction accessorInvoke = new InvokeInstruction(); InvokeInstruction accessorInvoke = new InvokeInstruction();
accessorInvoke.setType(InvocationType.SPECIAL); accessorInvoke.setType(InvocationType.SPECIAL);
accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "get", accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "getProperty",
Object.class, String.class, Object.class)); Object.class, String.class, Object.class));
accessorInvoke.getArguments().add(insn.getInstance()); accessorInvoke.getArguments().add(insn.getInstance());
accessorInvoke.getArguments().add(nameVar); accessorInvoke.getArguments().add(nameVar);