]> granicus.if.org Git - icu/commitdiff
ICU-12576 Updating Java DateTimePatternGenerator to use data sinks for data loading.
authorShane Carr <shane@unicode.org>
Mon, 6 Jun 2016 23:25:13 +0000 (23:25 +0000)
committerShane Carr <shane@unicode.org>
Mon, 6 Jun 2016 23:25:13 +0000 (23:25 +0000)
X-SVN-Rev: 38801

icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateTimeGeneratorTest.java

index 7cb0f15a892474b49a42dd4d1e10f3aee9e3006c..3414f33fb7833ec64a71af51a220dccfe752cffe 100644 (file)
@@ -117,38 +117,139 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
         if (result != null) {
             return result;
         }
+
         result = new DateTimePatternGenerator();
+        result.initData(uLocale);
+
+        // freeze and cache
+        result.freeze();
+        DTPNG_CACHE.put(localeKey, result);
+        return result;
+    }
+
+    private void initData(ULocale uLocale) {
+        // This instance of PatternInfo is required for calling some functions.  It is used for
+        // passing additional information to the caller.  We won't use this extra information, but
+        // we still need to make a temporary instance.
         PatternInfo returnInfo = new PatternInfo();
-        String shortTimePattern = null;
+
+        addCanonicalItems();
+        addICUPatterns(returnInfo, uLocale);
+        addCLDRData(returnInfo, uLocale);
+        setDateTimeFromCalendar(uLocale);
+        setDecimalSymbols(uLocale);
+        getAllowedHourFormats(uLocale);
+    }
+
+    private void addICUPatterns(PatternInfo returnInfo, ULocale uLocale) {
         // first load with the ICU patterns
         for (int i = DateFormat.FULL; i <= DateFormat.SHORT; ++i) {
             SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(i, uLocale);
-            result.addPattern(df.toPattern(), false, returnInfo);
+            addPattern(df.toPattern(), false, returnInfo);
             df = (SimpleDateFormat) DateFormat.getTimeInstance(i, uLocale);
-            result.addPattern(df.toPattern(), false, returnInfo);
+            addPattern(df.toPattern(), false, returnInfo);
+
             if (i == DateFormat.SHORT) {
-                // keep this pattern to populate other time field
-                // combination patterns by hackTimes later in this method.
-                shortTimePattern = df.toPattern();
-
-                // use hour style in SHORT time pattern as the default
-                // hour style for the locale
-                FormatParser fp = new FormatParser();
-                fp.set(shortTimePattern);
-                List<Object> items = fp.getItems();
-                for (int idx = 0; idx < items.size(); idx++) {
-                    Object item = items.get(idx);
-                    if (item instanceof VariableField) {
-                        VariableField fld = (VariableField)item;
-                        if (fld.getType() == HOUR) {
-                            result.defaultHourFormatChar = fld.toString().charAt(0);
-                            break;
-                        }
+                consumeShortTimePattern(df, returnInfo);
+            }
+        }
+    }
+
+    private void consumeShortTimePattern(SimpleDateFormat df, PatternInfo returnInfo) {
+        // keep this pattern to populate other time field
+        // combination patterns by hackTimes later in this method.
+        String shortTimePattern = df.toPattern();
+
+        // use hour style in SHORT time pattern as the default
+        // hour style for the locale
+        FormatParser fp = new FormatParser();
+        fp.set(shortTimePattern);
+        List<Object> items = fp.getItems();
+        for (int idx = 0; idx < items.size(); idx++) {
+            Object item = items.get(idx);
+            if (item instanceof VariableField) {
+                VariableField fld = (VariableField)item;
+                if (fld.getType() == HOUR) {
+                    defaultHourFormatChar = fld.toString().charAt(0);
+                    break;
+                }
+            }
+        }
+
+        // some languages didn't add mm:ss or HH:mm, so put in a hack to compute that from the short time.
+        hackTimes(returnInfo, shortTimePattern);
+    }
+
+    private class AppendItemFormatsSink extends UResource.Sink {
+        @Override
+        public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
+            UResource.Table itemsTable = value.getTable();
+            for (int i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
+                int field = getAppendFormatNumber(key);
+                assert field != -1;
+                if (getAppendItemFormat(field) == null) {
+                    setAppendItemFormat(field, value.toString());
+                }
+            }
+        }
+        public void fillInMissing() {
+            for (int i = 0; i < TYPE_LIMIT; ++i) {
+                if (getAppendItemFormat(i) == null) {
+                    setAppendItemFormat(i, "{0} \u251C{2}: {1}\u2524");
+                }
+            }
+        }
+    }
+
+    private class AppendItemNamesSink extends UResource.Sink {
+        @Override
+        public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
+            UResource.Table itemsTable = value.getTable();
+            for (int i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
+                int field = getCLDRFieldNumber(key);
+                if (field == -1) { continue; }
+                UResource.Table detailsTable = value.getTable();
+                for (int j = 0; detailsTable.getKeyAndValue(j, key, value); ++j) {
+                    if (!key.contentEquals("dn")) continue;
+                    if (getAppendItemName(field) == null) {
+                        setAppendItemName(field, value.toString());
                     }
+                    break;
+                }
+            }
+        }
+        public void fillInMissing() {
+            for (int i = 0; i < TYPE_LIMIT; ++i) {
+                if (getAppendItemName(i) == null) {
+                    setAppendItemName(i, "F" + i);
                 }
             }
         }
+    }
+
+    private class AvailableFormatsSink extends UResource.Sink {
+        PatternInfo returnInfo;
+        public AvailableFormatsSink(PatternInfo returnInfo) {
+            this.returnInfo = returnInfo;
+        }
+
+        @Override
+        public void put(UResource.Key key, UResource.Value value, boolean isRoot) {
+            UResource.Table formatsTable = value.getTable();
+            for (int i = 0; formatsTable.getKeyAndValue(i, key, value); ++i) {
+                String formatKey = key.toString();
+                if (!isAvailableFormatSet(formatKey)) {
+                    setAvailableFormat(formatKey);
+                    // Add pattern with its associated skeleton. Override any duplicate derived from std patterns,
+                    // but not a previous availableFormats entry:
+                    String formatValue = value.toString();
+                    addPatternWithSkeleton(formatValue, formatKey, !isRoot, returnInfo);
+                }
+            }
+        }
+    }
 
