Browse Source

Make <xmlcatalog> support the OASIS "Open Catalog" standard with

Norman Walsh resolver code from xml-commons.

Submitted by:	Craeg K Strong <cstrong at arielpartners.com>


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@273486 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 22 years ago
parent
commit
ceeb6bff93
14 changed files with 1250 additions and 105 deletions
  1. +4
    -0
      WHATSNEW
  2. +15
    -0
      build.xml
  3. +60
    -4
      docs/manual/CoreTypes/xmlcatalog.html
  4. +6
    -0
      docs/manual/install.html
  5. +11
    -1
      src/etc/testcases/taskdefs/optional/xmlvalidate.xml
  6. +1
    -0
      src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java
  7. +14
    -33
      src/main/org/apache/tools/ant/types/DTDLocation.java
  8. +77
    -0
      src/main/org/apache/tools/ant/types/EntityLocation.java
  9. +165
    -0
      src/main/org/apache/tools/ant/types/ResourceLocation.java
  10. +501
    -67
      src/main/org/apache/tools/ant/types/XMLCatalog.java
  11. +155
    -0
      src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java
  12. +207
    -0
      src/main/org/apache/tools/ant/types/resolver/ApacheCatalogResolver.java
  13. +23
    -0
      src/main/org/apache/tools/ant/types/resolver/package.html
  14. +11
    -0
      src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateTest.java

+ 4
- 0
WHATSNEW View File

@@ -67,6 +67,10 @@ Other changes:
define ids or paths and use Ant's location magic for filename resolutions define ids or paths and use Ant's location magic for filename resolutions
in the XML file. in the XML file.


* <xmlcatalog> will now support external catalogs according to the
OASIS "Open Catalog" standard - if resolver.jar from Apache's
xml-commons is in your CLASSPATH.

Changes from Ant 1.5.1Beta1 to 1.5.1 Changes from Ant 1.5.1Beta1 to 1.5.1
==================================== ====================================




+ 15
- 0
build.xml View File

@@ -51,6 +51,7 @@
<property name="ant.package" value="org/apache/tools/ant"/> <property name="ant.package" value="org/apache/tools/ant"/>
<property name="optional.package" value="${ant.package}/taskdefs/optional"/> <property name="optional.package" value="${ant.package}/taskdefs/optional"/>
<property name="optional.type.package" value="${ant.package}/types/optional"/> <property name="optional.type.package" value="${ant.package}/types/optional"/>
<property name="apache.resolver.type.package" value="${ant.package}/types/resolver"/>
<property name="util.package" value="${ant.package}/util"/> <property name="util.package" value="${ant.package}/util"/>
<property name="regexp.package" value="${util.package}/regexp"/> <property name="regexp.package" value="${util.package}/regexp"/>


@@ -195,6 +196,9 @@
<selector id="needs.xslp"> <selector id="needs.xslp">
<filename name="${optional.package}/XslpLiaison*"/> <filename name="${optional.package}/XslpLiaison*"/>
</selector> </selector>
<selector id="needs.apache.resolver">
<filename name="${apache.resolver.type.package}/**"/>
</selector>
<selector id="needs.junit"> <selector id="needs.junit">
<filename name="${optional.package}/junit/**"/> <filename name="${optional.package}/junit/**"/>
</selector> </selector>
@@ -341,6 +345,9 @@
<available property="xslp.present" <available property="xslp.present"
classname="com.kvisco.xsl.XSLProcessor" classname="com.kvisco.xsl.XSLProcessor"
classpathref="classpath"/> classpathref="classpath"/>
<available property="apache.resolver.present"
classname="org.apache.xml.resolver.tools.CatalogResolver"
classpathref="classpath" />
<available property="xalan.present" <available property="xalan.present"
classname="org.apache.xalan.xslt.XSLTProcessorFactory" classname="org.apache.xalan.xslt.XSLTProcessorFactory"
classpathref="classpath"/> classpathref="classpath"/>
@@ -570,6 +577,7 @@
<selector refid="needs.xalan1" unless="xalan.present"/> <selector refid="needs.xalan1" unless="xalan.present"/>
<selector refid="needs.xalan2" unless="xalan2.present"/> <selector refid="needs.xalan2" unless="xalan2.present"/>
<selector refid="needs.xslp" unless="xslp.present"/> <selector refid="needs.xslp" unless="xslp.present"/>
<selector refid="needs.apache.resolver" unless="apache.resolver.present"/>
<selector refid="needs.junit" unless="junit.present"/> <selector refid="needs.junit" unless="junit.present"/>
<selector refid="needs.jakarta.regexp" <selector refid="needs.jakarta.regexp"
unless="jakarta.regexp.present"/> unless="jakarta.regexp.present"/>
@@ -739,6 +747,7 @@
<selector refid="needs.xalan1"/> <selector refid="needs.xalan1"/>
<selector refid="needs.xalan2"/> <selector refid="needs.xalan2"/>
<selector refid="needs.xslp"/> <selector refid="needs.xslp"/>
<selector refid="needs.apache.resolver"/>
<selector refid="needs.junit"/> <selector refid="needs.junit"/>
<selector refid="needs.jakarta.regexp"/> <selector refid="needs.jakarta.regexp"/>
<selector refid="needs.jakarta.oro"/> <selector refid="needs.jakarta.oro"/>
@@ -792,6 +801,12 @@
<selector refid="needs.xslp"/> <selector refid="needs.xslp"/>
</jar> </jar>


