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<R2<String, String>, R3<Set<String>, String, String>>(); 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<String, Map<DtdType, ElementAttributeInfo>>(); // 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<DtdType, ElementAttributeInfo>(); 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 attributeDecl(String eName, String aName, String type, String mode, String value)143 public void attributeDecl(String eName, String aName, String type, String mode, String value) 144 throws SAXException { 145 if (SHOW) 146 System.out.println(myData.getDtdType() + "\tAttributeDecl\t" + eName + "\t" + aName + "\t" + type 147 + "\t" + mode + "\t" + value); 148 R2<String, String> key = Row.of(eName, aName); 149 Set<String> typeSet = getIdentifiers(type); 150 R3<Set<String>, String, String> value2 = Row.of(typeSet, mode, value); 151 R3<Set<String>, String, String> oldValue = myData.getElementAttribute2Data().get(key); 152 if (oldValue != null && !oldValue.equals(value2)) { 153 throw new IllegalArgumentException("Conflict in data: " + key + "\told: " + oldValue + "\tnew: " 154 + value2); 155 } 156 myData.getElementAttribute2Data().put(key, value2); 157 myData.getElement2Attributes().put(eName, aName); 158 } 159 getIdentifiers(String type)160 private Set<String> getIdentifiers(String type) { 161 Set<String> result = new LinkedHashSet<String>(); 162 idmatcher.reset(type); 163 while (idmatcher.find()) { 164 result.add(idmatcher.group()); 165 } 166 if (result.size() == 0) { 167 throw new IllegalArgumentException("No identifiers found in: " + type); 168 } 169 return result; 170 } 171 elementDecl(String name, String model)172 public void elementDecl(String name, String model) throws SAXException { 173 if (SHOW) System.out.println(myData.getDtdType() + "\tElement\t" + name + "\t" + model); 174 Set<String> identifiers = getIdentifiers(model); 175 // identifiers.remove("special"); 176 // identifiers.remove("alias"); 177 if (identifiers.size() == 0) { 178 identifiers.add("EMPTY"); 179 } 180 myData.getElement2Children().putAll(name, identifiers); 181 for (String identifier : identifiers) { 182 myData.getElement2Parents().put(identifier, name); 183 } 184 } 185 externalEntityDecl(String name, String publicId, String systemId)186 public void externalEntityDecl(String name, String publicId, String systemId) throws SAXException { 187 // TODO Auto-generated method stub 188 189 } 190 internalEntityDecl(String name, String value)191 public void internalEntityDecl(String name, String value) throws SAXException { 192 // TODO Auto-generated method stub 193 194 } 195 } 196 } 197