ICU-8474 fixes for serialization, which included change to equals

X-SVN-Rev: 33528
This commit is contained in:
Mark Davis 2013-04-15 06:19:53 +00:00
parent eea5fa79bb
commit 297e433797
9 changed files with 2878 additions and 449 deletions

View file

@ -7,6 +7,11 @@
package com.ibm.icu.text;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.text.ParseException;
import java.util.ArrayList;
@ -303,7 +308,7 @@ public class PluralRules implements Serializable {
}
public String toString() {
return "";
return null;
}
public int updateRepeatLimit(int limit) {
@ -374,7 +379,7 @@ public class PluralRules implements Serializable {
public static class NumberInfo implements Comparable<NumberInfo> {
public final double source;
public final int visibleFractionDigitCount;
public final int fractionalDigits;
public final long fractionalDigits;
public final long intValue;
public final boolean hasIntegerValue;
@ -384,6 +389,19 @@ public class PluralRules implements Serializable {
fractionalDigits = f;
intValue = (long)n;
hasIntegerValue = source == intValue;
// check values. TODO make into unit test.
//
// long visiblePower = (int) Math.pow(10, v);
// if (fractionalDigits > visiblePower) {
// throw new IllegalArgumentException();
// }
// double fraction = intValue + (fractionalDigits / (double) visiblePower);
// if (fraction != source) {
// double diff = Math.abs(fraction - source)/(Math.abs(fraction) + Math.abs(source));
// if (diff > 0.00000001d) {
// throw new IllegalArgumentException();
// }
// }
}
// Ugly, but for samples we don't care.
@ -391,17 +409,6 @@ public class PluralRules implements Serializable {
this(n,v,getFractionalDigits(n, v));
}
// Ugly, but for samples we don't care.
public static int decimals(double n) {
String temp = String.valueOf(n);
return temp.endsWith(".0") ? 0 : temp.length() - temp.indexOf('.') - 1;
}
// Ugly, but for samples we don't care.
public NumberInfo (String n) {
this(Double.parseDouble(n), getVisibleFractionCount(n));
}
private static int getFractionalDigits(double n, int v) {
if (v == 0) {
return 0;
@ -412,23 +419,35 @@ public class PluralRules implements Serializable {
}
}
private static int getVisibleFractionCount(String value) {
int decimalPos = value.trim().indexOf('.') + 1;
if (decimalPos == 0) {
return 0;
} else {
return value.length() - decimalPos - 1;
}
}
public NumberInfo(double n) {
this(n, decimals(n));
}
// Ugly, but for samples we don't care.
public static int decimals(double n) {
String temp = String.valueOf(n);
return temp.endsWith(".0") ? 0 : temp.length() - temp.indexOf('.') - 1;
}
public NumberInfo(long n) {
this(n,0);
}
// Ugly, but for samples we don't care.
public NumberInfo (String n) {
this(Double.parseDouble(n), getVisibleFractionCount(n));
}
private static int getVisibleFractionCount(String value) {
value = value.trim();
int decimalPos = value.indexOf('.') + 1;
if (decimalPos == 0) {
return 0;
} else {
return value.length() - decimalPos;
}
}
public double get(Operand operand) {
switch(operand) {
default: return source;
@ -446,13 +465,20 @@ public class PluralRules implements Serializable {
* We're not going to care about NaN.
*/
public int compareTo(NumberInfo other) {
if (intValue != other.intValue) {
return intValue < other.intValue ? -1 : 1;
}
if (source != other.source) {
return source < other.source ? -1 : 1;
}
if (visibleFractionDigitCount != other.visibleFractionDigitCount) {
return visibleFractionDigitCount < other.visibleFractionDigitCount ? -1 : 1;
}
return fractionalDigits - other.fractionalDigits;
long diff = fractionalDigits - other.fractionalDigits;
if (diff != 0) {
return diff < 0 ? -1 : 1;
}
return 0;
}
@Override
public boolean equals(Object arg0) {
@ -471,7 +497,7 @@ public class PluralRules implements Serializable {
@Override
public int hashCode() {
// TODO Auto-generated method stub
return fractionalDigits + 37 * (visibleFractionDigitCount + (int)(37 * source));
return (int)(fractionalDigits + 37 * (visibleFractionDigitCount + (int)(37 * source)));
}
@Override
public String toString() {
@ -660,13 +686,13 @@ public class PluralRules implements Serializable {
String[] pair = Utility.splitString(range, "..");
double low, high;
if (pair.length == 2) {
low = Long.parseLong(pair[0]);
high = Long.parseLong(pair[1]);
low = Double.parseDouble(pair[0]);
high = Double.parseDouble(pair[1]);
if (low > high) {
throw unexpected(range, condition);
}
} else if (pair.length == 1) {
low = high = Long.parseLong(pair[0]);
low = high = Double.parseDouble(pair[0]);
} else {
throw unexpected(range, condition);
}
@ -679,7 +705,7 @@ public class PluralRules implements Serializable {
vals = null;
}
} else {
lowBound = highBound = Long.parseLong(t);
lowBound = highBound = Double.parseDouble(t);
}
if (x != tokens.length) {
@ -761,6 +787,10 @@ public class PluralRules implements Serializable {
private static RuleList parseRuleChain(String description)
throws ParseException {
RuleList result = new RuleList();
// remove trailing ;
if (description.endsWith(";")) {
description = description.substring(0,description.length()-1);
}
String[] rules = Utility.split(description, ';');
for (int i = 0; i < rules.length; ++i) {
result.addRule(parseRule(rules[i].trim()));
@ -808,30 +838,30 @@ public class PluralRules implements Serializable {
if (upperBound != lowerBound) {
toAddTo.add(new NumberInfo(upperBound + offset));
}
// if (range_list != null) {
// // add from each range
// for (int i = 0; i < range_list.length; i += 2) {
// double lower = range_list[i];
// double upper = range_list[i+1];
// if (lower != lowerBound) {
// toAddTo.add(new NumberInfo(lower + offset));
// }
// if (upper != upperBound) {
// toAddTo.add(new NumberInfo(upper + offset));
// }
// }
// }
// if (range_list != null) {
// // add from each range
// for (int i = 0; i < range_list.length; i += 2) {
// double lower = range_list[i];
// double upper = range_list[i+1];
// if (lower != lowerBound) {
// toAddTo.add(new NumberInfo(lower + offset));
// }
// if (upper != upperBound) {
// toAddTo.add(new NumberInfo(upper + offset));
// }
// }
// }
if (!integersOnly) {
double average = (lowerBound + upperBound) / 2.0d;
toAddTo.add(new NumberInfo(average + offset));
// if (range_list != null) {
// // we will just add one value from the middle
// for (int i = 0; i < range_list.length; i += 2) {
// double lower = range_list[i];
// double upper = range_list[i+1];
// toAddTo.add(new NumberInfo((lower + upper) / 2.0d + offset));
// }
// }
// if (range_list != null) {
// // we will just add one value from the middle
// for (int i = 0; i < range_list.length; i += 2) {
// double lower = range_list[i];
// double upper = range_list[i+1];
// toAddTo.add(new NumberInfo((lower + upper) / 2.0d + offset));
// }
// }
}
}
@ -1095,6 +1125,9 @@ public class PluralRules implements Serializable {
Map<String, String> ordered = new TreeMap<String, String>(KEYWORD_COMPARATOR);
for (Rule rule : rules) {
String keyword = rule.getKeyword();
if (keyword.equals("other")) {
continue;
}
String constraint = rule.getConstraint();
ordered.put(keyword, constraint);
}
@ -1145,8 +1178,10 @@ public class PluralRules implements Serializable {
return a;
}
}
static final int[] TENS = {1, 10, 100, 1000};
private static final int[] TENS = {1, 10, 100, 1000, 10000, 100000, 1000000};
private static final int LIMIT_FRACTION_SAMPLES = 3;
private Set<NumberInfo> fractions(Set<NumberInfo> original) {
Set<NumberInfo> toAddTo = new HashSet<NumberInfo>();
@ -1156,25 +1191,52 @@ public class PluralRules implements Serializable {
result.add((int)base1.intValue);
}
List<Integer> ints = new ArrayList<Integer>(result);
Set<String> keywords = new HashSet<String>();
for (int j = 0; j < ints.size(); ++j) {
Integer base = ints.get(j);
Integer fract = ints.get((j == 0 ? ints.size() : j) -1);
for (int visibleFractions = 1; visibleFractions < 3; ++visibleFractions) {
for (int i = 1; i <= visibleFractions; ++i) {
// with visible fractions = 3, and fract = 1, then we should get x.10, 0.01
// with visible fractions = 3, and fract = 15, then we should get x.15, x.15
if (fract >= TENS[i]) {
continue;
String keyword = select(base);
if (keywords.contains(keyword)) {
continue;
}
keywords.add(keyword);
toAddTo.add(new NumberInfo(base,1)); // add .0
toAddTo.add(new NumberInfo(base,2)); // add .00
Integer fract = getDifferentCategory(ints, keyword);
if (fract >= TENS[LIMIT_FRACTION_SAMPLES-1]) { // make sure that we always get the value
toAddTo.add(new NumberInfo(base + "." + fract));
} else {
for (int visibleFractions = 1; visibleFractions < LIMIT_FRACTION_SAMPLES; ++visibleFractions) {
for (int i = 1; i <= visibleFractions; ++i) {
// with visible fractions = 3, and fract = 1, then we should get x.10, 0.01
// with visible fractions = 3, and fract = 15, then we should get x.15, x.15
if (fract >= TENS[i]) {
continue;
}
toAddTo.add(new NumberInfo(base + fract/(double)TENS[i], visibleFractions));
}
NumberInfo combo = new NumberInfo(base + fract/(double)TENS[i], visibleFractions);
toAddTo.add(combo);
}
}
}
return toAddTo;
}
/**
* @param ints
* @param base
* @return
*/
private Integer getDifferentCategory(List<Integer> ints, String keyword) {
for (int i = ints.size() - 1; i >= 0; --i) {
Integer other = ints.get(i);
String keywordOther = select(other);
if (!keywordOther.equals(keyword)) {
return other;
}
}
return 37;
}
private boolean addConditional(Set<NumberInfo> toAddTo, Set<NumberInfo> others, double trial) {
boolean added;
NumberInfo toAdd = new NumberInfo(trial);
@ -1307,7 +1369,7 @@ public class PluralRules implements Serializable {
* @deprecated This API is ICU internal only.
*/
public String select(NumberInfo sample) {
return rules.select(new NumberInfo(sample.source, sample.visibleFractionDigitCount, sample.fractionalDigits));
return rules.select(sample);
}
/**
@ -1472,65 +1534,67 @@ public class PluralRules implements Serializable {
addRelation(foundKeywords, keyword, s);
}
main:
if (foundKeywords.size() != keywords.size()) {
for (int i = 1; i < 1000; ++i) {
boolean done = addIfNotPresent(i, mentioned, foundKeywords);
if (done) break main;
if (foundKeywords.size() != keywords.size()) {
for (int i = 1; i < 1000; ++i) {
boolean done = addIfNotPresent(i, mentioned, foundKeywords);
if (done) break main;
}
// if we are not done, try tenths
for (int i = 10; i < 1000; ++i) {
boolean done = addIfNotPresent(i/10d, mentioned, foundKeywords);
if (done) break main;
}
System.out.println("Failed to find sample for each keyword: " + foundKeywords + "\n\t" + rules + "\n\t" + mentioned);
}
// if we are not done, try tenths
for (int i = 10; i < 1000; ++i) {
boolean done = addIfNotPresent(i/10d, mentioned, foundKeywords);
if (done) break main;
}
System.out.println("Failed to find sample for each keyword: " + foundKeywords + "\n\t" + rules + "\n\t" + mentioned);
}
mentioned.add(new NumberInfo(0)); // always there
mentioned.add(new NumberInfo(1)); // always there
mentioned.add(new NumberInfo(2)); // always there
mentioned.add(new NumberInfo(0.1,1)); // always there
mentioned.add(new NumberInfo(1.99,2)); // always there
mentioned.addAll(fractions(mentioned));
// Set<NumberInfo> toAddTo = mentioned;
// {
// // once done, manufacture values for the OTHER case
// int otherCount = 2;
// for (int i = 0; i < 1000; ++i) {
// }
// NumberInfo last = null;
// Set<NumberInfo> others = new LinkedHashSet<NumberInfo>();
// for (NumberInfo s : toAddTo) {
// double trial;
// if (last == null) {
// trial = s.source-0.5;
// } else {
// double diff = s.source - last.source;
// if (diff > 1.0d) {
// trial = Math.floor(s.source);
// if (trial == s.source) {
// --trial;
// }
// } else {
// trial = (s.source + last.source) / 2;
// }
// }
// if (trial >= 0) {
// addConditional(toAddTo, others, trial);
// }
// last = s;
// }
// double trial = last == null ? 0 : last.source;
// double fraction = 0;
// while (otherCount > 0) {
// if (addConditional(toAddTo, others, trial = trial * 2 + 1 + fraction)) {
// --otherCount;
// }
// fraction += 0.125;
// }
// toAddTo.addAll(others);
// others.clear();
// toAddTo.addAll(fractions(toAddTo, others));
//
// }
// Set<NumberInfo> toAddTo = mentioned;
// {
// // once done, manufacture values for the OTHER case
// int otherCount = 2;
// for (int i = 0; i < 1000; ++i) {
// }
// NumberInfo last = null;
// Set<NumberInfo> others = new LinkedHashSet<NumberInfo>();
// for (NumberInfo s : toAddTo) {
// double trial;
// if (last == null) {
// trial = s.source-0.5;
// } else {
// double diff = s.source - last.source;
// if (diff > 1.0d) {
// trial = Math.floor(s.source);
// if (trial == s.source) {
// --trial;
// }
// } else {
// trial = (s.source + last.source) / 2;
// }
// }
// if (trial >= 0) {
// addConditional(toAddTo, others, trial);
// }
// last = s;
// }
// double trial = last == null ? 0 : last.source;
// double fraction = 0;
// while (otherCount > 0) {
// if (addConditional(toAddTo, others, trial = trial * 2 + 1 + fraction)) {
// --otherCount;
// }
// fraction += 0.125;
// }
// toAddTo.addAll(others);
// others.clear();
// toAddTo.addAll(fractions(toAddTo, others));
//
// }
for (NumberInfo s : mentioned) {
String keyword = select(s.source, s.visibleFractionDigitCount, s.fractionalDigits);
String keyword = select(s);
Set<NumberInfo> list = sampleFractionMap.get(keyword);
if (list == null) {
list = new LinkedHashSet<NumberInfo>(); // will be sorted because the iteration is
@ -1675,12 +1739,21 @@ public class PluralRules implements Serializable {
return false;
}
int limit = Math.max(getRepeatLimit(), rhs.getRepeatLimit());
for (int i = 0; i < limit * 2; ++i) {
if (!select(i).equals(rhs.select(i))) {
return false;
for (String keyword : rhs.getKeywords()) {
String rules2 = getRules(keyword);
String rules3 = rhs.getRules(keyword);
if (rules2 != rules3) {
if (rules2 == null || !rules2.equals(rules3)) {
return false;
}
}
}
// int limit = Math.max(getRepeatLimit(), rhs.getRepeatLimit());
// for (int i = 0; i < limit * 2; ++i) {
// if (!select(i).equals(rhs.select(i))) {
// return false;
// }
// }
return true;
}
@ -1807,4 +1880,21 @@ public class PluralRules implements Serializable {
public String getRules(String keyword) {
return rules.getRules(keyword);
}
private void writeObject(
ObjectOutputStream out)
throws IOException {
throw new NotSerializableException();
}
private void readObject(ObjectInputStream in
) throws IOException, ClassNotFoundException {
throw new NotSerializableException();
}
private void readObjectNoData(
) throws ObjectStreamException {
throw new NotSerializableException();
}
private Object writeReplace() throws ObjectStreamException {
return new PluralRulesSerialProxy(toString());
}
}

View file

@ -0,0 +1,26 @@
/*
*******************************************************************************
* Copyright (C) 2013, Google Inc, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.text;
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* @author markdavis
*
*/
public class PluralRulesSerialProxy implements Serializable {
private static final long serialVersionUID = 42L;
private final String data;
PluralRulesSerialProxy(String rules) {
data = rules;
}
private Object readResolve() throws ObjectStreamException {
return PluralRules.createRules(data);
}
}

View file

@ -65,29 +65,45 @@ public abstract class PluralRulesFactory {
static Relation<ULocale,NumberInfo> EXTRA_SAMPLES = Relation.of(new HashMap<ULocale,Set<NumberInfo>>(), HashSet.class);
static {
String[][] overrides = {
{"bn", ""},
{"en,ca,de,et,fi,gl,it,nl,pt,sv,sw,ta,te,ur", "one: j is 1"},
{"cs,sk", "one: j is 1; few: j in 2..4; many: v is not 0"},
{"cy", "one: n is 1; two: n is 2; few: n is 3; many: n is 6"},
//{"el", "one: j is 1 or i is 0 and f is 1"},
{"da,is", "one: j is 1 or f is 1"},
{"fil", "one: j in 0..1"},
{"he", "one: j is 1; two: j is 2", "10,20"},
{"hi", "one: n within 0..1"},
{"hr", "one: j mod 10 is 1 and j mod 100 is not 11; few: j mod 10 in 2..4 and j mod 100 not in 12..14; many: j mod 10 is 0 or j mod 10 in 5..9 or j mod 100 in 11..14"},
{"hy", "one: n within 0..2 and n is not 2"},
// {"hr", "one: j mod 10 is 1 and j mod 100 is not 11; few: j mod 10 in 2..4 and j mod 100 not in 12..14; many: j mod 10 is 0 or j mod 10 in 5..9 or j mod 100 in 11..14"},
{"lv", "zero: n mod 10 is 0" +
" or n mod 10 in 11..19" +
" or v in 1..6 and f is not 0 and f mod 10 is 0" +
" or v in 1..6 and f mod 10 in 11..19;" +
"one: n mod 10 is 1 and n mod 100 is not 11" +
" or v in 1..6 and f mod 10 is 1 and f mod 100 is not 11" +
" or v not in 0..6 and f mod 10 is 1"},
" or v not in 0..6 and f mod 10 is 1"},
// {"lv", "zero: n mod 10 is 0" +
// " or n mod 10 in 11..19" +
// " or v in 1..6 and f is not 0 and f mod 10 is 0" +
// " or v in 1..6 and f mod 10 in 11..19;" +
// "one: n mod 10 is 1 and n mod 100 is not 11" +
// " or v in 1..6 and f mod 10 is 1 and f mod 100 is not 11" +
// " or v not in 0..6 and f mod 10 is 1"},
{"pl", "one: j is 1; few: j mod 10 in 2..4 and j mod 100 not in 12..14; many: j is not 1 and j mod 10 in 0..1 or j mod 10 in 5..9 or j mod 100 in 12..14"},
{"sl", "one: j mod 100 is 1; two: j mod 100 is 2; few: j mod 100 in 3..4 or v is not 0"},
{"sr", "one: j mod 10 is 1 and j mod 100 is not 11" +
" or v in 1..6 and f mod 10 is 1 and f mod 100 is not 11" +
" or v not in 0..6 and f mod 10 is 1;" +
// {"sr", "one: j mod 10 is 1 and j mod 100 is not 11" +
// " or v in 1..6 and f mod 10 is 1 and f mod 100 is not 11" +
// " or v not in 0..6 and f mod 10 is 1;" +
// "few: j mod 10 in 2..4 and j mod 100 not in 12..14" +
// " or v in 1..6 and f mod 10 in 2..4 and f mod 100 not in 12..14" +
// " or v not in 0..6 and f mod 10 in 2..4"
// },
{"sr,hr", "one: j mod 10 is 1 and j mod 100 is not 11" +
" or f mod 10 is 1 and f mod 100 is not 11;" +
"few: j mod 10 in 2..4 and j mod 100 not in 12..14" +
" or v in 1..6 and f mod 10 in 2..4 and f mod 100 not in 12..14" +
" or v not in 0..6 and f mod 10 in 2..4"
" or f mod 10 in 2..4 and f mod 100 not in 12..14"
},
// +
// " ; many: j mod 10 is 0 " +
// " or j mod 10 in 5..9 " +
@ -96,11 +112,14 @@ public abstract class PluralRulesFactory {
// " or v in 1..6 and f mod 10 in 5..9" +
// " or v in 1..6 and f mod 100 in 11..14" +
// " or v not in 0..6 and f mod 10 in 5..9"
},
{"ro", "one: j is 1; few: n is 0 or n is not 1 and n mod 100 in 1..19"},
{"ru,uk", "one: j mod 10 is 1 and j mod 100 is not 11;" +
" few: j mod 10 in 2..4 and j mod 100 not in 12..14;" +
" many: j mod 10 is 0 or j mod 10 in 5..9 or j mod 100 in 11..14"},
{"ru", "one: j mod 10 is 1 and j mod 100 is not 11;" +
" many: j mod 10 is 0 or j mod 10 in 5..9 or j mod 100 in 11..14"
// + "; many: j mod 10 is 0 or j mod 10 in 5..9 or j mod 100 in 11..14"
},
{"uk", "one: j mod 10 is 1 and j mod 100 is not 11; " +
"few: j mod 10 in 2..4 and j mod 100 not in 12..14; " +
"many: j mod 10 is 0 or j mod 10 in 5..9 or j mod 100 in 11..14"},
};
for (String[] pair : overrides) {
for (String locale : pair[0].split("\\s*,\\s*")) {
@ -160,4 +179,79 @@ public abstract class PluralRulesFactory {
return result == null ? ULocale.ROOT : result;
}
};
static String[][] OLDRULES = {
{"af", "one: n is 1"},
{"am", "one: n in 0..1"},
{"ar", "zero: n is 0; one: n is 1; two: n is 2; few: n mod 100 in 3..10; many: n mod 100 in 11..99"},
{"az", "other: null"},
{"bg", "one: n is 1"},
{"bn", "one: n is 1"},
{"ca", "one: n is 1"},
{"cs", "one: n is 1; few: n in 2..4"},
{"cy", "zero: n is 0; one: n is 1; two: n is 2; few: n is 3; many: n is 6"},
{"da", "one: n is 1"},
{"de", "one: n is 1"},
{"el", "one: n is 1"},
{"en", "one: n is 1"},
{"es", "one: n is 1"},
{"et", "one: n is 1"},
{"eu", "one: n is 1"},
{"fa", "other: null"},
{"fi", "one: n is 1"},
{"fil", "one: n in 0..1"},
{"fr", "one: n within 0..2 and n is not 2"},
{"gl", "one: n is 1"},
{"gu", "one: n is 1"},
{"hi", "one: n in 0..1"},
{"hr", "one: n mod 10 is 1 and n mod 100 is not 11; few: n mod 10 in 2..4 and n mod 100 not in 12..14; many: n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14"},
{"hu", "other: null"},
{"hy", "one: n is 1"},
{"id", "other: null"},
{"is", "one: n is 1"},
{"it", "one: n is 1"},
{"he", "one: n is 1; two: n is 2; many: n is not 0 and n mod 10 is 0"},
{"ja", "other: null"},
{"ka", "other: null"},
{"kk", "one: n is 1"},
{"km", "other: null"},
{"kn", "other: null"},
{"ko", "other: null"},
{"ky", "one: n is 1"},
{"lo", "other: null"},
{"lt", "one: n mod 10 is 1 and n mod 100 not in 11..19; few: n mod 10 in 2..9 and n mod 100 not in 11..19"},
{"lv", "zero: n is 0; one: n mod 10 is 1 and n mod 100 is not 11"},
{"mk", "one: n mod 10 is 1 and n is not 11"},
{"ml", "one: n is 1"},
{"mn", "one: n is 1"},
{"mr", "one: n is 1"},
{"ms", "other: null"},
{"my", "other: null"},
{"ne", "one: n is 1"},
{"nl", "one: n is 1"},
{"nb", "one: n is 1"},
{"pa", "one: n is 1"},
{"pl", "one: n is 1; few: n mod 10 in 2..4 and n mod 100 not in 12..14; many: n is not 1 and n mod 10 in 0..1 or n mod 10 in 5..9 or n mod 100 in 12..14"},
{"ps", "one: n is 1"},
{"pt", "one: n is 1"},
{"ro", "one: n is 1; few: n is 0 or n is not 1 and n mod 100 in 1..19"},
{"ru", "one: n mod 10 is 1 and n mod 100 is not 11; few: n mod 10 in 2..4 and n mod 100 not in 12..14; many: n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14"},
{"si", "other: null"},
{"sk", "one: n is 1; few: n in 2..4"},
{"sl", "one: n mod 100 is 1; two: n mod 100 is 2; few: n mod 100 in 3..4"},
{"sq", "one: n is 1"},
{"sr", "one: n mod 10 is 1 and n mod 100 is not 11; few: n mod 10 in 2..4 and n mod 100 not in 12..14; many: n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14"},
{"sv", "one: n is 1"},
{"sw", "one: n is 1"},
{"ta", "one: n is 1"},
{"te", "one: n is 1"},
{"th", "other: null"},
{"tr", "other: null"},
{"uk", "one: n mod 10 is 1 and n mod 100 is not 11; few: n mod 10 in 2..4 and n mod 100 not in 12..14; many: n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14"},
{"ur", "one: n is 1"},
{"uz", "other: null"},
{"vi", "other: null"},
{"zh", "other: null"},
{"zu", "one: n is 1"},
};
}

View file

@ -6,6 +6,12 @@
*/
package com.ibm.icu.dev.test.format;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
@ -36,11 +42,11 @@ import com.ibm.icu.util.ULocale;
* @author markdavis (Mark Davis) [for fractional support]
*/
public class PluralRulesTest extends TestFmwk {
static boolean USE_ALT = System.getProperty("alt_plurals") != null;
PluralRulesFactory factory = USE_ALT ? PluralRulesFactory.ALTERNATE : PluralRulesFactory.NORMAL;
public static void main(String[] args) throws Exception {
new PluralRulesTest().run(args);
}
@ -163,43 +169,43 @@ public class PluralRulesTest extends TestFmwk {
public void testUniqueRules() {
main:
for (ULocale locale : factory.getAvailableULocales()) {
PluralRules rules = factory.forLocale(locale);
Collection<NumberInfo> samples = rules.getFractionSamples();
Map<String,PluralRules> keywordToRule = new HashMap<String,PluralRules>();
for (String keyword : rules.getKeywords()) {
if (keyword.equals("other")) {
continue;
}
String rules2 = keyword + ":" + rules.getRules(keyword);
PluralRules singleRule = PluralRules.createRules(rules2);
if (singleRule == null) {
errln("Can't generate single rule for " + rules2);
PluralRules.createRules(rules2); // for debugging
continue main;
}
keywordToRule.put(keyword, singleRule);
}
Map<NumberInfo, String> collisionTest = new TreeMap();
for (NumberInfo sample : samples) {
collisionTest.clear();
for (Entry<String, PluralRules> entry: keywordToRule.entrySet()) {
String keyword = entry.getKey();
PluralRules rule = entry.getValue();
String foundKeyword = rule.select(sample);
if (foundKeyword.equals("other")) {
for (ULocale locale : factory.getAvailableULocales()) {
PluralRules rules = factory.forLocale(locale);
Collection<NumberInfo> samples = rules.getFractionSamples();
Map<String,PluralRules> keywordToRule = new HashMap<String,PluralRules>();
for (String keyword : rules.getKeywords()) {
if (keyword.equals("other")) {
continue;
}
String old = collisionTest.get(sample);
if (old != null) {
errln(locale + "\tNon-unique rules: " + sample + " => " + old + " & " + foundKeyword);
rule.select(sample);
} else {
collisionTest.put(sample, foundKeyword);
String rules2 = keyword + ":" + rules.getRules(keyword);
PluralRules singleRule = PluralRules.createRules(rules2);
if (singleRule == null) {
errln("Can't generate single rule for " + rules2);
PluralRules.createRules(rules2); // for debugging
continue main;
}
keywordToRule.put(keyword, singleRule);
}
Map<NumberInfo, String> collisionTest = new TreeMap();
for (NumberInfo sample : samples) {
collisionTest.clear();
for (Entry<String, PluralRules> entry: keywordToRule.entrySet()) {
String keyword = entry.getKey();
PluralRules rule = entry.getValue();
String foundKeyword = rule.select(sample);
if (foundKeyword.equals("other")) {
continue;
}
String old = collisionTest.get(sample);
if (old != null) {
errln(locale + "\tNon-unique rules: " + sample + " => " + old + " & " + foundKeyword);
rule.select(sample);
} else {
collisionTest.put(sample, foundKeyword);
}
}
}
}
}
}
private void checkCategoriesAndExpected(String title, String categoriesAndExpected, PluralRules rules) {
@ -224,14 +230,19 @@ public class PluralRulesTest extends TestFmwk {
}
private static String[][] equalityTestData = {
{ "a: n is 5",
"a: n in 2..6 and n not in 2..4 and n is not 6" },
{ "a: n in 2..3",
"a: n is 2 or n is 3",
"a: n is 3 and n in 2..5 or n is 2" },
{ "a: n is 12; b:n mod 10 in 2..3",
"b: n mod 10 in 2..3 and n is not 12; a: n in 12..12",
"b: n is 13; a: n is 12; b: n mod 10 is 2 or n mod 10 is 3" },
// once we add fractions, we had to retract the "test all possibilities" for equality,
// so we only have a limited set of equality tests now.
{ "a:n in 2;b:n in 5",
"b: n in 5;a: n in 2;" },
// { "a: n is 5",
// "a: n in 2..6 and n not in 2..4 and n is not 6" },
// { "a: n in 2..3",
// "a: n is 2 or n is 3",
// "a: n is 3 and n in 2..5 or n is 2" },
// { "a: n is 12; b:n mod 10 in 2..3",
// "b: n mod 10 in 2..3 and n is not 12; a: n in 12..12",
// "b: n is 13; a: n is 12; b: n mod 10 is 2 or n mod 10 is 3" },
};
private static String[][] inequalityTestData = {
@ -240,7 +251,10 @@ public class PluralRulesTest extends TestFmwk {
},
{ "a: n mod 3 is 2 and n is not 5",
"a: n mod 6 is 2 or n is 8 or n is 11"
}
},
// the following are currently inequal, but we may make them equal in the future.
{ "a: n in 2..5",
"a: n in 2..4,5" },
};
private void compareEquality(String id, Object[] objects, boolean shouldBeEqual) {
@ -655,4 +669,37 @@ public class PluralRulesTest extends TestFmwk {
"cy; zero: 0, 0.0; one: 1, 1.0; two: 2; few: 3; many: 6; other: 0.1, 2.5, 3.5, 5",
};
}
private <T extends Serializable> T serializeAndDeserialize(T original, Output<Integer> size) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream ostream = new ObjectOutputStream(baos);
ostream.writeObject(original);
ostream.flush();
byte bytes[] = baos.toByteArray();
size.value = bytes.length;
ObjectInputStream istream = new ObjectInputStream(new ByteArrayInputStream(bytes));
T reconstituted = (T)istream.readObject();
return reconstituted;
} catch(IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public void TestSerialization() {
Output<Integer> size = new Output<Integer>();
int max = 0;
for (ULocale locale : PluralRules.getAvailableULocales()) {
PluralRules item = PluralRules.forLocale(locale);
PluralRules item2 = serializeAndDeserialize(item, size);
logln(locale + "\tsize:\t" + size.value);
max = Math.max(max, size.value);
if (!assertEquals(locale + "\tPlural rules before and after serialization", item, item2)) {
PluralRules item3 = serializeAndDeserialize(item, size);
item.equals(item2);
}
}
logln("max \tsize:\t" + max);
}
}

View file

@ -6,23 +6,39 @@
*/
package com.ibm.icu.dev.test.format;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.ibm.icu.dev.test.format.PluralRulesTest.StandardPluralCategories;
import com.ibm.icu.dev.util.CollectionUtilities;
import com.ibm.icu.dev.util.Relation;
import com.ibm.icu.impl.Row;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.text.PluralRules.NumberInfo;
import com.ibm.icu.util.ULocale;
@ -31,214 +47,53 @@ import com.ibm.icu.util.ULocale;
* @author markdavis
*/
public class WritePluralRulesData {
// TODO use options
public static final String TARGETDIR = "/Users/markdavis/Google Drive/Backup-2012-10-09/Documents/indigo/Generated/icu/plurals/";
public static void main(String[] args) throws Exception {
if (args.length == 0) {
args = new String[] {"rules"};
}
for (String arg : args) {
if (arg.equalsIgnoreCase("samples")) {
generateSamples();
generateSamples(SampleStyle.modified);
} else if (arg.equalsIgnoreCase("original")) {
generateSamples(SampleStyle.original);
} else if (arg.startsWith("verify")) {
generateSamples(SampleStyle.verify);
} else if (arg.equalsIgnoreCase("rules")) {
showRules();
} else if (arg.equalsIgnoreCase("oldSnap")) {
generateLOCALE_SNAPSHOT(PluralRulesFactory.NORMAL);
} else if (arg.equalsIgnoreCase("newSnap")) {
generateLOCALE_SNAPSHOT(PluralRulesFactory.ALTERNATE);
} else if (arg.equalsIgnoreCase("fromList")) {
getOriginalSamples();
} else {
throw new IllegalArgumentException();
}
}
}
public static void generateLOCALE_SNAPSHOT(PluralRulesFactory pluralRulesFactory) {
StringBuilder builder = new StringBuilder();
Map<Set<StandardPluralCategories>, Relation<String, ULocale>> keywordsToData = new TreeMap(StandardPluralCategories.SHORTEST_FIRST);
for (ULocale locale : pluralRulesFactory.getAvailableULocales()) {
builder.setLength(0);
PluralRules rules = pluralRulesFactory.forLocale(locale);
boolean firstKeyword = true;
EnumSet<StandardPluralCategories> keywords = StandardPluralCategories.getSet(rules.getKeywords());
Relation<String, ULocale> samplesToLocales = keywordsToData.get(keywords);
if (samplesToLocales == null) {
keywordsToData.put(keywords, samplesToLocales = Relation.of(
new LinkedHashMap<String,Set<ULocale>>(), LinkedHashSet.class));
}
//System.out.println(locale);
for (StandardPluralCategories keyword : keywords) {
if (firstKeyword) {
firstKeyword = false;
} else {
builder.append(";\t");
}
Collection<NumberInfo> samples = rules.getFractionSamples(keyword.toString());
if (samples.size() == 0) {
throw new IllegalArgumentException();
}
builder.append(keyword).append(": ");
boolean first = true;
for (NumberInfo n : samples) {
if (first) {
first = false;
} else {
builder.append(", ");
}
builder.append(n);
// for (double j : samples) {
// double sample = i + j/100;
// }
}
}
samplesToLocales.put(builder.toString(), locale);
}
System.out.println(" static final String[] LOCALE_SNAPSHOT = {");
for (Entry<Set<StandardPluralCategories>, Relation<String, ULocale>> keywordsAndData : keywordsToData.entrySet()) {
System.out.println("\n // " + keywordsAndData.getKey());
for (Entry<String, Set<ULocale>> samplesAndLocales : keywordsAndData.getValue().keyValuesSet()) {
Set<ULocale> locales = samplesAndLocales.getValue();
// check functional equivalence
boolean[] isAvailable = new boolean[1];
for (ULocale locale : locales) {
ULocale base = pluralRulesFactory.getFunctionalEquivalent(locale, isAvailable);
if (!locales.contains(base) && base.toString().length() != 0) {
System.out.println("**" + locales + " doesn't contain " + base);
}
}
System.out.println(
" \"" + CollectionUtilities.join(locales, ",")
+ ";\t" + samplesAndLocales.getKey() + "\",");
}
}
System.out.println(" };");
}
private static class OldNewData extends Row.R4<String, String, String, String> {
public OldNewData(String oldRules, String oldSamples, String newRules, String newSamples) {
super(oldRules, oldSamples, newRules, newSamples);
}
}
static final String[] FOCUS_LOCALES = ("af,am,ar,az,bg,bn,ca,cs,cy,da,de,el,en,es,et,eu,fa,fi,fil,fr,gl,gu," +
"hi,hr,hu,hy,id,is,it,he,ja,ka,kk,km,kn,ko,ky,lo,lt,lv,mk,ml,mn,mr,ms,my,ne,nl,nb," +
"pa,pl,ps,pt,ro,ru,si,sk,sl,sq,sr,sv,sw,ta,te,th,tr,uk,ur,uz,vi,zh,zu").split("\\s*,\\s*");
public static void showRules() {
if (true) {
// for debugging
PluralRules rules = PluralRulesFactory.ALTERNATE.forLocale(new ULocale("lv"));
rules.select(2.0d, 2, 0);
}
// System.out.println(new TreeSet(Arrays.asList(locales)));
Relation<Map<String,OldNewData>, String> rulesToLocale = Relation.of(
new TreeMap<Map<String,OldNewData>, Set<String>>(
new CollectionUtilities.MapComparator<String,OldNewData>()), TreeSet.class);
for (String localeString : FOCUS_LOCALES) {
ULocale locale = new ULocale(localeString);
PluralRules oldRules = PluralRulesFactory.NORMAL.forLocale(locale);
PluralRules newRules = PluralRulesFactory.ALTERNATE.hasOverride(locale) ? PluralRulesFactory.ALTERNATE.forLocale(locale) : null;
Set<String> keywords = oldRules.getKeywords();
if (newRules != null) {
TreeSet<String> temp = new TreeSet<String>(PluralRules.KEYWORD_COMPARATOR);
temp.addAll(keywords);
temp.addAll(newRules.getKeywords());
keywords = temp;
}
Map<String,OldNewData> temp = new LinkedHashMap();
for (String keyword : keywords) {
Collection<NumberInfo> oldFractionSamples = oldRules.getFractionSamples(keyword);
Collection<NumberInfo> newFractionSamples = newRules == null ? null : newRules.getFractionSamples(keyword);
// add extra samples if we have some, or if the rules differ
if (newRules != null) {
oldFractionSamples = oldFractionSamples == null ? new TreeSet()
: new TreeSet(oldFractionSamples);
newFractionSamples = newFractionSamples == null ? new TreeSet()
: new TreeSet(newFractionSamples);
// if (extraSamples != null) {
// for (NumberPlus sample : extraSamples) {
// if (oldRules.select(sample.source, sample.visibleFractionDigitCount, sample.fractionalDigits).equals(keyword)) {
// oldFractionSamples.add(sample);
// }
// if (newRules != null && newRules.select(sample.source, sample.visibleFractionDigitCount, sample.fractionalDigits).equals(keyword)) {
// newFractionSamples.add(sample);
// }
// }
// }
// if the rules differ, then add samples from each to the other
if (newRules != null) {
for (NumberInfo sample : oldRules.getFractionSamples()) {
if (newRules.select(sample.source, sample.visibleFractionDigitCount, sample.fractionalDigits).equals(keyword)) {
newFractionSamples.add(sample);
}
}
for (NumberInfo sample : newRules.getFractionSamples()) {
if (oldRules.select(sample.source, sample.visibleFractionDigitCount, sample.fractionalDigits).equals(keyword)) {
oldFractionSamples.add(sample);
}
}
}
}
String oldRulesString = oldRules.getRules(keyword);
if (oldRulesString == null) {
oldRulesString = "";
}
String newRulesString = newRules == null ? "" : newRules.getRules(keyword);
if (newRulesString == null) {
newRulesString = "";
}
temp.put(keyword, new OldNewData(
oldRulesString,
oldFractionSamples == null ? "" : "'" + CollectionUtilities.join(oldFractionSamples, ", "),
newRulesString,
newFractionSamples == null ? "" : "'" + CollectionUtilities.join(newFractionSamples, ", ")
));
}
rulesToLocale.put(temp, locale.toString());
}
System.out.println("Locales\tPC\tOld Rules\tOld Samples\tNew Rules\tNew Samples");
for (Entry<Map<String, OldNewData>, Set<String>> entry : rulesToLocale.keyValuesSet()) {
String localeList = CollectionUtilities.join(entry.getValue(), " ");
for (Entry<String, OldNewData> keywordRulesSamples : entry.getKey().entrySet()) {
System.out.println(
localeList // locale
+ "\t" + keywordRulesSamples.getKey() // keyword
+ "\t" + keywordRulesSamples.getValue().get0() // rules
+ "\t" + keywordRulesSamples.getValue().get1() // samples
+ "\t" + keywordRulesSamples.getValue().get2() // rules
+ "\t" + keywordRulesSamples.getValue().get3() // samples
);
localeList = "";
}
}
if (false) {
System.out.println("\n\nOld Rules for Locales");
for (String localeString : FOCUS_LOCALES) {
ULocale locale = new ULocale(localeString);
PluralRules oldRules = PluralRules.forLocale(locale);
System.out.println("{\"" + locale.toString() + "\", \"" + oldRules.toString() + "\"},");
}
}
}
static String[][] SAMPLE_PATTERNS = {
{"af", "one", "{0} dag"},
{"af", "other", "{0} dae"},
{"am", "one", "{0} ቀን"},
{"am", "other", "{0} ቀናት"}, // fixed to 'other'
{"ar", "few", "{0} ساعات"},
{"ar", "many", "{0} ساعة"},
{"ar", "many", "{0} ساعة"},
{"ar", "one", "ساعة"},
{"ar", "other", "{0} ساعة"},
{"ar", "two", "ساعتان"},
{"ar", "zero", "{0} ساعة"},
{"bg", "one", "{0} ден"},
{"bg", "other", "{0} дена"},
{"bn", "one", "{0} টি আপেল"},
{"bn", "other", "আমার অনেকগুলি আপেল আছে"},
{"bn", "other", "{0} টি আপেল"},
//{"bn", "other", "আমার অনেকগুলি আপেল আছে"},
{"br", "few", "{0} deiz"},
{"br", "many", "{0} a zeizioù"},
{"br", "one", "{0} deiz"},
@ -289,14 +144,10 @@ public class WritePluralRulesData {
{"hr", "few", "za {0} mjeseca"},
{"hr", "many", "za {0} mjeseci"},
{"hr", "one", "za {0} mjesec"},
{"hr", "other", "za sljedeći broj mjeseci: {0}"},
{"hr", "other", "za {0} mjeseci"},
{"hu", "other", "{0} nap"},
{"hy", "few", "{0} օր"},
{"hy", "many", "{0} օր"},
{"hy", "one", "{0} օր"},
{"hy", "other", "{0} օր"},
{"hy", "two", "{0} օր"},
{"hy", "zero", "{0} օր"},
{"hy", "one", "այդ {0} ժամը"},
{"hy", "other", "այդ {0} ժամերը"},
{"id", "other", "{0} hari"},
{"is", "one", "{0} dagur"},
{"is", "other", "{0} dagar"},
@ -380,6 +231,333 @@ public class WritePluralRulesData {
{"zu", "one", "{0} usuku"}, // added from spreadsheet
{"zu", "other", "{0} izinsuku"}, // added from spreadsheet
};
static final Map<ULocale, SamplePatterns> localeToSamplePatterns = new LinkedHashMap();
static {
for (String[] row : SAMPLE_PATTERNS) {
ULocale locale = new ULocale(row[0]);
String keyword = row[1];
String sample = row[2];
SamplePatterns samplePatterns = localeToSamplePatterns.get(locale);
if (samplePatterns == null) {
localeToSamplePatterns.put(locale, samplePatterns = new SamplePatterns());
}
samplePatterns.put(keyword, sample);
}
}
static final String[][] ORIGINAL_SAMPLES = {
{"af", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"am", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"ar", "0.0, 0.1, 0.2, 0.4, 1.0, 1.2, 1.4, 2.0, 2.1, 2.4, 4.0, 4.1, 4.2, 12.0, 12.1, 12.2, 12.4, 101.0, 101.1, 101.2, 101.4, 0.00, 0.01, 0.02, 0.04, 0.30, 0.21, 0.22, 0.14, 1.00, 1.02, 1.04, 1.30, 1.21, 1.22, 1.14, 2.00, 2.01, 2.04, 2.30, 2.21, 2.22, 2.14, 4.00, 4.01, 4.02, 4.30, 4.21, 4.22, 4.14, 12.00, 12.01, 12.02, 12.04, 101.00, 101.01, 101.02, 101.04, 101.30, 101.21, 101.22, 101.14, 0.000, 0.001, 0.002, 0.110, 0.004, 0.030, 0.021, 0.022, 0.014, 0.200, 0.201, 0.202, 1.000, 1.002, 1.110, 1.004, 1.030, 1.021, 1.022, 1.014, 1.200, 1.201, 1.202, 2.000, 2.001, 2.110, 2.004, 2.030, 2.021, 2.022, 2.014, 2.200, 2.201, 2.202, 4.000, 4.001, 4.002, 4.030, 4.021, 4.022, 4.014, 4.200, 4.201, 4.202, 12.000, 12.001, 12.002, 12.110, 12.004, 12.200, 12.201, 12.202, 101.000, 101.001, 101.002, 101.110, 101.004, 101.030, 101.021, 101.022, 101.014"},
{"bg", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"bn", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"br", "0.0, 0.4, 0.6, 21.0, 21.4, 21.6, 22.0, 22.4, 22.6, 4.0, 4.6, 1000000.0, 1000000.4, 1000000.6, 6.0, 6.4, 0.00, 0.21, 0.22, 0.04, 0.20, 0.71, 0.72, 0.14, 0.06, 21.00, 21.22, 21.04, 21.20, 21.71, 21.72, 21.14, 21.06, 22.00, 22.21, 22.04, 22.20, 22.71, 22.72, 22.14, 22.06, 4.00, 4.21, 4.22, 4.20, 4.71, 4.72, 4.14, 4.06, 1000000.00, 1000000.21, 1000000.22, 1000000.04, 1000000.20, 1000000.71, 1000000.72, 1000000.14, 1000000.06, 6.00, 6.21, 6.22, 6.04, 0.000, 0.021, 0.022, 0.004, 0.020, 0.071, 0.072, 0.014, 0.006, 21.000, 21.022, 21.004, 21.020, 21.071, 21.072, 21.014, 21.006, 22.000, 22.021, 22.004, 22.020, 22.071, 22.072, 22.014, 22.006, 4.000, 4.021, 4.022, 4.020, 4.071, 4.072, 4.014, 4.006, 1000000.000, 1000000.021, 1000000.022, 1000000.004, 1000000.020, 1000000.071, 1000000.072, 1000000.014, 1000000.006, 6.000, 6.021, 6.022, 6.004"},
{"ca", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"cs", "0.0, 0.1, 0.3, 0.6, 1.0, 1.3, 1.6, 3.0, 3.1, 3.6, 6.0, 6.1, 6.3, 0.00, 0.01, 0.03, 0.20, 0.21, 0.13, 0.06, 1.00, 1.03, 1.20, 1.21, 1.13, 1.06, 3.00, 3.01, 3.20, 3.21, 3.13, 3.06, 6.00, 6.01, 6.03, 0.000, 0.001, 0.003, 0.020, 0.021, 0.013, 0.006, 1.000, 1.003, 1.020, 1.021, 1.013, 1.006, 3.000, 3.001, 3.020, 3.021, 3.013, 3.006, 6.000, 6.001, 6.003"},
{"da", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"de", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"dz", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"el", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"es", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"et", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"eu", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"fa", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"fi", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"fil", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"fr", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"gl", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"gu", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"he", "0.0, 0.1, 0.2, 0.4, 1.0, 1.2, 1.4, 2.0, 2.1, 2.4, 20.0, 20.1, 20.2, 20.4, 4.0, 4.1, 4.2, 0.00, 0.01, 0.02, 0.20, 0.21, 0.22, 0.04, 1.00, 1.02, 1.20, 1.21, 1.22, 1.04, 2.00, 2.01, 2.20, 2.21, 2.22, 2.04, 20.00, 20.01, 20.02, 20.21, 20.22, 20.04, 4.00, 4.01, 4.02, 4.20, 0.000, 0.001, 0.002, 0.020, 0.021, 0.022, 0.004, 1.000, 1.002, 1.020, 1.021, 1.022, 1.004, 2.000, 2.001, 2.020, 2.021, 2.022, 2.004, 20.000, 20.001, 20.002, 20.021, 20.022, 20.004, 4.000, 4.001, 4.002, 4.020"},
{"hi", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"hr", "0.0, 0.3, 0.6, 21.0, 21.3, 21.6, 3.0, 3.6, 6.0, 6.3, 0.00, 0.21, 0.03, 0.20, 0.13, 0.06, 21.00, 21.03, 21.20, 21.13, 21.06, 3.00, 3.21, 3.20, 3.13, 3.06, 6.00, 6.21, 6.03, 0.000, 0.021, 0.003, 0.020, 0.111, 0.013, 0.006, 21.000, 21.003, 21.020, 21.111, 21.013, 21.006, 3.000, 3.021, 3.020, 3.111, 3.013, 3.006, 6.000, 6.021, 6.003"},
{"hu", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"id", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"is", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"it", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"ja", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"km", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"kn", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"ko", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"lo", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"lt", "0.0, 0.3, 21.0, 21.3, 3.0, 11.0, 11.3, 0.00, 0.21, 0.03, 0.20, 0.13, 21.00, 21.03, 21.20, 21.13, 3.00, 3.21, 3.20, 3.13, 11.00, 11.21, 11.03, 0.000, 0.021, 0.003, 0.020, 0.111, 0.013, 21.000, 21.003, 21.020, 21.111, 21.013, 3.000, 3.021, 3.020, 3.111, 3.013, 11.000, 11.021, 11.003"},
{"lv", "0.0, 0.3, 21.0, 21.3, 3.0, 0.00, 0.21, 0.20, 0.03, 21.00, 21.20, 21.03, 3.00, 3.21, 0.000, 0.021, 0.020, 0.111, 0.003, 21.000, 21.020, 21.111, 21.003, 3.000, 3.021"},
{"ml", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"mr", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"ms", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"nb", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"ne", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"nl", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"pl", "0.0, 0.1, 0.3, 0.6, 1.0, 1.3, 1.6, 3.0, 3.1, 3.6, 6.0, 6.1, 6.3, 0.00, 0.01, 0.03, 0.20, 0.21, 0.13, 0.06, 1.00, 1.03, 1.20, 1.21, 1.13, 1.06, 3.00, 3.01, 3.20, 3.21, 3.13, 3.06, 6.00, 6.01, 6.03, 0.000, 0.001, 0.003, 0.020, 0.021, 0.013, 0.006, 1.000, 1.003, 1.020, 1.021, 1.013, 1.006, 3.000, 3.001, 3.020, 3.021, 3.013, 3.006, 6.000, 6.001, 6.003"},
{"pt", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"pt_PT", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"ro", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 21.0, 21.1, 21.3, 0.00, 0.01, 0.03, 0.30, 0.31, 0.23, 1.00, 1.03, 1.30, 1.31, 1.23, 3.00, 3.01, 3.30, 3.31, 3.23, 21.00, 21.01, 21.03, 0.000, 0.001, 0.110, 0.101, 0.003, 0.030, 0.031, 0.023, 1.000, 1.110, 1.101, 1.003, 1.030, 1.031, 1.023, 3.000, 3.001, 3.030, 3.031, 3.023, 21.000, 21.001, 21.110, 21.101, 21.003"},
{"ru", "0.0, 0.3, 0.6, 21.0, 21.3, 21.6, 3.0, 3.6, 6.0, 6.3, 0.00, 0.21, 0.03, 0.20, 0.13, 0.06, 21.00, 21.03, 21.20, 21.13, 21.06, 3.00, 3.21, 3.20, 3.13, 3.06, 6.00, 6.21, 6.03, 0.000, 0.021, 0.003, 0.020, 0.111, 0.013, 0.006, 21.000, 21.003, 21.020, 21.111, 21.013, 21.006, 3.000, 3.021, 3.020, 3.111, 3.013, 3.006, 6.000, 6.021, 6.003"},
{"si", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"sk", "0.0, 0.1, 0.3, 0.6, 1.0, 1.3, 1.6, 3.0, 3.1, 3.6, 6.0, 6.1, 6.3, 0.00, 0.01, 0.03, 0.20, 0.21, 0.13, 0.06, 1.00, 1.03, 1.20, 1.21, 1.13, 1.06, 3.00, 3.01, 3.20, 3.21, 3.13, 3.06, 6.00, 6.01, 6.03, 0.000, 0.001, 0.003, 0.020, 0.021, 0.013, 0.006, 1.000, 1.003, 1.020, 1.021, 1.013, 1.006, 3.000, 3.001, 3.020, 3.021, 3.013, 3.006, 6.000, 6.001, 6.003"},
{"sl", "0.0, 0.4, 0.6, 101.0, 101.4, 101.6, 102.0, 102.4, 102.6, 4.0, 4.6, 6.0, 6.4, 0.00, 0.04, 0.20, 0.21, 0.22, 0.14, 0.06, 101.00, 101.04, 101.20, 101.21, 101.22, 101.14, 101.06, 102.00, 102.04, 102.20, 102.21, 102.22, 102.14, 102.06, 4.00, 4.20, 4.21, 4.22, 4.14, 4.06, 6.00, 6.04, 0.000, 0.101, 0.102, 0.004, 0.020, 0.021, 0.022, 0.014, 0.006, 101.000, 101.102, 101.004, 101.020, 101.021, 101.022, 101.014, 101.006, 102.000, 102.101, 102.004, 102.020, 102.021, 102.022, 102.014, 102.006, 4.000, 4.101, 4.102, 4.020, 4.021, 4.022, 4.014, 4.006, 6.000, 6.101, 6.102, 6.004"},
{"sr", "0.0, 0.3, 0.6, 21.0, 21.3, 21.6, 3.0, 3.6, 6.0, 6.3, 0.00, 0.21, 0.03, 0.20, 0.13, 0.06, 21.00, 21.03, 21.20, 21.13, 21.06, 3.00, 3.21, 3.20, 3.13, 3.06, 6.00, 6.21, 6.03, 0.000, 0.021, 0.003, 0.020, 0.111, 0.013, 0.006, 21.000, 21.003, 21.020, 21.111, 21.013, 21.006, 3.000, 3.021, 3.020, 3.111, 3.013, 3.006, 6.000, 6.021, 6.003"},
{"sv", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"sw", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"ta", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"te", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"th", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"tr", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"uk", "0.0, 0.3, 0.6, 21.0, 21.3, 21.6, 3.0, 3.6, 6.0, 6.3, 0.00, 0.21, 0.03, 0.20, 0.13, 0.06, 21.00, 21.03, 21.20, 21.13, 21.06, 3.00, 3.21, 3.20, 3.13, 3.06, 6.00, 6.21, 6.03, 0.000, 0.021, 0.003, 0.020, 0.111, 0.013, 0.006, 21.000, 21.003, 21.020, 21.111, 21.013, 21.006, 3.000, 3.021, 3.020, 3.111, 3.013, 3.006, 6.000, 6.021, 6.003"},
{"ur", "0.0, 0.1, 0.3, 1.0, 1.3, 3.0, 3.1, 0.00, 0.01, 0.20, 0.21, 0.03, 1.00, 1.20, 1.21, 1.03, 3.00, 3.01, 0.000, 0.001, 0.020, 0.021, 0.003, 1.000, 1.020, 1.021, 1.003, 3.000, 3.001"},
{"vi", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"zh", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
{"zh_Hant", "0.0, 0.2, 2.0, 0.00, 0.20, 0.02, 2.00, 0.000, 0.020, 0.002, 2.000"},
};
static final Map<ULocale,List<NumberInfo>> LOCALE_TO_ORIGINALS = new HashMap();
static {
for (String[] pair : ORIGINAL_SAMPLES) {
ArrayList<NumberInfo> row = new ArrayList();
for (String s : pair[1].split("\\s*,\\s*")) {
row.add(new NumberInfo(s));
}
LOCALE_TO_ORIGINALS.put(new ULocale(pair[0]), row);
}
}
public static void getOriginalSamples() {
try {
File file = new File("/Users/markdavis/workspace/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/plurals.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
Map<String,String> localeToSamples = new TreeMap();
Matcher m = Pattern.compile("\\d+[.]\\d+").matcher("");
while (true) {
String line = br.readLine();
if (line == null) break;
String[] parts = line.split("\t");
String locale = parts[0];
String pattern = parts[1];
boolean found = m.reset(pattern).find();
String sample = found
? m.group()
: "-1";
String samples = localeToSamples.get(locale);
localeToSamples.put(locale, samples == null ? sample : samples + ", " + sample);
}
br.close();
for (Entry<String, String> entry : localeToSamples.entrySet()) {
System.out.println("{\"" + entry.getKey() + "\", \"" + entry.getValue() + "\"},");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void generateLOCALE_SNAPSHOT(PluralRulesFactory pluralRulesFactory) {
StringBuilder builder = new StringBuilder();
Map<Set<StandardPluralCategories>, Relation<String, ULocale>> keywordsToData = new TreeMap(StandardPluralCategories.SHORTEST_FIRST);
for (ULocale locale : pluralRulesFactory.getAvailableULocales()) {
builder.setLength(0);
PluralRules rules = pluralRulesFactory.forLocale(locale);
boolean firstKeyword = true;
EnumSet<StandardPluralCategories> keywords = StandardPluralCategories.getSet(rules.getKeywords());
Relation<String, ULocale> samplesToLocales = keywordsToData.get(keywords);
if (samplesToLocales == null) {
keywordsToData.put(keywords, samplesToLocales = Relation.of(
new LinkedHashMap<String,Set<ULocale>>(), LinkedHashSet.class));
}
//System.out.println(locale);
for (StandardPluralCategories keyword : keywords) {
if (firstKeyword) {
firstKeyword = false;
} else {
builder.append(";\t");
}
Collection<NumberInfo> samples = rules.getFractionSamples(keyword.toString());
if (samples.size() == 0) {
throw new IllegalArgumentException();
}
builder.append(keyword).append(": ");
boolean first = true;
for (NumberInfo n : samples) {
if (first) {
first = false;
} else {
builder.append(", ");
}
builder.append(n);
// for (double j : samples) {
// double sample = i + j/100;
// }
}
}
samplesToLocales.put(builder.toString(), locale);
}
System.out.println(" static final String[] LOCALE_SNAPSHOT = {");
for (Entry<Set<StandardPluralCategories>, Relation<String, ULocale>> keywordsAndData : keywordsToData.entrySet()) {
System.out.println("\n // " + keywordsAndData.getKey());
for (Entry<String, Set<ULocale>> samplesAndLocales : keywordsAndData.getValue().keyValuesSet()) {
Set<ULocale> locales = samplesAndLocales.getValue();
// check functional equivalence
boolean[] isAvailable = new boolean[1];
for (ULocale locale : locales) {
ULocale base = pluralRulesFactory.getFunctionalEquivalent(locale, isAvailable);
if (!locales.contains(base) && base.toString().length() != 0) {
System.out.println("**" + locales + " doesn't contain " + base);
}
}
System.out.println(
" \"" + CollectionUtilities.join(locales, ",")
+ ";\t" + samplesAndLocales.getKey() + "\",");
}
}
System.out.println(" };");
}
private static class OldNewData extends Row.R5<String, String, String, String, String> {
public OldNewData(String oldRules, String oldSamples, String newRules, String newSamples, String intDiff) {
super(oldRules, oldSamples, newRules, newSamples, intDiff);
}
}
public static void showRules() {
if (true) {
// for debugging
PluralRules rules = PluralRulesFactory.ALTERNATE.forLocale(new ULocale("lv"));
rules.select(2.0d, 2, 0);
}
// System.out.println(new TreeSet(Arrays.asList(locales)));
Relation<Map<String,OldNewData>, String> rulesToLocale = Relation.of(
new TreeMap<Map<String,OldNewData>, Set<String>>(
new CollectionUtilities.MapComparator<String,OldNewData>()), TreeSet.class);
for (String localeString : FOCUS_LOCALES) {
ULocale locale = new ULocale(localeString);
PluralRules oldRules = PluralRulesFactory.NORMAL.forLocale(locale);
PluralRules newRules = PluralRulesFactory.ALTERNATE.hasOverride(locale) ? PluralRulesFactory.ALTERNATE.forLocale(locale) : null;
Set<String> keywords = oldRules.getKeywords();
if (newRules != null) {
TreeSet<String> temp = new TreeSet<String>(PluralRules.KEYWORD_COMPARATOR);
temp.addAll(keywords);
temp.addAll(newRules.getKeywords());
keywords = temp;
}
Map<String,OldNewData> temp = new LinkedHashMap();
for (String keyword : keywords) {
Collection<NumberInfo> oldFractionSamples = oldRules.getFractionSamples(keyword);
Collection<NumberInfo> newFractionSamples = newRules == null ? null : newRules.getFractionSamples(keyword);
// add extra samples if we have some, or if the rules differ
if (newRules != null) {
oldFractionSamples = oldFractionSamples == null ? new TreeSet()
: new TreeSet(oldFractionSamples);
newFractionSamples = newFractionSamples == null ? new TreeSet()
: new TreeSet(newFractionSamples);
// if (extraSamples != null) {
// for (NumberPlus sample : extraSamples) {
// if (oldRules.select(sample.source, sample.visibleFractionDigitCount, sample.fractionalDigits).equals(keyword)) {
// oldFractionSamples.add(sample);
// }
// if (newRules != null && newRules.select(sample.source, sample.visibleFractionDigitCount, sample.fractionalDigits).equals(keyword)) {
// newFractionSamples.add(sample);
// }
// }
// }
// if the rules differ, then add samples from each to the other
if (newRules != null) {
for (NumberInfo sample : oldRules.getFractionSamples()) {
if (newRules.select(sample).equals(keyword)) {
newFractionSamples.add(sample);
}
}
for (NumberInfo sample : newRules.getFractionSamples()) {
if (oldRules.select(sample).equals(keyword)) {
oldFractionSamples.add(sample);
}
}
}
}
String intDiff = newRules == null ? "" : getDiffs(oldFractionSamples, newFractionSamples);
String oldRulesString = rulesForDisplay(oldRules, keyword);
if (oldRulesString == null) {
oldRulesString = "";
}
String newRulesString = newRules == null ? "" : rulesForDisplay(newRules, keyword);
if (newRulesString == null) {
newRulesString = "";
}
if (oldRulesString.length() == 0 && newRulesString.length() != 0) {
oldRulesString = "<NEW SPLITS>";
} else if (oldRulesString.length() != 0 && newRulesString.length() == 0 && newRules != null) {
newRulesString = "<NEW MERGES>";
}
temp.put(keyword, new OldNewData(
oldRulesString,
oldFractionSamples == null ? "" : "'" + CollectionUtilities.join(oldFractionSamples, ", "),
newRulesString,
newFractionSamples == null ? "" : "'" + CollectionUtilities.join(newFractionSamples, ", "),
intDiff.length() == 0 ? "" : "'" + intDiff
));
}
rulesToLocale.put(temp, locale.toString());
}
System.out.println("Locales\tPC\tOld Rules\tOld Sample Numbers\tNew Rules\tNew Sample Numbers\tInt-Diff");
for (Entry<Map<String, OldNewData>, Set<String>> entry : rulesToLocale.keyValuesSet()) {
String localeList = CollectionUtilities.join(entry.getValue(), " ");
for (Entry<String, OldNewData> keywordRulesSamples : entry.getKey().entrySet()) {
System.out.println(
localeList // locale
+ "\t" + keywordRulesSamples.getKey() // keyword
+ "\t" + keywordRulesSamples.getValue().get0() // rules
+ "\t" + keywordRulesSamples.getValue().get1() // samples
+ "\t" + keywordRulesSamples.getValue().get2() // rules
+ "\t" + keywordRulesSamples.getValue().get3() // samples
+ "\t" + keywordRulesSamples.getValue().get4() // int diff
);
localeList = "";
}
}
if (false) {
System.out.println("\n\nOld Rules for Locales");
for (String localeString : FOCUS_LOCALES) {
ULocale locale = new ULocale(localeString);
PluralRules oldRules = PluralRules.forLocale(locale);
System.out.println("{\"" + locale.toString() + "\", \"" + oldRules.toString() + "\"},");
}
}
}
/**
* @param oldFractionSamples
* @param newFractionSamples
* @return
*/
private static String getDiffs(Collection<NumberInfo> oldFractionSamples,
Collection<NumberInfo> newFractionSamples) {
oldFractionSamples = oldFractionSamples == null ? Collections.EMPTY_SET : oldFractionSamples;
newFractionSamples = newFractionSamples == null ? Collections.EMPTY_SET : newFractionSamples;
TreeSet<NumberInfo> additions = new TreeSet(newFractionSamples);
additions.removeAll(oldFractionSamples);
TreeSet<NumberInfo> removals = new TreeSet(oldFractionSamples);
removals.removeAll(newFractionSamples);
StringBuffer result = new StringBuffer();
addInts(additions, "+", result);
addInts(removals, "-", result);
return result.toString();
}
private static void addInts(TreeSet<NumberInfo> additions, String title, StringBuffer result) {
for (NumberInfo n : additions) {
if (n.visibleFractionDigitCount == 0) {
if (result.length() != 0) {
result.append("; ");
}
result.append(title).append(n);
}
}
}
static final Set<String> NEW_LOCALES = new HashSet(Arrays.asList("az,ka,kk,ky,mk,mn,my,pa,ps,sq,uz".split("\\s*,\\s*")));
static class SamplePatterns {
@ -389,7 +567,7 @@ public class WritePluralRulesData {
if (keywordToPattern.containsKey(keyword)) {
throw new IllegalArgumentException("Duplicate keyword <" + keyword + ">");
} else {
keywordToPattern.put(keyword, sample);
keywordToPattern.put(keyword, sample.replace(" ", "\u00A0"));
}
}
public void checkErrors(Set<String> set) {
@ -397,7 +575,7 @@ public class WritePluralRulesData {
for (String keyword : set) {
String error = "";
String sample = keywordToPattern.get(keyword);
String skeleton = sample.replace(" ", "").replace("{0}", "");
String skeleton = sample.replace(" ", "").replaceAll("\\s*\\{0\\}\\s*", "");
String oldSkeletonKeyword = skeletonToKeyword.get(skeleton);
if (oldSkeletonKeyword != null) {
if (error.length() != 0) {
@ -416,22 +594,22 @@ public class WritePluralRulesData {
}
}
static void generateSamples() {
Map<ULocale, SamplePatterns> localeToSamplePatterns = new LinkedHashMap();
for (String[] row : SAMPLE_PATTERNS) {
ULocale locale = new ULocale(row[0]);
String keyword = row[1];
String sample = row[2];
SamplePatterns samplePatterns = localeToSamplePatterns.get(locale);
if (samplePatterns == null) {
localeToSamplePatterns.put(locale, samplePatterns = new SamplePatterns());
}
samplePatterns.put(keyword, sample);
}
enum SampleStyle {original, modified, verify}
static void generateSamples(SampleStyle sampleStyle) throws IOException {
LinkedHashSet<ULocale> skippedLocales = new LinkedHashSet<ULocale>();
System.out.println("Locale\tPC\tPattern\tSample\tErrors");
System.out.println("Locale\tPC\tPattern\tSamples\tRules\tErrors (" + sampleStyle + ")");
BufferedWriter writer = null;
for (String localeString : FOCUS_LOCALES) {
ULocale locale = new ULocale(localeString);
if (sampleStyle == SampleStyle.verify) {
writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(TARGETDIR + "fraction-" + locale + ".tsv"), Charset.forName("UTF-8")));
writer.write("Plural Category\tEnglish Number\tFormatted Sample\tAcceptable?\tReplacement\n");
}
NumberFormat nf = NumberFormat.getInstance(new ULocale(locale.toString()+"@numbers=latn"));
PluralRules newRules = PluralRulesFactory.ALTERNATE.forLocale(locale);
SamplePatterns samplePatterns = localeToSamplePatterns.get(locale);
if (samplePatterns == null && NEW_LOCALES.contains(localeString)) {
@ -442,108 +620,105 @@ public class WritePluralRulesData {
samplePatterns.checkErrors(newRules.getKeywords());
// now print.
for (String keyword : newRules.getKeywords()) {
if (sampleStyle != SampleStyle.modified) {
Collection<NumberInfo> samples = getSamples(newRules, keyword,
sampleStyle == SampleStyle.verify ? null : locale);
for (NumberInfo sample : samples) {
String pattern = samplePatterns.keywordToPattern.get(keyword);
String str = format(pattern, nf, sample);
if (sampleStyle == SampleStyle.verify) {
writer.write(keyword + "\t'" + sample + "\t" + str + "\n");
} else {
System.out.println(locale + "\t" + keyword + "\t" + sample + "\t" + str);
}
}
continue;
}
String pattern = null;
String error = null;
Collection<NumberInfo> samples = newRules.getFractionSamples(keyword);
Collection<NumberInfo> samples = getSamples(newRules, keyword, null);
NumberInfo first = samples.iterator().next();
String sample = "??? " + first.toString();
String rule = "";
if (samplePatterns == null) {
pattern = "???";
error = "\tERROR: Locale data missing";
} else {
pattern = samplePatterns.keywordToPattern.get(keyword);
rule = rulesForDisplay(newRules, keyword);
error = samplePatterns.keywordToErrors.get(keyword);
if (pattern == null) {
pattern = "???";
error = "\tERROR: Needed for new rules";
} else {
sample = pattern.replace("{0}", first.toString());
StringBuilder buffer = new StringBuilder();
for (NumberInfo x : samples) {
if (buffer.length() != 0) {
buffer.append("; ");
}
String str = format(pattern, nf, x);
buffer.append(str);
}
sample = buffer.toString();
}
}
System.out.println(locale + "\t" + keyword
+ "\t" + pattern
+ "\t" + sample
+ "\t" + rule
+ error
);
}
if (sampleStyle == SampleStyle.verify) {
writer.close();
}
}
System.out.println("SKIP:\t\t\t" + skippedLocales);
}
private static Collection<NumberInfo> getSamples(PluralRules newRules, String keyword, ULocale locale) {
if (locale == null) {
return newRules.getFractionSamples(keyword);
}
Collection<NumberInfo> result = new ArrayList();
List<NumberInfo> originals = LOCALE_TO_ORIGINALS.get(locale);
if (originals == null) {
return newRules.getFractionSamples(keyword);
}
for (NumberInfo s : originals) {
if (keyword.equals(newRules.select(s))) {
result.add(s);
}
}
if (result.size() == 0) {
return newRules.getFractionSamples(keyword);
}
return result;
}
private static String rulesForDisplay(PluralRules newRules, String keyword) {
String rule;
rule = newRules.getRules(keyword);
rule = rule != null ? rule.replace(" ", "\u00A0").replace("\u00A0or", " or")
: keyword.equals("other") ? "<all else>"
: "";
return rule;
}
private static String format(String pattern, NumberFormat nf, NumberInfo x) {
nf.setMaximumFractionDigits(x.visibleFractionDigitCount);
nf.setMinimumFractionDigits(x.visibleFractionDigitCount);
String str = nf.format(x.source);
return pattern.replace("{0}", str);
}
/**
*
*/
private static void generateVerificationSamples() {
// TODO Auto-generated method stub
}
static String[][] OLDRULES = {
{"af", "one: n is 1"},
{"am", "one: n in 0..1"},
{"ar", "zero: n is 0; one: n is 1; two: n is 2; few: n mod 100 in 3..10; many: n mod 100 in 11..99"},
{"az", "other: null"},
{"bg", "one: n is 1"},
{"bn", "one: n is 1"},
{"ca", "one: n is 1"},
{"cs", "one: n is 1; few: n in 2..4"},
{"cy", "zero: n is 0; one: n is 1; two: n is 2; few: n is 3; many: n is 6"},
{"da", "one: n is 1"},
{"de", "one: n is 1"},
{"el", "one: n is 1"},
{"en", "one: n is 1"},
{"es", "one: n is 1"},
{"et", "one: n is 1"},
{"eu", "one: n is 1"},
{"fa", "other: null"},
{"fi", "one: n is 1"},
{"fil", "one: n in 0..1"},
{"fr", "one: n within 0..2 and n is not 2"},
{"gl", "one: n is 1"},
{"gu", "one: n is 1"},
{"hi", "one: n in 0..1"},
{"hr", "one: n mod 10 is 1 and n mod 100 is not 11; few: n mod 10 in 2..4 and n mod 100 not in 12..14; many: n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14"},
{"hu", "other: null"},
{"hy", "one: n is 1"},
{"id", "other: null"},
{"is", "one: n is 1"},
{"it", "one: n is 1"},
{"he", "one: n is 1; two: n is 2; many: n is not 0 and n mod 10 is 0"},
{"ja", "other: null"},
{"ka", "other: null"},
{"kk", "one: n is 1"},
{"km", "other: null"},
{"kn", "other: null"},
{"ko", "other: null"},
{"ky", "one: n is 1"},
{"lo", "other: null"},
{"lt", "one: n mod 10 is 1 and n mod 100 not in 11..19; few: n mod 10 in 2..9 and n mod 100 not in 11..19"},
{"lv", "zero: n is 0; one: n mod 10 is 1 and n mod 100 is not 11"},
{"mk", "one: n mod 10 is 1 and n is not 11"},
{"ml", "one: n is 1"},
{"mn", "one: n is 1"},
{"mr", "one: n is 1"},
{"ms", "other: null"},
{"my", "other: null"},
{"ne", "one: n is 1"},
{"nl", "one: n is 1"},
{"nb", "one: n is 1"},
{"pa", "one: n is 1"},
{"pl", "one: n is 1; few: n mod 10 in 2..4 and n mod 100 not in 12..14; many: n is not 1 and n mod 10 in 0..1 or n mod 10 in 5..9 or n mod 100 in 12..14"},
{"ps", "one: n is 1"},
{"pt", "one: n is 1"},
{"ro", "one: n is 1; few: n is 0 or n is not 1 and n mod 100 in 1..19"},
{"ru", "one: n mod 10 is 1 and n mod 100 is not 11; few: n mod 10 in 2..4 and n mod 100 not in 12..14; many: n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14"},
{"si", "other: null"},
{"sk", "one: n is 1; few: n in 2..4"},
{"sl", "one: n mod 100 is 1; two: n mod 100 is 2; few: n mod 100 in 3..4"},
{"sq", "one: n is 1"},
{"sr", "one: n mod 10 is 1 and n mod 100 is not 11; few: n mod 10 in 2..4 and n mod 100 not in 12..14; many: n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14"},
{"sv", "one: n is 1"},
{"sw", "one: n is 1"},
{"ta", "one: n is 1"},
{"te", "one: n is 1"},
{"th", "other: null"},
{"tr", "other: null"},
{"uk", "one: n mod 10 is 1 and n mod 100 is not 11; few: n mod 10 in 2..4 and n mod 100 not in 12..14; many: n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14"},
{"ur", "one: n is 1"},
{"uz", "other: null"},
{"vi", "other: null"},
{"zh", "other: null"},
{"zu", "one: n is 1"},
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 1996-2012, International Business Machines Corporation and *
* Copyright (C) 1996-2013, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
@ -125,6 +125,32 @@ public class CompatibilityTest extends TestFmwk
private static final String[][] SKIP_CASES = {
{"ICU_3.8.1", "com.ibm.icu.text.PluralFormat.dat"},
{"ICU_3.8.1", "com.ibm.icu.text.PluralRules.dat"},
// ICU 52 is not serialization-compatible with previous versions.
{"ICU_3.8.1", "com.ibm.icu.text.PluralFormat.dat"},
{"ICU_3.8.1", "com.ibm.icu.text.PluralRules.dat"},
{"ICU_4.0", "com.ibm.icu.text.PluralFormat.dat"},
{"ICU_4.0", "com.ibm.icu.text.PluralRules.dat"},
{"ICU_4.2.1", "com.ibm.icu.text.PluralFormat.dat"},
{"ICU_4.2.1", "com.ibm.icu.text.PluralRules.dat"},
{"ICU_4.4", "com.ibm.icu.text.PluralFormat.dat"},
{"ICU_4.4", "com.ibm.icu.text.PluralRules.dat"},
{"ICU_4.4", "com.ibm.icu.text.CurrencyPluralInfo.dat"},
{"ICU_4.6", "com.ibm.icu.text.PluralFormat.dat"},
{"ICU_4.6", "com.ibm.icu.text.PluralRules.dat"},
{"ICU_4.6", "com.ibm.icu.text.CurrencyPluralInfo.dat"},
{"ICU_4.8", "com.ibm.icu.text.PluralFormat.dat"},
{"ICU_4.8", "com.ibm.icu.text.PluralRules.dat"},
{"ICU_4.8", "com.ibm.icu.text.CurrencyPluralInfo.dat"},
{"ICU_49.1", "com.ibm.icu.text.CurrencyPluralInfo.dat"},
{"ICU_49.1", "com.ibm.icu.text.PluralFormat.dat"},
{"ICU_49.1", "com.ibm.icu.text.PluralRules.dat"},
{"ICU_50.1", "com.ibm.icu.text.CurrencyPluralInfo.dat"},
{"ICU_50.1", "com.ibm.icu.text.PluralFormat.dat"},
{"ICU_50.1", "com.ibm.icu.text.PluralRules.dat"},
{"ICU_51.1", "com.ibm.icu.text.CurrencyPluralInfo.dat"},
{"ICU_51.1", "com.ibm.icu.text.PluralFormat.dat"},
{"ICU_51.1", "com.ibm.icu.text.PluralRules.dat"},
{"ICU_3.6", "com.ibm.icu.text.RuleBasedNumberFormat.dat"},
{"ICU_3.8.1", "com.ibm.icu.text.RuleBasedNumberFormat.dat"},
{"ICU_4.0", "com.ibm.icu.text.RuleBasedNumberFormat.dat"},

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (c) 2004-2012, International Business Machines
* Copyright (c) 2004-2013, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
*
@ -2192,6 +2192,19 @@ public class FormatTests
return a.equals(b);
}
}
public static class PluralRulesSerialProxyHandler implements SerializableTest.Handler {
// Tested through PluralRules, so just a stub here to keep CoverageTest happy
final String[] cannedRules = {};
public Object[] getTestObjects() {
return new PluralRules[cannedRules.length];
}
public boolean hasSameBehavior(Object a, Object b) {
return a.equals(b);
}
}
public static class TimeUnitFormatHandler implements SerializableTest.Handler {

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 1996-2012, International Business Machines Corporation and *
* Copyright (C) 1996-2013, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
@ -672,6 +672,7 @@ public class SerializableTest extends TestFmwk.TestGroup
map.put("com.ibm.icu.impl.DateNumberFormat", new FormatTests.DateNumberFormatHandler());
map.put("com.ibm.icu.text.PluralFormat", new FormatTests.PluralFormatHandler());
map.put("com.ibm.icu.text.PluralRules", new FormatTests.PluralRulesHandler());
map.put("com.ibm.icu.text.PluralRulesSerialProxy", new FormatTests.PluralRulesSerialProxyHandler());
map.put("com.ibm.icu.text.TimeUnitFormat", new FormatTests.TimeUnitFormatHandler());
map.put("com.ibm.icu.text.SelectFormat", new FormatTests.SelectFormatHandler());
map.put("com.ibm.icu.impl.TimeZoneNamesImpl", new FormatTests.TimeZoneNamesHandler());