Return-Path: Delivered-To: apmail-jakarta-ant-dev-archive@apache.org Received: (qmail 83476 invoked from network); 2 Mar 2002 17:38:47 -0000 Received: from unknown (HELO nagoya.betaversion.org) (192.18.49.131) by daedalus.apache.org with SMTP; 2 Mar 2002 17:38:47 -0000 Received: (qmail 23131 invoked by uid 97); 2 Mar 2002 17:38:38 -0000 Delivered-To: qmlist-jakarta-archive-ant-dev@jakarta.apache.org Received: (qmail 23112 invoked by uid 97); 2 Mar 2002 17:38:37 -0000 Mailing-List: contact ant-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Ant Developers List" Reply-To: "Ant Developers List" Delivered-To: mailing list ant-dev@jakarta.apache.org Received: (qmail 23101 invoked from network); 2 Mar 2002 17:38:37 -0000 Message-ID: <3C81A863.4000805@multitask.com.au> Date: Sun, 03 Mar 2002 15:36:51 +1100 From: dIon Gillard User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:0.9.8+) Gecko/20020226 X-Accept-Language: en-us, en MIME-Version: 1.0 To: ant-dev@jakarta.apache.org Subject: [PATCH] Initial XCatalog support to allow shared DTD Locations to be used across tasks Content-Type: multipart/mixed; boundary="------------020606040406000801040008" X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N --------------020606040406000801040008 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Ok, here's the first working version, after promising to do it at least 2 weeks ago :) I've modified TraXLiaison and XSLTProcess, and added a new data type, XCatalog. An XCatalog is a way of grouping entities/dtd for resolution and supplying local file, resource name or URL for a publicId in XML source code. At the moment, I've only modified the XSLTProcess class (style task). But if these are ok, I'm happy to 'fix' XMLValidate and EJBJar as well as anything else that needs it. I've attached a sample build file showing its use as well, that I've been using for testing. If these look ok, let me know. If not, fire away.... -- dIon Gillard, Multitask Consulting http://adslgateway.multitask.com.au/developers --------------020606040406000801040008 Content-Type: text/plain; name="TraXLiaison.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="TraXLiaison.diff" Index: src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java =================================================================== RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java,v retrieving revision 1.10 diff -u -r1.10 TraXLiaison.java --- src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java 20 Jan 2002 20:45:57 -0000 1.10 +++ src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java 2 Mar 2002 17:05:24 -0000 @@ -63,6 +63,12 @@ import org.apache.tools.ant.taskdefs.XSLTLoggerAware; import org.apache.tools.ant.taskdefs.XSLTLogger; +import org.xml.sax.InputSource; +import org.xml.sax.EntityResolver; +import org.xml.sax.XMLReader; + +import javax.xml.parsers.SAXParserFactory; + import javax.xml.transform.TransformerFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; @@ -72,6 +78,9 @@ import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; + +import javax.xml.transform.sax.SAXSource; /** * Concrete liaison for XSLT processor implementing TraX. (ie JAXP 1.1) @@ -95,6 +104,9 @@ private Transformer transformer = null; private XSLTLogger logger; + + /** possible resolver for publicIds */ + private EntityResolver resolver; public TraXLiaison() throws Exception { tfactory = TransformerFactory.newInstance(); @@ -128,7 +140,23 @@ try { fis = new FileInputStream(infile); fos = new FileOutputStream(outfile); - StreamSource src = new StreamSource(fis); + // 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 (resolver != null) { + if (tfactory.getFeature(SAXSource.FEATURE)) { + SAXParserFactory spFactory = SAXParserFactory.newInstance(); + spFactory.setNamespaceAware( true ); + XMLReader reader = spFactory.newSAXParser().getXMLReader(); + reader.setEntityResolver(resolver); + 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)); StreamResult res = new StreamResult(fos); // not sure what could be the need of this... @@ -219,4 +247,10 @@ logger.log(msg.toString()); } + /** Set the class to resolve entities during the transformation + */ + public void setEntityResolver(EntityResolver aResolver) throws Exception { + resolver = aResolver; + } + } //-- TraXLiaison --------------020606040406000801040008 Content-Type: text/plain; name="XSLTProcess.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="XSLTProcess.diff" Index: src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java =================================================================== RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java,v retrieving revision 1.33 diff -u -r1.33 XSLTProcess.java --- src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java 10 Jan 2002 13:59:32 -0000 1.33 +++ src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java 2 Mar 2002 17:04:32 -0000 @@ -54,11 +54,9 @@ package org.apache.tools.ant.taskdefs; - +import java.lang.reflect.Method; import java.io.File; import java.util.Enumeration; - - import java.util.Vector; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; @@ -67,7 +65,8 @@ 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.XCatalog; +import org.xml.sax.EntityResolver; /** * A Task to process via XSLT a set of XML documents. This is @@ -94,64 +93,70 @@ */ public class XSLTProcess extends MatchingTask implements XSLTLogger { - + private File destDir = null; - + private File baseDir = null; - + private String xslFile = null; - + private String targetExtension = ".html"; private Vector params = new Vector(); - + private File inFile = null; - + private File outFile = null; - + private String processor; private Path classpath = null; private XSLTLiaison liaison; private boolean stylesheetLoaded = false; - + private boolean force = false; - + private FileUtils fileUtils; - + private String outputtype = null; - + + /** for resolving entities such as dtds */ + private XCatalog xcatalog; + + private static final String TRAX_LIAISON_CLASS = + "org.apache.tools.ant.taskdefs.optional.TraXLiaison"; + /** * Creates a new XSLTProcess Task. **/ public XSLTProcess() { fileUtils = FileUtils.newFileUtils(); } //-- XSLTProcess - + /** * Executes the task. */ - + public void execute() throws BuildException { DirectoryScanner scanner; String[] list; String[] dirs; - + if (xslFile == null) { throw new BuildException("no stylesheet specified", location); } - + if (baseDir == null) { baseDir = project.resolveFile("."); } - + liaison = getLiaison(); - + // check if liaison wants to log errors using us as logger if(liaison instanceof XSLTLoggerAware) { ((XSLTLoggerAware)liaison).setLogger(this); } - + log("Using "+liaison.getClass().toString(), Project.MSG_VERBOSE); - + File stylesheet = project.resolveFile(xslFile); if (!stylesheet.exists()) { stylesheet = fileUtils.resolveFile(baseDir, xslFile); @@ -164,18 +169,18 @@ log(" basedir, not the tasks\'s basedir."); } } - + // if we have an in file and out then process them if (inFile != null && outFile != null) { process(inFile, outFile, stylesheet); return; } - + /* * if we get here, in and out have not been specified, we are * in batch processing mode. */ - + //-- make sure Source directory exists... if (destDir == null ) { String msg = "destdir attributes must be set!"; @@ -183,13 +188,13 @@ } scanner = getDirectoryScanner(baseDir); log("Transforming into "+destDir, Project.MSG_INFO); - + // Process all the files marked for styling list = scanner.getIncludedFiles(); for (int i = 0;i < list.length; ++i) { process( baseDir, list[i], destDir, stylesheet ); } - + // Process all the directoried marked for styling dirs = scanner.getIncludedDirectories(); for (int j = 0;j < dirs.length;++j){ @@ -199,21 +204,21 @@ } } } //-- execute - + /** * Set whether to check dependencies, or always generate. **/ public void setForce(boolean force) { this.force = force; } //-- setForce - + /** * Set the base directory. **/ public void setBasedir(File dir) { baseDir = dir; } //-- setSourceDir - + /** * Set the destination directory into which the XSL result * files should be copied to @@ -222,7 +227,7 @@ public void setDestdir(File dir) { destDir = dir; } //-- setDestDir - + /** * Set the desired file extension to be used for the target * @param name the extension to use @@ -230,7 +235,7 @@ public void setExtension(String name) { targetExtension = name; } //-- setDestDir - + /** * Sets the file to use for styling relative to the base directory * of this task. @@ -238,14 +243,14 @@ public void setStyle(String xslFile) { this.xslFile = xslFile; } - + /** * Set the classpath to load the Processor through (attribute). */ public void setClasspath(Path classpath) { createClasspath().append(classpath); } - + /** * Set the classpath to load the Processor through (nested element). */ @@ -255,7 +260,7 @@ } return classpath.createPath(); } - + /** * Set the classpath to load the Processor through via reference * (attribute). @@ -263,12 +268,20 @@ public void setClasspathRef(Reference r) { createClasspath().setRefid(r); } - - + + public void setProcessor(String processor) { this.processor = processor; } - + + /** + * store the xcatalog for resolving entities + */ + public XCatalog createXcatalog() { + xcatalog = new XCatalog(); + return xcatalog; + } + /** * Load processor here instead of in setProcessor - this will be * called from within execute, so we have access to the latest @@ -292,7 +305,7 @@ liaison = (XSLTLiaison) loadClass(proc).newInstance(); } } - + /** * Load named class either via the system classloader or a given * custom classloader. @@ -307,21 +320,21 @@ return c; } } - + /** * Sets an out file */ public void setOut(File outFile){ this.outFile = outFile; } - + /** * Sets an input xml file to be styled */ public void setIn(File inFile){ this.inFile = inFile; } - + /** * Processes the given input XML file and stores the result * in the given resultFile. @@ -329,18 +342,18 @@ private void process(File baseDir, String xmlFile, File destDir, File stylesheet) throws BuildException { - + String fileExt=targetExtension; File outFile=null; File inFile=null; - + try { long styleSheetLastModified = stylesheet.lastModified(); inFile = new File(baseDir,xmlFile); int dotPos = xmlFile.lastIndexOf('.'); - if(dotPos>0){ + if (dotPos>0) { outFile = new File(destDir,xmlFile.substring(0,xmlFile.lastIndexOf('.'))+fileExt); - }else{ + } else { outFile = new File(destDir,xmlFile+fileExt); } if (force || @@ -348,7 +361,7 @@ styleSheetLastModified > outFile.lastModified()) { ensureDirectoryFor( outFile ); log("Processing "+inFile+" to "+outFile); - + configureLiaison(stylesheet); liaison.transform(inFile, outFile); } @@ -360,12 +373,12 @@ if (outFile != null) { outFile.delete(); } - + throw new BuildException(ex); } - + } //-- processXML - + private void process(File inFile, File outFile, File stylesheet) throws BuildException { try{ long styleSheetLastModified = stylesheet.lastModified(); @@ -383,22 +396,22 @@ }catch (Exception ex) { log("Failed to process " + inFile, Project.MSG_INFO); if(outFile!=null) { - outFile.delete(); + outFile.delete(); } throw new BuildException(ex); } } - + private void ensureDirectoryFor( File targetFile ) throws BuildException { File directory = new File( targetFile.getParent() ); if (!directory.exists()) { if (!directory.mkdirs()) { throw new BuildException("Unable to create directory: " - + directory.getAbsolutePath() ); + + directory.getAbsolutePath() ); } } } - + protected XSLTLiaison getLiaison() { // if processor wasn't specified, see if TraX is available. If not, // default it to xslp or xalan, depending on which is in the classpath @@ -429,40 +442,40 @@ } return liaison; } - + public Param createParam() { Param p = new Param(); params.addElement(p); return p; } - + public class Param { private String name=null; private String expression=null; - + public void setName(String name){ this.name = name; } - + public void setExpression(String expression){ this.expression = expression; } - + public String getName() throws BuildException{ if(name==null) { - throw new BuildException("Name attribute is missing."); + throw new BuildException("Name attribute is missing."); } return name; } - + public String getExpression() throws BuildException{ if(expression==null) { - throw new BuildException("Expression attribute is missing."); + throw new BuildException("Expression attribute is missing."); } return expression; } } - + /** * Set the output type to use for the transformation. Only "xml" (the * default) is guaranteed to work for all parsers. Xalan2 also @@ -472,7 +485,7 @@ public void setOutputtype(String type) { this.outputtype = type; } - + /** * Loads the stylesheet and set xsl:param parameters. */ @@ -481,7 +494,7 @@ return; } stylesheetLoaded = true; - + try { log( "Loading stylesheet " + stylesheet, Project.MSG_INFO); liaison.setStylesheet( stylesheet ); @@ -489,10 +502,21 @@ Param p = (Param)e.nextElement(); liaison.addParam( p.getName(), p.getExpression() ); } + // if liaison is a TraxLiason, use XCatalog as the entity + // resolver + if (liaison.getClass().getName().equals(TRAX_LIAISON_CLASS) && + xcatalog != null) { + log("Configuring TraxLiaison and calling entity resolver", + Project.MSG_DEBUG); + Method resolver = liaison.getClass() + .getDeclaredMethod("setEntityResolver", + new Class[] {EntityResolver.class}); + resolver.invoke(liaison, new Object[] {xcatalog}); + } } catch (Exception ex) { log("Failed to read stylesheet " + stylesheet, Project.MSG_INFO); throw new BuildException(ex); } } - + } //-- XSLTProcess --------------020606040406000801040008 Content-Type: text/plain; name="XCatalog.java" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="XCatalog.java" /* * The Apache Software License, Version 1.1 * * Copyright (c) 2002 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 acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Ant", 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 names without prior written * permission of the Apache Group. * * 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. For more * information on the Apache Software Foundation, please see * . */ package org.apache.tools.ant.types; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.net.URL; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * This object represents a path as used by CLASSPATH or PATH * environment variable. *

* * <sometask>
*   <catalog>
*     <dtd publicId="" location="/path/to/file.jar" />
*     <dtd publicId location="/path/to/file2.jar" /gt;
*     <entity publicId="" location="/path/to/file3.jar" />
*     <entity publicId="" location="/path/to/file4.jar" />
*   </catalog>
* </sometask>
*
*

* The object implemention sometask must provide a method called * createCatalog which returns an instance of XCatalog. * Nested dtd and entity definitions are handled by the XCatalog object and * must be labeled dtd and entity respectively.

* *

Possible future extension could allow a catalog file instead of nested * elements, or use Norman Walsh's entity resolver from xml-commons

* * @author dIon Gillard * @version $Id$ */ public class XCatalog extends DataType implements Cloneable, EntityResolver { //-- Fields ---------------------------------------------------------------- /** maps public id to DTD object */ private Hashtable entities = new Hashtable(); /** holds dtd/entity objects until needed */ private Vector elements = new Vector(); //-- Methods --------------------------------------------------------------- /** * @return the elements of the catalog - dtd objects */ private Vector getElements() { return elements; } private void setElements(Vector aVector) { elements = aVector; entities.clear(); Iterator elementIterator = elements.iterator(); DTD element = null; while (elementIterator.hasNext()) { element = (DTD)elementIterator.next(); entities.put(element.getPublicId(), element); } } private void addElement(DTD aDTD) { getElements().add(aDTD); // update entities entities.put(aDTD.getPublicId(), aDTD); } // need methods to create DTD and entity elements and add to the map // not sure why I should be coding a add vs create method - I'm choosing // create as the DTD class may not be known(?) by ant. /** * Creates the nested <dtd> element. */ public DTD createDtd() throws BuildException { if (isReference()) { throw noChildrenAllowed(); } DTD dtd = new DTD(); getElements().add(dtd); return dtd; } /** * Creates the nested <entity> element */ public DTD createEntity() throws BuildException { return createDtd(); } /** * Makes this instance in effect a reference to another XCatalog instance. * *

You must not set another attribute or nest elements inside * this element if you make it a reference.

*/ public void setRefid(Reference r) throws BuildException { if (!elements.isEmpty()) { throw tooManyAttributes(); } // change this to get the objects from the other reference Object o = r.getReferencedObject(getProject()); // we only support references to other XCatalogs if (o instanceof XCatalog) { // set all elements from referenced catalog to this one XCatalog catalog = (XCatalog) o; setElements(catalog.getElements()); } else { String msg = r.getRefId()+" doesn\'t refer to an XCatalog"; throw new BuildException(msg); } super.setRefid(r); } public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { InputSource source = null; DTD matchingDTD = (DTD)entities.get(publicId); if (matchingDTD != null) { // check if publicId is mapped to a file log("Matching DTD found for publicId: '" + publicId + "' location: '" + matchingDTD.getLocation() + "'", Project.MSG_DEBUG); File dtdFile = new File(matchingDTD.getLocation()); if (dtdFile.exists() && dtdFile.canRead()) { source = new InputSource( new FileInputStream(dtdFile) ); source.setSystemId(dtdFile.toURL().toExternalForm()); log("matched a readable file", Project.MSG_DEBUG); } else { // check if publicId is a resource // FIXME: ClassLoader: should this be context? ClassLoader loader = Thread.currentThread().getContextClassLoader(); InputStream is = loader.getResourceAsStream( matchingDTD.getLocation() ); if (is != null) { source = new InputSource(is); source.setSystemId(loader.getResource( matchingDTD.getLocation()).toExternalForm()); log("matched a resource", Project.MSG_DEBUG); } else { // check if it's a URL try { URL dtdUrl = new URL(matchingDTD.getLocation()); InputStream dtdIs = dtdUrl.openStream(); if (dtdIs != null) { source = new InputSource(dtdIs); source.setSystemId(dtdUrl.toExternalForm()); log("matched as a URL", Project.MSG_DEBUG); } else { log("No match, parser will use: '" + systemId + "'", Project.MSG_DEBUG); } } catch ( IOException ioe) { //ignore } } } } // else let the parser handle it as a URI as we don't know what to // do with it return source; } //-- Inner classes --------------------------------------------------------- /** * Helper class to handle the DTD and Entity nested elements * Shamelessly stolen from the EjbJar task and documented * * @author Tim Fennell * @author dIon Gillard */ public static class DTD { /** publicId of the dtd/entity */ private String publicId = null; /** location of the dtd/entity - a file/resource/URL*/ private String location = null; /** * @param publicId uniquely identifies the resource */ public void setPublicId(String publicId) { this.publicId = publicId; } /** * @param location the location of the resource associated with the * publicId */ public void setLocation(String location) { this.location = location; } /** * @return the publicId */ public String getPublicId() { return publicId; } /** * @return the location of the resource identified by the publicId */ public String getLocation() { return location; } } } --------------020606040406000801040008 Content-Type: text/xml; name="testXCat.xml" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testXCat.xml" --------------020606040406000801040008 Content-Type: text/plain; charset=us-ascii -- To unsubscribe, e-mail: For additional commands, e-mail: --------------020606040406000801040008--