package org.unicode.cldr.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.unicode.cldr.draft.FileUtilities;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.DeclHandler;

import com.ibm.icu.impl.Relation;
import com.ibm.icu.text.UTF16;

/**
 * @deprecated
 */
public class FindDTDOrder implements DeclHandler, ContentHandler, ErrorHandler {
    static final boolean SHOW_PROGRESS = CldrUtility.getProperty("verbose", false);
    static final boolean SHOW_ALL = CldrUtility.getProperty("show_all", false);
    private static final boolean DEBUG = false;

    private static FindDTDOrder INSTANCE;

    private boolean recordingAttributeElements;

    public static void main(String[] args) throws IOException {
        System.out.println("Outdated, no longer used");
        FindDTDOrder me = getInstance();
        me.showData();
    }

    public static FindDTDOrder getInstance() {
        synchronized (FindDTDOrder.class) {
            if (INSTANCE == null) {
                try {
                    FindDTDOrder me = new FindDTDOrder();
                    XMLReader xmlReader = CLDRFile.createXMLReader(true);
                    xmlReader.setContentHandler(me);
                    xmlReader.setErrorHandler(me);
                    xmlReader.setProperty(
                        "http://xml.org/sax/properties/declaration-handler", me);

                    FileInputStream fis;
                    InputSource is;
                    me.recordingAttributeElements = true;
                    String filename = CLDRPaths.MAIN_DIRECTORY + "/root.xml";
                    File file = new File(filename);
                    if (DEBUG) {
                        System.out.println("Opening " + file.getCanonicalFile());
                    }
                    File dtd = new File(file.getCanonicalPath() + "/../" + "../../common/dtd/ldml.dtd");
                    if (DEBUG) {
                        System.out.println("Opening " + dtd.getCanonicalFile());
                    }

                    fis = new FileInputStream(filename);
                    if (DEBUG) {
                        BufferedReader b = new BufferedReader(new InputStreamReader(fis));
                        for (int i = 0; i < 30; ++i) {
                            String line = b.readLine();
                            System.out.println(line);
                        }
                        throw new IllegalArgumentException("just testing");
                    }
                    is = new InputSource(fis);
                    is.setSystemId(file.getCanonicalPath() + "/../");
                    xmlReader.parse(is);
                    fis.close();

                    me.recordingAttributeElements = false;
                    filename = CLDRPaths.DEFAULT_SUPPLEMENTAL_DIRECTORY
                        + "/supplementalData.xml";
                    File file2 = new File(filename);
                    if (DEBUG) {
                        System.out.println("Opening " + file2.getCanonicalFile());
                    }

                    fis = new FileInputStream(filename);
                    is = new InputSource(fis);
                    is.setSystemId(file.getCanonicalPath() + "/../");
                    xmlReader.parse(is);
                    fis.close();
                    // Then Attributes
                    List<String> rawDtdAttributeOrder = Collections.unmodifiableList(new ArrayList<String>(me.attributeSet));
                    List<String> cldrFileAttributeOrder = CLDRFile.getAttributeOrder();

                    LinkedHashSet<String> modifiedDtdOrder = new LinkedHashSet<String>(cldrFileAttributeOrder);
                    // add items, keeping the ordering stable
                    modifiedDtdOrder.retainAll(rawDtdAttributeOrder); // remove any superfluous stuff
                    modifiedDtdOrder.addAll(rawDtdAttributeOrder);

                    // certain stuff always goes at the end
                    modifiedDtdOrder.removeAll(me.getCommonAttributes());
                    modifiedDtdOrder.addAll(me.getCommonAttributes());

                    // now make a list for comparison
                    List<String> dtdAttributeOrder = new ArrayList<String>(modifiedDtdOrder);

                    // fix to and from
                    dtdAttributeOrder.remove("from");
                    dtdAttributeOrder.add(dtdAttributeOrder.indexOf("to"), "from");

                    me.attributeList = Collections.unmodifiableList(dtdAttributeOrder);
                    me.checkData();
                    me.orderingList = Collections.unmodifiableList(me.orderingList);

                    // me.writeAttributeElements();
                    INSTANCE = me;
                } catch (Exception e) {
                    throw (IllegalArgumentException) new IllegalArgumentException().initCause(e);
                }
            }
        }
        return INSTANCE;
    }

