diff --git a/WHATSNEW b/WHATSNEW
index a21d954a0..61a5aee7e 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -99,6 +99,9 @@ Fixed bugs:
Other changes:
--------------
+* A new filter reader namely tokenfilter has been added. Bugzilla
+ Report 18312.
+
* A new attribute named skip is added to the TailFilter and
HeadFilter filter readers.
diff --git a/build.xml b/build.xml
index f1d3ce353..57b663c7b 100644
--- a/build.xml
+++ b/build.xml
@@ -202,7 +202,10 @@
-
+
+
+
+
diff --git a/docs/manual/CoreTypes/filterchain.html b/docs/manual/CoreTypes/filterchain.html
index c10aea4f8..f20646326 100644
--- a/docs/manual/CoreTypes/filterchain.html
+++ b/docs/manual/CoreTypes/filterchain.html
@@ -101,6 +101,8 @@ nested elements.
StripLineComments
TabsToSpaces
TailFilter
+DeleteCharacters
+TokenFilter
@@ -552,7 +554,7 @@ that represent comments as specified by the user.
This removes all lines that begin with #, --, REM, rem and //
<filterreader classname="org.apache.tools.ant.filters.StripLineComments">
- <param type="comment" value="#"/>
+ <param type="comment" value="e;#"/>
<param type="comment" value="--"/>
<param type="comment" value="REM "/>
<param type="comment" value="rem "/>
@@ -786,6 +788,480 @@ lines 49-58 are extracted)
</loadfile>
+
+
+ This filter deletes specified characters.
+ since Ant 1.6
+ This filter is only available in the convenience form.
+
+
+
+ Parameter Name |
+ Parameter Value |
+ Required |
+
+
+ chars |
+
+ The characters to delete. This attribute is
+ backslash enabled.
+ |
+ Yes |
+
+
+
+
Examples:
+
+Delete tabs and returns from the data.
+
+<deletecharacters chars="\t\r"/>
+
+
+
+
+This filter tokenizes the inputstream into strings and passes these
+strings to filters of strings. Unlike the other filterreaders, this does
+not support params, only convenience methods are implemented.
+The tokenizer and the string filters are defined by nested elements.
+since Ant 1.6
+
+Only one tokenizer element may be used, the LineTokenizer is the
+default if none are specified. A tokenizer
+splits the input into token strings and trailing delimiter strings.
+
+There may be zero or more string filters. A string filter processes
+a token and either returns a string or a null.
+It the string is not null it is passed to the next filter. This
+proceeds until all the filters are called.
+If a string is returned after all the filters, the string is
+outputs with its associated token delimitier
+(if one is present).
+The trailing delimiter may be overridden by the delimOutput
+attribute.
+
+blackslash interpretation
+A number of attributes (including delimOutput) interpret
+backslash escapes. The following are understood: \n, \r, \f, \t
+and \\.
+
+
+
+
+ Attribute |
+ Description |
+ Required |
+
+
+ delimOutput |
+
+ This overrides the tokendelimiter
+ returned by the tokenizer if it is not empty. This
+ attribute is backslash enabled.
+ |
+ No |
+
+
+
+
+ The following tokenizers are provided by the default distribution.
+
+ LineTokenizer
+ FileTokenizer
+ StringTokenizer
+
+
+ The following string filters are provided by the default distribution.
+
+ ReplaceString
+ ContainsString
+ ReplaceRegex
+ ContainsRegex
+ Trim
+ IgnoreBlank
+ DeleteCharacters
+
+
+ The following string filters are provided by the optional distribution.
+
+ ScriptFilter
+
+
+Some of the filters may be used directly within a filter chain. In this
+case a tokenfilter is created implicitly. An extra attribute "byline"
+is added to the filter to specify whether to use a linetokenizer
+(byline="true") or a filetokenizer (byline="false"). The default
+is "true".
+
+
+
LineTokenizer
+This tokenizer splits the input into lines.
+The tokenizer delimits lines
+by "\r", "\n" or "\r\n".
+This is the default tokenizer.
+Examples:
+
+Convert input current line endings to unix style line endings.
+This currently has no effect when used in the copy task.
+
+<tokenfilter delimoutput="\n"/>
+
+
+
+Remove blank lines.
+
+<tokenfilter>
+ <ignoreblank/>
+</tokenfilter>
+
+
+
+FileTokenizer
+This tokenizer treats all the input as a token. So be
+careful not to use this on very large input.
+Examples:
+
+Replace the first occurance of package with //package.
+
+<tokenfilter>
+ <filetokenizer/>
+ <replaceregex pattern="([\n\r]+[ \t]*|^[ \t]*)package"
+ flags="s"
+ replace="\1//package"/>
+</tokenfilter>
+
+
+StringTokenizer
+This tokenizer is based on java.util.StringTokenizer.
+It splits up the input into strings separated by white space, or
+by a specified list of delimiting characters.
+If the stream starts with delimiter characters, the first
+token will be the empty string (unless the delimsaretokens
+attribute is used).
+
+
+
+ Attribute |
+ Description |
+ Required |
+
+
+ delims |
+ The delimiter characters. White space
+ is used if this is not set. (White space is defined
+ in this case by java.lang.Character.isWhitespace()).
+ |
+ No |
+
+
+ delimsaretokens |
+ If this is true,
+ each delimiter character is returned as a token |
+ No |
+
+
+ suppressdelims |
+ If this is true, delimiters are not returned. |
+ No |
+
+
+
+Examples:
+
+Surround each non space token with a "[]".
+
+
+<tokenfilter>
+ <stringtokenizer/>
+ <replaceregex pattern="(.+)" replace="[\1]"/>
+</tokenfilter>
+
+
+
+ReplaceString
+This is a simple filter to replace strings.
+This filter may be used directly within a filterchain.
+
+
+
+ Attribute |
+ Description |
+ Required |
+
+
+ from |
+ The string that must be replaced. |
+ Yes |
+
+
+ to |
+ The new value for the replaced string. When omitted
+ an empty string is used.
+ |
+ No |
+
+
+
+Examples:
+
+Replace "sun" with "moon".
+
+
+<tokenfilter>
+ <replacestring from="sun" to="moon"/>
+</tokenfilter>
+
+
+ContainsString
+This is a simple filter to filter tokens that contains
+a specified string.
+
+
+
+ Attribute |
+ Description |
+ Required |
+
+
+ contains |
+ The string that the token must contain. |
+ Yes |
+
+
+
+Examples:
+
+Include only lines that contain "foo";
+
+
+<tokenfilter>
+ <containsstring contains="foo"/>
+</tokenfilter>
+
+
+
+ReplaceRegex
+This string filter replaces regular expressions. See
+ReplaceRegexp
+for an explanation on regular expressions.
+This filter may be used directly within a filterchain.
+
+
+
+ Attribute |
+ Description |
+ Required |
+
+
+ pattern |
+ The regular expression pattern to match in
+ the token. |
+ Yes |
+
+
+ replace |
+ The substitution pattern to replace the matched
+ regular expression. When omitted an empty string is used. |
+ No |
+
+
+ flags |
+ See
+ReplaceRegexp
+for an explanation of regex flags. |
+ No |
+
+
+
+Examples:
+
+Replace all occurances of "hello" with "world", ignoring case.
+
+
+<tokenfilter>
+ <replaceregex pattern="hello" replace="world" flags="gi"/>
+</tokenfilter>
+
+
+
+ContainsRegex
+This filters strings that match regular expressions.
+The filter may optionally replace the matched regular expression.
+See
+ReplaceRegexp
+for an explanation on regular expressions.
+This filter may be used directly within a filterchain.
+
+
+
+ Attribute |
+ Description |
+ Required |
+
+
+ pattern |
+ The regular expression pattern to match in
+ the token. |
+ Yes |
+
+
+ replace |
+ The substitution pattern to replace the matched
+ regular expression. When omitted the orignal token is returned.
+ |
+ No |
+
+
+ flags |
+ See
+ReplaceRegexp
+for an explanation of regex flags. |
+ No |
+
+
+
+Examples:
+
+Filter lines that contain "hello" or "world", ignoring case.
+
+
+<tokenfilter>
+ <containsregex pattern="(hello|world)" flags="i"/>
+</tokenfilter>
+
+
+
+This example replaces lines like "SUITE(TestSuite, bits);" with
+"void register_bits();" and removes other lines.
+
+
+<tokenfilter>
+ <containsregex
+ pattern="^ *SUITE\(.*,\s*(.*)\s*\).*"
+ replace="void register_\1();"/>
+</tokenfilter>
+
+
+Trim
+This filter trims whitespace from the start and end of
+tokens.
+This filter may be used directly within a filterchain.
+IgnoreBlank
+This filter removes empty tokens.
+This filter may be used directly within a filterchain.
+DeleteCharacters
+This filter deletes specified characters from tokens.
+
+
+
+ Attribute |
+ Description |
+ Required |
+
+
+ chars |
+ The characters to delete. This attribute
+ is backslash enabled. |
+ Yes |
+
+
+
+Examples:
+
+Delete tabs from lines, trim the lines and removes empty lines.
+
+
+<tokenfilter>
+ <deletecharacters chars="\t"/>
+ <trim/>
+ <ignoreblank/>
+</tokenfilter>
+
+
+
+ScriptFilter
+This is an optional filter that executes a script in a
+Apache BSF
+supported language.
+See the Script task for
+an explanation of scripts and dependencies.
+
+
+The script is provided with an object self that has
+getToken() and setToken(String) methods.
+
+
+This filter may be used directly within a filterchain.
+
+
+ Attribute |
+ Description |
+ Required |
+
+
+ language |
+ The programming language the script is written in.
+Must be a supported Apache BSF language |
+ Yes |
+
+
+ src |
+ The location of the script as a file, if not inline
+ |
+ No |
+
+
+
+
+Examples:
+
+Convert to uppercase.
+
+<tokenfilter>
+ <scriptfilter language="javascript">
+ self.setToken(self.getToken().toUpperCase());
+ </scriptfilter>
+</tokenfilter>
+
+
+
+Custom tokenizers and string filters
+
+Custom string filters and tokenizers may be plugged in by
+extending the interfaces org.apache.tools.ant.filters.TokenFilter.Filter
+and org.apache.tools.ant.filters.TokenFilter.Tokenizer respectly.
+
+They are defined the build file using <typedef/>. For
+example a string filter that capitalizes words may be declared as:
+
+package my.customant;
+import org.apache.tools.ant.filters.TokenFilter;
+
+public class Capitalize
+ implements TokenFilter.Filter
+{
+ public String filter(String token) {
+ if (token.length() == 0)
+ return token;
+ return token.substring(0, 1).toUpperCase() +
+ token.substring(1);
+ }
+}
+
+
+This may be used as follows:
+
+ <typedef type="capitalize" classname="my.customant.Capitalize"
+ classpath="my.customant.path"/>
+ <copy file="input" tofile="output">
+ <filterchain>
+ <tokenfilter>
+ <stringtokenizer/>
+ <capitalize/>
+ </tokenfilter>
+ </filterchain>
+ </copy>
+
+
Copyright © 2002-2003 Apache Software Foundation. All rights
diff --git a/src/etc/testcases/filters/dynamicfilter.xml b/src/etc/testcases/filters/dynamicfilter.xml
new file mode 100644
index 000000000..51cd43a0c
--- /dev/null
+++ b/src/etc/testcases/filters/dynamicfilter.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hello world
+
+
+
+
+
+
+
+
+
+
diff --git a/src/etc/testcases/filters/tokenfilter.xml b/src/etc/testcases/filters/tokenfilter.xml
new file mode 100644
index 000000000..59354e280
--- /dev/null
+++ b/src/etc/testcases/filters/tokenfilter.xml
@@ -0,0 +1,314 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hello
+
+ World
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is the contents of the trimmed file.
+ This is the second line.
+
+
+
+
+
+
+
+
+ This is the contents of the trimmed file.
+ This is the second line.
+
+
+
+
+
+
+
+
+
+ This is foo bar
+
+
+
+
+
+
+
+
+
+ This is a number
+ of words
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a number
+ of words
+
+
+
+
+
+
+
+
+
+
+ This is a number
+ of words
+
+
+
+
+
+
+
+
+
+
+ This is a number
+ of words
+
+
+
+
+
+
+
+
+
+
+
+
+
+ this is the sun
+
+
+
+
+
+
+
+
+
+
+ this is a line contains foo
+ this line does not
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hello Hello HELLO hello
+ cat Cat cat
+ Sun Sun Sun
+ WhiteSpace tab
+ This is a line with digits - 1234 -- there
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hello Hello HELLO hello
+
+
+
+
+
+
+
+
+
+ hello world
+ this is the moon
+ World here
+
+
+
+
+
+
+
+
+
+
+
+
+ hello world
+ this is the moon
+ World here
+
+
+
+
+
+
+
+
+
+
+
+
+ SUITE(TestSuite, bits);
+ here
+
+
+
+
+
+
+
+
+
+
+
+
+ This is some ### s
+ some ****
+
+
+
+
+
+
+
+
+
+
+
+ hello world
+
+
+
+
+
+ self.setToken(self.getToken().toUpperCase());
+
+
+
+
+
+
+
+
+ hello moon
+
+
+
+
+ self.setToken(self.getToken().toUpperCase());
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hello world
+
+
+
+
+
diff --git a/src/main/org/apache/tools/ant/filters/TokenFilter.java b/src/main/org/apache/tools/ant/filters/TokenFilter.java
new file mode 100644
index 000000000..b8a3681cc
--- /dev/null
+++ b/src/main/org/apache/tools/ant/filters/TokenFilter.java
@@ -0,0 +1,918 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 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 "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 apache@apache.org.
+ *
+ * 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
+ * .
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.Enumeration;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DynamicConfigurator;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.types.Substitution;
+import org.apache.tools.ant.util.regexp.Regexp;
+
+/**
+ * This splits up input into tokens and passes
+ * the tokens to a sequence of filters.
+ *
+ * @author Peter Reilly
+ * @since Ant 1.6
+ * @see BaseFilterReader
+ * @see ChainableReader
+ * @see DynamicConfigurator
+ */
+public class TokenFilter
+ extends BaseFilterReader
+ implements ChainableReader, DynamicConfigurator
+{
+ /**
+ * input stream tokenizers implement this interface
+ */
+ public interface Tokenizer {
+ /**
+ * get the next token from the input stream
+ * @param in the input stream
+ * @return the next token, or null for the end
+ * of the stream
+ */
+ public String getToken(Reader in)
+ throws IOException;
+ /**
+ * return the string between tokens, after the
+ * previous token.
+ * @return the intra-token string
+ */
+ public String getPostToken();
+ }
+
+ /**
+ * string filters implement this interface
+ */
+ public interface Filter {
+ /**
+ * filter and/of modify a string
+ *
+ * @param filter the string to filter
+ * @return the modified string or null if the
+ * string did not pass the filter
+ */
+ public String filter(String string);
+ }
+
+
+ /** string filters */
+ private Vector filters = new Vector();
+ /** the tokenizer to use on the input stream */
+ private Tokenizer tokenizer = null;
+ /** the output token termination */
+ private String delimOutput = null;
+ /** the current string token from the input stream */
+ private String line = null;
+ /** the position in the current string token */
+ private int linePos = 0;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public TokenFilter() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be null
.
+ */
+ public TokenFilter(final Reader in) {
+ super(in);
+ }
+
+
+ /**
+ * Returns the next character in the filtered stream, only including
+ * lines from the original stream which match all of the specified
+ * regular expressions.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+
+ public int read() throws IOException {
+ if (tokenizer == null)
+ tokenizer = new LineTokenizer();
+
+ while (line == null || line.length() == 0) {
+ line = tokenizer.getToken(in);
+ if (line == null)
+ return -1;
+ for (Enumeration e = filters.elements(); e.hasMoreElements();)
+ {
+ Filter filter = (Filter) e.nextElement();
+ line = filter.filter(line);
+ if (line == null)
+ break;
+ }
+ linePos = 0;
+ if (line != null) {
+ if (tokenizer.getPostToken().length() != 0) {
+ if (delimOutput != null)
+ line = line + delimOutput;
+ else
+ line = line + tokenizer.getPostToken();
+ }
+ }
+ }
+ int ch = line.charAt(linePos);
+ linePos ++;
+ if (linePos == line.length())
+ line = null;
+ return ch;
+ }
+
+ /**
+ * Creates a new TokenFilter using the passed in
+ * Reader for instantiation.
+ *
+ * @param reader A Reader object providing the underlying stream.
+ *
+ * @return a new filter based on this configuration
+ */
+
+ public final Reader chain(final Reader reader) {
+ TokenFilter newFilter = new TokenFilter(reader);
+ newFilter.filters = filters;
+ newFilter.tokenizer = tokenizer;
+ newFilter.delimOutput = delimOutput;
+ newFilter.setProject(getProject());
+ return newFilter;
+ }
+
+ /**
+ * set the output delimitor.
+ * @param delimOutput replaces the delim string returned by the
+ * tokenizer, it it present.
+ */
+
+ public void setDelimOutput(String delimOutput) {
+ this.delimOutput = resolveBackSlash(delimOutput);
+ }
+
+ // -----------------------------------------
+ // Predefined tokenizers
+ // -----------------------------------------
+
+ /**
+ * add a line tokenizer - this is the default.
+ */
+
+ public void addLineTokenizer(LineTokenizer tokenizer) {
+ if (this.tokenizer != null)
+ throw new BuildException("Only one tokenizer allowed");
+ this.tokenizer = tokenizer;
+ }
+
+ /**
+ * add a string tokenizer
+ */
+
+ public void addStringTokenizer(StringTokenizer tokenizer) {
+ if (this.tokenizer != null)
+ throw new BuildException("Only one tokenizer allowed");
+ this.tokenizer = tokenizer;
+ }
+
+ /**
+ * add a file tokenizer
+ */
+ public void addFileTokenizer(FileTokenizer tokenizer) {
+ if (this.tokenizer != null)
+ throw new BuildException("Only one tokenizer allowed");
+ this.tokenizer = tokenizer;
+ }
+
+
+ // -----------------------------------------
+ // Predefined filters
+ // -----------------------------------------
+
+ /** replace string filter */
+ public void addReplaceString(ReplaceString filter) {
+ filters.addElement(filter);
+ }
+
+ /** contains string filter */
+ public void addContainsString(ContainsString filter) {
+ filters.addElement(filter);
+ }
+
+ /** replace regex filter */
+ public void addReplaceRegex(ReplaceRegex filter) {
+ filters.addElement(filter);
+ }
+
+ /** contains regex filter */
+ public void addContainsRegex(ContainsRegex filter) {
+ filters.addElement(filter);
+ }
+
+ /** trim filter */
+ public void addTrim(Trim filter) {
+ filters.addElement(filter);
+ }
+
+ /** ignore blank filter */
+ public void addIgnoreBlank(IgnoreBlank filter) {
+ filters.addElement(filter);
+ }
+
+ /** delete chars */
+ public void addDeleteCharacters(DeleteCharacters filter) {
+ filters.addElement(filter);
+ }
+
+ public void add(Filter filter) {
+ filters.addElement(filter);
+ }
+
+ /**
+ * create the named datatype and check if it
+ * is a filter or a tokenizer
+ *
+ * @throws BuildException if unknown datatype or incorrect datatype
+ */
+
+ public Object createDynamicElement(String name)
+ {
+ if (getProject() == null)
+ throw new BuildException(
+ "createDynamicElement.TokenFilter" +
+ " - Unable to get the project");
+
+ Object obj = getProject().createDataType(name);
+ if (obj == null)
+ throw new BuildException("Unknown type " + name);
+ if (obj instanceof Filter)
+ filters.addElement(obj);
+ else if (obj instanceof Tokenizer) {
+ if (this.tokenizer != null)
+ throw new BuildException("Only one tokenizer allowed");
+ tokenizer = (Tokenizer) obj;
+ }
+ else
+ throw new BuildException(
+ "type " + name + " is not a TokenFilter.Filter or " +
+ "TokenFiler.Tokenizer");
+ return obj;
+ }
+
+
+ /**
+ * Needed for dynamic element support.
+ *
+ * @throws BuildException always
+ */
+
+ public void setDynamicAttribute(String name, String value) {
+ throw new BuildException("Unknown attribute " + name);
+ }
+
+ // --------------------------------------------
+ //
+ // Tokenizer Classes
+ //
+ // --------------------------------------------
+
+ /**
+ * class to read the complete input into a string
+ */
+ public static class FileTokenizer
+ implements Tokenizer
+ {
+ /**
+ * Get the complete input as a string
+ *
+ * @return the complete input
+ */
+ public String getToken(Reader in)
+ throws IOException
+ {
+ StringBuffer output = new StringBuffer();
+ char[] buffer = new char[8192];
+ while (true) {
+ int nread = in.read(buffer, 0, 8192);
+ if (nread == -1)
+ break;
+ output.append(buffer, 0, nread);
+ }
+ if (output.length() == 0)
+ return null;
+ return output.toString();
+ }
+
+ /**
+ * Return an empty string
+ *
+ * @return an empty string
+ */
+ public String getPostToken() {
+ return "";
+ }
+ }
+
+
+ /**
+ * class to tokenize the input as lines seperated
+ * by \r (mac style), \r\n (dos/windows style) or \n (unix style)
+ */
+ public static class LineTokenizer
+ implements Tokenizer
+ {
+ String lineEnd = "";
+ int pushed = -2;
+
+ public String getToken(Reader in)
+ throws IOException
+ {
+ int ch = -1;
+ if (pushed != -2) {
+ ch = pushed;
+ pushed = -2;
+ }
+ else
+ ch = in.read();
+ if (ch == -1) {
+ return null;
+ }
+
+ lineEnd = "";
+ StringBuffer line = new StringBuffer();
+
+ int state = 0;
+ while (ch != -1) {
+ if (state == 0) {
+ if (ch == '\r') {
+ state = 1;
+ }
+ else if (ch == '\n') {
+ lineEnd = "\n";
+ break;
+ }
+ else {
+ line.append((char) ch);
+ }
+ }
+ else {
+ state = 0;
+ if (ch == '\n') {
+ lineEnd = "\r\n";
+ }
+ else {
+ pushed = ch;
+ lineEnd = "\r";
+ }
+ break;
+ }
+ ch = in.read();
+ }
+ if (ch == -1 && state == 1) {
+ lineEnd = "\r";
+ }
+
+ return line.toString();
+ }
+
+ public String getPostToken() {
+ return lineEnd;
+ }
+ }
+
+ /**
+ * class to tokenize the input as areas seperated
+ * by white space, or by a specified list of
+ * delim characters. Behaves like java.util.StringTokenizer.
+ * if the stream starts with delim characters, the first
+ * token will be an empty string (unless the treat tokens
+ * as delims flag is set).
+ */
+ public static class StringTokenizer
+ implements Tokenizer
+ {
+ private String intraString = "";
+ private int pushed = -2;
+ private char[] delims = null;
+ private boolean delimsAreTokens = false;
+ private boolean suppressDelims = false;
+
+ public void setDelims(String delims) {
+ this.delims = resolveBackSlash(delims).toCharArray();
+ }
+
+ public void setDelimsAreTokens(boolean delimsAreTokens) {
+ this.delimsAreTokens = delimsAreTokens;
+ }
+ public void setSuppressDelims(boolean suppressDelims) {
+ this.suppressDelims = suppressDelims;
+ }
+
+ public String getToken(Reader in)
+ throws IOException
+ {
+ int ch = -1;
+ if (pushed != -2) {
+ ch = pushed;
+ pushed = -2;
+ }
+ else
+ ch = in.read();
+ if (ch == -1) {
+ return null;
+ }
+ boolean inToken = true;
+ intraString = "";
+ StringBuffer word = new StringBuffer();
+ StringBuffer padding = new StringBuffer();
+ while (ch != -1) {
+ char c = (char) ch;
+ boolean isDelim = isDelim(c);
+ if (inToken) {
+ if (isDelim) {
+ if (delimsAreTokens) {
+ if (word.length() == 0) {
+ word.append(c);
+ }
+ else {
+ pushed = ch;
+ }
+ break;
+ }
+ padding.append(c);
+ inToken = false;
+ }
+ else
+ word.append(c);
+ }
+ else {
+ if (isDelim) {
+ padding.append(c);
+ }
+ else {
+ pushed = ch;
+ break;
+ }
+ }
+ ch = in.read();
+ }
+ intraString = padding.toString();
+ return word.toString();
+ }
+
+ public String getPostToken() {
+ if (suppressDelims)
+ return "";
+ return intraString;
+ }
+
+ private boolean isDelim(char ch) {
+ if (delims == null)
+ return Character.isWhitespace(ch);
+ for (int i = 0; i < delims.length; ++i)
+ if (delims[i] == ch)
+ return true;
+ return false;
+ }
+ }
+
+ // --------------------------------------------
+ //
+ // Filter classes
+ //
+ // --------------------------------------------
+
+ public static abstract class ChainableReaderFilter
+ implements ChainableReader, Filter
+ {
+ private boolean byLine = true;
+
+ public void setByLine(boolean byLine) {
+ this.byLine = byLine;
+ }
+
+ public Reader chain(Reader reader) {
+ TokenFilter tokenFilter = new TokenFilter(reader);
+ if (!byLine)
+ tokenFilter.addFileTokenizer(new FileTokenizer());
+ tokenFilter.add(this);
+ return tokenFilter;
+ }
+ }
+
+ /**
+ * Simple replace string filter.
+ */
+ public static class ReplaceString
+ extends ChainableReaderFilter
+ {
+ private String from;
+ private String to;
+
+ public void setFrom(String from) {
+ this.from = from;
+ }
+ public void setTo(String to) {
+ this.to = to;
+ }
+
+ /**
+ * CAP from the Replace task
+ */
+ public String filter(String line) {
+ if (from == null)
+ throw new BuildException("Missing from in stringreplace");
+ StringBuffer ret = new StringBuffer();
+ int start = 0;
+ int found = line.indexOf(from);
+ while (found >= 0) {
+ // write everything up to the from
+ if (found > start) {
+ ret.append(line.substring(start, found));
+ }
+
+ // write the replacement to
+ if (to != null) {
+ ret.append(to);
+ }
+
+ // search again
+ start = found + from.length();
+ found = line.indexOf(line, start);
+ }
+
+ // write the remaining characters
+ if (line.length() > start) {
+ ret.append(line.substring(start, line.length()));
+ }
+
+ return ret.toString();
+ }
+ }
+
+ /**
+ * Simple filter to filter lines contains strings
+ */
+ public static class ContainsString
+ implements Filter
+ {
+ private String contains;
+
+ public void setContains(String contains) {
+ this.contains = contains;
+ }
+
+ public String filter(String line) {
+ if (contains == null)
+ throw new BuildException("Missing contains in containsstring");
+ if (line.indexOf(contains) > -1)
+ return line;
+ return null;
+ }
+ }
+
+ /**
+ * filter to replace regex.
+ */
+ public static class ReplaceRegex
+ extends ChainableReaderFilter
+ {
+ private String from;
+ private String to;
+ private Project project;
+ private RegularExpression regularExpression;
+ private Substitution substitution;
+ private boolean initialized = false;
+ private String flags = "";
+ private int options;
+ private Regexp regexp;
+
+
+ public void setPattern(String from) {
+ this.from = from;
+ }
+ public void setReplace(String to) {
+ this.to = to;
+ }
+
+ public void setProject(Project p) {
+ this.project = p;
+ }
+
+ public void setFlags(String flags) {
+ this.flags = flags;
+ }
+
+ private void initialize() {
+ if (initialized)
+ return;
+ options = convertRegexOptions(flags);
+ if (from == null)
+ throw new BuildException("Missing pattern in replaceregex");
+ regularExpression = new RegularExpression();
+ regularExpression.setPattern(from);
+ regexp = regularExpression.getRegexp(project);
+ if (to == null)
+ to = "";
+ substitution = new Substitution();
+ substitution.setExpression(to);
+ }
+
+ public String filter(String line) {
+ initialize();
+
+ if (!regexp.matches(line, options)) {
+ return line;
+ }
+ return regexp.substitute(
+ line, substitution.getExpression(project), options);
+ }
+ }
+
+ /**
+ * filter to filter tokens matching regular expressions.
+ */
+ public static class ContainsRegex
+ extends ChainableReaderFilter
+ {
+ private String from;
+ private String to;
+ private Project project;
+ private RegularExpression regularExpression;
+ private Substitution substitution;
+ private boolean initialized = false;
+ private String flags = "";
+ private int options;
+ private Regexp regexp;
+
+
+ public void setPattern(String from) {
+ this.from = from;
+ }
+ public void setReplace(String to) {
+ this.to = to;
+ }
+
+ public void setProject(Project p) {
+ this.project = p;
+ }
+
+ public void setFlags(String flags) {
+ this.flags = flags;
+ }
+
+ private void initialize() {
+ if (initialized)
+ return;
+ options = convertRegexOptions(flags);
+ if (from == null)
+ throw new BuildException("Missing from in containsregex");
+ regularExpression = new RegularExpression();
+ regularExpression.setPattern(from);
+ regexp = regularExpression.getRegexp(project);
+ if (to == null)
+ return;
+ substitution = new Substitution();
+ substitution.setExpression(to);
+ }
+
+ public String filter(String line) {
+ initialize();
+
+
+ if (!regexp.matches(line, options)) {
+ return null;
+ }
+ if (substitution == null)
+ return line;
+ return regexp.substitute(
+ line, substitution.getExpression(project), options);
+ }
+ }
+
+ /** Filter to trim white space */
+ public static class Trim
+ extends ChainableReaderFilter
+ {
+ public String filter(String line) {
+ return line.trim();
+ }
+ }
+
+
+
+ /** Filter remove empty tokens */
+ public static class IgnoreBlank
+ extends ChainableReaderFilter
+ {
+ public String filter(String line) {
+ if (line.trim().length() == 0)
+ return null;
+ return line;
+ }
+ }
+
+ /**
+ * Filter to delete characters
+ */
+ public static class DeleteCharacters
+ implements Filter, ChainableReader
+ {
+ // Attributes
+ /** the list of characters to remove from the input */
+ private String deleteChars = "";
+
+ /** Set the list of characters to delete */
+ public void setChars(String deleteChars) {
+ this.deleteChars = resolveBackSlash(deleteChars);
+ }
+
+ /** remove characters from a string */
+ public String filter(String string) {
+ StringBuffer output = new StringBuffer(string.length());
+ for (int i = 0; i < string.length(); ++i) {
+ char ch = string.charAt(i);
+ if (! isDeleteCharacter(ch))
+ output.append(ch);
+ }
+ return output.toString();
+ }
+
+ /**
+ * factory method to provide a reader that removes
+ * the characters from a reader as part of a filter
+ * chain
+ */
+ public Reader chain(Reader reader) {
+ return new BaseFilterReader(reader) {
+ public int read()
+ throws IOException
+ {
+ while (true) {
+ int c = in.read();
+ if (c == -1)
+ return c;
+ if (! isDeleteCharacter((char) c))
+ return c;
+ }
+ }
+ };
+ }
+
+ /** check if the character c is to be deleted */
+ private boolean isDeleteCharacter(char c) {
+ for (int d = 0; d < deleteChars.length(); ++d) {
+ if (deleteChars.charAt(d) == c) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ // --------------------------------------------------------
+ // static utility methods - could be placed somewhere else
+ // --------------------------------------------------------
+
+ /**
+ * xml does not do "c" like interpetation of strings.
+ * i.e. \n\r\t etc.
+ * this methid processes \n, \r, \t, \f, \\
+ * also subs \s -> " \n\r\t\f"
+ * a trailing '\' will be ignored
+ *
+ * @param input raw string with possible embedded '\'s
+ * @return converted string
+ */
+ public static String resolveBackSlash(String input) {
+ StringBuffer b = new StringBuffer();
+ boolean backSlashSeen = false;
+ for (int i = 0; i < input.length(); ++i) {
+ char c = input.charAt(i);
+ if (! backSlashSeen) {
+ if (c == '\\')
+ backSlashSeen = true;
+ else
+ b.append(c);
+ }
+ else {
+ switch (c) {
+ case '\\':
+ b.append((char) '\\');
+ break;
+ case 'n':
+ b.append((char) '\n');
+ break;
+ case 'r':
+ b.append((char) '\r');
+ break;
+ case 't':
+ b.append((char) '\t');
+ break;
+ case 'f':
+ b.append((char) '\f');
+ break;
+ case 's':
+ b.append(" \t\n\r\f");
+ break;
+ default:
+ b.append(c);
+ }
+ backSlashSeen = false;
+ }
+ }
+ return b.toString();
+ }
+
+ /**
+ * convert regex option flag characters to regex options
+ *
+ *
g - Regexp.REPLACE_ALL
+ * i - Regexp.MATCH_CASE_INSENSITIVE
+ * m - Regexp.MATCH_MULTILINE
+ * s - Regexp.MATCH_SINGLELINE
+ *
+ */
+ public static int convertRegexOptions(String flags) {
+ if (flags == null)
+ return 0;
+ int options = 0;
+ if (flags.indexOf('g') != -1)
+ options |= Regexp.REPLACE_ALL;
+ if (flags.indexOf('i') != -1)
+ options |= Regexp.MATCH_CASE_INSENSITIVE;
+ if (flags.indexOf('m') != -1)
+ options |= Regexp.MATCH_MULTILINE;
+ if (flags.indexOf('s') != -1)
+ options |= Regexp.MATCH_SINGLELINE;
+ return options;
+ }
+}
diff --git a/src/main/org/apache/tools/ant/types/FilterChain.java b/src/main/org/apache/tools/ant/types/FilterChain.java
index cbaf8a4ce..321b15245 100644
--- a/src/main/org/apache/tools/ant/types/FilterChain.java
+++ b/src/main/org/apache/tools/ant/types/FilterChain.java
@@ -54,7 +54,13 @@
package org.apache.tools.ant.types;
import java.util.Vector;
+import java.io.StringWriter;
+import java.io.Reader;
+import java.io.IOException;
+
import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DynamicConfigurator;
+import org.apache.tools.ant.filters.ChainableReader;
import org.apache.tools.ant.filters.ClassConstants;
import org.apache.tools.ant.filters.EscapeUnicode;
import org.apache.tools.ant.filters.ExpandProperties;
@@ -68,13 +74,19 @@ import org.apache.tools.ant.filters.StripLineBreaks;
import org.apache.tools.ant.filters.StripLineComments;
import org.apache.tools.ant.filters.TabsToSpaces;
import org.apache.tools.ant.filters.TailFilter;
+import org.apache.tools.ant.filters.TokenFilter;
+import org.apache.tools.ant.filters.BaseFilterReader;
+import org.apache.tools.ant.taskdefs.Concat;
+
/**
* FilterChain may contain a chained set of filter readers.
*
* @author Magesh Umasankar
*/
-public final class FilterChain extends DataType implements Cloneable {
+public final class FilterChain extends DataType
+ implements Cloneable, DynamicConfigurator
+{
private Vector filterReaders = new Vector();
@@ -145,6 +157,69 @@ public final class FilterChain extends DataType implements Cloneable {
filterReaders.addElement(escapeUnicode);
}
+ /**
+ * @since Ant 1.6
+ */
+ public final void addTokenFilter(final TokenFilter tokenFilter) {
+ filterReaders.addElement(tokenFilter);
+ }
+
+ /**
+ * delete characters filter
+ * @since Ant 1.6
+ */
+ public void addDeleteCharacters(TokenFilter.DeleteCharacters filter) {
+ filterReaders.addElement(filter);
+ }
+
+ /**
+ * containsregex
+ * @since Ant 1.6
+ */
+ public void addContainsRegex(TokenFilter.ContainsRegex filter)
+ {
+ filterReaders.addElement(filter);
+ }
+
+ /**
+ * replaceregex
+ * @since Ant 1.6
+ */
+ public void addReplaceRegex(TokenFilter.ReplaceRegex filter)
+ {
+ filterReaders.addElement(filter);
+ }
+
+ /**
+ * trim
+ * @since Ant 1.6
+ */
+ public void addTrim(TokenFilter.Trim filter)
+ {
+ filterReaders.addElement(filter);
+ }
+
+ /**
+ * replacestring
+ * @since Ant 1.6
+ */
+ public void addReplaceString(
+ TokenFilter.ReplaceString filter)
+ {
+ filterReaders.addElement(filter);
+ }
+
+ /**
+ * ignoreBlank
+ * @since Ant 1.6
+ */
+ public void addIgnoreBlank(
+ TokenFilter.IgnoreBlank filter)
+ {
+ filterReaders.addElement(filter);
+ }
+
+
/**
* Makes this instance in effect a reference to another FilterChain
* instance.
@@ -171,4 +246,38 @@ public final class FilterChain extends DataType implements Cloneable {
super.setRefid(r);
}
+
+ /**
+ * create the named datatype and check if it
+ * is a filter.
+ *
+ * @throws BuildException if unknown datatype or incorrect datatype
+ * @since Ant 1.6
+ */
+
+ public Object createDynamicElement(String name)
+ {
+ if (getProject() == null)
+ throw new BuildException("Unable to get the project");
+
+ Object obj = getProject().createDataType(name);
+ if (obj == null)
+ throw new BuildException("Unknown type " + name);
+ if (! (obj instanceof ChainableReader))
+ throw new BuildException(
+ "type " + name + " is not a filterreader");
+ filterReaders.addElement(obj);
+ return obj;
+ }
+
+ /**
+ * Needed for dynamic element support.
+ *
+ * @throws BuildException always
+ */
+
+ public void setDynamicAttribute(String name, String value) {
+ throw new BuildException("Unknown attribute " + name);
+ }
+
}
diff --git a/src/main/org/apache/tools/ant/types/defaults.properties b/src/main/org/apache/tools/ant/types/defaults.properties
index 10b34082a..34b5d168a 100644
--- a/src/main/org/apache/tools/ant/types/defaults.properties
+++ b/src/main/org/apache/tools/ant/types/defaults.properties
@@ -16,3 +16,5 @@ extensionSet=org.apache.tools.ant.taskdefs.optional.extension.ExtensionSet
extension=org.apache.tools.ant.taskdefs.optional.extension.ExtensionAdapter
libfileset=org.apache.tools.ant.taskdefs.optional.extension.LibFileSet
selector=org.apache.tools.ant.types.selectors.SelectSelector
+scriptfilter=org.apache.tools.ant.types.optional.ScriptFilter
+
diff --git a/src/main/org/apache/tools/ant/types/optional/ScriptFilter.java b/src/main/org/apache/tools/ant/types/optional/ScriptFilter.java
new file mode 100644
index 000000000..b08618ee8
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/optional/ScriptFilter.java
@@ -0,0 +1,269 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 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 "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 apache@apache.org.
+ *
+ * 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
+ * .
+ */
+package org.apache.tools.ant.types.optional;
+
+import org.apache.tools.ant.filters.TokenFilter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import org.apache.bsf.BSFException;
+import org.apache.bsf.BSFManager;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+
+/**
+ * Most of this is CAP (Cut And Paste) from the Script task
+ * ScriptFilter class, implements TokenFilter.Filter
+ * for scripts to use.
+ * This provides the same beans as the Script Task
+ * to a script.
+ * The script is meant to use get self.token and
+ * set self.token in the reply.
+ */
+
+public class ScriptFilter
+ extends TokenFilter.ChainableReaderFilter
+{
+ /** The current project - set by ant reflection */
+ private Project project;
+ /** The language - attribute of element */
+ private String language;
+ /** The script - inline text or external file */
+ private String script = "";
+ /** The beans - see ScriptTask */
+ private Hashtable beans = new Hashtable();
+ /** Has this object been initialized ? */
+ private boolean initialized = false;
+ /** the BSF manager */
+ private BSFManager manager;
+ /** the token used by the script */
+ private String token;
+
+ /** Called by ant reflection to set the project */
+ public void setProject(Project project) {
+ this.project = project;
+ }
+
+ /** this is provided to allow easier CAP from the ScriptTask */
+ private Project getProject() {
+ return project;
+ }
+
+ /**
+ * Defines the language (required).
+ *
+ * @param msg Sets the value for the script variable.
+ */
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+
+ /**
+ * Add a list of named objects to the list to be exported to the script
+ * CAP from taskdefs.optional.Script
+ */
+ private void addBeans(Hashtable dictionary) {
+ for (Enumeration e = dictionary.keys(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+
+ boolean isValid = key.length() > 0 &&
+ Character.isJavaIdentifierStart(key.charAt(0));
+
+ for (int i = 1; isValid && i < key.length(); i++) {
+ isValid = Character.isJavaIdentifierPart(key.charAt(i));
+ }
+
+ try {
+ if (isValid) {
+ beans.put(key, dictionary.get(key));
+ }
+ }
+ catch (Throwable t) {
+ throw new BuildException(t);
+ //System.err.println("What the helll");
+ }
+ }
+ }
+ /**
+ * Initialize, mostly CAP from taskdefs.option.Script#execute()
+ *
+ * @exception BuildException if someting goes wrong
+ */
+ private void init() throws BuildException {
+ if (initialized)
+ return;
+ initialized = true;
+ if (language == null)
+ throw new BuildException(
+ "scriptfilter: language is not defined");
+
+ try {
+ addBeans(getProject().getProperties());
+ addBeans(getProject().getUserProperties());
+ addBeans(getProject().getTargets());
+ addBeans(getProject().getReferences());
+
+ beans.put("project", getProject());
+
+ beans.put("self", this);
+
+ manager = new BSFManager ();
+
+ for (Enumeration e = beans.keys() ; e.hasMoreElements() ;) {
+ String key = (String) e.nextElement();
+ Object value = beans.get(key);
+ manager.declareBean(key, value, value.getClass());
+ }
+
+ }
+ catch (BSFException e) {
+ Throwable t = e;
+ Throwable te = e.getTargetException();
+ if (te != null) {
+ if (te instanceof BuildException) {
+ throw (BuildException) te;
+ } else {
+ t = te;
+ }
+ }
+ throw new BuildException(t);
+ }
+
+ }
+
+ /**
+ * The current token
+ *
+ * @param token the string filtered by the script
+ */
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ /**
+ * The current token
+ *
+ * @return the string filtered by the script
+ */
+ public String getToken() {
+ return token;
+ }
+
+ /**
+ * Called filter the token.
+ * This sets the token in this object, calls
+ * the script and returns the token.
+ *
+ * @param token the token to be filtered
+ * @return the filtered token
+ */
+ public String filter(String token) {
+ init();
+ setToken(token);
+ try {
+ manager.exec(language, "", 0, 0, script);
+ return getToken();
+ }
+ catch (BSFException be) {
+ Throwable t = be;
+ Throwable te = be.getTargetException();
+ if (te != null) {
+ if (te instanceof BuildException) {
+ throw (BuildException) te;
+ } else {
+ t = te;
+ }
+ }
+ throw new BuildException(t);
+ }
+ }
+ /**
+ * Load the script from an external file ; optional.
+ *
+ * @param msg Sets the value for the script variable.
+ */
+ public void setSrc(String fileName) {
+ File file = new File(fileName);
+ if (!file.exists()) {
+ throw new BuildException("file " + fileName + " not found.");
+ }
+
+ int count = (int) file.length();
+ byte data[] = new byte[count];
+
+ try {
+ FileInputStream inStream = new FileInputStream(file);
+ inStream.read(data);
+ inStream.close();
+ } catch (IOException e) {
+ throw new BuildException(e);
+ }
+
+ script += new String(data);
+ }
+
+ /**
+ * The script text.
+ *
+ * @param msg Sets the value for the script variable.
+ */
+ public void addText(String text) {
+ this.script += text;
+ }
+}
diff --git a/src/testcases/org/apache/tools/ant/filters/DynamicFilterTest.java b/src/testcases/org/apache/tools/ant/filters/DynamicFilterTest.java
new file mode 100644
index 000000000..dca8874e0
--- /dev/null
+++ b/src/testcases/org/apache/tools/ant/filters/DynamicFilterTest.java
@@ -0,0 +1,167 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 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 "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 apache@apache.org.
+ *
+ * 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
+ * .
+ */
+
+package org.apache.tools.ant.filters;
+
+import java.io.File;
+import java.io.Reader;
+import java.io.FileReader;
+import java.io.IOException;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * @author Peter Reilly
+ */
+public class DynamicFilterTest extends BuildFileTest {
+
+ public DynamicFilterTest(String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ configureProject("src/etc/testcases/filters/dynamicfilter.xml");
+ executeTarget("init");
+ }
+
+ public void tearDown() {
+ executeTarget("cleanup");
+ }
+ public void testCustomFilter() throws IOException {
+ expectFileContains("dynamicfilter", "result/dynamicfilter",
+ "hellO wOrld");
+ }
+
+ // ------------------------------------------------------
+ // Helper methods
+ // -----------------------------------------------------
+
+
+ private void assertStringContains(String string, String contains) {
+ assertTrue("[" + string + "] does not contain [" + contains +"]",
+ string.indexOf(contains) > -1);
+ }
+
+ private void assertStringNotContains(String string, String contains) {
+ assertTrue("[" + string + "] does contain [" + contains +"]",
+ string.indexOf(contains) == -1);
+ }
+
+ private String getFileString(String filename)
+ throws IOException
+ {
+ Reader r = null;
+ try {
+ r = new FileReader(getProject().resolveFile(filename));
+ return FileUtils.newFileUtils().readFully(r);
+ }
+ finally {
+ try {r.close();} catch (Throwable ignore) {}
+ }
+
+ }
+
+ private String getFileString(String target, String filename)
+ throws IOException
+ {
+ executeTarget(target);
+ return getFileString(filename);
+ }
+
+ private void expectFileContains(String name, String contains)
+ throws IOException
+ {
+ String content = getFileString(name);
+ assertTrue(
+ "expecting file " + name + " to contain " + contains +
+ " but got " + content, content.indexOf(contains) > -1);
+ }
+
+ private void expectFileContains(
+ String target, String name, String contains)
+ throws IOException
+ {
+ executeTarget(target);
+ expectFileContains(name, contains);
+ }
+
+ public static class CustomFilter implements ChainableReader {
+ char replace = 'x';
+ char with = 'y';
+
+ public void setReplace(char replace) {
+ this.replace = replace;
+ }
+
+ public void setWith(char with) {
+ this.with = with;
+ }
+
+ public Reader chain(final Reader rdr) {
+ return new BaseFilterReader(rdr) {
+ public int read()
+ throws IOException
+ {
+ int c = in.read();
+ if (c == replace)
+ return with;
+ else
+ return c;
+ }
+ };
+ }
+ }
+}
diff --git a/src/testcases/org/apache/tools/ant/filters/TokenFilterTest.java b/src/testcases/org/apache/tools/ant/filters/TokenFilterTest.java
new file mode 100644
index 000000000..691755610
--- /dev/null
+++ b/src/testcases/org/apache/tools/ant/filters/TokenFilterTest.java
@@ -0,0 +1,319 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 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 "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 apache@apache.org.
+ *
+ * 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
+ * .
+ */
+
+package org.apache.tools.ant.filters;
+
+import java.io.File;
+import java.io.Reader;
+import java.io.FileReader;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * @author Peter Reilly
+ */
+public class TokenFilterTest extends BuildFileTest {
+
+ public TokenFilterTest(String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ configureProject("src/etc/testcases/filters/tokenfilter.xml");
+ executeTarget("init");
+ }
+
+ public void tearDown() {
+ //executeTarget("cleanup");
+ }
+
+ /** make sure tokenfilter exists */
+ public void testTokenfilter() throws IOException {
+ executeTarget("tokenfilter");
+ }
+
+ public void testTrimignore() throws IOException {
+ expectLogContaining("trimignore", "Hello-World");
+ }
+
+ public void testStringTokenizer() throws IOException {
+ expectLogContaining(
+ "stringtokenizer", "#This#is#a#number#of#words#");
+ }
+
+ public void testUnixLineOutput() throws IOException {
+ expectFileContains(
+ "unixlineoutput", "result/unixlineoutput",
+ "\nThis\nis\na\nnumber\nof\nwords\n");
+ }
+
+ public void testDosLineOutput() throws IOException {
+ expectFileContains(
+ "doslineoutput", "result/doslineoutput",
+ "\r\nThis\r\nis\r\na\r\nnumber\r\nof\r\nwords\r\n");
+ }
+
+ public void testFileTokenizer() throws IOException {
+ String contents = getFileString(
+ "filetokenizer", "result/filetokenizer");
+ assertStringContains(contents, " of words");
+ assertStringNotContains(contents, " This is");
+ }
+
+ public void testReplaceString() throws IOException {
+ expectFileContains(
+ "replacestring", "result/replacestring",
+ "this is the moon");
+ }
+
+ public void testContainsString() throws IOException {
+ String contents = getFileString(
+ "containsstring", "result/containsstring");
+ assertStringContains(contents, "this is a line contains foo");
+ assertStringNotContains(contents, "this line does not");
+ }
+
+ public void testReplaceRegex() throws IOException {
+ if (! hasRegex("testReplaceRegex"))
+ return;
+ String contents = getFileString(
+ "replaceregex", "result/replaceregex");
+ assertStringContains(contents, "world world world world");
+ assertStringContains(contents, "dog Cat dog");
+ assertStringContains(contents, "moon Sun Sun");
+ assertStringContains(contents, "found WhiteSpace");
+ assertStringContains(contents, "Found digits [1234]");
+ assertStringNotContains(contents, "This is a line with digits");
+ }
+
+ public void testFilterReplaceRegex() throws IOException {
+ if (! hasRegex("testFilterReplaceRegex"))
+ return;
+ String contents = getFileString(
+ "filterreplaceregex", "result/filterreplaceregex");
+ assertStringContains(contents, "world world world world");
+ }
+
+ public void testTrimFile() throws IOException {
+ String contents = getFileString(
+ "trimfile", "result/trimfile");
+ assertTrue("no ws at start", contents.startsWith("This is th"));
+ assertTrue("no ws at end", contents.endsWith("second line."));
+ assertStringContains(contents, " This is the second");
+ }
+
+ public void testTrimFileByLine() throws IOException {
+ String contents = getFileString(
+ "trimfilebyline", "result/trimfilebyline");
+ assertFalse("no ws at start", contents.startsWith("This is th"));
+ assertFalse("no ws at end", contents.endsWith("second line."));
+ assertStringNotContains(contents, " This is the second");
+ assertStringContains(contents, "file.\nThis is the second");
+ }
+
+ public void testFilterReplaceString() throws IOException {
+ String contents = getFileString(
+ "filterreplacestring", "result/filterreplacestring");
+ assertStringContains(contents, "This is the moon");
+ }
+
+ public void testContainsRegex() throws IOException {
+ if (! hasRegex("testContainsRegex"))
+ return;
+ String contents = getFileString(
+ "containsregex", "result/containsregex");
+ assertStringContains(contents, "hello world");
+ assertStringNotContains(contents, "this is the moon");
+ assertStringContains(contents, "World here");
+ }
+
+ public void testFilterContainsRegex() throws IOException {
+ if (! hasRegex("testFilterContainsRegex"))
+ return;
+ String contents = getFileString(
+ "filtercontainsregex", "result/filtercontainsregex");
+ assertStringContains(contents, "hello world");
+ assertStringNotContains(contents, "this is the moon");
+ assertStringContains(contents, "World here");
+ }
+
+ public void testContainsRegex2() throws IOException {
+ if (! hasRegex("testContainsRegex2"))
+ return;
+ String contents = getFileString(
+ "containsregex2", "result/containsregex2");
+ assertStringContains(contents, "void register_bits();");
+ }
+
+ public void testDeleteCharacters() throws IOException {
+ String contents = getFileString(
+ "deletecharacters", "result/deletechars");
+ assertStringNotContains(contents, "#");
+ assertStringNotContains(contents, "*");
+ assertStringContains(contents, "This is some ");
+ }
+
+ public void testScriptFilter() throws IOException {
+ if (! hasScript("testScriptFilter"))
+ return;
+
+ expectFileContains("scriptfilter", "result/scriptfilter",
+ "HELLO WORLD");
+ }
+
+
+ public void testScriptFilter2() throws IOException {
+ if (! hasScript("testScriptFilter"))
+ return;
+
+ expectFileContains("scriptfilter2", "result/scriptfilter2",
+ "HELLO MOON");
+ }
+
+ public void testCustomTokenFilter() throws IOException {
+ expectFileContains("customtokenfilter", "result/custom",
+ "Hello World");
+ }
+
+ // ------------------------------------------------------
+ // Helper methods
+ // -----------------------------------------------------
+ private boolean hasScript(String test) {
+ try {
+ executeTarget("hasscript");
+ }
+ catch (Throwable ex) {
+ System.out.println(
+ test + ": skipped - script not present ");
+ ex.printStackTrace(System.out);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean hasRegex(String test) {
+ try {
+ executeTarget("hasregex");
+ expectFileContains("result/replaceregexp", "bye world");
+ }
+ catch (Throwable ex) {
+ System.out.println(test + ": skipped - regex not present "
+ + ex);
+ return false;
+ }
+ return true;
+ }
+
+ private void assertStringContains(String string, String contains) {
+ assertTrue("[" + string + "] does not contain [" + contains +"]",
+ string.indexOf(contains) > -1);
+ }
+
+ private void assertStringNotContains(String string, String contains) {
+ assertTrue("[" + string + "] does contain [" + contains +"]",
+ string.indexOf(contains) == -1);
+ }
+
+ private String getFileString(String filename)
+ throws IOException
+ {
+ Reader r = null;
+ try {
+ r = new FileReader(getProject().resolveFile(filename));
+ return FileUtils.newFileUtils().readFully(r);
+ }
+ finally {
+ try {r.close();} catch (Throwable ignore) {}
+ }
+
+ }
+
+ private String getFileString(String target, String filename)
+ throws IOException
+ {
+ executeTarget(target);
+ return getFileString(filename);
+ }
+
+ private void expectFileContains(String name, String contains)
+ throws IOException
+ {
+ String content = getFileString(name);
+ assertTrue(
+ "expecting file " + name + " to contain " + contains +
+ " but got " + content, content.indexOf(contains) > -1);
+ }
+
+ private void expectFileContains(
+ String target, String name, String contains)
+ throws IOException
+ {
+ executeTarget(target);
+ expectFileContains(name, contains);
+ }
+
+ public static class Capitalize
+ implements TokenFilter.Filter
+ {
+ public String filter(String token) {
+ if (token.length() == 0)
+ return token;
+ return token.substring(0, 1).toUpperCase() +
+ token.substring(1);
+ }
+ }
+
+}