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