+    private void addCLDRData(PatternInfo returnInfo, ULocale uLocale) {
         ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, uLocale);
         // Get the correct calendar type
         String calendarTypeToUse = uLocale.getKeywordValue("calendar");
@@ -168,34 +269,21 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
             //      #9987 resolved the issue of alias table when full path is specified in getWithFallback,
             //      but there is no easy solution when the equivalent operation is done by multiple operations.
             //      This issue is addressed in #9964.
-            ICUResourceBundle itemBundle = rb.getWithFallback("calendar/" + calendarTypeToUse + "/appendItems");
-            for (int i=0; i<itemBundle.getSize(); ++i) {
-                ICUResourceBundle formatBundle = (ICUResourceBundle)itemBundle.get(i);
-                String formatName = itemBundle.get(i).getKey();
-                String value = formatBundle.getString();
-                result.setAppendItemFormat(getAppendFormatNumber(formatName), value);
-            }
+            AppendItemFormatsSink sink = new AppendItemFormatsSink();
+            rb.getAllItemsWithFallback("calendar/" + calendarTypeToUse + "/appendItems", sink);
+            sink.fillInMissing();
         }catch(MissingResourceException e) {
         }
 
         // CLDR item names
         try {
-            ICUResourceBundle itemBundle = rb.getWithFallback("fields");
-            ICUResourceBundle fieldBundle, dnBundle;
-            for (int i=0; i<TYPE_LIMIT; ++i) {
-                if ( isCLDRFieldName(i) ) {
-                    fieldBundle = itemBundle.getWithFallback(CLDR_FIELD_NAME[i]);
-                    dnBundle = fieldBundle.getWithFallback("dn");
-                    String value = dnBundle.getString();
-                    //System.out.println("Field name:"+value);
-                    result.setAppendItemName(i, value);
-                }
-            }
+            AppendItemNamesSink sink = new AppendItemNamesSink();
+            rb.getAllItemsWithFallback("fields", sink);
+            sink.fillInMissing();
         }catch(MissingResourceException e) {
         }
 
         // set the AvailableFormat in CLDR
