package org.unicode.cldr.util; import java.io.PrintWriter; import java.util.BitSet; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo; import org.unicode.cldr.util.SupplementalDataInfo.PluralType; import com.ibm.icu.dev.util.CollectionUtilities; import com.ibm.icu.impl.Relation; import com.ibm.icu.lang.UCharacter; import com.ibm.icu.text.NumberFormat; import com.ibm.icu.text.PluralRules; import com.ibm.icu.text.UnicodeSet; import com.ibm.icu.util.ULocale; public class PluralSnapshot implements Comparable { public enum Plurals { zero, one, two, few, many, other("x"); final String abb; Plurals(String s) { abb = s; } Plurals() { abb = name().substring(0, 1); } public String abbreviated() { return abb; } } public enum Integral { integer, fraction } static final int LEN = 128; static Set zeroOne = new TreeSet(); static { zeroOne.add(0.0d); zeroOne.add(1.0d); } public final int count; public final int count01; EnumSet coveredBy01 = EnumSet.noneOf(Plurals.class); EnumSet not01 = EnumSet.noneOf(Plurals.class); Plurals[] plurals = new Plurals[LEN]; private BitSet pluralsTransitionAt; static NumberFormat nf = NumberFormat.getInstance(ULocale.ENGLISH); public static class SnapshotInfo implements Iterable>> { // private Relation rulesToLocales = Relation.of(new HashMap>(), // TreeSet.class); private Relation snapshotToLocales = Relation.of( new TreeMap>(), TreeSet.class); private BitSet pluralsTransitionAt = new BitSet(); private Integral integral; private SnapshotInfo(PluralType pluralType, Integral integral) { this.integral = integral; SupplementalDataInfo supplementalDataInfo = SupplementalDataInfo.getInstance(); Map rulesToSnapshot = new HashMap(); for (String locale : supplementalDataInfo.getPluralLocales(pluralType)) { PluralInfo plurals = supplementalDataInfo.getPlurals(pluralType, locale); String rules = plurals.getRules(); PluralSnapshot snap = rulesToSnapshot.get(rules); if (snap == null) { PluralRules pluralRules = PluralRules.createRules(rules); snap = new PluralSnapshot(pluralRules, integral, pluralsTransitionAt); rulesToSnapshot.put(rules, snap); } snapshotToLocales.put(snap, locale); } } public Iterator>> iterator() { return snapshotToLocales.keyValuesSet().iterator(); } public String toOverview() { StringBuilder result = new StringBuilder(); result.append("Transitions:\t 0"); for (int i = pluralsTransitionAt.nextSetBit(0); i >= 0; i = pluralsTransitionAt.nextSetBit(i + 1)) { result.append(",").append(i); } return result.toString(); } public String toHtmlStringHeader() { StringBuilder result = new StringBuilder(); result.append(""); int next = -2; for (int i = pluralsTransitionAt.nextSetBit(0); i >= 0; i = next) { next = pluralsTransitionAt.nextSetBit(i + 1); result.append("").append(i); if (integral == Integral.fraction) { result.append(".x"); } int vnext = next == -1 ? LEN : next; if (vnext > i + 1) { result.append("-").append(String.valueOf(vnext - 1) + (integral == Integral.fraction ? ".x" : "")); } result.append(""); } result.append(""); return result.toString(); } } private static final EnumMap> SINGLETONS = new EnumMap>( PluralType.class); static { SINGLETONS.put(PluralType.cardinal, new EnumMap(Integral.class)); SINGLETONS.put(PluralType.ordinal, new EnumMap(Integral.class)); } private EnumSet found; public static SnapshotInfo getInstance(PluralType pluralType, Integral integral) { EnumMap temp = SINGLETONS.get(pluralType); SnapshotInfo result = temp.get(integral); if (result == null) { temp.put(integral, result = new SnapshotInfo(pluralType, integral)); } return result; } PluralSnapshot(PluralRules pluralRules, Integral integral, BitSet pluralsTransitionAt) { this.pluralsTransitionAt = pluralsTransitionAt; double offset = integral == Integral.integer ? 0 : 0.5; found = EnumSet.noneOf(Plurals.class); not01 = EnumSet.noneOf(Plurals.class); pluralsTransitionAt.set(0); for (int i = 0; i < plurals.length; ++i) { final double probe = i + offset; final Plurals plural = Plurals.valueOf(pluralRules.select(probe)); plurals[i] = plural; found.add(plural); if (probe != 0.0d && probe != 1.0d) { not01.add(plural); } if (i > 0 && plural != plurals[i - 1]) { pluralsTransitionAt.set(i); } } coveredBy01.addAll(found); coveredBy01.removeAll(not01); count = found.size(); count01 = 2 + not01.size(); } @Override public int compareTo(PluralSnapshot other) { int diff = count - other.count; if (diff != 0) return diff; diff = UnicodeSet.compare(found, other.found); if (diff != 0) return diff; Iterator it = other.not01.iterator(); for (Plurals p : not01) { // same length, so ok Plurals otherOne = it.next(); diff = p.compareTo(otherOne); if (diff != 0) return diff; } for (int i = 0; i < plurals.length; ++i) { diff = plurals[i].compareTo(other.plurals[i]); if (diff != 0) return diff; } return 0; } public boolean equals(Object other) { return compareTo((PluralSnapshot) other) == 0; } public int hashCode() { return count; // brain dead but we don't care. } public String toString() { StringBuilder result = new StringBuilder(); result.append("Plurals: 0, 1, ").append(CollectionUtilities.join(not01, ", ")); if (coveredBy01.size() != 0) { result.append("\nCovered by {0,1}:\t").append(coveredBy01); } result.append("\nInt:\t"); appendItems(result, plurals, 0); return result.toString(); } public String toHtmlString() { StringBuilder result = new StringBuilder(); result.append(""); Plurals lastItem = null; int colSpan = 0; for (int i = pluralsTransitionAt.nextSetBit(0); i >= 0; i = pluralsTransitionAt.nextSetBit(i + 1)) { Plurals item = plurals[i]; if (item == lastItem) { colSpan += 1; continue; } if (lastItem != null) { appendCell(result, colSpan, lastItem); colSpan = 0; } colSpan += 1; lastItem = item; } appendCell(result, colSpan, lastItem); result.append(""); return result.toString(); } private void appendCell(StringBuilder result, int colSpan, Plurals item) { result.append("") .append(item.abbreviated()).append(""); } private static void appendItems(StringBuilder result, T[] plurals3, double offset) { int start = 0; result.append(plurals3[0]).append("=").append(nf.format(start + offset)); for (int i = 1; i < plurals3.length; ++i) { if (!plurals3[i].equals(plurals3[i - 1])) { if (i - 1 != start) { result.append("-").append(nf.format(i - 1 + offset)); } result.append("; ").append(plurals3[i]).append("=").append(nf.format(i + offset)); start = i; } } if (plurals3.length - 1 != start) { result.append("-").append(nf.format(plurals3.length - 1 + offset)); } } public static String getDefaultStyles() { return "