diff --git a/docs/manual/CoreTasks/style.html b/docs/manual/CoreTasks/style.html index 2bf74927a..37fda2296 100644 --- a/docs/manual/CoreTasks/style.html +++ b/docs/manual/CoreTasks/style.html @@ -102,6 +102,18 @@ element which is used to perform Entity and URI resolution

No + + factory + + fully qualified class name of the transformer + factory to use. For example + org.apache.xalan.processor.TransformerFactoryImpl + or org.apache.xalan.xsltc.trax.TransformerFactoryImpl + or net.sf.saxon.TransformerFactoryImpl... + + No. Works only with 'trax'/default processor + and defaults to JAXP lookup mechanism. + includes comma- or space-separated list of patterns of files that must be included. @@ -207,6 +219,46 @@ XSLT specifications. +

attribute ('trax' processors only)

+

Used to specify settings of the processor. +The attribute names and values are entirely processor specific +so you must be aware of the implementation to figure them out. +Read the documentation of your processor. +For example, in Xalan 2.x: +

+And in Saxon 7.x: + +

Parameters

+ + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
nameName of the attributeYes
valuevalue of the attribute.Yes
+ +

Examples

diff --git a/src/etc/testcases/taskdefs/optional/xslt.xml b/src/etc/testcases/taskdefs/optional/xslt.xml
index 75814a10c..03900453c 100644
--- a/src/etc/testcases/taskdefs/optional/xslt.xml
+++ b/src/etc/testcases/taskdefs/optional/xslt.xml
@@ -31,7 +31,6 @@
      
   
 
-
   
     
+  
+
+  
 
diff --git a/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java b/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java
index 472388b5f..4d10a288f 100644
--- a/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java
+++ b/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java
@@ -54,7 +54,6 @@
 
 package org.apache.tools.ant.taskdefs;
 
-import java.lang.reflect.Method;
 import java.io.File;
 import java.util.Enumeration;
 import java.util.Vector;
@@ -62,12 +61,12 @@ import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.DirectoryScanner;
 import org.apache.tools.ant.Project;
 import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.DynamicConfigurator;
 import org.apache.tools.ant.taskdefs.optional.TraXLiaison;
 import org.apache.tools.ant.types.Path;
 import org.apache.tools.ant.types.Reference;
 import org.apache.tools.ant.util.FileUtils;
 import org.apache.tools.ant.types.XMLCatalog;
-import org.xml.sax.EntityResolver;
 
 /**
  * Processes a set of XML documents via XSLT. This is
@@ -79,6 +78,7 @@ import org.xml.sax.EntityResolver;
  * @author Sam Ruby
  * @author Russell Gold
  * @author Stefan Bodewig
+  @author Stephane Bailliez
  *
  * @since Ant 1.1
  *
@@ -113,8 +113,7 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
     /** Classpath to use when trying to load the XSL processor */
     private Path classpath = null;
 
-    /** The Liason implementation to use to communicate with the XSL
-     *  processor */
+    /** implementation to use to communicate with the XSL processor */
     private XSLTLiaison liaison;
 
     /** Flag which indicates if the stylesheet has been loaded into
@@ -127,21 +126,21 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
     /** Utilities used for file operations */
     private FileUtils fileUtils;
 
-    /** XSL output method to be used */
+    /** XSL output properties to be used */
     private Vector outputProperties = new Vector();
 
     /** for resolving entities such as dtds */
     private XMLCatalog xmlCatalog = new XMLCatalog();
 
-    /** Name of the TRAX Liason class */
+    /** Name of the TRAX Liaison class */
     private static final String TRAX_LIAISON_CLASS =
                         "org.apache.tools.ant.taskdefs.optional.TraXLiaison";
 
-    /** Name of the now-deprecated XSLP Liason class */
+    /** Name of the now-deprecated XSLP Liaison class */
     private static final String XSLP_LIAISON_CLASS =
                         "org.apache.tools.ant.taskdefs.optional.XslpLiaison";
 
-    /** Name of the Xalan liason class */
+    /** Name of the Xalan liaison class */
     private static final String XALAN_LIAISON_CLASS =
                         "org.apache.tools.ant.taskdefs.optional.XalanLiaison";
 
@@ -152,6 +151,18 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
      */
     private boolean performDirectoryScan = true;
 
+    /**
+     * the factory class name to use for TraXLiaison
+     * @since Ant 1.6
+     */
+    private String factory = null;
+
+    /**
+     * the list of factory attributes to use for TraXLiaison
+     * @since Ant 1.6
+     */
+    private Vector attributes = new Vector();
+
     /**
      * Creates a new XSLTProcess Task.
      */