-        ICUResourceBundle availFormatsBundle = null;
         try {
             //      ICU4J getWithFallback does not work well when
             //      1) A nested table is an alias to /LOCALE/...
@@ -203,63 +291,26 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
             //      #9987 resolved the issue of alias table when full path is specified in getWithFallback,
             //      but there is no easy solution when the equivalent operation is done by multiple operations.
             //      This issue is addressed in #9964.
-            availFormatsBundle = rb.getWithFallback("calendar/" + calendarTypeToUse + "/availableFormats");
+            AvailableFormatsSink sink = new AvailableFormatsSink(returnInfo);
+            rb.getAllItemsWithFallback("calendar/" + calendarTypeToUse + "/availableFormats", sink);
         } catch (MissingResourceException e) {
-            // fall through
-        }
-
-        boolean override = true;
-        while (availFormatsBundle != null) {
-            for (int i = 0; i < availFormatsBundle.getSize(); i++) {
-                String formatKey = availFormatsBundle.get(i).getKey();
-
-                if (!result.isAvailableFormatSet(formatKey)) {
-                    result.setAvailableFormat(formatKey);
-                    // Add pattern with its associated skeleton. Override any duplicate derived from std patterns,
-                    // but not a previous availableFormats entry:
-                    String formatValue = availFormatsBundle.get(i).getString();
-                    result.addPatternWithSkeleton(formatValue, formatKey, override, returnInfo);
-                }
-            }
-
-            ICUResourceBundle pbundle = (ICUResourceBundle)availFormatsBundle.getParent();
-            if (pbundle == null) {
-                break;
-            }
-            try {
-                availFormatsBundle = pbundle.getWithFallback("calendar/" + calendarTypeToUse + "/availableFormats");
-            } catch (MissingResourceException e) {
-                availFormatsBundle = null;
-            }
-            if (availFormatsBundle != null && pbundle.getULocale().getBaseName().equals("root")) {
-                override = false;
-            }
-        }
-
-        // assume it is always big endian (ok for CLDR right now)
-        // some languages didn't add mm:ss or HH:mm, so put in a hack to compute that from the short time.
-        if (shortTimePattern != null) {
-            hackTimes(result, returnInfo, shortTimePattern);
         }
+    }
 
-        result.setDateTimeFormat(Calendar.getDateTimePattern(Calendar.getInstance(uLocale), uLocale, DateFormat.MEDIUM));
+    private void setDateTimeFromCalendar(ULocale uLocale) {
+        String dateTimeFormat = Calendar.getDateTimePattern(Calendar.getInstance(uLocale), uLocale, DateFormat.MEDIUM);
+        setDateTimeFormat(dateTimeFormat);
+    }
 
+    private void setDecimalSymbols(ULocale uLocale) {
         // decimal point for seconds
         DecimalFormatSymbols dfs = new DecimalFormatSymbols(uLocale);
-        result.setDecimal(String.valueOf(dfs.getDecimalSeparator()));
-
-        // List of allowed hour formats
-        result.allowedHourFormats = getAllowedHourFormats(uLocale); // already frozen
-
-        // freeze and cache
-        result.freeze();
-        DTPNG_CACHE.put(localeKey, result);
-        return result;
+        setDecimal(String.valueOf(dfs.getDecimalSeparator()));
     }
 
     private static final String[] LAST_RESORT_ALLOWED_HOUR_FORMAT = {"H"};
 
