1 package org.unicode.cldr.util; 2 3 import java.io.File; 4 import java.util.ArrayList; 5 import java.util.EnumMap; 6 import java.util.LinkedHashSet; 7 import java.util.List; 8 import java.util.Locale; 9 import java.util.Map; 10 import java.util.Set; 11 import java.util.TreeMap; 12 import java.util.concurrent.ConcurrentHashMap; 13 14 import org.unicode.cldr.util.StandardCodes.LstrType; 15 16 import com.google.common.base.Splitter; 17 18 public class Validity { 19 20 public enum Status { 21 regular, special, // for languages only (special codes like mul) 22 macroregion, // regions only (from M.49) 23 deprecated, reserved, private_use, // for clients of cldr with prior agreements 24 unknown, invalid; // (anything else) 25 } 26 27 private static final ConcurrentHashMap<String, Validity> cache = new ConcurrentHashMap<>(); 28 29 private final Map<LstrType, Map<Status, Set<String>>> typeToStatusToCodes; 30 private final Map<LstrType, Map<String, Status>> typeToCodeToStatus; 31 getInstance()32 public static Validity getInstance() { 33 return getInstance(CLDRPaths.VALIDITY_DIRECTORY); 34 } 35 getInstance(String validityDirectory)36 public static Validity getInstance(String validityDirectory) { 37 Validity result = cache.get(validityDirectory); 38 if (result == null) { 39 final Validity value = new Validity(validityDirectory); 40 result = cache.putIfAbsent(validityDirectory, value); 41 if (result == null) { 42 result = value; 43 } 44 } 45 return result; 46 } 47 Validity(String validityDirectory)48 private Validity(String validityDirectory) { 49 Splitter space = Splitter.on(PatternCache.get("\\s+")).trimResults().omitEmptyStrings(); 50 Map<LstrType, Map<Status, Set<String>>> data = new EnumMap<>(LstrType.class); 51 Map<LstrType, Map<String, Status>> codeToStatus = new EnumMap<>(LstrType.class); 52 final String basePath = validityDirectory; 53 54 for (String file : new File(basePath).list()) { 55 if (!file.endsWith(".xml")) { 56 continue; 57 } 58 LstrType type = null; 59 try { 60 type = LstrType.fromString(file.substring(0, file.length() - 4)); 61 } catch (Exception e) { 62 continue; 63 } 64 List<Pair<String, String>> lineData = new ArrayList<>(); 65 Map<Status, Set<String>> submap = data.get(type); 66 if (submap == null) { 67 data.put(type, submap = new EnumMap<>(Status.class)); 68 } 69 Map<String, Status> subCodeToStatus = codeToStatus.get(type); 70 if (subCodeToStatus == null) { 71 codeToStatus.put(type, subCodeToStatus = new TreeMap<>()); 72 } 73 74 XMLFileReader.loadPathValues(basePath + file, lineData, true); 75 for (Pair<String, String> item : lineData) { 76 XPathParts parts = XPathParts.getFrozenInstance(item.getFirst()); 77 if (!"id".equals(parts.getElement(-1))) { 78 continue; 79 } 80 LstrType typeAttr = LstrType.fromString(parts.getAttributeValue(-1, "type")); 81 if (typeAttr != type) { 82 throw new IllegalArgumentException("Corrupt value for " + type); 83 } 84 Status subtypeAttr = Status.valueOf(parts.getAttributeValue(-1, "idStatus")); 85 Set<String> set = submap.get(subtypeAttr); 86 if (set == null) { 87 submap.put(subtypeAttr, set = new LinkedHashSet<>()); 88 } 89 for (String value : space.split(item.getSecond())) { 90 if (type == LstrType.subdivision) { 91 value = value.toLowerCase(Locale.ROOT).replace("-", ""); 92 } 93 int dashPos = value.indexOf('~'); 94 if (dashPos < 0) { 95 set.add(value); 96 } else { 97 StringRange.expand(value.substring(0, dashPos), value.substring(dashPos + 1), set); 98 } 99 } 100 for (String code : set) { 101 subCodeToStatus.put(code, subtypeAttr); 102 } 103 } 104 } 105 if (data.keySet().size() < 5) { 106 throw new IllegalArgumentException("Bad directory for validity files"); 107 } 108 typeToStatusToCodes = CldrUtility.protectCollectionX(data); 109 typeToCodeToStatus = CldrUtility.protectCollectionX(codeToStatus); 110 } 111 112 /** 113 * 114 * @deprecated Use {@link #getStatusToCodes(LstrType)} 115 */ 116 @Deprecated getData()117 public Map<LstrType, Map<Status, Set<String>>> getData() { 118 return typeToStatusToCodes; 119 } 120 getStatusToCodes(LstrType type)121 public Map<Status, Set<String>> getStatusToCodes(LstrType type) { 122 return typeToStatusToCodes.get(type); 123 } 124 getCodeToStatus(LstrType type)125 public Map<String, Status> getCodeToStatus(LstrType type) { 126 return typeToCodeToStatus.get(type); 127 } 128 } 129