xref: /aosp_15_r20/external/cldr/tools/cldr-code/src/main/java/org/unicode/cldr/util/FindDTDOrder.java (revision 912701f9769bb47905792267661f0baf2b85bed5)
1 package org.unicode.cldr.util;
2 
3 import com.ibm.icu.impl.Relation;
4 import com.ibm.icu.text.UTF16;
5 import java.io.BufferedReader;
6 import java.io.File;
7 import java.io.FileInputStream;
8 import java.io.IOException;
9 import java.io.InputStreamReader;
10 import java.io.PrintWriter;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.LinkedHashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.TreeMap;
22 import java.util.TreeSet;
23 import org.unicode.cldr.draft.FileUtilities;
24 import org.xml.sax.Attributes;
25 import org.xml.sax.ContentHandler;
26 import org.xml.sax.ErrorHandler;
27 import org.xml.sax.InputSource;
28 import org.xml.sax.Locator;
29 import org.xml.sax.SAXException;
30 import org.xml.sax.SAXParseException;
31 import org.xml.sax.XMLReader;
32 import org.xml.sax.ext.DeclHandler;
33 
34 /**
35  * @deprecated
36  */
37 @Deprecated
38 public class FindDTDOrder implements DeclHandler, ContentHandler, ErrorHandler {
39     static final boolean SHOW_PROGRESS = CldrUtility.getProperty("verbose", false);
40     static final boolean SHOW_ALL = CldrUtility.getProperty("show_all", false);
41     private static final boolean DEBUG = false;
42 
43     private static FindDTDOrder INSTANCE;
44 
45     private boolean recordingAttributeElements;
46 
main(String[] args)47     public static void main(String[] args) throws IOException {
48         System.out.println("Outdated, no longer used");
49         FindDTDOrder me = getInstance();
50         me.showData();
51     }
52 
getInstance()53     public static FindDTDOrder getInstance() {
54         synchronized (FindDTDOrder.class) {
55             if (INSTANCE == null) {
56                 try {
57                     FindDTDOrder me = new FindDTDOrder();
58                     XMLReader xmlReader = CLDRFile.createXMLReader(true);
59                     xmlReader.setContentHandler(me);
60                     xmlReader.setErrorHandler(me);
61                     xmlReader.setProperty("http://xml.org/sax/properties/declaration-handler", me);
62 
63                     FileInputStream fis;
64                     InputSource is;
65                     me.recordingAttributeElements = true;
66                     String filename = CLDRPaths.MAIN_DIRECTORY + "/root.xml";
67                     File file = new File(filename);
68                     if (DEBUG) {
69                         System.out.println("Opening " + file.getCanonicalFile());
70                     }
71                     File dtd =
72                             new File(
73                                     PathUtilities.getNormalizedPathString(file)
74                                             + "/../"
75                                             + "../../common/dtd/ldml.dtd");
76                     if (DEBUG) {
77                         System.out.println("Opening " + dtd.getCanonicalFile());
78                     }
79 
80                     fis = new FileInputStream(filename);
81                     if (DEBUG) {
82                         BufferedReader b = new BufferedReader(new InputStreamReader(fis));
83                         for (int i = 0; i < 30; ++i) {
84                             String line = b.readLine();
85                             System.out.println(line);
86                         }
87                         throw new IllegalArgumentException("just testing");
88                     }
89                     is = new InputSource(fis);
90                     is.setSystemId(PathUtilities.getNormalizedPathString(file) + "/../");
91                     xmlReader.parse(is);
92                     fis.close();
93 
94                     me.recordingAttributeElements = false;
95                     filename = CLDRPaths.DEFAULT_SUPPLEMENTAL_DIRECTORY + "/supplementalData.xml";
96                     File file2 = new File(filename);
97                     if (DEBUG) {
98                         System.out.println("Opening " + file2.getCanonicalFile());
99                     }
100 
101                     fis = new FileInputStream(filename);
102                     is = new InputSource(fis);
103                     is.setSystemId(PathUtilities.getNormalizedPathString(file) + "/../");
104                     xmlReader.parse(is);
105                     fis.close();
106                     // Then Attributes
107                     List<String> rawDtdAttributeOrder =
108                             Collections.unmodifiableList(new ArrayList<>(me.attributeSet));
109                     List<String> cldrFileAttributeOrder = CLDRFile.getAttributeOrder();
110 
111                     LinkedHashSet<String> modifiedDtdOrder =
112                             new LinkedHashSet<>(cldrFileAttributeOrder);
113                     // add items, keeping the ordering stable
114                     modifiedDtdOrder.retainAll(
115                             rawDtdAttributeOrder); // remove any superfluous stuff
116                     modifiedDtdOrder.addAll(rawDtdAttributeOrder);
117 
118                     // certain stuff always goes at the end
119                     modifiedDtdOrder.removeAll(me.getCommonAttributes());
120                     modifiedDtdOrder.addAll(me.getCommonAttributes());
121 
122                     // now make a list for comparison
123                     List<String> dtdAttributeOrder = new ArrayList<>(modifiedDtdOrder);
124 
125                     // fix to and from
126                     dtdAttributeOrder.remove("from");
127                     dtdAttributeOrder.add(dtdAttributeOrder.indexOf("to"), "from");
128 
129                     me.attributeList = Collections.unmodifiableList(dtdAttributeOrder);
130                     me.checkData();
131                     me.orderingList = Collections.unmodifiableList(me.orderingList);
132 
133                     // me.writeAttributeElements();
134                     INSTANCE = me;
135                 } catch (Exception e) {
136                     throw (IllegalArgumentException) new IllegalArgumentException().initCause(e);
137                 }
138             }
139         }
140         return INSTANCE;
141     }
142 
writeAttributeElements()143     public void writeAttributeElements() {
144         System.out.println(
145                 CldrUtility.LINE_SEPARATOR
146                         + "======== Start Attributes to Elements (unblocked) "
147                         + CldrUtility.LINE_SEPARATOR);
148         for (String attribute : attributeToElements.keySet()) {
149             Set<String> filtered = new TreeSet<>();
150             for (String element : attributeToElements.getAll(attribute)) {
151                 if (!isBlocked(element)) {
152                     filtered.add(element);
153                 }
154             }
155             System.out.println(attribute + "\t" + CldrUtility.join(filtered, " "));
156         }
157         System.out.println(
158                 CldrUtility.LINE_SEPARATOR
159                         + "======== End Attributes to Elements"
160                         + CldrUtility.LINE_SEPARATOR);
161         System.out.println(
162                 CldrUtility.LINE_SEPARATOR
163                         + "======== Start Elements to Children (skipping alias, special)"
164                         + CldrUtility.LINE_SEPARATOR);
165         showElementTree("ldml", "", new HashSet<String>());
166         System.out.println(
167                 CldrUtility.LINE_SEPARATOR
168                         + "======== End Elements to Children"
169                         + CldrUtility.LINE_SEPARATOR);
170     }
171 
showElementTree(String element, String indent, HashSet<String> seenSoFar)172     private void showElementTree(String element, String indent, HashSet<String> seenSoFar) {
173         // skip blocking elements
174         if (isBlocked(element)) {
175             return;
176         }
177         Set<String> children = elementToChildren.getAll(element);
178         if (seenSoFar.contains(element)) {
179             System.out.println(
180                     indent
181                             + element
182                             + (children == null || children.size() == 0
183                                     ? ""
184                                     : "\t*dup*\t" + children));
185             return;
186         }
187         System.out.println(indent + element);
188         seenSoFar.add(element);
189         if (children != null) {
190             indent += "\t";
191             for (String child : children) {
192                 showElementTree(child, indent, seenSoFar);
193             }
194         }
195     }
196 
isBlocked(String element)197     private boolean isBlocked(String element) {
198         return isAncestorOf("supplementalData", element)
199                 || isAncestorOf("collation", element)
200                 || isAncestorOf("cldrTest", element)
201                 || isAncestorOf("transform", element);
202     }
203 
204     Relation<String, String> ancestorToDescendant = null;
205 
isAncestorOf(String possibleAncestor, String possibleDescendent)206     private boolean isAncestorOf(String possibleAncestor, String possibleDescendent) {
207         if (ancestorToDescendant == null) {
208             ancestorToDescendant = Relation.of(new TreeMap<String, Set<String>>(), TreeSet.class);
209             buildPairwiseRelations(new ArrayList<String>(), "ldml");
210         }
211         Set<String> possibleDescendents = ancestorToDescendant.getAll(possibleAncestor);
212         if (possibleDescendents == null) return false;
213         return possibleDescendents.contains(possibleDescendent);
214     }
215 
buildPairwiseRelations(List<String> parents, String current)216     private void buildPairwiseRelations(List<String> parents, String current) {
217         Set<String> children = elementToChildren.getAll(current);
218         if (children == null || children.size() == 0) return;
219 
220         // we make a new list, since otherwise the iteration fails in recursion (because of the
221         // modification)
222         // if this were performance-sensitive we'd do it differently
223         ArrayList<String> newParents = new ArrayList<>(parents);
224         newParents.add(current);
225 
226         for (String child : children) {
227             for (String ancestor : newParents) {
228                 ancestorToDescendant.put(ancestor, child);
229                 buildPairwiseRelations(newParents, child);
230             }
231         }
232     }
233 
234     PrintWriter log = null;
235 
236     Set elementOrderings = new LinkedHashSet(); // set of orderings
237 
238     Set<String> allDefinedElements = new LinkedHashSet<>();
239 
240     boolean showReason = false;
241 
242     Object DONE = new Object(); // marker
243 
244     Relation<String, String> elementToChildren =
245             Relation.of(new TreeMap<String, Set<String>>(), TreeSet.class);
246 
FindDTDOrder()247     FindDTDOrder() {
248         log = new PrintWriter(System.out);
249     }
250 
251     private List<String> orderingList = new ArrayList<>();
252 
checkData()253     public void checkData() {
254         // verify that the ordering is the consistent for all child elements
255         // do this by building an ordering from the lists.
256         // The first item has no greater item in any set. So find an item that is
257         // only first
258         MergeLists<String> mergeLists =
259                 new MergeLists<>(new TreeSet<>(new UTF16.StringComparator(true, false, 0)))
260                         .add(Arrays.asList("ldml"))
261                         .addAll(elementOrderings); //
262         List<String> result = mergeLists.merge();
263         Collection badOrder = MergeLists.hasConsistentOrderWithEachOf(result, elementOrderings);
264         if (badOrder != null) {
265             throw new IllegalArgumentException("Failed to find good order: " + badOrder);
266         }
267 
268         showReason = false;
269         orderingList.add("ldml");
270         if (SHOW_PROGRESS) {
271             log.println("SHOW_PROGRESS ");
272             for (Iterator it = elementOrderings.iterator(); it.hasNext(); ) {
273                 Object value = it.next();
274                 log.println(value);
275             }
276         }
277         while (true) {
278             Object first = getFirst();
279             if (first == DONE) break;
280             if (first != null) {
281                 // log.println("Adding:\t" + first);
282                 if (orderingList.contains(first)) {
283                     throw new IllegalArgumentException("Already present: " + first);
284                 }
285                 orderingList.add(first.toString());
286             } else {
287                 showReason = true;
288                 getFirst();
289                 if (SHOW_PROGRESS) log.println();
290                 if (SHOW_PROGRESS) log.println("Failed ordering. So far:");
291                 for (Iterator<String> it = orderingList.iterator(); it.hasNext(); )
292                     if (SHOW_PROGRESS) log.print("\t" + it.next());
293                 if (SHOW_PROGRESS) log.println();
294                 if (SHOW_PROGRESS) log.println("Items:");
295                 // for (Iterator it = element_childComparator.keySet().iterator();
296                 // it.hasNext();) showRow(it.next(), true);
297                 if (SHOW_PROGRESS) log.println();
298                 break;
299             }
300         }
301 
302         if (DEBUG) {
303             System.out.println("New code in CLDRFile:\n" + result);
304             System.out.println("Old code in CLDRFile:\n" + orderingList);
305         }
306         // System.out.println("New code2: " + CldrUtility.breakLines(CldrUtility.join(result, " "),
307         // sep,
308         // FIRST_LETTER_CHANGE.matcher(""), 80));
309 
310         Set<String> missing = new TreeSet<>(allDefinedElements);
311         missing.removeAll(orderingList);
312         orderingList.addAll(missing);
313 
314         attributeEquivalents = new XEquivalenceClass(null);
315         for (Iterator it = attribEquiv.keySet().iterator(); it.hasNext(); ) {
316             Object ename = it.next();
317             Set s = attribEquiv.get(ename);
318             Iterator it2 = s.iterator();
319             Object first = it2.next();
320             while (it2.hasNext()) {
321                 attributeEquivalents.add(first, it2.next(), ename);
322             }
323         }
324     }
325 
326     String sep = CldrUtility.LINE_SEPARATOR + "\t\t\t";
327 
showData()328     private void showData() throws IOException {
329 
330         // finish up
331         String oldAttributeOrder = breakLines(CLDRFile.getAttributeOrder());
332         log.println("Successful Ordering...");
333         log.println();
334         log.println("Old Attribute Ordering: ");
335         log.println(oldAttributeOrder);
336 
337         String newAttributeOrder = breakLines(attributeList);
338 
339         if (newAttributeOrder.equals(oldAttributeOrder)) {
340             log.println("*** New Attribute Ordering: <same>");
341             log.println("*** No changes required...");
342         } else {
343             log.println("*** New Attribute Ordering: ");
344             log.println(newAttributeOrder);
345             log.println("*** Replace in CLDRFile elementOrdering  & supplementalMetadata ***");
346         }
347 
348         log.println("Attribute Eq: ");
349         for (Iterator it = attributeEquivalents.getSamples().iterator(); it.hasNext(); ) {
350             log.println(
351                     "\t"
352                             + getJavaList(
353                                     new TreeSet(attributeEquivalents.getEquivalences(it.next()))));
354         }
355         if (SHOW_PROGRESS) {
356             for (Iterator it = attributeEquivalents.getEquivalenceSets().iterator();
357                     it.hasNext(); ) {
358                 Object last = null;
359                 Set s = (Set) it.next();
360                 for (Iterator it2 = s.iterator(); it2.hasNext(); ) {
361                     Object temp = it2.next();
362                     if (last != null)
363                         log.println(
364                                 last
365                                         + " ~ "
366                                         + temp
367                                         + "\t"
368                                         + attributeEquivalents.getReasons(last, temp));
369                     last = temp;
370                 }
371                 log.println();
372             }
373         }
374 
375         String oldOrder = getJavaList(CLDRFile.getElementOrder());
376         log.println("Old Element Ordering:\n" + oldOrder);
377 
378         String newOrder = '"' + breakLines(orderingList) + '"';
379         if (newOrder.equals(oldOrder)) {
380             log.println("*** New Element Ordering: <same>");
381             log.println("*** No changes required...");
382         } else {
383             log.println("*** New Element Ordering:\n" + newOrder);
384             log.println("*** Replace in CLDRFile elementOrdering  & supplementalMetadata ***");
385         }
386 
387         if (SHOW_ALL) {
388             log.println("Old Size: " + CLDRFile.getElementOrder().size());
389             Set temp = new HashSet(CLDRFile.getElementOrder());
390             temp.removeAll(orderingList);
391             log.println("Old - New: " + temp);
392             log.println("New Size: " + orderingList.size());
393             temp = new HashSet(orderingList);
394             temp.removeAll(CLDRFile.getElementOrder());
395             log.println("New - Old: " + temp);
396 
397             Differ differ = new Differ(200, 1);
398             Iterator oldIt = CLDRFile.getElementOrder().iterator();
399             Iterator newIt = orderingList.iterator();
400             while (oldIt.hasNext() || newIt.hasNext()) {
401                 if (oldIt.hasNext()) differ.addA(oldIt.next());
402                 if (newIt.hasNext()) differ.addB(newIt.next());
403                 differ.checkMatch(!oldIt.hasNext() && !newIt.hasNext());
404 
405                 if (differ.getACount() != 0 || differ.getBCount() != 0) {
406                     log.println("Same: " + differ.getA(-1));
407                     for (int i = 0; i < differ.getACount(); ++i) {
408                         log.println("\tOld: " + differ.getA(i));
409                     }
410                     for (int i = 0; i < differ.getBCount(); ++i) {
411                         log.println("\t\tNew: " + differ.getB(i));
412                     }
413                     log.println("Same: " + differ.getA(differ.getACount()));
414                 }
415             }
416             log.println("Done with differences");
417         }
418 
419         log.flush();
420 
421         writeNewSupplemental(
422                 CLDRPaths.SUPPLEMENTAL_DIRECTORY,
423                 "supplementalMetadata.xml",
424                 "<attributeOrder>",
425                 "</attributeOrder>",
426                 "<elementOrder>",
427                 "</elementOrder>",
428                 "\t\t\t",
429                 CldrUtility.LINE_SEPARATOR + "\t\t");
430         writeNewSupplemental(
431                 CLDRPaths.BASE_DIRECTORY + "/tools/cldr-code/src/main/java/org/unicode/cldr/util/",
432                 "CLDRFile.java",
433                 "// START MECHANICALLY attributeOrdering GENERATED BY FindDTDOrder",
434                 "// END MECHANICALLY attributeOrdering GENERATED BY FindDTDOrder",
435                 "// START MECHANICALLY elementOrdering GENERATED BY FindDTDOrder",
436                 "// END MECHANICALLY elementOrdering GENERATED BY FindDTDOrder",
437                 "\t\t\t\t\t\"",
438                 '"' + CldrUtility.LINE_SEPARATOR + "\t\t\t\t\t");
439     }
440 
writeNewSupplemental( String dir, String filename, String startAttributeTag, String endAttributeTag, String startElementTag, String endElementTag, String startSep, String endSep)441     private void writeNewSupplemental(
442             String dir,
443             String filename,
444             String startAttributeTag,
445             String endAttributeTag,
446             String startElementTag,
447             String endElementTag,
448             String startSep,
449             String endSep)
450             throws IOException {
451         BufferedReader oldFile = FileUtilities.openUTF8Reader(dir, filename);
452         Log.setLogNoBOM(CLDRPaths.GEN_DIRECTORY + "/DTDOrder/" + filename);
453 
454         // CldrUtility.copyUpTo(oldFile, PatternCache.get("\\s*" +
455         // startAttributeTag +
456         // "\\s*"), Log.getLog(), true);
457         // Log.println(startSep + breakLines(attributeSet) + endSep + endAttributeTag);
458         // CldrUtility.copyUpTo(oldFile, PatternCache.get("\\s*" +
459         // endAttributeTag +
460         // "\\s*"), null, true);
461 
462         CldrUtility.copyUpTo(
463                 oldFile, PatternCache.get("\\s*" + startElementTag + "\\s*"), Log.getLog(), true);
464         Log.println(startSep + breakLines(orderingList) + endSep + endElementTag);
465         CldrUtility.copyUpTo(
466                 oldFile, PatternCache.get("\\s*" + endElementTag + "\\s*"), null, true);
467 
468         CldrUtility.copyUpTo(oldFile, null, Log.getLog(), false); // copy to end
469 
470         Log.close();
471         oldFile.close();
472     }
473 
breakLines(Collection orderingList)474     private String breakLines(Collection orderingList) {
475         final String joined = CldrUtility.join(orderingList, " ");
476         return joined; // return Utility.breakLines(joined, sep, FIRST_LETTER_CHANGE.matcher(""),
477         // 80);
478     }
479 
getJavaList(Collection orderingList)480     private String getJavaList(Collection orderingList) {
481         boolean first2 = true;
482         StringBuffer result = new StringBuffer();
483         result.append('"');
484         for (Iterator it = orderingList.iterator(); it.hasNext(); ) {
485             if (first2) first2 = false;
486             else result.append(" ");
487             result.append(it.next().toString());
488         }
489         result.append('"');
490         return result.toString();
491     }
492 
493     /**
494      * @param parent
495      * @param skipEmpty TODO
496      */
497     // private void showRow(Object parent, boolean skipEmpty) {
498     // List items = (List) element_childComparator.get(parent);
499     // if (skipEmpty && items.size() == 0) return;
500     // if (SHOW_PROGRESS) log.print(parent);
501     // for (Iterator it2 = items.iterator(); it2.hasNext();) if (SHOW_PROGRESS)
502     // log.print("\t" + it2.next());
503     // if (SHOW_PROGRESS) log.println();
504     // }
505     /**
506      * @param orderingList
507      */
getFirst()508     private Object getFirst() {
509         Set firstItems = new TreeSet();
510         Set nonFirstItems = new TreeSet();
511         for (Iterator it = elementOrderings.iterator(); it.hasNext(); ) {
512             List list = (List) it.next();
513             if (list.size() != 0) {
514                 firstItems.add(list.get(0));
515                 for (int i = 1; i < list.size(); ++i) {
516                     nonFirstItems.add(list.get(i));
517                 }
518             }
519         }
520         if (firstItems.size() == 0 && nonFirstItems.size() == 0) return DONE;
521         firstItems.removeAll(nonFirstItems);
522         if (firstItems.size() == 0) return null; // failure
523         Object result = firstItems.iterator().next();
524         removeEverywhere(result);
525         return result;
526     }
527 
528     /**
529      * @param possibleFirst
530      */
removeEverywhere(Object possibleFirst)531     private void removeEverywhere(Object possibleFirst) {
532         // and remove from all the lists
533         for (Iterator it2 = elementOrderings.iterator(); it2.hasNext(); ) {
534             List list2 = (List) it2.next();
535             if (SHOW_PROGRESS && list2.contains(possibleFirst)) {
536                 log.println("Removing " + possibleFirst + " from " + list2);
537             }
538             while (list2.remove(possibleFirst))
539                 ; // repeat until returns false
540         }
541     }
542 
543     // private boolean isNeverNotFirst(Object possibleFirst) {
544     // if (showReason) if (SHOW_PROGRESS) log.println("Trying: " + possibleFirst);
545     // for (Iterator it2 = element_childComparator.keySet().iterator();
546     // it2.hasNext();) {
547     // Object key = it2.next();
548     // List list2 = (List) element_childComparator.get(key);
549     // int pos = list2.indexOf(possibleFirst);
550     // if (pos > 0) {
551     // if (showReason) {
552     // if (SHOW_PROGRESS) log.print("Failed at:\t");
553     // showRow(key, false);
554     // }
555     // return false;
556     // }
557     // }
558     // return true;
559     // }
560 
561     static final Set<String> ELEMENT_SKIP_LIST =
562             new HashSet<>(
563                     Arrays.asList(
564                             new String[] {
565                                 "collation",
566                                 "base",
567                                 "settings",
568                                 "suppress_contractions",
569                                 "optimize",
570                                 "rules",
571                                 "reset",
572                                 "context",
573                                 "p",
574                                 "pc",
575                                 "s",
576                                 "sc",
577                                 "t",
578                                 "tc",
579                                 "i",
580                                 "ic",
581                                 "extend",
582                                 "x"
583                             }));
584 
585     static final Set<String> SUBELEMENT_SKIP_LIST =
586             new HashSet<>(Arrays.asList(new String[] {"PCDATA", "EMPTY", "ANY"}));
587 
588     // refine later; right now, doesn't handle multiple elements well.
589     @Override
elementDecl(String name, String model)590     public void elementDecl(String name, String model) throws SAXException {
591         // if (ELEMENT_SKIP_LIST.contains(name)) return;
592         if (name.indexOf("contractions") >= 0
593                 || model.indexOf(
594                                 "[alias, base, settings, suppress, contractions, optimize, rules, special]")
595                         >= 0) {}
596         allDefinedElements.add(name);
597         if (SHOW_PROGRESS) {
598             log.println("Element\t" + name + "\t" + model);
599         }
600         String[] list = model.split("[^-_A-Z0-9a-z]+");
601         List<String> mc = new ArrayList<>();
602         /*
603          * if (name.equals("currency")) { mc.add("alias"); mc.add("symbol");
604          * mc.add("pattern"); }
605          */
606         for (int i = 0; i < list.length; ++i) {
607             if (list[i].length() == 0) continue;
608             if (list[i].equals("ANY") && !name.equals("special")) {
609                 System.err.println("WARNING- SHOULD NOT HAVE 'ANY': " + name + "\t" + model);
610             }
611             if (SUBELEMENT_SKIP_LIST.contains(list[i])) continue;
612             // if (SHOW_PROGRESS) log.print("\t" + list[i]);
613             if (mc.contains(list[i])) {
614                 if (name.equals("currency") && list[i].equals("displayName")
615                         || list[i].equals("symbol")
616                         || list[i].equals("pattern")) {
617                     // do nothing, exception
618                 } else if (name.equals("rules")
619                         && (list[i].equals("reset") || list[i].equals("import"))) {
620                     // do nothing, exception
621                 } else {
622                     throw new IllegalArgumentException(
623                             "Duplicate element in definition of  "
624                                     + name
625                                     + ":\t"
626                                     + list[i]
627                                     + ":\t"
628                                     + Arrays.asList(list)
629                                     + ":\t"
630                                     + mc);
631                 }
632             } else {
633                 mc.add(list[i]);
634             }
635         }
636         if (recordingAttributeElements) {
637             Set<String> children = new TreeSet<>(mc);
638             children.remove("alias");
639             children.remove("special");
640             children.remove("cp");
641             elementToChildren.putAll(name, children);
642         }
643         allDefinedElements.addAll(mc);
644 
645         if (mc.size() < 1) {
646             if (SHOW_PROGRESS) {
647                 log.println("\tSKIPPING\t" + name + "\t" + mc);
648             }
649         } else {
650             if (SHOW_PROGRESS) {
651                 log.println("\t" + name + "\t" + mc);
652             }
653             elementOrderings.add(mc);
654         }
655 
656         // if (SHOW_PROGRESS) log.println();
657     }
658 
659     Set<String> skipCommon =
660             new LinkedHashSet<>(
661                     Arrays.asList(
662                             new String[] {
663                                 "validSubLocales", "standard", "references", "alt", "draft",
664                             }));
665 
666     Set<String> attributeSet = new TreeSet<>();
667 
668     {
669         attributeSet.add("_q");
670         attributeSet.addAll(skipCommon);
671     }
672 
673     List<String> attributeList;
674 
675     Map<String, Set<String>> attribEquiv = new TreeMap<>();
676 
677     Relation<String, String> attributeToElements =
678             Relation.of(new TreeMap<String, Set<String>>(), TreeSet.class);
679     private XEquivalenceClass attributeEquivalents;
680 
681     @Override
attributeDecl(String eName, String aName, String type, String mode, String value)682     public void attributeDecl(String eName, String aName, String type, String mode, String value)
683             throws SAXException {
684         if (SHOW_ALL) log.println("attributeDecl");
685         // if (SHOW_ALL) log.println("Attribute\t" + eName + "\t" +
686         // aName + "\t" + type + "\t" + mode + "\t" + value);
687         if (SHOW_PROGRESS)
688             System.out.println(
689                     "Attribute\t"
690                             + eName
691                             + "\t"
692                             + aName
693                             + "\t"
694                             + type
695                             + "\t"
696                             + mode
697                             + "\t"
698                             + value);
699         if (!skipCommon.contains(aName)) {
700             attributeSet.add(aName);
701             Set<String> l = attribEquiv.get(eName);
702             if (l == null) attribEquiv.put(eName, l = new TreeSet<>());
703             l.add(aName);
704         }
705         if (recordingAttributeElements) {
706             attributeToElements.put(aName, eName);
707         }
708     }
709 
710     @Override
internalEntityDecl(String name, String value)711     public void internalEntityDecl(String name, String value) throws SAXException {
712         if (SHOW_ALL) log.println("internalEntityDecl");
713         // if (SHOW_ALL) log.println("Internal Entity\t" + name +
714         // "\t" + value);
715     }
716 
717     @Override
externalEntityDecl(String name, String publicId, String systemId)718     public void externalEntityDecl(String name, String publicId, String systemId)
719             throws SAXException {
720         if (SHOW_ALL) log.println("externalEntityDecl");
721         // if (SHOW_ALL) log.println("Internal Entity\t" + name +
722         // "\t" + publicId + "\t" + systemId);
723     }
724 
725     /*
726      * (non-Javadoc)
727      *
728      * @see org.xml.sax.ContentHandler#endDocument()
729      */
730     @Override
endDocument()731     public void endDocument() throws SAXException {
732         if (SHOW_ALL) log.println("endDocument");
733     }
734 
735     /*
736      * (non-Javadoc)
737      *
738      * @see org.xml.sax.ContentHandler#startDocument()
739      */
740     @Override
startDocument()741     public void startDocument() throws SAXException {
742         if (SHOW_ALL) log.println("startDocument");
743     }
744 
745     /*
746      * (non-Javadoc)
747      *
748      * @see org.xml.sax.ContentHandler#characters(char[], int, int)
749      */
750     @Override
characters(char[] ch, int start, int length)751     public void characters(char[] ch, int start, int length) throws SAXException {
752         if (SHOW_ALL) log.println("characters");
753     }
754 
755     /*
756      * (non-Javadoc)
757      *
758      * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
759      */
760     @Override
ignorableWhitespace(char[] ch, int start, int length)761     public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
762         if (SHOW_ALL) log.println("ignorableWhitespace");
763     }
764 
765     /*
766      * (non-Javadoc)
767      *
768      * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
769      */
770     @Override
endPrefixMapping(String prefix)771     public void endPrefixMapping(String prefix) throws SAXException {
772         if (SHOW_ALL) log.println("endPrefixMapping");
773     }
774 
775     /*
776      * (non-Javadoc)
777      *
778      * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
779      */
780     @Override
skippedEntity(String name)781     public void skippedEntity(String name) throws SAXException {
782         if (SHOW_ALL) log.println("skippedEntity");
783     }
784 
785     /*
786      * (non-Javadoc)
787      *
788      * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
789      */
790     @Override
setDocumentLocator(Locator locator)791     public void setDocumentLocator(Locator locator) {
792         if (SHOW_ALL) log.println("setDocumentLocator");
793     }
794 
795     /*
796      * (non-Javadoc)
797      *
798      * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String,
799      * java.lang.String)
800      */
801     @Override
processingInstruction(String target, String data)802     public void processingInstruction(String target, String data) throws SAXException {
803         if (SHOW_ALL) log.println("processingInstruction");
804     }
805 
806     /*
807      * (non-Javadoc)
808      *
809      * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String,
810      * java.lang.String)
811      */
812     @Override
startPrefixMapping(String prefix, String uri)813     public void startPrefixMapping(String prefix, String uri) throws SAXException {
814         if (SHOW_ALL) log.println("startPrefixMapping");
815     }
816 
817     /*
818      * (non-Javadoc)
819      *
820      * @see org.xml.sax.ContentHandler#endElement(java.lang.String,
821      * java.lang.String, java.lang.String)
822      */
823     @Override
endElement(String namespaceURI, String localName, String qName)824     public void endElement(String namespaceURI, String localName, String qName)
825             throws SAXException {
826         if (SHOW_ALL) log.println("endElement");
827     }
828 
829     /*
830      * (non-Javadoc)
831      *
832      * @see org.xml.sax.ContentHandler#startElement(java.lang.String,
833      * java.lang.String, java.lang.String, org.xml.sax.Attributes)
834      */
835     @Override
startElement(String namespaceURI, String localName, String qName, Attributes atts)836     public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
837             throws SAXException {
838         if (SHOW_ALL) log.println("startElement");
839     }
840 
841     /*
842      * (non-Javadoc)
843      *
844      * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
845      */
846     @Override
error(SAXParseException exception)847     public void error(SAXParseException exception) throws SAXException {
848         if (SHOW_ALL) log.println("error");
849         throw exception;
850     }
851 
852     /*
853      * (non-Javadoc)
854      *
855      * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
856      */
857     @Override
fatalError(SAXParseException exception)858     public void fatalError(SAXParseException exception) throws SAXException {
859         if (SHOW_ALL) log.println("fatalError");
860         throw exception;
861     }
862 
863     /*
864      * (non-Javadoc)
865      *
866      * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
867      */
868     @Override
warning(SAXParseException exception)869     public void warning(SAXParseException exception) throws SAXException {
870         if (SHOW_ALL) log.println("warning");
871         throw exception;
872     }
873 
getAttributeOrder()874     public List<String> getAttributeOrder() {
875         return attributeList;
876     }
877 
getElementOrder()878     public List<String> getElementOrder() {
879         return orderingList;
880     }
881 
getCommonAttributes()882     public Set<String> getCommonAttributes() {
883         return skipCommon;
884     }
885 }
886