ICU-20255 revert to reflection for methods not yet in Android API level 21..23
(cherry picked from commit cee3c150ab648b6cee46fb377598c157824d3a61)
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/JavaTimeZone.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/JavaTimeZone.java
index b078c78..e71e31f 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/JavaTimeZone.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/JavaTimeZone.java
@@ -10,6 +10,8 @@
import java.io.IOException;
import java.io.ObjectInputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.Date;
import java.util.TreeSet;
@@ -33,6 +35,7 @@ public class JavaTimeZone extends TimeZone {
private java.util.TimeZone javatz;
private transient java.util.Calendar javacal;
+ private static Method mObservesDaylightTime;
static {
AVAILABLESET = new TreeSet<>();
@@ -40,6 +43,14 @@ public class JavaTimeZone extends TimeZone {
for (int i = 0; i < availableIds.length; i++) {
AVAILABLESET.add(availableIds[i]);
}
+
+ try {
+ mObservesDaylightTime = java.util.TimeZone.class.getMethod("observesDaylightTime", (Class[]) null);
+ } catch (NoSuchMethodException e) {
+ // Android API level 21..23
+ } catch (SecurityException e) {
+ // not visible
+ }
}
/**
@@ -187,7 +198,17 @@ public boolean useDaylightTime() {
*/
@Override
public boolean observesDaylightTime() {
- return javatz.observesDaylightTime();
+ if (mObservesDaylightTime != null) {
+ // Java 7+, Android API level 24+
+ // https://developer.android.com/reference/java/util/TimeZone
+ try {
+ return (Boolean)mObservesDaylightTime.invoke(javatz, (Object[]) null);
+ } catch (IllegalAccessException e) {
+ } catch (IllegalArgumentException e) {
+ } catch (InvocationTargetException e) {
+ }
+ }
+ return super.observesDaylightTime();
}
/* (non-Javadoc)
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 11bf41d..f650784 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,6 +10,8 @@
package com.ibm.icu.util;
import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.Iterator;
import java.util.List;
@@ -548,14 +550,20 @@ public Locale toLocale() {
static {
defaultULocale = forLocale(defaultLocale);
- // On JRE 7+, Locale.getDefault() should reflect the
- // property value to the Locale's default. So ICU just relies on
- // Locale.getDefault().
-
- for (Category cat: Category.values()) {
- int idx = cat.ordinal();
- defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat);
- defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]);
+ if (JDKLocaleHelper.hasLocaleCategories()) {
+ for (Category cat: Category.values()) {
+ int idx = cat.ordinal();
+ defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat);
+ defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]);
+ }
+ } else {
+ // Android API level 21..23 does not have separate category locales,
+ // use the non-category default for all.
+ for (Category cat: Category.values()) {
+ int idx = cat.ordinal();
+ defaultCategoryLocales[idx] = defaultLocale;
+ defaultCategoryULocales[idx] = defaultULocale;
+ }
}
}
@@ -585,7 +593,17 @@ public static ULocale getDefault() {
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
+ // Java 7's behavior on Android API level 21..23.
+ for (Category cat : Category.values()) {
+ int idx = cat.ordinal();
+ defaultCategoryLocales[idx] = currentDefault;
+ defaultCategoryULocales[idx] = forLocale(currentDefault);
+ }
+ } }
return defaultULocale;
}
}
@@ -633,13 +651,40 @@ public static ULocale getDefault(Category category) {
// 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 Android API level 21..23
+ // ICU4J checks if the default locale has changed and update
+ // category ULocales here if necessary.
- Locale currentCategoryDefault = JDKLocaleHelper.getDefault(category);
- if (!defaultCategoryLocales[idx].equals(currentCategoryDefault)) {
- defaultCategoryLocales[idx] = currentCategoryDefault;
- defaultCategoryULocales[idx] = forLocale(currentCategoryDefault);
+ // 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 Android API level 21..23
+ // there is no good way to detect the event, ICU4J simply
+ // checks 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 Android API level 21..23.
}
-
return defaultCategoryULocales[idx];
}
}
@@ -3955,10 +4000,65 @@ private LocaleExtensions extensions() {
* JDK Locale Helper
*/
private static final class JDKLocaleHelper {
+ // Java 7 has java.util.Locale.Category.
+ // Android API level 21..23 do not yet have it; only API level 24 (Nougat) adds it.
+ // https://developer.android.com/reference/java/util/Locale.Category
+ private static boolean hasLocaleCategories = false;
+
+ private static Method mGetDefault;
+ private static Method mSetDefault;
+ private static Object eDISPLAY;
+ private static Object eFORMAT;
+
+ static {
+ do {
+ 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) {
String language = loc.getLanguage();
String script = "";
@@ -4123,39 +4223,53 @@ public static Locale toLocale(ULocale uloc) {
}
public static Locale getDefault(Category category) {
- java.util.Locale.Category cat = null;
- if (category != null) {
+ if (hasLocaleCategories) {
+ Object cat = null;
switch (category) {
case DISPLAY:
- cat = java.util.Locale.Category.DISPLAY;
+ cat = eDISPLAY;
break;
case FORMAT:
- cat = java.util.Locale.Category.FORMAT;
+ cat = eFORMAT;
break;
}
- }
- if (cat != null) {
- return Locale.getDefault(cat);
+ if (cat != null) {
+ try {
+ return (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 Locale.getDefault();
}
public static void setDefault(Category category, Locale newLocale) {
- java.util.Locale.Category cat = null;
- if (category != null) {
+ if (hasLocaleCategories) {
+ Object cat = null;
switch (category) {
case DISPLAY:
- cat = java.util.Locale.Category.DISPLAY;
+ cat = eDISPLAY;
break;
case FORMAT:
- cat = java.util.Locale.Category.FORMAT;
+ cat = eFORMAT;
break;
}
- }
- if (cat != null) {
- Locale.setDefault(cat, newLocale);
- } else {
- Locale.setDefault(newLocale);
+ 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
+ }
+ }
}
}
}