-    private static String[] getAllowedHourFormats(ULocale uLocale) {
+    private void getAllowedHourFormats(ULocale uLocale) {
         // key can be either region or locale (lang_region)
         //        ZW{
         //            allowed{
@@ -291,7 +342,44 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
                 list = LAST_RESORT_ALLOWED_HOUR_FORMAT;
             }
         }
-        return list;
+        allowedHourFormats = list;
+    }
+
+    private static class DayPeriodAllowedHoursSink extends UResource.Sink {
+        HashMap<String, String[]> tempMap;
+
+        private DayPeriodAllowedHoursSink(HashMap<String, String[]> tempMap) {
+            this.tempMap = tempMap;
+        }
+
+        @Override
+        public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
+            UResource.Table timeData = value.getTable();
+            for (int i = 0; timeData.getKeyAndValue(i, key, value); ++i) {
+                String regionOrLocale = key.toString();
+                UResource.Table formatList = value.getTable();
+                for (int j = 0; formatList.getKeyAndValue(j, key, value); ++j) {
+                    if (key.contentEquals("allowed")) {  // Ignore "preferred" list.
+                        tempMap.put(regionOrLocale, value.getStringArrayOrStringAsArray());
+                    }
+                }
+            }
+        }
+    }
+
+    // Get the data for dayperiod C.
+    static final Map<String, String[]> LOCALE_TO_ALLOWED_HOUR;
+    static {
+        HashMap<String, String[]> temp = new HashMap<String, String[]>();
+        ICUResourceBundle suppData = (ICUResourceBundle)ICUResourceBundle.getBundleInstance(
+                ICUData.ICU_BASE_NAME,
+                "supplementalData",
+                ICUResourceBundle.ICU_DATA_CLASS_LOADER);
+
+        DayPeriodAllowedHoursSink allowedHoursSink = new DayPeriodAllowedHoursSink(temp);
+        suppData.getAllItemsWithFallback("timeData", allowedHoursSink);
+
+        LOCALE_TO_ALLOWED_HOUR = Collections.unmodifiableMap(temp);
     }
 
     /**
@@ -312,16 +400,16 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
         this.defaultHourFormatChar = defaultHourFormatChar;
     }
 
-    private static void hackTimes(DateTimePatternGenerator result, PatternInfo returnInfo, String hackPattern) {
-        result.fp.set(hackPattern);
+    private void hackTimes(PatternInfo returnInfo, String shortTimePattern) {
+        fp.set(shortTimePattern);
         StringBuilder mmss = new StringBuilder();
         // to get mm:ss, we strip all but mm literal ss
         boolean gotMm = false;
-        for (int i = 0; i < result.fp.items.size(); ++i) {
-            Object item = result.fp.items.get(i);
+        for (int i = 0; i < fp.items.size(); ++i) {
+            Object item = fp.items.get(i);
             if (item instanceof String) {
                 if (gotMm) {
-                    mmss.append(result.fp.quoteLiteral(item.toString()));
+                    mmss.append(fp.quoteLiteral(item.toString()));
                 }
             } else {
                 char ch = item.toString().charAt(0);
@@ -333,7 +421,7 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
                         break; // failed
                     }
                     mmss.append(item);
-                    result.addPattern(mmss.toString(), false, returnInfo);
+                    addPattern(mmss.toString(), false, returnInfo);
                     break;
                 } else if (gotMm || ch == 'z' || ch == 'Z' || ch == 'v' || ch == 'V') {
                     break; // failed
@@ -344,8 +432,8 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
         // the easiest way to do this is to mark the stuff we want to nuke, then remove it in a second pass.
         BitSet variables = new BitSet();
         BitSet nuke = new BitSet();
-        for (int i = 0; i < result.fp.items.size(); ++i) {
-            Object item = result.fp.items.get(i);
+        for (int i = 0; i < fp.items.size(); ++i) {
+            Object item = fp.items.get(i);
             if (item instanceof VariableField) {
                 variables.set(i);
                 char ch = item.toString().charAt(0);
@@ -358,8 +446,8 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
                 }
             }
         }
-        String hhmm = getFilteredPattern(result.fp, nuke);
-        result.addPattern(hhmm, false, returnInfo);
+        String hhmm = getFilteredPattern(fp, nuke);
+        addPattern(hhmm, false, returnInfo);
     }
 
     private static String getFilteredPattern(FormatParser fp, BitSet nuke) {
@@ -388,24 +476,22 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
      * @deprecated This API is ICU internal only.
      */
     @Deprecated
