Home | History | Annotate | Download | only in test
      1 package org.unicode.cldr.test;
      2 
      3 import java.util.HashMap;
      4 import java.util.HashSet;
      5 import java.util.Map;
      6 import java.util.Map.Entry;
      7 import java.util.Set;
      8 import java.util.TreeMap;
      9 import java.util.regex.Matcher;
     10 import java.util.regex.Pattern;
     11 
     12 import org.unicode.cldr.util.CLDRFile;
     13 import org.unicode.cldr.util.CLDRFile.Status;
     14 import org.unicode.cldr.util.DateTimeCanonicalizer.DateTimePatternType;
     15 import org.unicode.cldr.util.PatternCache;
     16 
     17 import com.ibm.icu.text.DateTimePatternGenerator;
     18 import com.ibm.icu.text.DateTimePatternGenerator.VariableField;
     19 
     20 /**
     21  * Class for computing the date order of date formats.
     22  * This class is was originally package-visible, but has been modified to public
     23  * for the sake of the unit test.
     24  */
     25 public class DateOrder implements Comparable<DateOrder> {
     26     private int etype1;
     27     private int etype2;
     28 
     29     public DateOrder(int a, int b) {
     30         etype1 = a;
     31         etype2 = b;
     32     }
     33 
     34     @Override
     35     public boolean equals(Object obj) {
     36         DateOrder that = (DateOrder) obj;
     37         return that.etype1 == etype1 && that.etype2 == etype2;
     38     }
     39 
     40     @Override
     41     public int hashCode() {
     42         return etype1 * 37 + etype2;
     43     }
     44 
     45     @Override
     46     public String toString() {
     47         return "<" + toString2(etype1) + "," + toString2(etype2) + ">";
     48     }
     49 
     50     private String toString2(int etype) {
     51         switch (etype >> 1) {
     52 
     53         }
     54         return (VariableField.getCanonicalCode(etype >> 1)) + ((etype & 1) == 0 ? "" : "");
     55     }
     56 
     57     @Override
     58     public int compareTo(DateOrder that) {
     59         int diff;
     60         if (0 != (diff = etype1 - that.etype1)) {
     61             return diff;
     62         }
     63         return etype2 - that.etype2;
     64     }
     65 
     66     public static Map<String, Map<DateOrder, String>> getOrderingInfo(CLDRFile plain, CLDRFile resolved,
     67         DateTimePatternGenerator.FormatParser fp) {
     68         Map<String, Map<DateOrder, String>> pathsWithConflictingOrder2sample = new HashMap<String, Map<DateOrder, String>>();
     69         Status status = new Status();
     70         try {
     71             Map<String, Map<DateOrder, Set<String>>> type2order2set = new HashMap<String, Map<DateOrder, Set<String>>>();
     72             Matcher typeMatcher = PatternCache.get("\\[@type=\"([^\"]*)\"]").matcher("");
     73             int[] soFar = new int[50];
     74             int lenSoFar = 0;
     75             for (String path : resolved) {
     76                 if (DateTimePatternType.STOCK_AVAILABLE_INTERVAL_PATTERNS.contains(DateTimePatternType.fromPath(path))) {
     77                     if (path.contains("[@id=\"Ed\"]")) {
     78                         continue;
     79                     }
     80                     if (!path.equals(status.pathWhereFound)) {
     81                         continue;
     82                     }
     83                     typeMatcher.reset(path).find();
     84                     String type = typeMatcher.group(1);
     85                     Map<DateOrder, Set<String>> pairCount = type2order2set.get(type);
     86                     if (pairCount == null) {
     87                         type2order2set.put(type, pairCount = new HashMap<DateOrder, Set<String>>());
     88                     }
     89                     boolean isInterval = path.contains("intervalFormatItem");
     90                     lenSoFar = 0;
     91                     String value = resolved.getStringValue(path);
     92                     // register a comparison for all of the items so far
     93                     for (Object item : fp.set(value).getItems()) {
     94                         if (item instanceof VariableField) {
     95                             VariableField variable = (VariableField) item;
     96                             int eType = variable.getType() * 2 + (variable.isNumeric() ? 1 : 0);
     97                             if (isInterval && find(eType, soFar, lenSoFar)) {
     98                                 lenSoFar = 0; // restart the clock
     99                                 soFar[lenSoFar++] = eType;
    100                                 continue;
    101                             }
    102                             for (int i = 0; i < lenSoFar; ++i) {
    103                                 DateOrder order = new DateOrder(soFar[i], eType);
    104                                 Set<String> paths = pairCount.get(order);
    105                                 if (paths == null) {
    106                                     pairCount.put(order, paths = new HashSet<String>());
    107                                 }
    108                                 paths.add(path);
    109                             }
    110                             soFar[lenSoFar++] = eType;
    111                         }
    112                     }
    113                 }
    114             }
    115             // determine conflicts, and mark
    116             for (Entry<String, Map<DateOrder, Set<String>>> typeAndOrder2set : type2order2set.entrySet()) {
    117                 Map<DateOrder, Set<String>> pairCount = typeAndOrder2set.getValue();
    118                 HashSet<DateOrder> alreadySeen = new HashSet<DateOrder>();
    119                 for (Entry<DateOrder, Set<String>> entry : pairCount.entrySet()) {
    120                     DateOrder thisOrder = entry.getKey();
    121                     if (alreadySeen.contains(thisOrder)) {
    122                         continue;
    123                     }
    124                     DateOrder reverseOrder = new DateOrder(thisOrder.etype2, thisOrder.etype1);
    125                     Set<String> reverseSet = pairCount.get(reverseOrder);
    126                     DateOrder sample = thisOrder.compareTo(reverseOrder) < 0 ? thisOrder : reverseOrder;
    127 
    128                     Set<String> thisPaths = entry.getValue();
    129                     if (reverseSet != null) {
    130                         addConflictingPaths(plain, sample, reverseSet, thisPaths, pathsWithConflictingOrder2sample);
    131                         addConflictingPaths(plain, sample, thisPaths, reverseSet, pathsWithConflictingOrder2sample);
    132                         alreadySeen.add(reverseOrder);
    133                     }
    134                 }
    135             }
    136             // for debugging, show conflicts
    137             if (CheckDates.GREGORIAN_ONLY) {
    138                 for (Entry<String, Map<DateOrder, String>> entry : pathsWithConflictingOrder2sample.entrySet()) {
    139                     String path1 = entry.getKey();
    140                     String locale1 = resolved.getSourceLocaleID(path1, status);
    141                     String value1 = resolved.getStringValue(path1);
    142                     Map<DateOrder, String> orderString = entry.getValue();
    143                     for (Entry<DateOrder, String> entry2 : orderString.entrySet()) {
    144                         DateOrder order2 = entry2.getKey();
    145                         String path2 = entry2.getValue();
    146                         String locale2 = resolved.getSourceLocaleID(path2, status);
    147                         String value2 = resolved.getStringValue(path2);
    148                         System.out.println(order2 + "\t" + value1 + "\t" + value2 + "\t" + locale1 + "\t" + locale2
    149                             + "\t" + path1 + "\t" + path2);
    150                     }
    151                 }
    152             }
    153         } catch (RuntimeException e) {
    154             throw e;
    155         }
    156         return pathsWithConflictingOrder2sample;
    157     }
    158 
    159     /**
    160      * Add paths with a conflicting date order to the specified map.
    161      *
    162      * @param cldrFile
    163      * @param order
    164      * @param paths
    165      *            the set of paths to add conflicting paths for
    166      * @param conflictingPaths
    167      *            the set of conflicting paths
    168      * @param pathsWithConflictingOrder2sample
    169      */
    170     private static void addConflictingPaths(CLDRFile cldrFile, DateOrder order, Set<String> paths,
    171         Set<String> conflictingPaths, Map<String, Map<DateOrder, String>> pathsWithConflictingOrder2sample) {
    172         for (String first : paths) {
    173             FormatType firstType = FormatType.getType(first);
    174             for (String otherPath : conflictingPaths) {
    175                 FormatType otherType = FormatType.getType(otherPath);
    176                 // Add the first conflicting path that has a high enough
    177                 // importance to be considered.
    178                 if (!otherType.isLessImportantThan(firstType)) {
    179                     addItem(cldrFile, first, order, otherPath, pathsWithConflictingOrder2sample);
    180                     break;
    181                 }
    182             }
    183         }
    184     }
    185 
    186     private static boolean find(int eType, int[] soFar, int lenSoFar) {
    187         for (int i = 0; i < lenSoFar; ++i) {
    188             if (eType == soFar[i]) {
    189                 return true;
    190             }
    191         }
    192         return false;
    193     }
    194 
    195     private static void addItem(CLDRFile plain, String path, DateOrder sample,
    196         String conflictingPath, Map<String, Map<DateOrder, String>> pathsWithConflictingOrder2sample) {
    197         String value = plain.getStringValue(path);
    198         if (value == null) {
    199             return;
    200         }
    201         Map<DateOrder, String> order2path = pathsWithConflictingOrder2sample.get(path);
    202         if (order2path == null) {
    203             pathsWithConflictingOrder2sample.put(path, order2path = new TreeMap<DateOrder, String>());
    204         }
    205         order2path.put(sample, conflictingPath);
    206     }
    207 
    208     /**
    209      * Enum for deciding the priority of paths for checking date order
    210      * consistency.
    211      */
    212     private enum FormatType {
    213         DATE(3), TIME(3), AVAILABLE(2), INTERVAL(1);
    214         private static final Pattern DATETIME_PATTERN = PatternCache.get("/(date|time|available|interval)Formats");
    215         // Types with a higher value have higher importance.
    216         private int importance;
    217 
    218         private FormatType(int importance) {
    219             this.importance = importance;
    220         }
    221 
    222         /**
    223          * @param path
    224          * @return the format type of the specified path
    225          */
    226         public static FormatType getType(String path) {
    227             Matcher matcher = DATETIME_PATTERN.matcher(path);
    228             if (matcher.find()) {
    229                 return FormatType.valueOf(matcher.group(1).toUpperCase());
    230             }
    231             throw new IllegalArgumentException("Path is not a datetime format type: " + path);
    232         }
    233 
    234         /**
    235          * @return true if this FormatType is of lower importance than otherType
    236          */
    237         public boolean isLessImportantThan(FormatType otherType) {
    238             return otherType.importance - importance > 0;
    239         }
    240     }
    241 }