From 5770df541316d24bbaf5388a4e44fb184ff33121 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Tue, 20 May 2014 14:14:38 +0400 Subject: [PATCH] Further implementation of localized Calendar --- .../org/teavm/classlib/impl/JCLPlugin.java | 4 +- .../classlib/impl/unicode/CLDRHelper.java | 12 ++++ .../unicode/CLDRHelperNativeGenerator.java | 51 ++++++++++++++ .../java/util/CalendarNativeGenerator.java | 14 ++++ .../java/util/LocaleNativeGenerator.java | 11 +-- .../util/LocaleSettingsNativeGenerator.java | 70 ++++++++++++++++--- .../teavm/classlib/java/util/TCalendar.java | 43 +++++++++--- .../org/teavm/classlib/java/util/TLocale.java | 12 +++- 8 files changed, 192 insertions(+), 25 deletions(-) create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java 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 46a2f9b7a..57d446888 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 @@ -44,7 +44,9 @@ public class JCLPlugin implements TeaVMPlugin { host.add(javacSupport); Generator localeGen = new LocaleSettingsNativeGenerator(host.getClassLoader(), host.getProperties()); - host.add(new MethodReference("java.util.Locale", "readCLDR", ValueType.VOID), localeGen); + 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", "readWeeksFromCLDR", ValueType.VOID), localeGen); host.add(new MethodReference("java.util.Locale", "getDefaultLocale", ValueType.object("java.lang.String")), localeGen); } 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 d50257933..63dcf7c29 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 @@ -23,4 +23,16 @@ public class CLDRHelper { public static String getCode(String language, String country) { return !country.isEmpty() ? language + "-" + country : language; } + + public static String getLikelySubtags(String localeCode) { + readLikelySubtagsFromCLDR(); + String subtags = getLikelySubtagsImpl(localeCode); + return subtags != null ? subtags : localeCode; + } + + // Defined by JCLPlugin + private static native void readLikelySubtagsFromCLDR(); + + // TODO: implement using CLDR + private static native String getLikelySubtagsImpl(String localeCode); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java new file mode 100644 index 000000000..e91a15a95 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java @@ -0,0 +1,51 @@ +/* + * 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 org.teavm.codegen.SourceWriter; +import org.teavm.dependency.DependencyChecker; +import org.teavm.dependency.DependencyPlugin; +import org.teavm.dependency.MethodDependency; +import org.teavm.javascript.ni.Generator; +import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class CLDRHelperNativeGenerator implements Generator, DependencyPlugin { + @Override + public void methodAchieved(DependencyChecker checker, MethodDependency method) { + switch (method.getMethod().getName()) { + case "getLikelySubtagsImpl": + method.getResult().propagate("java.lang.String"); + break; + } + } + + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { + switch (methodRef.getName()) { + case "getLikelySubtagsImpl": + writer.append("var data = ").appendClass("java.util.Locale").append(".$CLDR.likelySubtags[$rt_ustr(") + .append(context.getParameterName(1)).append(")];").softNewLine(); + writer.append("return data ? $rt_str(data) : null;").softNewLine(); + break; + } + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/CalendarNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/CalendarNativeGenerator.java index 4a74b3249..78ea92407 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/CalendarNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/CalendarNativeGenerator.java @@ -28,6 +28,20 @@ import org.teavm.model.MethodReference; public class CalendarNativeGenerator implements Generator { @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { + switch (methodRef.getName()) { + case "getFirstDayOfWeek": + generateWeekMethod(context, writer, "firstDay"); + break; + case "getMinimalDaysInFirstWeek": + generateWeekMethod(context, writer, "minDays"); + break; + } + } + private void generateWeekMethod(GeneratorContext context, SourceWriter writer, String property) + throws IOException { + writer.append("var result = ").appendClass("java.util.Locale").append(".$CLDR.").append(property) + .append("[$rt_ustr(").append(context.getParameterName(1)).append(")];").softNewLine(); + writer.append("return result ? result : -1;").softNewLine(); } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java index 1a8fec60b..e52f47b24 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java @@ -32,17 +32,20 @@ public class LocaleNativeGenerator implements Generator, DependencyPlugin { @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { + case "prepareCLDR": + writer.appendClass("java.util.Locale").append(".$CLDR = {};").softNewLine(); + break; case "getDisplayCountry": - writer.append("var result = ").appendClass("java.util.Locale").append(".$CLDR[$rt_ustr(") + writer.append("var result = ").appendClass("java.util.Locale").append(".$CLDR.territories[$rt_ustr(") .append(context.getParameterName(1)).append(")];").softNewLine(); - writer.append("result = result ? result.territories[$rt_ustr(") + writer.append("result = result ? result[$rt_ustr(") .append(context.getParameterName(2)).append(")] : undefined;").softNewLine(); writer.append("return result ? $rt_str(result) : null").softNewLine(); break; case "getDisplayLanguage": - writer.append("var result = ").appendClass("java.util.Locale").append(".$CLDR[$rt_ustr(") + writer.append("var result = ").appendClass("java.util.Locale").append(".$CLDR.languages[$rt_ustr(") .append(context.getParameterName(1)).append(")];").softNewLine(); - writer.append("result = result ? result.languages[$rt_ustr(") + writer.append("result = result ? result[$rt_ustr(") .append(context.getParameterName(2)).append(")] : undefined;").softNewLine(); writer.append("return result ? $rt_str(result) : null;").softNewLine(); break; 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 68fd00c1b..6a5c3b664 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 @@ -181,8 +181,14 @@ public class LocaleSettingsNativeGenerator implements Generator { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { init(); switch (methodRef.getName()) { - case "readCLDR": - generateReadCLDR(writer); + case "readLanguagesFromCLDR": + generateReadLanguagesFromCLDR(writer); + break; + case "readCountriesFromCLDR": + generateReadCountriesFromCLDR(writer); + break; + case "readWeeksFromCLDR": + generateReadWeeksFromCDLR(writer); break; case "getDefaultLocale": generateGetDefaultLocale(writer); @@ -190,11 +196,16 @@ public class LocaleSettingsNativeGenerator implements Generator { } } - private void generateReadCLDR(SourceWriter writer) throws IOException { - writer.append("if (").appendClass("java.util.Locale").append("$CLDR").append("{").indent().softNewLine(); + private void generateDefender(SourceWriter writer, String property) throws IOException { + writer.append("if (").appendClass("java.util.Locale").append(".$CLDR.").append(property) + .append(")").ws().append("{").indent().softNewLine(); writer.append("return;").softNewLine(); writer.outdent().append("}").softNewLine(); - writer.appendClass("java.util.Locale").append(".$CLDR = {").indent().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) { @@ -204,7 +215,6 @@ public class LocaleSettingsNativeGenerator implements Generator { writer.append('"').append(Renderer.escapeString(entry.getKey())).append('"').ws().append(":").ws() .append('{').indent().softNewLine(); - writer.append("\"languages\"").ws().append(':').ws().append('{').indent().softNewLine(); boolean first = true; for (Map.Entry langEntry : entry.getValue().languages.entrySet()) { if (!first) { @@ -214,10 +224,24 @@ public class LocaleSettingsNativeGenerator implements Generator { writer.append('"').append(Renderer.escapeString(langEntry.getKey())).append('"').ws().append(':') .ws().append('"').append(Renderer.escapeString(langEntry.getValue())).append('"'); } - writer.outdent().append("},").softNewLine(); + writer.outdent().append('}'); + } + writer.outdent().append("}").softNewLine(); + } - writer.append("\"territories\"").ws().append(':').ws().append('{').indent().softNewLine(); - first = true; + private void generateReadCountriesFromCLDR(SourceWriter writer) throws IOException { + generateDefender(writer, "territories"); + writer.appendClass("java.util.Locale").append(".$CLDR.territories = {").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().territories.entrySet()) { if (!first) { writer.append(',').softNewLine(); @@ -226,13 +250,39 @@ public class LocaleSettingsNativeGenerator implements Generator { writer.append('"').append(Renderer.escapeString(langEntry.getKey())).append('"').ws().append(':') .ws().append('"').append(Renderer.escapeString(langEntry.getValue())).append('"'); } - writer.outdent().append('}'); writer.outdent().append('}'); } writer.outdent().append("}").softNewLine(); } + private void generateReadWeeksFromCDLR(SourceWriter writer) throws IOException { + generateDefender(writer, "minDays"); + writer.appendClass("java.util.Locale").append(".$CLDR.minDays = {").indent().softNewLine(); + boolean firstLocale = true; + for (Map.Entry entry : minDaysMap.entrySet()) { + if (!firstLocale) { + writer.append(",").softNewLine(); + } + firstLocale = false; + writer.append('"').append(Renderer.escapeString(entry.getKey())).append('"').ws().append(':') + .ws().append('"').append(entry.getValue()).append('"'); + } + writer.outdent().append("}").softNewLine(); + + writer.appendClass("java.util.Locale").append(".$CLDR.firstDay = {").indent().softNewLine(); + firstLocale = true; + for (Map.Entry entry : firstDayMap.entrySet()) { + if (!firstLocale) { + writer.append(",").softNewLine(); + } + firstLocale = false; + writer.append('"').append(Renderer.escapeString(entry.getKey())).append('"').ws().append(':') + .ws().append('"').append(entry.getValue()).append('"'); + } + writer.outdent().append("}").softNewLine(); + } + private void generateGetDefaultLocale(SourceWriter writer) throws IOException { String locale = properties.getProperty("java.util.Locale.default", "en_EN"); writer.append("return $rt_str(\"").append(Renderer.escapeString(locale)).append("\");").softNewLine(); 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 41acbb2ec..5b499dceb 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 @@ -36,6 +36,7 @@ import org.teavm.classlib.impl.unicode.CLDRHelper; import org.teavm.classlib.java.io.TSerializable; import org.teavm.classlib.java.lang.TCloneable; import org.teavm.classlib.java.lang.TComparable; +import org.teavm.javascript.ni.GeneratedBy; public abstract class TCalendar implements TSerializable, TCloneable, TComparable { protected boolean areFieldsSet; @@ -140,7 +141,6 @@ public abstract class TCalendar implements TSerializable, TCloneable, TComparabl public static final int PM = 1; - @SuppressWarnings("nls") private static String[] fieldNames = { "ERA=", "YEAR=", "MONTH=", "WEEK_OF_YEAR=", "WEEK_OF_MONTH=", "DAY_OF_MONTH=", "DAY_OF_YEAR=", "DAY_OF_WEEK=", "DAY_OF_WEEK_IN_MONTH=", "AM_PM=", "HOUR=", "HOUR_OF_DAY", "MINUTE=", "SECOND=", "MILLISECOND=", "ZONE_OFFSET=", "DST_OFFSET=" }; @@ -154,19 +154,46 @@ public abstract class TCalendar implements TSerializable, TCloneable, TComparabl isSet = new boolean[FIELD_COUNT]; areFieldsSet = isTimeSet = false; setLenient(true); - initLocales(); - String localeCode = CLDRHelper.getCode(locale.getLanguage(), locale.getCountry()); - setFirstDayOfWeek(getFirstDayOfWeek(localeCode)); - setMinimalDaysInFirstWeek(getMinimalDaysInFirstWeek(localeCode)); + readWeeksFromCDLR(); + setFirstDayOfWeek(resolveFirstDayOfWeek(locale)); + setMinimalDaysInFirstWeek(resolveMinimalDaysInFirstWeek(locale)); } // Generated by JCLPlugin - private static native void initLocales(); + private static native void readWeeksFromCDLR(); - // TODO: implement using CLDR + 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(subtags.lastIndexOf('-') + 1) : ""; + } + return country; + } + + private static int resolveFirstDayOfWeek(TLocale locale) { + String country = resolveCountry(locale); + int day = getFirstDayOfWeek(country); + if (day < 0) { + day = getFirstDayOfWeek("001"); + } + return day; + } + + @GeneratedBy(LocaleSettingsNativeGenerator.class) private static native int getFirstDayOfWeek(String localeCode); - // TODO: implement using CLDR + private static int resolveMinimalDaysInFirstWeek(TLocale locale) { + String country = resolveCountry(locale); + int days = getMinimalDaysInFirstWeek(country); + if (days < 0) { + days = getMinimalDaysInFirstWeek("001"); + } + return days; + } + + @GeneratedBy(LocaleSettingsNativeGenerator.class) private static native int getMinimalDaysInFirstWeek(String localeCode); abstract public void add(int field, int value); 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 18625a98d..17ef12eaa 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 @@ -68,7 +68,7 @@ public final class TLocale implements TCloneable, TSerializable { String localeName = getDefaultLocale(); int countryIndex = localeName.indexOf('_'); defaultLocale = new TLocale(localeName.substring(0, countryIndex), localeName.substring(countryIndex) + 1, ""); - readCLDR(); + prepareCLDR(); } private transient String countryCode; @@ -79,8 +79,14 @@ public final class TLocale implements TCloneable, TSerializable { @PluggableDependency(LocaleNativeGenerator.class) private static native String getDefaultLocale(); + @GeneratedBy(LocaleNativeGenerator.class) + private static native void prepareCLDR(); + // Redefined by JCLPlugin - private static native void readCLDR(); + private static native void readCountriesFromCLDR(); + + // Redefined by JCLPlugin + private static native void readLanguagesFromCLDR(); public TLocale(String language) { this(language, "", ""); @@ -162,6 +168,7 @@ public final class TLocale implements TCloneable, TSerializable { } public String getDisplayCountry(TLocale locale) { + readCountriesFromCLDR(); String result = getDisplayCountry(locale.getLanguage() + "-" + locale.getCountry(), countryCode); if (result == null) { result = getDisplayCountry(locale.getLanguage(), countryCode); @@ -178,6 +185,7 @@ 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);