diff --git a/WHATSNEW b/WHATSNEW
index e0ecaafc2..6b9a0201a 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -9,6 +9,8 @@ Other changes:
* has a new srcfile attribute that can make it read
properties files and output them instead of Ant's properties.
+* will now resolve filters recursively.
+
Changes from Ant 1.4.1 to Ant 1.5
=================================
diff --git a/docs/manual/CoreTypes/filterset.html b/docs/manual/CoreTypes/filterset.html
index a4b3fc00c..a0a8b3cff 100644
--- a/docs/manual/CoreTypes/filterset.html
+++ b/docs/manual/CoreTypes/filterset.html
@@ -19,6 +19,7 @@ children of
endtoken
attributes to define what to match.
Filtersets are used for doing
replacements in tasks such as <copy>
, etc.
+Nested filters are possible and a message will be given in case a value in a filter chain is called for a second time and thus causing an infinite loop. The originating token will be passed back when an infinite loop occurs.
Filterset
diff --git a/src/main/org/apache/tools/ant/types/FilterSet.java b/src/main/org/apache/tools/ant/types/FilterSet.java
index 75beed3d7..1e2ed7998 100644
--- a/src/main/org/apache/tools/ant/types/FilterSet.java
+++ b/src/main/org/apache/tools/ant/types/FilterSet.java
@@ -75,6 +75,7 @@ import org.apache.tools.ant.Project;
* A filter set may have begintoken and endtokens defined.
*
* @author Michael McCallum
+ * @author Martin van den Bemt
*/
public class FilterSet extends DataType implements Cloneable {
@@ -354,6 +355,10 @@ public class FilterSet extends DataType implements Cloneable {
b.append(line.substring(i, index));
if (tokens.containsKey(token)) {
value = (String) tokens.get(token);
+ if (!value.equals(token)) {
+ // we have another token, let's parse it.
+ value = replaceTokens(value, token);
+ }
log("Replacing: " + beginToken + token + endToken
+ " -> " + value, Project.MSG_VERBOSE);
b.append(value);
@@ -376,6 +381,54 @@ public class FilterSet extends DataType implements Cloneable {
}
}
+ /** 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 value the value / token to parse
+ * @param parent the parant token (= the token it was parsed from)
+ */
+ private String replaceTokens(String line, String parent)
+ throws BuildException
+ {
+ if (passedTokens == null) {
+ passedTokens = new Vector();
+ }
+ if (passedTokens.contains(parent) && !duplicateToken) {
+ duplicateToken = true;
+ StringBuffer sb = new StringBuffer();
+ sb.append("Inifinite 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());
+ return parent;
+ }
+ passedTokens.addElement(parent);
+ String value = this.replaceTokens(line);
+ if (value.indexOf(getBeginToken()) == -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);
+ if (passedTokens.size() == 0) {
+ value = getBeginToken()+value+getEndToken();
+ duplicateToken = false;
+ }
+ }
+ }
+ return value;
+ }
+
/**
* Create a new filter
*
diff --git a/src/testcases/org/apache/tools/ant/types/FilterSetTest.java b/src/testcases/org/apache/tools/ant/types/FilterSetTest.java
index da1311ccd..c31dd182e 100644
--- a/src/testcases/org/apache/tools/ant/types/FilterSetTest.java
+++ b/src/testcases/org/apache/tools/ant/types/FilterSetTest.java
@@ -67,6 +67,7 @@ import java.io.*;
* FilterSet testing
*
* @author Conor MacNeill
+ * @author Martin van den Bemt
*/
public class FilterSetTest extends BuildFileTest {
@@ -102,6 +103,42 @@ public class FilterSetTest extends BuildFileTest {
"src/etc/testcases/types/dest3.txt"));
}
+ /**
+ * This will test the recursive FilterSet. Which means that if
+ * the filter value @test@ contains another filter value, it will
+ * actually resolve.
+ */
+ public void testRecursive() {
+ System.out.println("testRecursive");
+ String result = "it works line";
+ String line="@test@ line";
+ FilterSet fs = new FilterSet();
+ fs.addFilter("test", "@test1@");
+ fs.addFilter("test1","@test2@");
+ fs.addFilter("test2", "it works");
+ fs.setBeginToken("@");
+ fs.setEndToken("@");
+ assertEquals(result, fs.replaceTokens(line));
+ }
+
+ /**
+ * Test to see what happens when the resolving occurs in an
+ * infinite loop.
+ */
+ public void testInfinite() {
+ System.out.println("testInfinite");
+ String result = "@test@ line testvalue";
+ String line = "@test@ line @test3@";
+ FilterSet fs = new FilterSet();
+ fs.addFilter("test", "@test1@");
+ fs.addFilter("test1","@test2@");
+ fs.addFilter("test2", "@test@");
+ fs.addFilter("test3", "testvalue");
+ fs.setBeginToken("@");
+ fs.setEndToken("@");
+ assertEquals(result, fs.replaceTokens(line));
+ }
+
private boolean compareFiles(String name1, String name2) {
File file1 = new File(name1);
File file2 = new File(name2);