<jar destfile="${build.lib}/${optional.jars.prefix}-apache-resolver.jar"
basedir="${build.classes}"
manifest="${manifest.tmp}">
<selector refid="needs.apache.resolver"/>
</jar>

<jar destfile="${build.lib}/${optional.jars.prefix}-junit.jar" <jar destfile="${build.lib}/${optional.jars.prefix}-junit.jar"
basedir="${build.classes}" basedir="${build.classes}"
manifest="${manifest.tmp}"> manifest="${manifest.tmp}">


+ 60
- 4
docs/manual/CoreTypes/xmlcatalog.html View File

@@ -19,6 +19,10 @@ documents
to efficiently allow a local substitution for a resource available on the to efficiently allow a local substitution for a resource available on the
web. web.
</p> </p>
<p><b>Note:</b> This task <em>uses, but does not depend on</em> external
libraries not included in the Ant distribution. See <a
href="../install.html#librarydependencies">Library Dependencies</a> for more
information.</p>
<p>This data type provides a catalog of resource locations based <p>This data type provides a catalog of resource locations based
on the <a on the <a
href="http://oasis-open.org/committees/entity/spec-2001-08-06.html"> href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
@@ -59,17 +63,34 @@ task uses XMLCatalogs for both entity and URI resolution.</p>
<p>XMLCatalogs are specified as either a reference to another XMLCatalog, <p>XMLCatalogs are specified as either a reference to another XMLCatalog,
defined defined
previously in a build file, or as a list of <code>dtd</code> or previously in a build file, or as a list of <code>dtd</code> or
<code>entity</code> locations. A separate classpath for entity resolution
<code>entity</code> locations. In addition, external catalog files
may be specified in <code>catalogfiles</code> 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 <code>classpath</code> elements; may be specified inline via nested <code>classpath</code> elements;
otherwise the system classpath is used for this as well.</p> otherwise the system classpath is used for this as well.</p>
<p>XMLCatalogs can also be nested inside other XMLCatalogs. For <p>XMLCatalogs can also be nested inside other XMLCatalogs. For
example, a "superset" XMLCatalog could be made by including several example, a "superset" XMLCatalog could be made by including several
nested XMLCatalogs that referred to other, previously defined nested XMLCatalogs that referred to other, previously defined
XMLCatalogs.</p> XMLCatalogs.</p>
<p>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 <a
href="http://oasis-open.org/committees/entity/background/9401.html">
plain text format</a> or <a
href="http://www.oasis-open.org/committees/entity/spec-2001-08-06.html">
XML format</a>. If the xml-commons resolver library is not found in the
classpath, external catalog files, specified in <code>catalogfiles</code>
filesets, will be ignored and a warning will be logged. In this case, however,
processing of inline entries will proceed normally.</p>
<p>Currently, only <code>&lt;dtd&gt;</code> and <p>Currently, only <code>&lt;dtd&gt;</code> and
<code>&lt;entity&gt;</code> elements may be specified inline; these <code>&lt;entity&gt;</code> elements may be specified inline; these
roughly correspond to OASIS catalog entry types <code>PUBLIC</code> and roughly correspond to OASIS catalog entry types <code>PUBLIC</code> and
<code>URI</code> respectively.</p>
<code>URI</code> respectively. By contrast, external catalog files
may use any of the entry types defined in the
<a href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
+OASIS specification</a>.</p>
<h3><a name="ResolverAlgorithm">Entity/DTD/URI Resolution Algorithm</a></h3> <h3><a name="ResolverAlgorithm">Entity/DTD/URI Resolution Algorithm</a></h3>


