/*
* 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.
*/
/*
* $Id: TransformerFactoryImpl.java 468640 2006-10-28 06:53:53Z minchau $
*/
package org.apache.xalan.processor;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Properties;
import javax.xml.XMLConstants;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
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 javax.xml.transform.stream.StreamSource;
import org.apache.xalan.res.XSLMessages;
import org.apache.xalan.res.XSLTErrorResources;
import org.apache.xalan.transformer.TrAXFilter;
import org.apache.xalan.transformer.TransformerIdentityImpl;
import org.apache.xalan.transformer.TransformerImpl;
import org.apache.xalan.transformer.XalanProperties;
import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM;
import org.apache.xml.utils.DefaultErrorHandler;
import org.apache.xml.utils.SystemIDResolver;
import org.apache.xml.utils.TreeWalker;
import org.apache.xml.utils.StylesheetPIHandler;
import org.apache.xml.utils.StopParseException;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.XMLFilter;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* The TransformerFactoryImpl, which implements the TRaX TransformerFactory
* interface, processes XSLT stylesheets into a Templates object
* (a StylesheetRoot).
*/
public class TransformerFactoryImpl extends SAXTransformerFactory
{
/**
* The path/filename of the property file: XSLTInfo.properties
* Maintenance note: see also
* org.apache.xpath.functions.FuncSystemProperty.XSLT_PROPERTIES
*/
public static final String XSLT_PROPERTIES =
"org/apache/xalan/res/XSLTInfo.properties";
/**
*
State of secure processing feature.
*/ private boolean m_isSecureProcessing = false; /** * Constructor TransformerFactoryImpl * */ public TransformerFactoryImpl() { } /** Static string to be used for incremental feature */ public static final String FEATURE_INCREMENTAL = "http://xml.apache.org/xalan/features/incremental"; /** Static string to be used for optimize feature */ public static final String FEATURE_OPTIMIZE = "http://xml.apache.org/xalan/features/optimize"; /** Static string to be used for source_location feature */ public static final String FEATURE_SOURCE_LOCATION = XalanProperties.SOURCE_LOCATION; public javax.xml.transform.Templates processFromNode(Node node) throws TransformerConfigurationException { try { TemplatesHandler builder = newTemplatesHandler(); TreeWalker walker = new TreeWalker(builder, new org.apache.xml.utils.DOM2Helper(), builder.getSystemId()); walker.traverse(node); return builder.getTemplates(); } catch (org.xml.sax.SAXException se) { if (m_errorListener != null) { try { m_errorListener.fatalError(new TransformerException(se)); } catch (TransformerConfigurationException ex) { throw ex; } catch (TransformerException ex) { throw new TransformerConfigurationException(ex); } return null; } else { // Should remove this later... but right now diagnostics from // TransformerConfigurationException are not good. // se.printStackTrace(); throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_PROCESSFROMNODE_FAILED, null), se); //"processFromNode failed", se); } } catch (TransformerConfigurationException tce) { // Assume it's already been reported to the error listener. throw tce; } /* catch (TransformerException tce) { // Assume it's already been reported to the error listener. throw new TransformerConfigurationException(tce.getMessage(), tce); }*/ catch (Exception e) { if (m_errorListener != null) { try { m_errorListener.fatalError(new TransformerException(e)); } catch (TransformerConfigurationException ex) { throw ex; } catch (TransformerException ex) { throw new TransformerConfigurationException(ex); } return null; } else { // Should remove this later... but right now diagnostics from // TransformerConfigurationException are not good. // se.printStackTrace(); throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_PROCESSFROMNODE_FAILED, null), e); //"processFromNode failed", //e); } } } /** * The systemID that was specified in * processFromNode(Node node, String systemID). */ private String m_DOMsystemID = null; /** * The systemID that was specified in * processFromNode(Node node, String systemID). * * @return The systemID, or null. */ String getDOMsystemID() { return m_DOMsystemID; } /** * Process the stylesheet from a DOM tree, if the * processor supports the "http://xml.org/trax/features/dom/input" * feature. * * @param node A DOM tree which must contain * valid transform instructions that this processor understands. * @param systemID The systemID from where xsl:includes and xsl:imports * should be resolved from. * * @return A Templates object capable of being used for transformation purposes. * * @throws TransformerConfigurationException */ javax.xml.transform.Templates processFromNode(Node node, String systemID) throws TransformerConfigurationException { m_DOMsystemID = systemID; return processFromNode(node); } /** * Get InputSource specification(s) that are associated with the * given document specified in the source param, * via the xml-stylesheet processing instruction * (see http://www.w3.org/TR/xml-stylesheet/), and that matches * the given criteria. Note that it is possible to return several stylesheets * that match the criteria, in which case they are applied as if they were * a list of imports or cascades. * *Note that DOM2 has it's own mechanism for discovering stylesheets. * Therefore, there isn't a DOM version of this method.
* * * @param source The XML source that is to be searched. * @param media The media attribute to be matched. May be null, in which * case the prefered templates will be used (i.e. alternate = no). * @param title The value of the title attribute to match. May be null. * @param charset The value of the charset attribute to match. May be null. * * @return A Source object capable of being used to create a Templates object. * * @throws TransformerConfigurationException */ public Source getAssociatedStylesheet( Source source, String media, String title, String charset) throws TransformerConfigurationException { String baseID; InputSource isource = null; Node node = null; XMLReader reader = null; if (source instanceof DOMSource) { DOMSource dsource = (DOMSource) source; node = dsource.getNode(); baseID = dsource.getSystemId(); } else { isource = SAXSource.sourceToInputSource(source); baseID = isource.getSystemId(); } // What I try to do here is parse until the first startElement // is found, then throw a special exception in order to terminate // the parse. StylesheetPIHandler handler = new StylesheetPIHandler(baseID, media, title, charset); // Use URIResolver. Patch from Dmitri Ilyin if (m_uriResolver != null) { handler.setURIResolver(m_uriResolver); } try { if (null != node) { TreeWalker walker = new TreeWalker(handler, new org.apache.xml.utils.DOM2Helper(), baseID); walker.traverse(node); } else { // Use JAXP1.1 ( if possible ) try { javax.xml.parsers.SAXParserFactory factory = javax.xml.parsers.SAXParserFactory.newInstance(); factory.setNamespaceAware(true); if (m_isSecureProcessing) { try { factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); } catch (org.xml.sax.SAXException e) {} } javax.xml.parsers.SAXParser jaxpParser = factory.newSAXParser(); reader = jaxpParser.getXMLReader(); } catch (javax.xml.parsers.ParserConfigurationException ex) { throw new org.xml.sax.SAXException(ex); } catch (javax.xml.parsers.FactoryConfigurationError ex1) { throw new org.xml.sax.SAXException(ex1.toString()); } catch (NoSuchMethodError ex2){} catch (AbstractMethodError ame){} if (null == reader) { reader = XMLReaderFactory.createXMLReader(); } // Need to set options! reader.setContentHandler(handler); reader.parse(isource); } } catch (StopParseException spe) { // OK, good. } catch (org.xml.sax.SAXException se) { throw new TransformerConfigurationException( "getAssociatedStylesheets failed", se); } catch (IOException ioe) { throw new TransformerConfigurationException( "getAssociatedStylesheets failed", ioe); } return handler.getAssociatedStylesheet(); } /** * Create a new Transformer object that performs a copy * of the source to the result. * * @return A Transformer object that may be used to perform a transformation * in a single thread, never null. * * @throws TransformerConfigurationException May throw this during * the parse when it is constructing the * Templates object and fails. */ public TemplatesHandler newTemplatesHandler() throws TransformerConfigurationException { return new StylesheetHandler(this); } /** *Set a feature for this TransformerFactory
and Transformer
s
* or Template
s created by this factory.
* Feature names are fully qualified {@link java.net.URI}s.
* Implementations may define their own features.
* An {@link TransformerConfigurationException} is thrown if this TransformerFactory
or the
* Transformer
s or Template
s it creates cannot support the feature.
* It is possible for an TransformerFactory
to expose a feature value but be unable to change its state.
*
See {@link javax.xml.transform.TransformerFactory} for full documentation of specific features.
* * @param name Feature name. * @param value Is feature statetrue
or false
.
*
* @throws TransformerConfigurationException if this TransformerFactory
* or the Transformer
s or Template
s it creates cannot support this feature.
* @throws NullPointerException If the name
parameter is null.
*/
public void setFeature(String name, boolean value)
throws TransformerConfigurationException {
// feature name cannot be null
if (name == null) {
throw new NullPointerException(
XSLMessages.createMessage(
XSLTErrorResources.ER_SET_FEATURE_NULL_NAME, null));
}
// secure processing?
if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
m_isSecureProcessing = value;
}
// This implementation does not support the setting of a feature other than
// the secure processing feature.
else
{
throw new TransformerConfigurationException(
XSLMessages.createMessage(
XSLTErrorResources.ER_UNSUPPORTED_FEATURE,
new Object[] {name}));
}
}
/**
* Look up the value of a feature.
* The feature name is any fully-qualified URI. It is * possible for an TransformerFactory to recognize a feature name but * to be unable to return its value; this is especially true * in the case of an adapter for a SAX1 Parser, which has * no way of knowing whether the underlying parser is * validating, for example.
* * @param name The feature name, which is a fully-qualified URI. * @return The current state of the feature (true or false). */ public boolean getFeature(String name) { // feature name cannot be null if (name == null) { throw new NullPointerException( XSLMessages.createMessage( XSLTErrorResources.ER_GET_FEATURE_NULL_NAME, null)); } // Try first with identity comparison, which // will be faster. if ((DOMResult.FEATURE == name) || (DOMSource.FEATURE == name) || (SAXResult.FEATURE == name) || (SAXSource.FEATURE == name) || (StreamResult.FEATURE == name) || (StreamSource.FEATURE == name) || (SAXTransformerFactory.FEATURE == name) || (SAXTransformerFactory.FEATURE_XMLFILTER == name)) return true; else if ((DOMResult.FEATURE.equals(name)) || (DOMSource.FEATURE.equals(name)) || (SAXResult.FEATURE.equals(name)) || (SAXSource.FEATURE.equals(name)) || (StreamResult.FEATURE.equals(name)) || (StreamSource.FEATURE.equals(name)) || (SAXTransformerFactory.FEATURE.equals(name)) || (SAXTransformerFactory.FEATURE_XMLFILTER.equals(name))) return true; // secure processing? else if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) return m_isSecureProcessing; else // unknown feature return false; } /** * Flag set by FEATURE_OPTIMIZE. * This feature specifies whether to Optimize stylesheet processing. By * default it is set to true. */ private boolean m_optimize = true; /** Flag set by FEATURE_SOURCE_LOCATION. * This feature specifies whether the transformation phase should * keep track of line and column numbers for the input source * document. Note that this works only when that * information is available from the source -- in other words, if you * pass in a DOM, there's little we can do for you. * * The default is false. Setting it true may significantly * increase storage cost per node. */ private boolean m_source_location = false; /** * Flag set by FEATURE_INCREMENTAL. * This feature specifies whether to produce output incrementally, rather than * waiting to finish parsing the input before generating any output. By * default this attribute is set to false. */ private boolean m_incremental = false; /** * Allows the user to set specific attributes on the underlying * implementation. * * @param name The name of the attribute. * @param value The value of the attribute; Boolean or String="true"|"false" * * @throws IllegalArgumentException thrown if the underlying * implementation doesn't recognize the attribute. */ public void setAttribute(String name, Object value) throws IllegalArgumentException { if (name.equals(FEATURE_INCREMENTAL)) { if(value instanceof Boolean) { // Accept a Boolean object.. m_incremental = ((Boolean)value).booleanValue(); } else if(value instanceof String) { // .. or a String object m_incremental = (new Boolean((String)value)).booleanValue(); } else { // Give a more meaningful error message throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value); } } else if (name.equals(FEATURE_OPTIMIZE)) { if(value instanceof Boolean) { // Accept a Boolean object.. m_optimize = ((Boolean)value).booleanValue(); } else if(value instanceof String) { // .. or a String object m_optimize = (new Boolean((String)value)).booleanValue(); } else { // Give a more meaningful error message throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value); } } // Custom Xalan feature: annotate DTM with SAX source locator fields. // This gets used during SAX2DTM instantiation. // // %REVIEW% Should the name of this field really be in XalanProperties? // %REVIEW% I hate that it's a global static, but didn't want to change APIs yet. else if(name.equals(FEATURE_SOURCE_LOCATION)) { if(value instanceof Boolean) { // Accept a Boolean object.. m_source_location = ((Boolean)value).booleanValue(); } else if(value instanceof String) { // .. or a String object m_source_location = (new Boolean((String)value)).booleanValue(); } else { // Give a more meaningful error message throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value); } } else { throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NOT_SUPPORTED, new Object[]{name})); //name + "not supported"); } } /** * Allows the user to retrieve specific attributes on the underlying * implementation. * * @param name The name of the attribute. * @return value The value of the attribute. * * @throws IllegalArgumentException thrown if the underlying * implementation doesn't recognize the attribute. */ public Object getAttribute(String name) throws IllegalArgumentException { if (name.equals(FEATURE_INCREMENTAL)) { return new Boolean(m_incremental); } else if (name.equals(FEATURE_OPTIMIZE)) { return new Boolean(m_optimize); } else if (name.equals(FEATURE_SOURCE_LOCATION)) { return new Boolean(m_source_location); } else throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_ATTRIB_VALUE_NOT_RECOGNIZED, new Object[]{name})); //name + " attribute not recognized"); } /** * Create an XMLFilter that uses the given source as the * transformation instructions. * * @param src The source of the transformation instructions. * * @return An XMLFilter object, or null if this feature is not supported. * * @throws TransformerConfigurationException */ public XMLFilter newXMLFilter(Source src) throws TransformerConfigurationException { Templates templates = newTemplates(src); if( templates==null ) return null; return newXMLFilter(templates); } /** * Create an XMLFilter that uses the given source as the * transformation instructions. * * @param templates non-null reference to Templates object. * * @return An XMLFilter object, or null if this feature is not supported. * * @throws TransformerConfigurationException */ public XMLFilter newXMLFilter(Templates templates) throws TransformerConfigurationException { try { return new TrAXFilter(templates); } catch( TransformerConfigurationException ex ) { if( m_errorListener != null) { try { m_errorListener.fatalError( ex ); return null; } catch( TransformerConfigurationException ex1 ) { throw ex1; } catch( TransformerException ex1 ) { throw new TransformerConfigurationException(ex1); } } throw ex; } } /** * Get a TransformerHandler object that can process SAX * ContentHandler events into a Result, based on the transformation * instructions specified by the argument. * * @param src The source of the transformation instructions. * * @return TransformerHandler ready to transform SAX events. * * @throws TransformerConfigurationException */ public TransformerHandler newTransformerHandler(Source src) throws TransformerConfigurationException { Templates templates = newTemplates(src); if( templates==null ) return null; return newTransformerHandler(templates); } /** * Get a TransformerHandler object that can process SAX * ContentHandler events into a Result, based on the Templates argument. * * @param templates The source of the transformation instructions. * * @return TransformerHandler ready to transform SAX events. * @throws TransformerConfigurationException */ public TransformerHandler newTransformerHandler(Templates templates) throws TransformerConfigurationException { try { TransformerImpl transformer = (TransformerImpl) templates.newTransformer(); transformer.setURIResolver(m_uriResolver); TransformerHandler th = (TransformerHandler) transformer.getInputContentHandler(true); return th; } catch( TransformerConfigurationException ex ) { if( m_errorListener != null ) { try { m_errorListener.fatalError( ex ); return null; } catch (TransformerConfigurationException ex1 ) { throw ex1; } catch (TransformerException ex1 ) { throw new TransformerConfigurationException(ex1); } } throw ex; } } // /** The identity transform string, for support of newTransformerHandler() // * and newTransformer(). */ // private static final String identityTransform = // "