1 package org.unicode.cldr.icu; 2 3 import java.io.File; 4 import java.util.ArrayList; 5 import java.util.HashMap; 6 import java.util.List; 7 import java.util.Map; 8 9 import org.unicode.cldr.util.SupplementalDataInfo.PluralType; 10 import org.xml.sax.Attributes; 11 import org.xml.sax.SAXException; 12 13 import com.ibm.icu.impl.Row.R2; 14 15 /** 16 * Class for converting CLDR plurals files to a format suitable for outputting 17 * ICU data with. It might be possible for PluralsMapper and LdmlLocaleMapper to 18 * share a parent class, but there isn't currently a need for that so they're 19 * kept separate for the time being. 20 * 21 * @author jchye 22 */ 23 public class PluralsMapper { 24 private int numRuleSets = 0; 25 private String supplementalDir; 26 private Map<String, Integer> ruleOrder; 27 28 /** 29 * Constructor. A SupplementalDataInfo object is used rather than the 30 * supplemental directory because the supplemental data parsing is already 31 * done for us. The RegexLookup method used by LdmlLocaleMapper wouldn't 32 * work well, since there would only be one regex. 33 * 34 * @param supplementalDataInfo 35 */ PluralsMapper(String supplementalDir)36 public PluralsMapper(String supplementalDir) { 37 this.supplementalDir = supplementalDir; 38 ruleOrder = new HashMap<String, Integer>(); 39 } 40 41 /** 42 * @return CLDR data converted to an ICU-friendly format 43 */ fillFromCldr()44 public IcuData fillFromCldr() { 45 IcuData icuData = new IcuData("plurals.xml, ordinals.xml", "plurals", false); 46 fillType(PluralType.cardinal, icuData); 47 fillType(PluralType.ordinal, icuData); 48 return icuData; 49 } 50 fillType(PluralType type, IcuData icuData)51 private void fillType(PluralType type, IcuData icuData) { 52 PluralsHandler handler = new PluralsHandler(type, icuData); 53 String filename = type == PluralType.cardinal ? "plurals.xml" : "ordinals.xml"; 54 File inputFile = new File(supplementalDir, filename); // handle ordinals too. 55 MapperUtils.parseFile(inputFile, handler); 56 } 57 58 private class PluralsHandler extends MapperUtils.EmptyHandler { 59 private StringBuffer currentText; 60 private String currentCount; 61 private String[] currentLocales; 62 // List of plural counts and corresponding rules. 63 private List<R2<String, String>> currentRules; 64 private IcuData icuData; 65 String prefix; 66 PluralsHandler(PluralType type, IcuData icuData)67 public PluralsHandler(PluralType type, IcuData icuData) { 68 this.icuData = icuData; 69 prefix = type == PluralType.cardinal ? "/locales/" : "/locales_ordinals/"; 70 currentText = new StringBuffer(); 71 currentRules = new ArrayList<R2<String, String>>(); 72 } 73 74 @Override characters(char[] ch, int start, int length)75 public void characters(char[] ch, int start, int length) throws SAXException { 76 currentText.append(ch, start, length); 77 } 78 79 @Override startElement(String uri, String localName, String qName, Attributes attr)80 public void startElement(String uri, String localName, String qName, Attributes attr) throws SAXException { 81 if (qName.equals("pluralRules")) { 82 currentLocales = attr.getValue("locales").split("\\s+"); 83 } else if (qName.equals("pluralRule")) { 84 currentCount = attr.getValue("count"); 85 } 86 } 87 88 @Override endElement(String uri, String localName, String qName)89 public void endElement(String uri, String localName, String qName) throws SAXException { 90 if (qName.equals("pluralRules")) { 91 // add locale path to ICU data 92 StringBuffer ruleBuffer = new StringBuffer(); 93 for (R2<String, String> rule : currentRules) { 94 ruleBuffer.append(rule.toString() + '\n'); 95 } 96 Integer setNum = ruleOrder.get(ruleBuffer.toString()); 97 // Only add the rules to the ICU file if they aren't a duplicate 98 // of an earlier rule set. 99 if (setNum == null) { 100 setNum = numRuleSets; 101 ruleOrder.put(ruleBuffer.toString(), setNum); 102 for (R2<String, String> rule : currentRules) { 103 icuData.add("/rules/set" + numRuleSets + '/' + rule.get0(), rule.get1()); 104 } 105 numRuleSets++; 106 } 107 String setName = currentRules.size() == 0 ? "" : "set" + setNum; 108 for (String locale : currentLocales) { 109 icuData.add(prefix + locale, setName); 110 } 111 currentRules.clear(); 112 } else if (qName.equals("pluralRule")) { 113 currentRules.add(new R2<String, String>(currentCount, 114 currentText.toString())); 115 currentText.setLength(0); 116 } 117 } 118 } 119 } 120