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 File validityDir = new File(basePath); 55 if (!validityDir.isDirectory()) { 56 throw new IllegalArgumentException("Not a directory: " + validityDir.getAbsolutePath()); 57 } 58 for (String file : validityDir.list()) { 59 if (!file.endsWith(".xml")) { 60 continue; 61 } 62 LstrType type = null; 63 try { 64 type = LstrType.fromString(file.substring(0, file.length() - 4)); 65 } catch (Exception e) { 66 continue; 67 } 68 List<Pair<String, String>> lineData = new ArrayList<>(); 69 Map<Status, Set<String>> submap = data.get(type); 70 if (submap == null) { 71 data.put(type, submap = new EnumMap<>(Status.class)); 72 } 73 Map<String, Status> subCodeToStatus = codeToStatus.get(type); 74 if (subCodeToStatus == null) { 75 codeToStatus.put(type, subCodeToStatus = new TreeMap<>()); 76 } 77 78 XMLFileReader.loadPathValues(basePath + file, lineData, true); 79 for (Pair<String, String> item : lineData) { 80 XPathParts parts = XPathParts.getFrozenInstance(item.getFirst()); 81 if (!"id".equals(parts.getElement(-1))) { 82 continue; 83 } 84 LstrType typeAttr = LstrType.fromString(parts.getAttributeValue(-1, "type")); 85 if (typeAttr != type) { 86 throw new IllegalArgumentException("Corrupt value for " + type); 87 } 88 Status subtypeAttr = Status.valueOf(parts.getAttributeValue(-1, "idStatus")); 89 Set<String> set = submap.get(subtypeAttr); 90 if (set == null) { 91 submap.put(subtypeAttr, set = new LinkedHashSet<>()); 92 } 93 for (String value : space.split(item.getSecond())) { 94 if (type == LstrType.subdivision) { 95 value = value.toLowerCase(Locale.ROOT).replace("-", ""); 96 } 97 int dashPos = value.indexOf('~'); 98 if (dashPos < 0) { 99 set.add(value); 100 } else { 101 StringRange.expand(value.substring(0, dashPos), value.substring(dashPos + 1), set); 102 } 103 } 104 for (String code : set) { 105 subCodeToStatus.put(code, subtypeAttr); 106 } 107 } 108 } 109 if (data.keySet().size() < 5) { 110 throw new IllegalArgumentException("Bad directory for validity files: " + validityDir.getAbsolutePath()); 111 } 112 typeToStatusToCodes = CldrUtility.protectCollectionX(data); 113 typeToCodeToStatus = CldrUtility.protectCollectionX(codeToStatus); 114 } 115 116 /** 117 * 118 * @deprecated Use {@link #getStatusToCodes(LstrType)} 119 */ 120 @Deprecated getData()121 public Map<LstrType, Map<Status, Set<String>>> getData() { 122 return typeToStatusToCodes; 123 } 124 getStatusToCodes(LstrType type)125 public Map<Status, Set<String>> getStatusToCodes(LstrType type) { 126 return typeToStatusToCodes.get(type); 127 } 128 getCodeToStatus(LstrType type)129 public Map<String, Status> getCodeToStatus(LstrType type) { 130 return typeToCodeToStatus.get(type); 131 } 132 } 133