/** * Copyright (C) 2002-2012 The FreeCol Team * * This file is part of FreeCol. * * FreeCol is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * FreeCol is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with FreeCol. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.freecol.client.gui.i18n; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; import org.freecolandroid.xml.stream.XMLInputFactory; import org.freecolandroid.xml.stream.XMLStreamConstants; import org.freecolandroid.xml.stream.XMLStreamException; import org.freecolandroid.xml.stream.XMLStreamReader; import net.sf.freecol.client.gui.i18n.Number.Category; /** * See the * <a href="http://cldr.unicode.org/index/cldr-spec/plural-rules"> * Common Locale Data Repository</a>. */ public class NumberRules { private static final Logger logger = Logger.getLogger(NumberRules.class.getName()); /** * A rule that always returns category "other". */ public static final Number OTHER_NUMBER_RULE = new OtherNumberRule(); /** * A rule that assigns 1 to category "one", 2 to category "two" * and all other numbers to category "other". */ public static final Number DUAL_NUMBER_RULE = new DualNumberRule(); /** * A rule that assigns 1 to category "one" and all other numbers * to category "other". */ public static final Number PLURAL_NUMBER_RULE = new PluralNumberRule(); /** * A rule that assigns 0 and 1 to category "one", and all other * number to category "other". */ public static final Number ZERO_ONE_NUMBER_RULE = new ZeroOneNumberRule(); private static Map<String, Number> numberMap = new HashMap<String, Number>(); /** * Creates a new <code>NumberRules</code> instance from the given * input stream, which must contain an XML representation of the * CLDR plural rules. * * @param in an <code>InputStream</code> value */ public NumberRules(InputStream in) { load(in); } /** * Returns a rule appropriate for the given language, or the * OTHER_NUMBER_RULE if none has been defined. * * @param lang a <code>String</code> value * @return a <code>Number</code> value */ public static Number getNumberForLanguage(String lang) { Number number = numberMap.get(lang); return number == null ? OTHER_NUMBER_RULE : number; } /** * Describe <code>isInitialized</code> method here. * * @return a <code>boolean</code> value */ public static boolean isInitialized() { return !numberMap.isEmpty(); } /** * Describe <code>load</code> method here. * * @param in an <code>InputStream</code> value */ public static void load(InputStream in) { try { XMLStreamReader xsr = XMLInputFactory.newInstance().createXMLStreamReader(in); readFromXML(xsr); } catch (Exception e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); logger.warning(sw.toString()); throw new RuntimeException("Error parsing number rules."); } } /** * Describe <code>readFromXML</code> method here. * * @param in a <code>XMLStreamReader</code> value * @exception XMLStreamException if an error occurs */ private static void readFromXML(XMLStreamReader in) throws XMLStreamException { while (in.nextTag() != XMLStreamConstants.END_ELEMENT) { String tag = in.getLocalName(); if ("version".equals(tag)) { in.nextTag(); } else if ("generation".equals(tag)) { in.nextTag(); } else if ("plurals".equals(tag)) { while (in.nextTag() != XMLStreamConstants.END_ELEMENT) { tag = in.getLocalName(); if ("pluralRules".equals(tag)) { readChild(in); } } } } } /** * Describe <code>readChild</code> method here. * * @param in a <code>XMLStreamReader</code> value * @exception XMLStreamException if an error occurs */ private static void readChild(XMLStreamReader in) throws XMLStreamException { String[] locales = in.getAttributeValue(null, "locales").split(" "); if (locales != null) { DefaultNumberRule numberRule = new DefaultNumberRule(); while (in.nextTag() != XMLStreamConstants.END_ELEMENT) { if ("pluralRule".equals(in.getLocalName())) { Category category = Category.valueOf(in.getAttributeValue(null, "count")); Rule rule = new Rule(in.getElementText()); numberRule.addRule(category, rule); } } Number number = null; switch(numberRule.countRules()) { case 0: number = OTHER_NUMBER_RULE; break; case 1: Rule rule = numberRule.getRule(Category.one); if (rule != null) { if ("n is 1".equals(rule.toString())) { number = PLURAL_NUMBER_RULE; } else if ("n in 0..1".equals(rule.toString())) { number = ZERO_ONE_NUMBER_RULE; } } break; case 2: Rule oneRule = numberRule.getRule(Category.one); Rule twoRule = numberRule.getRule(Category.two); if (oneRule != null && "n is 1".equals(oneRule.toString()) && twoRule != null && "n is 2".equals(twoRule.toString())) { number = DUAL_NUMBER_RULE; } break; default: number = numberRule; } for (String locale : locales) { numberMap.put(locale, number); } } } }