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 + -
显示快捷键?