When an entity, DTD, or URI is looked up by the XML processor, the When an entity, DTD, or URI is looked up by the XML processor, the
@@ -101,6 +122,18 @@ will <em>not</em> resolve an entity whose <code>location</code> is
<code>blat.dtd</code>. <code>blat.dtd</code>.




<h4>3a. Apache xml-commons resolver lookup</h4>

<p>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 <a
href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">OASIS
standard</a>).</p>

<h4>3. URL-space lookup</h4> <h4>3. URL-space lookup</h4>


<p>Finally, we attempt to make a URL out of the <code>location</code>. <p>Finally, we attempt to make a URL out of the <code>location</code>.
@@ -171,6 +204,22 @@ basedir.
<p>The classpath to use for <a href="#ResolverAlgorithm">entity <p>The classpath to use for <a href="#ResolverAlgorithm">entity
resolution</a>. The nested <code>&lt;classpath&gt;</code> is a resolution</a>. The nested <code>&lt;classpath&gt;</code> is a
<a href="../using.html#path">path</a>-like structure.</p> <a href="../using.html#path">path</a>-like structure.</p>
<h4>catalogfiles</h4>
<p>
The nested <code>catalogfiles</code> element specifies a <a
href="../CoreTypes/fileset.html">FileSet</a>. All files included in
this fileset are assumed to be OASIS catalog files, in either
<a href="http://oasis-open.org/committees/entity/background/9401.html">
plain text format</a> or <a
href="http://www.oasis-open.org/committees/entity/spec-2001-08-06.html">
XML format</a>. Multiple <code>catalogfiles</code> 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
<code>catalogfiles</code> will be ignored and a warning will be
logged.
</p>
<h3>Examples</h3> <h3>Examples</h3>
<p>Set up an XMLCatalog with a single dtd referenced locally in a user's <p>Set up an XMLCatalog with a single dtd referenced locally in a user's
home home
@@ -197,7 +246,8 @@ filesystem (relative to the Ant project basedir) or in the classpath:
</pre></blockquote> </pre></blockquote>


<p>Set up an XMLCatalog with a combination of DTDs and entities as <p>Set up an XMLCatalog with a combination of DTDs and entities as
well as a nested XMLCatalog:</p>
well as a nested XMLCatalog and external catalog files in both
formats:</p>


<blockquote><pre> <blockquote><pre>
&lt;xmlcatalog id=&quot;allcatalogs&quot;&gt; &lt;xmlcatalog id=&quot;allcatalogs&quot;&gt;
@@ -207,7 +257,13 @@ well as a nested XMLCatalog:</p>
&lt;entity &lt;entity
publicId=&quot;LargeLogo&quot; publicId=&quot;LargeLogo&quot;
location=&quot;com/arielpartners/images/ariel-logo-large.gif&quot;/&gt; location=&quot;com/arielpartners/images/ariel-logo-large.gif&quot;/&gt;
&lt;xmlcatalog refid="commonDTDs"/&gt;
&lt;xmlcatalog refid="commonDTDs"/&gt;
&lt;catalogfiles
dir=&quot;/anetwork/drive&quot;
includes=&quot;**/catalog&quot;/&gt;
&lt;catalogfiles
dir=&quot;/my/catalogs&quot;
includes=&quot;**/catalog.xml&quot;/&gt;
&lt;/xmlcatalog&gt; &lt;/xmlcatalog&gt;
</pre></blockquote> </pre></blockquote>
<p>To reference the above XMLCatalog in an <code>xslt</code> task:<p> <p>To reference the above XMLCatalog in an <code>xslt</code> task:<p>


+ 6
- 0
docs/manual/install.html View File

@@ -381,6 +381,12 @@ Installing Ant / Optional Tasks</a> section above.</p>
<td><a href="http://www.clarkware.com/software/JDepend.html" <td><a href="http://www.clarkware.com/software/JDepend.html"
target="_top">http://www.clarkware.com/software/JDepend.html</a></td> target="_top">http://www.clarkware.com/software/JDepend.html</a></td>
</tr> </tr>
<tr>
<td>resolver.jar</td>
<td>xmlcatalog datatype <em>only if support for external catalog files is desired</em></td>
<td><a href="http://xml.apache.org/dist/commons"
target="_top">http://xml.apache.org/dist/commons</a></td>
</tr>
</table> </table>
<br> <br>
<hr> <hr>


+ 11
- 1
src/etc/testcases/taskdefs/optional/xmlvalidate.xml View File

@@ -38,8 +38,18 @@
</xmlcatalog> </xmlcatalog>
</xmlvalidate> </xmlvalidate>
</target> </target>
<target name="xmlcatalogfiles">
<xmlvalidate warn="false">
<fileset dir="xml" includes="**/about.xml"/>
<xmlcatalog classpath="xml">
<catalogfiles dir="xml" includes="catalog"/>
<dtd publicID="-//stevo//DTD doc 1.0//EN"
location="doc.dtd"/>
</xmlcatalog>
</xmlvalidate>
</target>


<target name="testSchemaGood"> <target name="testSchemaGood">
<xmlvalidate warn="false" lenient="no" > <xmlvalidate warn="false" lenient="no" >
<fileset dir="xml" includes="endpiece.xml"/> <fileset dir="xml" includes="endpiece.xml"/>


+ 1
- 0
src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java View File

@@ -67,6 +67,7 @@ import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference; import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.XMLCatalog; import org.apache.tools.ant.types.XMLCatalog;
import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.FileUtils;
import javax.xml.transform.URIResolver;


/** /**
* Processes a set of XML documents via XSLT. This is * Processes a set of XML documents via XSLT. This is


+ 14
- 33
src/main/org/apache/tools/ant/types/DTDLocation.java View File

@@ -54,44 +54,25 @@
package org.apache.tools.ant.types; package org.apache.tools.ant.types;


/** /**
* Helper class to handle the DTD and Entity nested elements.
* <p>Helper class to handle the DTD nested element. Instances of
* this class correspond to the <code>PUBLIC</code> catalog entry type
* of the <a
* href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
* OASIS "Open Catalog" standard</a>.</p>
* *
* <p>Possible Future Enhancement: Bring the Ant element name into
* conformance with the OASIS standard.</p>
*
* @see org.apache.xml.resolver.Catalog
* @author Conor MacNeill * @author Conor MacNeill
* @author dIon Gillard * @author dIon Gillard
* @author <a href="mailto:cstrong@arielpartners.com">Craeg Strong</a>
* @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;
}
} }


+ 77
- 0
src/main/org/apache/tools/ant/types/EntityLocation.java View File

@@ -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
* <http://www.apache.org/>.
*/
package org.apache.tools.ant.types;

/**
* Helper class to handle the Entity nested element. Instances of
* this class correspond to the <code>URI</code> catalog entry type of
* the <a
* href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
* OASIS "Open Catalog" standard</a>.</p>
*
* <p>Possible Future Enhancement: Bring the Ant element name into
* conformance with the OASIS standard.</p>
*
* @see org.apache.xml.resolver.Catalog
* @author Conor MacNeill
* @author dIon Gillard
* @author <a href="mailto:cstrong@arielpartners.com">Craeg Strong</a>
*/
public class EntityLocation extends ResourceLocation {

public EntityLocation() {
super("URI");
}

}

+ 165
- 0
src/main/org/apache/tools/ant/types/ResourceLocation.java View File

@@ -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
* <http://www.apache.org/>.
*/
package org.apache.tools.ant.types;

import java.net.URL;

