From 4159176aa776a6647a173cadf1ca3c442218ed2a Mon Sep 17 00:00:00 2001 From: Yoshito Umaoka Date: Sat, 28 Jul 2018 01:14:46 -0400 Subject: [PATCH] ICU-13413 Updated ULocale class to directly calls Java 7 Locale methods Previously, ULocale had to work on Java 6, therefore, Java 7 specific Locale methods needed to be invoked by reflection. For the next release, we can directly call Java 7 Locale APIs, so rather complicated reflection implementation were replaced with simple API calls. --- .../core/src/com/ibm/icu/util/ULocale.java | 435 +++--------------- .../.settings/org.eclipse.jdt.core.prefs | 7 +- 2 files changed, 69 insertions(+), 373 deletions(-) diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/ULocale.java b/icu4j/main/classes/core/src/com/ibm/icu/util/ULocale.java index 4d5a31f7ac6..2ec08ff73a9 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/ULocale.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/ULocale.java @@ -10,11 +10,6 @@ package com.ibm.icu.util; import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.security.AccessControlException; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.text.ParseException; import java.util.Iterator; import java.util.List; @@ -178,8 +173,8 @@ public final class ULocale implements Serializable, Comparable { // JRE 6 or older does not. JDK's Locale.SIMPLIFIED_CHINESE is actually // zh_CN, not zh_Hans. This is same in Java 7 or later versions. // - // ULocale#toLocale() implementation uses Java reflection to create a Locale - // with a script tag. When a new ULocale is constructed with the single arg + // ULocale#toLocale() implementation create a Locale with a script tag. + // When a new ULocale is constructed with the single arg // constructor, the volatile field 'Locale locale' is initialized by // #toLocale() method. // @@ -553,45 +548,14 @@ public final class ULocale implements Serializable, Comparable { static { defaultULocale = forLocale(defaultLocale); - // For Java 6 or older JRE, ICU initializes the default script from - // "user.script" system property. The system property was added - // in Java 7. On JRE 7, Locale.getDefault() should reflect the + // On JRE 7+, Locale.getDefault() should reflect the // property value to the Locale's default. So ICU just relies on // Locale.getDefault(). - // Note: The "user.script" property is only used by initialization. - // - if (JDKLocaleHelper.hasLocaleCategories()) { - for (Category cat: Category.values()) { - int idx = cat.ordinal(); - defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat); - defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]); - } - } else { - // Make sure the current default Locale is original. - // If not, it means that someone updated the default Locale. - // In this case, user.XXX properties are already out of date - // and we should not use user.script. - if (JDKLocaleHelper.isOriginalDefaultLocale(defaultLocale)) { - // Use "user.script" if available - String userScript = JDKLocaleHelper.getSystemProperty("user.script"); - if (userScript != null && LanguageTag.isScript(userScript)) { - // Note: Builder or forLanguageTag cannot be used here - // when one of Locale fields is not well-formed. - BaseLocale base = defaultULocale.base(); - BaseLocale newBase = BaseLocale.getInstance(base.getLanguage(), userScript, - base.getRegion(), base.getVariant()); - defaultULocale = getInstance(newBase, defaultULocale.extensions()); - } - } - - // Java 6 or older does not have separated category locales, - // use the non-category default for all - for (Category cat: Category.values()) { - int idx = cat.ordinal(); - defaultCategoryLocales[idx] = defaultLocale; - defaultCategoryULocales[idx] = defaultULocale; - } + for (Category cat: Category.values()) { + int idx = cat.ordinal(); + defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat); + defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]); } } @@ -600,21 +564,6 @@ public final class ULocale implements Serializable, Comparable { *

* The default ULocale is synchronized to the default Java Locale. This method checks * the current default Java Locale and returns an equivalent ULocale. - *

- * Note: Before Java 7, the {@link java.util.Locale} was not able to represent a - * locale's script. Therefore, the script field in the default ULocale is always empty unless - * a ULocale with non-empty script is explicitly set by {@link #setDefault(ULocale)} - * on Java 6 or older systems. - *

- * Note for ICU 49 or later: Some JRE implementations allow users to override the default - * {@link java.util.Locale} using system properties - user.language, - * user.country and user.variant. In addition to these system - * properties, some Java 7 implementations support user.script for overriding the - * default Locale's script. - * ICU 49 and later versions use the user.script system property on Java 6 - * or older systems supporting other user.* system properties to initialize - * the default ULocale. The user.script override for default ULocale is not - * used on Java 7, or if the current Java default Locale is changed after start up. * * @return the default ULocale. * @stable ICU 2.8 @@ -636,17 +585,6 @@ public final class ULocale implements Serializable, Comparable { if (!defaultLocale.equals(currentDefault)) { defaultLocale = currentDefault; defaultULocale = forLocale(currentDefault); - - if (!JDKLocaleHelper.hasLocaleCategories()) { - // Detected Java default Locale change. - // We need to update category defaults to match the - // Java 7's behavior on Java 6 or older environment. - for (Category cat : Category.values()) { - int idx = cat.ordinal(); - defaultCategoryLocales[idx] = currentDefault; - defaultCategoryULocales[idx] = forLocale(currentDefault); - } - } } return defaultULocale; } @@ -695,40 +633,13 @@ public final class ULocale implements Serializable, Comparable { // cyclic dependency for category default. return ULocale.ROOT; } - if (JDKLocaleHelper.hasLocaleCategories()) { - Locale currentCategoryDefault = JDKLocaleHelper.getDefault(category); - if (!defaultCategoryLocales[idx].equals(currentCategoryDefault)) { - defaultCategoryLocales[idx] = currentCategoryDefault; - defaultCategoryULocales[idx] = forLocale(currentCategoryDefault); - } - } else { - // java.util.Locale.setDefault(Locale) in Java 7 updates - // category locale defaults. On Java 6 or older environment, - // ICU4J checks if the default locale has changed and update - // category ULocales here if necessary. - // Note: When java.util.Locale.setDefault(Locale) is called - // with a Locale same with the previous one, Java 7 still - // updates category locale defaults. On Java 6 or older env, - // there is no good way to detect the event, ICU4J simply - // check if the default Java Locale has changed since last - // time. - - Locale currentDefault = Locale.getDefault(); - if (!defaultLocale.equals(currentDefault)) { - defaultLocale = currentDefault; - defaultULocale = forLocale(currentDefault); - - for (Category cat : Category.values()) { - int tmpIdx = cat.ordinal(); - defaultCategoryLocales[tmpIdx] = currentDefault; - defaultCategoryULocales[tmpIdx] = forLocale(currentDefault); - } - } - - // No synchronization with JDK Locale, because category default - // is not supported in Java 6 or older versions + Locale currentCategoryDefault = JDKLocaleHelper.getDefault(category); + if (!defaultCategoryLocales[idx].equals(currentCategoryDefault)) { + defaultCategoryLocales[idx] = currentCategoryDefault; + defaultCategoryULocales[idx] = forLocale(currentCategoryDefault); } + return defaultCategoryULocales[idx]; } } @@ -2176,7 +2087,7 @@ public final class ULocale implements Serializable, Comparable { // parse out the acceptLanguage into an array TreeMap map = - new TreeMap(); + new TreeMap<>(); StringBuilder languageRangeBuf = new StringBuilder(); StringBuilder qvalBuf = new StringBuilder(); int state = 0; @@ -3921,7 +3832,7 @@ public final class ULocale implements Serializable, Comparable { // other extensions are at the same level. // e.g. @a=ext-for-aa;calendar=japanese;m=ext-for-mm;x=priv-use - TreeMap kwds = new TreeMap(); + TreeMap kwds = new TreeMap<>(); for (Character key : extKeys) { Extension ext = exts.getExtension(key); if (ext instanceof UnicodeLocaleExtension) { @@ -4042,114 +3953,11 @@ public final class ULocale implements Serializable, Comparable { * JDK Locale Helper */ private static final class JDKLocaleHelper { - private static boolean hasScriptsAndUnicodeExtensions = false; - private static boolean hasLocaleCategories = false; - - /* - * New methods in Java 7 Locale class - */ - private static Method mGetScript; - private static Method mGetExtensionKeys; - private static Method mGetExtension; - private static Method mGetUnicodeLocaleKeys; - private static Method mGetUnicodeLocaleAttributes; - private static Method mGetUnicodeLocaleType; - private static Method mForLanguageTag; - - private static Method mGetDefault; - private static Method mSetDefault; - private static Object eDISPLAY; - private static Object eFORMAT; - - /* - * This table is used for mapping between ICU and special Java - * 6 locales. When an ICU locale matches with - * /, the ICU locale is mapped to locale. - * For example, both ja_JP@calendar=japanese and ja@calendar=japanese - * are mapped to Java locale "ja_JP_JP". ICU locale "nn" is mapped - * to Java locale "no_NO_NY". - */ - private static final String[][] JAVA6_MAPDATA = { - // { , , , , - { "ja_JP_JP", "ja_JP", "calendar", "japanese", "ja"}, - { "no_NO_NY", "nn_NO", null, null, "nn"}, - { "th_TH_TH", "th_TH", "numbers", "thai", "th"}, - }; - - static { - do { - try { - mGetScript = Locale.class.getMethod("getScript", (Class[]) null); - mGetExtensionKeys = Locale.class.getMethod("getExtensionKeys", (Class[]) null); - mGetExtension = Locale.class.getMethod("getExtension", char.class); - mGetUnicodeLocaleKeys = Locale.class.getMethod("getUnicodeLocaleKeys", (Class[]) null); - mGetUnicodeLocaleAttributes = Locale.class.getMethod("getUnicodeLocaleAttributes", (Class[]) null); - mGetUnicodeLocaleType = Locale.class.getMethod("getUnicodeLocaleType", String.class); - mForLanguageTag = Locale.class.getMethod("forLanguageTag", String.class); - - hasScriptsAndUnicodeExtensions = true; - } catch (NoSuchMethodException e) { - } catch (IllegalArgumentException e) { - } catch (SecurityException e) { - // TODO : report? - } - - try { - Class cCategory = null; - Class[] classes = Locale.class.getDeclaredClasses(); - for (Class c : classes) { - if (c.getName().equals("java.util.Locale$Category")) { - cCategory = c; - break; - } - } - if (cCategory == null) { - break; - } - mGetDefault = Locale.class.getDeclaredMethod("getDefault", cCategory); - mSetDefault = Locale.class.getDeclaredMethod("setDefault", cCategory, Locale.class); - - Method mName = cCategory.getMethod("name", (Class[]) null); - Object[] enumConstants = cCategory.getEnumConstants(); - for (Object e : enumConstants) { - String catVal = (String)mName.invoke(e, (Object[])null); - if (catVal.equals("DISPLAY")) { - eDISPLAY = e; - } else if (catVal.equals("FORMAT")) { - eFORMAT = e; - } - } - if (eDISPLAY == null || eFORMAT == null) { - break; - } - - hasLocaleCategories = true; - } catch (NoSuchMethodException e) { - } catch (IllegalArgumentException e) { - } catch (IllegalAccessException e) { - } catch (InvocationTargetException e) { - } catch (SecurityException e) { - // TODO : report? - } - } while (false); - } private JDKLocaleHelper() { } - public static boolean hasLocaleCategories() { - return hasLocaleCategories; - } - public static ULocale toULocale(Locale loc) { - return hasScriptsAndUnicodeExtensions ? toULocale7(loc) : toULocale6(loc); - } - - public static Locale toLocale(ULocale uloc) { - return hasScriptsAndUnicodeExtensions ? toLocale7(uloc) : toLocale6(uloc); - } - - private static ULocale toULocale7(Locale loc) { String language = loc.getLanguage(); String script = ""; String country = loc.getCountry(); @@ -4158,57 +3966,49 @@ public final class ULocale implements Serializable, Comparable { Set attributes = null; Map keywords = null; - try { - script = (String) mGetScript.invoke(loc, (Object[]) null); - @SuppressWarnings("unchecked") - Set extKeys = (Set) mGetExtensionKeys.invoke(loc, (Object[]) null); - if (!extKeys.isEmpty()) { - for (Character extKey : extKeys) { - if (extKey.charValue() == 'u') { - // Found Unicode locale extension + script = loc.getScript(); + Set extKeys = loc.getExtensionKeys(); + if (!extKeys.isEmpty()) { + for (Character extKey : extKeys) { + if (extKey.charValue() == 'u') { + // Found Unicode locale extension - // attributes - @SuppressWarnings("unchecked") - Set uAttributes = (Set) mGetUnicodeLocaleAttributes.invoke(loc, (Object[]) null); - if (!uAttributes.isEmpty()) { - attributes = new TreeSet(); - for (String attr : uAttributes) { - attributes.add(attr); - } + // attributes + @SuppressWarnings("unchecked") + Set uAttributes = loc.getUnicodeLocaleAttributes(); + if (!uAttributes.isEmpty()) { + attributes = new TreeSet<>(); + for (String attr : uAttributes) { + attributes.add(attr); } + } - // keywords - @SuppressWarnings("unchecked") - Set uKeys = (Set) mGetUnicodeLocaleKeys.invoke(loc, (Object[]) null); - for (String kwKey : uKeys) { - String kwVal = (String) mGetUnicodeLocaleType.invoke(loc, kwKey); - if (kwVal != null) { - if (kwKey.equals("va")) { - // va-* is interpreted as a variant - variant = (variant.length() == 0) ? kwVal : kwVal + "_" + variant; - } else { - if (keywords == null) { - keywords = new TreeMap(); - } - keywords.put(kwKey, kwVal); + // keywords + Set uKeys = loc.getUnicodeLocaleKeys(); + for (String kwKey : uKeys) { + String kwVal = loc.getUnicodeLocaleType(kwKey); + if (kwVal != null) { + if (kwKey.equals("va")) { + // va-* is interpreted as a variant + variant = (variant.length() == 0) ? kwVal : kwVal + "_" + variant; + } else { + if (keywords == null) { + keywords = new TreeMap<>(); } + keywords.put(kwKey, kwVal); } } - } else { - String extVal = (String) mGetExtension.invoke(loc, extKey); - if (extVal != null) { - if (keywords == null) { - keywords = new TreeMap(); - } - keywords.put(String.valueOf(extKey), extVal); + } + } else { + String extVal = loc.getExtension(extKey); + if (extVal != null) { + if (keywords == null) { + keywords = new TreeMap<>(); } + keywords.put(String.valueOf(extKey), extVal); } } } - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); } // JDK locale no_NO_NY is not interpreted as Nynorsk by ICU, @@ -4253,7 +4053,7 @@ public final class ULocale implements Serializable, Comparable { attrBuf.append(attr); } if (keywords == null) { - keywords = new TreeMap(); + keywords = new TreeMap<>(); } keywords.put(LOCALE_ATTRIBUTE_KEY, attrBuf.toString()); } @@ -4286,26 +4086,7 @@ public final class ULocale implements Serializable, Comparable { return new ULocale(getName(buf.toString()), loc); } - private static ULocale toULocale6(Locale loc) { - ULocale uloc = null; - String locStr = loc.toString(); - if (locStr.length() == 0) { - uloc = ULocale.ROOT; - } else { - for (int i = 0; i < JAVA6_MAPDATA.length; i++) { - if (JAVA6_MAPDATA[i][0].equals(locStr)) { - LocaleIDParser p = new LocaleIDParser(JAVA6_MAPDATA[i][1]); - p.setKeywordValue(JAVA6_MAPDATA[i][2], JAVA6_MAPDATA[i][3]); - locStr = p.getName(); - break; - } - } - uloc = new ULocale(getName(locStr), loc); - } - return uloc; - } - - private static Locale toLocale7(ULocale uloc) { + public static Locale toLocale(ULocale uloc) { Locale loc = null; String ulocStr = uloc.getName(); if (uloc.getScript().length() > 0 || ulocStr.contains("@")) { @@ -4329,14 +4110,7 @@ public final class ULocale implements Serializable, Comparable { // for variant. Because ICU always normalizes variant to // upper case, we convert language tag to upper case here. tag = AsciiUtil.toUpperString(tag); - - try { - loc = (Locale)mForLanguageTag.invoke(null, tag); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } + loc = Locale.forLanguageTag(tag); } if (loc == null) { // Without script or keywords, use a Locale constructor, @@ -4346,120 +4120,41 @@ public final class ULocale implements Serializable, Comparable { return loc; } - private static Locale toLocale6(ULocale uloc) { - String locstr = uloc.getBaseName(); - for (int i = 0; i < JAVA6_MAPDATA.length; i++) { - if (locstr.equals(JAVA6_MAPDATA[i][1]) || locstr.equals(JAVA6_MAPDATA[i][4])) { - if (JAVA6_MAPDATA[i][2] != null) { - String val = uloc.getKeywordValue(JAVA6_MAPDATA[i][2]); - if (val != null && val.equals(JAVA6_MAPDATA[i][3])) { - locstr = JAVA6_MAPDATA[i][0]; - break; - } - } else { - locstr = JAVA6_MAPDATA[i][0]; - break; - } - } - } - LocaleIDParser p = new LocaleIDParser(locstr); - String[] names = p.getLanguageScriptCountryVariant(); - return new Locale(names[0], names[2], names[3]); - } - public static Locale getDefault(Category category) { - Locale loc = Locale.getDefault(); - if (hasLocaleCategories) { - Object cat = null; + java.util.Locale.Category cat = null; + if (category != null) { switch (category) { case DISPLAY: - cat = eDISPLAY; + cat = java.util.Locale.Category.DISPLAY; break; case FORMAT: - cat = eFORMAT; + cat = java.util.Locale.Category.FORMAT; break; } - if (cat != null) { - try { - loc = (Locale)mGetDefault.invoke(null, cat); - } catch (InvocationTargetException e) { - // fall through - use the base default - } catch (IllegalArgumentException e) { - // fall through - use the base default - } catch (IllegalAccessException e) { - // fall through - use the base default - } - } } - return loc; + if (cat != null) { + return Locale.getDefault(cat); + } + return Locale.getDefault(); } public static void setDefault(Category category, Locale newLocale) { - if (hasLocaleCategories) { - Object cat = null; + java.util.Locale.Category cat = null; + if (category != null) { switch (category) { case DISPLAY: - cat = eDISPLAY; + cat = java.util.Locale.Category.DISPLAY; break; case FORMAT: - cat = eFORMAT; + cat = java.util.Locale.Category.FORMAT; break; } - if (cat != null) { - try { - mSetDefault.invoke(null, cat, newLocale); - } catch (InvocationTargetException e) { - // fall through - no effects - } catch (IllegalArgumentException e) { - // fall through - no effects - } catch (IllegalAccessException e) { - // fall through - no effects - } - } } - } - - // Returns true if the given Locale matches the original - // default locale initialized by JVM by checking user.XXX - // system properties. When the system properties are not accessible, - // this method returns false. - public static boolean isOriginalDefaultLocale(Locale loc) { - if (hasScriptsAndUnicodeExtensions) { - String script = ""; - try { - script = (String) mGetScript.invoke(loc, (Object[]) null); - } catch (Exception e) { - return false; - } - - return loc.getLanguage().equals(getSystemProperty("user.language")) - && loc.getCountry().equals(getSystemProperty("user.country")) - && loc.getVariant().equals(getSystemProperty("user.variant")) - && script.equals(getSystemProperty("user.script")); - } - return loc.getLanguage().equals(getSystemProperty("user.language")) - && loc.getCountry().equals(getSystemProperty("user.country")) - && loc.getVariant().equals(getSystemProperty("user.variant")); - } - - public static String getSystemProperty(String key) { - String val = null; - final String fkey = key; - if (System.getSecurityManager() != null) { - try { - val = AccessController.doPrivileged(new PrivilegedAction() { - @Override - public String run() { - return System.getProperty(fkey); - } - }); - } catch (AccessControlException e) { - // ignore - } + if (cat != null) { + Locale.setDefault(cat, newLocale); } else { - val = System.getProperty(fkey); + Locale.setDefault(newLocale); } - return val; } } } diff --git a/icu4j/tools/build/.settings/org.eclipse.jdt.core.prefs b/icu4j/tools/build/.settings/org.eclipse.jdt.core.prefs index cb972096a48..d91a75926be 100644 --- a/icu4j/tools/build/.settings/org.eclipse.jdt.core.prefs +++ b/icu4j/tools/build/.settings/org.eclipse.jdt.core.prefs @@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -98,7 +98,8 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=ignore org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16