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