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