diff --git a/WHATSNEW b/WHATSNEW
index fc5c90ed2..c797b3fbb 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -67,6 +67,10 @@ Other changes:
define ids or paths and use Ant's location magic for filename resolutions
in the XML file.
+*
Note: This task uses, but does not depend on external +libraries not included in the Ant distribution. See Library Dependencies for more +information.
This data type provides a catalog of resource locations based on the @@ -59,17 +63,34 @@ task uses XMLCatalogs for both entity and URI resolution.
XMLCatalogs are specified as either a reference to another XMLCatalog,
defined
previously in a build file, or as a list of dtd
or
-entity
locations. A separate classpath for entity resolution
+entity
locations. In addition, external catalog files
+may be specified in catalogfiles
filesets, but they will
+be ignored unless the resolver library from xml-commons is available
+in the system classpath. A separate classpath for entity resolution
may be specified inline via nested classpath
elements;
otherwise the system classpath is used for this as well.
XMLCatalogs can also be nested inside other XMLCatalogs. For example, a "superset" XMLCatalog could be made by including several nested XMLCatalogs that referred to other, previously defined XMLCatalogs.
+Resource locations can be specified either in-line or in
+external catalog file(s), or both. In order to use an external
+catalog file, the xml-commons resolver library ("resolver.jar")
+must be in your path. External catalog files may be either
+plain text format or
+XML format. If the xml-commons resolver library is not found in the
+classpath, external catalog files, specified in catalogfiles
+filesets, will be ignored and a warning will be logged. In this case, however,
+processing of inline entries will proceed normally.
Currently, only <dtd>
and
<entity>
elements may be specified inline; these
roughly correspond to OASIS catalog entry types PUBLIC
and
-URI
respectively.
URI
respectively. By contrast, external catalog files
+may use any of the entry types defined in the
+
++OASIS specification.
location
is
blat.dtd
.
+What happens next depends on whether the resolver library from +xml-commons is available on the classpath. If so, we defer all +further attempts at resolving to it. The resolver library supports +extremely sophisticated functionality like URL rewriting and so on, +which can be accessed by making the appropriate entries in external +catalog files (XMLCatalog does not yet provide inline support for all +of the entries defined in the OASIS +standard).
+Finally, we attempt to make a URL out of the location
.
@@ -171,6 +204,22 @@ basedir.
The classpath to use for entity
resolution. The nested <classpath>
is a
path-like structure.
+The nested catalogfiles
element specifies a FileSet. All files included in
+this fileset are assumed to be OASIS catalog files, in either
+
+plain text format or
+XML format. Multiple catalogfiles
filesets may be
+specified. Of course, if you use wildcards in your fileset, you will
+want to use some sort of naming convention to ensure that you don't
+accidentally match non-catalog files. If the resolver library from
+xml-commons is not available in the classpath, all
+catalogfiles
will be ignored and a warning will be
+logged.
+
Set up an XMLCatalog with a single dtd referenced locally in a user's home @@ -197,7 +246,8 @@ filesystem (relative to the Ant project basedir) or in the classpath:
Set up an XMLCatalog with a combination of DTDs and entities as -well as a nested XMLCatalog:
+well as a nested XMLCatalog and external catalog files in both +formats:<xmlcatalog id="allcatalogs"> @@ -207,7 +257,13 @@ well as a nested XMLCatalog: <entity publicId="LargeLogo" location="com/arielpartners/images/ariel-logo-large.gif"/> - <xmlcatalog refid="commonDTDs"/> + <xmlcatalog refid="commonDTDs"/> + <catalogfiles + dir="/anetwork/drive" + includes="**/catalog"/> + <catalogfiles + dir="/my/catalogs" + includes="**/catalog.xml"/> </xmlcatalog>
To reference the above XMLCatalog in an xslt
task:
diff --git a/docs/manual/install.html b/docs/manual/install.html index aae41cce8..3c19beb88 100644 --- a/docs/manual/install.html +++ b/docs/manual/install.html @@ -381,6 +381,12 @@ Installing Ant / Optional Tasks section above.
Helper class to handle the DTD nested element. Instances of
+ * this class correspond to the PUBLIC
catalog entry type
+ * of the
+ * OASIS "Open Catalog" standard.
Possible Future Enhancement: Bring the Ant element name into + * conformance with the OASIS standard.
+ * + * @see org.apache.xml.resolver.Catalog * @author Conor MacNeill * @author dIon Gillard + * @author Craeg Strong + * @version $Id$ */ -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; - } +public class DTDLocation extends ResourceLocation { - /** - * @param location the location of the resource associated with the - * publicId - */ - public void setLocation(String location) { - this.location = location; + public DTDLocation() { + super("PUBLIC"); } - /** - * @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/EntityLocation.java b/src/main/org/apache/tools/ant/types/EntityLocation.java new file mode 100644 index 000000000..c5351f989 --- /dev/null +++ b/src/main/org/apache/tools/ant/types/EntityLocation.java @@ -0,0 +1,77 @@ +/* + * 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 + *URI
catalog entry type of
+ * the
+ * OASIS "Open Catalog" standard.
+ *
+ * Possible Future Enhancement: Bring the Ant element name into + * conformance with the OASIS standard.
+ * + * @see org.apache.xml.resolver.Catalog + * @author Conor MacNeill + * @author dIon Gillard + * @author Craeg Strong + */ +public class EntityLocation extends ResourceLocation { + + public EntityLocation() { + super("URI"); + } + +} diff --git a/src/main/org/apache/tools/ant/types/ResourceLocation.java b/src/main/org/apache/tools/ant/types/ResourceLocation.java new file mode 100644 index 000000000..c23eeaf39 --- /dev/null +++ b/src/main/org/apache/tools/ant/types/ResourceLocation.java @@ -0,0 +1,165 @@ +/* + * 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 + *Helper class to handle the <dtd>
and
+ * <entity>
nested elements. These correspond to
+ * the PUBLIC
and URI
catalog entry types,
+ * respectively, as defined in the
+ * OASIS "Open Catalog" standard.
Possible Future Enhancements: + *
PUBLIC
or URI
.
+ *
+ * @see org.apache.xml.resolver.Catalog
+ */
+ public String getName() {
+ return name;
+ }
+
+} //-- ResourceLocation
diff --git a/src/main/org/apache/tools/ant/types/XMLCatalog.java b/src/main/org/apache/tools/ant/types/XMLCatalog.java
index aec564582..223dd1d9c 100644
--- a/src/main/org/apache/tools/ant/types/XMLCatalog.java
+++ b/src/main/org/apache/tools/ant/types/XMLCatalog.java
@@ -54,6 +54,8 @@
package org.apache.tools.ant.types;
+import java.lang.reflect.Method;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -72,6 +74,7 @@ import javax.xml.transform.URIResolver;
import javax.xml.transform.sax.SAXSource;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.util.FileUtils;
import org.xml.sax.EntityResolver;
@@ -138,13 +141,14 @@ import org.xml.sax.XMLReader;
* @author Craeg Strong
* @version $Id$
*/
-public class XMLCatalog extends DataType implements Cloneable, EntityResolver, URIResolver {
+public class XMLCatalog extends DataType
+ implements Cloneable, EntityResolver, URIResolver {
/** File utilities instance */
private FileUtils fileUtils = FileUtils.newFileUtils();
//-- Fields ----------------------------------------------------------------
- /** holds dtd/entity objects until needed */
+ /** Holds dtd/entity objects and catalog filesets until needed. */
private Vector elements = new Vector();
/**
@@ -152,6 +156,14 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
*/
private Path classpath;
+ /**
+ * The name of the bridge to the Apache xml-commons resolver
+ * class, used to determine whether resolver.jar is present in the
+ * classpath.
+ */
+ public static final String APACHE_RESOLVER
+ = "org.apache.tools.ant.types.resolver.ApacheCatalogResolver";
+
//-- Methods ---------------------------------------------------------------
public XMLCatalog() {
@@ -159,9 +171,11 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
}
/**
- * Returns the elements of the catalog - DTDLocation objects.
+ * Returns the elements of the catalog - ResolverLocation and FileSet
+ * objects.
*
- * @return the elements of the catalog - DTDLocation objects
+ * @return the elements of the catalog - ResolverLocation and FileSet
+ * objects
*/
private Vector getElements() {
return elements;
@@ -177,14 +191,18 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
}
/**
- * Set the list of DTDLocation objects in the catalog. Not
- * allowed if this catalog is itself a reference to another
- * catalog -- that is, a catalog cannot both refer to another
- * and contain elements or other attributes.
+ * Set the list of ResourceLocation objects and FileSets in the catalog.
+ * Not allowed if this catalog is itself a reference to another catalog --
+ * that is, a catalog cannot both refer to another and contain
+ * elements or other attributes.
*
- * @param aVector the new list of DTD Locations to use in the catalog.
+ * @param aVector the new list of ResourceLocations and FileSets
+ * to use in the catalog.
*/
private void setElements(Vector aVector) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
elements = aVector;
}
@@ -237,6 +255,22 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
setChecked( false );
}
+ /** Creates the nested <catalogfiles>
element. Not
+ * allowed if this catalog is itself a reference to another catalog -- that
+ * is, a catalog cannot both refer to another and contain elements
+ * or other attributes.
+ *
+ * @param fs the fileset of external catalogs.
+ * @exception BuildException
+ * if this is a reference and no nested elements are allowed.
+ */
+ public void addCatalogfiles(FileSet fs) throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ getElements().addElement(fs);
+ }
+
/**
* Creates the nested <dtd>
element. Not
* allowed if this catalog is itself a reference to another
@@ -248,7 +282,7 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
* @exception BuildException if this is a reference and no nested
* elements are allowed.
*/
- public void addDTD(DTDLocation dtd) throws BuildException {
+ public void addDTD(ResourceLocation dtd) throws BuildException {
if (isReference()) {
throw noChildrenAllowed();
}
@@ -256,6 +290,9 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
getElements().addElement(dtd);
setChecked( false );
}
+ public void addDTD(DTDLocation dtd) throws BuildException {
+ addDTD((ResourceLocation)dtd);
+ }
/**
* Creates the nested <entity>
element. Not
@@ -263,13 +300,34 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
* catalog -- that is, a catalog cannot both refer to another
* and contain elements or other attributes.
*
- * @param dtd the information about the URI resource mapping to be
- * added to the catalog
+ * @param entity the information about the URI resource mapping 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);
+ public void addEntity(EntityLocation entity) throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ getElements().addElement(entity);
+ }
+
+ /**
+ * Creates the nested <entity>
element. Not
+ * allowed if this catalog is itself a reference to another
+ * catalog -- that is, a catalog cannot both refer to another
+ * and contain elements or other attributes.
+ *
+ * @param entity the information about the URI resource mapping to
+ * be added to the catalog.
+ * @exception BuildException if this is a reference and no nested
+ * elements are allowed.
+ */
+ public void addEntity(DTDLocation entity) throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ getElements().addElement(entity);
}
/**
@@ -337,17 +395,18 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
- if (!isChecked()) {
- // make sure we don't have a circular reference here
- Stack stk = new Stack();
- stk.push(this);
- dieOnCircularReference(stk, getProject());
- }
+ if (!isChecked()) {
+ // make sure we don't have a circular reference here
+ Stack stk = new Stack();
+ stk.push(this);
+ dieOnCircularReference(stk, getProject());
+ }
log("resolveEntity: '" + publicId + "': '" + systemId + "'",
Project.MSG_DEBUG);
- InputSource inputSource = resolveEntityImpl(publicId );
+ InputSource inputSource =
+ getCatalogResolver().resolveEntity(publicId, systemId);
if (inputSource == null) {
log("No matching catalog entry found, parser will use: '" +
@@ -365,12 +424,12 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
public Source resolve(String href, String base)
throws TransformerException {
- if (!isChecked()) {
- // make sure we don't have a circular reference here
- Stack stk = new Stack();
- stk.push(this);
- dieOnCircularReference(stk, getProject());
- }
+ if (!isChecked()) {
+ // make sure we don't have a circular reference here
+ Stack stk = new Stack();
+ stk.push(this);
+ dieOnCircularReference(stk, getProject());
+ }
SAXSource source = null;
@@ -389,11 +448,11 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
//
source = new SAXSource();
try
- {
- URL baseURL = new URL(base);
- URL url = (uri.length() == 0 ? baseURL : new URL(baseURL, uri));
- source.setInputSource(new InputSource(url.toString()));
- }
+ {
+ URL baseURL = new URL(base);
+ URL url = (uri.length() == 0 ? baseURL : new URL(baseURL, uri));
+ source.setInputSource(new InputSource(url.toString()));
+ }
catch (MalformedURLException ex) {
// At this point we are probably in failure mode, but
// try to use the bare URI as a last gasp
@@ -406,25 +465,67 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
}
/**
- * Find a DTDLocation instance for the given publicId.
+ * The instance of the CatalogResolver strategy to use.
+ */
+ private static CatalogResolver catalogResolver = null;
+
+ /**
+ * Factory method for creating the appropriate CatalogResolver
+ * strategy implementation.
+ * Until we query the classpath, we don't know whether the Apache + * resolver (Norm Walsh's library from xml-commons) is available or not. + * This method determines whether the library is available and creates the + * appropriate implementation of CatalogResolver based on the answer.
+ *This is an application of the Gang of Four Strategy Pattern + * combined with Template Method.
* * @param publicId the publicId of the Resource for which local information * is required - * @return a DTDLocation instance with information on the local location + * @return a ResourceLocation instance with information on the local location * of the Resource or null if no such information is available */ - private DTDLocation findMatchingEntry(String publicId) { - Enumeration elements = getElements().elements(); - DTDLocation element = null; - while (elements.hasMoreElements()) { - element = (DTDLocation) elements.nextElement(); - if (element.getPublicId().equals(publicId)) { - return element; + private CatalogResolver getCatalogResolver() { + + if (catalogResolver == null) { + + AntClassLoader loader = null; + + loader = new AntClassLoader(project, Path.systemClasspath); + + try { + Class clazz = loader.forceLoadSystemClass(APACHE_RESOLVER); + Object obj = clazz.newInstance(); + // + // Success! The xml-commons resolver library is + // available, so use it. + // + catalogResolver = new ApacheResolver(clazz, obj); + } + catch (Throwable ex) { + // + // The xml-commons resolver library is not + // available, so we can't use it. + // + catalogResolver = new InternalResolver(); + // + // If anyThis is called from the URIResolver to set an EntityResolver
* on the SAX parser to be used for new XML documents that are
@@ -464,6 +565,29 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
source.setXMLReader(reader);
}
+ /**
+ * Find a ResourceLocation instance for the given publicId.
+ *
+ * @param publicId the publicId of the Resource for which local information is
+ * required.
+ * @return a ResourceLocation instance with information on the local location
+ * of the Resource or null if no such information is available.
+ */
+ private ResourceLocation findMatchingEntry(String publicId) {
+ Enumeration enum = getElements().elements();
+ ResourceLocation element = null;
+ while (enum.hasMoreElements()) {
+ Object o = enum.nextElement();
+ if (o instanceof ResourceLocation) {
+ element = (ResourceLocation)o;
+ if (element.getPublicId().equals(publicId)) {
+ return element;
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Utility method to remove trailing fragment from a URI.
* For example,
@@ -484,17 +608,17 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
}
/**
- * Utility method to lookup a DTDLocation in the filesystem.
+ * Utility method to lookup a ResourceLocation in the filesystem.
*
* @return An InputSource for reading the file, or null
* if the file does not exist or is not readable.
*/
- private InputSource filesystemLookup(DTDLocation matchingEntry) {
+ private InputSource filesystemLookup(ResourceLocation matchingEntry) {
String uri = matchingEntry.getLocation();
//
- // The DTDLocation may specify a relative path for its
+ // The ResourceLocation may specify a relative path for its
// location attribute. This is resolved using the appropriate
// base.
//
@@ -522,12 +646,12 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
}
/**
- * Utility method to lookup a DTDLocation in the classpath.
+ * Utility method to lookup a ResourceLocation in the classpath.
*
* @return An InputSource for reading the resource, or null
* if the resource does not exist in the classpath or is not readable.
*/
- private InputSource classpathLookup(DTDLocation matchingEntry) {
+ private InputSource classpathLookup(ResourceLocation matchingEntry) {
InputSource source = null;
@@ -557,7 +681,7 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
}
/**
- * Utility method to lookup a DTDLocation in URL-space.
+ * Utility method to lookup a ResourceLocation in URL-space.
*
* @return An InputSource for reading the resource, or null
* if the resource does not identify a valid URL or is not readable.
@@ -602,31 +726,33 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
/**
* Implements the guts of the resolveEntity() lookup strategy.
*/
- private InputSource resolveEntityImpl(String publicId) {
+ /*
+ private InputSource resolveEntityImpl(String publicId) {
- InputSource result = null;
+ InputSource result = null;
- DTDLocation matchingEntry = findMatchingEntry(publicId);
+ ResourceLocation matchingEntry = findMatchingEntry(publicId);
- if (matchingEntry != null) {
+ if (matchingEntry != null) {
- log("Matching catalog entry found for publicId: '" +
- matchingEntry.getPublicId() + "' location: '" +
- matchingEntry.getLocation() + "'",
- Project.MSG_DEBUG);
+ log("Matching catalog entry found for publicId: '" +
+ matchingEntry.getPublicId() + "' location: '" +
+ matchingEntry.getLocation() + "'",
+ Project.MSG_DEBUG);
- result = filesystemLookup(matchingEntry);
+ result = filesystemLookup(matchingEntry);
- if (result == null) {
- result = classpathLookup(matchingEntry);
- }
+ if (result == null) {
+ result = classpathLookup(matchingEntry);
+ }
- if (result == null) {
- result = urlLookup(matchingEntry.getLocation(), null);
- }
- }
- return result;
- }
+ if (result == null) {
+ result = urlLookup(matchingEntry.getLocation(), null);
+ }
+ }
+ return result;
+ }
+ */
/**
* Implements the guts of the resolve() lookup strategy.
@@ -636,7 +762,7 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
SAXSource result = null;
InputSource source = null;
- DTDLocation matchingEntry = findMatchingEntry(href);
+ ResourceLocation matchingEntry = findMatchingEntry(href);
if (matchingEntry != null) {
@@ -661,4 +787,312 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
}
return result;
}
+
+ /**
+ * Interface implemented by both the InternalResolver strategy and
+ * the ApacheResolver strategy.
+ */
+ private interface CatalogResolver extends URIResolver, EntityResolver {
+
+ InputSource resolveEntity(String publicId, String systemId);
+
+ Source resolve(String href, String base) throws TransformerException;
+ }
+
+ /**
+ * The InternalResolver strategy is used if the Apache resolver
+ * library (Norm Walsh's library from xml-commons) is not
+ * available. In this case, external catalog files will be
+ * ignored.
+ *
+ */
+ private class InternalResolver implements CatalogResolver {
+
+ public InternalResolver() {
+ log("Apache resolver library not found, internal resolver will be used",
+ Project.MSG_INFO);
+ }
+
+ public InputSource resolveEntity(String publicId,
+ String systemId) {
+
+ InputSource result = null;
+
+ ResourceLocation matchingEntry = findMatchingEntry(publicId);
+
+ if (matchingEntry != null) {
+
+ log("Matching catalog entry found for publicId: '" +
+ matchingEntry.getPublicId() + "' location: '" +
+ matchingEntry.getLocation() + "'",
+ Project.MSG_DEBUG);
+
+ result = filesystemLookup(matchingEntry);
+
+ if (result == null) {
+ result = classpathLookup(matchingEntry);
+ }
+
+ if (result == null) {
+ result = urlLookup(matchingEntry.getLocation(), null);
+ }
+ }
+ return result;
+ }
+
+
+
+ public Source resolve(String href, String base)
+ throws TransformerException {
+
+ SAXSource result = null;
+ InputSource source = null;
+
+ ResourceLocation matchingEntry = findMatchingEntry(href);
+
+ if (matchingEntry != null) {
+
+ log("Matching catalog entry found for uri: '" +
+ matchingEntry.getPublicId() + "' location: '" +
+ matchingEntry.getLocation() + "'",
+ Project.MSG_DEBUG);
+
+ source = filesystemLookup(matchingEntry);
+
+ if (source == null) {
+ source = classpathLookup(matchingEntry);
+ }
+
+ if (source == null) {
+ source = urlLookup(matchingEntry.getLocation(), base);
+ }
+
+ if (source != null) {
+ result = new SAXSource(source);
+ }
+ }
+ return result;
+ }
+ }
+
+ /**
+ * The ApacheResolver strategy is used if the Apache resolver
+ * library (Norm Walsh's library from xml-commons) is available in
+ * the classpath. The ApacheResolver is a essentially a superset
+ * of the InternalResolver.
+ *
+ */
+ private class ApacheResolver implements CatalogResolver {
+
+ private Method setXMLCatalog = null;
+ private Method parseCatalog = null;
+ private Method resolveEntity = null;
+ private Method resolve = null;
+
+ /** The instance of the ApacheCatalogResolver bridge class */
+ private Object resolverImpl = null;
+
+ private boolean externalCatalogsProcessed = false;
+
+ public ApacheResolver(Class resolverImplClass,
+ Object resolverImpl) {
+
+ this.resolverImpl = resolverImpl;
+
+ //
+ // Get Method instances for each of the methods we need to
+ // call on the resolverImpl using reflection. We can't
+ // call them directly, because they require on the
+ // xml-commons resolver library which may not be available
+ // in the classpath.
+ //
+ try {
+ setXMLCatalog =
+ resolverImplClass.getMethod("setXMLCatalog",
+ new Class[]
+ { XMLCatalog.class });
+
+ parseCatalog =
+ resolverImplClass.getMethod("parseCatalog",
+ new Class[]
+ { String.class });
+
+ resolveEntity =
+ resolverImplClass.getMethod("resolveEntity",
+ new Class[]
+ { String.class, String.class });
+
+ resolve =
+ resolverImplClass.getMethod("resolve",
+ new Class[]
+ { String.class, String.class });
+ }
+ catch (NoSuchMethodException ex) {
+ throw new BuildException(ex);
+ }
+
+ log("Apache resolver library found, xml-commons resolver will be used",
+ Project.MSG_INFO);
+ }
+
+ public InputSource resolveEntity(String publicId,
+ String systemId) {
+ InputSource result = null;
+
+ processExternalCatalogs();
+
+ ResourceLocation matchingEntry = findMatchingEntry(publicId);
+
+ if (matchingEntry != null) {
+
+ log("Matching catalog entry found for publicId: '" +
+ matchingEntry.getPublicId() + "' location: '" +
+ matchingEntry.getLocation() + "'",
+ Project.MSG_DEBUG);
+
+ result = filesystemLookup(matchingEntry);
+
+ if (result == null) {
+ result = classpathLookup(matchingEntry);
+ }
+
+ if (result == null) {
+ try {
+ result =
+ (InputSource)resolveEntity.invoke(resolverImpl,
+ new Object[]
+ { publicId, systemId });
+ }
+ catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+ }
+ else {
+ //
+ // We didn't match a ResourceLocation, but since we
+ // only support PUBLIC and URI entry types, it is
+ // still possible that there is another entry in an
+ // external catalog that will match. We call Apache
+ // resolver's resolveEntity method to cover this
+ // possibility.
+ //
+ try {
+ result =
+ (InputSource)resolveEntity.invoke(resolverImpl,
+ new Object[]
+ { publicId, systemId });
+ }
+ catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+
+ return result;
+ }
+
+ public Source resolve(String href, String base)
+ throws TransformerException {
+
+ SAXSource result = null;
+ InputSource source = null;
+
+ processExternalCatalogs();
+
+ ResourceLocation matchingEntry = findMatchingEntry(href);
+
+ if (matchingEntry != null) {
+
+ log("Matching catalog entry found for uri: '" +
+ matchingEntry.getPublicId() + "' location: '" +
+ matchingEntry.getLocation() + "'",
+ Project.MSG_DEBUG);
+
+ source = filesystemLookup(matchingEntry);
+
+ if (source == null) {
+ source = classpathLookup(matchingEntry);
+ }
+
+ if (source != null) {
+ result = new SAXSource(source);
+ } else {
+ try {
+ result =
+ (SAXSource)resolve.invoke(resolverImpl,
+ new Object[]
+ { href, base });
+ }
+ catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+ }
+ else {
+ //
+ // We didn't match a ResourceLocation, but since we
+ // only support PUBLIC and URI entry types, it is
+ // still possible that there is another entry in an
+ // external catalog that will match. We call Apache
+ // resolver's resolveEntity method to cover this
+ // possibility.
+ //
+ try {
+ result =
+ (SAXSource)resolve.invoke(resolverImpl,
+ new Object[]
+ { href, base });
+ }
+ catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Process each external catalog file specified in a
+ * <catalogfiles>
FileSet. It will be
+ * parsed by the resolver library, and the individual elements
+ * will be added back to us (that is, the controlling
+ * XMLCatalog instance) via a callback mechanism.
+ */
+ private void processExternalCatalogs() {
+
+ if (externalCatalogsProcessed == false) {
+
+ try {
+ setXMLCatalog.invoke(resolverImpl,
+ new Object[]
+ { XMLCatalog.this });
+ }
+ catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+
+ Enumeration enum = getElements().elements();
+ while (enum.hasMoreElements()) {
+ Object o = enum.nextElement();
+ if (o instanceof FileSet) {
+ FileSet fs = (FileSet)o;
+ DirectoryScanner ds =
+ fs.getDirectoryScanner(getProject());
+ String[] files = ds.getIncludedFiles();
+ for (int i = 0; i < files.length; i++) {
+ File catFile = new File(ds.getBasedir(), files[i]);
+ try {
+ parseCatalog.invoke(resolverImpl,
+ new Object[]
+ { catFile.getPath() });
+ }
+ catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+ }
+ }
+ }
+ externalCatalogsProcessed = true;
+ }
+ }
}
diff --git a/src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java b/src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java
new file mode 100644
index 000000000..32dadc4ba
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java
@@ -0,0 +1,155 @@
+/*
+ * 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
+ * <dtd>
+ * and <entity>
) are not added to ApacheCatalog.
+ * See XMLCatalog.java for the details of the entity and URI
+ * resolution algorithms.
+ *
+ * @see org.apache.tools.ant.types.XMLCatalog.CatalogResolver
+ * @author Craeg Strong
+ * @version $Id$
+ */
+public class ApacheCatalog extends Catalog {
+
+ /** The resolver object to callback. */
+ private ApacheCatalogResolver resolver = null;
+
+ /**
+ *
Create a new ApacheCatalog instance.
+ * + *This method overrides the superclass method of the same name + * in order to set the resolver object for callbacks. The reason + * we have to do this is that internally Catalog creates a new + * instance of itself for each external catalog file processed. + * That is, if two external catalog files are processed, there + * will be a total of two ApacheCatalog instances, and so on.
+ */ + protected Catalog newCatalog() { + ApacheCatalog cat = (ApacheCatalog)super.newCatalog(); + cat.setResolver(resolver); + return cat; + } + + /** Set the resolver object to callback. */ + public void setResolver(ApacheCatalogResolver resolver) { + this.resolver = resolver; + } + + /** + *This method overrides the superclass method of the same name + * in order to add catalog entries back to the controlling + * XMLCatalog instance. In this way, we can add classpath lookup + * for these entries.
+ * + *When we add an external catalog file, the entries inside it + * get parsed by this method. Therefore, we override it to add + * each of them back to the controlling XMLCatalog instance. This + * is done by performing a callback to the ApacheCatalogResolver, + * which in turn calls the XMLCatalog.
+ * + *XMLCatalog currently only understands PUBLIC
+ * and URI
entry types, so we ignore the other types.
This class extends the CatalogResolver class provided by Norman + * Walsh's resolver library in xml-commons. It provides the bridge + * between the Ant XMLCatalog datatype and the xml-commons Catalog + * class. XMLCatalog calls methods in this class using Reflection in + * order to avoid requiring the xml-commons resolver library in the + * path.
+ * + *The {@link org.apache.tools.ant.types.resolver.ApacheCatalog + * ApacheCatalog} class is used to parse external catalog files, which + * can be in either + * plain text format or + * XML format.
+ * + *For each entry found in an external catalog file, if any, an + * instance of {@link org.apache.tools.ant.types.ResourceLocation + * ResourceLocation} is created and added to the controlling + * XMLCatalog datatype. In this way, these entries will be included + * in XMLCatalog's lookup algorithm. See XMLCatalog.java for more + * details.
+ * + * @see org.apache.tools.ant.types.XMLCatalog.CatalogResolver + * @see org.apache.xml.resolver.CatalogManager + * @author Craeg Strong + * @version $Id$ + */ + +public class ApacheCatalogResolver extends CatalogResolver { + + /** The XMLCatalog object to callback. */ + private XMLCatalog xmlCatalog = null; + + static + { + // + // If you don't do this, you get all sorts of annoying + // warnings about a missing properties file. However, it + // seems to work just fine with default values. Ultimately, + // we should probably include a "CatalogManager.properties" + // file in the ant jarfile with some default property + // settings. See CatalogManager.java for more details. + // + CatalogManager.ignoreMissingProperties(true); + + // + // Make sure CatalogResolver instantiates ApacheCatalog, + // rather than a plain Catalog + // + System.setProperty("xml.catalog.className", + ApacheCatalog.class.getName()); + + // debug + // System.setProperty("xml.catalog.verbosity", "4"); + } + + /** Set the XMLCatalog object to callback. */ + public void setXMLCatalog(XMLCatalog xmlCatalog) { + this.xmlCatalog = xmlCatalog; + } + + /** + * XMLCatalog calls this to add an external catalog file for each + * file within a<catalogfiles>
fileset.
+ */
+ public void parseCatalog(String file) {
+
+ ApacheCatalog catalog = (ApacheCatalog)getCatalog();
+
+ // Pass in reference to ourselves so we can be called back.
+ catalog.setResolver(this);
+
+ try {
+ catalog.parseCatalog(file);
+ }
+ catch(MalformedURLException ex) {
+ throw new BuildException(ex);
+ }
+ catch(IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+
+ /**
+ * Add a PUBLIC catalog entry to the controlling XMLCatalog instance. + * ApacheCatalog calls this for each PUBLIC entry found in an external + * catalog file.
+ * + * @param publicid The public ID of the resource + * @param systemid The system ID (aka location) of the resource + * @param base The base URL of the resource. If the systemid + * specifies a relative URL/pathname, it is resolved using the + * base. The default base for an external catalog file is the + * directory in which the catalog is located. + * + */ + public void addPublicEntry(String publicid, + String systemid, + String base) { + + DTDLocation dtd = new DTDLocation(); + dtd.setBase(base); + dtd.setPublicId(publicid); + dtd.setLocation(systemid); + + xmlCatalog.addDTD(dtd); + } + + /** + *Add a URI catalog entry to the controlling XMLCatalog instance. + * ApacheCatalog calls this for each URI entry found in an external + * catalog file.
+ * + * @param URI The URI of the resource + * @param altURI The URI to which the resource should be mapped + * (aka the location) + * @param base The base URL of the resource. If the altURI + * specifies a relative URL/pathname, it is resolved using the + * base. The default base for an external catalog file is the + * directory in which the catalog is located. + * + */ + public void addURIEntry(String uri, + String altURI, + String base) { + + EntityLocation entity = new EntityLocation(); + entity.setBase(base); + entity.setPublicId(uri); + entity.setLocation(altURI); + + xmlCatalog.addEntity(entity); + } + +} //-- ApacheCatalogResolver diff --git a/src/main/org/apache/tools/ant/types/resolver/package.html b/src/main/org/apache/tools/ant/types/resolver/package.html new file mode 100644 index 000000000..944c69eb7 --- /dev/null +++ b/src/main/org/apache/tools/ant/types/resolver/package.html @@ -0,0 +1,23 @@ + +Ant integration with xml-commons resolver. + +These classes enhance the <xmlcatalog>
datatype
+to support external catalog files using the xml-commons resolver, in
+accordance with the
+
+OASIS "Open Catalog" standard. They will be used if and only if
+the xml-commons resolver library is available on the classpath.
Copyright © 2002 Apache Software Foundation. All rights +Reserved.
+ diff --git a/src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateTest.java b/src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateTest.java index 410e1d37a..60eeb50d7 100644 --- a/src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateTest.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateTest.java @@ -121,6 +121,17 @@ public class XmlValidateTest extends BuildFileTest { executeTarget("xmlcatalog"); } + /** + * catalogfiles fileset should be ignored + * if resolver.jar is not present, but will + * be used if it is. either way, test should + * work b/c we have a nested dtd with the same + * entity + */ + public void testXmlCatalogFiles() { + executeTarget("xmlcatalogfiles"); + } + /** * Test nested xmlcatalog definitions */