    public void writeAttributeElements() {
        System.out.println(CldrUtility.LINE_SEPARATOR + "======== Start Attributes to Elements (unblocked) "
            + CldrUtility.LINE_SEPARATOR);
        for (String attribute : attributeToElements.keySet()) {
            Set<String> filtered = new TreeSet<String>();
            for (String element : attributeToElements.getAll(attribute)) {
                if (!isBlocked(element)) {
                    filtered.add(element);
                }
            }
            System.out.println(attribute + "\t" + CldrUtility.join(filtered, " "));
        }
        System.out.println(CldrUtility.LINE_SEPARATOR + "======== End Attributes to Elements"
            + CldrUtility.LINE_SEPARATOR);
        System.out.println(CldrUtility.LINE_SEPARATOR + "======== Start Elements to Children (skipping alias, special)"
            + CldrUtility.LINE_SEPARATOR);
        showElementTree("ldml", "", new HashSet<String>());
        System.out.println(CldrUtility.LINE_SEPARATOR + "======== End Elements to Children"
            + CldrUtility.LINE_SEPARATOR);
    }

    private void showElementTree(String element, String indent, HashSet<String> seenSoFar) {
        // skip blocking elements
        if (isBlocked(element)) {
            return;
        }
        Set<String> children = elementToChildren.getAll(element);
        if (seenSoFar.contains(element)) {
            System.out.println(indent + element
                + (children == null || children.size() == 0 ? "" : "\t*dup*\t" + children));
            return;
        }
        System.out.println(indent + element);
        seenSoFar.add(element);
        if (children != null) {
            indent += "\t";
            for (String child : children) {
                showElementTree(child, indent, seenSoFar);
            }
        }
    }

    private boolean isBlocked(String element) {
        return isAncestorOf("supplementalData", element)
            || isAncestorOf("collation", element)
            || isAncestorOf("cldrTest", element)
            || isAncestorOf("transform", element);
    }

    Relation<String, String> ancestorToDescendant = null;

    private boolean isAncestorOf(String possibleAncestor, String possibleDescendent) {
        if (ancestorToDescendant == null) {
            ancestorToDescendant = Relation.of(new TreeMap<String, Set<String>>(), TreeSet.class);
            buildPairwiseRelations(new ArrayList<String>(), "ldml");
        }
        Set<String> possibleDescendents = ancestorToDescendant.getAll(possibleAncestor);
        if (possibleDescendents == null) return false;
        return possibleDescendents.contains(possibleDescendent);
    }

    private void buildPairwiseRelations(List<String> parents, String current) {
        Set<String> children = elementToChildren.getAll(current);
        if (children == null || children.size() == 0) return;

        // we make a new list, since otherwise the iteration fails in recursion (because of the modification)
        // if this were performance-sensitive we'd do it differently
        ArrayList<String> newParents = new ArrayList<String>(parents);
        newParents.add(current);

        for (String child : children) {
            for (String ancestor : newParents) {
                ancestorToDescendant.put(ancestor, child);
                buildPairwiseRelations(newParents, child);
            }
        }
    }

    PrintWriter log = null;

    Set elementOrderings = new LinkedHashSet(); // set of orderings

    Set<String> allDefinedElements = new LinkedHashSet<String>();

    boolean showReason = false;

    Object DONE = new Object(); // marker

    Relation<String, String> elementToChildren = Relation.of(new TreeMap<String, Set<String>>(),
        TreeSet.class);

    FindDTDOrder() {
        log = new PrintWriter(System.out);
    }

    private List<String> orderingList = new ArrayList<String>();

