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