1 package org.unicode.cldr.util; 2 3 import java.util.ArrayList; 4 import java.util.EnumMap; 5 import java.util.EnumSet; 6 import java.util.HashMap; 7 import java.util.LinkedHashMap; 8 import java.util.List; 9 import java.util.Locale; 10 import java.util.Map; 11 import java.util.Map.Entry; 12 import java.util.TreeMap; 13 14 import org.unicode.cldr.util.ChainedMap.M3; 15 import org.unicode.cldr.util.DayPeriods.DayPeriod; 16 17 import com.google.common.base.Splitter; 18 import com.ibm.icu.impl.Row.R3; 19 import com.ibm.icu.util.ULocale; 20 21 public class DayPeriodConverter { 22 private static final boolean TO_CODE = true; 23 24 // HACK TO SET UP DATA 25 // Will be replaced by real data table in the future 26 27 static class DayInfo { 28 ULocale locale; 29 DayPeriods.DayPeriod[] data = new DayPeriod[24]; 30 Map<DayPeriod, String> toNativeName = new EnumMap<DayPeriod, String>(DayPeriod.class); 31 Map<String, DayPeriod> toDayPeriod = new HashMap<String, DayPeriod>(); 32 33 @Override toString()34 public String toString() { 35 String result = "make(\"" + locale + "\""; 36 DayPeriod lastDayPeriod = null; 37 for (int i = 0; i < 24; ++i) { 38 DayPeriod dayPeriod = data[i]; 39 if (dayPeriod != lastDayPeriod) { 40 result += ")\n.add(\"" 41 + dayPeriod 42 + "\", \"" 43 + toNativeName.get(dayPeriod) 44 + "\""; 45 lastDayPeriod = dayPeriod; 46 } 47 result += ", " + i; 48 } 49 result += ")\n.build();\n"; 50 /* 51 make("en") 52 .add("MORNING", "morning", 6, 7, 8, 9, 10, 11) 53 .add("AFTERNOON", "afternoon", 12, 13, 14, 15, 16, 17) 54 .add("EVENING", "evening", 18, 19, 20) 55 .add("NIGHT", "night", 0, 1, 2, 3, 4, 5, 21, 22, 23) 56 .build(); 57 */ 58 return result; 59 } 60 toCldr()61 public String toCldr() { 62 String result = "\t\t<dayPeriodRules locales=\"" + locale + "\">\n"; 63 DayPeriod lastDayPeriod = data[0]; 64 int start = 0; 65 for (int i = 1; i < 24; ++i) { 66 DayPeriod dayPeriod = data[i]; 67 if (dayPeriod != lastDayPeriod) { 68 result = addPeriod(result, lastDayPeriod, start, i); 69 lastDayPeriod = dayPeriod; 70 start = i; 71 } 72 } 73 result = addPeriod(result, lastDayPeriod, start, 24); 74 result += "\t\t</dayPeriodRules>"; 75 return result; 76 } 77 addPeriod(String result, DayPeriod dayPeriod, int start, int i)78 private String addPeriod(String result, DayPeriod dayPeriod, int start, int i) { 79 result += "\t\t\t<dayPeriodRule type=\"" 80 + dayPeriod.toString().toLowerCase(Locale.ENGLISH) 81 + "\" from=\"" 82 + start + ":00" 83 + "\" before=\"" 84 + i + ":00" 85 + "\"/> <!-- " + toNativeName.get(dayPeriod) 86 + " -->\n"; 87 return result; 88 } 89 } 90 91 static final Map<ULocale, DayInfo> DATA = new LinkedHashMap<>(); 92 static { 93 for (String[] x : DayPeriodData.RAW_DATA) { 94 ULocale locale = new ULocale(x[0]); 95 int start = Integer.parseInt(x[1]); 96 DayPeriod dayPeriod = DayPeriod.valueOf(x[2]); 97 String nativeName = x[3].trim(); 98 DayInfo data = DATA.get(locale); 99 if (data == null) { DATA.put(locale, data = new DayInfo())100 DATA.put(locale, data = new DayInfo()); 101 } 102 data.locale = locale; 103 for (int i = start; i < 24; ++i) { 104 data.data[i] = dayPeriod; 105 } 106 String old = data.toNativeName.get(dayPeriod); 107 if (old != null && !old.equals(nativeName)) { 108 throw new IllegalArgumentException(locale + " inconsistent native name for " 109 + dayPeriod + ", old: «" + old + "», new: «" + nativeName + "»"); 110 } 111 DayPeriod oldDp = data.toDayPeriod.get(nativeName); 112 if (oldDp != null && oldDp != dayPeriod) { 113 throw new IllegalArgumentException(locale + " inconsistent day periods for name «" 114 + nativeName + "», old: " + oldDp + ", new: " + dayPeriod); 115 } data.toDayPeriod.put(nativeName, dayPeriod)116 data.toDayPeriod.put(nativeName, dayPeriod); data.toNativeName.put(dayPeriod, nativeName)117 data.toNativeName.put(dayPeriod, nativeName); 118 } 119 } 120 main(String[] args)121 public static void main(String[] args) { 122 // generateFormat(); 123 generateFieldNames(); 124 } 125 generateFieldNames()126 private static void generateFieldNames() { 127 SupplementalDataInfo sdi = CLDRConfig.getInstance().getSupplementalDataInfo(); 128 Factory factory = CLDRConfig.getInstance().getFullCldrFactory(); 129 EnumSet<DayPeriodInfo.DayPeriod> dayPeriodSet = EnumSet.noneOf(DayPeriodInfo.DayPeriod.class); 130 131 String prefix = getPrefix("stand-alone"); 132 133 for (String locale : sdi.getDayPeriodLocales(DayPeriodInfo.Type.format)) { 134 ULocale uLocale = new ULocale(locale); 135 DayPeriodInfo dayPeriodInfo = sdi.getDayPeriods(DayPeriodInfo.Type.format, locale); 136 System.out.println("# " + locale); 137 dayPeriodSet.clear(); 138 dayPeriodSet.addAll(dayPeriodInfo.getPeriods()); 139 final boolean fieldType = false; 140 if (!fieldType) { 141 //System.out.println("\t<dayPeriodContext type=\"stand-alone\">"); 142 //System.out.println("\t\t<dayPeriodWidth type=\"wide\">"); 143 CLDRFile cldrFile = factory.make(locale, false); 144 for (String path : cldrFile) { 145 if (path.endsWith("/alias")) { 146 continue; 147 } 148 if (path.startsWith(prefix)) { 149 XPathParts parts = XPathParts.getFrozenInstance(path); 150 String width = parts.getAttributeValue(-2, "type"); 151 DayPeriodInfo.DayPeriod period = DayPeriodInfo.DayPeriod.fromString(parts.getAttributeValue(-1, "type")); 152 String draft = parts.getAttributeValue(-1, "draft"); 153 //if (period != DayPeriodInfo.DayPeriod.am || period != DayPeriodInfo.DayPeriod.pm || width.equals("wide")) { 154 System.out.println("#old: «" + cldrFile.getStringValue(path) + "»" 155 + ", width: " + width 156 + ", period: " + period 157 + (draft == null ? "" : ", draft: " + draft)); 158 System.out.println("locale=" + locale 159 + " ; action=delete" 160 + " ; path=" + path); 161 //} 162 } 163 } 164 165 // CLDRFile cldrFile = factory.make(locale, true); 166 addOldDayPeriod(cldrFile, DayPeriodInfo.DayPeriod.am); 167 addOldDayPeriod(cldrFile, DayPeriodInfo.DayPeriod.pm); 168 } 169 for (DayPeriodInfo.DayPeriod dayPeriod : dayPeriodSet) { 170 if (fieldType) { 171 String name = getNativeName(uLocale, dayPeriod); 172 System.out.println("\t<field type=\"dayPeriod-" + dayPeriod + "\">"); 173 System.out.println("\t\t<displayName>" + name + "</displayName>"); 174 System.out.println("\t</field>"); 175 } else { 176 String name = getNativeName(uLocale, dayPeriod); 177 //System.out.println("\t\t\t<dayPeriod type=\"" + dayPeriod + "\">" + name + "</dayPeriod>"); 178 //ldml/dates/calendars/calendar[@type="gregorian"]/dayPeriods/dayPeriodContext[@type="format"]/dayPeriodWidth[@type="narrow"]/dayPeriod[@type="am"] 179 // String path = "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dayPeriods" 180 // + "/dayPeriodContext[@type=\"stand-alone\"]" 181 // + "/dayPeriodWidth[@type=\"wide\"]" 182 // + "/dayPeriod[@type=\"" + dayPeriod + "\"]"; 183 if (name != null) { 184 showModLine(locale, dayPeriod, name, false); 185 } 186 } 187 } 188 // if (!fieldType) { 189 // System.out.println("\t\t</dayPeriodWidth>"); 190 // System.out.println("\t</dayPeriodContext>"); 191 // } 192 System.out.println(); 193 } 194 } 195 showModLine(String locale, DayPeriodInfo.DayPeriod dayPeriod, String name, boolean draft)196 private static void showModLine(String locale, DayPeriodInfo.DayPeriod dayPeriod, String name, boolean draft) { 197 String path = getPath("stand-alone", "wide", dayPeriod); 198 if (draft) { 199 path += "[@draft=\"provisional\"]"; 200 } 201 System.out.println("locale=" + locale 202 + " ; action=add" 203 + " ; new_value=" + name 204 + " ; new_path=" + path); 205 } 206 addOldDayPeriod(CLDRFile cldrFile, DayPeriodInfo.DayPeriod period)207 private static void addOldDayPeriod(CLDRFile cldrFile, DayPeriodInfo.DayPeriod period) { 208 String amString = cldrFile.getStringValue(getPath("format", "wide", period)); 209 if (amString != null) { 210 String locale = cldrFile.getLocaleID(); 211 showModLine(locale, period, amString, !locale.equals("root") && !locale.equals("en")); 212 //System.out.println("\t\t\t<dayPeriod type=\"" + period + "\">" + amString + "</dayPeriod>"); 213 } 214 } 215 getPath(String context, String width, DayPeriodInfo.DayPeriod dayPeriod)216 static String getPath(String context, String width, DayPeriodInfo.DayPeriod dayPeriod) { 217 return getPrefix(context) 218 + "/dayPeriodWidth[@type=\"" + width + "\"]" 219 + "/dayPeriod[@type=\"" + dayPeriod + "\"]"; 220 } 221 getPrefix(String context)222 private static String getPrefix(String context) { 223 return "//ldml/dates/calendars" 224 + "/calendar[@type=\"gregorian\"]" 225 + "/dayPeriods" 226 + "/dayPeriodContext[@type=\"" + context + "\"]"; 227 } 228 generateFormat()229 static void generateFormat() { 230 SupplementalDataInfo sdi = CLDRConfig.getInstance().getSupplementalDataInfo(); 231 M3<ULocale, Integer, DayPeriodInfo.DayPeriod> allData = ChainedMap.of(new TreeMap(LanguageGroup.COMPARATOR), new TreeMap(), 232 DayPeriodInfo.DayPeriod.class); 233 for (String locale : sdi.getDayPeriodLocales(DayPeriodInfo.Type.selection)) { 234 ULocale ulocale = new ULocale(locale); 235 NoonMidnight nm = NoonMidnight.get(locale); 236 boolean hasNoon = nm != null && nm.noon != null; 237 boolean hasMidnight = nm != null && nm.midnight != null; 238 DayPeriodInfo data = sdi.getDayPeriods(DayPeriodInfo.Type.selection, locale); 239 if (hasMidnight) { 240 allData.put(ulocale, 0, DayPeriodInfo.DayPeriod.midnight); 241 } 242 if (hasNoon) { 243 allData.put(ulocale, 12 * DayPeriodInfo.HOUR, DayPeriodInfo.DayPeriod.noon); 244 } 245 int lastTime = -1; 246 DayPeriodInfo.DayPeriod lastPeriod = null; 247 for (int index = 0; index < data.getPeriodCount(); ++index) { 248 R3<Integer, Boolean, DayPeriodInfo.DayPeriod> row = data.getPeriod(index); 249 DayPeriodInfo.DayPeriod period = row.get2(); 250 int time = row.get0(); 251 if (!row.get1()) { 252 time += 1; 253 } else if (hasMidnight && time == 0) { 254 time += 1; 255 } else if (hasNoon) { 256 if (time == 12 * DayPeriodInfo.HOUR) { 257 time += 1; 258 } else if (lastTime < 12 * DayPeriodInfo.HOUR && time > 12 * DayPeriodInfo.HOUR) { 259 System.out.println(locale + ": Splitting " + lastTime + ", " + lastPeriod + ", " + time + ", " + period); 260 allData.put(ulocale, 12 * DayPeriodInfo.HOUR + 1, lastPeriod); 261 } 262 } 263 allData.put(ulocale, time, period); 264 lastTime = time; 265 lastPeriod = period; 266 } 267 } 268 for (Entry<ULocale, Map<Integer, org.unicode.cldr.util.DayPeriodInfo.DayPeriod>> entry : allData) { 269 ULocale locale = entry.getKey(); 270 System.out.println("\t\t<dayPeriodRules locales=\"" + locale + "\">"); 271 ArrayList<Entry<Integer, DayPeriodInfo.DayPeriod>> list = new ArrayList<>(entry.getValue().entrySet()); 272 for (int i = 0; i < list.size(); ++i) { 273 Entry<Integer, org.unicode.cldr.util.DayPeriodInfo.DayPeriod> item = list.get(i); 274 //System.out.println(item.getKey() + " = " + item.getValue()); 275 DayPeriodInfo.DayPeriod dayPeriod = item.getValue(); 276 int start = item.getKey(); 277 int end = i + 1 == list.size() ? 24 * DayPeriodInfo.HOUR : list.get(i + 1).getKey(); 278 String startType = "from"; 279 if ((start & 1) != 0) { 280 startType = "after"; 281 } 282 start /= DayPeriodInfo.HOUR; 283 end /= DayPeriodInfo.HOUR; 284 System.out.println("\t\t\t<dayPeriodRule type=\"" 285 + dayPeriod.toString().toLowerCase(Locale.ENGLISH) 286 + "\" " 287 + (start == end ? "at=\"" + start 288 : startType + "=\"" + start + ":00\" before=\"" + end) 289 + ":00\"/>" + 290 " <!-- " + getNativeName(locale, dayPeriod) 291 + " -->"); 292 } 293 System.out.println("\t\t</dayPeriodRules>"); 294 } 295 } 296 oldMain()297 void oldMain() { 298 // SupplementalDataInfo sdi = CLDRConfig.getInstance().getSupplementalDataInfo(); 299 // 300 // for (String locale : sdi.getDayPeriodLocales(DayPeriodInfo.Type.selection)) { 301 // 302 // StringBuilder result = new StringBuilder("\t\t<dayPeriodRules locales=\"" + locale + "\">\n"; 303 // for (int index = 0; index < data.getPeriodCount(); ++index) { 304 // R3<Integer, Boolean, DayPeriodInfo.DayPeriod> periodData = data.getPeriod(index); 305 // public String toCldr() { 306 // DayPeriod lastDayPeriod = data[0]; 307 // int start = 0; 308 // for (int i = 1; i < 24; ++i) { 309 // DayPeriod dayPeriod = data[i]; 310 // if (dayPeriod != lastDayPeriod) { 311 // result = addPeriod(result, lastDayPeriod, start, i); 312 // lastDayPeriod = dayPeriod; 313 // start = i; 314 // } 315 // } 316 // result = addPeriod(result, lastDayPeriod, start, 24); 317 // result += "\t\t</dayPeriodRules>"; 318 // return result; 319 // } 320 // 321 // private String addPeriod(String result, DayPeriod dayPeriod, int start, int i) { 322 // result += "\t\t\t<dayPeriodRule type=\"" 323 // + dayPeriod.toString().toLowerCase(Locale.ENGLISH) 324 // + "\" from=\"" 325 // + start + ":00" 326 // + "\" before=\"" 327 // + i + ":00" 328 // + "\"/> <!-- " + toNativeName.get(dayPeriod) 329 // + " -->\n"; 330 // return result; 331 // } 332 // 333 // } 334 // NoonMidnight nm = NoonMidnight.get(locale); 335 // 336 // System.out.println(locale + "\t" + nm); 337 // } 338 } 339 340 static final ULocale ROOT2 = new ULocale("root"); 341 getNativeName(ULocale locale, DayPeriodInfo.DayPeriod dayPeriod)342 private static String getNativeName(ULocale locale, DayPeriodInfo.DayPeriod dayPeriod) { 343 NoonMidnight nm = NoonMidnight.get(locale.toString()); 344 if (nm == null) { 345 return null; 346 } 347 if (dayPeriod == DayPeriodInfo.DayPeriod.noon) { 348 return CldrUtility.ifNull(nm.noon, "missing-" + dayPeriod); 349 } else if (dayPeriod == DayPeriodInfo.DayPeriod.midnight) { 350 return CldrUtility.ifNull(nm.midnight, "missing-" + dayPeriod); 351 } 352 if (locale.equals(ROOT2)) { 353 if (dayPeriod == DayPeriodInfo.DayPeriod.morning1) { 354 return "am"; 355 } else if (dayPeriod == DayPeriodInfo.DayPeriod.afternoon1) { 356 return "pm"; 357 } 358 } 359 DayInfo data = DATA.get(locale); 360 if (data == null) { 361 return "missing-" + dayPeriod; 362 } 363 DayPeriod otherDayPeriod = DayPeriod.valueOf(dayPeriod.toString().toUpperCase(Locale.ENGLISH)); 364 return data.toNativeName.get(otherDayPeriod); 365 } 366 writeOld()367 static void writeOld() { 368 System.out.println("\t<dayPeriodRuleSet type=\"selection\">"); 369 for (Entry<ULocale, DayInfo> foo : DATA.entrySet()) { 370 check(foo.getKey(), foo.getValue()); 371 System.out.println(foo.getValue().toCldr()); 372 } 373 System.out.println("\t</dayPeriodRuleSet>"); 374 } 375 check(ULocale locale, DayInfo value)376 private static void check(ULocale locale, DayInfo value) { 377 check(locale, DayPeriod.MORNING1, DayPeriod.MORNING2, value); 378 check(locale, DayPeriod.AFTERNOON1, DayPeriod.AFTERNOON2, value); 379 check(locale, DayPeriod.EVENING1, DayPeriod.EVENING2, value); 380 check(locale, DayPeriod.NIGHT1, DayPeriod.NIGHT2, value); 381 DayPeriod lastDp = value.data[23]; 382 for (DayPeriod dp : value.data) { 383 if (lastDp.compareTo(dp) > 0) { 384 if ((lastDp == DayPeriod.NIGHT1 || lastDp == DayPeriod.NIGHT2) && dp == DayPeriod.MORNING1) { 385 } else { 386 throw new IllegalArgumentException(locale + " " + lastDp + " > " + dp); 387 } 388 } 389 lastDp = dp; 390 } 391 } 392 check(ULocale locale, DayPeriod morning1, DayPeriod morning2, DayInfo value)393 private static void check(ULocale locale, DayPeriod morning1, DayPeriod morning2, DayInfo value) { 394 if (value.toNativeName.containsKey(morning2) && !value.toNativeName.containsKey(morning1)) { 395 throw new IllegalArgumentException(locale + " Contains " + morning2 + ", but not " + morning1); 396 } 397 } 398 399 static class NoonMidnight { NoonMidnight(List<String> items)400 public NoonMidnight(List<String> items) { 401 locale = items.get(0); 402 midnight = CldrUtility.ifEqual(items.get(1), "N/A", null); 403 noon = CldrUtility.ifEqual(items.get(2), "N/A", null); 404 } 405 406 final String locale; 407 final String noon; 408 final String midnight; 409 get(String locale)410 static NoonMidnight get(String locale) { 411 return NOON_MIDNIGHT_MAP.get(locale); 412 } 413 414 @Override toString()415 public String toString() { 416 return locale + ", " + noon + ", " + midnight; 417 } 418 419 static Map<String, NoonMidnight> NOON_MIDNIGHT_MAP = new HashMap<>(); 420 static final String[] NOON_MIDNIGHT = { 421 "en|midnight|noon", 422 "af|middernag|N/A", 423 "nl|middernacht|N/A", 424 "de|Mitternacht|N/A", 425 "da|midnat|N/A", 426 "nb|midnatt|N/A", 427 "sv|midnatt|N/A", 428 "is|miðnætti|hádegi", 429 "pt|meia-noite|meio-dia", 430 "pt_PT|meia-noite|meio-dia", 431 "gl|medianoite|mediodía", 432 "es|medianoche|mediodía", 433 "es_419|medianoche|mediodía", 434 "ca|mitjanit|migdia", 435 "it|mezzanotte|mezzogiorno", 436 "ro|miezul nopții|amiază", 437 "fr|minuit|midi", 438 "fr-CA|minuit|midi", 439 "hr|ponoć|podne", 440 "bs|u ponoć|u podne", 441 "sr|поноћ|подне", 442 "sl|polnoč|poldne", 443 "cs|půlnoc|poledne", 444 "sk|polnoc|poludnie", 445 "pl|północ|południe", 446 "bg|полунощ|N/A", 447 "mk|на полноќ|напладне", 448 "ru|полночь|полдень", 449 "uk|північ|полудень", 450 "lt|vidurnaktis|vidurdienis", 451 "lv|pusnakts|pusdienlaiks", 452 "el|N/A|μεσημέρι", 453 "fa|نیمهشب|ظهر", 454 "hy|կեսգիշեր|կեսօր", 455 "ka|შუაღამე|შუადღე", 456 "sq|mesnatë|mesditë", 457 "ur|آدھی رات|N/A", 458 "hi|आधी रात|दोपहर", 459 "bn|N/A|N/A", 460 "gu|અડધી રાત|બપોર", 461 "mr|मध्यरात्री|मध्यान्ह", 462 "ne|मध्यरात|मध्यान्ह", 463 "pa|ਅੱਧੀ ਰਾਤ|ਦੁਪਹਿਰ", 464 "si|මැදියම|දවල්", 465 "ta|நள்ளிரவு|நன்பகல்", 466 "te|అర్థరాత్రి|మధ్యాహ్నం", 467 "ml|അർദ്ധരാത്രി|ഉച്ചയ്ക്ക്", 468 "kn|ಮಧ್ಯರಾತ್ರಿ|ಮಧ್ಯಾಹ್ನ", 469 "zh|半夜|中午", 470 "zh-TW|子夜|正午", 471 "zh-HK|深夜|中午", 472 "ja|真夜中|正午", 473 "ko|자정|정오", 474 "tr|gece yarısı|öğlen", 475 "az|gecəyarı|günorta", 476 "kk|түн жарымы|талтүс", 477 "ky|түн ортосу|чак түш", 478 "uz|yarim tun|tush", 479 "et|kesköö|keskpäev", 480 "fi|keskiyö|keskipäivä", 481 "hu|éjfél|dél", 482 "th|เที่ยงคืน|เที่ยง", 483 "lo|ທ່ຽງຄືນ|ທ່ຽງ", 484 "ar| منتصف الليل|ظهرا", 485 "he|חצות|N/A", 486 "id|tengah malam|tengah hari", 487 "ms|Tengah malam|Tengah hari", 488 "fil|hating-gabi|tangaling-tapat", 489 "vi|nửa đêm|trưa", 490 "km|អាធ្រាត្រ|ថ្ងៃត្រង់", 491 "sw|saa sita za usiku|saa sita za mchana", 492 "zu|N/A|N/A", 493 "am|እኩለ ሌሊት|ቀትር", 494 "eu|gauerdia|eguerdia", 495 "mn|шөнө дунд|үд дунд", 496 "my|သန်းခေါင်ယံ|မွန်းတည့်", 497 }; 498 static { 499 Splitter BAR = Splitter.on('|').trimResults(); 500 for (String s : NOON_MIDNIGHT) { 501 List<String> items = BAR.splitToList(s); 502 NoonMidnight nm = new NoonMidnight(items); 503 NOON_MIDNIGHT_MAP.put(items.get(0), nm); 504 } 505 } 506 } 507 } 508