All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ibm.icu.text.PluralFormat Maven / Gradle / Ivy

Go to download

International Component for Unicode for Java (ICU4J) is a mature, widely used Java library providing Unicode and Globalization support

There is a newer version: 76.1
Show newest version
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
 *******************************************************************************
 * Copyright (C) 2007-2016, International Business Machines Corporation and
 * others. All Rights Reserved.
 *******************************************************************************
 */

package com.ibm.icu.text;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

import com.ibm.icu.number.FormattedNumber;
import com.ibm.icu.number.LocalizedNumberFormatter;
import com.ibm.icu.text.PluralRules.FixedDecimal;
import com.ibm.icu.text.PluralRules.IFixedDecimal;
import com.ibm.icu.text.PluralRules.PluralType;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.ULocale.Category;

/**
 * PluralFormat supports the creation of internationalized
 * messages with plural inflection. It is based on plural
 * selection, i.e. the caller specifies messages for each
 * plural case that can appear in the user's language and the
 * PluralFormat selects the appropriate message based on
 * the number.
 *
 * 

The Problem of Plural Forms in Internationalized Messages

*

* Different languages have different ways to inflect * plurals. Creating internationalized messages that include plural * forms is only feasible when the framework is able to handle plural * forms of all languages correctly. ChoiceFormat * doesn't handle this well, because it attaches a number interval to * each message and selects the message whose interval contains a * given number. This can only handle a finite number of * intervals. But in some languages, like Polish, one plural case * applies to infinitely many intervals (e.g., the paucal case applies to * numbers ending with 2, 3, or 4 except those ending with 12, 13, or * 14). Thus ChoiceFormat is not adequate. *

* PluralFormat deals with this by breaking the problem * into two parts: *

    *
  • It uses PluralRules that can define more complex * conditions for a plural case than just a single interval. These plural * rules define both what plural cases exist in a language, and to * which numbers these cases apply. *
  • It provides predefined plural rules for many languages. Thus, the programmer * need not worry about the plural cases of a language and * does not have to define the plural cases; they can simply * use the predefined keywords. The whole plural formatting of messages can * be done using localized patterns from resource bundles. For predefined plural * rules, see the CLDR Language Plural Rules page at * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html *
* *

Usage of PluralFormat

*

Note: Typically, plural formatting is done via MessageFormat * with a plural argument type, * rather than using a stand-alone PluralFormat. *

* This discussion assumes that you use PluralFormat with * a predefined set of plural rules. You can create one using one of * the constructors that takes a ULocale object. To * specify the message pattern, you can either pass it to the * constructor or set it explicitly using the * applyPattern() method. The format() * method takes a number object and selects the message of the * matching plural case. This message will be returned. * *

Patterns and Their Interpretation
*

* The pattern text defines the message output for each plural case of the * specified locale. Syntax: *

 * pluralStyle = [offsetValue] (selector '{' message '}')+
 * offsetValue = "offset:" number
 * selector = explicitValue | keyword
 * explicitValue = '=' number  // adjacent, no white space in between
 * keyword = [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+
 * message: see {@link MessageFormat}
 * 
* Pattern_White_Space between syntax elements is ignored, except * between the {curly braces} and their sub-message, * and between the '=' and the number of an explicitValue. *

* There are 6 predefined case keywords in CLDR/ICU - 'zero', 'one', 'two', 'few', 'many' and * 'other'. You always have to define a message text for the default plural case * "other" which is contained in every rule set. * If you do not specify a message text for a particular plural case, the * message text of the plural case "other" gets assigned to this * plural case. *

* When formatting, the input number is first matched against the explicitValue clauses. * If there is no exact-number match, then a keyword is selected by calling * the PluralRules with the input number minus the offset. * (The offset defaults to 0 if it is omitted from the pattern string.) * If there is no clause with that keyword, then the "other" clauses is returned. *

* An unquoted pound sign (#) in the selected sub-message * itself (i.e., outside of arguments nested in the sub-message) * is replaced by the input number minus the offset. * The number-minus-offset value is formatted using a * NumberFormat for the PluralFormat's locale. If you * need special number formatting, you have to use a MessageFormat * and explicitly specify a NumberFormat argument. * Note: That argument is formatting without subtracting the offset! * If you need a custom format and have a non-zero offset, then you need to pass the * number-minus-offset value as a separate parameter. * *

For a usage example, see the {@link MessageFormat} class documentation. * *

Defining Custom Plural Rules

*

If you need to use PluralFormat with custom rules, you can * create a PluralRules object and pass it to * PluralFormat's constructor. If you also specify a locale in this * constructor, this locale will be used to format the number in the message * texts. *

* For more information about PluralRules, see * {@link PluralRules}. * * @author tschumann (Tim Schumann) * @stable ICU 3.8 */ public class PluralFormat extends UFormat { private static final long serialVersionUID = 1L; /** * The locale used for standard number formatting and getting the predefined * plural rules (if they were not defined explicitely). * @serial */ private ULocale ulocale = null; /** * The plural rules used for plural selection. * @serial */ private PluralRules pluralRules = null; /** * The applied pattern string. * @serial */ private String pattern = null; /** * The MessagePattern which contains the parsed structure of the pattern string. */ transient private MessagePattern msgPattern; /** * Obsolete with use of MessagePattern since ICU 4.8. Used to be: * The format messages for each plural case. It is a mapping: * String(plural case keyword) --> String * (message for this plural case). * @serial */ private Map parsedValues = null; /** * This NumberFormat is used for the standard formatting of * the number inserted into the message. * @serial */ private NumberFormat numberFormat = null; /** * The offset to subtract before invoking plural rules. */ transient private double offset = 0; /** * Creates a new cardinal-number PluralFormat for the default FORMAT 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, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null); } /** * Creates a new cardinal-number PluralFormat for a given locale. * @param ulocale the PluralFormat 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, PluralType.CARDINAL, ulocale, null); } /** * Creates a new cardinal-number PluralFormat for a given * {@link java.util.Locale}. * @param locale the PluralFormat will be configured with * rules for this locale. This locale will also be used for standard * number formatting. * @stable ICU 54 */ public PluralFormat(Locale locale) { this(ULocale.forLocale(locale)); } /** * Creates a new cardinal-number PluralFormat for a given set of rules. * The standard number formatting will be done using the default FORMAT locale. * @param rules defines the behavior of the PluralFormat * object. * @see Category#FORMAT * @stable ICU 3.8 */ public PluralFormat(PluralRules rules) { init(rules, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null); } /** * Creates a new cardinal-number PluralFormat 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. * @param rules defines the behavior of the PluralFormat * object. * @stable ICU 3.8 */ public PluralFormat(ULocale ulocale, PluralRules rules) { init(rules, PluralType.CARDINAL, ulocale, null); } /** * Creates a new cardinal-number PluralFormat for a given set of rules. * The standard number formatting will be done using the given locale. * @param locale the default number formatting will be done using this * locale. * @param rules defines the behavior of the PluralFormat * object. * @stable ICU 54 */ public PluralFormat(Locale locale, PluralRules rules) { this(ULocale.forLocale(locale), rules); } /** * Creates a new PluralFormat 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). * @stable ICU 50 */ public PluralFormat(ULocale ulocale, PluralType type) { init(null, type, ulocale, null); } /** * Creates a new PluralFormat for the plural type. * The standard number formatting will be done using the given {@link java.util.Locale}. * @param locale the default number formatting will be done using this * locale. * @param type The plural type (e.g., cardinal or ordinal). * @stable ICU 54 */ public PluralFormat(Locale locale, PluralType type) { this(ULocale.forLocale(locale), type); } /** * Creates a new cardinal-number PluralFormat for a given pattern string. * The default FORMAT locale will be used to get the set of plural rules and for * standard number formatting. * @param pattern the pattern for this PluralFormat. * @throws IllegalArgumentException if the pattern is invalid. * @see Category#FORMAT * @stable ICU 3.8 */ public PluralFormat(String pattern) { init(null, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null); applyPattern(pattern); } /** * Creates a new cardinal-number PluralFormat for a given pattern string and * locale. * The locale will be used to get the set of plural rules and for * standard number formatting. *

Example code:{@.jcite com.ibm.icu.samples.text.pluralformat.PluralFormatSample:---PluralFormatExample} * @param ulocale the PluralFormat will be configured with * rules for this locale. This locale will also be used for standard * number formatting. * @param pattern the pattern for this PluralFormat. * @throws IllegalArgumentException if the pattern is invalid. * @stable ICU 3.8 */ public PluralFormat(ULocale ulocale, String pattern) { init(null, PluralType.CARDINAL, ulocale, null); applyPattern(pattern); } /** * Creates a new cardinal-number PluralFormat for a given set of rules and a * pattern. * The standard number formatting will be done using the default FORMAT locale. * @param rules defines the behavior of the PluralFormat * object. * @param pattern the pattern for this PluralFormat. * @throws IllegalArgumentException if the pattern is invalid. * @see Category#FORMAT * @stable ICU 3.8 */ public PluralFormat(PluralRules rules, String pattern) { init(rules, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null); applyPattern(pattern); } /** * Creates a new cardinal-number PluralFormat for a given set of rules, a * pattern and a locale. * @param ulocale the PluralFormat will be configured with * rules for this locale. This locale will also be used for standard * number formatting. * @param rules defines the behavior of the PluralFormat * object. * @param pattern the pattern for this PluralFormat. * @throws IllegalArgumentException if the pattern is invalid. * @stable ICU 3.8 */ public PluralFormat(ULocale ulocale, PluralRules rules, String pattern) { init(rules, PluralType.CARDINAL, ulocale, null); applyPattern(pattern); } /** * Creates a new PluralFormat for a plural type, a * pattern and a locale. * @param ulocale the PluralFormat 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 PluralFormat. * @throws IllegalArgumentException if the pattern is invalid. * @stable ICU 50 */ public PluralFormat(ULocale ulocale, PluralType type, String pattern) { init(null, type, ulocale, null); applyPattern(pattern); } /** * Creates a new PluralFormat for a plural type, a * pattern and a locale. * @param ulocale the PluralFormat 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 PluralFormat. * @param numberFormat The number formatter to use. * @throws IllegalArgumentException if the pattern is invalid. */ /*package*/ PluralFormat(ULocale ulocale, PluralType type, String pattern, NumberFormat numberFormat) { init(null, type, ulocale, numberFormat); applyPattern(pattern); } /* * Initializes the PluralRules object. * Postcondition:
* ulocale : is locale
* pluralRules: if rules != null * it's set to rules, otherwise it is the * predefined plural rule set for the locale * ulocale.
* parsedValues: is null
* pattern: is null
* numberFormat: a NumberFormat for the locale * ulocale. */ private void init(PluralRules rules, PluralType type, ULocale locale, NumberFormat numberFormat) { ulocale = locale; pluralRules = (rules == null) ? PluralRules.forLocale(ulocale, type) : rules; resetPattern(); this.numberFormat = (numberFormat == null) ? NumberFormat.getInstance(ulocale) : numberFormat; } private void resetPattern() { pattern = null; if(msgPattern != null) { msgPattern.clear(); } offset = 0; } /** * Sets the pattern used by this plural format. * The method parses the pattern and creates a map of format strings * for the plural rules. * Patterns and their interpretation are specified in the class description. * * @param pattern the pattern for this plural format. * @throws IllegalArgumentException if the pattern is invalid. * @stable ICU 3.8 */ public void applyPattern(String pattern) { this.pattern = pattern; if (msgPattern == null) { msgPattern = new MessagePattern(); } try { msgPattern.parsePluralStyle(pattern); offset = msgPattern.getPluralOffset(0); } catch(RuntimeException e) { resetPattern(); throw e; } } /** * Returns the pattern for this PluralFormat. * * @return the pattern string * @stable ICU 4.2 */ public String toPattern() { return pattern; } /** * Finds the PluralFormat sub-message for the given number, or the "other" sub-message. * @param pattern A MessagePattern. * @param partIndex the index of the first PluralFormat argument style part. * @param selector the PluralSelector for mapping the number (minus offset) to a keyword. * @param context worker object for the selector. * @param number a number to be matched to one of the PluralFormat argument's explicit values, * or mapped via the PluralSelector. * @return the sub-message start part index. */ /*package*/ static int findSubMessage( MessagePattern pattern, int partIndex, PluralSelector selector, Object context, double number) { int count=pattern.countParts(); double offset; MessagePattern.Part part=pattern.getPart(partIndex); if(part.getType().hasNumericValue()) { offset=pattern.getNumericValue(part); ++partIndex; } else { offset=0; } // The keyword is null until we need to match against a non-explicit, not-"other" value. // Then we get the keyword from the selector. // (In other words, we never call the selector if we match against an explicit value, // or if the only non-explicit keyword is "other".) String keyword=null; // When we find a match, we set msgStart>0 and also set this boolean to true // to avoid matching the keyword again (duplicates are allowed) // while we continue to look for an explicit-value match. boolean haveKeywordMatch=false; // msgStart is 0 until we find any appropriate sub-message. // We remember the first "other" sub-message if we have not seen any // appropriate sub-message before. // We remember the first matching-keyword sub-message if we have not seen // one of those before. // (The parser allows [does not check for] duplicate keywords. // We just have to make sure to take the first one.) // We avoid matching the keyword twice by also setting haveKeywordMatch=true // at the first keyword match. // We keep going until we find an explicit-value match or reach the end of the plural style. int msgStart=0; // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples // until ARG_LIMIT or end of plural-only pattern. do { part=pattern.getPart(partIndex++); MessagePattern.Part.Type type=part.getType(); if(type==MessagePattern.Part.Type.ARG_LIMIT) { break; } assert type==MessagePattern.Part.Type.ARG_SELECTOR; // part is an ARG_SELECTOR followed by an optional explicit value, and then a message if(pattern.getPartType(partIndex).hasNumericValue()) { // explicit value like "=2" part=pattern.getPart(partIndex++); if(number==pattern.getNumericValue(part)) { // matches explicit value return partIndex; } } else if(!haveKeywordMatch) { // plural keyword like "few" or "other" // Compare "other" first and call the selector if this is not "other". if(pattern.partSubstringMatches(part, "other")) { if(msgStart==0) { msgStart=partIndex; if(keyword!=null && keyword.equals("other")) { // This is the first "other" sub-message, // and the selected keyword is also "other". // Do not match "other" again. haveKeywordMatch=true; } } } else { if(keyword==null) { keyword=selector.select(context, number-offset); if(msgStart!=0 && keyword.equals("other")) { // We have already seen an "other" sub-message. // Do not match "other" again. haveKeywordMatch=true; // Skip keyword matching but do getLimitPartIndex(). } } if(!haveKeywordMatch && pattern.partSubstringMatches(part, keyword)) { // keyword matches msgStart=partIndex; // Do not match this keyword again. haveKeywordMatch=true; } } } partIndex=pattern.getLimitPartIndex(partIndex); } while(++partIndexPluralFormat
object yet, the formatted number will * be returned. * @return the string containing the formatted plural message. * @stable ICU 4.0 */ public final String format(double number) { return format(number, number); } /** * Formats a plural message for a given number and appends the formatted * message to the given StringBuffer. * @param number a number object (instance of Number for which * the plural message should be formatted. If no pattern has been * applied to this PluralFormat object yet, the * formatted number will be returned. * Note: If this object is not an instance of Number, * the toAppendTo will not be modified. * @param toAppendTo the formatted message will be appended to this * StringBuffer. * @param pos will be ignored by this method. * @return the string buffer passed in as toAppendTo, with formatted text * appended. * @throws IllegalArgumentException if number is not an instance of Number * @stable ICU 3.8 */ @Override public StringBuffer format(Object number, StringBuffer toAppendTo, FieldPosition pos) { if (!(number instanceof Number)) { throw new IllegalArgumentException("'" + number + "' is not a Number"); } Number numberObject = (Number) number; toAppendTo.append(format(numberObject, numberObject.doubleValue())); return toAppendTo; } private String format(Number numberObject, double number) { // If no pattern was applied, return the formatted number. if (msgPattern == null || msgPattern.countParts() == 0) { return numberFormat.format(numberObject); } // Get the appropriate sub-message. // Select it based on the formatted number-offset. double numberMinusOffset = number - offset; String numberString; IFixedDecimal dec; if(numberFormat instanceof DecimalFormat) { // Call NumberFormatter to get both the DecimalQuantity and the string. LocalizedNumberFormatter f = ((DecimalFormat) numberFormat).toNumberFormatter(); FormattedNumber result; if (offset == 0) { // could be BigDecimal etc. result = f.format(numberObject); } else { result = f.format(numberMinusOffset); } numberString = result.toString(); dec = result.getFixedDecimal(); } else { if (offset == 0) { numberString = numberFormat.format(numberObject); } else { numberString = numberFormat.format(numberMinusOffset); } dec = new FixedDecimal(numberMinusOffset); } int partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, dec, number); // Replace syntactic # signs in the top level of this sub-message // (not in nested arguments) with the formatted number-offset. StringBuilder result = null; int prevIndex = msgPattern.getPart(partIndex).getLimit(); for (;;) { MessagePattern.Part part = msgPattern.getPart(++partIndex); MessagePattern.Part.Type type = part.getType(); int index = part.getIndex(); if (type == MessagePattern.Part.Type.MSG_LIMIT) { if (result == null) { return pattern.substring(prevIndex, index); } else { return result.append(pattern, prevIndex, index).toString(); } } else if (type == MessagePattern.Part.Type.REPLACE_NUMBER || // JDK compatibility mode: Remove SKIP_SYNTAX. (type == MessagePattern.Part.Type.SKIP_SYNTAX && msgPattern.jdkAposMode())) { if (result == null) { result = new StringBuilder(); } result.append(pattern, prevIndex, index); if (type == MessagePattern.Part.Type.REPLACE_NUMBER) { result.append(numberString); } prevIndex = part.getLimit(); } else if (type == MessagePattern.Part.Type.ARG_START) { if (result == null) { result = new StringBuilder(); } result.append(pattern, prevIndex, index); prevIndex = index; partIndex = msgPattern.getLimitPartIndex(partIndex); index = msgPattern.getPart(partIndex).getLimit(); MessagePattern.appendReducedApostrophes(pattern, prevIndex, index, result); prevIndex = index; } } } /** * This method is not yet supported by PluralFormat. * @param text the string to be parsed. * @param parsePosition defines the position where parsing is to begin, * and upon return, the position where parsing left off. If the position * has not changed upon return, then parsing failed. * @return nothing because this method is not yet implemented. * @throws UnsupportedOperationException will always be thrown by this method. * @stable ICU 3.8 */ public Number parse(String text, ParsePosition parsePosition) { // You get number ranges from this. You can't get an exact number. throw new UnsupportedOperationException(); } /** * This method is not yet supported by PluralFormat. * @param source the string to be parsed. * @param pos defines the position where parsing is to begin, * and upon return, the position where parsing left off. If the position * has not changed upon return, then parsing failed. * @return nothing because this method is not yet implemented. * @throws UnsupportedOperationException will always be thrown by this method. * @stable ICU 3.8 */ @Override public Object parseObject(String source, ParsePosition pos) { throw new UnsupportedOperationException(); } /** * This method returns the PluralRules type found from parsing. * @param source the string to be parsed. * @param pos defines the position where parsing is to begin, * and upon return, the position where parsing left off. If the position * is a negative index, then parsing failed. * @return Returns the PluralRules type. For example, it could be "zero", "one", "two", "few", "many" or "other") */ /*package*/ String parseType(String source, RbnfLenientScanner scanner, FieldPosition pos) { // If no pattern was applied, return null. if (msgPattern == null || msgPattern.countParts() == 0) { pos.setBeginIndex(-1); pos.setEndIndex(-1); return null; } int partIndex = 0; int currMatchIndex; int count=msgPattern.countParts(); int startingAt = pos.getBeginIndex(); if (startingAt < 0) { startingAt = 0; } // The keyword is null until we need to match against a non-explicit, not-"other" value. // Then we get the keyword from the selector. // (In other words, we never call the selector if we match against an explicit value, // or if the only non-explicit keyword is "other".) String keyword = null; String matchedWord = null; int matchedIndex = -1; // Iterate over (ARG_SELECTOR ARG_START message ARG_LIMIT) tuples // until the end of the plural-only pattern. while (partIndex < count) { MessagePattern.Part partSelector=msgPattern.getPart(partIndex++); if (partSelector.getType() != MessagePattern.Part.Type.ARG_SELECTOR) { // Bad format continue; } MessagePattern.Part partStart=msgPattern.getPart(partIndex++); if (partStart.getType() != MessagePattern.Part.Type.MSG_START) { // Bad format continue; } MessagePattern.Part partLimit=msgPattern.getPart(partIndex++); if (partLimit.getType() != MessagePattern.Part.Type.MSG_LIMIT) { // Bad format continue; } String currArg = pattern.substring(partStart.getLimit(), partLimit.getIndex()); if (scanner != null) { // If lenient parsing is turned ON, we've got some time consuming parsing ahead of us. int[] scannerMatchResult = scanner.findText(source, currArg, startingAt); currMatchIndex = scannerMatchResult[0]; } else { currMatchIndex = source.indexOf(currArg, startingAt); } if (currMatchIndex >= 0 && currMatchIndex >= matchedIndex && (matchedWord == null || currArg.length() > matchedWord.length())) { matchedIndex = currMatchIndex; matchedWord = currArg; keyword = pattern.substring(partStart.getLimit(), partLimit.getIndex()); } } if (keyword != null) { pos.setBeginIndex(matchedIndex); pos.setEndIndex(matchedIndex + matchedWord.length()); return keyword; } // Not found! pos.setBeginIndex(-1); pos.setEndIndex(-1); return null; } /** * Sets the locale used by this PluraFormat object. * Note: Calling this method resets this PluraFormat object, * 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, PluralRules.PluralType)} * with PluralType.CARDINAL. * @param ulocale the ULocale used to configure the * formatter. If ulocale is null, the * default FORMAT locale will be used. * @see Category#FORMAT * @deprecated ICU 50 This method clears the pattern and might create * a different kind of PluralRules instance; * use one of the constructors to create a new instance instead. */ @Deprecated public void setLocale(ULocale ulocale) { if (ulocale == null) { ulocale = ULocale.getDefault(Category.FORMAT); } init(null, PluralType.CARDINAL, ulocale, null); } /** * Sets the number format used by this formatter. You only need to * call this if you want a different number format than the default * formatter for the locale. * @param format the number format to use. * @stable ICU 3.8 */ public void setNumberFormat(NumberFormat format) { numberFormat = format; } /** * {@inheritDoc} * @stable ICU 3.8 */ @Override public boolean equals(Object rhs) { if(this == rhs) { return true; } if(rhs == null || getClass() != rhs.getClass()) { return false; } PluralFormat pf = (PluralFormat)rhs; return Objects.equals(ulocale, pf.ulocale) && Objects.equals(pluralRules, pf.pluralRules) && Objects.equals(msgPattern, pf.msgPattern) && Objects.equals(numberFormat, pf.numberFormat); } /** * Returns true if this equals the provided PluralFormat. * @param rhs the PluralFormat to compare against * @return true if this equals rhs * @stable ICU 3.8 */ public boolean equals(PluralFormat rhs) { return equals((Object)rhs); } /** * {@inheritDoc} * @stable ICU 3.8 */ @Override public int hashCode() { return pluralRules.hashCode() ^ parsedValues.hashCode(); } /** * {@inheritDoc} * @stable ICU 3.8 */ @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append("locale=" + ulocale); buf.append(", rules='" + pluralRules + "'"); buf.append(", pattern='" + pattern + "'"); buf.append(", format='" + numberFormat + "'"); return buf.toString(); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); pluralRulesWrapper = new PluralSelectorAdapter(); // Ignore the parsedValues from an earlier class version (before ICU 4.8) // and rebuild the msgPattern. parsedValues = null; if (pattern != null) { applyPattern(pattern); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy