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