From 1e8307708de490453db28c89f777eb4a9c08767b Mon Sep 17 00:00:00 2001 From: Stephane Bailliez Date: Thu, 11 Jul 2002 22:52:55 +0000 Subject: [PATCH] Allow to specify a given factory implementation for the transformer (xsltc, xalan, saxon...) and to specify processor specific settings. TraxLiaison has been completely refactored because the factory needed to be created once everything was set up. I don't think it is final state since it might be better to specify factory attributes and element in a element rather than how it is now. XSLTProcess starts to bevery difficult to read with all the inner classes as well... git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@273089 13f79535-47bb-0310-9956-ffa450edef68 --- docs/manual/CoreTasks/style.html | 52 +++ src/etc/testcases/taskdefs/optional/xslt.xml | 19 +- .../tools/ant/taskdefs/XSLTProcess.java | 121 ++++++- .../ant/taskdefs/optional/TraXLiaison.java | 323 ++++++++++++------ .../tools/ant/taskdefs/optional/XsltTest.java | 13 +- 5 files changed, 411 insertions(+), 117 deletions(-) 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:
+     * 
    + *
  • http://xml.apache.org/xalan/features/optimize (true|false)
  • + *
  • http://xml.apache.org/xalan/features/incremental (true|false)
  • + *
+ * @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"); + } }