blob: 8c7390fcce1ce3a13b824884e4b24c18eee8fc32 [file] [log] [blame]
package org.unicode.cldr.tool;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.unicode.cldr.util.Builder;
import org.unicode.cldr.util.CLDRConfig;
import org.unicode.cldr.util.CLDRFile;
import org.unicode.cldr.util.Containment;
import org.unicode.cldr.util.DateTimeCanonicalizer.DateTimePatternType;
import org.unicode.cldr.util.Factory;
import org.unicode.cldr.util.LanguageTagParser;
import org.unicode.cldr.util.PreferredAndAllowedHour;
import org.unicode.cldr.util.SupplementalDataInfo.OfficialStatus;
import org.unicode.cldr.util.SupplementalDataInfo.PopulationData;
import org.unicode.cldr.util.With;
import com.ibm.icu.dev.util.CollectionUtilities;
import com.ibm.icu.impl.Relation;
import com.ibm.icu.text.DateTimePatternGenerator.FormatParser;
import com.ibm.icu.text.DateTimePatternGenerator.VariableField;
import com.ibm.icu.text.UnicodeSet;
public class FindPreferredHours {
private static CLDRConfig INFO = ToolConfig.getToolInstance();
private static final CLDRFile ENGLISH = INFO.getEnglish();
private static final UnicodeSet DIGITS = new UnicodeSet("[0-9]").freeze();
private static final Set<Character> ONLY24 = Collections.unmodifiableSet(new LinkedHashSet<Character>(Arrays
.asList('H')));
private final static Map<String, Set<Character>> OVERRIDE_ALLOWED = Builder
.with(new HashMap<String, Set<Character>>())
.put("RU", ONLY24)
.put("IL", ONLY24)
.freeze();
private final static Map<String, Character> CONFLICT_RESOLUTION = Builder.with(new HashMap<String, Character>())
.put("DJ", 'h')
.put("KM", 'H')
.put("MG", 'H')
.put("MU", 'H')
.put("MZ", 'H')
.put("SC", 'H')
.put("CM", 'H')
.put("TD", 'h')
.put("DZ", 'h')
.put("MA", 'h')
.put("TN", 'h')
.put("BW", 'h')
.put("LS", 'h')
.put("NA", 'h')
.put("SZ", 'h')
.put("ZA", 'h')
.put("GH", 'h')
.put("MR", 'h')
.put("NG", 'h')
.put("TG", 'H')
.put("CA", 'h')
.put("US", 'h')
.put("CN", 'h')
.put("MO", 'h')
.put("PH", 'H')
.put("IN", 'h')
.put("LK", 'H')
.put("CY", 'h')
.put("IL", 'H')
.put("SY", 'h')
.put("MK", 'H')
.put("VU", 'h')
.put("TO", 'H')
.put("001", 'H')
.freeze();
static final class Hours implements Comparable<Hours> {
final DateTimePatternType type;
final char variable;
public Hours(DateTimePatternType type, String variable) {
this.type = type;
this.variable = variable.charAt(0);
}
@Override
public int compareTo(Hours arg0) {
// TODO Auto-generated method stub
int result = type.compareTo(arg0.type);
if (result != 0) return result;
return variable < arg0.variable ? -1 : variable > arg0.variable ? 1 : 0;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return type + ":" + variable;
}
@Override
public boolean equals(Object obj) {
return obj instanceof Hours && compareTo((Hours) obj) == 0;
}
}
public static void main(String[] args) {
final Relation<String, Hours> lang2Hours = Relation.of(new TreeMap<String, Set<Hours>>(), TreeSet.class);
final Factory factory = INFO.getCldrFactory();
final FormatParser formatDateParser = new FormatParser();
final LikelySubtags likely2Max = new LikelySubtags(INFO.getSupplementalDataInfo());
for (final String locale : factory.getAvailable()) {
if (locale.equals("root")) {
continue;
}
// if (locale.charAt(0) > 'b') {
// continue;
// }
final CLDRFile cldrFile = factory.make(locale, true);
for (String path : With.in(cldrFile)) {
// if (path.contains("/timeFormats")) {
// System.out.println(path);
// }
DateTimePatternType type = DateTimePatternType.fromPath(path);
if (type == DateTimePatternType.NA || type == DateTimePatternType.GMT) {
continue;
}
String value = cldrFile.getStringValue(path);
formatDateParser.set(value);
for (Object item : formatDateParser.getItems()) {
if (item instanceof VariableField) {
String itemString = item.toString();
if (PreferredAndAllowedHour.HourStyle.isHourCharacter(itemString)) {
lang2Hours.put(locale, new Hours(type, itemString));
}
}
}
}
System.out.println(locale + "\t" + lang2Hours.get(locale));
// for (Entry<String, Set<String>> e : lang2Hours.keyValuesSet()) {
// System.out.println(e);
// }
}
// gather data per region
Map<String, Relation<Character, String>> region2Preferred2locales = new TreeMap<String, Relation<Character, String>>();
Relation<String, Character> region2Allowed = Relation.of(new TreeMap<String, Set<Character>>(), TreeSet.class);
final LanguageTagParser ltp = new LanguageTagParser();
for (Entry<String, Set<Hours>> localeAndHours : lang2Hours.keyValuesSet()) {
String locale = localeAndHours.getKey();
String maxLocale = likely2Max.maximize(locale);
if (maxLocale == null) {
System.out.println("*** Missing likely for " + locale);
continue;
}
String region = ltp.set(maxLocale).getRegion();
if (region.isEmpty()) {
System.out.println("*** Missing region for " + locale + ", " + maxLocale);
continue;
}
if (DIGITS.containsSome(region) && !region.equals("001")) {
System.out.println("*** Skipping multicountry region for " + locale + ", " + maxLocale);
continue;
}
for (Hours hours : localeAndHours.getValue()) {
region2Allowed.put(region, hours.variable);
if (hours.type == DateTimePatternType.STOCK) {
Relation<Character, String> items = region2Preferred2locales.get(region);
if (items == null) {
region2Preferred2locales.put(region,
items = Relation.of(new TreeMap<Character, Set<String>>(), TreeSet.class));
}
items.put(hours.variable, locale);
}
}
}
// now invert
Relation<PreferredAndAllowedHour, String> preferred2Region = Relation.of(
new TreeMap<PreferredAndAllowedHour, Set<String>>(), TreeSet.class);
StringBuilder overrides = new StringBuilder("\n");
for (Entry<String, Relation<Character, String>> e : region2Preferred2locales.entrySet()) {
String region = e.getKey();
Set<Character> allowed = region2Allowed.get(region);
Relation<Character, String> preferredSet = e.getValue();
Character resolvedValue = CONFLICT_RESOLUTION.get(region);
if (resolvedValue != null) {
if (preferredSet.size() == 1) {
overrides.append(region + " didn't need override!!\n");
} else {
LinkedHashSet<Entry<Character, String>> oldValues = new LinkedHashSet<Entry<Character, String>>();
StringBuilder oldValuesString = new StringBuilder();
for (Entry<Character, String> x : preferredSet.keyValueSet()) {
if (!x.getKey().equals(resolvedValue)) {
oldValues.add(x);
oldValuesString.append(x.getKey() + "=" + x.getValue() + "; ");
}
}
for (Entry<Character, String> x : oldValues) {
preferredSet.remove(x.getKey(), x.getValue());
}
overrides.append(region + " has multiple values. Overriding with CONFLICT_RESOLUTION to "
+ resolvedValue + " and discarded values " + oldValuesString + "\n");
}
}
Set<Character> allAllowed = new TreeSet<Character>();
Character preferred = null;
for (Entry<Character, Set<String>> pref : preferredSet.keyValuesSet()) {
allAllowed.addAll(allowed);
if (preferred == null) {
preferred = pref.getKey();
} else {
overrides.append(region + " has multiple preferred values! " + preferredSet + "\n");
}
// else {
// if (!haveFirst) {
// System.out.print("*** Conflict in\t" + region + "\t" + ENGLISH.getName("territory", region) +
// "\twith\t");
// System.out.println(preferred + "\t" + locales);
// haveFirst = true;
// }
// //System.out.println("\t" + pref.getKey() + "\t" + pref.getValue());
// }
}
Set<Character> overrideAllowed = OVERRIDE_ALLOWED.get(region);
if (overrideAllowed != null) {
allAllowed = overrideAllowed;
overrides.append(region + " overriding allowed to " + overrideAllowed + "\n");
}
try {
preferred2Region.put(new PreferredAndAllowedHour(preferred, allAllowed), region);
} catch (RuntimeException e1) {
throw e1;
}
String subcontinent = Containment.getSubcontinent(region);
String continent = Containment.getContinent(region);
String tag = CollectionUtilities.join(preferredSet.keySet(), ",");
if (tag.equals("h")) {
tag += "*";
}
System.out.println(tag
+ "\t" + region
+ "\t" + ENGLISH.getName("territory", region)
+ "\t" + subcontinent
+ "\t" + ENGLISH.getName("territory", subcontinent)
+ "\t" + continent
+ "\t" + ENGLISH.getName("territory", continent)
+ "\t" + showInfo(preferredSet));
}
// now present
System.out.println(" <timeData>");
for (Entry<PreferredAndAllowedHour, Set<String>> e : preferred2Region.keyValuesSet()) {
PreferredAndAllowedHour preferredAndAllowedHour = e.getKey();
Set<String> regions = e.getValue();
System.out.println(" <hours "
+ "preferred=\""
+ preferredAndAllowedHour.preferred
+ "\""
+ " allowed=\""
+ CollectionUtilities.join(preferredAndAllowedHour.allowed, " ")
+ "\""
+ " regions=\"" + CollectionUtilities.join(regions, " ") + "\""
+ "/>");
}
System.out.println(" </timeData>");
System.out.println(overrides);
}
private static String showInfo(Relation<Character, String> preferredSet) {
StringBuilder b = new StringBuilder();
for (Character key : Arrays.asList('H', 'h')) {
if (b.length() != 0) {
b.append('\t');
}
b.append(key).append('\t');
Set<String> value = preferredSet.get(key);
if (value != null) {
boolean needSpace = false;
for (String locale : value) {
if (needSpace) {
b.append(" ");
} else {
needSpace = true;
}
b.append(locale);
boolean isOfficial = false;
isOfficial = isOfficial(locale, isOfficial);
if (isOfficial) {
b.append('°');
}
}
}
}
return b.toString();
}
private static boolean isOfficial(String locale, boolean isOfficial) {
LanguageTagParser ltp = new LanguageTagParser().set(locale);
PopulationData data = INFO.getSupplementalDataInfo().getLanguageAndTerritoryPopulationData(
ltp.getLanguageScript(), ltp.getRegion());
if (data == null) {
data = INFO.getSupplementalDataInfo().getLanguageAndTerritoryPopulationData(
ltp.getLanguage(), ltp.getRegion());
}
if (data != null) {
OfficialStatus status = data.getOfficialStatus();
if (status == OfficialStatus.official || status == OfficialStatus.de_facto_official) {
isOfficial = true;
}
}
return isOfficial;
}
}