@@ -170,6 +181,14 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
         performDirectoryScan = b;
     }
 
+    /**
+     * Set the factory to use for the TraXLiaison.
+     * @param value the name of the  factory
+     */
+    public void setFactory(String value){
+        factory = value;
+    }
+
     /**
      * Executes the task.
      *
@@ -299,6 +318,8 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
     /**
      * Name of the stylesheet to use - given either relative
      * to the project's basedir or as an absolute path; required.
+     *
+     * @param xslFile the stylesheet to use
      */
     public void setStyle(String xslFile) {
         this.xslFile = xslFile;
@@ -579,7 +600,7 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
     /**
      * The Param inner class used to store XSL parameters
      */
-    public class Param {
+    public static class Param {
         /** The parameter name */
         private String name = null;
 
@@ -690,6 +711,77 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
         }
     }
 
+
+    /**
+     * Create an instance of a factory attribute.
+     * @return the newly created factory attribute
+     * @since Ant 1.6
+     */
+    public Attribute createAttribute() {
+        Attribute attr = new Attribute();
+        attributes.addElement(attr);
+        return attr;
+    }
+
+    /**
+     * A JAXP factory attribute. This is mostly processor specific, for
+     * example for Xalan 2.3+, the following attributes could be set:
+     * 
+     * @since Ant 1.6
+     */
+    public static class Attribute implements DynamicConfigurator {
+
+        /** attribute name, mostly processor specific */
+        private String name;
+
+        /** attribute value, often a boolean string */
+        private Object value;
+
+        /**
+         * @return the attribute name.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * @return the output property value.
+         */
+        public Object getValue() {
+            return value;
+        }
+
+        public Object createDynamicElement(String name) throws BuildException {
+            return null;
+        }
+
+        public void setDynamicAttribute(String name, String value)
+                throws BuildException {
+            // only 'name' and 'value' exist.
+            if ("name".equalsIgnoreCase(name)) {
+                this.name = value;
+            } else if ("value".equalsIgnoreCase(name)) {
+                // a value must be of a given type
+                // say boolean|integer|string that are mostly used.
+                if ("true".equalsIgnoreCase(value)
+                        || "false".equalsIgnoreCase(value) ){
+                    this.value = new Boolean(value);
+                } else {
+                    try {
+                        this.value = new Integer(value);
+                    } catch (NumberFormatException e) {
+                        this.value = value;
+                    }
+                }
+            } else {
+                throw new BuildException("Unsupported attribute: " + name);
+            }
+        }
+    }
+
     /**
      * Initialize internal instance of XMLCatalog
      */
@@ -733,12 +825,23 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
      * @param liaison the TRaXLiaison to configure.
      */
     protected void configureTraXLiaison(TraXLiaison liaison){
+        if (factory != null) {
+            liaison.setFactory(factory);
+        }
+
         // use XMLCatalog as the entity resolver and URI resolver
         if (xmlCatalog != null) {
             liaison.setEntityResolver(xmlCatalog);
             liaison.setURIResolver(xmlCatalog);
         }
 
+        // configure factory attributes
+        for (Enumeration attrs = attributes.elements();
+                attrs.hasMoreElements();) {
+            Attribute attr = (Attribute)attrs.nextElement();
+            liaison.setAttribute(attr.getName(), attr.getValue());
+        }
+
         // configure output properties
         for (Enumeration props = outputProperties.elements();
                 props.hasMoreElements();) {
diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java b/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java
index 1e47a6c43..c6c793073 100644
--- a/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java
+++ b/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java
@@ -58,6 +58,11 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.BufferedOutputStream;
+import java.io.BufferedInputStream;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.util.Vector;
 
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.taskdefs.XSLTLiaison;
@@ -80,6 +85,7 @@ import javax.xml.transform.stream.StreamResult;
 import javax.xml.transform.stream.StreamSource;
 import javax.xml.transform.Source;
 import javax.xml.transform.URIResolver;
+import javax.xml.transform.SourceLocator;
 
 import javax.xml.transform.sax.SAXSource;
 
@@ -93,118 +99,154 @@ import javax.xml.transform.sax.SAXSource;
  */
 public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware {
 
+    /**
+     * the name of the factory implementation class to use
+     * or null for default JAXP lookup.
+     */
+    private String factoryName = null;
+
     /** The trax TransformerFactory */
     private TransformerFactory tfactory = null;
 
-    /** stylesheet stream, close it asap */
-    private FileInputStream xslStream = null;
-
-    /** Stylesheet template */
-    private Templates templates = null;
-
-    /** transformer */
-    private Transformer transformer = null;
+    /** stylesheet to use for transformation */
+    private File stylesheet;
 
     private XSLTLogger logger;
 
     /** possible resolver for publicIds */
     private EntityResolver entityResolver;
 
+    /** transformer to use for processing files */
+    private Transformer transformer;
+
     /** possible resolver for URIs */
     private URIResolver uriResolver;
 
-    public TraXLiaison() throws Exception {
-        tfactory = TransformerFactory.newInstance();
-        tfactory.setErrorListener(this);
-    }
-
-
-    /**
-     * Set the output property for the current transformer.
-     * Note that the stylesheet must be set prior to calling
-     * this method.
-     * @param name the output property name.
-     * @param value the output property value.
-     */
-    public void setOutputProperty(String name, String value){
-        if (transformer == null){
-            throw new IllegalStateException("stylesheet must be set prior to setting the output properties");
-        }
-        transformer.setOutputProperty(name, value);
-    }
+    /** transformer output properties */
+    private Vector outputProperties = new Vector();
 
-//------------------- IMPORTANT
-    // 1) Don't use the StreamSource(File) ctor. It won't work with
-    // xalan prior to 2.2 because of systemid bugs.
+    /** stylesheet parameters */
+    private Vector params = new Vector();
 
-    // 2) Use a stream so that you can close it yourself quickly
-    // and avoid keeping the handle until the object is garbaged.
-    // (always keep control), otherwise you won't be able to delete
-    // the file quickly on windows.
+    /** factory attributes */
+    private Vector attributes = new Vector();
 
-    // 3) Always set the systemid to the source for imports, includes...
-    // in xsl and xml...
+    public TraXLiaison() throws Exception {
+    }
 
     public void setStylesheet(File stylesheet) throws Exception {
-        xslStream = new FileInputStream(stylesheet);
-        StreamSource src = new StreamSource(xslStream);
-        src.setSystemId(getSystemId(stylesheet));
-        templates = tfactory.newTemplates(src);
-        transformer = templates.newTransformer();
-        transformer.setErrorListener(this);
+        this.stylesheet = stylesheet;
     }
 
     public void transform(File infile, File outfile) throws Exception {
-        FileInputStream fis = null;
-        FileOutputStream fos = null;
+        if (transformer == null) {
+            transformer = newTransformer();
+        }
+
+        InputStream fis = null;
+        OutputStream fos = null;
         try {
-            fis = new FileInputStream(infile);
-            fos = new FileOutputStream(outfile);
-            // FIXME: need to use a SAXSource as the source for the transform
-            // so we can plug in our own entity resolver
-            Source src = null;
-            if (entityResolver != null) {
-                if (tfactory.getFeature(SAXSource.FEATURE)) {
-                    SAXParserFactory spFactory = SAXParserFactory.newInstance();
-                    spFactory.setNamespaceAware(true);
-                    XMLReader reader = spFactory.newSAXParser().getXMLReader();
-                    reader.setEntityResolver(entityResolver);
-                    src = new SAXSource(reader, new InputSource(fis));
-                } else {
-                    throw new IllegalStateException("xcatalog specified, but " +
-                        "parser doesn't support SAX");
-                }
-            } else {
-                src = new StreamSource(fis);
-            }
-            src.setSystemId(getSystemId(infile));
+            fis = new BufferedInputStream(new FileInputStream(infile));
+            fos = new BufferedOutputStream(new FileOutputStream(outfile));
             StreamResult res = new StreamResult(fos);
             // not sure what could be the need of this...
             res.setSystemId(getSystemId(outfile));
-
-            if (uriResolver != null)
-                transformer.setURIResolver(uriResolver);
-
+            Source src = getSource(fis, infile);
             transformer.transform(src, res);
         } finally {
             // make sure to close all handles, otherwise the garbage
             // collector will close them...whenever possible and
             // Windows may complain about not being able to delete files.
             try {
-                if (xslStream != null){
-                    xslStream.close();
-                }
-            } catch (IOException ignored){}
-            try {
-                if (fis != null){
+                if (fis != null) {
                     fis.close();
                 }
-            } catch (IOException ignored){}
+            } catch (IOException ignored) {
+            }
             try {
-                if (fos != null){
+                if (fos != null) {
                     fos.close();
                 }
-            } catch (IOException ignored){}
+            } catch (IOException ignored) {
+            }
+        }
+    }
+
+    /**
+     * Get the source instance from the stream and id of the file.
+     * @param is the stream containing the stylesheet data.
+     * @param infile the file that will be used for the systemid.
+     * @return the configured source instance matching the stylesheet.
+     * @throws Exception if there is a problem creating the source.
+     */
+    private Source getSource(InputStream is, File infile) throws Exception {
+        // todo: is this comment still relevant ??
+        // FIXME: need to use a SAXSource as the source for the transform
+        // so we can plug in our own entity resolver
+        Source src = null;
+        if (entityResolver != null) {
+            if (getFactory().getFeature(SAXSource.FEATURE)) {
+                SAXParserFactory spFactory = SAXParserFactory.newInstance();
+                spFactory.setNamespaceAware(true);
+                XMLReader reader = spFactory.newSAXParser().getXMLReader();
+                reader.setEntityResolver(entityResolver);
+                src = new SAXSource(reader, new InputSource(is));
+            } else {
+                throw new IllegalStateException("xcatalog specified, but " +
+                        "parser doesn't support SAX");
+            }
+        } else {
+            src = new StreamSource(is);
+        }
+        src.setSystemId(getSystemId(infile));
+        return src;
+    }
+
+    /**
+     * Create a new transformer based on the liaison settings
+     * @return the newly created and configured transformer.
+     * @throws Exception thrown if there is an error during creation.
+     * @see #setStylesheet(java.io.File)
+     * @see #addParam(java.lang.String, java.lang.String)
+     * @see #setOutputProperty(java.lang.String, java.lang.String)
+     */
+    private Transformer newTransformer() throws Exception {
+        // WARN: Don't use the StreamSource(File) ctor. It won't work with
+        // xalan prior to 2.2 because of systemid bugs.
+
+        // Use a stream so that you can close it yourself quickly
+        // and avoid keeping the handle until the object is garbaged.
+        // (always keep control), otherwise you won't be able to delete
+        // the file quickly on windows.
+        InputStream xslStream = new BufferedInputStream(
+                new FileInputStream(stylesheet));
+        try {
+            StreamSource src = new StreamSource(xslStream);
+            // Always set the systemid to the source for imports, includes...
+            // in xsl and xml...
+            src.setSystemId(getSystemId(stylesheet));
+            Templates templates = getFactory().newTemplates(src);
+            Transformer transformer = templates.newTransformer();
+
+            // configure the transformer...
+            transformer.setErrorListener(this);
+            if (uriResolver != null) {
+                transformer.setURIResolver(uriResolver);
+            }
+            for (int i = 0; i < params.size(); i++) {
+                final String[] pair = (String[]) params.elementAt(i);
+                transformer.setParameter(pair[0], pair[1]);
+            }
+            for (int i = 0; i < outputProperties.size(); i++) {
+                final String[] pair = (String[]) outputProperties.elementAt(i);
+                transformer.setOutputProperty(pair[0], pair[1]);
+            }
+            return transformer;
+        } finally {
+            try {
+                xslStream.close();
+            } catch (IOException ignored) {
+            }
         }
     }
 
@@ -212,7 +254,7 @@ public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware
     // crimson will complain that it cannot resolve relative entities
     // because it grabs the base uri via lastIndexOf('/') without
     // making sure it is really a /'ed path
-    protected String getSystemId(File file){
+    protected String getSystemId(File file) {
         String path = file.getAbsolutePath();
         path = path.replace('\\', '/');
 
@@ -224,24 +266,107 @@ public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware
         return FILE_PROTOCOL_PREFIX + path;
     }
 
-    public void addParam(String name, String value){
-        transformer.setParameter(name, value);
+
+    /**
+     * return the Transformer factory associated to this liaison.
+     * @return the Transformer factory associated to this liaison.
+     * @throws BuildException thrown if there is a problem creating
+     * the factory.
+     * @see #setFactory(String)
+     * @since Ant 1.6
+     */
+    private TransformerFactory getFactory() throws BuildException {
+        if (tfactory != null) {
+            return tfactory;
+        }
+        // not initialized yet, so create the factory
+        if (factoryName == null) {
+            tfactory = TransformerFactory.newInstance();
+        } else {
+            try {
+                Class clazz = Class.forName(factoryName);
+                tfactory = (TransformerFactory) clazz.newInstance();
+            } catch (Exception e) {
+                throw new BuildException(e);
+            }
+        }
+        tfactory.setErrorListener(this);
+
+        // specific attributes for the transformer
+        for (int i = 0; i < attributes.size(); i++) {
+            final Object[] pair = (Object[])attributes.elementAt(i);
+            tfactory.setAttribute((String)pair[0], pair[1]);
+        }
+        return tfactory;
+    }
+
+
+    /**
+     * Set the factory name to use instead of JAXP default lookup.
+     * @param name the fully qualified class name of the factory to use
+     * or null for the default JAXP look up mechanism.
+     * @since Ant 1.6
+     */
+    public void setFactory(String name) {
+        factoryName = name;
+    }
+
+    /**
+     * Set a custom attribute for the JAXP factory implementation.
+     * @param name the attribute name.
+     * @param value the value of the attribute, usually a boolean
+     * string or object.
+     * @since Ant 1.6
+     */
+    public void setAttribute(String name, Object value){
+        final Object[] pair = new Object[]{name, value};
+        attributes.addElement(pair);
+    }
+
+    /**
+     * Set the output property for the current transformer.
+     * Note that the stylesheet must be set prior to calling
+     * this method.
+     * @param name the output property name.
+     * @param value the output property value.
+     * @since Ant 1.5
+     */
+    public void setOutputProperty(String name, String value) {
+        final String[] pair = new String[]{name, value};
+        outputProperties.addElement(pair);
+    }
+
+    /** Set the class to resolve entities during the transformation
+     */
+    public void setEntityResolver(EntityResolver aResolver) {
+        entityResolver = aResolver;
+    }
+
+    /** Set the class to resolve URIs during the transformation
+     */
+    public void setURIResolver(URIResolver aResolver) {
+        uriResolver = aResolver;
+    }
+
+    public void addParam(String name, String value) {
+        final String[] pair = new String[]{name, value};
+        params.addElement(pair);
     }
 
     public void setLogger(XSLTLogger l) {
         logger = l;
     }
 
-    public void error(TransformerException e)  {
+    public void error(TransformerException e) {
         logError(e, "Error");
     }
 
-    public void fatalError(TransformerException e)  {
+    public void fatalError(TransformerException e) {
         logError(e, "Fatal Error");
         throw new BuildException("Fatal error during transformation", e);
     }
 
-    public void warning(TransformerException e)  {
+    public void warning(TransformerException e) {
         logError(e, "Warning");
     }
 
@@ -251,20 +376,24 @@ public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware
         }
 
         StringBuffer msg = new StringBuffer();
-        if (e.getLocator() != null) {
-            if (e.getLocator().getSystemId() != null) {
-                String url = e.getLocator().getSystemId();
+        SourceLocator locator = e.getLocator();
+        if (locator != null) {
+            String systemid = locator.getSystemId();
+            if (systemid != null) {
+                String url = systemid;
                 if (url.startsWith("file:///")) {
-                  url = url.substring(8);
+                    url = url.substring(8);
                 }
                 msg.append(url);
             } else {
                 msg.append("Unknown file");
             }
-            if (e.getLocator().getLineNumber() != -1) {
-                msg.append(":" + e.getLocator().getLineNumber());
-                if (e.getLocator().getColumnNumber() != -1) {
-                    msg.append(":" + e.getLocator().getColumnNumber());
+            int line = locator.getLineNumber();
+            if (line != -1) {
+                msg.append(":" + line);
+                int column = locator.getColumnNumber();
+                if (column != -1) {
+                    msg.append(":" + column);
                 }
             }
         }
@@ -277,16 +406,4 @@ public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware
         logger.log(msg.toString());
     }
 
-    /** Set the class to resolve entities during the transformation
-     */
-    public void setEntityResolver(EntityResolver aResolver) {
-        entityResolver = aResolver;
-    }
-
-    /** Set the class to resolve URIs during the transformation
-     */
-    public void setURIResolver(URIResolver aResolver) {
-        uriResolver = aResolver;
-    }
-
 } //-- TraXLiaison
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/optional/XsltTest.java b/src/testcases/org/apache/tools/ant/taskdefs/optional/XsltTest.java
index 168203add..90f2fa3a6 100644
--- a/src/testcases/org/apache/tools/ant/taskdefs/optional/XsltTest.java
+++ b/src/testcases/org/apache/tools/ant/taskdefs/optional/XsltTest.java
@@ -53,9 +53,6 @@
  */
 package org.apache.tools.ant.taskdefs.optional;
 
-import java.io.*;
-import java.util.Properties;
-
 import org.apache.tools.ant.BuildFileTest;
 
 /**
@@ -114,9 +111,17 @@ public class XsltTest extends BuildFileTest {
     public void testCatalog() throws Exception {
          executeTarget("testCatalog");
     }
-    
+
     public void testOutputProperty() throws Exception {
       executeTarget("testOutputProperty");
     }
+
+    public void testFactory() throws Exception {
+        executeTarget("testFactory");
+    }
+
+    public void testAttribute() throws Exception {
+        executeTarget("testAttribute");
+    }
 }