    public void checkData() {
        // verify that the ordering is the consistent for all child elements
        // do this by building an ordering from the lists.
        // The first item has no greater item in any set. So find an item that is
        // only first
        MergeLists<String> mergeLists = new MergeLists<String>(new TreeSet<String>(new UTF16.StringComparator(true,
            false, 0)))
            .add(Arrays.asList("ldml"))
            .addAll(elementOrderings); //
        List<String> result = mergeLists.merge();
        Collection badOrder = MergeLists.hasConsistentOrderWithEachOf(result, elementOrderings);
        if (badOrder != null) {
            throw new IllegalArgumentException("Failed to find good order: " + badOrder);
        }

        showReason = false;
        orderingList.add("ldml");
        if (SHOW_PROGRESS) {
            log.println("SHOW_PROGRESS ");
            for (Iterator it = elementOrderings.iterator(); it.hasNext();) {
                Object value = it.next();
                log.println(value);
            }
        }
        while (true) {
            Object first = getFirst();
            if (first == DONE)
                break;
            if (first != null) {
                // log.println("Adding:\t" + first);
                if (orderingList.contains(first)) {
                    throw new IllegalArgumentException("Already present: " + first);
                }
                orderingList.add(first.toString());
            } else {
                showReason = true;
                getFirst();
                if (SHOW_PROGRESS)
                    log.println();
                if (SHOW_PROGRESS)
                    log.println("Failed ordering. So far:");
                for (Iterator<String> it = orderingList.iterator(); it.hasNext();)
                    if (SHOW_PROGRESS)
                        log.print("\t" + it.next());
                if (SHOW_PROGRESS)
                    log.println();
                if (SHOW_PROGRESS)
                    log.println("Items:");
                // for (Iterator it = element_childComparator.keySet().iterator();
                // it.hasNext();) showRow(it.next(), true);
                if (SHOW_PROGRESS)
                    log.println();
                break;
            }
        }

        if (DEBUG) {
            System.out.println("New code in CLDRFile:\n" + result);
            System.out.println("Old code in CLDRFile:\n" + orderingList);
        }
        // System.out.println("New code2: " + CldrUtility.breakLines(CldrUtility.join(result, " "), sep,
        // FIRST_LETTER_CHANGE.matcher(""), 80));

        Set<String> missing = new TreeSet<String>(allDefinedElements);
        missing.removeAll(orderingList);
        orderingList.addAll(missing);

        attributeEquivalents = new XEquivalenceClass(null);
        for (Iterator it = attribEquiv.keySet().iterator(); it.hasNext();) {
            Object ename = it.next();
            Set s = attribEquiv.get(ename);
            Iterator it2 = s.iterator();
            Object first = it2.next();
            while (it2.hasNext()) {
                attributeEquivalents.add(first, it2.next(), ename);
            }
        }

    }

    String sep = CldrUtility.LINE_SEPARATOR + "\t\t\t";

    private void showData() throws IOException {

        // finish up
        String oldAttributeOrder = breakLines(CLDRFile.getAttributeOrder());
        log.println("Successful Ordering...");
        log.println();
        log.println("Old Attribute Ordering: ");
        log.println(oldAttributeOrder);

        String newAttributeOrder = breakLines(attributeList);

        if (newAttributeOrder.equals(oldAttributeOrder)) {
            log.println("*** New Attribute Ordering: <same>");
            log.println("*** No changes required...");
        } else {
            log.println("*** New Attribute Ordering: ");
            log.println(newAttributeOrder);
            log.println("*** Replace in CLDRFile elementOrdering  & supplementalMetadata ***");
        }

        log.println("Attribute Eq: ");
        for (Iterator it = attributeEquivalents.getSamples().iterator(); it.hasNext();) {
            log.println("\t"
                + getJavaList(new TreeSet(attributeEquivalents.getEquivalences(it.next()))));
        }
        if (SHOW_PROGRESS) {
            for (Iterator it = attributeEquivalents.getEquivalenceSets().iterator(); it.hasNext();) {
                Object last = null;
                Set s = (Set) it.next();
                for (Iterator it2 = s.iterator(); it2.hasNext();) {
                    Object temp = it2.next();
                    if (last != null)
                        log.println(last + " ~ " + temp + "\t" + attributeEquivalents.getReasons(last, temp));
                    last = temp;
                }
                log.println();
            }
        }

        String oldOrder = getJavaList(CLDRFile.getElementOrder());
        log.println("Old Element Ordering:\n"
            + oldOrder);

        String newOrder = '"' + breakLines(orderingList) + '"';
        if (newOrder.equals(oldOrder)) {
            log.println("*** New Element Ordering: <same>");
            log.println("*** No changes required...");
        } else {
            log.println("*** New Element Ordering:\n" + newOrder);
            log.println("*** Replace in CLDRFile elementOrdering  & supplementalMetadata ***");
        }

        if (SHOW_ALL) {
            log.println("Old Size: " + CLDRFile.getElementOrder().size());
            Set temp = new HashSet(CLDRFile.getElementOrder());
            temp.removeAll(orderingList);
            log.println("Old - New: " + temp);
            log.println("New Size: " + orderingList.size());
            temp = new HashSet(orderingList);
            temp.removeAll(CLDRFile.getElementOrder());
            log.println("New - Old: " + temp);

            Differ differ = new Differ(200, 1);
            Iterator oldIt = CLDRFile.getElementOrder().iterator();
            Iterator newIt = orderingList.iterator();
            while (oldIt.hasNext() || newIt.hasNext()) {
                if (oldIt.hasNext())
                    differ.addA(oldIt.next());
                if (newIt.hasNext())
                    differ.addB(newIt.next());
                differ.checkMatch(!oldIt.hasNext() && !newIt.hasNext());

                if (differ.getACount() != 0 || differ.getBCount() != 0) {
                    log.println("Same: " + differ.getA(-1));
                    for (int i = 0; i < differ.getACount(); ++i) {
                        log.println("\tOld: " + differ.getA(i));
                    }
                    for (int i = 0; i < differ.getBCount(); ++i) {
                        log.println("\t\tNew: " + differ.getB(i));
                    }
                    log.println("Same: " + differ.getA(differ.getACount()));
                }
            }
            log.println("Done with differences");
        }

        log.flush();

        writeNewSupplemental(CLDRPaths.SUPPLEMENTAL_DIRECTORY, "supplementalMetadata.xml",
            "<attributeOrder>", "</attributeOrder>",
            "<elementOrder>", "</elementOrder>", "\t\t\t", CldrUtility.LINE_SEPARATOR + "\t\t");
        writeNewSupplemental(CLDRPaths.BASE_DIRECTORY + "/tools/java/org/unicode/cldr/util/",
            "CLDRFile.java",
            "// START MECHANICALLY attributeOrdering GENERATED BY FindDTDOrder",
            "// END MECHANICALLY attributeOrdering GENERATED BY FindDTDOrder",
            "// START MECHANICALLY elementOrdering GENERATED BY FindDTDOrder",
            "// END MECHANICALLY elementOrdering GENERATED BY FindDTDOrder",
            "\t\t\t\t\t\"",
            '"' + CldrUtility.LINE_SEPARATOR + "\t\t\t\t\t");
    }

