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