• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  ******************************************************************************
3  * Copyright (C) 2006-2009,2012, International Business Machines Corporation  *
4  * and others. All Rights Reserved.                                           *
5  ******************************************************************************
6  * $Source$
7  * $Revision$
8  ******************************************************************************
9  */
10 package org.unicode.cldr.test;
11 
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.Collection;
15 import java.util.Date;
16 import java.util.Iterator;
17 import java.util.LinkedHashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.TreeMap;
21 
22 import org.unicode.cldr.util.CLDRFile;
23 import org.unicode.cldr.util.ICUServiceBuilder;
24 import org.unicode.cldr.util.XPathParts;
25 
26 import com.ibm.icu.text.DateFormat;
27 import com.ibm.icu.text.DateTimePatternGenerator;
28 import com.ibm.icu.text.DateTimePatternGenerator.PatternInfo;
29 
30 /**
31  * Temporary class while refactoring.
32  *
33  * @author markdavis
34  *
35  */
36 class FlexibleDateFromCLDR {
37     DateTimePatternGenerator gen = DateTimePatternGenerator.getEmptyInstance();
38     private transient ICUServiceBuilder icuServiceBuilder = new ICUServiceBuilder();
39 
40     static List<String> tests = Arrays.asList(new String[] {
41 
42         "HHmmssSSSvvvv", // 'complete' time
43         "HHmm",
44         "HHmmvvvv",
45         "HHmmss",
46         "HHmmssSSSSS",
47         "HHmmssvvvv",
48 
49         "MMMd",
50         "Md",
51 
52         "YYYYD", // (maybe?)
53 
54         "yyyyww",
55         "yyyywwEEE",
56 
57         "yyyyQQQQ",
58         "yyyyMM",
59 
60         "yyyyMd",
61         "yyyyMMMd",
62         "yyyyMMMEEEd",
63 
64         "GyyyyMMMd",
65         "GyyyyMMMEEEd", // 'complete' date
66 
67         "YYYYwEEE", // year, week of year, weekday
68         "yyyyDD", // year, day of year
69         "yyyyMMFE", // year, month, nth day of week in month
70         // misc
71         "eG", "dMMy", "GHHmm", "yyyyHHmm", "Kmm", "kmm",
72         "MMdd", "ddHH", "yyyyMMMd", "yyyyMMddHHmmss",
73         "GEEEEyyyyMMddHHmmss",
74         "GuuuuQMMMMwwWddDDDFEEEEaHHmmssSSSvvvv", // bizarre case just for testing
75     });
76 
set(CLDRFile cldrFile)77     public void set(CLDRFile cldrFile) {
78         icuServiceBuilder.setCldrFile(cldrFile);
79         gen = DateTimePatternGenerator.getEmptyInstance(); // for now
80         failureMap.clear();
81     }
82 
83     /**
84      *
85      */
showFlexibles()86     public void showFlexibles() {
87         Map<String, String> items = gen.getSkeletons(new LinkedHashMap<String, String>());
88         System.out.println("ERRORS");
89         for (Iterator<String> it = failureMap.keySet().iterator(); it.hasNext();) {
90             String item = it.next();
91             String value = failureMap.get(item);
92             System.out.println("\t" + value);
93         }
94         for (int i = 0; i < DateTimePatternGenerator.TYPE_LIMIT; ++i) {
95             String format = gen.getAppendItemFormat(i);
96             if (format.indexOf('\u251C') >= 0) {
97                 System.out.println("\tMissing AppendItem format:\t" + DISPLAY_NAME_MAP[i]);
98             }
99             if (i == DateTimePatternGenerator.FRACTIONAL_SECOND) continue; // don't need this field
100             String name = gen.getAppendItemName(i);
101             if (name.matches("F[0-9]+")) {
102                 System.out.println("\tMissing Field Name:\t" + DISPLAY_NAME_MAP[i]);
103             }
104         }
105         System.out.println("SKELETON\t=> PATTERN LIST");
106         for (Iterator<String> it = items.keySet().iterator(); it.hasNext();) {
107             String skeleton = it.next();
108             System.out.println("\t\"" + skeleton + "\"\t=>\t\"" + items.get(skeleton) + "\"");
109         }
110         System.out.println("REDUNDANTS");
111         Collection<String> redundants = gen.getRedundants(new ArrayList<String>());
112         for (String item : redundants) {
113             System.out.println("\t" + item);
114         }
115         System.out.println("TESTS");
116         for (String item : tests) {
117             try {
118                 String pat = gen.getBestPattern(item);
119                 String sample = "<can't format>";
120                 try {
121                     DateFormat df = icuServiceBuilder.getDateFormat("gregorian", pat);
122                     sample = df.format(new Date());
123                 } catch (RuntimeException e) {
124                 }
125                 System.out.println("\t\"" + item + "\"\t=>\t\"" + pat + "\"\t=>\t\"" + sample + "\"");
126             } catch (RuntimeException e) {
127                 System.out.println(e.getMessage()); // e.printStackTrace();
128             }
129         }
130         System.out.println("END");
131     }
132 
133     Map<String, String> failureMap = new TreeMap<>();
134 
135     /**
136      * @param path
137      * @param value
138      * @param fullPath
139      */
checkFlexibles(String path, String value, String fullPath)140     public void checkFlexibles(String path, String value, String fullPath) {
141         if (path.indexOf("numbers/symbols/decimal") >= 0) {
142             gen.setDecimal(value);
143             return;
144         }
145         if (path.indexOf("gregorian") < 0) return;
146         if (path.indexOf("/appendItem") >= 0) {
147             XPathParts parts = XPathParts.getFrozenInstance(path);
148             String key = parts.getAttributeValue(-1, "request");
149             try {
150                 gen.setAppendItemFormat(getIndex(key, APPEND_ITEM_NAME_MAP), value);
151             } catch (RuntimeException e) {
152                 failureMap.put(path, "\tWarning: can't set AppendItemFormat:\t" + key + ":\t" + value);
153             }
154             return;
155         }
156         if (path.indexOf("/fields") >= 0) {
157             XPathParts parts = XPathParts.getFrozenInstance(path);
158             String key = parts.getAttributeValue(-2, "type");
159             try {
160                 gen.setAppendItemName(getIndex(key, DISPLAY_NAME_MAP), value);
161             } catch (RuntimeException e) {
162                 failureMap.put(path, "\tWarning: can't set AppendItemName:\t" + key + ":\t" + value);
163             }
164             return;
165         }
166 
167         if (path.indexOf("pattern") < 0 && path.indexOf("dateFormatItem") < 0 && path.indexOf("intervalFormatItem") < 0) return;
168         // set the am/pm preference
169         if (path.indexOf("timeFormatLength[@type=\"short\"]") >= 0) {
170             fp.set(value);
171             for (Object item : fp.getItems()) {
172                 if (item instanceof DateTimePatternGenerator.VariableField) {
173                     if (item.toString().charAt(0) == 'h') {
174                         isPreferred12Hour = true;
175                     }
176                 }
177             }
178         }
179         if (path.indexOf("dateTimeFormatLength") > 0) return; // exclude {1} {0}
180         if (path.indexOf("intervalFormatItem") < 0) {
181             // add to generator
182             try {
183                 gen.addPattern(value, false, patternInfo);
184                 switch (patternInfo.status) {
185                 case PatternInfo.CONFLICT:
186                     failureMap.put(path, "Conflicting Patterns: \"" + value + "\"\t&\t\"" + patternInfo.conflictingPattern
187                         + "\"");
188                     break;
189                 }
190             } catch (RuntimeException e) {
191                 failureMap.put(path, e.getMessage());
192             }
193         }
194     }
195 
getDTPGForCalendarType(String calendarType, List<CLDRFile> parentCLDRFiles)196     public DateTimePatternGenerator getDTPGForCalendarType(String calendarType, List<CLDRFile> parentCLDRFiles) {
197         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getEmptyInstance();
198         switch (calendarType) {
199             default:
200                 addAvailableFormatsForFile(dtpg, calendarType, parentCLDRFiles.get(0));
201                 int hyphenIndex = calendarType.indexOf('-');
202                 if (hyphenIndex > 0) { // e.g. islamic-umalqura, ethiopic-amete-alem
203                     // we inherit from the untruncated form
204                     String baseType = calendarType.substring(0,hyphenIndex);
205                     addAvailableFormatsForFile(dtpg, baseType, parentCLDRFiles.get(0));
206                 }
207                 // then fall through to generic (sideways)
208             case "generic":
209                 addAvailableFormatsForFile(dtpg, "generic", parentCLDRFiles.get(0));
210                 // then fall through to gregorian (sideways)
211             case "gregorian":
212                 // this inherits upward from parents
213                 addAvailableFormatsWithParents(dtpg, "gregorian", parentCLDRFiles);
214                 break;
215 
216             case "dangi":
217                 addAvailableFormatsForFile(dtpg, "dangi", parentCLDRFiles.get(0));
218                 // fall through to chinese (sideways)
219             case "chinese":
220                 // this inherits upward from parents
221                 addAvailableFormatsWithParents(dtpg, "chinese", parentCLDRFiles);
222                 break;
223         }
224         return dtpg;
225     }
226 
addAvailableFormatsWithParents(DateTimePatternGenerator dtpg, String calendarType, List<CLDRFile> parentCLDRFiles)227     private void addAvailableFormatsWithParents(DateTimePatternGenerator dtpg, String calendarType, List<CLDRFile> parentCLDRFiles) {
228         for (Iterator<CLDRFile> it = parentCLDRFiles.iterator(); it.hasNext();) {
229             CLDRFile file = it.next();
230             addAvailableFormatsForFile(dtpg, calendarType, file);
231         }
232     }
233 
234     private static String DATE_FORMAT_ITEM_ID_PREFIX = "dateFormatItem[@id=\"";
235 
addAvailableFormatsForFile(DateTimePatternGenerator dtpg, String calendarType, CLDRFile file)236     private void addAvailableFormatsForFile(DateTimePatternGenerator dtpg, String calendarType, CLDRFile file) {
237         String toppath = "//ldml/dates/calendars/calendar[@type=\"" + calendarType + "\"]/dateTimeFormats/availableFormats";
238         // relevant paths here might include the following (but we want to skip alt=variant):
239         // ...dateTimeFormats/availableFormats/dateFormatItem[@id="..."]
240         // ...dateTimeFormats/availableFormats/dateFormatItem[@id="..."][@draft="..."]
241         // ...dateTimeFormats/availableFormats/dateFormatItem[@id="..."][@count="..."]
242         // ...dateTimeFormats/availableFormats/dateFormatItem[@id="..."][@count="..."][@draft="..."]
243         // ...dateTimeFormats/availableFormats/dateFormatItem[@id="..."][@alt="variant"]
244         boolean isRoot = file.getLocaleID().equals("root");
245         for (Iterator<String> it = file.iterator(toppath); it.hasNext();) {
246             String path = it.next();
247             int startIndex = path.indexOf(DATE_FORMAT_ITEM_ID_PREFIX);
248             if (startIndex < 0 || path.indexOf("[@alt=", startIndex) >= 0) {
249                 continue;
250             }
251             startIndex += DATE_FORMAT_ITEM_ID_PREFIX.length();
252             int endIndex = path.indexOf("\"]", startIndex);
253             String skeleton = path.substring(startIndex,endIndex);
254             String pattern = file.getWinningValue(path);
255             dtpg.addPatternWithSkeleton(pattern, skeleton, !isRoot, patternInfo);
256         }
257     }
258 
stripLiterals(String pattern)259     private String stripLiterals(String pattern) {
260         int i = 0, patlen = pattern.length();
261         StringBuilder stripped = new StringBuilder(patlen);
262         boolean inLiteral = false;
263         while (i < patlen) {
264             char c = pattern.charAt(i++);
265             if (c == '\'') {
266                 inLiteral = !inLiteral;
267             } else if (!inLiteral) {
268                 stripped.append(c);
269             }
270         }
271         return stripped.toString();
272     }
273 
checkValueAgainstSkeleton(String path, String value)274     public String checkValueAgainstSkeleton(String path, String value) {
275         String failure = null;
276         String skeleton = null;
277         String strippedPattern = null;
278         if (path.contains("dateFormatItem")) {
279             XPathParts parts = XPathParts.getFrozenInstance(path);
280             skeleton = parts.findAttributeValue("dateFormatItem", "id"); // the skeleton
281             strippedPattern = gen.getSkeleton(value); // the pattern stripped of literals
282         } else if (path.contains("intervalFormatItem")) {
283             XPathParts parts = XPathParts.getFrozenInstance(path);
284             skeleton = parts.findAttributeValue("intervalFormatItem", "id"); // the skeleton
285             strippedPattern = stripLiterals(value); // can't use gen on intervalFormat pattern (throws exception)
286         }
287         if (skeleton != null && strippedPattern != null) {
288             if (skeleton.indexOf('H') >= 0 || skeleton.indexOf('k') >= 0) { // if skeleton uses 24-hour time
289                 if (strippedPattern.indexOf('h') >= 0 || strippedPattern.indexOf('K') >= 0) { // but pattern uses 12...
290                     failure = "Skeleton uses 24-hour cycle (H,k) but pattern uses 12-hour (h,K)";
291                 }
292             } else if (skeleton.indexOf('h') >= 0 || skeleton.indexOf('K') >= 0) { // if skeleton uses 12-hour time
293                 if (strippedPattern.indexOf('H') >= 0 || strippedPattern.indexOf('k') >= 0) { // but pattern uses 24...
294                     failure = "Skeleton uses 12-hour cycle (h,K) but pattern uses 24-hour (H,k)";
295                 }
296             } else if (skeleton.indexOf('G') >= 0 && strippedPattern.indexOf('G') < 0 &&
297                         strippedPattern.indexOf('r') < 0 && strippedPattern.indexOf('U') < 0) {
298                 // If skeleton has G, pattern should have G (or for cyclic calendars like chinese/dangi, r and/or U)
299                 failure = "Skeleton includes 'G' (era) but pattern does not have 'G' (or 'r' or 'U' for chinese/dangi calendars)";
300             }
301         }
302         return failure;
303     }
304 
305     DateTimePatternGenerator.FormatParser fp = new DateTimePatternGenerator.FormatParser();
306 
307     boolean isPreferred12Hour = false;
308 
309     static private String[] DISPLAY_NAME_MAP = {
310         "era", "year", "quarter", "month", "week", "week_in_month", "weekday",
311         "day", "day_of_year", "day_of_week_in_month", "dayperiod",
312         "hour", "minute", "second", "fractional_second", "zone", "-"
313     };
314 
315     static private String[] APPEND_ITEM_NAME_MAP = {
316         "Era", "Year", "Quarter", "Month", "Week", "Week", "Day-Of-Week",
317         "Day", "Day", "Day-Of-Week", "-",
318         "Hour", "Minute", "Second", "-", "Timezone", "-"
319     };
320 
getIndex(String s, String[] strings)321     int getIndex(String s, String[] strings) {
322         for (int i = 0; i < strings.length; ++i) {
323             if (s.equals(strings[i])) return i;
324         }
325         return -1;
326     }
327 
328     PatternInfo patternInfo = new PatternInfo();
329 
getRedundants(Collection<String> output)330     public Collection<String> getRedundants(Collection<String> output) {
331         return gen.getRedundants(output);
332     }
333 
getFailurePath(Object path)334     public Object getFailurePath(Object path) {
335         return failureMap.get(path);
336     }
337 
preferred12Hour()338     public boolean preferred12Hour() {
339         return isPreferred12Hour;
340     }
341 }
342