From 44735223d94bae810d33e7f820d587bfb49728fd Mon Sep 17 00:00:00 2001 From: Conor MacNeill Date: Tue, 5 Mar 2002 13:49:47 +0000 Subject: [PATCH] New datatype - xcatalog - used to store information about locally available XML DTDs. This allows such info to be shared among a number of different tasks such as XMLValidate, ejbjar, style, etc. Also checkstyle cleanup of the affected files. Submitted by: dIon Gillard git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271745 13f79535-47bb-0310-9956-ffa450edef68 --- .../tools/ant/taskdefs/XSLTProcess.java | 352 +++++++++++++----- .../ant/taskdefs/optional/TraXLiaison.java | 36 +- .../apache/tools/ant/types/DTDLocation.java | 97 +++++ .../org/apache/tools/ant/types/XCatalog.java | 257 +++++++++++++ .../tools/ant/types/defaults.properties | 2 + 5 files changed, 644 insertions(+), 100 deletions(-) create mode 100644 src/main/org/apache/tools/ant/types/DTDLocation.java create mode 100644 src/main/org/apache/tools/ant/types/XCatalog.java diff --git a/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java b/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java index 9ebba9fd7..e15a8ee46 100644 --- a/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java +++ b/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java @@ -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.AntClassLoader; 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 @@ -98,31 +97,65 @@ import org.apache.tools.ant.util.FileUtils; */ public class XSLTProcess extends MatchingTask implements XSLTLogger { - + /** destination directory */ private File destDir = null; - + + /** where to find the source XML file, default is the project's basedir */ private File baseDir = null; - + + /** XSL stylesheet */ private String xslFile = null; - + + /** extension of the files produced by XSL processing */ private String targetExtension = ".html"; - private Vector params = new Vector(); + /** additional parameters to be passed to the stylesheets */ + private Vector params = new Vector(); + + /** Input XML document to be used */ private File inFile = null; - + + /** Output file */ private File outFile = null; - + + /** The name of the XSL processor to use */ private String processor; + + /** 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 */ private XSLTLiaison liaison; + + /** Flag which indicates if the stylesheet has been loaded into + * the processor */ private boolean stylesheetLoaded = false; - + + /** force output of target files even if they already exist */ private boolean force = false; - + + /** Utilities used for file operations */ private FileUtils fileUtils; - + + /** XSL output method to be used */ private String outputtype = null; - + + /** for resolving entities such as dtds */ + private XCatalog xcatalog; + + /** Name of the TRAX Liason class */ + private static final String TRAX_LIAISON_CLASS = + "org.apache.tools.ant.taskdefs.optional.TraXLiaison"; + + /** Name of the now-deprecated XSLP Liason class */ + private static final String XSLP_LIASON_CLASS = + "org.apache.tools.ant.taskdefs.optional.XslpLiaison"; + + /** Name of the Xalan liason class */ + private static final String XALAN_LIASON_CLASS = + "org.apache.tools.ant.taskdefs.optional.XalanLiaison"; + /** * Whether to style all files in the included directories as well. * @@ -136,10 +169,11 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { public XSLTProcess() { fileUtils = FileUtils.newFileUtils(); } //-- XSLTProcess - + /** * Whether to style all files in the included directories as well. * + * @param b true if files in included directories are processed. * @since 1.35, Ant 1.5 */ public void setScanIncludedDirectories(boolean b) { @@ -148,29 +182,31 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { /** * Executes the task. + * + * @exception BuildException if there is an execution problem. */ 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) { + if (liaison instanceof XSLTLoggerAware) { ((XSLTLoggerAware)liaison).setLogger(this); } - - log("Using "+liaison.getClass().toString(), Project.MSG_VERBOSE); - + + log("Using " + liaison.getClass().toString(), Project.MSG_VERBOSE); + File stylesheet = project.resolveFile(xslFile); if (!stylesheet.exists()) { stylesheet = fileUtils.resolveFile(baseDir, xslFile); @@ -179,62 +215,66 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { * the wrong version has been used. */ if (stylesheet.exists()) { - log("DEPRECATED - the style attribute should be relative to the project\'s"); + log("DEPRECATED - the style attribute should be relative " + + "to the project\'s"); 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!"; throw new BuildException(msg); } scanner = getDirectoryScanner(baseDir); - log("Transforming into "+destDir, Project.MSG_INFO); - + 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) { + for (int i = 0; i < list.length; ++i) { process( baseDir, list[i], destDir, stylesheet ); } - if (performDirectoryScan) { // Process all the directories marked for styling dirs = scanner.getIncludedDirectories(); - for (int j = 0;j < dirs.length;++j){ - list=new File(baseDir,dirs[j]).list(); - for (int i = 0;i < list.length;++i) { + for (int j = 0; j < dirs.length; ++j){ + list = new File(baseDir, dirs[j]).list(); + for (int i = 0; i < list.length; ++i) { process( baseDir, list[i], destDir, stylesheet ); } } } } //-- execute - + /** * Set whether to check dependencies, or always generate. + * + * @param force true if always generate. **/ public void setForce(boolean force) { this.force = force; } //-- setForce - + /** * Set the base directory. + * + * @param dir 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 @@ -243,7 +283,7 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { 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 @@ -251,24 +291,30 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { public void setExtension(String name) { targetExtension = name; } //-- setDestDir - + /** * Sets the file to use for styling relative to the base directory * of this task. + * + * @param xslFile the stylesheet to use */ public void setStyle(String xslFile) { this.xslFile = xslFile; } - + /** * Set the classpath to load the Processor through (attribute). + * + * @param classpath the classpath to use when loading the XSL processor */ public void setClasspath(Path classpath) { createClasspath().append(classpath); } - + /** * Set the classpath to load the Processor through (nested element). + * + * @return a path instance to be configured by the Ant core. */ public Path createClasspath() { if (classpath == null) { @@ -276,47 +322,71 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { } return classpath.createPath(); } - + /** * Set the classpath to load the Processor through via reference * (attribute). + * + * @param r the id of the Ant path instance to act as the classpath + * for loading the XSL processor */ public void setClasspathRef(Reference r) { createClasspath().setRefid(r); } - - + + /** + * Set the name of the XSL processor to use + * + * @param processor the name of the XSL processor + */ public void setProcessor(String processor) { this.processor = processor; } - + + /** + * store the xcatalog for resolving entities + * + * @param xcatalog the xcatalog instance to use to look up DTDs + */ + public void addXcatalog(XCatalog xcatalog) { + this.xcatalog = xcatalog; + } + /** * Load processor here instead of in setProcessor - this will be * called from within execute, so we have access to the latest * classpath. + * + * @param proc the name of the processor to load. + * @exception Exception if the processor cannot be loaded. */ private void resolveProcessor(String proc) throws Exception { if (proc.equals("trax")) { final Class clazz = - loadClass("org.apache.tools.ant.taskdefs.optional.TraXLiaison"); + loadClass(TRAX_LIAISON_CLASS); liaison = (XSLTLiaison)clazz.newInstance(); } else if (proc.equals("xslp")) { - log("DEPRECATED - xslp processor is deprecated. Use trax or xalan instead."); + log("DEPRECATED - xslp processor is deprecated. Use trax or " + + "xalan instead."); final Class clazz = - loadClass("org.apache.tools.ant.taskdefs.optional.XslpLiaison"); + loadClass(XSLP_LIASON_CLASS); liaison = (XSLTLiaison) clazz.newInstance(); } else if (proc.equals("xalan")) { final Class clazz = - loadClass("org.apache.tools.ant.taskdefs.optional.XalanLiaison"); + loadClass(XALAN_LIASON_CLASS); liaison = (XSLTLiaison)clazz.newInstance(); } else { liaison = (XSLTLiaison) loadClass(proc).newInstance(); } } - + /** * Load named class either via the system classloader or a given * custom classloader. + * + * @param classname the name of the class to load. + * @return the requested class. + * @exception Exception if the class could not be loaded. */ private Class loadClass(String classname) throws Exception { if (classpath == null) { @@ -328,36 +398,46 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { return c; } } - + /** * Sets an out file + * + * @param outFile the output File instance. */ public void setOut(File outFile){ this.outFile = outFile; } - + /** * Sets an input xml file to be styled + * + * @param inFile the input file */ public void setIn(File inFile){ this.inFile = inFile; } - + /** * Processes the given input XML file and stores the result * in the given resultFile. + * + * @param baseDir the base directory for resolving files. + * @param xmlFile the input file + * @param destDir the destination directory + * @param stylesheet the stylesheet to use. + * @exception BuildException if the processing fails. */ private void process(File baseDir, String xmlFile, File destDir, File stylesheet) throws BuildException { - - String fileExt=targetExtension; - File outFile=null; - File inFile=null; - + + String fileExt = targetExtension; + File outFile = null; + File inFile = null; + try { long styleSheetLastModified = stylesheet.lastModified(); - inFile = new File(baseDir,xmlFile); + inFile = new File(baseDir, xmlFile); if (inFile.isDirectory()) { log("Skipping " + inFile + " it is a directory.", @@ -366,17 +446,18 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { } int dotPos = xmlFile.lastIndexOf('.'); - if(dotPos>0){ - outFile = new File(destDir,xmlFile.substring(0,xmlFile.lastIndexOf('.'))+fileExt); - }else{ - outFile = new File(destDir,xmlFile+fileExt); + if (dotPos > 0) { + outFile = new File(destDir, + xmlFile.substring(0, xmlFile.lastIndexOf('.')) + fileExt); + } else { + outFile = new File(destDir, xmlFile + fileExt); } if (force || inFile.lastModified() > outFile.lastModified() || styleSheetLastModified > outFile.lastModified()) { ensureDirectoryFor( outFile ); - log("Processing "+inFile+" to "+outFile); - + log("Processing " + inFile + " to " + outFile); + configureLiaison(stylesheet); liaison.transform(inFile, outFile); } @@ -388,45 +469,70 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { if (outFile != null) { outFile.delete(); } - + throw new BuildException(ex); } - + } //-- processXML - - private void process(File inFile, File outFile, File stylesheet) throws BuildException { - try{ + + /** + * Process the input file to the output file with the given stylesheet. + * + * @param inFile the input file to process. + * @param outFile the detination file. + * @param stylesheet the stylesheet to use. + * @exception BuildException if the processing fails. + */ + private void process(File inFile, File outFile, File stylesheet) + throws BuildException { + try { long styleSheetLastModified = stylesheet.lastModified(); - log("In file "+inFile+" time: " + inFile.lastModified() , Project.MSG_DEBUG); - log("Out file "+outFile+" time: " + outFile.lastModified() , Project.MSG_DEBUG); - log("Style file "+xslFile+" time: " + styleSheetLastModified , Project.MSG_DEBUG); + log("In file " + inFile + " time: " + inFile.lastModified(), + Project.MSG_DEBUG); + log("Out file " + outFile + " time: " + outFile.lastModified(), + Project.MSG_DEBUG); + log("Style file " + xslFile + " time: " + styleSheetLastModified, + Project.MSG_DEBUG); if (force || inFile.lastModified() > outFile.lastModified() || styleSheetLastModified > outFile.lastModified()) { ensureDirectoryFor( outFile ); - log("Processing " + inFile + " to " + outFile, Project.MSG_INFO); + log("Processing " + inFile + " to " + outFile, + Project.MSG_INFO); configureLiaison(stylesheet); liaison.transform(inFile, outFile); } - }catch (Exception ex) { + } catch (Exception ex) { log("Failed to process " + inFile, Project.MSG_INFO); - if(outFile!=null) { - outFile.delete(); + if (outFile != null) { + outFile.delete(); } throw new BuildException(ex); } } - - private void ensureDirectoryFor( File targetFile ) throws BuildException { + + /** + * Ensure the directory exists for a given file + * + * @param targetFile the file for which the directories are required. + * @exception BuildException if the directories cannot be created. + */ + 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() ); } } } - + + /** + * Get the Liason implementation to use in processing. + * + * @return an instance of the XSLTLiason interface. + */ 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 @@ -457,40 +563,74 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { } return liaison; } - + + /** + * Create an instance of an XSL parameter for configuration by Ant. + * + * @return an instance of the Param class to be configured. + */ public Param createParam() { Param p = new Param(); params.addElement(p); return p; } - + + /** + * The Param inner class used to store XSL parameters + */ public class Param { - private String name=null; - private String expression=null; - + /** The parameter name */ + private String name = null; + + /** The parameter's XSL expression */ + private String expression = null; + + /** + * Set the parameter name. + * + * @param name the name of the parameter. + */ public void setName(String name){ this.name = name; } - + + /** + * The XSL expression for the parameter value + * + * @param expression the XSL expression representing the + * parameter's value. + */ public void setExpression(String expression){ this.expression = expression; } - + + /** + * Get the parameter name + * + * @return the parameter name + * @exception BuildException if the name is not set. + */ public String getName() throws BuildException{ - if(name==null) { - throw new BuildException("Name attribute is missing."); + if (name == null) { + throw new BuildException("Name attribute is missing."); } return name; } - + + /** + * Get the parameter expression + * + * @return the parameter expression + * @exception BuildException if the expression is not set. + */ public String getExpression() throws BuildException{ - if(expression==null) { - throw new BuildException("Expression attribute is missing."); + if (expression == null) { + 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 @@ -500,27 +640,41 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { public void setOutputtype(String type) { this.outputtype = type; } - + /** * Loads the stylesheet and set xsl:param parameters. + * + * @param stylesheet the file form which to load the stylesheet. + * @exception BuildException if the stylesheet cannot be loaded. */ protected void configureLiaison(File stylesheet) throws BuildException { if (stylesheetLoaded) { return; } stylesheetLoaded = true; - + try { log( "Loading stylesheet " + stylesheet, Project.MSG_INFO); liaison.setStylesheet( stylesheet ); - for(Enumeration e = params.elements();e.hasMoreElements();) { + for (Enumeration e = params.elements(); e.hasMoreElements(); ) { 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 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 3ee742fec..27c5529f9 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java @@ -63,6 +63,12 @@ import org.apache.tools.ant.taskdefs.XSLTLiaison; 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.ErrorListener; 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 @@ public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware 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 @@ public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware 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... @@ -225,4 +253,10 @@ 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) throws Exception { + resolver = aResolver; + } + } //-- TraXLiaison diff --git a/src/main/org/apache/tools/ant/types/DTDLocation.java b/src/main/org/apache/tools/ant/types/DTDLocation.java new file mode 100644 index 000000000..bae830ac8 --- /dev/null +++ b/src/main/org/apache/tools/ant/types/DTDLocation.java @@ -0,0 +1,97 @@ +/* + * 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; + +/** + * Helper class to handle the DTD and Entity nested elements. + * + * @author Conor MacNeill + * @author dIon Gillard + */ +public class DTDLocation { + /** 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; + } +} + diff --git a/src/main/org/apache/tools/ant/types/XCatalog.java b/src/main/org/apache/tools/ant/types/XCatalog.java new file mode 100644 index 000000000..7575db510 --- /dev/null +++ b/src/main/org/apache/tools/ant/types/XCatalog.java @@ -0,0 +1,257 @@ +/* + * 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.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 data type provides a catalog of DTD locations + *

+ * + * <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>
+ *
+ *

+ * 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 ---------------------------------------------------------------- + + /** holds dtd/entity objects until needed */ + private Vector elements = new Vector(); + + //-- Methods --------------------------------------------------------------- + + /** + * @return the elements of the catalog - DTDLocation objects + */ + private Vector getElements() { + return elements; + } + + /** + * Set the list of DTDLocation object sin the catalog + * + * @param aVector the new list of DTD Locations to use in the catalog. + */ + private void setElements(Vector aVector) { + elements = aVector; + } + + /** + * Add a DTD Location to the catalog + * + * @param aDTD the DTDLocation instance to be aded to the catalog + */ + private void addElement(DTDLocation aDTD) { + getElements().add(aDTD); + } + + /** + * Creates the nested <dtd> element. + * + * @param dtd the infromation about the DTD to be added to the catalog + * @exception BuildException if this is a reference and no nested + * elements are allowed. + */ + public void addDTD(DTDLocation dtd) throws BuildException { + if (isReference()) { + throw noChildrenAllowed(); + } + getElements().add(dtd); + } + + /** + * Creates the nested <entity> element + * + * @param dtd the infromation about the DTD to be added to the catalog + * @exception BuildException if this is a reference and no nested + * elements are allowed. + */ + public void addEntity(DTDLocation dtd) throws BuildException { + addDTD(dtd); + } + + /** + * 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.

+ * + * @param r the reference to which this catalogi instance is associated + * @exception BuildException if this instance already has been configured. + */ + 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); + } + + /** + * @see org.xml.sax.EntityResolver#resolveEntity + */ + public InputSource resolveEntity(String publicId, String systemId) + throws SAXException, IOException { + InputSource source = null; + DTDLocation matchingDTD = findMatchingDTD(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 { + log("No match, parser will use: '" + systemId + "'", + Project.MSG_DEBUG); + } + // else let the parser handle it as a URI as we don't know what to + // do with it + return source; + } + + /** + * Find a DTDLocation instance for the given publicId. + * + * @param publicId the publicId of the DTD for which local information is + * required + * @return a DTDLocation instance with information on the local location + * of the DTD or null if no such information is available + */ + private DTDLocation findMatchingDTD(String publicId) { + Iterator elements = getElements().iterator(); + DTDLocation element = null; + while (elements.hasNext()) { + element = (DTDLocation)elements.next(); + if (element.getPublicId().equals(publicId)) { + return element; + } + } + return null; + } + +} + diff --git a/src/main/org/apache/tools/ant/types/defaults.properties b/src/main/org/apache/tools/ant/types/defaults.properties index 46fbab50d..3deff353d 100644 --- a/src/main/org/apache/tools/ant/types/defaults.properties +++ b/src/main/org/apache/tools/ant/types/defaults.properties @@ -8,3 +8,5 @@ description=org.apache.tools.ant.types.Description classfileset=org.apache.tools.ant.types.optional.depend.ClassfileSet substitution=org.apache.tools.ant.types.Substitution regexp=org.apache.tools.ant.types.RegularExpression +xcatalog=org.apache.tools.ant.types.XCatalog +