]> granicus.if.org Git - icu/commitdiff
ICU-12579 Updating MeasureFormat Java data sink to new API.
authorShane Carr <shane@unicode.org>
Fri, 10 Jun 2016 20:31:21 +0000 (20:31 +0000)
committerShane Carr <shane@unicode.org>
Fri, 10 Jun 2016 20:31:21 +0000 (20:31 +0000)
X-SVN-Rev: 38822

icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/MeasureUnitTest.java

index 441737aef9c726fa5ee69926782aa6e944ef6beb..bba5abca2fc4d2329cc97e85f728f99134646b2c 100644 (file)
@@ -741,117 +741,114 @@ public class MeasureFormat extends UFormat {
      * C++: Each inner sink class has a reference to the main outer sink.
      * Java: Use non-static inner classes instead.
      */
-    private static final class UnitDataSink extends UResource.TableSink {
-        /**
-         * Sink for a table of display patterns. For example,
-         * unitsShort/duration/hour contains other{"{0} hrs"}.
-         */
-        class UnitPatternSink extends UResource.TableSink {
-            String[] patterns;
-
-            void setFormatterIfAbsent(int index, UResource.Value value, int minPlaceholders) {
-                if (patterns == null) {
-                    EnumMap<FormatWidth, String[]> styleToPatterns =
-                            cacheData.unitToStyleToPatterns.get(unit);
-                    if (styleToPatterns == null) {
-                        styleToPatterns =
-                                new EnumMap<FormatWidth, String[]>(FormatWidth.class);
-                        cacheData.unitToStyleToPatterns.put(unit, styleToPatterns);
-                    } else {
-                        patterns = styleToPatterns.get(width);
-                    }
-                    if (patterns == null) {
-                        patterns = new String[MeasureFormatData.PATTERN_COUNT];
-                        styleToPatterns.put(width, patterns);
-                    }
+    private static final class UnitDataSink extends UResource.Sink {
+        void setFormatterIfAbsent(int index, UResource.Value value, int minPlaceholders) {
+            if (patterns == null) {
+                EnumMap<FormatWidth, String[]> styleToPatterns =
+                        cacheData.unitToStyleToPatterns.get(unit);
+                if (styleToPatterns == null) {
+                    styleToPatterns =
+                            new EnumMap<FormatWidth, String[]>(FormatWidth.class);
+                    cacheData.unitToStyleToPatterns.put(unit, styleToPatterns);
+                } else {
+                    patterns = styleToPatterns.get(width);
                 }
-                if (patterns[index] == null) {
-                    patterns[index] = SimpleFormatterImpl.compileToStringMinMaxArguments(
-                            value.getString(), sb, minPlaceholders, 1);
+                if (patterns == null) {
+                    patterns = new String[MeasureFormatData.PATTERN_COUNT];
+                    styleToPatterns.put(width, patterns);
                 }
             }
+            if (patterns[index] == null) {
+                patterns[index] = SimpleFormatterImpl.compileToStringMinMaxArguments(
+                        value.getString(), sb, minPlaceholders, 1);
+            }
+        }
 
-            @Override
-            public void put(UResource.Key key, UResource.Value value) {
-                if (key.contentEquals("dnam")) {
-                    // Skip the unit display name for now.
-                } else if (key.contentEquals("per")) {
-                    // For example, "{0}/h".
-                    setFormatterIfAbsent(MeasureFormatData.PER_UNIT_INDEX, value, 1);
-                } else {
-                    // The key must be one of the plural form strings. For example:
-                    // one{"{0} hr"}
-                    // other{"{0} hrs"}
-                    setFormatterIfAbsent(StandardPlural.indexFromString(key), value, 0);
-                }
+        /**
+         * Consume a display pattern. For example,
+         * unitsShort/duration/hour contains other{"{0} hrs"}.
+         */
+        void consumePattern(UResource.Key key, UResource.Value value) {
+            if (key.contentEquals("dnam")) {
+                // Skip the unit display name for now.
+            } else if (key.contentEquals("per")) {
+                // For example, "{0}/h".
+                setFormatterIfAbsent(MeasureFormatData.PER_UNIT_INDEX, value, 1);
+            } else {
+                // The key must be one of the plural form strings. For example:
+                // one{"{0} hr"}
+                // other{"{0} hrs"}
+                setFormatterIfAbsent(StandardPlural.indexFromString(key), value, 0);
             }
         }
-        UnitPatternSink patternSink = new UnitPatternSink();
 
         /**
-         * Sink for a table of per-unit tables. For example,
+         * Consume a table of per-unit tables. For example,
          * unitsShort/duration contains tables for duration-unit subtypes day & hour.
          */
-        class UnitSubtypeSink extends UResource.TableSink {
-            @Override
-            public UResource.TableSink getOrCreateTableSink(UResource.Key key) {
-                // Should we ignore or reject unknown units?
-                unit = MeasureUnit.internalGetInstance(type, key.toString());  // never null
-                // Trigger a fresh lookup of the patterns for this unit+width.
-                patternSink.patterns = null;
-                return patternSink;
+        void consumeSubtypeTable(UResource.Key key, UResource.Value value) {
+            unit = MeasureUnit.internalGetInstance(type, key.toString());  // never null
+            // Trigger a fresh lookup of the patterns for this unit+width.
+            patterns = null;
+
+            if (value.getType() == ICUResourceBundle.STRING) {
+                // Units like "coordinate" that don't have plural variants
+                setFormatterIfAbsent(StandardPlural.OTHER.ordinal(), value, 0);
+            } else if (value.getType() == ICUResourceBundle.TABLE) {
+                // Units that have plural variants
+                UResource.Table patternTableTable = value.getTable();
+                for (int i = 0; patternTableTable.getKeyAndValue(i, key, value); i++) {
+                    consumePattern(key, value);
+                }
+            } else {
+                throw new ICUException("Data for unit '" + unit + "' is in an unknown format");
             }
         }
-        UnitSubtypeSink subtypeSink = new UnitSubtypeSink();
 
         /**
-         * Sink for compound x-per-y display pattern. For example,
+         * Consume compound x-per-y display pattern. For example,
          * unitsShort/compound/per may be "{0}/{1}".
          */
-        class UnitCompoundSink extends UResource.TableSink {
-            @Override
-            public void put(UResource.Key key, UResource.Value value) {
-                if (key.contentEquals("per")) {
-                    cacheData.styleToPerPattern.put(width,
-                            SimpleFormatterImpl.compileToStringMinMaxArguments(
-                                    value.getString(), sb, 2, 2));
-                }
+        void consumeCompoundPattern(UResource.Key key, UResource.Value value) {
+            if (key.contentEquals("per")) {
+                cacheData.styleToPerPattern.put(width,
+                        SimpleFormatterImpl.compileToStringMinMaxArguments(
+                                value.getString(), sb, 2, 2));
             }
         }
-        UnitCompoundSink compoundSink = new UnitCompoundSink();
 
         /**
-         * Sink for a table of unit type tables. For example,
+         * Consume a table of unit type tables. For example,
          * unitsShort contains tables for area & duration.
          * It also contains a table for the compound/per pattern.
          */
-        class UnitTypeSink extends UResource.TableSink {
-            @Override
-            public UResource.TableSink getOrCreateTableSink(UResource.Key key) {
-                if (key.contentEquals("currency")) {
-                    // Skip.
-                } else if (key.contentEquals("compound")) {
-                    if (!cacheData.hasPerFormatter(width)) {
-                        return compoundSink;
+        void consumeUnitTypesTable(UResource.Key key, UResource.Value value) {
+            if (key.contentEquals("currency")) {
+                // Skip.
+            } else if (key.contentEquals("compound")) {
+                if (!cacheData.hasPerFormatter(width)) {
+                    UResource.Table compoundTable = value.getTable();
+                    for (int i = 0; compoundTable.getKeyAndValue(i, key, value); i++) {
+                        consumeCompoundPattern(key, value);
                     }
-                } else {
-                    type = key.toString();
-                    return subtypeSink;
                 }
-                return null;
+            } else {
+                type = key.toString();
+                UResource.Table subtypeTable = value.getTable();
+                for (int i = 0; subtypeTable.getKeyAndValue(i, key, value); i++) {
+                    consumeSubtypeTable(key, value);
+                }
             }
         }
-        UnitTypeSink typeSink = new UnitTypeSink();
 
         UnitDataSink(MeasureFormatData outputData) {
             cacheData = outputData;
         }
-        @Override
-        public void put(UResource.Key key, UResource.Value value) {
+
+        void consumeAlias(UResource.Key key, UResource.Value value) {
             // Handle aliases like
             // units:alias{"/LOCALE/unitsShort"}
             // which should only occur in the root bundle.
-            if (value.getType() != ICUResourceBundle.ALIAS) { return; }
             FormatWidth sourceWidth = widthFromKey(key);
             if (sourceWidth == null) {
                 // Alias from something we don't care about.
@@ -870,12 +867,14 @@ public class MeasureFormat extends UFormat {
             }
             cacheData.widthFallback[sourceWidth.ordinal()] = targetWidth;
         }
-        @Override
-        public UResource.TableSink getOrCreateTableSink(UResource.Key key) {
+
+        public void consumeTable(UResource.Key key, UResource.Value value) {
             if ((width = widthFromKey(key)) != null) {
-                return typeSink;
+                UResource.Table unitTypesTable = value.getTable();
+                for (int i = 0; unitTypesTable.getKeyAndValue(i, key, value); i++) {
+                    consumeUnitTypesTable(key, value);
+                }
             }
-            return null;
         }
 
         static FormatWidth widthFromKey(UResource.Key key) {
@@ -906,6 +905,19 @@ public class MeasureFormat extends UFormat {
             return null;
         }
 
+        @Override
+        public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
+            // Main entry point to sink
+            UResource.Table widthsTable = value.getTable();
+            for (int i = 0; widthsTable.getKeyAndValue(i, key, value); i++) {
+                if (value.getType() == ICUResourceBundle.ALIAS) {
+                    consumeAlias(key, value);
+                } else {
+                    consumeTable(key, value);
+                }
+            }
+        }
+
         // Output data.
         MeasureFormatData cacheData;
 
@@ -916,6 +928,7 @@ public class MeasureFormat extends UFormat {
 
         // Temporary
         StringBuilder sb = new StringBuilder();
+        String[] patterns;
     }
 
     /**
@@ -926,7 +939,7 @@ public class MeasureFormat extends UFormat {
                 (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_UNIT_BASE_NAME, locale);
         MeasureFormatData cacheData = new MeasureFormatData();
         UnitDataSink sink = new UnitDataSink(cacheData);
-        resource.getAllTableItemsWithFallback("", sink);
+        resource.getAllItemsWithFallback("", sink);
         return cacheData;
     }
 
index 7adda14dda25994b0287876f53891a606a0f98aa..1023c31a6143ddf8db135d9c39985306aea4cd43 100644 (file)
@@ -250,29 +250,18 @@ public class MeasureUnit implements Serializable {
     private static final class MeasureUnitSink extends UResource.Sink {
         @Override
         public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
-            UResource.Table unitFormatsTable = value.getTable();
-            for (int i1 = 0; unitFormatsTable.getKeyAndValue(i1, key, value); ++i1) {
-
-                // Only consume the tables related to units, defined as those beginning with "units".
-                if (!key.startsWith("units")) {
+            UResource.Table unitTypesTable = value.getTable();
+            for (int i2 = 0; unitTypesTable.getKeyAndValue(i2, key, value); ++i2) {
+                // Skip "compound" since it is treated differently from the other units
+                if (key.contentEquals("compound")) {
                     continue;
                 }
 
-                UResource.Table unitTypesTable = value.getTable();
-                for (int i2 = 0; unitTypesTable.getKeyAndValue(i2, key, value); ++i2) {
-
-                    // Special case: "compound" does not have plural variants.
-                    if (key.contentEquals("compound")) {
-                        continue;
-                    }
-
-                    String unitType = key.toString();
-
-                    UResource.Table unitNamesTable = value.getTable();
-                    for (int i3 = 0; unitNamesTable.getKeyAndValue(i3, key, value); ++i3) {
-                        String unitName = key.toString();
-                        internalGetInstance(unitType, unitName);
-                    }
+                String unitType = key.toString();
+                UResource.Table unitNamesTable = value.getTable();
+                for (int i3 = 0; unitNamesTable.getKeyAndValue(i3, key, value); ++i3) {
+                    String unitName = key.toString();
+                    internalGetInstance(unitType, unitName);
                 }
             }
         }
@@ -323,9 +312,9 @@ public class MeasureUnit implements Serializable {
 
         // Load the unit types.  Use English, since we know that that is a superset.
         ICUResourceBundle rb1 = (ICUResourceBundle) UResourceBundle.getBundleInstance(
-                ICUData.ICU_BASE_NAME,
+                ICUData.ICU_UNIT_BASE_NAME,
                 "en");
-        rb1.getAllItemsWithFallback("", new MeasureUnitSink());
+        rb1.getAllItemsWithFallback("units", new MeasureUnitSink());
 
         // Load the currencies
         ICUResourceBundle rb2 = (ICUResourceBundle) UResourceBundle.getBundleInstance(
index 81dd49bd745102b0e9c3bffc526ff3a13a3c91d7..b3e5dddebb81408d2653e6150b030fba47ebfe17 100644 (file)
@@ -12,6 +12,7 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
+import java.lang.reflect.Field;
 import java.text.FieldPosition;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -1637,6 +1638,7 @@ public class MeasureUnitTest extends TestFmwk {
         assertTrue("MeasureUnit: unexpectedly few currencies defined", MeasureUnit.getAvailable("currency").size() > 50);
     }
 
+    @Test
     public void testParseObject() {
         MeasureFormat mf = MeasureFormat.getInstance(Locale.GERMAN, FormatWidth.NARROW);
         try {
@@ -1647,6 +1649,48 @@ public class MeasureUnitTest extends TestFmwk {
         }
     }
 
+    @Test
+    public void testCLDRUnitAvailability() {
+        Set<MeasureUnit> knownUnits = new HashSet<MeasureUnit>();
+        Class cMeasureUnit, cTimeUnit;
+        try {
+            cMeasureUnit = Class.forName("com.ibm.icu.util.MeasureUnit");
+            cTimeUnit = Class.forName("com.ibm.icu.util.TimeUnit");
+        } catch (ClassNotFoundException e) {
+            fail("Count not load MeasureUnit or TimeUnit class: " + e.getMessage());
+            return;
+        }
+        for (Field field : cMeasureUnit.getFields()) {
+            if (field.getGenericType() == cMeasureUnit || field.getGenericType() == cTimeUnit) {
+                try {
+                    MeasureUnit unit = (MeasureUnit) field.get(cMeasureUnit);
+                    knownUnits.add(unit);
+                } catch (IllegalArgumentException e) {
+                    fail(e.getMessage());
+                    return;
+                } catch (IllegalAccessException e) {
+                    fail(e.getMessage());
+                    return;
+                }
+            }
+        }
+        for (String type : MeasureUnit.getAvailableTypes()) {
+            if (type.equals("currency") || type.equals("compound")) {
+                continue;
+            }
+            for (MeasureUnit unit : MeasureUnit.getAvailable(type)) {
+                if (!knownUnits.contains(unit)) {
+                    String message = "Unit present in CLDR but not available via constant in MeasureUnit: " + unit;
+                    if (unit.getType().equals("coordinate")) {
+                        logKnownIssue("12573", message);
+                    } else {
+                        fail(message);
+                    }
+                }
+            }
+        }
+    }
+
     // DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
     // for MeasureFormat during the release process.
     static Map<MeasureUnit, Pair<MeasureUnit, MeasureUnit>> getUnitsToPerParts() {