/**
* <p>Helper class to handle the <code>&lt;dtd&gt;</code> and
* <code>&lt;entity&gt;</code> nested elements. These correspond to
* the <code>PUBLIC</code> and <code>URI</code> catalog entry types,
* respectively, as defined in the <a
* href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
* OASIS "Open Catalog" standard</a>.</p>
*
* <p>Possible Future Enhancements:
* <ul>
* <li>Bring the Ant element names into conformance with the OASIS standard</li>
* <li>Add support for additional OASIS catalog entry types</li>
* </ul>
* </p>
*
* @see org.apache.xml.resolver.Catalog
* @author Conor MacNeill
* @author dIon Gillard
* @author <a href="mailto:cstrong@arielpartners.com">Craeg Strong</a>
* @version $Id$
*/
public class ResourceLocation {

//-- Fields ----------------------------------------------------------------

/**
* name of the catalog entry type, as per OASIS spec.
*/
protected String name = null;

/** publicId of the dtd/entity. */
private String publicId = null;

/** location of the dtd/entity - a file/resource/URL. */
private String location = null;

/**
* base URL of the dtd/entity, or null. If null, the Ant project
* basedir is assumed. If the location specifies a relative
* URL/pathname, it is resolved using the base. The default base
* for an external catalog file is the directory in which it is
* located.
*/
private String base = null;

//-- Methods ---------------------------------------------------------------

protected ResourceLocation(String name) {
this.name = name;
}
/**
* @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;
}

/**
* @param base the base URL of the resource associated with the
* publicId. If the location specifies a relative URL/pathname,
* it is resolved using the base. The default base for an
* external catalog file is the directory in which it is located.
*/
public void setBase(String base) {
this.base = base;
}

/**
* @return the publicId of the resource.
*/
public String getPublicId() {
return publicId;
}

/**
* @return the location of the resource identified by the publicId.
*/
public String getLocation() {
return location;
}

/**
* @return the base of the resource identified by the publicId.
*/
public String getBase() {
return base;
}

/**
* @return the name of the catalog entry type. Currently this is
* one of <code>PUBLIC</code> or <code>URI</code>.
*
* @see org.apache.xml.resolver.Catalog
*/
public String getName() {
return name;
}

} //-- ResourceLocation

+ 501
- 67
src/main/org/apache/tools/ant/types/XMLCatalog.java View File

@@ -54,6 +54,8 @@


package org.apache.tools.ant.types; package org.apache.tools.ant.types;


import java.lang.reflect.Method;

import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@@ -72,6 +74,7 @@ import javax.xml.transform.URIResolver;
import javax.xml.transform.sax.SAXSource; import javax.xml.transform.sax.SAXSource;
import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project; import org.apache.tools.ant.Project;
import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.FileUtils;
import org.xml.sax.EntityResolver; import org.xml.sax.EntityResolver;
@@ -138,13 +141,14 @@ import org.xml.sax.XMLReader;
* @author <a href="mailto:cstrong@arielpartners.com">Craeg Strong</a> * @author <a href="mailto:cstrong@arielpartners.com">Craeg Strong</a>
* @version $Id$ * @version $Id$
*/ */
public class XMLCatalog extends DataType implements Cloneable, EntityResolver, URIResolver {
public class XMLCatalog extends DataType
implements Cloneable, EntityResolver, URIResolver {
/** File utilities instance */ /** File utilities instance */
private FileUtils fileUtils = FileUtils.newFileUtils(); private FileUtils fileUtils = FileUtils.newFileUtils();


//-- Fields ---------------------------------------------------------------- //-- Fields ----------------------------------------------------------------


/** holds dtd/entity objects until needed */
/** Holds dtd/entity objects and catalog filesets until needed. */
private Vector elements = new Vector(); private Vector elements = new Vector();


/** /**
@@ -152,6 +156,14 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
*/ */
private Path classpath; 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 --------------------------------------------------------------- //-- Methods ---------------------------------------------------------------


public XMLCatalog() { 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() { private Vector getElements() {
return elements; 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
* <em>and</em> 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 <em>and</em> 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) { private void setElements(Vector aVector) {
if (isReference()) {
throw noChildrenAllowed();
}
elements = aVector; elements = aVector;
} }


@@ -237,6 +255,22 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
setChecked( false ); setChecked( false );
} }


/** Creates the nested <code>&lt;catalogfiles&gt;</code> element. Not
* allowed if this catalog is itself a reference to another catalog -- that
* is, a catalog cannot both refer to another <em>and</em> 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 <code>&lt;dtd&gt;</code> element. Not * Creates the nested <code>&lt;dtd&gt;</code> element. Not
* allowed if this catalog is itself a reference to another * 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 * @exception BuildException if this is a reference and no nested
* elements are allowed. * elements are allowed.
*/ */
public void addDTD(DTDLocation dtd) throws BuildException {
public void addDTD(ResourceLocation dtd) throws BuildException {
if (isReference()) { if (isReference()) {
throw noChildrenAllowed(); throw noChildrenAllowed();
} }
@@ -256,6 +290,9 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
getElements().addElement(dtd); getElements().addElement(dtd);
setChecked( false ); setChecked( false );
} }
public void addDTD(DTDLocation dtd) throws BuildException {
addDTD((ResourceLocation)dtd);
}


/** /**
* Creates the nested <code>&lt;entity&gt;</code> element. Not * Creates the nested <code>&lt;entity&gt;</code> 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 * catalog -- that is, a catalog cannot both refer to another
* <em>and</em> contain elements or other attributes. * <em>and</em> 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 * @exception BuildException if this is a reference and no nested
* elements are allowed. * 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 <code>&lt;entity&gt;</code> element. Not
* allowed if this catalog is itself a reference to another
* catalog -- that is, a catalog cannot both refer to another
* <em>and</em> 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) public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException { 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 + "'", log("resolveEntity: '" + publicId + "': '" + systemId + "'",
Project.MSG_DEBUG); Project.MSG_DEBUG);


InputSource inputSource = resolveEntityImpl(publicId );
InputSource inputSource =
getCatalogResolver().resolveEntity(publicId, systemId);


if (inputSource == null) { if (inputSource == null) {
log("No matching catalog entry found, parser will use: '" + 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) public Source resolve(String href, String base)
throws TransformerException { 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; SAXSource source = null;


@@ -389,11 +448,11 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
// //
source = new SAXSource(); source = new SAXSource();
try 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) { catch (MalformedURLException ex) {
// At this point we are probably in failure mode, but // At this point we are probably in failure mode, but
// try to use the bare URI as a last gasp // 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.
* <p> 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.</p>
* <p>This is an application of the Gang of Four Strategy Pattern
* combined with Template Method.</p>
* *
* @param publicId the publicId of the Resource for which local information * @param publicId the publicId of the Resource for which local information
* is required * 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 * 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 any <catalogfiles> are specified, warn that they
// will be ignored.
//
Enumeration enum = getElements().elements();
while (enum.hasMoreElements()) {
Object o = enum.nextElement();
if (o instanceof FileSet) {
log("Warning: External catalogfiles will be ignored",
Project.MSG_WARN);
break;
}
}
} }
} }
return null;
return catalogResolver;
} }



/** /**
* <p>This is called from the URIResolver to set an EntityResolver * <p>This is called from the URIResolver to set an EntityResolver
* on the SAX parser to be used for new XML documents that are * 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); 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. * Utility method to remove trailing fragment from a URI.
* For example, * 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 <code>null</code> * @return An InputSource for reading the file, or <code>null</code>
* if the file does not exist or is not readable. * if the file does not exist or is not readable.
*/ */
private InputSource filesystemLookup(DTDLocation matchingEntry) {
private InputSource filesystemLookup(ResourceLocation matchingEntry) {


String uri = matchingEntry.getLocation(); 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 // location attribute. This is resolved using the appropriate
// base. // 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 <code>null</code> * @return An InputSource for reading the resource, or <code>null</code>
* if the resource does not exist in the classpath or is not readable. * 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; 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 <code>null</code> * @return An InputSource for reading the resource, or <code>null</code>
* if the resource does not identify a valid URL or is not readable. * 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. * 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. * Implements the guts of the resolve() lookup strategy.
@@ -636,7 +762,7 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
SAXSource result = null; SAXSource result = null;
InputSource source = null; InputSource source = null;


DTDLocation matchingEntry = findMatchingEntry(href);
ResourceLocation matchingEntry = findMatchingEntry(href);


if (matchingEntry != null) { if (matchingEntry != null) {


@@ -661,4 +787,312 @@ public class XMLCatalog extends DataType implements Cloneable, EntityResolver, U
} }
return result; 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
* <code>&lt;catalogfiles&gt;</code> 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;
}
}
} }

+ 155
- 0
src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java View File

@@ -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
* <http://www.apache.org/>.
*/

package org.apache.tools.ant.types.resolver;

import org.apache.xml.resolver.Catalog;
import org.apache.xml.resolver.CatalogEntry;

import org.apache.xml.resolver.helpers.Debug;
import org.apache.xml.resolver.helpers.PublicId;

