From 9f6552fadbf414a55a63c34aecbb70fca1b95772 Mon Sep 17 00:00:00 2001 From: Magesh Umasankar Date: Mon, 14 Apr 2003 18:02:24 +0000 Subject: [PATCH] New tokenfilter for filterchain PR: 18312 Submitted by: peter.reilly@corvil.com (peter reilly) git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@274449 13f79535-47bb-0310-9956-ffa450edef68 --- WHATSNEW | 3 + build.xml | 5 +- docs/manual/CoreTypes/filterchain.html | 478 ++++++++- src/etc/testcases/filters/dynamicfilter.xml | 34 + src/etc/testcases/filters/tokenfilter.xml | 314 ++++++ .../apache/tools/ant/filters/TokenFilter.java | 918 ++++++++++++++++++ .../apache/tools/ant/types/FilterChain.java | 111 ++- .../tools/ant/types/defaults.properties | 2 + .../ant/types/optional/ScriptFilter.java | 269 +++++ .../tools/ant/filters/DynamicFilterTest.java | 167 ++++ .../tools/ant/filters/TokenFilterTest.java | 319 ++++++ 11 files changed, 2617 insertions(+), 3 deletions(-) create mode 100644 src/etc/testcases/filters/dynamicfilter.xml create mode 100644 src/etc/testcases/filters/tokenfilter.xml create mode 100644 src/main/org/apache/tools/ant/filters/TokenFilter.java create mode 100644 src/main/org/apache/tools/ant/types/optional/ScriptFilter.java create mode 100644 src/testcases/org/apache/tools/ant/filters/DynamicFilterTest.java create mode 100644 src/testcases/org/apache/tools/ant/filters/TokenFilterTest.java 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

FilterReader

@@ -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>
 
+

DeleteCharacters

+ +

This filter deletes specified characters.

+

since Ant 1.6

+

This filter is only available in the convenience form.

+ + + + + + + + + + + + +
Parameter NameParameter ValueRequired
chars + The characters to delete. This attribute is + backslash enabled. + Yes
+

+

Examples:

+ +Delete tabs and returns from the data. +
+<deletecharacters chars="\t\r"/>
+
+ + +

TokenFilter

+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 \\. + + + + + + + + + + + + + +
AttributeDescriptionRequired
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). + + + + + + + + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
delimsThe delimiter characters. White space + is used if this is not set. (White space is defined + in this case by java.lang.Character.isWhitespace()). + No
delimsaretokensIf this is true, + each delimiter character is returned as a tokenNo
suppressdelimsIf 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. + + + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
fromThe string that must be replaced.Yes
toThe 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. + + + + + + + + + + + + +
AttributeDescriptionRequired
containsThe 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. + + + + + + + + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
patternThe regular expression pattern to match in + the token.Yes
replaceThe substitution pattern to replace the matched + regular expression. When omitted an empty string is used.No
flagsSee +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. + + + + + + + + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
patternThe regular expression pattern to match in + the token.Yes
replaceThe substitution pattern to replace the matched + regular expression. When omitted the orignal token is returned. + No
flagsSee +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. + + + + + + + + + + + + +
AttributeDescriptionRequired
charsThe 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.

+ + + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
language The programming language the script is written in. +Must be a supported Apache BSF languageYes
srcThe 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); + } + } + +}