-    public static int getAppendFormatNumber(String string) {
+    public static int getAppendFormatNumber(UResource.Key key) {
         for (int i = 0; i < CLDR_FIELD_APPEND.length; ++i) {
-            if (CLDR_FIELD_APPEND[i].equals(string)) return i;
+            if (key.contentEquals(CLDR_FIELD_APPEND[i])) {
+                return i;
+            }
         }
         return -1;
-
     }
 
-    private static boolean isCLDRFieldName(int index) {
-        if ((index<0) && (index>=TYPE_LIMIT)) {
-            return false;
-        }
-        if (CLDR_FIELD_NAME[index].charAt(0) == '*') {
-            return false;
-        }
-        else {
-            return true;
+    private static int getCLDRFieldNumber(UResource.Key key) {
+        for (int i = 0; i < CLDR_FIELD_NAME.length; ++i) {
+            if (key.contentEquals(CLDR_FIELD_NAME[i])) {
+                return i;
+            }
         }
+        return -1;
     }
 
     /**
@@ -437,43 +523,6 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
         return getBestPattern(skeleton, null, options);
     }
 
-    private static class DayPeriodAllowedHoursSink extends UResource.Sink {
-        HashMap<String, String[]> tempMap;
-
-        private DayPeriodAllowedHoursSink(HashMap<String, String[]> tempMap) {
-            this.tempMap = tempMap;
-        }
-
-        @Override
-        public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
-            UResource.Table timeData = value.getTable();
-            for (int i = 0; timeData.getKeyAndValue(i, key, value); ++i) {
-                String regionOrLocale = key.toString();
-                UResource.Table formatList = value.getTable();
-                for (int j = 0; formatList.getKeyAndValue(j, key, value); ++j) {
-                    if (key.contentEquals("allowed")) {  // Ignore "preferred" list.
-                        tempMap.put(regionOrLocale, value.getStringArrayOrStringAsArray());
-                    }
-                }
-            }
-        }
-    }
-
-    // Get the data for dayperiod C.
-    static final Map<String, String[]> LOCALE_TO_ALLOWED_HOUR;
-    static {
-        HashMap<String, String[]> temp = new HashMap<String, String[]>();
-        ICUResourceBundle suppData = (ICUResourceBundle)ICUResourceBundle.getBundleInstance(
-                ICUData.ICU_BASE_NAME,
-                "supplementalData",
-                ICUResourceBundle.ICU_DATA_CLASS_LOADER);
-
-        DayPeriodAllowedHoursSink allowedHoursSink = new DayPeriodAllowedHoursSink(temp);
-        suppData.getAllItemsWithFallback("timeData", allowedHoursSink);
-
-        LOCALE_TO_ALLOWED_HOUR = Collections.unmodifiableMap(temp);
-    }
-
     /*
      * getBestPattern which takes optional skip matcher
      */
@@ -1679,12 +1728,6 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
     private String dateTimeFormat = "{1} {0}";
     private String[] appendItemFormats = new String[TYPE_LIMIT];
     private String[] appendItemNames = new String[TYPE_LIMIT];
-    {
-        for (int i = 0; i < TYPE_LIMIT; ++i) {
-            appendItemFormats[i] = "{0} \u251C{2}: {1}\u2524";
-            appendItemNames[i] = "F" + i;
-        }
-    }
     private char defaultHourFormatChar = 'H';
     //private boolean chineseMonthHack = false;
     //private boolean isComplete = false;
@@ -1772,25 +1815,14 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
         return i-1;
     }
 
-    /**
-     *
-     */
-    private void complete() {
+    private void addCanonicalItems() {
         PatternInfo patternInfo = new PatternInfo();
         // make sure that every valid field occurs once, with a "default" length
         for (int i = 0; i < CANONICAL_ITEMS.length; ++i) {
-            //char c = (char)types[i][0];
             addPattern(String.valueOf(CANONICAL_ITEMS[i]), false, patternInfo);
         }
-        //isComplete = true;
-    }
-    {
-        complete();
     }
 
-    /**
-     *
-     */
     private PatternWithMatcher getBestRaw(DateTimeMatcher source, int includeMask, DistanceInfo missingFields, DateTimeMatcher skipMatcher) {
         //      if (SHOW_DISTANCE) System.out.println("Searching for: " + source.pattern
         //      + ", mask: " + showMask(includeMask));
index 8029c400bb54c07014fda6b442aa0e52f3101a3c..533a2f93bfcdfd766761c76ef5b4f5c79850c455 100644 (file)
@@ -818,7 +818,7 @@ public class DateTimeGeneratorTest extends TestFmwk {
                 errln("DateTimePatternGenerator.getCanonicalSkeletonAllowingDuplicates(String) did " +
                         "return the expected result when passing " + cases[i] +
                         " and expected " + results[i] + " but got " +
-                        dtpg.getSkeleton(cases[i]));
+                        dtpg.getCanonicalSkeletonAllowingDuplicates(cases[i]));
             }
         }
     }
@@ -936,22 +936,39 @@ public class DateTimeGeneratorTest extends TestFmwk {
     }
 
     /* Tests the method
-     *        public String getAppendItemFormat(int field)
+     *        public String setAppendItemFormat(int field)
      */
     @Test
-    public void TestGetAppendItemFormat(){
+    public void TestSetAppendItemFormat(){
         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
         String[] cases = {"d","u","m","m","y"};
         for(int i=0; i<cases.length; i++){
             dtpg.setAppendItemFormat(i, cases[i]);
             if(!dtpg.getAppendItemFormat(i).equals(cases[i])){
-                errln("DateTimePatternGeneratorgetAppendItemFormat(int field) " +
+                errln("DateTimePatternGenerator.getAppendItemFormat(int field) " +
                         "did not return as expected. Value set at " + i + " was " +
                         cases[i] + " but got back " + dtpg.getAppendItemFormat(i));
             }
         }
     }
 
+    /* Tests the method
+     *        public String getAppendItemFormat(int field)
+     */
+    @Test
+    public void TestGetAppendItemFormat(){
+        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
+        int[] fields = {DateTimePatternGenerator.ERA,DateTimePatternGenerator.DAY,DateTimePatternGenerator.SECOND};
+        String[] results = {"{0} {1}","{0} ({2}: {1})","{0} ({2}: {1})"};
+        for(int i=0; i<fields.length; i++){
+            if(!dtpg.getAppendItemFormat(fields[i]).equals(results[i])){
+                errln("DateTimePatternGenerator.getAppendItemFormat(int field) " +
+                        "did not return as expected. For field " + fields[i] + ", was expecting " +
+                        results[i] + " but got back " + dtpg.getAppendItemFormat(fields[i]));
+            }
+        }
+    }
+
     /* Tests the method
      *    public String getAppendItemName(int field)
      */
@@ -989,7 +1006,8 @@ public class DateTimeGeneratorTest extends TestFmwk {
         for (AppendItemName appendItemName: appendItemNames) {
             String name = dtpgfi.getAppendItemName(appendItemName.field);
             if (!name.equals(appendItemName.name)) {
-                errln("DateTimePatternGenerator.getAppendItemName returns invalid name for field " + appendItemName.field);
+                errln("DateTimePatternGenerator.getAppendItemName returns invalid name for field " + appendItemName.field
+                        + ": got " + name + " but expected " + appendItemName.name);
             }
         }
     }