Hi everyone,

I made a patch, so FilterSet allows nested tokens. A short example.
If you currently have these token/value pairs =
@[EMAIL PROTECTED]/usr/local
@[EMAIL PROTECTED]@root@/test.txt

If you call @test-path@ from your file, it will not resolve the @root@
anymore, which can be very annoying ..

Since you can also get into an infinite loop, by using the eg the values
@[EMAIL PROTECTED]@path@
@[EMAIL PROTECTED]@root@
I made some extra code to handle this, so it will just parse in the
@path@ (if you called that from the file to be filtered) and I will
write a message to System.out with the reason it could parse it (log
doesn't seem to get passed back to console, so I used this).

There is only one problem : it is not backward compatible, since the old
way is to ignore the nested ifs and just put the value there. Since I
don't know any use cases myself where you want the token to be
unresolved if you put a value there, I will leave the decision to put
this in to the committers ;))

Attached is the change in documtation and FilterSet.java (in one diff,
from the root of the jakarta-ant module) and a seperate testcase called
FilterSetRecursiveTest.java. Don't know if the name is correct (nested
could be better), but it is easy to rename ;).
I ran the FilterSetTest separately and it passes..

Have fun with it ;)

Mvgr,
Martin








/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 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 [EMAIL PROTECTED]
 *
 * 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 junit.framework.TestCase;

/**
 * This will test the recursive FilterSet.
 * Which means that if the filter value @test@ contains another filter
 * value, it will actually resolve.
 * It also contains test to see what happens when the resolving occurs
 * in an infinite loop.
 * 
 * @author <a href="mailto:[EMAIL PROTECTED]">Martin van den Bemt</a>
 */
public class FilterSetRecursiveTest extends TestCase {

	/**
	 * Constructor for FilterSetRecursiveTest.
	 * @param arg0
	 */
	public FilterSetRecursiveTest(String arg0) {
		super(arg0);
	}

	public static void main(String[] args) {
		junit.textui.TestRunner.run(FilterSetRecursiveTest.class);
	}
	
	/** 
	 * The actual recursive test
	 * The endresult should be it works
	 */
	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));
	}
	
	/**
	 *  Tests having a inifinite 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));
	}
}
Index: docs/manual/CoreTypes/filterset.html
===================================================================
RCS file: /home/cvspublic/jakarta-ant/docs/manual/CoreTypes/filterset.html,v
retrieving revision 1.6
diff -u -r1.6 filterset.html
--- docs/manual/CoreTypes/filterset.html	25 Mar 2002 20:21:11 -0000	1.6
+++ docs/manual/CoreTypes/filterset.html	20 May 2002 15:18:49 -0000
@@ -19,6 +19,7 @@
 <code>endtoken</code> attributes to define what to match.</p>
 <p>Filtersets are used for doing 
 replacements in tasks such as <code>&lt;copy&gt;</code>, etc.</p>
+<p>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.
 
 <H2>Filterset</H2>
 
Index: src/main/org/apache/tools/ant/types/FilterSet.java
===================================================================
RCS file: /home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/types/FilterSet.java,v
retrieving revision 1.13
diff -u -r1.13 FilterSet.java
--- src/main/org/apache/tools/ant/types/FilterSet.java	15 Apr 2002 14:56:34 -0000	1.13
+++ src/main/org/apache/tools/ant/types/FilterSet.java	20 May 2002 15:20:38 -0000
@@ -75,6 +75,7 @@
  * A filter set may have begintoken and endtokens defined.
  *
  * @author     <A href="mailto:[EMAIL PROTECTED]">  Michael McCallum  </A>
+ * @author     <A href="mailto:[EMAIL PROTECTED]">  Martin van den Bemt </A>
  */
 public class FilterSet extends DataType implements Cloneable {
     
@@ -356,6 +357,10 @@
                         value = (String) tokens.get(token);
                         log("Replacing: " + beginToken + token + endToken 
                             + " -> " + value, Project.MSG_VERBOSE);
+                        if (!value.equals(token)) {
+                            // we have another token, let's parse it.
+                            value = replaceTokens(value, token);
+                        }
                         b.append(value);
                         i = index + beginToken.length() + token.length() 
                             + endToken.length();
@@ -374,6 +379,54 @@
         } else {
            return line;
         }
+    }
+    
+    /** 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;
     }
     
     /**

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to