• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.util;
2 
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.IOException;
6 import java.util.Collections;
7 import java.util.HashMap;
8 import java.util.LinkedHashMap;
9 import java.util.LinkedHashSet;
10 import java.util.Map;
11 import java.util.Set;
12 import java.util.TreeMap;
13 import java.util.regex.Matcher;
14 
15 import org.xml.sax.InputSource;
16 import org.xml.sax.SAXException;
17 import org.xml.sax.XMLReader;
18 import org.xml.sax.ext.DeclHandler;
19 
20 import com.ibm.icu.impl.Relation;
21 import com.ibm.icu.impl.Row;
22 import com.ibm.icu.impl.Row.R2;
23 import com.ibm.icu.impl.Row.R3;
24 import com.ibm.icu.util.ICUUncheckedIOException;
25 
26 public class ElementAttributeInfo {
27 
28     private DtdType dtdType;
29     private Map<R2<String, String>, R3<Set<String>, String, String>> elementAttribute2Data = new TreeMap<>();
30     private Relation<String, String> element2children = Relation.of(new LinkedHashMap<String, Set<String>>(), LinkedHashSet.class);
31     private Relation<String, String> element2parents = Relation.of(new LinkedHashMap<String, Set<String>>(), LinkedHashSet.class);
32     private Relation<String, String> element2attributes = Relation.of(new LinkedHashMap<String, Set<String>>(), LinkedHashSet.class);
33 
34     static Map<String, Map<DtdType, ElementAttributeInfo>> cache = new HashMap<>(); // new
35     // HashMap<DtdType,
36     // Data>();
37 
getInstance(DtdType dtdType)38     public static final ElementAttributeInfo getInstance(DtdType dtdType) {
39         return getInstance(CLDRPaths.COMMON_DIRECTORY, dtdType);
40     }
41 
getInstance(String commonDirectory, DtdType dtdType)42     public static final ElementAttributeInfo getInstance(String commonDirectory, DtdType dtdType) {
43         Map<DtdType, ElementAttributeInfo> result = cache.get(commonDirectory);
44         if (result == null) {
45             try {
46                 File file = new File(commonDirectory);
47                 String canonicalCommonDirectory;
48                 canonicalCommonDirectory = file.getCanonicalFile().toString();
49                 if (!commonDirectory.equals(canonicalCommonDirectory)) {
50                     result = cache.get(commonDirectory);
51                     if (result != null) {
52                         cache.put(commonDirectory, result);
53                     }
54                 }
55                 if (result == null) {
56                     result = new HashMap<>();
57                     // pick short files that are in repository
58                     result.put(DtdType.ldml, new ElementAttributeInfo(canonicalCommonDirectory + "/main/root.xml",
59                         DtdType.ldml));
60                     result.put(DtdType.supplementalData, new ElementAttributeInfo(canonicalCommonDirectory
61                         + "/supplemental/plurals.xml", DtdType.supplementalData));
62                     result.put(DtdType.ldmlBCP47, new ElementAttributeInfo(canonicalCommonDirectory
63                         + "/bcp47/calendar.xml", DtdType.ldmlBCP47));
64                     result.put(DtdType.keyboard, new ElementAttributeInfo(canonicalCommonDirectory
65                         + "/../keyboards/android/ar-t-k0-android.xml", DtdType.keyboard));
66                     result.put(DtdType.platform, new ElementAttributeInfo(canonicalCommonDirectory
67                         + "/../keyboards/android/_platform.xml", DtdType.keyboard));
68                     cache.put(commonDirectory, result);
69                     cache.put(canonicalCommonDirectory, result);
70                 }
71             } catch (IOException e) {
72                 throw new ICUUncheckedIOException(e);
73             }
74         }
75         return result.get(dtdType);
76     }
77 
78     // static {
79     // try {
80     // addFromDTD(CldrUtility.COMMON_DIRECTORY + "main/en.xml", DtdType.ldml);
81     // addFromDTD(CldrUtility.COMMON_DIRECTORY + "supplemental/characters.xml", DtdType.supplementalData);
82     // addFromDTD(CldrUtility.COMMON_DIRECTORY + "bcp47/calendar.xml", DtdType.ldmlBCP47);
83     // } catch (IOException e) {
84     // throw new IllegalArgumentException(e);
85     // }
86     // }
87 
ElementAttributeInfo(String filename, DtdType type)88     private ElementAttributeInfo(String filename, DtdType type) throws IOException {
89         // StringBufferInputStream fis = new StringBufferInputStream(
90         // "<!DOCTYPE ldml SYSTEM \"http://www.unicode.org/cldr/dtd/1.2/ldml.dtd\"><ldml></ldml>");
91         FileInputStream fis = new FileInputStream(filename);
92         try {
93             XMLReader xmlReader = CLDRFile.createXMLReader(true);
94             this.dtdType = type;
95             MyDeclHandler me = new MyDeclHandler(this);
96             xmlReader.setProperty("http://xml.org/sax/properties/declaration-handler", me);
97             InputSource is = new InputSource(fis);
98             is.setSystemId(filename);
99             // xmlReader.setContentHandler(me);
100             // xmlReader.setErrorHandler(me);
101             xmlReader.parse(is);
102             this.elementAttribute2Data = Collections.unmodifiableMap(getElementAttribute2Data()); // TODO, protect rows
103             getElement2Children().freeze();
104             getElement2Parents().freeze();
105             getElement2Attributes().freeze();
106         } catch (Exception e) {
107             e.printStackTrace();
108         } finally {
109             fis.close();
110         }
111     }
112 
getDtdType()113     private DtdType getDtdType() {
114         return dtdType;
115     }
116 
getElementAttribute2Data()117     public Map<R2<String, String>, R3<Set<String>, String, String>> getElementAttribute2Data() {
118         return elementAttribute2Data;
119     }
120 
getElement2Children()121     public Relation<String, String> getElement2Children() {
122         return element2children;
123     }
124 
getElement2Parents()125     public Relation<String, String> getElement2Parents() {
126         return element2parents;
127     }
128 
getElement2Attributes()129     public Relation<String, String> getElement2Attributes() {
130         return element2attributes;
131     }
132 
133     static class MyDeclHandler implements DeclHandler {
134         private static final boolean SHOW = false;
135         private ElementAttributeInfo myData;
136 
137         Matcher idmatcher = PatternCache.get("[a-zA-Z0-9][-_a-zA-Z0-9]*").matcher("");
138 
MyDeclHandler(ElementAttributeInfo indata)139         public MyDeclHandler(ElementAttributeInfo indata) {
140             myData = indata;
141         }
142 
143         @Override
attributeDecl(String eName, String aName, String type, String mode, String value)144         public void attributeDecl(String eName, String aName, String type, String mode, String value)
145             throws SAXException {
146             if (SHOW)
147                 System.out.println(myData.getDtdType() + "\tAttributeDecl\t" + eName + "\t" + aName + "\t" + type
148                     + "\t" + mode + "\t" + value);
149             R2<String, String> key = Row.of(eName, aName);
150             Set<String> typeSet = getIdentifiers(type);
151             R3<Set<String>, String, String> value2 = Row.of(typeSet, mode, value);
152             R3<Set<String>, String, String> oldValue = myData.getElementAttribute2Data().get(key);
153             if (oldValue != null && !oldValue.equals(value2)) {
154                 throw new IllegalArgumentException("Conflict in data: " + key + "\told: " + oldValue + "\tnew: "
155                     + value2);
156             }
157             myData.getElementAttribute2Data().put(key, value2);
158             myData.getElement2Attributes().put(eName, aName);
159         }
160 
getIdentifiers(String type)161         private Set<String> getIdentifiers(String type) {
162             Set<String> result = new LinkedHashSet<>();
163             idmatcher.reset(type);
164             while (idmatcher.find()) {
165                 result.add(idmatcher.group());
166             }
167             if (result.size() == 0) {
168                 throw new IllegalArgumentException("No identifiers found in: " + type);
169             }
170             return result;
171         }
172 
173         @Override
elementDecl(String name, String model)174         public void elementDecl(String name, String model) throws SAXException {
175             if (SHOW) System.out.println(myData.getDtdType() + "\tElement\t" + name + "\t" + model);
176             Set<String> identifiers = getIdentifiers(model);
177             // identifiers.remove("special");
178             // identifiers.remove("alias");
179             if (identifiers.size() == 0) {
180                 identifiers.add("EMPTY");
181             }
182             myData.getElement2Children().putAll(name, identifiers);
183             for (String identifier : identifiers) {
184                 myData.getElement2Parents().put(identifier, name);
185             }
186         }
187 
188         @Override
externalEntityDecl(String name, String publicId, String systemId)189         public void externalEntityDecl(String name, String publicId, String systemId) throws SAXException {
190             // TODO Auto-generated method stub
191 
192         }
193 
194         @Override
internalEntityDecl(String name, String value)195         public void internalEntityDecl(String name, String value) throws SAXException {
196             // TODO Auto-generated method stub
197 
198         }
199     }
200 }
201