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