| /* |
| * @(#)$Id$ |
| * |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 2001 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xalan" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 2001, Sun |
| * Microsystems., http://www.sun.com. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| * |
| * @author G. Todd Miller |
| * @author Morten Jorgensen |
| * @author Santiago Pericas-Geertsen |
| * |
| */ |
| |
| package org.apache.xalan.xsltc.trax; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.FileNotFoundException; |
| import java.io.Reader; |
| import java.io.InputStream; |
| import java.io.FileInputStream; |
| import java.io.ByteArrayInputStream; |
| import java.io.FilenameFilter; |
| import java.net.URL; |
| import java.net.MalformedURLException; |
| import java.util.Vector; |
| import java.util.Hashtable; |
| import java.util.Properties; |
| import java.util.Enumeration; |
| import java.util.zip.ZipFile; |
| import java.util.zip.ZipEntry; |
| |
| import javax.xml.transform.*; |
| import javax.xml.transform.sax.*; |
| import javax.xml.transform.dom.*; |
| import javax.xml.transform.stream.*; |
| import javax.xml.parsers.SAXParserFactory; |
| |
| import org.xml.sax.*; |
| import org.w3c.dom.Document; |
| |
| import org.apache.xalan.xsltc.Translet; |
| import org.apache.xalan.xsltc.runtime.AbstractTranslet; |
| |
| import org.apache.xalan.xsltc.compiler.XSLTC; |
| import org.apache.xalan.xsltc.compiler.SourceLoader; |
| import org.apache.xalan.xsltc.compiler.CompilerException; |
| import org.apache.xalan.xsltc.compiler.util.ErrorMsg; |
| |
| /** |
| * Implementation of a JAXP1.1 TransformerFactory for Translets. |
| */ |
| public class TransformerFactoryImpl |
| extends SAXTransformerFactory implements SourceLoader, ErrorListener |
| { |
| /** |
| * 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 |
| * <?xml-stylesheet ...?> 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 <code>true</code> when debugging is enabled. |
| */ |
| private boolean _debug = false; |
| |
| /** |
| * Set to <code>true</code> when templates are inlined. |
| */ |
| private boolean _enableInlining = false; |
| |
| /** |
| * Set to <code>true</code> when we want to generate |
| * translet classes from the stylesheet. |
| */ |
| private boolean _generateTranslet = false; |
| |
| /** |
| * If this is set to <code>true</code>, 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; |
| |
| /** |
| * Number of indent spaces when indentation is turned on. |
| */ |
| private int _indentNumber = -1; |
| |
| /** |
| * A reference to a SAXParserFactory. |
| */ |
| private SAXParserFactory _parserFactory = null; |
| |
| /** |
| * javax.xml.transform.sax.TransformerFactory implementation. |
| * Contains nothing yet |
| */ |
| public TransformerFactoryImpl() { |
| } |
| |
| /** |
| * 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("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()); |
| } |
| |
| /** |
| * 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 |
| }; |
| |
| // Inefficient, but array is small |
| for (int i =0; i < features.length; i++) { |
| if (name.equals(features[i])) { |
| return true; |
| } |
| } |
| // 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 |
| { |
| // First create a hashtable that maps Source refs. to parameters |
| if (_piParams == null) { |
| _piParams = new Hashtable(); |
| } |
| // Store the parameters for this Source in the Hashtable |
| _piParams.put(source, new PIParamWrapper(media, title, charset)); |
| // Return the same Source - we'll locate the stylesheet later |
| return source; |
| } |
| |
| /** |
| * 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 _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 = getTransletClassName(source); |
| |
| 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 |
| // <?xml-stylesheet ...?> 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 |
| if (!_transletName.equals(DEFAULT_TRANSLET_NAME)) |
| xsltc.setClassName(_transletName); |
| |
| 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) { |
| // Pass compiler errors to the error listener |
| if (_errorListener != null) { |
| passErrorsToListener(xsltc.getErrors()); |
| } |
| else { |
| xsltc.printErrors(); |
| } |
| ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR); |
| throw new TransformerConfigurationException(err.toString()); |
| } |
| |
| 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 org.apache.xalan.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; |
| } |
| |
| /** |
| * This method is synchronized to allow instances of this class to |
| * be shared among threads. A tranformer object will call this |
| * method to get an XMLReader. A different instance of an XMLReader |
| * is returned/cached for each thread. |
| */ |
| public synchronized XMLReader getXMLReader() throws Exception { |
| // First check if factory is instantiated |
| if (_parserFactory == null) { |
| _parserFactory = SAXParserFactory.newInstance(); |
| _parserFactory.setNamespaceAware(true); |
| } |
| XMLReader result = (XMLReader) _xmlReader.get(); |
| if (result == null) { |
| _xmlReader.set( |
| result = _parserFactory.newSAXParser().getXMLReader()); |
| } |
| return result; |
| } |
| |
| /** |
| * 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 fully qualified class name of the translet |
| * |
| * @param source The Source |
| * @return The full name of the translet class |
| */ |
| private String getTransletClassName(Source source) |
| { |
| String transletBaseName = null; |
| if (!_transletName.equals(DEFAULT_TRANSLET_NAME)) |
| transletBaseName = _transletName; |
| else { |
| String systemId = source.getSystemId(); |
| if (systemId != null) { |
| String baseName = Util.baseName(systemId); |
| if (baseName != null) |
| transletBaseName = Util.noExtName(baseName); |
| } |
| } |
| |
| if (transletBaseName == null) |
| transletBaseName = DEFAULT_TRANSLET_NAME; |
| |
| if (_packageName != null) |
| return _packageName + "." + transletBaseName; |
| else |
| return transletBaseName; |
| } |
| |
| /** |
| * 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; |
| } |
| } |