public void clear() {}
    }

    /**
     * used to identify a reference to a schema document
     * if the same document is referenced twice with the same key, then
     * we only need to parse it once.
     *
     * When 2 XSDKey's are compared, the following table can be used to
     * determine whether they are equal:
     *      inc     red     imp     pre     ins
     * inc  N/L      ?      N/L     N/L     N/L
     * red   ?      N/L      ?       ?       ?
     * imp  N/L      ?      N/P     N/P     N/P
     * pre  N/L      ?      N/P     N/P     N/P
     * ins  N/L      ?      N/P     N/P     N/P
     *
     * Where: N/L: duplicate when they have the same namespace and location.
     *         ? : not clear from the spec.
     *             REVISIT: to simplify the process, also considering
     *             it's very rare, we treat them as not duplicate.
     *        N/P: not possible. imp/pre/ins are referenced by namespace.
     *             when the first time we encounter a schema document for a
     *             namespace, we create a grammar and store it in the grammar
     *             bucket. when we see another reference to the same namespace,
     *             we first check whether a grammar with the same namespace is
     *             already in the bucket, which is true in this case, so we
     *             won't create another XSDKey.
     *
     * Conclusion from the table: two XSDKey's are duplicate only when all of
     * the following are true:
     * 1. They are both "redefine", or neither is "redefine";
     * 2. They have the same namespace;
     * 3. They have the same non-null location.
     *
     * About 3: if neither has a non-null location, then it's the case where
     * 2 input streams are provided, but no system ID is provided. We can't tell
     * whether the 2 streams have the same content, so we treat them as not
     * duplicate.
     */
    private static class XSDKey {
        String systemId;
        short  referType;
        // for inclue/redefine, this is the enclosing namespace
        // for import/preparse/instance, this is the target namespace
        String referNS;

        XSDKey(String systemId, short referType, String referNS) {
            this.systemId = systemId;
            this.referType = referType;
            this.referNS = referNS;
        }

        public int hashCode() {
            // according to the description at the beginning of this class,
            // we use the hashcode of the namespace as the hashcoe of this key.
            return referNS == null ? 0 : referNS.hashCode();
        }

        public boolean equals(Object obj) {
            if (!(obj instanceof XSDKey)) {
                return false;
            }
            XSDKey key = (XSDKey)obj;

            // condition 1: both are redefine
            /** if (referType == XSDDescription.CONTEXT_REDEFINE ||
                    key.referType == XSDDescription.CONTEXT_REDEFINE) {
                if (referType != key.referType)
                    return false;
            }**/

            // condition 2: same namespace
            if (referNS != key.referNS)
                return false;

            // condition 3: same non-null location
            if (systemId == null || !systemId.equals(key.systemId)) {
                return false;
            }

            return true;
        }
    }

    private static final class SAX2XNIUtil extends ErrorHandlerWrapper {
        public static XMLParseException createXMLParseException0(SAXParseException exception) {
            return createXMLParseException(exception);
        }
        public static XNIException createXNIException0(SAXException exception) {
            return createXNIException(exception);
        }
    }