/**
* This class extends the Catalog class provided by Norman Walsh's
* resolver library in xml-commons in order to add classpath entity
* and URI resolution. Since XMLCatalog already does classpath
* resolution, we simply add all CatalogEntry instances back to the
* controlling XMLCatalog instance. This is done via a callback
* mechanism. ApacheCatalog is <em>only</em> used for external
* catalog files. Inline entries (currently <code>&lt;dtd&gt;</code>
* and <code>&lt;entity&gt;</code>) 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 <a href="mailto:cstrong@arielpartners.com">Craeg Strong</a>
* @version $Id$
*/
public class ApacheCatalog extends Catalog {

/** The resolver object to callback. */
private ApacheCatalogResolver resolver = null;

/**
* <p>Create a new ApacheCatalog instance.</p>
*
* <p>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.</p>
*/
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;
}
/**
* <p>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.</p>
*
* <p>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.</p>
*
* <p>XMLCatalog currently only understands <code>PUBLIC</code>
* and <code>URI</code> entry types, so we ignore the other types.</p>
*
* @param entry The CatalogEntry to process.
*/
public void addEntry(CatalogEntry entry) {

int type = entry.getEntryType();

if (type == PUBLIC) {

String publicid = PublicId.normalize(entry.getEntryArg(0));
String systemid = normalizeURI(entry.getEntryArg(1));

if (resolver == null) {
Debug.message(1, "Internal Error: null ApacheCatalogResolver");
}
else {
resolver.addPublicEntry(publicid, systemid, base.toExternalForm());
}

} else if (type == URI) {
String uri = normalizeURI(entry.getEntryArg(0));
String altURI = normalizeURI(entry.getEntryArg(1));

if (resolver == null) {
Debug.message(1, "Internal Error: null ApacheCatalogResolver");
}
else {
resolver.addURIEntry(uri, altURI, base.toExternalForm());
}

}
super.addEntry(entry);
}

} //- ApacheCatalog

+ 207
- 0
src/main/org/apache/tools/ant/types/resolver/ApacheCatalogResolver.java View File

@@ -0,0 +1,207 @@
/*
* 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
* <http://www.apache.org/>.
*/

package org.apache.tools.ant.types.resolver;

import java.io.IOException;

import java.net.MalformedURLException;

import org.apache.tools.ant.BuildException;

import org.apache.tools.ant.types.XMLCatalog;
import org.apache.tools.ant.types.DTDLocation;
import org.apache.tools.ant.types.EntityLocation;

import org.apache.xml.resolver.Catalog;
import org.apache.xml.resolver.CatalogManager;

import org.apache.xml.resolver.tools.CatalogResolver;

/**
* <p>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.</p>
*
* <p>The {@link org.apache.tools.ant.types.resolver.ApacheCatalog
* ApacheCatalog} class is used to parse external catalog files, which
* can be in either <a
* href="http://oasis-open.org/committees/entity/background/9401.html">
* plain text format</a> or <a
* href="http://www.oasis-open.org/committees/entity/spec-2001-08-06.html">
* XML format</a>.</p>
*
* <p>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.</p>
*
* @see org.apache.tools.ant.types.XMLCatalog.CatalogResolver
* @see org.apache.xml.resolver.CatalogManager
* @author <a href="mailto:cstrong@arielpartners.com">Craeg Strong</a>
* @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 <code>&lt;catalogfiles&gt;</code> 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);
}
}

/**
* <p>Add a PUBLIC catalog entry to the controlling XMLCatalog instance.
* ApacheCatalog calls this for each PUBLIC entry found in an external
* catalog file.</p>
*
* @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);
}

/**
* <p>Add a URI catalog entry to the controlling XMLCatalog instance.
* ApacheCatalog calls this for each URI entry found in an external
* catalog file.</p>
*
* @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

+ 23
- 0
src/main/org/apache/tools/ant/types/resolver/package.html View File

@@ -0,0 +1,23 @@
<body>
Ant integration with xml-commons resolver.

<p>These classes enhance the <code>&lt;xmlcatalog&gt;</code> datatype
to support external catalog files using the xml-commons resolver, in
accordance with the
<a href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
OASIS "Open Catalog" standard</a>. They will be used if and only if
the xml-commons resolver library is available on the classpath.</p>

@see <A HREF="http://xml.apache.org/commons">Apache xml-commons Project</A>

@see org.apache.tools.ant.types.XMLCatalog
@see org.apache.tools.ant.types.resolver.ApacheCatalogResolver
@see org.apache.tools.ant.types.resolver.ApacheCatalog

@author <A HREF="mailto:cstrong@arielpartners.com">Craeg Strong</A>


<hr>
<p align="center">Copyright &copy; 2002 Apache Software Foundation. All rights
Reserved.</p>
</body>

+ 11
- 0
src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateTest.java View File

@@ -121,6 +121,17 @@ public class XmlValidateTest extends BuildFileTest {
executeTarget("xmlcatalog"); 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 * Test nested xmlcatalog definitions
*/ */


Loading…
Cancel
Save