--- /dev/null 2014-09-08 10:45:56.830930409 -0700 +++ new/test/javax/xml/jaxp/libs/org/apache/qetest/xslwrapper/TraxSAXWrapper.java 2015-01-09 15:42:29.637199593 -0800 @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + */ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.qetest.xslwrapper; + +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.util.Properties; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.Templates; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TemplatesHandler; +import javax.xml.transform.sax.TransformerHandler; +import javax.xml.transform.stream.StreamResult; +import static jaxp.library.JAXPTestUtilities.filenameToURL; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +/** + * Implementation of TransformWrapper that uses the TrAX API and uses + * SAXSource/SAXResult whenever possible. + * This implementation uses SAX to build the stylesheet and to perform the + * transformation. + *
Important! The underlying System property of + * javax.xml.transform.TransformerFactory will determine the actual TrAX + * implementation used. This value will be reported out in our + * getProcessorInfo() method.
+ * + */ +public class TraxSAXWrapper extends TransformWrapperHelper { + /** + * SAXTransformerFactory we actually use; constructed in newProcessor(). + */ + private SAXTransformerFactory saxFactory; + + /** + * Templates to use for buildStylesheet(). + */ + private Templates builtTemplates; + + /** + * Get a general description of this wrapper itself. + * + * @return Uses TrAX to perform transforms from SAXSource(systemId) + */ + public String getDescription() { + return "Uses TrAX to perform transforms from SAXSource(stream)"; + } + + /** + * Get a specific description of the wrappered processor. + * + * @return specific description of the underlying processor or transformer + * implementation: this should include both the general product name, as + * well as specific version info. If possible, should be implemented without + * actively creating an underlying processor. + */ + @Override + public Properties getProcessorInfo() { + Properties p = TraxWrapperUtils.getTraxInfo(); + p.put("traxwrapper.method", "sax"); + p.put("traxwrapper.desc", getDescription()); + return p; + } + + /** + * Actually create/initialize an underlying processor or factory. + * + * For TrAX/javax.xml.transform implementations, this creates a new + * TransformerFactory. + * + * @param options Hashtable of options, unused. + * + * @return (Object)getProcessor() as a side-effect, this will be null if + * there was any problem creating the processor OR if the underlying + * implementation doesn't use this + * @throws javax.xml.transform.TransformerConfigurationException when + * actual implementation doesn't support StreamSource or StreamResult. + */ + @Override + public TransformerFactory newProcessor(Properties options) throws TransformerConfigurationException { + newProcessorOpts = options; + reset(false); + factory = TransformerFactory.newInstance(); + // Verify the factory supports SAX! + if (!(factory.getFeature(SAXSource.FEATURE) + && factory.getFeature(SAXResult.FEATURE))) { + throw new TransformerConfigurationException("TraxSAXWrapper.newProcessor: factory does not support SAX!"); + } + // Set any of our options as Attributes on the factory + TraxWrapperUtils.setAttributes(factory, options); + saxFactory = (SAXTransformerFactory) factory; + return saxFactory; + } + + /** + * Transform supplied xmlName file with the stylesheet in the xslName file + * into a resultName file using SAX. + * + * Pseudocode:
+ * // Read/build stylesheet
+ * xslReader.setContentHandler(templatesHandler);
+ * xslReader.parse(xslName);
+ *
+ * xslOutputProps = templates.getOutputProperties();
+ * // Set features and tie in DTD, lexical, etc. handling
+ *
+ * serializingHandler.getTransformer().setOutputProperties(xslOutputProps);
+ * serializingHandler.setResult(new StreamResult(outBytes));
+ * stylesheetHandler.setResult(new SAXResult(serializingHandler));
+ * xmlReader.setContentHandler(stylesheetHandler);
+ * // Perform Transform
+ * xmlReader.parse(xmlName);
+ * // Separately: write bytes to disk
+ *
+ *
+ * @param xmlName local path\filename of XML file to transform
+ * @param xslName local path\filename of XSL stylesheet to use
+ * @param resultName local path\filename to put result in
+ *
+ * @throws Exception any underlying exceptions from the wrappered processor
+ * are simply allowed to propagate; throws a RuntimeException if any other
+ * problems prevent us from actually completing the operation
+ */
+ @Override
+ public void transform(String xmlName, String xslName, String resultName)
+ throws Exception {
+ preventFootShooting();
+
+ // Create a ContentHandler to handle parsing of the xsl
+ TemplatesHandler templatesHandler = saxFactory.newTemplatesHandler();
+
+ // Create an XMLReader and set its ContentHandler.
+ // Be sure to use the JAXP methods only!
+ XMLReader xslReader = getJAXPXMLReader();
+ xslReader.setContentHandler(templatesHandler);
+
+ // read/build Templates from StreamSource
+ xslReader.parse(filenameToURL(xslName));
+
+ // Get the Templates object from the ContentHandler.
+ Templates templates = templatesHandler.getTemplates();
+ // Get the outputProperties from the stylesheet, so
+ // we can later use them for the serialization
+ Properties xslOutputProps = templates.getOutputProperties();
+
+ // Create a ContentHandler to handle parsing of the XML
+ TransformerHandler stylesheetHandler = saxFactory.newTransformerHandler(templates);
+ // Also set systemId to the stylesheet
+ stylesheetHandler.setSystemId(filenameToURL(xslName));
+
+ // Untimed: Set any of our options as Attributes on the transformer
+ TraxWrapperUtils.setAttributes(stylesheetHandler.getTransformer(), newProcessorOpts);
+
+ // Apply any parameters needed
+ applyParameters(stylesheetHandler.getTransformer());
+
+ // Use a new XMLReader to parse the XML document
+ XMLReader xmlReader = getJAXPXMLReader();
+ xmlReader.setContentHandler(stylesheetHandler);
+
+ // Set the ContentHandler to also function as LexicalHandler,
+ // includes "lexical" events (e.g., comments and CDATA).
+ xmlReader.setProperty(
+ "http://xml.org/sax/properties/lexical-handler",
+ stylesheetHandler);
+
+ // Also attempt to set as a DeclHandler, which Xalan-J
+ // supports even though it is not required by JAXP
+ // Ignore exceptions for other processors since this
+ // is not a required setting
+ try {
+ xmlReader.setProperty(
+ "http://xml.org/sax/properties/declaration-handler",
+ stylesheetHandler);
+ } catch (SAXException se) { /* no-op - ignore */ }
+
+ // added by sb. Tie together DTD and other handling
+ xmlReader.setDTDHandler(stylesheetHandler);
+ try {
+ xmlReader.setFeature(
+ "http://xml.org/sax/features/namespace-prefixes",
+ true);
+ } catch (SAXException se) { /* no-op - ignore */ }
+ try {
+ xmlReader.setFeature(
+ "http://apache.org/xml/features/validation/dynamic",
+ true);
+ } catch (SAXException se) { /* no-op - ignore */ }
+
+ // Create a 'pipe'-like identity transformer, so we can
+ // easily get our results serialized to disk
+ TransformerHandler serializingHandler = saxFactory.newTransformerHandler();
+ // Set the stylesheet's output properties into the output
+ // via it's Transformer
+ serializingHandler.getTransformer().setOutputProperties(xslOutputProps);
+
+ // Create StreamResult to byte stream in memory
+ ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+ StreamResult byteResult = new StreamResult(outBytes);
+ serializingHandler.setResult(byteResult);
+
+ // Create a SAXResult dumping into our 'pipe' serializer
+ // and tie in lexical handling (is any other handling needed?)
+ SAXResult saxResult = new SAXResult(serializingHandler);
+ saxResult.setLexicalHandler(serializingHandler);
+
+ // Set the original stylesheet to dump into our result
+ stylesheetHandler.setResult(saxResult);
+
+ // Parse the XML input document and do transform
+ xmlReader.parse(filenameToURL(xmlName));
+
+ // writeResults from the byte array
+ byte[] writeBytes = outBytes.toByteArray(); // Should this be timed too or not?
+ try (FileOutputStream writeStream = new FileOutputStream(resultName)) {
+ writeStream.write(writeBytes);
+ }
+ }
+
+ /**
+ * Pre-build/pre-compile a stylesheet.
+ *
+ * Although the actual mechanics are implementation-dependent, most
+ * processors have some method of pre-setting up the data needed by the
+ * stylesheet itself for later use in transforms. In
+ * TrAX/javax.xml.transform, this equates to creating a Templates object.
+ *
+ * Sets isStylesheetReady() to true if it succeeds. Users can then call
+ * transformWithStylesheet(xmlName, resultName) to actually perform a
+ * transformation with this pre-built stylesheet.
+ *
+ * @param xslName local path\filename of XSL stylesheet to use
+ *
+ * @throws Exception any underlying exceptions from the wrapped processor
+ * are simply allowed to propagate; throws a RuntimeException if any other
+ * problems prevent us from actually completing the operation
+ *
+ * @see #transformWithStylesheet(String xmlName, String resultName)
+ */
+ @Override
+ public void buildStylesheet(String xslName) throws Exception {
+ preventFootShooting();
+
+ // Create a ContentHandler to handle parsing of the xsl
+ TemplatesHandler templatesHandler = saxFactory.newTemplatesHandler();
+
+ // Create an XMLReader and set its ContentHandler.
+ XMLReader xslReader = getJAXPXMLReader();
+ xslReader.setContentHandler(templatesHandler);
+
+ // read/build Templates from StreamSource
+ xslReader.parse(filenameToURL(xslName));
+
+ // Also set systemId to the stylesheet
+ templatesHandler.setSystemId(filenameToURL(xslName));
+
+ // Get the Templates object from the ContentHandler.
+ builtTemplates = templatesHandler.getTemplates();
+ m_stylesheetReady = true;
+ }
+
+ /**
+ * Transform supplied xmlName file with a pre-built/pre-compiled stylesheet
+ * into a resultName file.
+ *
+ * User must have called buildStylesheet(xslName) beforehand, obviously.
+ * Names are assumed to be local path\filename references, and will be
+ * converted to URLs as needed.
+ *
+ * @param xmlName local path\filename of XML file to transform
+ * @param resultName local path\filename to put result in
+ *
+ * @throws Exception any underlying exceptions from the wrappered processor
+ * are simply allowed to propagate; throws a RuntimeException if any other
+ * problems prevent us from actually completing the operation; throws an
+ * IllegalStateException if isStylesheetReady() == false.
+ *
+ * @see #buildStylesheet(String xslName)
+ */
+ @Override
+ public void transformWithStylesheet(String xmlName, String resultName)
+ throws Exception {
+ if (!isStylesheetReady()) {
+ throw new IllegalStateException("transformWithStylesheet() when isStylesheetReady() == false");
+ }
+ preventFootShooting();
+
+ // Get the outputProperties from the stylesheet, so
+ // we can later use them for the serialization
+ Properties xslOutputProps = builtTemplates.getOutputProperties();
+
+ // Create a ContentHandler to handle parsing of the XML
+ TransformerHandler stylesheetHandler = saxFactory.newTransformerHandler(builtTemplates);
+
+ // Untimed: Set any of our options as Attributes on the transformer
+ TraxWrapperUtils.setAttributes(stylesheetHandler.getTransformer(), newProcessorOpts);
+
+ // Apply any parameters needed
+ applyParameters(stylesheetHandler.getTransformer());
+
+ // Use a new XMLReader to parse the XML document
+ XMLReader xmlReader = getJAXPXMLReader();
+ xmlReader.setContentHandler(stylesheetHandler);
+
+ // Set the ContentHandler to also function as LexicalHandler,
+ // includes "lexical" events (e.g., comments and CDATA).
+ xmlReader.setProperty(
+ "http://xml.org/sax/properties/lexical-handler",
+ stylesheetHandler);
+ xmlReader.setProperty(
+ "http://xml.org/sax/properties/declaration-handler",
+ stylesheetHandler);
+
+ // added by sb. Tie together DTD and other handling
+ xmlReader.setDTDHandler(stylesheetHandler);
+ try {
+ xmlReader.setFeature(
+ "http://xml.org/sax/features/namespace-prefixes",
+ true);
+ } catch (SAXException se) { /* no-op - ignore */ }
+ try {
+ xmlReader.setFeature(
+ "http://apache.org/xml/features/validation/dynamic",
+ true);
+ } catch (SAXException se) { /* no-op - ignore */ }
+
+ // Create a 'pipe'-like identity transformer, so we can
+ // easily get our results serialized to disk
+ TransformerHandler serializingHandler = saxFactory.newTransformerHandler();
+ // Set the stylesheet's output properties into the output
+ // via it's Transformer
+ serializingHandler.getTransformer().setOutputProperties(xslOutputProps);
+
+ // Create StreamResult to byte stream in memory
+ ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+ StreamResult byteResult = new StreamResult(outBytes);
+ serializingHandler.setResult(byteResult);
+
+ // Create a SAXResult dumping into our 'pipe' serializer
+ // and tie in lexical handling (is any other handling needed?)
+ SAXResult saxResult = new SAXResult(serializingHandler);
+ saxResult.setLexicalHandler(serializingHandler);
+
+ // Set the original stylesheet to dump into our result
+ stylesheetHandler.setResult(saxResult);
+
+ // Timed: Parse the XML input document and do transform
+ xmlReader.parse(filenameToURL(xmlName));
+
+ // Timed: writeResults from the byte array
+ byte[] writeBytes = outBytes.toByteArray(); // Should this be timed too or not?
+ try (FileOutputStream writeStream = new FileOutputStream(resultName)) {
+ writeStream.write(writeBytes);
+ }
+ }
+
+ /**
+ * Transform supplied xmlName file with a stylesheet found in an
+ * xml-stylesheet PI into a resultName file.
+ *
+ * Names are assumed to be local path\filename references, and will be
+ * converted to URLs as needed. Implementations will use whatever facilities
+ * exist in their wrappered processor to fetch and build the stylesheet to
+ * use for the transform.
+ *
+ * @param xmlName local path\filename of XML file to transform
+ * @param resultName local path\filename to put result in
+ *
+ * @throws Exception any underlying exceptions from the wrappered processor
+ * are simply allowed to propagate; throws a RuntimeException if any other
+ * problems prevent us from actually completing the operation
+ */
+ @Override
+ public void transformEmbedded(String xmlName, String resultName)
+ throws Exception {
+ throw new RuntimeException("TraxSAXWrapper.transformEmbedded not implemented yet!");
+ }
+
+ /**
+ * Reset our parameters and wrapper state, and optionally force creation of
+ * a new underlying processor implementation.
+ *
+ * This always clears our built stylesheet and any parameters that have been
+ * set. If newProcessor is true, also forces a re-creation of our underlying
+ * processor as if by calling newProcessor().
+ *
+ * @param newProcessor if we should reset our underlying processor
+ * implementation as well
+ */
+ @Override
+ public void reset(boolean newProcessor) {
+ super.reset(newProcessor); // clears indent and parameters
+ m_stylesheetReady = false;
+ builtTemplates = null;
+ if (newProcessor) {
+ try {
+ newProcessor(newProcessorOpts);
+ } catch (TransformerConfigurationException ignore) {}
+ }
+ }
+
+ /**
+ * Apply a single parameter to a Transformer.
+ *
+ * Overridden to take a Transformer and call setParameter().
+ *
+ * @param transformer a Transformer object.
+ * @param namespace for the parameter, may be null
+ * @param name for the parameter, should not be null
+ * @param value for the parameter, may be null
+ */
+ @Override
+ protected void applyParameter(Transformer transformer, String namespace,
+ String name, String value) {
+ try {
+ // Munge the namespace into the name per
+ // javax.xml.transform.Transformer.setParameter()
+ if (null != namespace) {
+ name = "{" + namespace + "}" + name;
+ }
+ transformer.setParameter(name, value);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("applyParameter threw: " + e.toString());
+ }
+ }
+
+ /**
+ * Worker method to get an XMLReader.
+ *
+ * Not the most efficient of methods, but makes the code simpler.
+ *
+ * @return a new XMLReader for use, with setNamespaceAware(true)
+ * @throws java.lang.Exception
+ */
+ protected XMLReader getJAXPXMLReader() throws Exception {
+ // Be sure to use the JAXP methods only!
+ SAXParserFactory sfactory = SAXParserFactory.newInstance();
+ sfactory.setNamespaceAware(true);
+ return sfactory.newSAXParser().getXMLReader();
+ }
+}