/* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed 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,v 1.73 2004/02/23 10:29:36 aruny Exp $ */ package com.sun.org.apache.xalan.internal.xsltc.trax; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; import java.util.Hashtable; import java.util.Properties; import java.util.Vector; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.xml.XMLConstants; import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParser; import javax.xml.parsers.ParserConfigurationException; 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 com.sun.org.apache.xml.internal.utils.StylesheetPIHandler; import com.sun.org.apache.xml.internal.utils.StopParseException; import com.sun.org.apache.xalan.internal.xsltc.compiler.SourceLoader; import com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; import com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager; import org.xml.sax.InputSource; import org.xml.sax.XMLFilter; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; /** * Implementation of a JAXP1.1 TransformerFactory for Translets. */ public class TransformerFactoryImpl extends SAXTransformerFactory implements SourceLoader, ErrorListener { /** *

Name of class as a constant to use for debugging.

*/ private static final String CLASS_NAME = "TransformerFactoryImpl"; // Public constants for attributes supported by the XSLTC TransformerFactory. public final static String TRANSLET_NAME = "translet-name"; public final static String DESTINATION_DIRECTORY = "destination-directory"; public final static String PACKAGE_NAME = "package-name"; public final static String JAR_NAME = "jar-name"; public final static String GENERATE_TRANSLET = "generate-translet"; public final static String AUTO_TRANSLET = "auto-translet"; public final static String USE_CLASSPATH = "use-classpath"; public final static String DEBUG = "debug"; public final static String ENABLE_INLINING = "enable-inlining"; public final static String INDENT_NUMBER = "indent-number"; /** * This error listener is used only for this factory and is not passed to * the Templates or Transformer objects that we create. */ private ErrorListener _errorListener = this; /** * This URIResolver is passed to all created Templates and Transformers */ private URIResolver _uriResolver = null; /** * As Gregor Samsa awoke one morning from uneasy dreams he found himself * transformed in his bed into a gigantic insect. He was lying on his hard, * as it were armour plated, back, and if he lifted his head a little he * could see his big, brown belly divided into stiff, arched segments, on * top of which the bed quilt could hardly keep in position and was about * to slide off completely. His numerous legs, which were pitifully thin * compared to the rest of his bulk, waved helplessly before his eyes. * "What has happened to me?", he thought. It was no dream.... */ protected static String DEFAULT_TRANSLET_NAME = "GregorSamsa"; /** * The class name of the translet */ private String _transletName = DEFAULT_TRANSLET_NAME; /** * The destination directory for the translet */ private String _destinationDirectory = null; /** * The package name prefix for all generated translet classes */ private String _packageName = null; /** * The jar file name which the translet classes are packaged into */ private String _jarFileName = null; /** * This Hashtable is used to store parameters for locating * processing instructions in XML docs. */ private Hashtable _piParams = null; /** * Use a thread local variable to store a copy of an XML Reader. */ static ThreadLocal _xmlReader = new ThreadLocal(); /** * The above hashtable stores objects of this class. */ private static class PIParamWrapper { public String _media = null; public String _title = null; public String _charset = null; public PIParamWrapper(String media, String title, String charset) { _media = media; _title = title; _charset = charset; } } /** * Set to true when debugging is enabled. */ private boolean _debug = false; /** * Set to true when templates are inlined. */ private boolean _enableInlining = false; /** * Set to true when we want to generate * translet classes from the stylesheet. */ private boolean _generateTranslet = false; /** * If this is set to true, we attempt to use translet classes * for transformation if possible without compiling the stylesheet. The * translet class is only used if its timestamp is newer than the timestamp * of the stylesheet. */ private boolean _autoTranslet = false; /** * If this is set to true, we attempt to load the translet * from the CLASSPATH. */ private boolean _useClasspath = false; /** * Number of indent spaces when indentation is turned on. */ private int _indentNumber = -1; /** * The provider of the XSLTC DTM Manager service. This is fixed for any * instance of this class. In order to change service providers, a new * XSLTC TransformerFactory must be instantiated. * @see XSLTCDTMManager#getDTMManagerClass() */ private Class m_DTMManagerClass; /** *

State of secure processing feature.

*/ private boolean featureSecureProcessing = false; /** * javax.xml.transform.sax.TransformerFactory implementation. */ public TransformerFactoryImpl() { m_DTMManagerClass = XSLTCDTMManager.getDTMManagerClass(); } /** * javax.xml.transform.sax.TransformerFactory implementation. * Set the error event listener for the TransformerFactory, which is used * for the processing of transformation instructions, and not for the * transformation itself. * * @param listener The error listener to use with the TransformerFactory * @throws IllegalArgumentException */ public void setErrorListener(ErrorListener listener) throws IllegalArgumentException { if (listener == null) { ErrorMsg err = new ErrorMsg(ErrorMsg.ERROR_LISTENER_NULL_ERR, "TransformerFactory"); throw new IllegalArgumentException(err.toString()); } _errorListener = listener; } /** * javax.xml.transform.sax.TransformerFactory implementation. * Get the error event handler for the TransformerFactory. * * @return The error listener used with the TransformerFactory */ public ErrorListener getErrorListener() { return _errorListener; } /** * javax.xml.transform.sax.TransformerFactory implementation. * Returns the value set for a TransformerFactory attribute * * @param name The attribute name * @return An object representing the attribute value * @throws IllegalArgumentException */ public Object getAttribute(String name) throws IllegalArgumentException { // Return value for attribute 'translet-name' if (name.equals(TRANSLET_NAME)) { return _transletName; } else if (name.equals(GENERATE_TRANSLET)) { return new Boolean(_generateTranslet); } else if (name.equals(AUTO_TRANSLET)) { return new Boolean(_autoTranslet); } // Throw an exception for all other attributes ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_INVALID_ATTR_ERR, name); throw new IllegalArgumentException(err.toString()); } /** * javax.xml.transform.sax.TransformerFactory implementation. * Sets the value for a TransformerFactory attribute. * * @param name The attribute name * @param value An object representing the attribute value * @throws IllegalArgumentException */ public void setAttribute(String name, Object value) throws IllegalArgumentException { // Set the default translet name (ie. class name), which will be used // for translets that cannot be given a name from their system-id. if (name.equals(TRANSLET_NAME) && value instanceof String) { _transletName = (String) value; return; } else if (name.equals(DESTINATION_DIRECTORY) && value instanceof String) { _destinationDirectory = (String) value; return; } else if (name.equals(PACKAGE_NAME) && value instanceof String) { _packageName = (String) value; return; } else if (name.equals(JAR_NAME) && value instanceof String) { _jarFileName = (String) value; return; } else if (name.equals(GENERATE_TRANSLET)) { if (value instanceof Boolean) { _generateTranslet = ((Boolean) value).booleanValue(); return; } else if (value instanceof String) { _generateTranslet = ((String) value).equalsIgnoreCase("true"); return; } } else if (name.equals(AUTO_TRANSLET)) { if (value instanceof Boolean) { _autoTranslet = ((Boolean) value).booleanValue(); return; } else if (value instanceof String) { _autoTranslet = ((String) value).equalsIgnoreCase("true"); return; } } else if (name.equals(USE_CLASSPATH)) { if (value instanceof Boolean) { _useClasspath = ((Boolean) value).booleanValue(); return; } else if (value instanceof String) { _useClasspath = ((String) value).equalsIgnoreCase("true"); return; } } else if (name.equals(DEBUG)) { if (value instanceof Boolean) { _debug = ((Boolean) value).booleanValue(); return; } else if (value instanceof String) { _debug = ((String) value).equalsIgnoreCase("true"); return; } } else if (name.equals(ENABLE_INLINING)) { if (value instanceof Boolean) { _enableInlining = ((Boolean) value).booleanValue(); return; } else if (value instanceof String) { _enableInlining = ((String) value).equalsIgnoreCase("true"); return; } } else if (name.equals(INDENT_NUMBER)) { if (value instanceof String) { try { _indentNumber = Integer.parseInt((String) value); return; } catch (NumberFormatException e) { // Falls through } } else if (value instanceof Integer) { _indentNumber = ((Integer) value).intValue(); return; } } // Throw an exception for all other attributes final ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_INVALID_ATTR_ERR, name); throw new IllegalArgumentException(err.toString()); } /** *

Set a feature for this TransformerFactory and Transformers * or Templates 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 * Transformers or Templates 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 state true or false. * * @throws TransformerConfigurationException if this TransformerFactory * or the Transformers or Templates 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( "Trying to set a feature with a null name: " + CLASS_NAME + "#setFeature(null, " + value + ")" ); } // secure processing? if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) { featureSecureProcessing = value; // all done processing feature return; } // unknown feature throw new TransformerConfigurationException( "Trying to set the unknown feature \"" + name + "\": " + CLASS_NAME + "#setFeature(" + name + ", " + value + ")" ); } /** * javax.xml.transform.sax.TransformerFactory implementation. * Look up the value of a feature (to see if it is supported). * This method must be updated as the various methods and features of this * class are implemented. * * @param name The feature name * @return 'true' if feature is supported, 'false' if not */ public boolean getFeature(String name) { // All supported features should be listed here String[] features = { DOMSource.FEATURE, DOMResult.FEATURE, SAXSource.FEATURE, SAXResult.FEATURE, StreamSource.FEATURE, StreamResult.FEATURE, SAXTransformerFactory.FEATURE, SAXTransformerFactory.FEATURE_XMLFILTER }; // feature name cannot be null if (name == null) { throw new NullPointerException( "Trying to get a feature with a null name: " + CLASS_NAME + "#getFeature(null)"); } // Inefficient, but array is small for (int i =0; i < features.length; i++) { if (name.equals(features[i])) { return true; } } // secure processing? if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) { return featureSecureProcessing; } // Feature not supported return false; } /** * javax.xml.transform.sax.TransformerFactory implementation. * Get the object that is used by default during the transformation to * resolve URIs used in document(), xsl:import, or xsl:include. * * @return The URLResolver used for this TransformerFactory and all * Templates and Transformer objects created using this factory */ public URIResolver getURIResolver() { return _uriResolver; } /** * javax.xml.transform.sax.TransformerFactory implementation. * Set the object that is used by default during the transformation to * resolve URIs used in document(), xsl:import, or xsl:include. Note that * this does not affect Templates and Transformers that are already * created with this factory. * * @param resolver The URLResolver used for this TransformerFactory and all * Templates and Transformer objects created using this factory */ public void setURIResolver(URIResolver resolver) { _uriResolver = resolver; } /** * javax.xml.transform.sax.TransformerFactory implementation. * Get the stylesheet specification(s) associated via the xml-stylesheet * processing instruction (see http://www.w3.org/TR/xml-stylesheet/) with * the document document specified in the source parameter, and that match * the given criteria. * * @param source The XML source document. * @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 suitable for passing to the TransformerFactory. * @throws TransformerConfigurationException */ public Source getAssociatedStylesheet(Source source, String media, String title, String charset) throws TransformerConfigurationException { String baseId; XMLReader reader = null; InputSource isource = null; /** * Fix for bugzilla bug 24187 */ StylesheetPIHandler _stylesheetPIHandler = new StylesheetPIHandler(null,media,title,charset); try { if (source instanceof DOMSource ) { final DOMSource domsrc = (DOMSource) source; baseId = domsrc.getSystemId(); final org.w3c.dom.Node node = domsrc.getNode(); final DOM2SAX dom2sax = new DOM2SAX(node); _stylesheetPIHandler.setBaseId(baseId); dom2sax.setContentHandler( _stylesheetPIHandler); dom2sax.parse(); } else { isource = SAXSource.sourceToInputSource(source); baseId = isource.getSystemId(); SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); SAXParser jaxpParser = factory.newSAXParser(); reader = jaxpParser.getXMLReader(); if (reader == null) { reader = XMLReaderFactory.createXMLReader(); } _stylesheetPIHandler.setBaseId(baseId); reader.setContentHandler(_stylesheetPIHandler); reader.parse(isource); } if (_uriResolver != null ) { _stylesheetPIHandler.setURIResolver(_uriResolver); } } catch (StopParseException e ) { // startElement encountered so do not parse further } catch (javax.xml.parsers.ParserConfigurationException e) { throw new TransformerConfigurationException( "getAssociatedStylesheets failed", e); } catch (org.xml.sax.SAXException se) { throw new TransformerConfigurationException( "getAssociatedStylesheets failed", se); } catch (IOException ioe ) { throw new TransformerConfigurationException( "getAssociatedStylesheets failed", ioe); } return _stylesheetPIHandler.getAssociatedStylesheet(); } /** * javax.xml.transform.sax.TransformerFactory implementation. * Create a Transformer object that copies the input document to the result. * * @return A Transformer object that simply copies the source to the result. * @throws TransformerConfigurationException */ public Transformer newTransformer() throws TransformerConfigurationException { TransformerImpl result = new TransformerImpl(new Properties(), _indentNumber, this); if (_uriResolver != null) { result.setURIResolver(_uriResolver); } return result; } /** * javax.xml.transform.sax.TransformerFactory implementation. * Process the Source into a Templates object, which is a a compiled * representation of the source. Note that this method should not be * used with XSLTC, as the time-consuming compilation is done for each * and every transformation. * * @return A Templates object that can be used to create Transformers. * @throws TransformerConfigurationException */ public Transformer newTransformer(Source source) throws TransformerConfigurationException { final Templates templates = newTemplates(source); final Transformer transformer = templates.newTransformer(); if (_uriResolver != null) { transformer.setURIResolver(_uriResolver); } return(transformer); } /** * Pass warning messages from the compiler to the error listener */ private void passWarningsToListener(Vector messages) throws TransformerException { if (_errorListener == null || messages == null) { return; } // Pass messages to listener, one by one final int count = messages.size(); for (int pos = 0; pos < count; pos++) { String message = messages.elementAt(pos).toString(); _errorListener.error( new TransformerConfigurationException(message)); } } /** * Pass error messages from the compiler to the error listener */ private void passErrorsToListener(Vector messages) { try { if (_errorListener == null || messages == null) { return; } // Pass messages to listener, one by one final int count = messages.size(); for (int pos = 0; pos < count; pos++) { String message = messages.elementAt(pos).toString(); _errorListener.error(new TransformerException(message)); } } catch (TransformerException e) { // nada } } /** * javax.xml.transform.sax.TransformerFactory implementation. * Process the Source into a Templates object, which is a a compiled * representation of the source. * * @param stylesheet The input stylesheet - DOMSource not supported!!! * @return A Templates object that can be used to create Transformers. * @throws TransformerConfigurationException */ public Templates newTemplates(Source source) throws TransformerConfigurationException { // If the _useClasspath attribute is true, try to load the translet from // the CLASSPATH and create a template object using the loaded // translet. if (_useClasspath) { String transletName = getTransletBaseName(source); if (_packageName != null) transletName = _packageName + "." + transletName; try { final Class clazz = ObjectFactory.findProviderClass( transletName, ObjectFactory.findClassLoader(), true); resetTransientAttributes(); return new TemplatesImpl(new Class[]{clazz}, transletName, null, _indentNumber, this); } catch (ClassNotFoundException cnfe) { ErrorMsg err = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, transletName); throw new TransformerConfigurationException(err.toString()); } catch (Exception e) { ErrorMsg err = new ErrorMsg( new ErrorMsg(ErrorMsg.RUNTIME_ERROR_KEY) + e.getMessage()); throw new TransformerConfigurationException(err.toString()); } } // If _autoTranslet is true, we will try to load the bytecodes // from the translet classes without compiling the stylesheet. if (_autoTranslet) { byte[][] bytecodes = null; String transletClassName = getTransletBaseName(source); if (_packageName != null) transletClassName = _packageName + "." + transletClassName; if (_jarFileName != null) bytecodes = getBytecodesFromJar(source, transletClassName); else bytecodes = getBytecodesFromClasses(source, transletClassName); if (bytecodes != null) { if (_debug) { if (_jarFileName != null) System.err.println(new ErrorMsg( ErrorMsg.TRANSFORM_WITH_JAR_STR, transletClassName, _jarFileName)); else System.err.println(new ErrorMsg( ErrorMsg.TRANSFORM_WITH_TRANSLET_STR, transletClassName)); } // Reset the per-session attributes to their default values // after each newTemplates() call. resetTransientAttributes(); return new TemplatesImpl(bytecodes, transletClassName, null, _indentNumber, this); } } // Create and initialize a stylesheet compiler final XSLTC xsltc = new XSLTC(); if (_debug) xsltc.setDebug(true); if (_enableInlining) xsltc.setTemplateInlining(true); xsltc.init(); // Set a document loader (for xsl:include/import) if defined if (_uriResolver != null) { xsltc.setSourceLoader(this); } // Pass parameters to the Parser to make sure it locates the correct // PI in an XML input document if ((_piParams != null) && (_piParams.get(source) != null)) { // Get the parameters for this Source object PIParamWrapper p = (PIParamWrapper)_piParams.get(source); // Pass them on to the compiler (which will pass then to the parser) if (p != null) { xsltc.setPIParameters(p._media, p._title, p._charset); } } // Set the attributes for translet generation int outputType = XSLTC.BYTEARRAY_OUTPUT; if (_generateTranslet || _autoTranslet) { // Set the translet name xsltc.setClassName(getTransletBaseName(source)); if (_destinationDirectory != null) xsltc.setDestDirectory(_destinationDirectory); else { String xslName = getStylesheetFileName(source); if (xslName != null) { File xslFile = new File(xslName); String xslDir = xslFile.getParent(); if (xslDir != null) xsltc.setDestDirectory(xslDir); } } if (_packageName != null) xsltc.setPackageName(_packageName); if (_jarFileName != null) { xsltc.setJarFileName(_jarFileName); outputType = XSLTC.BYTEARRAY_AND_JAR_OUTPUT; } else outputType = XSLTC.BYTEARRAY_AND_FILE_OUTPUT; } // Compile the stylesheet final InputSource input = Util.getInputSource(xsltc, source); byte[][] bytecodes = xsltc.compile(null, input, outputType); final String transletName = xsltc.getClassName(); // Output to the jar file if the jar file name is set. if ((_generateTranslet || _autoTranslet) && bytecodes != null && _jarFileName != null) { try { xsltc.outputToJar(); } catch (java.io.IOException e) { } } // Reset the per-session attributes to their default values // after each newTemplates() call. resetTransientAttributes(); // Pass compiler warnings to the error listener if (_errorListener != this) { try { passWarningsToListener(xsltc.getWarnings()); } catch (TransformerException e) { throw new TransformerConfigurationException(e); } } else { xsltc.printWarnings(); } // Check that the transformation went well before returning if (bytecodes == null) { ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR); TransformerConfigurationException exc = new TransformerConfigurationException(err.toString()); // Pass compiler errors to the error listener if (_errorListener != null) { passErrorsToListener(xsltc.getErrors()); // As required by TCK 1.2, send a fatalError to the // error listener because compilation of the stylesheet // failed and no further processing will be possible. try { _errorListener.fatalError(exc); } catch (TransformerException te) { // well, we tried. } } else { xsltc.printErrors(); } throw exc; } return new TemplatesImpl(bytecodes, transletName, xsltc.getOutputProperties(), _indentNumber, this); } /** * javax.xml.transform.sax.SAXTransformerFactory implementation. * Get a TemplatesHandler object that can process SAX ContentHandler * events into a Templates object. * * @return A TemplatesHandler object that can handle SAX events * @throws TransformerConfigurationException */ public TemplatesHandler newTemplatesHandler() throws TransformerConfigurationException { final TemplatesHandlerImpl handler = new TemplatesHandlerImpl(_indentNumber, this); if (_uriResolver != null) { handler.setURIResolver(_uriResolver); } return handler; } /** * javax.xml.transform.sax.SAXTransformerFactory implementation. * Get a TransformerHandler object that can process SAX ContentHandler * events into a Result. This method will return a pure copy transformer. * * @return A TransformerHandler object that can handle SAX events * @throws TransformerConfigurationException */ public TransformerHandler newTransformerHandler() throws TransformerConfigurationException { final Transformer transformer = newTransformer(); if (_uriResolver != null) { transformer.setURIResolver(_uriResolver); } return new TransformerHandlerImpl((TransformerImpl) transformer); } /** * javax.xml.transform.sax.SAXTransformerFactory implementation. * 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 A TransformerHandler object that can handle SAX events * @throws TransformerConfigurationException */ public TransformerHandler newTransformerHandler(Source src) throws TransformerConfigurationException { final Transformer transformer = newTransformer(src); if (_uriResolver != null) { transformer.setURIResolver(_uriResolver); } return new TransformerHandlerImpl((TransformerImpl) transformer); } /** * javax.xml.transform.sax.SAXTransformerFactory implementation. * Get a TransformerHandler object that can process SAX ContentHandler * events into a Result, based on the transformation instructions * specified by the argument. * * @param templates Represents a pre-processed stylesheet * @return A TransformerHandler object that can handle SAX events * @throws TransformerConfigurationException */ public TransformerHandler newTransformerHandler(Templates templates) throws TransformerConfigurationException { final Transformer transformer = templates.newTransformer(); final TransformerImpl internal = (TransformerImpl)transformer; return new TransformerHandlerImpl(internal); } /** * javax.xml.transform.sax.SAXTransformerFactory implementation. * 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); } /** * javax.xml.transform.sax.SAXTransformerFactory implementation. * 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(Templates templates) throws TransformerConfigurationException { try { return new com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter(templates); } catch (TransformerConfigurationException e1) { if (_errorListener != null) { try { _errorListener.fatalError(e1); return null; } catch (TransformerException e2) { new TransformerConfigurationException(e2); } } throw e1; } } /** * Receive notification of a recoverable error. * The transformer must continue to provide normal parsing events after * invoking this method. It should still be possible for the application * to process the document through to the end. * * @param exception The warning information encapsulated in a transformer * exception. * @throws TransformerException if the application chooses to discontinue * the transformation (always does in our case). */ public void error(TransformerException e) throws TransformerException { Throwable wrapped = e.getException(); if (wrapped != null) { System.err.println(new ErrorMsg(ErrorMsg.ERROR_PLUS_WRAPPED_MSG, e.getMessageAndLocation(), wrapped.getMessage())); } else { System.err.println(new ErrorMsg(ErrorMsg.ERROR_MSG, e.getMessageAndLocation())); } throw e; } /** * Receive notification of a non-recoverable error. * The application must assume that the transformation cannot continue * after the Transformer has invoked this method, and should continue * (if at all) only to collect addition error messages. In fact, * Transformers are free to stop reporting events once this method has * been invoked. * * @param exception The warning information encapsulated in a transformer * exception. * @throws TransformerException if the application chooses to discontinue * the transformation (always does in our case). */ public void fatalError(TransformerException e) throws TransformerException { Throwable wrapped = e.getException(); if (wrapped != null) { System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_PLUS_WRAPPED_MSG, e.getMessageAndLocation(), wrapped.getMessage())); } else { System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_MSG, e.getMessageAndLocation())); } throw e; } /** * Receive notification of a warning. * Transformers can use this method to report conditions that are not * errors or fatal errors. The default behaviour is to take no action. * After invoking this method, the Transformer must continue with the * transformation. It should still be possible for the application to * process the document through to the end. * * @param exception The warning information encapsulated in a transformer * exception. * @throws TransformerException if the application chooses to discontinue * the transformation (never does in our case). */ public void warning(TransformerException e) throws TransformerException { Throwable wrapped = e.getException(); if (wrapped != null) { System.err.println(new ErrorMsg(ErrorMsg.WARNING_PLUS_WRAPPED_MSG, e.getMessageAndLocation(), wrapped.getMessage())); } else { System.err.println(new ErrorMsg(ErrorMsg.WARNING_MSG, e.getMessageAndLocation())); } } /** * This method implements XSLTC's SourceLoader interface. It is used to * glue a TrAX URIResolver to the XSLTC compiler's Input and Import classes. * * @param href The URI of the document to load * @param context The URI of the currently loaded document * @param xsltc The compiler that resuests the document * @return An InputSource with the loaded document */ public InputSource loadSource(String href, String context, XSLTC xsltc) { try { if (_uriResolver != null) { final Source source = _uriResolver.resolve(href, context); if (source != null) { return Util.getInputSource(xsltc, source); } } } catch (TransformerException e) { // Falls through } return null; } /** * Reset the per-session attributes to their default values */ private void resetTransientAttributes() { _transletName = DEFAULT_TRANSLET_NAME; _destinationDirectory = null; _packageName = null; _jarFileName = null; } /** * Load the translet classes from local .class files and return * the bytecode array. * * @param source The xsl source * @param fullClassName The full name of the translet * @return The bytecode array */ private byte[][] getBytecodesFromClasses(Source source, String fullClassName) { if (fullClassName == null) return null; String xslFileName = getStylesheetFileName(source); File xslFile = null; if (xslFileName != null) xslFile = new File(xslFileName); // Find the base name of the translet final String transletName; int lastDotIndex = fullClassName.lastIndexOf('.'); if (lastDotIndex > 0) transletName = fullClassName.substring(lastDotIndex+1); else transletName = fullClassName; // Construct the path name for the translet class file String transletPath = fullClassName.replace('.', '/'); if (_destinationDirectory != null) { transletPath = _destinationDirectory + "/" + transletPath + ".class"; } else { if (xslFile != null && xslFile.getParent() != null) transletPath = xslFile.getParent() + "/" + transletPath + ".class"; else transletPath = transletPath + ".class"; } // Return null if the translet class file does not exist. File transletFile = new File(transletPath); if (!transletFile.exists()) return null; // Compare the timestamps of the translet and the xsl file. // If the translet is older than the xsl file, return null // so that the xsl file is used for the transformation and // the translet is regenerated. if (xslFile != null && xslFile.exists()) { long xslTimestamp = xslFile.lastModified(); long transletTimestamp = transletFile.lastModified(); if (transletTimestamp < xslTimestamp) return null; } // Load the translet into a bytecode array. Vector bytecodes = new Vector(); int fileLength = (int)transletFile.length(); if (fileLength > 0) { FileInputStream input = null; try { input = new FileInputStream(transletFile); } catch (FileNotFoundException e) { return null; } byte[] bytes = new byte[fileLength]; try { readFromInputStream(bytes, input, fileLength); input.close(); } catch (IOException e) { return null; } bytecodes.addElement(bytes); } else return null; // Find the parent directory of the translet. String transletParentDir = transletFile.getParent(); if (transletParentDir == null) transletParentDir = System.getProperty("user.dir"); File transletParentFile = new File(transletParentDir); // Find all the auxiliary files which have a name pattern of "transletClass$nnn.class". final String transletAuxPrefix = transletName + "$"; File[] auxfiles = transletParentFile.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { return (name.endsWith(".class") && name.startsWith(transletAuxPrefix)); } }); // Load the auxiliary class files and add them to the bytecode array. for (int i = 0; i < auxfiles.length; i++) { File auxfile = auxfiles[i]; int auxlength = (int)auxfile.length(); if (auxlength > 0) { FileInputStream auxinput = null; try { auxinput = new FileInputStream(auxfile); } catch (FileNotFoundException e) { continue; } byte[] bytes = new byte[auxlength]; try { readFromInputStream(bytes, auxinput, auxlength); auxinput.close(); } catch (IOException e) { continue; } bytecodes.addElement(bytes); } } // Convert the Vector of byte[] to byte[][]. final int count = bytecodes.size(); if ( count > 0) { final byte[][] result = new byte[count][1]; for (int i = 0; i < count; i++) { result[i] = (byte[])bytecodes.elementAt(i); } return result; } else return null; } /** * Load the translet classes from the jar file and return the bytecode. * * @param source The xsl source * @param fullClassName The full name of the translet * @return The bytecode array */ private byte[][] getBytecodesFromJar(Source source, String fullClassName) { String xslFileName = getStylesheetFileName(source); File xslFile = null; if (xslFileName != null) xslFile = new File(xslFileName); // Construct the path for the jar file String jarPath = null; if (_destinationDirectory != null) jarPath = _destinationDirectory + "/" + _jarFileName; else { if (xslFile != null && xslFile.getParent() != null) jarPath = xslFile.getParent() + "/" + _jarFileName; else jarPath = _jarFileName; } // Return null if the jar file does not exist. File file = new File(jarPath); if (!file.exists()) return null; // Compare the timestamps of the jar file and the xsl file. Return null // if the xsl file is newer than the jar file. if (xslFile != null && xslFile.exists()) { long xslTimestamp = xslFile.lastModified(); long transletTimestamp = file.lastModified(); if (transletTimestamp < xslTimestamp) return null; } // Create a ZipFile object for the jar file ZipFile jarFile = null; try { jarFile = new ZipFile(file); } catch (IOException e) { return null; } String transletPath = fullClassName.replace('.', '/'); String transletAuxPrefix = transletPath + "$"; String transletFullName = transletPath + ".class"; Vector bytecodes = new Vector(); // Iterate through all entries in the jar file to find the // translet and auxiliary classes. Enumeration entries = jarFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = (ZipEntry)entries.nextElement(); String entryName = entry.getName(); if (entry.getSize() > 0 && (entryName.equals(transletFullName) || (entryName.endsWith(".class") && entryName.startsWith(transletAuxPrefix)))) { try { InputStream input = jarFile.getInputStream(entry); int size = (int)entry.getSize(); byte[] bytes = new byte[size]; readFromInputStream(bytes, input, size); input.close(); bytecodes.addElement(bytes); } catch (IOException e) { return null; } } } // Convert the Vector of byte[] to byte[][]. final int count = bytecodes.size(); if (count > 0) { final byte[][] result = new byte[count][1]; for (int i = 0; i < count; i++) { result[i] = (byte[])bytecodes.elementAt(i); } return result; } else return null; } /** * Read a given number of bytes from the InputStream into a byte array. * * @param bytes The byte array to store the input content. * @param input The input stream. * @param size The number of bytes to read. */ private void readFromInputStream(byte[] bytes, InputStream input, int size) throws IOException { int n = 0; int offset = 0; int length = size; while (length > 0 && (n = input.read(bytes, offset, length)) > 0) { offset = offset + n; length = length - n; } } /** * Return the base class name of the translet. * The translet name is resolved using the following rules: * 1. if the _transletName attribute is set and its value is not "GregorSamsa", * then _transletName is returned. * 2. otherwise get the translet name from the base name of the system ID * 3. return "GregorSamsa" if the result from step 2 is null. * * @param source The input Source * @return The name of the translet class */ private String getTransletBaseName(Source source) { String transletBaseName = null; if (!_transletName.equals(DEFAULT_TRANSLET_NAME)) return _transletName; else { String systemId = source.getSystemId(); if (systemId != null) { String baseName = Util.baseName(systemId); if (baseName != null) { baseName = Util.noExtName(baseName); transletBaseName = Util.toJavaName(baseName); } } } return (transletBaseName != null) ? transletBaseName : DEFAULT_TRANSLET_NAME; } /** * Return the local file name from the systemId of the Source object * * @param source The Source * @return The file name in the local filesystem, or null if the * systemId does not represent a local file. */ private String getStylesheetFileName(Source source) { String systemId = source.getSystemId(); if (systemId != null) { File file = new File(systemId); if (file.exists()) return systemId; else { URL url = null; try { url = new URL(systemId); } catch (MalformedURLException e) { return null; } if ("file".equals(url.getProtocol())) return url.getFile(); else return null; } } else return null; } /** * Returns the Class object the provides the XSLTC DTM Manager service. */ protected Class getDTMManagerClass() { return m_DTMManagerClass; } }