]> granicus.if.org Git - icu/commitdiff
ICU-9132 Java support for MessageFormat selectordinal and PluralRules.PluralType...
authorMarkus Scherer <markus.icu@gmail.com>
Fri, 13 Apr 2012 20:59:37 +0000 (20:59 +0000)
committerMarkus Scherer <markus.icu@gmail.com>
Fri, 13 Apr 2012 20:59:37 +0000 (20:59 +0000)
X-SVN-Rev: 31705

icu4j/main/classes/core/src/com/ibm/icu/impl/PluralRulesLoader.java
icu4j/main/classes/core/src/com/ibm/icu/text/MessageFormat.java
icu4j/main/classes/core/src/com/ibm/icu/text/MessagePattern.java
icu4j/main/classes/core/src/com/ibm/icu/text/MessagePatternUtil.java
icu4j/main/classes/core/src/com/ibm/icu/text/PluralFormat.java
icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java
icu4j/main/shared/data/icudata.jar
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/MessagePatternUtilTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralFormatUnitTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/TestMessageFormat.java

index 64e20f031eb51a862e8f52671a0110283eb98267..1f89bb8e0615a85c898f4ec11c05a004d6200227 100644 (file)
@@ -16,6 +16,7 @@ import java.util.Set;
 import java.util.TreeMap;
 
 import com.ibm.icu.text.PluralRules;
+import com.ibm.icu.text.PluralRules.PluralType;
 import com.ibm.icu.util.ULocale;
 import com.ibm.icu.util.UResourceBundle;
 
