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