mbenson 2005/02/03 11:18:07
Modified: . WHATSNEW
src/main/org/apache/tools/ant/types FilterSet.java
src/testcases/org/apache/tools/ant/types FilterSetTest.java
docs/manual/CoreTypes filterset.html
Log:
Add recurse attribute to filterset.
Revision Changes Path
1.740 +3 -0 ant/WHATSNEW
Index: WHATSNEW
===================================================================
RCS file: /home/cvs/ant/WHATSNEW,v
retrieving revision 1.739
retrieving revision 1.740
diff -u -r1.739 -r1.740
--- WHATSNEW 2 Feb 2005 23:55:49 -0000 1.739
+++ WHATSNEW 3 Feb 2005 19:18:06 -0000 1.740
@@ -205,6 +205,9 @@
* <native2ascii> now also supports Kaffe's version.
+* Recursive token expansion in a filterset can now be disabled by
+ setting its recurse attribute to false.
+
Fixed bugs:
-----------
1.31 +116 -105 ant/src/main/org/apache/tools/ant/types/FilterSet.java
Index: FilterSet.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/types/FilterSet.java,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -r1.30 -r1.31
--- FilterSet.java 12 Nov 2004 15:15:22 -0000 1.30
+++ FilterSet.java 3 Feb 2005 19:18:07 -0000 1.31
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2004 The Apache Software Foundation
+ * Copyright 2001-2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
*/
package org.apache.tools.ant.types;
-// java io classes
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -24,10 +23,10 @@
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;
+
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
-
/**
* A set of filters to be applied to something.
*
@@ -37,64 +36,64 @@
public class FilterSet extends DataType implements Cloneable {
/**
- * Individual filter component of filterset
+ * Individual filter component of filterset.
*
*/
public static class Filter {
- /** Token which will be replaced in the filter operation */
+ /** Token which will be replaced in the filter operation. */
String token;
- /** The value which will replace the token in the filtering
operation */
+ /** The value which will replace the token in the filtering
operation. */
String value;
/**
- * Constructor for the Filter object
+ * Constructor for the Filter object.
*
- * @param token The token which will be replaced when filtering
- * @param value The value which will replace the token when
filtering
+ * @param token The token which will be replaced when filtering.
+ * @param value The value which will replace the token when
filtering.
*/
public Filter(String token, String value) {
- this.token = token;
- this.value = value;
+ setToken(token);
+ setValue(value);
}
/**
- * No argument conmstructor
+ * No-argument conmstructor.
*/
public Filter() {
}
/**
- * Sets the Token attribute of the Filter object
+ * Sets the Token attribute of the Filter object.
*
- * @param token The new Token value
+ * @param token The new Token value.
*/
public void setToken(String token) {
this.token = token;
}
/**
- * Sets the Value attribute of the Filter object
+ * Sets the Value attribute of the Filter object.
*
- * @param value The new Value value
+ * @param value The new Value value.
*/
public void setValue(String value) {
this.value = value;
}
/**
- * Gets the Token attribute of the Filter object
+ * Gets the Token attribute of the Filter object.
*
- * @return The Token value
+ * @return The Token value.
*/
public String getToken() {
return token;
}
/**
- * Gets the Value attribute of the Filter object
+ * Gets the Value attribute of the Filter object.
*
- * @return The Value value
+ * @return The Value value.
*/
public String getValue() {
return value;
@@ -108,7 +107,7 @@
public class FiltersFile {
/**
- * Constructor for the Filter object
+ * Constructor for the FiltersFile object.
*/
public FiltersFile() {
}
@@ -132,19 +131,27 @@
private String startOfToken = DEFAULT_TOKEN_START;
private String endOfToken = DEFAULT_TOKEN_END;
+ /** Contains a list of parsed tokens */
+ private Vector passedTokens;
+ /** if a duplicate token is found, this is set to true */
+ private boolean duplicateToken = false;
+
+ private boolean recurse = true;
+ private Hashtable filterHash = null;
+
/**
* List of ordered filters and filter files.
*/
private Vector filters = new Vector();
/**
- * Default constructor
+ * Default constructor.
*/
public FilterSet() {
}
/**
- * Create a Filterset from another filterset
+ * Create a Filterset from another filterset.
*
* @param filterset the filterset upon which this filterset will be
based.
*/
@@ -154,11 +161,11 @@
}
/**
- * Get the filters in the filter set
+ * Get the filters in the filter set.
*
- * @return a Vector of Filter instances
+ * @return a Vector of Filter instances.
*/
- protected Vector getFilters() {
+ protected synchronized Vector getFilters() {
if (isReference()) {
return getRef().getFilters();
}
@@ -166,7 +173,7 @@
}
/**
- * Get the referred filter set
+ * Get the referenced filter set.
*
* @return the filterset from the reference.
*/
@@ -179,21 +186,23 @@
*
* @return The hash of the tokens and values for quick lookup.
*/
- public Hashtable getFilterHash() {
- int filterSize = getFilters().size();
- Hashtable filterHash = new Hashtable(filterSize + 1);
- for (Enumeration e = getFilters().elements(); e.hasMoreElements();) {
- Filter filter = (Filter) e.nextElement();
- filterHash.put(filter.getToken(), filter.getValue());
+ public synchronized Hashtable getFilterHash() {
+ if (filterHash == null) {
+ filterHash = new Hashtable(getFilters().size());
+ for (Enumeration e = getFilters().elements();
e.hasMoreElements();) {
+ Filter filter = (Filter) e.nextElement();
+ filterHash.put(filter.getToken(), filter.getValue());
+ }
}
return filterHash;
}
/**
- * set the file containing the filters for this filterset.
+ * Set the file containing the filters for this filterset.
*
- * @param filtersFile sets the filter fil to read filters for this
filter set from.
- * @exception BuildException if there is a problem reading the filters
+ * @param filtersFile sets the filter file from which to read filters
+ * for this filter set.
+ * @exception BuildException if there is a problem reading the filters.
*/
public void setFiltersfile(File filtersFile) throws BuildException {
if (isReference()) {
@@ -203,9 +212,9 @@
}
/**
- * The string used to id the beginning of a token.
+ * Set the string used to id the beginning of a token.
*
- * @param startOfToken The new Begintoken value
+ * @param startOfToken The new Begintoken value.
*/
public void setBeginToken(String startOfToken) {
if (isReference()) {
@@ -218,9 +227,9 @@
}
/**
- * Get the begin token for this filterset
+ * Get the begin token for this filterset.
*
- * @return the filter set's begin token for filtering
+ * @return the filter set's begin token for filtering.
*/
public String getBeginToken() {
if (isReference()) {
@@ -229,11 +238,10 @@
return startOfToken;
}
-
/**
- * The string used to id the end of a token.
+ * Set the string used to id the end of a token.
*
- * @param endOfToken The new Endtoken value
+ * @param endOfToken The new Endtoken value.
*/
public void setEndToken(String endOfToken) {
if (isReference()) {
@@ -246,9 +254,9 @@
}
/**
- * Get the end token for this filterset
+ * Get the end token for this filterset.
*
- * @return the filter set's end token for replacement delimiting
+ * @return the filter set's end token for replacement delimiting.
*/
public String getEndToken() {
if (isReference()) {
@@ -257,24 +265,36 @@
return endOfToken;
}
+ /**
+ * Set whether recursive token expansion is enabled.
+ * @param recurse <code>boolean</code> whether to recurse.
+ */
+ public void setRecurse(boolean recurse) {
+ this.recurse = recurse;
+ }
+
+ /**
+ * Get whether recursive token expansion is enabled.
+ * @return <code>boolean</code> whether enabled.
+ */
+ public boolean isRecurse() {
+ return recurse;
+ }
/**
* Read the filters from the given file.
*
- * @param filtersFile the file from which filters are read
- * @exception BuildException Throw a build exception when unable to
read the
- * file.
+ * @param filtersFile the file from which filters are read.
+ * @exception BuildException when the file cannot be read.
*/
- public void readFiltersFromFile(File filtersFile) throws BuildException {
+ public synchronized void readFiltersFromFile(File filtersFile) throws
BuildException {
if (isReference()) {
throw tooManyAttributes();
}
-
if (!filtersFile.exists()) {
throw new BuildException("Could not read filters from file "
+ filtersFile + " as it doesn't
exist.");
}
-
if (filtersFile.isFile()) {
log("Reading filters from " + filtersFile, Project.MSG_VERBOSE);
FileInputStream in = null;
@@ -315,10 +335,10 @@
* This resets the passedTokens and calls iReplaceTokens to
* do the actual replacements.
*
- * @param line The line to process the tokens in.
- * @return The string with the tokens replaced.
+ * @param line The line in which to process embedded tokens.
+ * @return The input string after token replacement.
*/
- public String replaceTokens(String line) {
+ public synchronized String replaceTokens(String line) {
passedTokens = null; // reset for new line
return iReplaceTokens(line);
}
@@ -331,7 +351,7 @@
* @param line The line to process the tokens in.
* @return The string with the tokens replaced.
*/
- private String iReplaceTokens(String line) {
+ private synchronized String iReplaceTokens(String line) {
String beginToken = getBeginToken();
String endToken = getEndToken();
int index = line.indexOf(beginToken);
@@ -345,6 +365,7 @@
String value = null;
do {
+ //can't have zero-length token
int endIndex = line.indexOf(endToken,
index + beginToken.length() + 1);
if (endIndex == -1) {
@@ -355,7 +376,7 @@
b.append(line.substring(i, index));
if (tokens.containsKey(token)) {
value = (String) tokens.get(token);
- if (!value.equals(token)) {
+ if (recurse && !value.equals(token)) {
// we have another token, let's parse it.
value = replaceTokens(value, token);
}
@@ -381,48 +402,40 @@
}
}
- /** Contains a list of parsed tokens */
- private Vector passedTokens;
- /** if a ducplicate token is found, this is set to true */
- private boolean duplicateToken = false;
-
/**
* This parses tokens which point to tokens.
* It also maintains a list of currently used tokens, so we cannot
- * get into an infinite loop
- * @param line the value / token to parse
- * @param parent the parant token (= the token it was parsed from)
+ * get into an infinite loop.
+ * @param line the value / token to parse.
+ * @param parent the parent token (= the token it was parsed from).
*/
- private String replaceTokens(String line, String parent)
+ private synchronized String replaceTokens(String line, String parent)
throws BuildException {
+ String beginToken = getBeginToken();
+ String endToken = getEndToken();
if (passedTokens == null) {
passedTokens = new Vector();
}
if (passedTokens.contains(parent) && !duplicateToken) {
duplicateToken = true;
- StringBuffer sb = new StringBuffer();
- sb.append("Infinite loop in tokens. Currently known tokens : ");
- sb.append(passedTokens);
- sb.append("\nProblem token : " + getBeginToken() + parent
- + getEndToken());
- sb.append(" called from " + getBeginToken()
- + passedTokens.lastElement());
- sb.append(getEndToken());
- System.out.println(sb.toString());
+ System.out.println(
+ "Infinite loop in tokens. Currently known tokens : "
+ + passedTokens.toString() + "\nProblem token : " + beginToken
+ + parent + endToken + " called from " + beginToken
+ + passedTokens.lastElement().toString() + endToken);
return parent;
}
passedTokens.addElement(parent);
String value = iReplaceTokens(line);
- if (value.indexOf(getBeginToken()) == -1 && !duplicateToken) {
+ if (value.indexOf(beginToken) == -1 && !duplicateToken) {
duplicateToken = false;
passedTokens = null;
} else if (duplicateToken) {
// should always be the case...
if (passedTokens.size() > 0) {
- value = (String) passedTokens.lastElement();
- passedTokens.removeElementAt(passedTokens.size() - 1);
+ value = (String) passedTokens.remove(passedTokens.size() -
1);
if (passedTokens.size() == 0) {
- value = getBeginToken() + value + getEndToken();
+ value = beginToken + value + endToken;
duplicateToken = false;
}
}
@@ -431,21 +444,22 @@
}
/**
- * Create a new filter
+ * Add a new filter.
*
- * @param filter the filter to be added
+ * @param filter the filter to be added.
*/
- public void addFilter(Filter filter) {
+ public synchronized void addFilter(Filter filter) {
if (isReference()) {
throw noChildrenAllowed();
}
filters.addElement(filter);
+ filterHash = null;
}
/**
- * Create a new FiltersFile
+ * Create a new FiltersFile.
*
- * @return The filter that was created.
+ * @return The filtersfile that was created.
*/
public FiltersFile createFiltersfile() {
if (isReference()) {
@@ -455,49 +469,49 @@
}
/**
- * Add a new filter made from the given token and value.
- *
- * @param token The token for the new filter.
- * @param value The value for the new filter.
- */
- public void addFilter(String token, String value) {
+ * Add a new filter made from the given token and value.
+ *
+ * @param token The token for the new filter.
+ * @param value The value for the new filter.
+ */
+ public synchronized void addFilter(String token, String value) {
if (isReference()) {
throw noChildrenAllowed();
}
- filters.addElement(new Filter(token, value));
+ addFilter(new Filter(token, value));
}
/**
- * Add a Filterset to this filter set
- *
- * @param filterSet the filterset to be added to this filterset
- */
- public void addConfiguredFilterSet(FilterSet filterSet) {
+ * Add a Filterset to this filter set.
+ *
+ * @param filterSet the filterset to be added to this filterset
+ */
+ public synchronized void addConfiguredFilterSet(FilterSet filterSet) {
if (isReference()) {
throw noChildrenAllowed();
}
for (Enumeration e = filterSet.getFilters().elements();
e.hasMoreElements();) {
- filters.addElement(e.nextElement());
+ addFilter((Filter) e.nextElement());
}
}
/**
- * Test to see if this filter set it empty.
+ * Test to see if this filter set has filters.
*
- * @return Return true if there are filter in this set otherwise false.
+ * @return Return true if there are filters in this set.
*/
- public boolean hasFilters() {
+ public synchronized boolean hasFilters() {
return getFilters().size() > 0;
}
/**
- * clone the filterset
+ * Clone the filterset.
*
- * @return a deep clone of this filterset
+ * @return a deep clone of this filterset.
*
* @throws BuildException if the clone cannot be performed.
*/
- public Object clone() throws BuildException {
+ public synchronized Object clone() throws BuildException {
if (isReference()) {
return ((FilterSet) getRef()).clone();
} else {
@@ -511,8 +525,5 @@
}
}
}
-
}
-
-
1.15 +18 -1
ant/src/testcases/org/apache/tools/ant/types/FilterSetTest.java
Index: FilterSetTest.java
===================================================================
RCS file:
/home/cvs/ant/src/testcases/org/apache/tools/ant/types/FilterSetTest.java,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -r1.14 -r1.15
--- FilterSetTest.java 7 Dec 2004 09:10:38 -0000 1.14
+++ FilterSetTest.java 3 Feb 2005 19:18:07 -0000 1.15
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2002,2004 The Apache Software Foundation
+ * Copyright 2001-2002, 2004-2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -99,6 +99,23 @@
assertEquals(result, fs.replaceTokens(line));
}
+ /**
+ * Test to see what happens when the resolving occurs in
+ * what would be an infinite loop, but with recursion disabled.
+ */
+ public void testRecursionDisabled() {
+ String result = "@test1@ line testvalue";
+ String line = "@test@ line @test2@";
+ FilterSet fs = new FilterSet();
+ fs.addFilter("test", "@test1@");
+ fs.addFilter("test1","@test@");
+ fs.addFilter("test2", "testvalue");
+ fs.setBeginToken("@");
+ fs.setEndToken("@");
+ fs.setRecurse(false);
+ assertEquals(result, fs.replaceTokens(line));
+ }
+
public void testNestedFilterSets() {
executeTarget("test-nested-filtersets");
1.15 +86 -79 ant/docs/manual/CoreTypes/filterset.html
Index: filterset.html
===================================================================
RCS file: /home/cvs/ant/docs/manual/CoreTypes/filterset.html,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -r1.14 -r1.15
--- filterset.html 9 Feb 2004 21:50:07 -0000 1.14
+++ filterset.html 3 Feb 2005 19:18:07 -0000 1.15
@@ -1,19 +1,19 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<HTML>
-<HEAD>
- <TITLE>FilterSet Type</TITLE>
-</HEAD>
+<html>
+<head>
+ <title>FilterSet Type</title>
+</head>
-<BODY>
-<H2><A name="filterset">FilterSet</A></H2>
+<body>
+<h2><a name="filterset">FilterSet</a></h2>
-<P>FilterSets are groups of filters. Filters can be defined as token-value
+<p>FilterSets are groups of filters. Filters can be defined as token-value
pairs
or be read in from a file. FilterSets can appear inside tasks that support
this
-feature or at the same level as <CODE><target></CODE> - i.e., as
+feature or at the same level as <code><target></code> - i.e., as
children of
-<CODE><project></CODE>.</P>
+<code><project></code>.</p>
<p>FilterSets support the <code>id</code> and <code>refid</code>
attributes. You can define a FilterSet with an <code>id</code>
@@ -35,92 +35,99 @@
you should ensure that the set of files being filtered are all text files.
</p>
-<H2>Filterset</H2>
+<h2>Filterset</h2>
-<TABLE cellSpacing=0 cellPadding=2 border=1>
- <TR>
- <TD vAlign=top><B>Attribute</B></TD>
- <TD vAlign=top><B>Description</B></TD>
- <TD vAlign=top><B>Default</B></TD>
- <TD vAlign=top align="center"><B>Required</B></TD>
- </TR>
- <TR>
- <TD vAlign=top>begintoken</TD>
- <TD vAlign=top>The string marking the beginning of a token (eg.,
- <code>@DATE@</code>).</TD>
- <TD vAlign=top>@</TD>
- <TD vAlign=top align="center">No</TD>
- </TR>
- <TR>
- <TD vAlign=top>endtoken</TD>
- <TD vAlign=top>The string marking the end of a token (eg.,
- <code>@DATE@</code>).</TD>
- <TD vAlign=top>@</TD>
- <TD vAlign=top align="center">No</TD>
- </TR>
-</TABLE>
-
-<H2>Filter</H2>
-<TABLE cellSpacing=0 cellPadding=2 border=1>
- <TR>
- <TD vAlign=top><B>Attribute</B></TD>
- <TD vAlign=top><B>Description</B></TD>
- <TD vAlign=top align="center"><B>Required</B></TD>
- </TR>
- <TR>
- <TD vAlign=top>token</TD>
- <TD vAlign=top>The token to replace (eg.,
<code>@DATE@</code>)</TD>
- <TD vAlign=top align="center">Yes</TD>
- </TR>
- <TR>
- <TD vAlign=top>value</TD>
- <TD vAlign=top>The value to replace it with
- (eg., <code>Thursday, April 26, 2001</code>).</TD>
- <TD vAlign=top align="center">Yes</TD>
- </TR>
-</TABLE>
-
-<H2>Filtersfile</H2>
-<TABLE cellSpacing=0 cellPadding=2 border=1>
- <TR>
- <TD vAlign=top><B>Attribute</B></TD>
- <TD vAlign=top><B>Description</B></TD>
- <TD vAlign=top align="center"><B>Required</B></TD>
- </TR>
- <TR>
- <TD vAlign=top>file</TD>
- <TD vAlign=top>A properties file of
- name-value pairs from which to load the tokens.</TD>
- <TD vAlign=top align="center">Yes</TD>
- </TR>
-</TABLE>
+<table cellSpacing=0 cellPadding=2 border=1>
+ <tr>
+ <td vAlign=top><b>Attribute</b></td>
+ <td vAlign=top><b>Description</b></td>
+ <td vAlign=top><b>Default</b></td>
+ <td vAlign=top align="center"><b>Required</b></td>
+ </tr>
+ <tr>
+ <td vAlign=top>begintoken</td>
+ <td vAlign=top>The string marking the beginning of a token (eg.,
+ <code>@DATE@</code>).</td>
+ <td vAlign=top>@</td>
+ <td vAlign=top align="center">No</td>
+ </tr>
+ <tr>
+ <td vAlign=top>endtoken</td>
+ <td vAlign=top>The string marking the end of a token (eg.,
+ <code>@DATE@</code>).</td>
+ <td vAlign=top>@</td>
+ <td vAlign=top align="center">No</td>
+ </tr>
+ <tr>
+ <td vAlign=top>recurse</td>
+ <td vAlign=top>Indicates whether the replacement text of tokens
+ should be searched for more tokens. <b>Since Ant 1.6.3</b></td>
+ <td vAlign=top><i>true</i></td>
+ <td vAlign=top align="center">No</td>
+ </tr>
+</table>
+
+<h2>Filter</h2>
+<table cellSpacing=0 cellPadding=2 border=1>
+ <tr>
+ <td vAlign=top><b>Attribute</b></td>
+ <td vAlign=top><b>Description</b></td>
+ <td vAlign=top align="center"><b>Required</b></td>
+ </tr>
+ <tr>
+ <td vAlign=top>token</td>
+ <td vAlign=top>The token to replace (eg.,
<code>@DATE@</code>)</td>
+ <td vAlign=top align="center">Yes</td>
+ </tr>
+ <tr>
+ <td vAlign=top>value</td>
+ <td vAlign=top>The value to replace it with
+ (eg., <code>Thursday, April 26, 2001</code>).</td>
+ <td vAlign=top align="center">Yes</td>
+ </tr>
+</table>
+
+<h2>Filtersfile</h2>
+<table cellSpacing=0 cellPadding=2 border=1>
+ <tr>
+ <td vAlign=top><b>Attribute</b></td>
+ <td vAlign=top><b>Description</b></td>
+ <td vAlign=top align="center"><b>Required</b></td>
+ </tr>
+ <tr>
+ <td vAlign=top>file</td>
+ <td vAlign=top>A properties file of
+ name-value pairs from which to load the tokens.</td>
+ <td vAlign=top align="center">Yes</td>
+ </tr>
+</table>
-<H4>Examples</H4>
+<h4>Examples</h4>
<p>You are copying the <code>version.txt</code> file to the <code>dist</code>
directory from the <code>build</code> directory
but wish to replace the token <code>@DATE@</code> with today's
date.</p>
-<BLOCKQUOTE><PRE>
+<blockquote><pre>
<copy file="${build.dir}/version.txt"
toFile="${dist.dir}/version.txt">
<filterset>
<filter token="DATE" value="${TODAY}"/>
</filterset>
</copy>
-</PRE></BLOCKQUOTE>
+</pre></blockquote>
<p>You are copying the <code>version.txt</code> file to the <code>dist</code>
directory from the build directory
but wish to replace the token <code>%DATE*</code> with today's date.</p>
-<BLOCKQUOTE><PRE>
+<blockquote><pre>
<copy file="${build.dir}/version.txt"
toFile="${dist.dir}/version.txt">
<filterset begintoken="%" endtoken="*">
<filter token="DATE" value="${TODAY}"/>
</filterset>
</copy>
-</PRE></BLOCKQUOTE>
+</pre></blockquote>
<p>Copy all the docs but change all dates and appropriate notices as stored
in a file.</p>
-<BLOCKQUOTE><PRE>
+<blockquote><pre>
<copy toDir="${dist.dir}/docs">
<fileset dir="${build.dir}/docs">
<include name="**/*.html">
@@ -129,10 +136,10 @@
<filtersfile file="${user.dir}/dist.properties"/>
</filterset>
</copy>
-</PRE></BLOCKQUOTE>
+</pre></blockquote>
<p>Define a FilterSet and reference it later.</p>
-<BLOCKQUOTE><PRE>
+<blockquote><pre>
<filterset id="myFilterSet" begintoken="%"
endtoken="*">
<filter token="DATE" value="${TODAY}"/>
</filterset>
@@ -140,8 +147,8 @@
<copy file="${build.dir}/version.txt"
toFile="${dist.dir}/version.txt">
<filterset refid="myFilterSet"/>
</copy>
-</PRE></BLOCKQUOTE>
-<HR>
+</pre></blockquote>
+<hr>
-<P align=center>Copyright © 2001-2004 The Apache Software Foundation.
-All rights Reserved.</P></BODY></HTML>
+<p align=center>Copyright © 2001-2005 The Apache Software Foundation.
+All rights Reserved.</p></body></html>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]