• 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.ArrayList;
6 import java.util.Collections;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Locale;
11 import java.util.Map;
12 import java.util.Set;
13 import java.util.TreeSet;
14 
15 import org.unicode.cldr.tool.CLDRFileTransformer;
16 import org.unicode.cldr.tool.CLDRFileTransformer.LocaleTransform;
17 
18 import com.ibm.icu.util.ICUUncheckedIOException;
19 
20 /**
21  * List of locale IDs which are somehow 'special'. Parses SpecialLocales.txt
22  *
23  * @author srl
24  *
25  */
26 public class SpecialLocales {
27     private static final String INCLUDE_SUBLOCALES = "*";
28 
29     public enum Type {
30         /**
31          * Locale may not be modified by user.
32          */
33         readonly,
34         /**
35          * Locale may be modified by user. Contents aren't part of CLDR release and may change.
36          */
37         scratch
38     }
39 
40     /**
41      * Get the type of this locale
42      *
43      * @param l
44      * @return a Type or null
45      */
getType(CLDRLocale l)46     public static Type getType(CLDRLocale l) {
47         return getInstance().getTypeInternal(l);
48     }
49 
50     /**
51      * Get all CLDRLocales matching this type. Does not include wildcard (*) sublocales.
52      *
53      * @param t
54      * @return a set, or null if none found
55      */
getByType(Type t)56     public static Set<CLDRLocale> getByType(Type t) {
57         return getInstance().getByTypeInternal(t);
58     }
59 
60     /**
61      * Get the comment on this locale. Strip out @ text.
62      *
63      * @param l
64      * @return string or null
65      */
getComment(CLDRLocale l)66     public static String getComment(CLDRLocale l) {
67         return getCommentRaw(l).replaceAll("@", "");
68     }
69 
70     /**
71      * Get the comment on this locale. Include "@locale" markers.
72      *
73      * @param l
74      * @return string or null
75      */
getCommentRaw(CLDRLocale l)76     public static String getCommentRaw(CLDRLocale l) {
77         return getInstance().getCommentInternal(l).replaceAll("@@", "@" + l.getBaseName());
78     }
79 
80     private static final class SpecialLocalesHelper {
81         static final SpecialLocales SINGLETON = new SpecialLocales();
82     }
83 
84     /**
85      * Internal accessor. All access is via the static functions.
86      * @return
87      */
getInstance()88     private static synchronized SpecialLocales getInstance() {
89         return SpecialLocalesHelper.SINGLETON;
90     }
91 
92     private Map<CLDRLocale, Type> specials = new HashMap<>();
93     private Map<Type, Set<CLDRLocale>> types = new HashMap<>();
94     private Map<CLDRLocale, String> comments = new HashMap<>();
95     private Set<CLDRLocale> specialsWildcards = new HashSet<>();
96 
getByTypeInternal(Type t)97     public Set<CLDRLocale> getByTypeInternal(Type t) {
98         return types.get(t);
99     }
100 
getTypeInternal(CLDRLocale l)101     public Type getTypeInternal(CLDRLocale l) {
102         l = findLocale(l, l);
103         return specials.get(l);
104     }
105 
getCommentInternal(CLDRLocale l)106     public String getCommentInternal(CLDRLocale l) {
107         l = findLocale(l, l);
108         return comments.get(l);
109     }
110 
findLocale(CLDRLocale fromLocale, CLDRLocale origLocale)111     public CLDRLocale findLocale(CLDRLocale fromLocale, CLDRLocale origLocale) {
112         if (origLocale == fromLocale && specials.containsKey(origLocale)) {
113             return origLocale; // explicit locale - no search.
114         }
115         if (fromLocale == null) {
116             return origLocale;
117         }
118         if (specialsWildcards.contains(fromLocale)) {
119             return fromLocale;
120         }
121         return findLocale(fromLocale.getParent(), origLocale);
122     }
123     private static boolean DEBUG = false;
124 
125     /**
126      * Internal constructor
127      */
SpecialLocales()128     private SpecialLocales() {
129         // First, read the algorithmic locales.
130         for(final LocaleTransform lt : CLDRFileTransformer.LocaleTransform.values()) {
131             if(lt.getPolicyIfExisting() != CLDRFileTransformer.PolicyIfExisting.DISCARD) {
132                 continue;
133             }
134             // Add each of these as if they were in SpecialLocales.txt
135             CLDRLocale inputLocale = CLDRLocale.getInstance(lt.getInputLocale());
136             CLDRLocale outputLocale = CLDRLocale.getInstance(lt.getOutputLocale());
137 
138             // add as readonly
139             addToType(Type.readonly, outputLocale);
140 
141             // add similar comment to SpecialLocales.txt
142             comments.put(outputLocale, "@"+outputLocale.getBaseName()+" is generated from @"+inputLocale.getBaseName() +
143                 " via transliteration, and so @@ may not be edited directly. Edit @"+inputLocale.getBaseName()+" to make changes.");
144         }
145 
146         for(final DataFileRow r : DataFileRow.ROWS) {
147             // verify that the locale is valid
148             CLDRLocale l = null;
149             try {
150                 l = CLDRLocale.getInstance(r.id);
151             } catch (Exception e) {
152                 throw new IllegalArgumentException("Invalid CLDRLocale in SpecialLocales.txt:" + r.id);
153             }
154 
155             addToType(r.type, l);
156             if (r.includeSubLocs) {
157                 if(r.type == Type.scratch) {
158                     throw new IllegalArgumentException("Scratch locales cannot include sublocales: " + l);
159                 }
160                 specialsWildcards.add(l);
161             }
162             if (!r.comment.isEmpty()) {
163                 comments.put(l, r.comment);
164             }
165             if (DEBUG) {
166                 System.out.println(SpecialLocales.class.getSimpleName() + ": locale " + l + ", includejSublocs=" + r.includeSubLocs + ", type=" + r.type
167                     + ", comment: " + r.comment);
168             }
169 
170         }
171         specials = Collections.unmodifiableMap(specials);
172         specialsWildcards = Collections.unmodifiableSet(specialsWildcards);
173         comments = Collections.unmodifiableMap(comments);
174         types = Collections.unmodifiableMap(types);
175     }
176 
177     private static class DataFileRow {
178         public boolean includeSubLocs;
DataFileRow(String id, Type type, String comment, boolean includeSubLocs)179         public DataFileRow(String id, Type type, String comment, boolean includeSubLocs) {
180             this.id = id;
181             this.type = type;
182             this.comment = comment;
183             this.includeSubLocs = includeSubLocs;
184         }
185         public String id;
186         public Type type;
187         public String comment;
188 
189         public static List<DataFileRow> ROWS = readDataFile();
190 
readDataFile()191         static private List<DataFileRow> readDataFile() {
192             List<DataFileRow> rows = new ArrayList<>();
193             // from StandardCodes.java
194             String line;
195             int ln = 0;
196             try {
197                 BufferedReader lstreg = CldrUtility.getUTF8Data("SpecialLocales.txt");
198                 while (true) {
199                     line = lstreg.readLine();
200                     ln++;
201                     if (line == null)
202                         break;
203                     int commentPos = line.indexOf('#');
204                     if (commentPos >= 0) {
205                         line = line.substring(0, commentPos);
206                     }
207                     line = line.trim();
208                     if (line.length() == 0)
209                         continue;
210                     List<String> stuff = CldrUtility.splitList(line, ';', true);
211                     String id = stuff.get(0);
212                     boolean includeSublocs = (id.endsWith(INCLUDE_SUBLOCALES));
213                     if (includeSublocs) {
214                         id = id.substring(0, id.length() - INCLUDE_SUBLOCALES.length());
215                     }
216                     String type = stuff.get(1);
217                     String comment = stuff.get(2);
218                     Type t = null;
219 
220 
221                     // verify that the type is valid
222                     try {
223                         t = Type.valueOf(type.toLowerCase(Locale.ENGLISH));
224                     } catch (Exception e) {
225                         throw new IllegalArgumentException("Invalid SpecialLocales.Type in SpecialLocales.txt:" + ln + ": "
226                             + line);
227                     }
228 
229                     rows.add(new DataFileRow(id, t, comment, includeSublocs));
230                 }
231             } catch (IOException e) {
232                 throw new ICUUncheckedIOException("Internal Error", e);
233             }
234             return rows;
235         }
236     }
237 
addToType(Type t, CLDRLocale l)238     private Set<CLDRLocale> addToType(Type t, CLDRLocale l) {
239         Set<CLDRLocale> s = types.get(t);
240         if (s == null) {
241             s = new TreeSet<>();
242             types.put(t, s);
243         }
244         s.add(l);
245         specials.put(l, t);
246         return s;
247     }
248 
249     /**
250      * @param locale
251      * @return true if the locale type is scratch
252      * @deprecated use the CLDRLocale variant
253      */
254     @Deprecated
isScratchLocale(String locale)255     public static boolean isScratchLocale(String locale) {
256         return isScratchLocale(CLDRLocale.getInstance(locale));
257     }
258 
259     /**
260      * Check if this is a scratch (sandbox) locale
261      * @param loc
262      * @return true if it is a sandbox locale
263      */
isScratchLocale(CLDRLocale loc)264     public static boolean isScratchLocale(CLDRLocale loc) {
265         return getType(loc) == Type.scratch;
266     }
267 
268     /**
269      * Low level function to list scratch locales.
270      * Used for fetching the list prior to CLDRLocale startup.
271      * @return
272      */
getScratchLocaleIds()273     public static List<String> getScratchLocaleIds() {
274         List<String> ids = new ArrayList<>();
275         for(final DataFileRow r : DataFileRow.ROWS) {
276             if(r.type == Type.scratch) {
277                 ids.add(r.id);
278             }
279         }
280         return ids;
281     }
282 }
283