    private void writeNewSupplemental(String dir, String filename, String startAttributeTag, String endAttributeTag,
        String startElementTag, String endElementTag, String startSep, String endSep) throws IOException {
        BufferedReader oldFile = FileUtilities.openUTF8Reader(dir, filename);
        Log.setLogNoBOM(CLDRPaths.GEN_DIRECTORY + "/DTDOrder/" + filename);

        // CldrUtility.copyUpTo(oldFile, PatternCache.get("\\s*" +
        // startAttributeTag +
        // "\\s*"), Log.getLog(), true);
        // Log.println(startSep + breakLines(attributeSet) + endSep + endAttributeTag);
        // CldrUtility.copyUpTo(oldFile, PatternCache.get("\\s*" +
        // endAttributeTag +
        // "\\s*"), null, true);

        CldrUtility.copyUpTo(oldFile, PatternCache.get("\\s*" +
            startElementTag +
            "\\s*"), Log.getLog(), true);
        Log.println(startSep + breakLines(orderingList) + endSep + endElementTag);
        CldrUtility.copyUpTo(oldFile, PatternCache.get("\\s*" +
            endElementTag +
            "\\s*"), null, true);

        CldrUtility.copyUpTo(oldFile, null, Log.getLog(), false); // copy to end

        Log.close();
        oldFile.close();
    }

    private String breakLines(Collection orderingList) {
        final String joined = CldrUtility.join(orderingList, " ");
        return joined; // return Utility.breakLines(joined, sep, FIRST_LETTER_CHANGE.matcher(""), 80);
    }

    private String getJavaList(Collection orderingList) {
        boolean first2 = true;
        StringBuffer result = new StringBuffer();
        result.append('"');
        for (Iterator it = orderingList.iterator(); it.hasNext();) {
            if (first2)
                first2 = false;
            else
                result.append(" ");
            result.append(it.next().toString());
        }
        result.append('"');
        return result.toString();
    }

    /**
     * @param parent
     * @param skipEmpty
     *            TODO
     */
    // private void showRow(Object parent, boolean skipEmpty) {
    // List items = (List) element_childComparator.get(parent);
    // if (skipEmpty && items.size() == 0) return;
    // if (SHOW_PROGRESS) log.print(parent);
    // for (Iterator it2 = items.iterator(); it2.hasNext();) if (SHOW_PROGRESS)
    // log.print("\t" + it2.next());
    // if (SHOW_PROGRESS) log.println();
    // }
    /**
     * @param orderingList
     */
    private Object getFirst() {
        Set firstItems = new TreeSet();
        Set nonFirstItems = new TreeSet();
        for (Iterator it = elementOrderings.iterator(); it.hasNext();) {
            List list = (List) it.next();
            if (list.size() != 0) {
                firstItems.add(list.get(0));
                for (int i = 1; i < list.size(); ++i) {
                    nonFirstItems.add(list.get(i));
                }
            }
        }
        if (firstItems.size() == 0 && nonFirstItems.size() == 0)
            return DONE;
        firstItems.removeAll(nonFirstItems);
        if (firstItems.size() == 0)
            return null; // failure
        Object result = firstItems.iterator().next();
        removeEverywhere(result);
        return result;
    }

