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
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: +
Attribute | +Description | +Required | +
name | +Name of the attribute | +Yes | +
value | +value of the attribute. | +Yes | +
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"); + } }- http://xml.apache.org/xalan/features/optimize (true|false)
+ *- http://xml.apache.org/xalan/features/incremental (true|false)
+ *