Browse Source

Add recurse attribute to filterset.

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277580 13f79535-47bb-0310-9956-ffa450edef68
master
Matthew Jason Benson 20 years ago
parent
commit
3e94ca9450
4 changed files with 225 additions and 187 deletions
  1. +3
    -0
      WHATSNEW
  2. +88
    -81
      docs/manual/CoreTypes/filterset.html
  3. +116
    -105
      src/main/org/apache/tools/ant/types/FilterSet.java
  4. +18
    -1
      src/testcases/org/apache/tools/ant/types/FilterSetTest.java

+ 3
- 0
WHATSNEW View File

@@ -205,6 +205,9 @@ Other changes:

* <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:
-----------



+ 88
- 81
docs/manual/CoreTypes/filterset.html View File

@@ -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>&lt;target&gt;</CODE> - i.e., as
feature or at the same level as <code>&lt;target&gt;</code> - i.e., as
children of
<CODE>&lt;project&gt;</CODE>.</P>
<code>&lt;project&gt;</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 @@ 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.
</p>

<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>&#64;DATE&#64;</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>&#64;DATE&#64;</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>&#64;DATE&#64;</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>
<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>&#64;DATE&#64;</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>&#64;DATE&#64;</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>&#64;DATE&#64;</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>

<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>&#64;DATE&#64;</code> with today's date.</p>
<BLOCKQUOTE><PRE>
<blockquote><pre>
&lt;copy file=&quot;${build.dir}/version.txt&quot; toFile=&quot;${dist.dir}/version.txt&quot;&gt;
&lt;filterset&gt;
&lt;filter token=&quot;DATE&quot; value=&quot;${TODAY}&quot;/&gt;
&lt;/filterset&gt;
&lt;/copy&gt;
</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>
&lt;copy file=&quot;${build.dir}/version.txt&quot; toFile=&quot;${dist.dir}/version.txt&quot;&gt;
&lt;filterset begintoken=&quot;%&quot; endtoken=&quot;*&quot;&gt;
&lt;filter token=&quot;DATE&quot; value=&quot;${TODAY}&quot;/&gt;
&lt;/filterset&gt;
&lt;/copy&gt;
</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>
&lt;copy toDir=&quot;${dist.dir}/docs&quot;&gt;
&lt;fileset dir=&quot;${build.dir}/docs&quot;&gt;
&lt;include name=&quot;**/*.html&quot;&gt;
@@ -129,10 +136,10 @@ but wish to replace the token <code>%DATE*</code> with today's date.</p>
&lt;filtersfile file=&quot;${user.dir}/dist.properties&quot;/&gt;
&lt;/filterset&gt;
&lt;/copy&gt;
</PRE></BLOCKQUOTE>
</pre></blockquote>

<p>Define a FilterSet and reference it later.</p>
<BLOCKQUOTE><PRE>
<blockquote><pre>
&lt;filterset id=&quot;myFilterSet&quot; begintoken=&quot;%&quot; endtoken=&quot;*&quot;&gt;
&lt;filter token=&quot;DATE&quot; value=&quot;${TODAY}&quot;/&gt;
&lt;/filterset&gt;
@@ -140,8 +147,8 @@ but wish to replace the token <code>%DATE*</code> with today's date.</p>
&lt;copy file=&quot;${build.dir}/version.txt&quot; toFile=&quot;${dist.dir}/version.txt&quot;&gt;
&lt;filterset refid=&quot;myFilterSet&quot;/&gt;
&lt;/copy&gt;
</PRE></BLOCKQUOTE>
<HR>
</pre></blockquote>
<hr>

<P align=center>Copyright &copy; 2001-2004 The Apache Software Foundation.
All rights Reserved.</P></BODY></HTML>
<p align=center>Copyright &copy; 2001-2005 The Apache Software Foundation.
All rights Reserved.</p></body></html>

+ 116
- 105
src/main/org/apache/tools/ant/types/FilterSet.java View File

@@ -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 <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 @@ 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 {
}
}
}

}




+ 18
- 1
src/testcases/org/apache/tools/ant/types/FilterSetTest.java View File

@@ -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");



Loading…
Cancel
Save