    /**
     * @param possibleFirst
     */
    private void removeEverywhere(Object possibleFirst) {
        // and remove from all the lists
        for (Iterator it2 = elementOrderings.iterator(); it2.hasNext();) {
            List list2 = (List) it2.next();
            if (SHOW_PROGRESS && list2.contains(possibleFirst)) {
                log.println("Removing " + possibleFirst + " from " + list2);
            }
            while (list2.remove(possibleFirst))
                ; // repeat until returns false
        }
    }

    // private boolean isNeverNotFirst(Object possibleFirst) {
    // if (showReason) if (SHOW_PROGRESS) log.println("Trying: " + possibleFirst);
    // for (Iterator it2 = element_childComparator.keySet().iterator();
    // it2.hasNext();) {
    // Object key = it2.next();
    // List list2 = (List) element_childComparator.get(key);
    // int pos = list2.indexOf(possibleFirst);
    // if (pos > 0) {
    // if (showReason) {
    // if (SHOW_PROGRESS) log.print("Failed at:\t");
    // showRow(key, false);
    // }
    // return false;
    // }
    // }
    // return true;
    // }

    static final Set<String> ELEMENT_SKIP_LIST = new HashSet<String>(Arrays.asList(new String[] {
        "collation", "base", "settings", "suppress_contractions", "optimize",
        "rules", "reset", "context", "p", "pc", "s", "sc", "t", "tc",
        "i", "ic", "extend", "x" }));

    static final Set<String> SUBELEMENT_SKIP_LIST = new HashSet<String>(Arrays
        .asList(new String[] { "PCDATA", "EMPTY", "ANY" }));

    // refine later; right now, doesn't handle multiple elements well.
    public void elementDecl(String name, String model) throws SAXException {
        // if (ELEMENT_SKIP_LIST.contains(name)) return;
        if (name.indexOf("contractions") >= 0
            || model
            .indexOf("[alias, base, settings, suppress, contractions, optimize, rules, special]") >= 0) {
        }
        allDefinedElements.add(name);
        if (SHOW_PROGRESS) {
            log.println("Element\t" + name + "\t" + model);
        }
        String[] list = model.split("[^-_A-Z0-9a-z]+");
        List<String> mc = new ArrayList<String>();
        /*
         * if (name.equals("currency")) { mc.add("alias"); mc.add("symbol");
         * mc.add("pattern"); }
         */
        for (int i = 0; i < list.length; ++i) {
            if (list[i].length() == 0)
                continue;
            if (list[i].equals("ANY") && !name.equals("special")) {
                System.err.println("WARNING- SHOULD NOT HAVE 'ANY': " + name + "\t"
                    + model);
            }
            if (SUBELEMENT_SKIP_LIST.contains(list[i]))
                continue;
            // if (SHOW_PROGRESS) log.print("\t" + list[i]);
            if (mc.contains(list[i])) {
                if (name.equals("currency") && list[i].equals("displayName") || list[i].equals("symbol")
                    || list[i].equals("pattern")) {
                    // do nothing, exception
                } else if (name.equals("rules") && (list[i].equals("reset") || list[i].equals("import"))) {
                    // do nothing, exception
                } else {
                    throw new IllegalArgumentException("Duplicate element in definition of  " + name
                        + ":\t" + list[i] + ":\t" + Arrays.asList(list) + ":\t" + mc);
                }
            } else {
                mc.add(list[i]);
            }
        }
        if (recordingAttributeElements) {
            Set<String> children = new TreeSet<String>(mc);
            children.remove("alias");
            children.remove("special");
            children.remove("cp");
            elementToChildren.putAll(name, children);
        }
        allDefinedElements.addAll(mc);

        if (mc.size() < 1) {
            if (SHOW_PROGRESS) {
                log.println("\tSKIPPING\t" + name + "\t" + mc);
            }
        } else {
            if (SHOW_PROGRESS) {
                log.println("\t" + name + "\t" + mc);
            }
            elementOrderings.add(mc);
        }

        // if (SHOW_PROGRESS) log.println();
    }

