From 3e94ca9450e57d4f0321527cf3e3d53625a30c62 Mon Sep 17 00:00:00 2001
From: Matthew Jason Benson 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 FilterSet
+
+FilterSet
-<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.
Attribute | -Description | -Default | -Required | -
begintoken | -The string marking the beginning of a token (eg.,
- @DATE@ ). |
- @ | -No | -
endtoken | -The string marking the end of a token (eg.,
- @DATE@ ). |
- @ | -No | -
Attribute | -Description | -Required | -
token | -The token to replace (eg., @DATE@ ) |
- Yes | -
value | -The value to replace it with
- (eg., Thursday, April 26, 2001 ). |
- Yes | -
Attribute | -Description | -Required | -
file | -A properties file of - name-value pairs from which to load the tokens. | -Yes | -
Attribute | +Description | +Default | +Required | +
begintoken | +The string marking the beginning of a token (eg.,
+ @DATE@ ). |
+ @ | +No | +
endtoken | +The string marking the end of a token (eg.,
+ @DATE@ ). |
+ @ | +No | +
recurse | +Indicates whether the replacement text of tokens + should be searched for more tokens. Since Ant 1.6.3 | +true | +No | +
Attribute | +Description | +Required | +
token | +The token to replace (eg., @DATE@ ) |
+ Yes | +
value | +The value to replace it with
+ (eg., Thursday, April 26, 2001 ). |
+ Yes | +
Attribute | +Description | +Required | +
file | +A properties file of + name-value pairs from which to load the tokens. | +Yes | +
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 recurseboolean
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");