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