    Set<String> skipCommon = new LinkedHashSet<String>(Arrays.asList(new String[] { "validSubLocales",
        "standard", "references",
        "alt", "draft",
    }));

    Set<String> attributeSet = new TreeSet<String>();
    {
        attributeSet.add("_q");
        attributeSet.addAll(skipCommon);
    }
    List<String> attributeList;

    Map<String, Set<String>> attribEquiv = new TreeMap<String, Set<String>>();

    Relation<String, String> attributeToElements = Relation.of(new TreeMap<String, Set<String>>(),
        TreeSet.class);
    private XEquivalenceClass attributeEquivalents;

    public void attributeDecl(String eName, String aName, String type,
        String mode, String value) throws SAXException {
        if (SHOW_ALL)
            log.println("attributeDecl");
        // if (SHOW_ALL) log.println("Attribute\t" + eName + "\t" +
        // aName + "\t" + type + "\t" + mode + "\t" + value);
        if (SHOW_PROGRESS) System.out.println("Attribute\t" + eName + "\t" + aName + "\t" + type
            + "\t" + mode + "\t" + value);
        if (!skipCommon.contains(aName)) {
            attributeSet.add(aName);
            Set<String> l = attribEquiv.get(eName);
            if (l == null)
                attribEquiv.put(eName, l = new TreeSet<String>());
            l.add(aName);
        }
        if (recordingAttributeElements) {
            attributeToElements.put(aName, eName);
        }
    }

    public void internalEntityDecl(String name, String value) throws SAXException {
        if (SHOW_ALL)
            log.println("internalEntityDecl");
        // if (SHOW_ALL) log.println("Internal Entity\t" + name +
        // "\t" + value);
    }

    public void externalEntityDecl(String name, String publicId, String systemId)
        throws SAXException {
        if (SHOW_ALL)
            log.println("externalEntityDecl");
        // if (SHOW_ALL) log.println("Internal Entity\t" + name +
        // "\t" + publicId + "\t" + systemId);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ContentHandler#endDocument()
     */
    public void endDocument() throws SAXException {
        if (SHOW_ALL)
            log.println("endDocument");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ContentHandler#startDocument()
     */
    public void startDocument() throws SAXException {
        if (SHOW_ALL)
            log.println("startDocument");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
     */
    public void characters(char[] ch, int start, int length) throws SAXException {
        if (SHOW_ALL)
            log.println("characters");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
     */
    public void ignorableWhitespace(char[] ch, int start, int length)
        throws SAXException {
        if (SHOW_ALL)
            log.println("ignorableWhitespace");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
     */
    public void endPrefixMapping(String prefix) throws SAXException {
        if (SHOW_ALL)
            log.println("endPrefixMapping");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
     */
    public void skippedEntity(String name) throws SAXException {
        if (SHOW_ALL)
            log.println("skippedEntity");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
     */
    public void setDocumentLocator(Locator locator) {
        if (SHOW_ALL)
            log.println("setDocumentLocator");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String,
     * java.lang.String)
     */
    public void processingInstruction(String target, String data)
        throws SAXException {
        if (SHOW_ALL)
            log.println("processingInstruction");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String,
     * java.lang.String)
     */
    public void startPrefixMapping(String prefix, String uri) throws SAXException {
        if (SHOW_ALL)
            log.println("startPrefixMapping");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ContentHandler#endElement(java.lang.String,
     * java.lang.String, java.lang.String)
     */
    public void endElement(String namespaceURI, String localName, String qName)
        throws SAXException {
        if (SHOW_ALL)
            log.println("endElement");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ContentHandler#startElement(java.lang.String,
     * java.lang.String, java.lang.String, org.xml.sax.Attributes)
     */
    public void startElement(String namespaceURI, String localName, String qName,
        Attributes atts) throws SAXException {
        if (SHOW_ALL)
            log.println("startElement");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
     */
    public void error(SAXParseException exception) throws SAXException {
        if (SHOW_ALL)
            log.println("error");
        throw exception;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
     */
    public void fatalError(SAXParseException exception) throws SAXException {
        if (SHOW_ALL)
            log.println("fatalError");
        throw exception;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
     */
    public void warning(SAXParseException exception) throws SAXException {
        if (SHOW_ALL)
            log.println("warning");
        throw exception;
    }

    public List<String> getAttributeOrder() {
        return attributeList;
    }

    public List<String> getElementOrder() {
        return orderingList;
    }

    public Set<String> getCommonAttributes() {
        return skipCommon;
    }

}
