xml.java

来自「derby database source code.good for you.」· Java 代码 · 共 862 行 · 第 1/2 页

JAVA
862
字号
     * store the _parsed_ version for subsequent use.     * @param text The string value to check.     * @param preserveWS Whether or not to preserve     *  ignorable whitespace.     * @return  If 'text' constitutes a valid XML document,     *  it has been stored in this XML value and nothing     *  is returned; otherwise, an exception is thrown.     * @exception StandardException Thrown on parse error.     */    public void parseAndLoadXML(String text, boolean preserveWS)        throws StandardException    {        try {            if (preserveWS) {            // We're just going to use the text exactly as it            // is, so we just need to see if it parses.                 loadSAXReader();                saxReader.parse(                    new InputSource(new StringReader(text)));            }            else {            // We don't support this yet, so we shouldn't            // get here.                if (SanityManager.DEBUG)                    SanityManager.THROWASSERT("Tried to STRIP whitespace " +                        "but we shouldn't have made it this far");            }        } catch (Exception xe) {        // The text isn't a valid XML document.  Throw a StandardException        // with the parse exception nested in it.            throw StandardException.newException(                SQLState.LANG_NOT_AN_XML_DOCUMENT, xe);        }        // If we get here, the text is valid XML so go ahead        // and load/store it.        if (xmlStringValue == null)            xmlStringValue = new SQLChar();        xmlStringValue.setValue(text);        return;    }    /**     * The SQL/XML XMLSerialize operator.     * Converts this XML value into a string with a user-specified     * type, and returns that string via the received StringDataValue     * (if the received StringDataValue is non-null; else a new     * StringDataValue is returned).     * @param result The result of a previous call to this method,     *    null if not called yet.     * @param targetType The string type to which we want to serialize.     * @param targetWidth The width of the target type.     * @return A serialized (to string) version of this XML object,     *  in the form of a StringDataValue object.     * @exception StandardException    Thrown on error     */    public StringDataValue XMLSerialize(StringDataValue result,        int targetType, int targetWidth) throws StandardException    {        if (result == null) {            switch (targetType)            {                case Types.CHAR:        result = new SQLChar(); break;                case Types.VARCHAR:     result = new SQLVarchar(); break;                case Types.LONGVARCHAR: result = new SQLLongvarchar(); break;                case Types.CLOB:        result = new SQLClob(); break;                default:                // Shouldn't ever get here, as this check was performed                // at bind time.                    if (SanityManager.DEBUG) {                        SanityManager.THROWASSERT(                            "Should NOT have made it to XMLSerialize " +                            "with a non-string target type.");                    }                    return null;            }        }        // Else we're reusing a StringDataValue.  We only reuse        // the result if we're executing the _same_ XMLSERIALIZE        // call on multiple rows.  That means that all rows        // must have the same result type (targetType) and thus        // we know that the StringDataValue already has the        // correct type.  So we're set.        if (this.isNull()) {        // Attempts to serialize a null XML value lead to a null        // result (SQL/XML[2003] section 10.13).            result.setToNull();            return result;        }        // Get the XML value as a string.  For this UTF-8 impl,        // we already have it as a string, so just use that.        result.setValue(xmlStringValue.getString());        // Seems wrong to trunc an XML document, as it then becomes non-        // well-formed and thus useless.  So we throw an error (that's        // what the "true" in the next line says).        result.setWidth(targetWidth, 0, true);        return result;    }    /**     * The SQL/XML XMLExists operator.     * Takes an XML query expression (as a string) and an XML     * value and checks if at least one node in the XML     * value matches the query expression.  NOTE: For now,     * the query expression must be XPath only (XQuery not     * supported).     * @param xExpr The query expression, as a string.     * @param xml The XML value being queried.     * @return True if the received query expression matches at     *  least one node in the received XML value; unknown if     *  either the query expression or the xml value is null;     *  false otherwise.     * @exception StandardException Thrown on error     */    public BooleanDataValue XMLExists(StringDataValue xExpr,        XMLDataValue xml) throws StandardException    {        if ((xExpr == null) || xExpr.isNull())        // If the query is null, we assume unknown.            return SQLBoolean.unknownTruthValue();        if ((xml == null) || xml.isNull())        // Then per SQL/XML spec 8.4, we return UNKNOWN.            return SQLBoolean.unknownTruthValue();        return new SQLBoolean(xml.exists(xExpr.getString()));    }    /**     * Helper method for XMLExists.     * See if the received XPath expression returns at least     * one node when evaluated against _this_ XML value.     * @param xExpr The XPath expression.     * @return True if at least one node in this XML value     *  matches the received xExpr; false otherwise.     */    public boolean exists(String xExpr) throws StandardException    {        // NOTE: At some point we'll probably need to implement some        // some kind of query cache so that we don't have to recompile        // the same query over and over for every single XML row        // in a table.  That's what we do right now...        try {            xExpr = replaceDoubleQuotes(xExpr);            loadXSLTObjects();            // Take our simple stylesheet and plug in the query.            int pos = xsltStylesheet.indexOf(XPATH_PLACEHOLDER);            StringBuffer stylesheet = new StringBuffer(xsltStylesheet);            stylesheet.replace(pos, pos + XPATH_PLACEHOLDER.length(), xExpr);            // Create a Templates ContentHandler to handle parsing of the             // stylesheet.            TemplatesHandler templatesHandler =                 saxTFactory.newTemplatesHandler();            xsltReader.setContentHandler(templatesHandler);                // Now parse the generic stylesheet we created.            xsltReader.parse(                new InputSource(new StringReader(stylesheet.toString())));            // Get the Templates object (generated during the parsing of            // the stylesheet) from the TemplatesHandler.            Templates compiledQuery = templatesHandler.getTemplates();            // Create a Transformer ContentHandler to handle parsing of             // the XML Source.              TransformerHandler transformerHandler                 = saxTFactory.newTransformerHandler(compiledQuery);            // Reset the XMLReader's ContentHandler to the TransformerHandler.            xsltReader.setContentHandler(transformerHandler);            // Create an ExistsHandler.  When the XSLT transformation            // occurs, a period (".") will be thrown to this handler            // (via a SAX 'characters' event) for every matching            // node that XSLT finds.  This is how we know if a            // match was found.            ExistsHandler eH = new ExistsHandler();            transformerHandler.setResult(new SAXResult(eH));            // This call to "parse" is what does the query, because we            // passed in an XSLT handler with the compiled query above.            try {                xsltReader.parse(                    new InputSource(new StringReader(getString())));            } catch (Throwable th) {                if (th.getMessage().indexOf(                    "SAXException: " + QUERY_MATCH_STRING) == -1)                { // then this isn't the exception that means we have                  // a match; so re-throw it.                    throw new Exception(th.getMessage());                }            }            // Did we have any matches?            return eH.exists();        } catch (Exception xe) {        // We don't expect to get here.  Turn it into a        // StandardException, then throw it.            throw StandardException.newException(                SQLState.LANG_UNEXPECTED_XML_EXCEPTION, xe);        }    }    /* ****     * Helper classes and methods.     * */    /**     * Load an XMLReader for SAX events that can be used     * for parsing XML data.     *     * This method is currently only used for XMLPARSE, and     * the SQL/XML[2003] spec says that XMLPARSE should NOT     * perform validation -- Seciont 6.11:     *     *    "Perform a non-validating parse of a character string to     *    produce an XML value."     *     * Thus, we make sure to disable validation on the XMLReader     * loaded here.  At some point in the future we will probably     * want to add support for the XMLVALIDATE function--but until     * then, user is unable to validate the XML values s/he inserts.     *     * Note that, even with validation turned off, XMLPARSE     * _will_ still check the well-formedness of the values,     * and it _will_ still process DTDs to get default values,     * etc--but that's it; no validation errors will be thrown.     *     * For future reference: the features needed to perform     * validation (with Xerces) are:     *     * http://apache.org/xml/features/validation/schema     * http://apache.org/xml/features/validation/dynamic     */    protected void loadSAXReader() throws Exception    {        if (saxReader != null)        // already loaded.            return;        // Get an instance of an XMLReader.        saxReader = XMLReaderFactory.createXMLReader(XML_PARSER_CLASS);        // Turn off validation, since it's not allowed by        // SQL/XML[2003] spec.        saxReader.setFeature(            "http://xml.org/sax/features/validation", false);        // Make the parser namespace aware.        saxReader.setFeature(            "http://xml.org/sax/features/namespaces", true);        // We have to set the error handler in order to properly        // receive the parse errors.        saxReader.setErrorHandler(new XMLErrorHandler());    }    /**     * Prepare for an XSLT query by loading the objects     * required for such a query.  We should only have     * to do this once per XML object.     */    private void loadXSLTObjects() throws SAXException    {        if (xsltReader != null)        // we already loaded everything.            return;        // Instantiate a TransformerFactory.        TransformerFactory tFactory = TransformerFactory.newInstance();        // Cast the TransformerFactory to SAXTransformerFactory.        saxTFactory = (TransformerFactoryImpl)tFactory;        // Get an XML reader.        xsltReader = XMLReaderFactory.createXMLReader(XML_PARSER_CLASS);        // Make the parser namespace aware.  Note that because we        // only support a small subset of SQL/XML, and because we        // only allow XPath (as opposed to XQuery) expressions,        // there is no way for a user to specify namespace        // bindings as part of the XMLEXISTS operator.  This means        // that in order to query for a node name, the user must        // use the XPath functions "name()" and "local-name()"        // in conjunction with XPath 1.0 'namespace' axis.  For        // example:        //        // To see if any elements exist that have a specific name        // with ANY namespace:        //     //child::*[local-name()="someName"]        //        // To see if any elements exist that have a specific name        // with NO namespace:        //     //child::*[name()="someName"]        //        // To see if any elements exist that have a specific name        // in a specific namespace:        //     //child::*[local-name()=''someName'' and        //        namespace::*[string()=''http://www.some.namespace'']]        //        xsltReader.setFeature(            "http://xml.org/sax/features/namespaces", true);        // Create a very simple XSLT stylesheet.  This stylesheet        // will execute the XPath expression and, for every match,        // write a period (".") to the ExistsHandler (see the exists()        // method above).  Then, in order to see if at least one        // node matches, we just check to see if the ExistsHandler        // caught at least one 'characters' event.  If it did, then        // we know we had a match.        if (xsltStylesheet == null) {            StringBuffer sb = new StringBuffer();            sb.append("<xsl:stylesheet version=\"1.0\"\n");            sb.append("xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n");            sb.append(" <xsl:template match=\"/\">\n"); // Search whole doc...            sb.append("  <xsl:for-each select=\"");     // For every match...            sb.append(XPATH_PLACEHOLDER);               // using XPath expr...            sb.append("\">.</xsl:for-each>\n");         // Write a "."            sb.append(" </xsl:template>\n");            sb.append("</xsl:stylesheet>\n");            xsltStylesheet = sb.toString();        }    }    /**     * Takes a string (which is an XPath query specified by     * the user) and replaces any double quotes with single     * quotes.  We have to do this because a double quote     * in the XSLT stylesheet (which is where the user's     * query ends up) will be parsed as a query terminator     * thus will cause XSLT execution errors.     * @param queryText Text in which we want to replace double     *  quotes.     * @return queryText with all double quotes replaced by     *  single quotes.     */    private String replaceDoubleQuotes(String queryText)    {        int pos = queryText.indexOf("\"");        if (pos == -1)        // nothing to do.            return queryText;        StringBuffer sBuf = new StringBuffer(queryText);        while (pos >= 0) {            sBuf.replace(pos, pos+1, "'");            pos = queryText.indexOf("\"", pos+1);        }        return sBuf.toString();    }    /*     ** The XMLErrorHandler class is just a generic implementation     ** of the ErrorHandler interface.  It allows us to catch     ** and process XML parsing errors in a graceful manner.     */    private class XMLErrorHandler implements ErrorHandler    {        public void error (SAXParseException exception)            throws SAXException        {            throw new SAXException (exception);        }        public void fatalError (SAXParseException exception)            throws SAXException        {            throw new SAXException (exception);        }        public void warning (SAXParseException exception)            throws SAXException        {            throw new SAXException (exception);        }    }    /*     ** The ExistsHandler is what we pass to the XSLT processor     ** when we query.  The generic xsltStylesheet that we defined     ** above will throw a 'characters' event for every matching     ** node that is found by the XSLT transformation.  This     ** handler is the one that catches the event, and thus     ** it tells us whether or not we had a match.     */    private class ExistsHandler extends DefaultHandler    {        // Did we catch at least one 'characters' event?        private boolean atLeastOneMatch;        public ExistsHandler() {            atLeastOneMatch = false;        }        /*         * Catch a SAX 'characters' event, which tells us that         * we had at least one matching node.         */        public void characters(char[] ch, int start, int length)            throws SAXException        {            // If we get here, we had at least one matching node.            // Since that's all we need to know, we don't have            // to continue querying--we can stop the XSLT            // transformation now by throwing a SAX exception.            atLeastOneMatch = true;            throw new SAXException(QUERY_MATCH_STRING);        }        /*         * Tell whether or not this handler caught a match.         */        public boolean exists()        {            return atLeastOneMatch;        }    }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?