• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.tool;
2 
3 import com.google.common.base.Joiner;
4 import com.ibm.icu.impl.Relation;
5 import com.ibm.icu.text.DateTimePatternGenerator.FormatParser;
6 import com.ibm.icu.text.DateTimePatternGenerator.VariableField;
7 import com.ibm.icu.text.UnicodeSet;
8 import java.util.Arrays;
9 import java.util.Collections;
10 import java.util.HashMap;
11 import java.util.LinkedHashSet;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import java.util.Set;
15 import java.util.TreeMap;
16 import java.util.TreeSet;
17 import org.unicode.cldr.util.Builder;
18 import org.unicode.cldr.util.CLDRConfig;
19 import org.unicode.cldr.util.CLDRFile;
20 import org.unicode.cldr.util.Containment;
21 import org.unicode.cldr.util.DateTimeCanonicalizer.DateTimePatternType;
22 import org.unicode.cldr.util.Factory;
23 import org.unicode.cldr.util.LanguageTagParser;
24 import org.unicode.cldr.util.PreferredAndAllowedHour;
25 import org.unicode.cldr.util.SupplementalDataInfo.OfficialStatus;
26 import org.unicode.cldr.util.SupplementalDataInfo.PopulationData;
27 import org.unicode.cldr.util.With;
28 
29 public class FindPreferredHours {
30     private static CLDRConfig INFO = ToolConfig.getToolInstance();
31     private static final CLDRFile ENGLISH = INFO.getEnglish();
32     private static final UnicodeSet DIGITS = new UnicodeSet("[0-9]").freeze();
33 
34     private static final Set<Character> ONLY24 =
35             Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList('H')));
36 
37     private static final Map<String, Set<Character>> OVERRIDE_ALLOWED =
38             Builder.with(new HashMap<String, Set<Character>>())
39                     .put("RU", ONLY24)
40                     .put("IL", ONLY24)
41                     .freeze();
42 
43     private static final Map<String, Character> CONFLICT_RESOLUTION =
44             Builder.with(new HashMap<String, Character>())
45                     .put("DJ", 'h')
46                     .put("KM", 'H')
47                     .put("MG", 'H')
48                     .put("MU", 'H')
49                     .put("MZ", 'H')
50                     .put("SC", 'H')
51                     .put("CM", 'H')
52                     .put("TD", 'h')
53                     .put("DZ", 'h')
54                     .put("MA", 'h')
55                     .put("TN", 'h')
56                     .put("BW", 'h')
57                     .put("LS", 'h')
58                     .put("NA", 'h')
59                     .put("SZ", 'h')
60                     .put("ZA", 'h')
61                     .put("GH", 'h')
62                     .put("MR", 'h')
63                     .put("NG", 'h')
64                     .put("TG", 'H')
65                     .put("CA", 'h')
66                     .put("US", 'h')
67                     .put("CN", 'h')
68                     .put("MO", 'h')
69                     .put("PH", 'H')
70                     .put("IN", 'h')
71                     .put("LK", 'H')
72                     .put("CY", 'h')
73                     .put("IL", 'H')
74                     .put("SY", 'h')
75                     .put("MK", 'H')
76                     .put("VU", 'h')
77                     .put("TO", 'H')
78                     .put("001", 'H')
79                     .freeze();
80 
81     static final class Hours implements Comparable<Hours> {
82         final DateTimePatternType type;
83         final char variable;
84 
Hours(DateTimePatternType type, String variable)85         public Hours(DateTimePatternType type, String variable) {
86             this.type = type;
87             this.variable = variable.charAt(0);
88         }
89 
90         @Override
compareTo(Hours arg0)91         public int compareTo(Hours arg0) {
92             // TODO Auto-generated method stub
93             int result = type.compareTo(arg0.type);
94             if (result != 0) return result;
95             return variable < arg0.variable ? -1 : variable > arg0.variable ? 1 : 0;
96         }
97 
98         @Override
toString()99         public String toString() {
100             // TODO Auto-generated method stub
101             return type + ":" + variable;
102         }
103 
104         @Override
equals(Object obj)105         public boolean equals(Object obj) {
106             return obj instanceof Hours && compareTo((Hours) obj) == 0;
107         }
108     }
109 
main(String[] args)110     public static void main(String[] args) {
111         final Relation<String, Hours> lang2Hours =
112                 Relation.of(new TreeMap<String, Set<Hours>>(), TreeSet.class);
113         final Factory factory = INFO.getCldrFactory();
114         final FormatParser formatDateParser = new FormatParser();
115         final LikelySubtags likely2Max = new LikelySubtags();
116 
117         for (final String locale : factory.getAvailable()) {
118             if (locale.equals("root")) {
119                 continue;
120             }
121             // if (locale.charAt(0) > 'b') {
122             // continue;
123             // }
124             final CLDRFile cldrFile = factory.make(locale, true);
125             for (String path : With.in(cldrFile)) {
126                 // if (path.contains("/timeFormats")) {
127                 // System.out.println(path);
128                 // }
129                 DateTimePatternType type = DateTimePatternType.fromPath(path);
130                 if (type == DateTimePatternType.NA || type == DateTimePatternType.GMT) {
131                     continue;
132                 }
133                 String value = cldrFile.getStringValue(path);
134                 formatDateParser.set(value);
135                 for (Object item : formatDateParser.getItems()) {
136                     if (item instanceof VariableField) {
137                         String itemString = item.toString();
138                         if (PreferredAndAllowedHour.HourStyle.isHourCharacter(itemString)) {
139                             lang2Hours.put(locale, new Hours(type, itemString));
140                         }
141                     }
142                 }
143             }
144             System.out.println(locale + "\t" + lang2Hours.get(locale));
145             // for (Entry<String, Set<String>> e : lang2Hours.keyValuesSet()) {
146             // System.out.println(e);
147             // }
148         }
149 
150         // gather data per region
151 
152         Map<String, Relation<Character, String>> region2Preferred2locales = new TreeMap<>();
153         Relation<String, Character> region2Allowed =
154                 Relation.of(new TreeMap<String, Set<Character>>(), TreeSet.class);
155         final LanguageTagParser ltp = new LanguageTagParser();
156 
157         for (Entry<String, Set<Hours>> localeAndHours : lang2Hours.keyValuesSet()) {
158             String locale = localeAndHours.getKey();
159             String maxLocale = likely2Max.maximize(locale);
160             if (maxLocale == null) {
161                 System.out.println("*** Missing likely for " + locale);
162                 continue;
163             }
164             String region = ltp.set(maxLocale).getRegion();
165             if (region.isEmpty()) {
166                 System.out.println("*** Missing region for " + locale + ", " + maxLocale);
167                 continue;
168             }
169             if (DIGITS.containsSome(region) && !region.equals("001")) {
170                 System.out.println(
171                         "*** Skipping multicountry region for " + locale + ", " + maxLocale);
172                 continue;
173             }
174             for (Hours hours : localeAndHours.getValue()) {
175                 region2Allowed.put(region, hours.variable);
176                 if (hours.type == DateTimePatternType.STOCK) {
177                     Relation<Character, String> items = region2Preferred2locales.get(region);
178                     if (items == null) {
179                         region2Preferred2locales.put(
180                                 region,
181                                 items =
182                                         Relation.of(
183                                                 new TreeMap<Character, Set<String>>(),
184                                                 TreeSet.class));
185                     }
186                     items.put(hours.variable, locale);
187                 }
188             }
189         }
190 
191         // now invert
192         Relation<PreferredAndAllowedHour, String> preferred2Region =
193                 Relation.of(new TreeMap<PreferredAndAllowedHour, Set<String>>(), TreeSet.class);
194         StringBuilder overrides = new StringBuilder("\n");
195 
196         for (Entry<String, Relation<Character, String>> e : region2Preferred2locales.entrySet()) {
197             String region = e.getKey();
198             Set<Character> allowed = region2Allowed.get(region);
199             Relation<Character, String> preferredSet = e.getValue();
200             Character resolvedValue = CONFLICT_RESOLUTION.get(region);
201             if (resolvedValue != null) {
202                 if (preferredSet.size() == 1) {
203                     overrides.append(region + " didn't need override!!\n");
204                 } else {
205                     LinkedHashSet<Entry<Character, String>> oldValues = new LinkedHashSet<>();
206                     StringBuilder oldValuesString = new StringBuilder();
207                     for (Entry<Character, String> x : preferredSet.keyValueSet()) {
208                         if (!x.getKey().equals(resolvedValue)) {
209                             oldValues.add(x);
210                             oldValuesString.append(x.getKey() + "=" + x.getValue() + "; ");
211                         }
212                     }
213                     for (Entry<Character, String> x : oldValues) {
214                         preferredSet.remove(x.getKey(), x.getValue());
215                     }
216                     overrides.append(
217                             region
218                                     + " has multiple values. Overriding with CONFLICT_RESOLUTION to "
219                                     + resolvedValue
220                                     + " and discarded values "
221                                     + oldValuesString
222                                     + "\n");
223                 }
224             }
225 
226             Set<Character> allAllowed = new TreeSet<>();
227             Character preferred = null;
228 
229             for (Entry<Character, Set<String>> pref : preferredSet.keyValuesSet()) {
230                 allAllowed.addAll(allowed);
231                 if (preferred == null) {
232                     preferred = pref.getKey();
233                 } else {
234                     overrides.append(
235                             region + " has multiple preferred values! " + preferredSet + "\n");
236                 }
237                 // else {
238                 // if (!haveFirst) {
239                 // System.out.print("*** Conflict in\t" + region + "\t" +
240                 // ENGLISH.getName("territory", region) +
241                 // "\twith\t");
242                 // System.out.println(preferred + "\t" + locales);
243                 // haveFirst = true;
244                 // }
245                 // //System.out.println("\t" + pref.getKey() + "\t" + pref.getValue());
246                 // }
247             }
248             Set<Character> overrideAllowed = OVERRIDE_ALLOWED.get(region);
249             if (overrideAllowed != null) {
250                 allAllowed = overrideAllowed;
251                 overrides.append(region + " overriding allowed to " + overrideAllowed + "\n");
252             }
253             try {
254                 preferred2Region.put(new PreferredAndAllowedHour(preferred, allAllowed), region);
255             } catch (RuntimeException e1) {
256                 throw e1;
257             }
258             String subcontinent = Containment.getSubcontinent(region);
259             String continent = Containment.getContinent(region);
260             String tag = Joiner.on(",").join(preferredSet.keySet());
261             if (tag.equals("h")) {
262                 tag += "*";
263             }
264 
265             System.out.println(
266                     tag
267                             + "\t"
268                             + region
269                             + "\t"
270                             + ENGLISH.getName("territory", region)
271                             + "\t"
272                             + subcontinent
273                             + "\t"
274                             + ENGLISH.getName("territory", subcontinent)
275                             + "\t"
276                             + continent
277                             + "\t"
278                             + ENGLISH.getName("territory", continent)
279                             + "\t"
280                             + showInfo(preferredSet));
281         }
282 
283         // now present
284 
285         System.out.println("    <timeData>");
286         for (Entry<PreferredAndAllowedHour, Set<String>> e : preferred2Region.keyValuesSet()) {
287             PreferredAndAllowedHour preferredAndAllowedHour = e.getKey();
288             Set<String> regions = e.getValue();
289             System.out.println(
290                     "        <hours "
291                             + "preferred=\""
292                             + preferredAndAllowedHour.preferred
293                             + "\""
294                             + " allowed=\""
295                             + Joiner.on(" ").join(preferredAndAllowedHour.allowed)
296                             + "\""
297                             + " regions=\""
298                             + Joiner.on(" ").join(regions)
299                             + "\""
300                             + "/>");
301         }
302         System.out.println("    </timeData>");
303         System.out.println(overrides);
304     }
305 
showInfo(Relation<Character, String> preferredSet)306     private static String showInfo(Relation<Character, String> preferredSet) {
307         StringBuilder b = new StringBuilder();
308         for (Character key : Arrays.asList('H', 'h')) {
309             if (b.length() != 0) {
310                 b.append('\t');
311             }
312             b.append(key).append('\t');
313             Set<String> value = preferredSet.get(key);
314             if (value != null) {
315                 boolean needSpace = false;
316                 for (String locale : value) {
317                     if (needSpace) {
318                         b.append(" ");
319                     } else {
320                         needSpace = true;
321                     }
322                     b.append(locale);
323                     boolean isOfficial = false;
324                     isOfficial = isOfficial(locale, isOfficial);
325                     if (isOfficial) {
326                         b.append('°');
327                     }
328                 }
329             }
330         }
331         return b.toString();
332     }
333 
isOfficial(String locale, boolean isOfficial)334     private static boolean isOfficial(String locale, boolean isOfficial) {
335         LanguageTagParser ltp = new LanguageTagParser().set(locale);
336         PopulationData data =
337                 INFO.getSupplementalDataInfo()
338                         .getLanguageAndTerritoryPopulationData(
339                                 ltp.getLanguageScript(), ltp.getRegion());
340         if (data == null) {
341             data =
342                     INFO.getSupplementalDataInfo()
343                             .getLanguageAndTerritoryPopulationData(
344                                     ltp.getLanguage(), ltp.getRegion());
345         }
346         if (data != null) {
347             OfficialStatus status = data.getOfficialStatus();
348             if (status == OfficialStatus.official || status == OfficialStatus.de_facto_official) {
349                 isOfficial = true;
350             }
351         }
352         return isOfficial;
353     }
354 }
355