From 3e94ca9450e57d4f0321527cf3e3d53625a30c62 Mon Sep 17 00:00:00 2001 From: Matthew Jason Benson Date: Thu, 3 Feb 2005 19:18:07 +0000 Subject: [PATCH] Add recurse attribute to filterset. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277580 13f79535-47bb-0310-9956-ffa450edef68 --- WHATSNEW | 3 + docs/manual/CoreTypes/filterset.html | 169 +++++++------- .../org/apache/tools/ant/types/FilterSet.java | 221 +++++++++--------- .../apache/tools/ant/types/FilterSetTest.java | 19 +- 4 files changed, 225 insertions(+), 187 deletions(-) diff --git a/WHATSNEW b/WHATSNEW index 3a782c5cb..2d40a462b 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -205,6 +205,9 @@ Other changes: * 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: ----------- diff --git a/docs/manual/CoreTypes/filterset.html b/docs/manual/CoreTypes/filterset.html index fa4118207..30a1b3913 100644 --- a/docs/manual/CoreTypes/filterset.html +++ b/docs/manual/CoreTypes/filterset.html @@ -1,19 +1,19 @@ - - - FilterSet Type - + + + FilterSet Type + - -

FilterSet

+ +

FilterSet

-

FilterSets are groups of filters. Filters can be defined as token-value +

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 <target> - i.e., as +feature or at the same level as <target> - i.e., as children of -<project>.

+<project>.

FilterSets support the id and refid attributes. You can define a FilterSet with an id @@ -35,92 +35,99 @@ the copy operations will typically corrupt binary files. When applying filters you should ensure that the set of files being filtered are all text files.

-

Filterset

- - - - - - - - - - - - - - - - - - - - -
AttributeDescriptionDefaultRequired
begintokenThe string marking the beginning of a token (eg., - @DATE@).@No
endtokenThe string marking the end of a token (eg., - @DATE@).@No
- -

Filter

- - - - - - - - - - - - - - - - -
AttributeDescriptionRequired
tokenThe token to replace (eg., @DATE@)Yes
valueThe value to replace it with - (eg., Thursday, April 26, 2001).Yes
- -

Filtersfile

- - - - - - - - - - - -
AttributeDescriptionRequired
fileA properties file of - name-value pairs from which to load the tokens.Yes
- -

Examples

+

Filterset

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
AttributeDescriptionDefaultRequired
begintokenThe string marking the beginning of a token (eg., + @DATE@).@No
endtokenThe string marking the end of a token (eg., + @DATE@).@No
recurseIndicates whether the replacement text of tokens + should be searched for more tokens. Since Ant 1.6.3trueNo
+ +

Filter

+ + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
tokenThe token to replace (eg., @DATE@)Yes
valueThe value to replace it with + (eg., Thursday, April 26, 2001).Yes
+ +

Filtersfile

+ + + + + + + + + + + +
AttributeDescriptionRequired
fileA properties file of + name-value pairs from which to load the tokens.Yes
+ +

Examples

You are copying the version.txt file to the dist directory from the build directory but wish to replace the token @DATE@ with today's date.

-
+
 <copy file="${build.dir}/version.txt" toFile="${dist.dir}/version.txt">
   <filterset>
     <filter token="DATE" value="${TODAY}"/>
   </filterset>
 </copy>
-
+

You are copying the version.txt file to the dist directory from the build directory but wish to replace the token %DATE* with today's date.

-
+
 <copy file="${build.dir}/version.txt" toFile="${dist.dir}/version.txt">
   <filterset begintoken="%" endtoken="*">
     <filter token="DATE" value="${TODAY}"/>
   </filterset>
 </copy>
-
+

Copy all the docs but change all dates and appropriate notices as stored in a file.

-
+
 <copy toDir="${dist.dir}/docs">
   <fileset dir="${build.dir}/docs">
     <include name="**/*.html">
@@ -129,10 +136,10 @@ but wish to replace the token %DATE* with today's date.

<filtersfile file="${user.dir}/dist.properties"/> </filterset> </copy> -
+

Define a FilterSet and reference it later.

-
+
 <filterset id="myFilterSet" begintoken="%" endtoken="*">
   <filter token="DATE" value="${TODAY}"/>
 </filterset>
@@ -140,8 +147,8 @@ but wish to replace the token %DATE* with today's date.

<copy file="${build.dir}/version.txt" toFile="${dist.dir}/version.txt"> <filterset refid="myFilterSet"/> </copy> -
-
+
+
-

Copyright © 2001-2004 The Apache Software Foundation. -All rights Reserved.

+

Copyright © 2001-2005 The Apache Software Foundation. +All rights Reserved.

diff --git a/src/main/org/apache/tools/ant/types/FilterSet.java b/src/main/org/apache/tools/ant/types/FilterSet.java index 2ca449d13..7235ea869 100644 --- a/src/main/org/apache/tools/ant/types/FilterSet.java +++ b/src/main/org/apache/tools/ant/types/FilterSet.java @@ -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.Enumeration; 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 @@ import org.apache.tools.ant.Project; 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 FilterSet extends DataType implements Cloneable { public class FiltersFile { /** - * Constructor for the Filter object + * Constructor for the FiltersFile object. */ public FiltersFile() { } @@ -132,19 +131,27 @@ public class FilterSet extends DataType implements Cloneable { 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 @@ public class FilterSet extends DataType implements Cloneable { } /** - * 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 @@ public class FilterSet extends DataType implements Cloneable { } /** - * Get the referred filter set + * Get the referenced filter set. * * @return the filterset from the reference. */ @@ -179,21 +186,23 @@ public class FilterSet extends DataType implements Cloneable { * * @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 @@ public class FilterSet extends DataType implements Cloneable { } /** - * 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 @@ public class FilterSet extends DataType implements Cloneable { } /** - * 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 @@ public class FilterSet extends DataType implements Cloneable { 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 @@ public class FilterSet extends DataType implements Cloneable { } /** - * 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 @@ public class FilterSet extends DataType implements Cloneable { return endOfToken; } + /** + * Set whether recursive token expansion is enabled. + * @param recurse boolean whether to recurse. + */ + public void setRecurse(boolean recurse) { + this.recurse = recurse; + } + + /** + * Get whether recursive token expansion is enabled. + * @return boolean 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 @@ public class FilterSet extends DataType implements Cloneable { * 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 @@ public class FilterSet extends DataType implements Cloneable { * @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 @@ public class FilterSet extends DataType implements Cloneable { 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 @@ 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)) { + if (recurse && !value.equals(token)) { // we have another token, let's parse it. value = replaceTokens(value, token); } @@ -381,48 +402,40 @@ 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 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 @@ public class FilterSet extends DataType implements Cloneable { } /** - * 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 @@ public class FilterSet extends DataType implements Cloneable { } /** - * 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 @@ public class FilterSet extends DataType implements Cloneable { } } } - } - - diff --git a/src/testcases/org/apache/tools/ant/types/FilterSetTest.java b/src/testcases/org/apache/tools/ant/types/FilterSetTest.java index 68bea27ca..fe23a7671 100644 --- a/src/testcases/org/apache/tools/ant/types/FilterSetTest.java +++ b/src/testcases/org/apache/tools/ant/types/FilterSetTest.java @@ -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 @@ public class FilterSetTest extends BuildFileTest { 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");