@@ -24,12 +25,10 @@ import com.ibm.icu.util.UResourceBundle;
  */
 public class PluralRulesLoader {
     private final Map<String, PluralRules> rulesIdToRules;
-    private Map<String, String> localeIdToRulesId; // lazy init, use
-                                                   // getLocaleIdToRulesIdMap to
-                                                   // access
-    private Map<String, ULocale> rulesIdToEquivalentULocale; // lazy init, use
-                                                             // getRulesIdToEquivalentULocaleMap
-                                                             // to access
+    // lazy init, use getLocaleIdToRulesIdMap to access
+    private Map<String, String> localeIdToCardinalRulesId;
+    private Map<String, String> localeIdToOrdinalRulesId;
+    private Map<String, ULocale> rulesIdToEquivalentULocale;
 
     /**
      * Access through singleton.
@@ -42,7 +41,7 @@ public class PluralRulesLoader {
      * Returns the locales for which we have plurals data. Utility for testing.
      */
     public ULocale[] getAvailableULocales() {
-        Set<String> keys = getLocaleIdToRulesIdMap().keySet();
+        Set<String> keys = getLocaleIdToRulesIdMap(PluralType.CARDINAL).keySet();
         ULocale[] locales = new ULocale[keys.size()];
         int n = 0;
         for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
@@ -57,11 +56,11 @@ public class PluralRulesLoader {
     public ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable) {
         if (isAvailable != null && isAvailable.length > 0) {
             String localeId = ULocale.canonicalize(locale.getBaseName());
-            Map<String, String> idMap = getLocaleIdToRulesIdMap();
+            Map<String, String> idMap = getLocaleIdToRulesIdMap(PluralType.CARDINAL);
             isAvailable[0] = idMap.containsKey(localeId);
         }
 
-        String rulesId = getRulesIdForLocale(locale);
+        String rulesId = getRulesIdForLocale(locale, PluralType.CARDINAL);
         if (rulesId == null || rulesId.trim().length() == 0) {
             return ULocale.ROOT; // ultimate fallback
         }
@@ -78,9 +77,9 @@ public class PluralRulesLoader {
     /**
      * Returns the lazily-constructed map.
      */
-    private Map<String, String> getLocaleIdToRulesIdMap() {
+    private Map<String, String> getLocaleIdToRulesIdMap(PluralType type) {
         checkBuildRulesIdMaps();
-        return localeIdToRulesId;
+        return (type == PluralType.CARDINAL) ? localeIdToCardinalRulesId : localeIdToOrdinalRulesId;
     }
 
     /**
@@ -99,39 +98,53 @@ public class PluralRulesLoader {
     private void checkBuildRulesIdMaps() {
         boolean haveMap;
         synchronized (this) {
-            haveMap = localeIdToRulesId != null;
+            haveMap = localeIdToCardinalRulesId != null;
         }
         if (!haveMap) {
-            Map<String, String> tempLocaleIdToRulesId;
+            Map<String, String> tempLocaleIdToCardinalRulesId;
+            Map<String, String> tempLocaleIdToOrdinalRulesId;
             Map<String, ULocale> tempRulesIdToEquivalentULocale;
             try {
                 UResourceBundle pluralb = getPluralBundle();
+                // Read cardinal-number rules.
                 UResourceBundle localeb = pluralb.get("locales");
-                
+
                 // sort for convenience of getAvailableULocales
-                tempLocaleIdToRulesId = new TreeMap<String, String>();
+                tempLocaleIdToCardinalRulesId = new TreeMap<String, String>();
                 // not visible
                 tempRulesIdToEquivalentULocale = new HashMap<String, ULocale>();
-                
+
                 for (int i = 0; i < localeb.getSize(); ++i) {
                     UResourceBundle b = localeb.get(i);
                     String id = b.getKey();
                     String value = b.getString().intern();
-                    tempLocaleIdToRulesId.put(id, value);
+                    tempLocaleIdToCardinalRulesId.put(id, value);
 
                     if (!tempRulesIdToEquivalentULocale.containsKey(value)) {
                         tempRulesIdToEquivalentULocale.put(value, new ULocale(id));
                     }
                 }
+
+                // Read ordinal-number rules.
+                localeb = pluralb.get("locales_ordinals");
+                tempLocaleIdToOrdinalRulesId = new TreeMap<String, String>();
+                for (int i = 0; i < localeb.getSize(); ++i) {
+                    UResourceBundle b = localeb.get(i);
+                    String id = b.getKey();
+                    String value = b.getString().intern();
+                    tempLocaleIdToOrdinalRulesId.put(id, value);
+                }
             } catch (MissingResourceException e) {
                 // dummy so we don't try again
-                tempLocaleIdToRulesId = Collections.emptyMap();
+                tempLocaleIdToCardinalRulesId = Collections.emptyMap();
+                tempLocaleIdToOrdinalRulesId = Collections.emptyMap();
                 tempRulesIdToEquivalentULocale = Collections.emptyMap();
             }
             
             synchronized(this) {
-                if (localeIdToRulesId == null) {
-                    localeIdToRulesId = tempLocaleIdToRulesId;
+                if (localeIdToCardinalRulesId == null) {
+                    localeIdToCardinalRulesId = tempLocaleIdToCardinalRulesId;
+                    localeIdToOrdinalRulesId = tempLocaleIdToOrdinalRulesId;
                     rulesIdToEquivalentULocale = tempRulesIdToEquivalentULocale;
                 }
             }
@@ -143,8 +156,8 @@ public class PluralRulesLoader {
      * rulesId, return null. The rulesId might be the empty string if the rule
      * is the default rule.
      */
-    public String getRulesIdForLocale(ULocale locale) {
-        Map<String, String> idMap = getLocaleIdToRulesIdMap();
+    public String getRulesIdForLocale(ULocale locale, PluralType type) {
+        Map<String, String> idMap = getLocaleIdToRulesIdMap(type);
         String localeId = ULocale.canonicalize(locale.getBaseName());
         String rulesId = null;
         while (null == (rulesId = idMap.get(localeId))) {
@@ -216,8 +229,8 @@ public class PluralRulesLoader {
      * Returns the plural rules for the the locale. If we don't have data,
      * com.ibm.icu.text.PluralRules.DEFAULT is returned.
      */
-    public PluralRules forLocale(ULocale locale) {
-        String rulesId = getRulesIdForLocale(locale);
+    public PluralRules forLocale(ULocale locale, PluralRules.PluralType type) {
+        String rulesId = getRulesIdForLocale(locale, type);
         if (rulesId == null || rulesId.trim().length() == 0) {
             return PluralRules.DEFAULT;
         }
index aafa4bcbc31a931b353a01cee98f30f46000b462..167e043cf75db6af65e4a555cb9488693d86d73c 100644 (file)
@@ -36,6 +36,8 @@ import com.ibm.icu.impl.PatternProps;
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.text.MessagePattern.ArgType;
 import com.ibm.icu.text.MessagePattern.Part;
+import com.ibm.icu.text.PluralFormat.PluralSelector;
+import com.ibm.icu.text.PluralRules.PluralType;
 import com.ibm.icu.util.ULocale;
 import com.ibm.icu.util.ULocale.Category;
 
@@ -99,13 +101,14 @@ import com.ibm.icu.util.ULocale.Category;
  * <blockquote><pre>
  * message = messageText (argument messageText)*
  * argument = noneArg | simpleArg | complexArg
- * complexArg = choiceArg | pluralArg | selectArg
+ * complexArg = choiceArg | pluralArg | selectArg | selectordinalArg
  *
  * noneArg = '{' argNameOrNumber '}'
  * simpleArg = '{' argNameOrNumber ',' argType [',' argStyle] '}'
  * choiceArg = '{' argNameOrNumber ',' "choice" ',' choiceStyle '}'
  * pluralArg = '{' argNameOrNumber ',' "plural" ',' pluralStyle '}'
  * selectArg = '{' argNameOrNumber ',' "select" ',' selectStyle '}'
+ * selectordinalArg = '{' argNameOrNumber ',' "selectordinal" ',' pluralStyle '}'
  *
  * choiceStyle: see {@link ChoiceFormat}
  * pluralStyle: see {@link PluralFormat}
@@ -408,6 +411,7 @@ public class MessageFormat extends UFormat {
         // the locale has changed.
         stockNumberFormatter = stockDateFormatter = null;
         pluralProvider = null;
+        ordinalProvider = null;
         applyPattern(existingPattern);                              /*ibm.3550*/
     }
 
@@ -1327,11 +1331,10 @@ public class MessageFormat extends UFormat {
                 argResult = choiceResult;
                 haveArgResult = true;
                 sourceOffset = tempStatus.getIndex();
-            } else if(argType==ArgType.PLURAL || argType==ArgType.SELECT) {
+            } else if(argType.hasPluralStyle() || argType==ArgType.SELECT) {
                 // No can do!
-                throw new UnsupportedOperationException(argType==ArgType.PLURAL ?
-                        "Parsing of PluralFormat is not supported." :
-                        "Parsing of SelectFormat is not supported.");
+                throw new UnsupportedOperationException(
+                        "Parsing of plural/select/selectordinal argument is not supported.");
             } else {
                 // This should never happen.
                 throw new IllegalStateException("unexpected argType "+argType);
@@ -1441,6 +1444,7 @@ public class MessageFormat extends UFormat {
         other.stockNumberFormatter = stockNumberFormatter == null ? null : (Format) stockNumberFormatter.clone();
 
         other.pluralProvider = null;
+        other.ordinalProvider = null;
         return other;
     }
 
@@ -1560,6 +1564,7 @@ public class MessageFormat extends UFormat {
     private transient Format stockNumberFormatter;
 
     private transient PluralSelectorProvider pluralProvider;
+    private transient PluralSelectorProvider ordinalProvider;
 
     // *Important*: All fields must be declared *transient*.
     // See the longer comment above ulocale.
@@ -1697,15 +1702,24 @@ public class MessageFormat extends UFormat {
                 double number = ((Number)arg).doubleValue();
                 int subMsgStart=findChoiceSubMessage(msgPattern, i, number);
                 formatComplexSubMessage(subMsgStart, 0, args, argsMap, dest);
-            } else if(argType==ArgType.PLURAL) {
+            } else if(argType.hasPluralStyle()) {
                 if (!(arg instanceof Number)) {
                     throw new IllegalArgumentException("'" + arg + "' is not a Number");
                 }
                 double number = ((Number)arg).doubleValue();
-                if (pluralProvider == null) {
-                    pluralProvider = new PluralSelectorProvider(ulocale);
+                PluralSelector selector;
+                if(argType == ArgType.PLURAL) {
+                    if (pluralProvider == null) {
+                        pluralProvider = new PluralSelectorProvider(ulocale, PluralType.CARDINAL);
+                    }
+                    selector = pluralProvider;
+                } else {
+                    if (ordinalProvider == null) {
+                        ordinalProvider = new PluralSelectorProvider(ulocale, PluralType.ORDINAL);
+                    }
+                    selector = ordinalProvider;
                 }
-                int subMsgStart=PluralFormat.findSubMessage(msgPattern, i, pluralProvider, number);
+                int subMsgStart=PluralFormat.findSubMessage(msgPattern, i, selector, number);
                 double offset=msgPattern.getPluralOffset(i);
                 formatComplexSubMessage(subMsgStart, number-offset, args, argsMap, dest);
             } else if(argType==ArgType.SELECT) {
@@ -1940,17 +1954,19 @@ public class MessageFormat extends UFormat {
      * we do not need any PluralRules.
      */
     private static final class PluralSelectorProvider implements PluralFormat.PluralSelector {
-        public PluralSelectorProvider(ULocale loc) {
+        public PluralSelectorProvider(ULocale loc, PluralType type) {
             locale=loc;
+            this.type=type;
         }
         public String select(double number) {
             if(rules == null) {
-                rules = PluralRules.forLocale(locale);
+                rules = PluralRules.forLocale(locale, type);
             }
             return rules.select(number);
         }
         private ULocale locale;
         private PluralRules rules;
+        private PluralType type;
     }
 
     @SuppressWarnings("unchecked")
index 428f61d8f846a5d552a1abee87654fa9eb19c36d..80b0889a11c79d27c1cffa0e9de2a24e67860342 100644 (file)
@@ -10,6 +10,7 @@
 package com.ibm.icu.text;
 
 import java.util.ArrayList;
+import java.util.Locale;
 
 import com.ibm.icu.impl.ICUConfig;
 import com.ibm.icu.impl.PatternProps;
@@ -819,7 +820,7 @@ public final class MessagePattern implements Cloneable, Freezable<MessagePattern
          */
         CHOICE,
         /**
-         * The argument is a PluralFormat with an optional ARG_INT or ARG_DOUBLE offset
+         * The argument is a cardinal-number PluralFormat with an optional ARG_INT or ARG_DOUBLE offset
          * (e.g., offset:1)
          * and one or more (ARG_SELECTOR [explicit-value] message) tuples.
          * If the selector has an explicit value (e.g., =2), then
@@ -832,7 +833,24 @@ public final class MessagePattern implements Cloneable, Freezable<MessagePattern
          * The argument is a SelectFormat with one or more (ARG_SELECTOR, message) pairs.
          * @stable ICU 4.8
          */
-        SELECT
+        SELECT,
+        /**
+         * The argument is an ordinal-number PluralFormat
+         * with the same style parts sequence and semantics as {@link ArgType#PLURAL}.
+         * @draft ICU 50
+         * @provisional This API might change or be removed in a future release.
+         */
+        SELECTORDINAL;
+
+        /**
+         * @return true if the argument type has a plural style part sequence and semantics,
+         * for example {@link ArgType#PLURAL} and {@link ArgType#SELECTORDINAL}.
+         * @draft ICU 50
+         * @provisional This API might change or be removed in a future release.
+         */
+        public boolean hasPluralStyle() {
+            return this == PLURAL || this == SELECTORDINAL;
+        }
     }
 
     /**
@@ -931,7 +949,7 @@ public final class MessagePattern implements Cloneable, Freezable<MessagePattern
                         aposMode==ApostropheMode.DOUBLE_REQUIRED ||
                         c=='{' || c=='}' ||
                         (parentType==ArgType.CHOICE && c=='|') ||
-                        (parentType==ArgType.PLURAL && c=='#')
+                        (parentType.hasPluralStyle() && c=='#')
                     ) {
                         // skip the quote-starting apostrophe
                         addPart(Part.Type.SKIP_SYNTAX, index-1, 1, 0);
@@ -964,7 +982,7 @@ public final class MessagePattern implements Cloneable, Freezable<MessagePattern
                         needsAutoQuoting=true;
                     }
                 }
-            } else if(parentType==ArgType.PLURAL && c=='#') {
+            } else if(parentType.hasPluralStyle() && c=='#') {
                 // The unquoted # in a plural message fragment will be replaced
                 // with the (number-offset).
                 addPart(Part.Type.REPLACE_NUMBER, index-1, 1, 0);
@@ -1063,6 +1081,10 @@ public final class MessagePattern implements Cloneable, Freezable<MessagePattern
                 } else if(isSelect(typeIndex)) {
                     argType=ArgType.SELECT;
                 }
+            } else if(length==13) {
+                if(isSelect(typeIndex) && isOrdinal(typeIndex+6)) {
+                    argType=ArgType.SELECTORDINAL;
+                }
             }
             // change the ARG_START type from NONE to argType
             parts.get(argStart).value=(short)argType.ordinal();
@@ -1191,26 +1213,26 @@ public final class MessagePattern implements Cloneable, Freezable<MessagePattern
                 if(eos==inMessageFormatPattern(nestingLevel)) {
                     throw new IllegalArgumentException(
                         "Bad "+
-                        (argType==ArgType.PLURAL ? "plural" : "select")+
+                        argType.toString().toLowerCase(Locale.ROOT)+
                         " pattern syntax: "+prefix(start));
                 }
                 if(!hasOther) {
                     throw new IllegalArgumentException(
                         "Missing 'other' keyword in "+
-                        (argType==ArgType.PLURAL ? "plural" : "select")+
+                        argType.toString().toLowerCase(Locale.ROOT)+
                         " pattern in \""+prefix()+"\"");
                 }
                 return index;
             }
             int selectorIndex=index;
-            if(argType==ArgType.PLURAL && msg.charAt(selectorIndex)=='=') {
+            if(argType.hasPluralStyle() && msg.charAt(selectorIndex)=='=') {
                 // explicit-value plural selector: =double
                 index=skipDouble(index+1);
                 int length=index-selectorIndex;
                 if(length==1) {
                     throw new IllegalArgumentException(
                         "Bad "+
-                        (argType==ArgType.PLURAL ? "plural" : "select")+
+                        argType.toString().toLowerCase(Locale.ROOT)+
                         " pattern syntax: "+prefix(start));
                 }
                 if(length>Part.MAX_LENGTH) {
@@ -1225,11 +1247,11 @@ public final class MessagePattern implements Cloneable, Freezable<MessagePattern
                 if(length==0) {
                     throw new IllegalArgumentException(
                         "Bad "+
-                        (argType==ArgType.PLURAL ? "plural" : "select")+
+                        argType.toString().toLowerCase(Locale.ROOT)+
                         " pattern syntax: "+prefix(start));
                 }
                 // Note: The ':' in "offset:" is just beyond the skipIdentifier() range.
-                if( argType==ArgType.PLURAL && length==6 && index<msg.length() &&
+                if( argType.hasPluralStyle() && length==6 && index<msg.length() &&
                     msg.regionMatches(selectorIndex, "offset:", 0, 7)
                 ) {
                     // plural offset, not a selector
@@ -1270,7 +1292,7 @@ public final class MessagePattern implements Cloneable, Freezable<MessagePattern
             if(index==msg.length() || msg.charAt(index)!='{') {
                 throw new IllegalArgumentException(
                     "No message fragment after "+
-                    (argType==ArgType.PLURAL ? "plural" : "select")+
+                    argType.toString().toLowerCase(Locale.ROOT)+
                     " selector: "+prefix(selectorIndex));
             }
             index=parseMessage(index, 1, nestingLevel+1, argType);
@@ -1479,6 +1501,18 @@ public final class MessagePattern implements Cloneable, Freezable<MessagePattern
             ((c=msg.charAt(index))=='t' || c=='T');
     }
 
+    private boolean isOrdinal(int index) {
+        char c;
+        return
+            ((c=msg.charAt(index++))=='o' || c=='O') &&
+            ((c=msg.charAt(index++))=='r' || c=='R') &&
+            ((c=msg.charAt(index++))=='d' || c=='D') &&
+            ((c=msg.charAt(index++))=='i' || c=='I') &&
+            ((c=msg.charAt(index++))=='n' || c=='N') &&
+            ((c=msg.charAt(index++))=='a' || c=='A') &&
+            ((c=msg.charAt(index))=='l' || c=='L');
+    }
+
     /**
      * @return true if we are inside a MessageFormat (sub-)pattern,
      *         as opposed to inside a top-level choice/plural/select pattern.
index f3c601cfb441ada4f236072cef60a78b026418b9..929affc0fbff08b5387d89ffe51616363803d9cf 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-*   Copyright (C) 2011, International Business Machines
+*   Copyright (C) 2011-2012, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *******************************************************************************
 *   created on: 2011jul14
@@ -547,12 +547,16 @@ public final class MessagePatternUtil {
             break;
         case PLURAL:
             node.typeName = "plural";
-            node.complexStyle = buildPluralStyleNode(pattern, start, limit);
+            node.complexStyle = buildPluralStyleNode(pattern, start, limit, argType);
             break;
         case SELECT:
             node.typeName = "select";
             node.complexStyle = buildSelectStyleNode(pattern, start, limit);
             break;
+        case SELECTORDINAL:
+            node.typeName = "selectordinal";
+            node.complexStyle = buildPluralStyleNode(pattern, start, limit, argType);
+            break;
         default:
             // NONE type, nothing else to do
             break;
@@ -580,8 +584,9 @@ public final class MessagePatternUtil {
     }
 
     private static ComplexArgStyleNode buildPluralStyleNode(MessagePattern pattern,
-                                                            int start, int limit) {
-        ComplexArgStyleNode node = new ComplexArgStyleNode(MessagePattern.ArgType.PLURAL);
+                                                            int start, int limit,
+                                                            MessagePattern.ArgType argType) {
+        ComplexArgStyleNode node = new ComplexArgStyleNode(argType);
         MessagePattern.Part offset = pattern.getPart(start);
         if (offset.getType().hasNumericValue()) {
             node.explicitOffset = true;
index 97ef27d52428b989adc60d874c31d557645bb890..e225720f70450bc8173e1687141d74d119cb6a29 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *******************************************************************************
- * Copyright (C) 2007-2011, International Business Machines Corporation and    *
- * others. All Rights Reserved.                                                *
+ * Copyright (C) 2007-2012, International Business Machines Corporation and
+ * others. All Rights Reserved.
  *******************************************************************************
  */
 
@@ -14,6 +14,7 @@ import java.text.ParsePosition;
 import java.util.Map;
 
 import com.ibm.icu.impl.Utility;
+import com.ibm.icu.text.PluralRules.PluralType;
 import com.ibm.icu.util.ULocale;
 import com.ibm.icu.util.ULocale.Category;
 
@@ -176,29 +177,29 @@ public class PluralFormat extends UFormat {
     transient private double offset = 0;
 
     /**
-     * Creates a new <code>PluralFormat</code> for the default <code>FORMAT</code> locale.
+     * Creates a new cardinal-number <code>PluralFormat</code> for the default <code>FORMAT</code> locale.
      * This locale will be used to get the set of plural rules and for standard
      * number formatting.
      * @see Category#FORMAT
      * @stable ICU 3.8
      */
     public PluralFormat() {
-        init(null, ULocale.getDefault(Category.FORMAT));
+        init(null, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
-     * Creates a new <code>PluralFormat</code> for a given locale.
+     * Creates a new cardinal-number <code>PluralFormat</code> for a given locale.
      * @param ulocale the <code>PluralFormat</code> will be configured with
      *        rules for this locale. This locale will also be used for standard
      *        number formatting.
      * @stable ICU 3.8
      */
     public PluralFormat(ULocale ulocale) {
-        init(null, ulocale);
+        init(null, PluralType.CARDINAL, ulocale);
     }
 
     /**
-     * Creates a new <code>PluralFormat</code> for a given set of rules.
+     * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules.
      * The standard number formatting will be done using the default <code>FORMAT</code> locale.
      * @param rules defines the behavior of the <code>PluralFormat</code>
      *        object.
@@ -206,11 +207,11 @@ public class PluralFormat extends UFormat {
      * @stable ICU 3.8
      */
     public PluralFormat(PluralRules rules) {
-        init(rules, ULocale.getDefault(Category.FORMAT));
+        init(rules, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
-     * Creates a new <code>PluralFormat</code> for a given set of rules.
+     * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules.
      * The standard number formatting will be done using the given locale.
      * @param ulocale the default number formatting will be done using this
      *        locale.
@@ -219,11 +220,24 @@ public class PluralFormat extends UFormat {
      * @stable ICU 3.8
      */
     public PluralFormat(ULocale ulocale, PluralRules rules) {
-        init(rules, ulocale);
+        init(rules, PluralType.CARDINAL, ulocale);
     }
 
     /**
-     * Creates a new <code>PluralFormat</code> for a given pattern string.
+     * Creates a new <code>PluralFormat</code> for the plural type.
+     * The standard number formatting will be done using the given locale.
+     * @param ulocale the default number formatting will be done using this
+     *        locale.
+     * @param type The plural type (e.g., cardinal or ordinal).
+     * @draft ICU 50
+     * @provisional This API might change or be removed in a future release.
+     */
+    public PluralFormat(ULocale ulocale, PluralType type) {
+        init(null, type, ulocale);
+    }
+
+    /**
+     * Creates a new cardinal-number <code>PluralFormat</code> for a given pattern string.
      * The default <code>FORMAT</code> locale will be used to get the set of plural rules and for
      * standard number formatting.
      * @param  pattern the pattern for this <code>PluralFormat</code>.
@@ -232,12 +246,12 @@ public class PluralFormat extends UFormat {
      * @stable ICU 3.8
      */
     public PluralFormat(String pattern) {
-        init(null, ULocale.getDefault(Category.FORMAT));
+        init(null, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT));
         applyPattern(pattern);
     }
 
     /**
-     * Creates a new <code>PluralFormat</code> for a given pattern string and
+     * Creates a new cardinal-number <code>PluralFormat</code> for a given pattern string and
      * locale.
      * The locale will be used to get the set of plural rules and for
      * standard number formatting.
@@ -249,12 +263,12 @@ public class PluralFormat extends UFormat {
      * @stable ICU 3.8
      */
     public PluralFormat(ULocale ulocale, String pattern) {
-        init(null, ulocale);
+        init(null, PluralType.CARDINAL, ulocale);
         applyPattern(pattern);
     }
 
     /**
-     * Creates a new <code>PluralFormat</code> for a given set of rules and a
+     * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules and a
      * pattern.
      * The standard number formatting will be done using the default <code>FORMAT</code> locale.
      * @param rules defines the behavior of the <code>PluralFormat</code>
@@ -265,12 +279,12 @@ public class PluralFormat extends UFormat {
      * @stable ICU 3.8
      */
     public PluralFormat(PluralRules rules, String pattern) {
-        init(rules, ULocale.getDefault(Category.FORMAT));
+        init(rules, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT));
         applyPattern(pattern);
     }
 
     /**
-     * Creates a new <code>PluralFormat</code> for a given set of rules, a
+     * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules, a
      * pattern and a locale.
      * @param ulocale the <code>PluralFormat</code> will be configured with
      *        rules for this locale. This locale will also be used for standard
@@ -282,7 +296,24 @@ public class PluralFormat extends UFormat {
      * @stable ICU 3.8
      */
     public PluralFormat(ULocale ulocale, PluralRules rules, String pattern) {
-        init(rules, ulocale);
+        init(rules, PluralType.CARDINAL, ulocale);
+        applyPattern(pattern);
+    }
+
+    /**
+     * Creates a new <code>PluralFormat</code> for a plural type, a
+     * pattern and a locale.
+     * @param ulocale the <code>PluralFormat</code> will be configured with
+     *        rules for this locale. This locale will also be used for standard
+     *        number formatting.
+     * @param type The plural type (e.g., cardinal or ordinal).
+     * @param  pattern the pattern for this <code>PluralFormat</code>.
+     * @throws IllegalArgumentException if the pattern is invalid.
+     * @draft ICU 50
+     * @provisional This API might change or be removed in a future release.
+     */
+    public PluralFormat(ULocale ulocale, PluralType type, String pattern) {
+        init(null, type, ulocale);
         applyPattern(pattern);
     }
 
@@ -299,9 +330,9 @@ public class PluralFormat extends UFormat {
      *   <code>numberFormat</code>: a <code>NumberFormat</code> for the locale
      *                              <code>ulocale</code>.
      */
-    private void init(PluralRules rules, ULocale locale) {
+    private void init(PluralRules rules, PluralType type, ULocale locale) {
         ulocale = locale;
-        pluralRules = (rules == null) ? PluralRules.forLocale(ulocale)
+        pluralRules = (rules == null) ? PluralRules.forLocale(ulocale, type)
                                       : rules;
         resetPattern();
         numberFormat = NumberFormat.getInstance(ulocale);
@@ -590,7 +621,8 @@ public class PluralFormat extends UFormat {
      *     i.e., a pattern that was applied previously will be removed,
      *     and the NumberFormat is set to the default number format for
      *     the locale.  The resulting format behaves the same as one
-     *     constructed from {@link #PluralFormat(ULocale)}.
+     *     constructed from {@link #PluralFormat(ULocale, PluralType)}
+     *     with PluralType.CARDINAL.
      * @param ulocale the <code>ULocale</code> used to configure the
      *     formatter. If <code>ulocale</code> is <code>null</code>, the
      *     default <code>FORMAT</code> locale will be used.
@@ -601,7 +633,7 @@ public class PluralFormat extends UFormat {
         if (ulocale == null) {
             ulocale = ULocale.getDefault(Category.FORMAT);
         }
-        init(null, ulocale);
+        init(null, PluralType.CARDINAL, ulocale);
     }
 
     /**
index f12c756864c192ccc46186553eeb9657f4ad994c..53a5f45d0102154bbe72f37ca9a814b5057a4569 100644 (file)
@@ -148,6 +148,26 @@ public class PluralRules implements Serializable {
      */
     public static final double NO_UNIQUE_VALUE = -0.00123456777;
 
+    /**
+     * Type of plurals and PluralRules.
+     * @draft ICU 50
+     * @provisional This API might change or be removed in a future release.
+     */
+    public enum PluralType {
+        /**
+         * Plural rules for cardinal numbers: 1 file vs. 2 files.
+         * @draft ICU 50
+         * @provisional This API might change or be removed in a future release.
+         */
+        CARDINAL,
+        /**
+         * Plural rules for ordinal numbers: 1st file, 2nd file, 3rd file, 4th file, etc.
+         * @draft ICU 50
+         * @provisional This API might change or be removed in a future release.
+         */
+        ORDINAL
+    };
+
     /*
      * The default constraint that is always satisfied.
      */
@@ -798,9 +818,11 @@ public class PluralRules implements Serializable {
     // -------------------------------------------------------------------------
 
     /**
-     * Provides access to the predefined <code>PluralRules</code> for a given
+     * Provides access to the predefined cardinal-number <code>PluralRules</code> for a given
      * locale.
-     * ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>.
+     * Same as forLocale(locale, PluralType.CARDINAL).
+     *
+     * <p>ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>.
      * For these predefined rules, see CLDR page at
      * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
      *
@@ -814,7 +836,30 @@ public class PluralRules implements Serializable {
      * @stable ICU 3.8
      */
     public static PluralRules forLocale(ULocale locale) {
-      return PluralRulesLoader.loader.forLocale(locale);
+      return PluralRulesLoader.loader.forLocale(locale, PluralType.CARDINAL);
+    }
+
+    /**
+     * Provides access to the predefined <code>PluralRules</code> for a given
+     * locale and the plural type.
+     *
+     * <p>ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>.
+     * For these predefined rules, see CLDR page at
+     * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
+     *
+     * @param locale The locale for which a <code>PluralRules</code> object is
+     *   returned.
+     * @param type The plural type (e.g., cardinal or ordinal).
+     * @return The predefined <code>PluralRules</code> object for this locale.
+     *   If there's no predefined rules for this locale, the rules
+     *   for the closest parent in the locale hierarchy that has one will
+     *   be returned.  The final fallback always returns the default
+     *   rules.
+     * @draft ICU 50
+     * @provisional This API might change or be removed in a future release.
+     */
+    public static PluralRules forLocale(ULocale locale, PluralType type) {
+      return PluralRulesLoader.loader.forLocale(locale, type);
     }
 
     /*
index 4287ae31d62f5ea9d25c13cd67b756d314be67c3..ff145c726d89c6be4e5c3c0609f273c15df9efca 100755 (executable)
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:aa91eade60c4302ef379eab96b02b98f08781bcc728c6059616545c63b83ba2a
+oid sha256:d933870a3235e23ce8bc66928d928bc0a0fb28d510b17f52eb74d7a99949bc92
 size 7927279
index b798b60d58c9bcdde73d93c1e80625e13316eed3..6573dd29a2302c61931ffbe3e5a6c9ff63b7ac63 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-*   Copyright (C) 2011, International Business Machines
+*   Copyright (C) 2011-2012, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *******************************************************************************
 *   created on: 2011aug12
@@ -12,6 +12,7 @@ package com.ibm.icu.dev.test.format;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 
 import com.ibm.icu.text.MessagePattern;
 import com.ibm.icu.text.MessagePatternUtil;
@@ -65,6 +66,9 @@ public final class MessagePatternUtilTest extends com.ibm.icu.dev.test.TestFmwk
         private ExpectComplexArgNode expectSelectArg(Object name) {
             return expectComplexArg(name, MessagePattern.ArgType.SELECT);
         }
+        private ExpectComplexArgNode expectSelectOrdinalArg(Object name) {
+            return expectComplexArg(name, MessagePattern.ArgType.SELECTORDINAL);
+        }
         private ExpectComplexArgNode expectComplexArg(Object name, MessagePattern.ArgType argType) {
             ExpectComplexArgNode complexArg = new ExpectComplexArgNode(this, name, argType);
             contents.add(complexArg);
@@ -180,9 +184,7 @@ public final class MessagePatternUtilTest extends com.ibm.icu.dev.test.TestFmwk
     private class ExpectComplexArgNode extends ExpectArgNode {
         private ExpectComplexArgNode(ExpectMessageNode parent,
                                      Object name, MessagePattern.ArgType argType) {
-            super(name,
-                  argType == MessagePattern.ArgType.CHOICE ? "choice" :
-                  argType == MessagePattern.ArgType.PLURAL ? "plural" : "select");
+            super(name, argType.toString().toLowerCase(Locale.ROOT));
             this.argType = argType;
             this.parent = parent;
         }
@@ -394,6 +396,22 @@ public final class MessagePatternUtilTest extends com.ibm.icu.dev.test.TestFmwk
         expect.checkMatches(msg);
     }
 
+
+    public void TestSelectOrdinalArg() {
+        MessageNode msg = MessagePatternUtil.buildMessageNode(
+                "abc{num, selectordinal, offset:17 =0{null} few{fff} other {oooo}}xyz");
+        ExpectMessageNode expect = new ExpectMessageNode().
+            expectTextThatContains("abc").
+            expectSelectOrdinalArg("num").
+                expectOffset(17).
+                expectVariant("=0", 0).expectTextThatContains("null").finishVariant().
+                expectVariant("few").expectTextThatContains("fff").finishVariant().
+                expectVariant("other").expectTextThatContains("oooo").finishVariant().
+                finishComplexArg().
+            expectTextThatContains("xyz");
+        expect.checkMatches(msg);
+    }
+
     public void TestChoiceArg() {
         MessageNode msg = MessagePatternUtil.buildMessageNode(
                 "a_{0,choice,-∞ #-inf|  5≤ five | 99 # ninety'|'nine  }_z");
index d08f239f53f69242c9fb7af9e9a7e6f9758a6c89..429b5f6d4c6ae6954904add98cc8e571d7bb393a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *******************************************************************************
- * Copyright (C) 2007-2011, International Business Machines Corporation and    *
- * others. All Rights Reserved.                                                *
+ * Copyright (C) 2007-2012, International Business Machines Corporation and
+ * others. All Rights Reserved.
  *******************************************************************************
  */
 
@@ -19,6 +19,7 @@ import com.ibm.icu.text.MessageFormat;
 import com.ibm.icu.text.NumberFormat;
 import com.ibm.icu.text.PluralFormat;
 import com.ibm.icu.text.PluralRules;
+import com.ibm.icu.text.PluralRules.PluralType;
 import com.ibm.icu.util.ULocale;
 
 /**
@@ -324,4 +325,18 @@ public class PluralFormatUnitTest extends TestFmwk {
             }
         }
     }
+
+    public void TestOrdinalFormat() {
+        String pattern = "one{#st file}two{#nd file}few{#rd file}other{#th file}";
+        PluralFormat pf = new PluralFormat(ULocale.ENGLISH, PluralType.ORDINAL, pattern);
+        assertEquals("PluralFormat.format(321)", "321st file", pf.format(321));
+        assertEquals("PluralFormat.format(22)", "22nd file", pf.format(22));
+        assertEquals("PluralFormat.format(3)", "3rd file", pf.format(3));
+
+        // Code coverage: Use the other new-for-PluralType constructor as well.
+        pf = new PluralFormat(ULocale.ENGLISH, PluralType.ORDINAL);
+        pf.applyPattern(pattern);
+        assertEquals("PluralFormat.format(456)", "456th file", pf.format(456));
+        assertEquals("PluralFormat.format(111)", "111th file", pf.format(111));
+    }
 }
index 8ccb380c7bc2f5b20bc546e31377652d51f78b5b..bc28813d7d4282586a177e76b37938bf21c4610c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *******************************************************************************
- * Copyright (C) 2007-2011, International Business Machines Corporation and    *
- * others. All Rights Reserved.                                                *
+ * Copyright (C) 2007-2012, International Business Machines Corporation and
+ * others. All Rights Reserved.
  *******************************************************************************
  */
 package com.ibm.icu.dev.test.format;
@@ -18,6 +18,7 @@ import java.util.Set;
 import com.ibm.icu.dev.test.TestFmwk;
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.text.PluralRules;
+import com.ibm.icu.text.PluralRules.PluralType;
 import com.ibm.icu.util.ULocale;
 
 /**
@@ -386,4 +387,9 @@ public class PluralRulesTest extends TestFmwk {
              }
          }
      }
+
+     public void TestOrdinal() {
+         PluralRules pr = PluralRules.forLocale(ULocale.ENGLISH, PluralType.ORDINAL);
+         assertEquals("PluralRules(en-ordinal).select(2)", "two", pr.select(2));
+     }
 }
index d5f0844b0fff26ce445d6b04c3fbabc0d6e10a96..a08fafa10a139494a9754516a811ef7455b8aeeb 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2011, International Business Machines
+* Copyright (c) 2004-2012, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
@@ -1832,4 +1832,33 @@ public class TestMessageFormat extends com.ibm.icu.dev.test.TestFmwk {
         assertEquals("trim-named-arg format() failed", "x 3 y",
                      m.format(map, result, new FieldPosition(0)).toString());
     }
+
+    public void TestSelectOrdinal() {
+        // Test plural & ordinal together,
+        // to make sure that we get the correct cached PluralSelector for each.
+        MessageFormat m = new MessageFormat(
+            "{0,plural,one{1 file}other{# files}}, " +
+            "{0,selectordinal,one{#st file}two{#nd file}few{#rd file}other{#th file}}",
+            ULocale.ENGLISH);
+        Object[] args = new Object[] { 21 };
+        FieldPosition ignore = null;
+        StringBuffer result = new StringBuffer();
+        assertEquals("plural-and-ordinal format(21)", "21 files, 21st file",
+                     m.format(args, result, ignore).toString());
+
+        args[0] = 2;
+        result.delete(0, result.length());
+        assertEquals("plural-and-ordinal format(2) failed", "2 files, 2nd file",
+                     m.format(args, result, ignore).toString());
+
+        args[0] = 1;
+        result.delete(0, result.length());
+        assertEquals("plural-and-ordinal format(1) failed", "1 file, 1st file",
+                     m.format(args, result, ignore).toString());
+
+        args[0] = 3;
+        result.delete(0, result.length());
+        assertEquals("plural-and-ordinal format(3) failed", "3 files, 3rd file",
+                     m.format(args, result, ignore).toString());
+    }
 }