blob: 31f8db182340dbeda982b18d668dbf9135f83a98 [file] [log] [blame]
package org.unicode.cldr.test;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.unicode.cldr.util.CLDRFile;
import org.unicode.cldr.util.CLDRFile.Status;
import org.unicode.cldr.util.DateTimeCanonicalizer.DateTimePatternType;
import org.unicode.cldr.util.PatternCache;
import com.ibm.icu.text.DateTimePatternGenerator;
import com.ibm.icu.text.DateTimePatternGenerator.VariableField;
/**
* Class for computing the date order of date formats.
* This class is was originally package-visible, but has been modified to public
* for the sake of the unit test.
*/
public class DateOrder implements Comparable<DateOrder> {
private int etype1;
private int etype2;
public DateOrder(int a, int b) {
etype1 = a;
etype2 = b;
}
@Override
public boolean equals(Object obj) {
DateOrder that = (DateOrder) obj;
return that.etype1 == etype1 && that.etype2 == etype2;
}
@Override
public int hashCode() {
return etype1 * 37 + etype2;
}
@Override
public String toString() {
return "<" + toString2(etype1) + "," + toString2(etype2) + ">";
}
private String toString2(int etype) {
switch (etype >> 1) {
}
return (VariableField.getCanonicalCode(etype >> 1)) + ((etype & 1) == 0 ? "†††" : "ⁿ");
}
@Override
public int compareTo(DateOrder that) {
int diff;
if (0 != (diff = etype1 - that.etype1)) {
return diff;
}
return etype2 - that.etype2;
}
public static Map<String, Map<DateOrder, String>> getOrderingInfo(CLDRFile plain, CLDRFile resolved,
DateTimePatternGenerator.FormatParser fp) {
Map<String, Map<DateOrder, String>> pathsWithConflictingOrder2sample = new HashMap<String, Map<DateOrder, String>>();
Status status = new Status();
try {
Map<String, Map<DateOrder, Set<String>>> type2order2set = new HashMap<String, Map<DateOrder, Set<String>>>();
Matcher typeMatcher = PatternCache.get("\\[@type=\"([^\"]*)\"]").matcher("");
int[] soFar = new int[50];
int lenSoFar = 0;
for (String path : resolved) {
if (DateTimePatternType.STOCK_AVAILABLE_INTERVAL_PATTERNS.contains(DateTimePatternType.fromPath(path))) {
if (path.contains("[@id=\"Ed\"]")) {
continue;
}
if (!path.equals(status.pathWhereFound)) {
continue;
}
typeMatcher.reset(path).find();
String type = typeMatcher.group(1);
Map<DateOrder, Set<String>> pairCount = type2order2set.get(type);
if (pairCount == null) {
type2order2set.put(type, pairCount = new HashMap<DateOrder, Set<String>>());
}
boolean isInterval = path.contains("intervalFormatItem");
lenSoFar = 0;
String value = resolved.getStringValue(path);
// register a comparison for all of the items so far
for (Object item : fp.set(value).getItems()) {
if (item instanceof VariableField) {
VariableField variable = (VariableField) item;
int eType = variable.getType() * 2 + (variable.isNumeric() ? 1 : 0);
if (isInterval && find(eType, soFar, lenSoFar)) {
lenSoFar = 0; // restart the clock
soFar[lenSoFar++] = eType;
continue;
}
for (int i = 0; i < lenSoFar; ++i) {
DateOrder order = new DateOrder(soFar[i], eType);
Set<String> paths = pairCount.get(order);
if (paths == null) {
pairCount.put(order, paths = new HashSet<String>());
}
paths.add(path);
}
soFar[lenSoFar++] = eType;
}
}
}
}
// determine conflicts, and mark
for (Entry<String, Map<DateOrder, Set<String>>> typeAndOrder2set : type2order2set.entrySet()) {
Map<DateOrder, Set<String>> pairCount = typeAndOrder2set.getValue();
HashSet<DateOrder> alreadySeen = new HashSet<DateOrder>();
for (Entry<DateOrder, Set<String>> entry : pairCount.entrySet()) {
DateOrder thisOrder = entry.getKey();
if (alreadySeen.contains(thisOrder)) {
continue;
}
DateOrder reverseOrder = new DateOrder(thisOrder.etype2, thisOrder.etype1);
Set<String> reverseSet = pairCount.get(reverseOrder);
DateOrder sample = thisOrder.compareTo(reverseOrder) < 0 ? thisOrder : reverseOrder;
Set<String> thisPaths = entry.getValue();
if (reverseSet != null) {
addConflictingPaths(plain, sample, reverseSet, thisPaths, pathsWithConflictingOrder2sample);
addConflictingPaths(plain, sample, thisPaths, reverseSet, pathsWithConflictingOrder2sample);
alreadySeen.add(reverseOrder);
}
}
}
// for debugging, show conflicts
if (CheckDates.GREGORIAN_ONLY) {
for (Entry<String, Map<DateOrder, String>> entry : pathsWithConflictingOrder2sample.entrySet()) {
String path1 = entry.getKey();
String locale1 = resolved.getSourceLocaleID(path1, status);
String value1 = resolved.getStringValue(path1);
Map<DateOrder, String> orderString = entry.getValue();
for (Entry<DateOrder, String> entry2 : orderString.entrySet()) {
DateOrder order2 = entry2.getKey();
String path2 = entry2.getValue();
String locale2 = resolved.getSourceLocaleID(path2, status);
String value2 = resolved.getStringValue(path2);
System.out.println(order2 + "\t" + value1 + "\t" + value2 + "\t" + locale1 + "\t" + locale2
+ "\t" + path1 + "\t" + path2);
}
}
}
} catch (RuntimeException e) {
throw e;
}
return pathsWithConflictingOrder2sample;
}
/**
* Add paths with a conflicting date order to the specified map.
*
* @param cldrFile
* @param order
* @param paths
* the set of paths to add conflicting paths for
* @param conflictingPaths
* the set of conflicting paths
* @param pathsWithConflictingOrder2sample
*/
private static void addConflictingPaths(CLDRFile cldrFile, DateOrder order, Set<String> paths,
Set<String> conflictingPaths, Map<String, Map<DateOrder, String>> pathsWithConflictingOrder2sample) {
for (String first : paths) {
FormatType firstType = FormatType.getType(first);
for (String otherPath : conflictingPaths) {
FormatType otherType = FormatType.getType(otherPath);
// Add the first conflicting path that has a high enough
// importance to be considered.
if (!otherType.isLessImportantThan(firstType)) {
addItem(cldrFile, first, order, otherPath, pathsWithConflictingOrder2sample);
break;
}
}
}
}
private static boolean find(int eType, int[] soFar, int lenSoFar) {
for (int i = 0; i < lenSoFar; ++i) {
if (eType == soFar[i]) {
return true;
}
}
return false;
}
private static void addItem(CLDRFile plain, String path, DateOrder sample,
String conflictingPath, Map<String,
Map<DateOrder, String>> pathsWithConflictingOrder2sample) {
String value = plain.getStringValue(path);
if (value == null) {
return;
}
Map<DateOrder, String> order2path = pathsWithConflictingOrder2sample.get(path);
if (order2path == null) {
pathsWithConflictingOrder2sample.put(path, order2path = new TreeMap<DateOrder, String>());
}
order2path.put(sample, conflictingPath);
}
/**
* Enum for deciding the priority of paths for checking date order
* consistency.
*/
private enum FormatType {
DATE(3), TIME(3), AVAILABLE(2), INTERVAL(1);
private static final Pattern DATETIME_PATTERN = PatternCache.get("/(date|time|available|interval)Formats");
// Types with a higher value have higher importance.
private int importance;
private FormatType(int importance) {
this.importance = importance;
}
/**
* @param path
* @return the format type of the specified path
*/
public static FormatType getType(String path) {
Matcher matcher = DATETIME_PATTERN.matcher(path);
if (matcher.find()) {
return FormatType.valueOf(matcher.group(1).toUpperCase());
}
throw new IllegalArgumentException("Path is not a datetime format type: " + path);
}
/**
* @return true if this FormatType is of lower importance than otherType
*/
public boolean isLessImportantThan(FormatType otherType) {
return otherType.importance - importance > 0;
}
}
}