xref: /aosp_15_r20/external/cldr/tools/cldr-code/src/main/java/org/unicode/cldr/util/ElementAttributeInfo.java (revision 912701f9769bb47905792267661f0baf2b85bed5)
1 package org.unicode.cldr.util;
2 
3 import com.ibm.icu.impl.Relation;
4 import com.ibm.icu.impl.Row;
5 import com.ibm.icu.impl.Row.R2;
6 import com.ibm.icu.impl.Row.R3;
7 import com.ibm.icu.util.ICUUncheckedIOException;
8 import java.io.File;
9 import java.io.FileInputStream;
10 import java.io.IOException;
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.LinkedHashMap;
14 import java.util.LinkedHashSet;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.TreeMap;
18 import java.util.regex.Matcher;
19 import org.xml.sax.InputSource;
20 import org.xml.sax.SAXException;
21 import org.xml.sax.XMLReader;
22 import org.xml.sax.ext.DeclHandler;
23 
24 public class ElementAttributeInfo {
25 
26     private DtdType dtdType;
27     private Map<R2<String, String>, R3<Set<String>, String, String>> elementAttribute2Data =
28             new TreeMap<>();
29     private Relation<String, String> element2children =
30             Relation.of(new LinkedHashMap<String, Set<String>>(), LinkedHashSet.class);
31     private Relation<String, String> element2parents =
32             Relation.of(new LinkedHashMap<String, Set<String>>(), LinkedHashSet.class);
33     private Relation<String, String> element2attributes =
34             Relation.of(new LinkedHashMap<String, Set<String>>(), LinkedHashSet.class);
35 
36     static Map<String, Map<DtdType, ElementAttributeInfo>> cache = new HashMap<>(); // new
37     // HashMap<DtdType,
38     // Data>();
39 
getInstance(DtdType dtdType)40     public static final ElementAttributeInfo getInstance(DtdType dtdType) {
41         return getInstance(CLDRPaths.COMMON_DIRECTORY, dtdType);
42     }
43 
getInstance(String commonDirectory, DtdType dtdType)44     public static final ElementAttributeInfo getInstance(String commonDirectory, DtdType dtdType) {
45         Map<DtdType, ElementAttributeInfo> result = cache.get(commonDirectory);
46         if (result == null) {
47             try {
48                 File file = new File(commonDirectory);
49                 String canonicalCommonDirectory;
50                 canonicalCommonDirectory = file.getCanonicalFile().toString();
51                 if (!commonDirectory.equals(canonicalCommonDirectory)) {
52                     result = cache.get(commonDirectory);
53                     if (result != null) {
54                         cache.put(commonDirectory, result);
55                     }
56                 }
57                 if (result == null) {
58                     result = makeElementAttributeInfoMap(canonicalCommonDirectory);
59                     cache.put(commonDirectory, result);
60                     cache.put(canonicalCommonDirectory, result);
61                 }
62             } catch (IOException e) {
63                 throw new ICUUncheckedIOException(e);
64             }
65         }
66         final ElementAttributeInfo eai = result.get(dtdType);
67         if (eai == null) {
68             throw new NullPointerException(
69                     "ElementAttributeInfo.getInstance(…,"
70                             + dtdType.name()
71                             + ") returns null, please update this function");
72         }
73         return eai;
74     }
75 
addElementAttributeInfo( Map<DtdType, ElementAttributeInfo> result, DtdType type, String path)76     private static void addElementAttributeInfo(
77             Map<DtdType, ElementAttributeInfo> result, DtdType type, String path)
78             throws IOException {
79         if (!new File(path).canRead()) {
80             System.err.println(
81                     "ElementAttributeInfo: Warning: Sample file did not exist: "
82                             + path
83                             + " for DtdType "
84                             + type.name());
85             return; // file doesn't exist.
86         }
87         result.put(type, new ElementAttributeInfo(path, type));
88     }
89 
makeElementAttributeInfoMap( String canonicalCommonDirectory)90     private static Map<DtdType, ElementAttributeInfo> makeElementAttributeInfoMap(
91             String canonicalCommonDirectory) throws IOException {
92         Map<DtdType, ElementAttributeInfo> result;
93         result = new HashMap<>();
94         // pick short files that are in repository
95         // Add to this when a DTD is added
96         addElementAttributeInfo(result, DtdType.ldml, canonicalCommonDirectory + "/main/root.xml");
97         addElementAttributeInfo(
98                 result,
99                 DtdType.supplementalData,
100                 canonicalCommonDirectory + "/supplemental/plurals.xml");
101         addElementAttributeInfo(
102                 result, DtdType.ldmlBCP47, canonicalCommonDirectory + "/bcp47/calendar.xml");
103         addElementAttributeInfo(
104                 result,
105                 DtdType.keyboard3,
106                 canonicalCommonDirectory + "/../keyboards/3.0/fr-t-k0-test.xml");
107         addElementAttributeInfo(
108                 result,
109                 DtdType.keyboardTest3,
110                 canonicalCommonDirectory + "/../keyboards/test/fr-t-k0-test-test.xml");
111         return result;
112     }
113 
114     // static {
115     // try {
116     // addFromDTD(CldrUtility.COMMON_DIRECTORY + "main/en.xml", DtdType.ldml);
117     // addFromDTD(CldrUtility.COMMON_DIRECTORY + "supplemental/characters.xml",
118     // DtdType.supplementalData);
119     // addFromDTD(CldrUtility.COMMON_DIRECTORY + "bcp47/calendar.xml", DtdType.ldmlBCP47);
120     // } catch (IOException e) {
121     // throw new IllegalArgumentException(e);
122     // }
123     // }
124 
ElementAttributeInfo(String filename, DtdType type)125     private ElementAttributeInfo(String filename, DtdType type) throws IOException {
126         // StringBufferInputStream fis = new StringBufferInputStream(
127         // "<!DOCTYPE ldml SYSTEM \"http://www.unicode.org/cldr/dtd/1.2/ldml.dtd\"><ldml></ldml>");
128         FileInputStream fis = new FileInputStream(filename);
129         try {
130             XMLReader xmlReader = CLDRFile.createXMLReader(true);
131             this.dtdType = type;
132             MyDeclHandler me = new MyDeclHandler(this);
133             xmlReader.setProperty("http://xml.org/sax/properties/declaration-handler", me);
134             InputSource is = new InputSource(fis);
135             is.setSystemId(filename);
136             // xmlReader.setContentHandler(me);
137             // xmlReader.setErrorHandler(me);
138             xmlReader.parse(DoctypeXmlStreamWrapper.wrap(is));
139             this.elementAttribute2Data =
140                     Collections.unmodifiableMap(getElementAttribute2Data()); // TODO, protect rows
141             getElement2Children().freeze();
142             getElement2Parents().freeze();
143             getElement2Attributes().freeze();
144         } catch (Exception e) {
145             // TODO: why is this being caught here?
146             e.printStackTrace();
147         } finally {
148             fis.close();
149         }
150     }
151 
getDtdType()152     private DtdType getDtdType() {
153         return dtdType;
154     }
155 
getElementAttribute2Data()156     public Map<R2<String, String>, R3<Set<String>, String, String>> getElementAttribute2Data() {
157         return elementAttribute2Data;
158     }
159 
getElement2Children()160     public Relation<String, String> getElement2Children() {
161         return element2children;
162     }
163 
getElement2Parents()164     public Relation<String, String> getElement2Parents() {
165         return element2parents;
166     }
167 
getElement2Attributes()168     public Relation<String, String> getElement2Attributes() {
169         return element2attributes;
170     }
171 
172     static class MyDeclHandler implements DeclHandler {
173         private static final boolean SHOW = false;
174         private ElementAttributeInfo myData;
175 
176         Matcher idmatcher = PatternCache.get("[a-zA-Z0-9][-_a-zA-Z0-9]*").matcher("");
177 
MyDeclHandler(ElementAttributeInfo indata)178         public MyDeclHandler(ElementAttributeInfo indata) {
179             myData = indata;
180         }
181 
182         @Override
attributeDecl( String eName, String aName, String type, String mode, String value)183         public void attributeDecl(
184                 String eName, String aName, String type, String mode, String value)
185                 throws SAXException {
186             if (SHOW)
187                 System.out.println(
188                         myData.getDtdType()
189                                 + "\tAttributeDecl\t"
190                                 + eName
191                                 + "\t"
192                                 + aName
193                                 + "\t"
194                                 + type
195                                 + "\t"
196                                 + mode
197                                 + "\t"
198                                 + value);
199             R2<String, String> key = Row.of(eName, aName);
200             Set<String> typeSet = getIdentifiers(type);
201             R3<Set<String>, String, String> value2 = Row.of(typeSet, mode, value);
202             R3<Set<String>, String, String> oldValue = myData.getElementAttribute2Data().get(key);
203             if (oldValue != null && !oldValue.equals(value2)) {
204                 throw new IllegalArgumentException(
205                         "Conflict in data: " + key + "\told: " + oldValue + "\tnew: " + value2);
206             }
207             myData.getElementAttribute2Data().put(key, value2);
208             myData.getElement2Attributes().put(eName, aName);
209         }
210 
getIdentifiers(String type)211         private Set<String> getIdentifiers(String type) {
212             Set<String> result = new LinkedHashSet<>();
213             idmatcher.reset(type);
214             while (idmatcher.find()) {
215                 result.add(idmatcher.group());
216             }
217             if (result.size() == 0) {
218                 throw new IllegalArgumentException("No identifiers found in: " + type);
219             }
220             return result;
221         }
222 
223         @Override
elementDecl(String name, String model)224         public void elementDecl(String name, String model) throws SAXException {
225             if (SHOW) System.out.println(myData.getDtdType() + "\tElement\t" + name + "\t" + model);
226             Set<String> identifiers = getIdentifiers(model);
227             // identifiers.remove("special");
228             // identifiers.remove("alias");
229             if (identifiers.size() == 0) {
230                 identifiers.add("EMPTY");
231             }
232             myData.getElement2Children().putAll(name, identifiers);
233             for (String identifier : identifiers) {
234                 myData.getElement2Parents().put(identifier, name);
235             }
236         }
237 
238         @Override
externalEntityDecl(String name, String publicId, String systemId)239         public void externalEntityDecl(String name, String publicId, String systemId)
240                 throws SAXException {
241             // TODO Auto-generated method stub
242 
243         }
244 
245         @Override
internalEntityDecl(String name, String value)246         public void internalEntityDecl(String name, String value) throws SAXException {
247             // TODO Auto-generated method stub
248 
249         }
250     }
251 }
252