• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.util;
2 
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.util.EnumMap;
6 import java.util.HashSet;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10 import java.util.TreeMap;
11 import java.util.TreeSet;
12 
13 import com.google.common.collect.ImmutableSet;
14 import com.ibm.icu.impl.Relation;
15 import com.ibm.icu.util.ICUUncheckedIOException;
16 
17 class LocalesTxtReader {
18     Map<Organization, Map<String, Level>> platform_locale_level = null;
19     Map<Organization, Relation<Level, String>> platform_level_locale = null;
20     Map<String, Map<String, String>> platform_locale_levelString = null;
21     Map<Organization, Map<String, Integer>> organization_locale_weight = null;
22     Map<Organization, Map<String, Set<String>>> organization_locale_match = null;
23 
24     public static final String DEFAULT_NAME = "Locales.txt";
25 
LocalesTxtReader()26     public LocalesTxtReader() {
27     }
28 
29     /**
30      * Read from Locales.txt, from the default location
31      * @param lstreg stream to read from
32      */
read(StandardCodes sc)33     public LocalesTxtReader read(StandardCodes sc) {
34         try (BufferedReader lstreg = CldrUtility.getUTF8Data(DEFAULT_NAME);) {
35             return read(sc, lstreg);
36         } catch (IOException e) {
37             throw new ICUUncheckedIOException("Internal Error reading Locales.txt", e);
38         }
39     }
40 
41     /**
42      * Parse a Locales.txt file
43      * @param sc StandardCodes used for validation
44      * @param lstreg stream to read from
45      */
read(StandardCodes sc, BufferedReader lstreg)46     public LocalesTxtReader read(StandardCodes sc, BufferedReader lstreg) {
47         LocaleIDParser parser = new LocaleIDParser();
48         platform_locale_level = new EnumMap<>(Organization.class);
49         organization_locale_weight = new EnumMap<>(Organization.class);
50         organization_locale_match = new EnumMap<>(Organization.class);
51         SupplementalDataInfo sd = SupplementalDataInfo.getInstance();
52         Set<String> defaultContentLocales = sd.getDefaultContentLocales();
53         String line;
54         try {
55             while (true) {
56                 Integer weight = null; // @weight
57                 String pathMatch = null; // @pathMatch
58                 line = lstreg.readLine();
59                 if (line == null)
60                     break;
61                 int commentPos = line.indexOf('#');
62                 if (commentPos >= 0) {
63                     line = line.substring(0, commentPos);
64                 }
65                 line = line.trim();
66                 if (line.length() == 0)
67                     continue;
68                 List<String> stuff = CldrUtility.splitList(line, ';', true);
69                 Organization organization;
70 
71                 // verify that the organization is valid
72                 try {
73                     organization = Organization.fromString(stuff.get(0));
74                 } catch (Exception e) {
75                     throw new IllegalArgumentException("Invalid organization in Locales.txt: " + line);
76                 }
77 
78                 // verify that the locale is valid BCP47
79                 String localePart = stuff.get(1).trim();
80                 List<String> localeStuff = CldrUtility.splitList(localePart, ' ', true);
81                 Set<String> locales = new TreeSet<>();
82 
83                 for (final String entry : localeStuff) {
84                     if (entry.startsWith("@")) {
85                         List<String> kwStuff = CldrUtility.splitList(entry, '=', true);
86                         if (kwStuff.size() > 2 || kwStuff.size() < 1) {
87                             throw new IllegalArgumentException("Invalid @-command " + entry + " in Locales.txt: " + line);
88                         }
89                         final String atCommand = kwStuff.get(0);
90                         switch(atCommand) {
91                             case "@weight":
92                                 weight = Integer.parseInt(kwStuff.get(1));
93                                 break;
94 
95                             case "@pathMatch":
96                                 pathMatch = kwStuff.get(1);
97                             break;
98                             default:
99                                 throw new IllegalArgumentException("Unknown @-command " + atCommand + " in Locales.txt: " + line);
100                         }
101                     } else {
102                         locales.add(entry);
103                     }
104                 }
105 
106                 if (locales.size() != 1) {
107                     // require there to be exactly one locale.
108                     // This would allow collapsing into fewer lines.
109                     throw new IllegalArgumentException("Expected one locale entry in Locales.txt but got " + locales.size() + ": " + line);
110                 }
111 
112                 // extract the single locale, process as before
113                 String locale = locales.iterator().next();
114 
115                 if (!locale.equals(StandardCodes.ALL_LOCALES)) {
116                     parser.set(locale);
117                     String valid = sc.validate(parser);
118                     if (valid.length() != 0) {
119                         throw new IllegalArgumentException("Invalid locale in Locales.txt: " + line);
120                     }
121                     locale = parser.toString(); // normalize
122 
123                     // verify that the locale is not a default content locale
124                     if (defaultContentLocales.contains(locale)) {
125                         throw new IllegalArgumentException("Cannot have default content locale in Locales.txt: " + line);
126                     }
127                 }
128 
129                 Level status = Level.get(stuff.get(2));
130                 if (status == Level.UNDETERMINED) {
131                     System.out.println("Warning: Level unknown on: " + line);
132                 }
133                 Map<String, Level> locale_status = platform_locale_level.get(organization);
134                 if (locale_status == null) {
135                     platform_locale_level.put(organization, locale_status = new TreeMap<>());
136                 }
137                 locale_status.put(locale, status);
138 
139                 if (weight != null) {
140                     organization_locale_weight
141                         .computeIfAbsent(organization, ignored -> new TreeMap<>())
142                         .put(locale, weight);
143                 }
144                 if (pathMatch != null) {
145                     organization_locale_match
146                         .computeIfAbsent(organization, ignored -> new TreeMap<>())
147                         .put(locale, ImmutableSet.copyOf(pathMatch.split(",")));
148                 }
149             }
150         } catch (IOException e) {
151             throw new ICUUncheckedIOException("Internal Error", e);
152         }
153 
154         // backwards compat hack
155         platform_locale_levelString = new TreeMap<>();
156         platform_level_locale = new EnumMap<>(Organization.class);
157         for (Organization platform : platform_locale_level.keySet()) {
158             Map<String, String> locale_levelString = new TreeMap<>();
159             platform_locale_levelString.put(platform.toString(), locale_levelString);
160             Map<String, Level> locale_level = platform_locale_level.get(platform);
161             for (String locale : locale_level.keySet()) {
162                 locale_levelString.put(locale, locale_level.get(locale).toString());
163             }
164             Relation<Level, String> level_locale = Relation.of(new EnumMap(Level.class), HashSet.class);
165             level_locale.addAllInverted(locale_level).freeze();
166             platform_level_locale.put(platform, level_locale);
167         }
168         CldrUtility.protectCollection(platform_level_locale);
169         platform_locale_level = CldrUtility.protectCollection(platform_locale_level);
170         platform_locale_levelString = CldrUtility.protectCollection(platform_locale_levelString);
171         return this;
172     }
173 }
174