Browse Source

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
master
Magesh Umasankar 22 years ago
parent
commit
9f6552fadb
11 changed files with 2617 additions and 3 deletions
  1. +3
    -0
      WHATSNEW
  2. +4
    -1
      build.xml
  3. +477
    -1
      docs/manual/CoreTypes/filterchain.html
  4. +34
    -0
      src/etc/testcases/filters/dynamicfilter.xml
  5. +314
    -0
      src/etc/testcases/filters/tokenfilter.xml
  6. +918
    -0
      src/main/org/apache/tools/ant/filters/TokenFilter.java
  7. +110
    -1
      src/main/org/apache/tools/ant/types/FilterChain.java
  8. +2
    -0
      src/main/org/apache/tools/ant/types/defaults.properties
  9. +269
    -0
      src/main/org/apache/tools/ant/types/optional/ScriptFilter.java
  10. +167
    -0
      src/testcases/org/apache/tools/ant/filters/DynamicFilterTest.java
  11. +319
    -0
      src/testcases/org/apache/tools/ant/filters/TokenFilterTest.java

+ 3
- 0
WHATSNEW View File

@@ -99,6 +99,9 @@ Fixed bugs:


Other changes: 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 * A new attribute named skip is added to the TailFilter and
HeadFilter filter readers. HeadFilter filter readers.


+ 4
- 1
build.xml View File

@@ -202,7 +202,10 @@
<filename name="${ant.package}/listener/CommonsLoggingListener*"/> <filename name="${ant.package}/listener/CommonsLoggingListener*"/>
</selector> </selector>
<selector id="needs.bsf"> <selector id="needs.bsf">
<filename name="${optional.package}/Script*"/>
<or>
<filename name="${optional.package}/Script*"/>
<filename name="${optional.type.package}/Script*"/>
</or>
</selector> </selector>
<selector id="needs.stylebook"> <selector id="needs.stylebook">
<filename name="${optional.package}/StyleBook*"/> <filename name="${optional.package}/StyleBook*"/>


+ 477
- 1
docs/manual/CoreTypes/filterchain.html View File

@@ -101,6 +101,8 @@ nested elements.<BR>
<a href="#striplinecomments">StripLineComments</a><BR> <a href="#striplinecomments">StripLineComments</a><BR>
<a href="#tabstospaces">TabsToSpaces</a><BR> <a href="#tabstospaces">TabsToSpaces</a><BR>
<a href="#tailfilter">TailFilter</a><BR> <a href="#tailfilter">TailFilter</a><BR>
<a href="#deletecharacters">DeleteCharacters</a><BR>
<a href="#tokenfilter">TokenFilter</a><BR>


<H3><a name="filterreader">FilterReader</a></H3> <H3><a name="filterreader">FilterReader</a></H3>


@@ -552,7 +554,7 @@ that represent comments as specified by the user.
This removes all lines that begin with #, --, REM, rem and // This removes all lines that begin with #, --, REM, rem and //
<BLOCKQUOTE><PRE> <BLOCKQUOTE><PRE>
&lt;filterreader classname=&quot;org.apache.tools.ant.filters.StripLineComments&quot;&gt; &lt;filterreader classname=&quot;org.apache.tools.ant.filters.StripLineComments&quot;&gt;
&lt;param type=&quot;comment&quot; value="#&quot;/&gt;
&lt;param type=&quot;comment&quot; value=&quote;#&quot;/&gt;
&lt;param type=&quot;comment&quot; value=&quot;--&quot;/&gt; &lt;param type=&quot;comment&quot; value=&quot;--&quot;/&gt;
&lt;param type=&quot;comment&quot; value=&quot;REM &quot;/&gt; &lt;param type=&quot;comment&quot; value=&quot;REM &quot;/&gt;
&lt;param type=&quot;comment&quot; value=&quot;rem &quot;/&gt; &lt;param type=&quot;comment&quot; value=&quot;rem &quot;/&gt;
@@ -786,6 +788,480 @@ lines 49-58 are extracted)
&lt;/loadfile&gt; &lt;/loadfile&gt;
</PRE></BLOCKQUOTE> </PRE></BLOCKQUOTE>


<H3><a name="deletecharacters">DeleteCharacters</a></H3>

<p>This filter deletes specified characters.</p>
<p><em>since Ant 1.6</em></p>
<p>This filter is only available in the convenience form.</p>

<TABLE cellSpacing=0 cellPadding=2 border=1>
<TR>
<TD vAlign=top><B>Parameter Name</B></TD>
<TD vAlign=top><B>Parameter Value</B></TD>
<TD vAlign=top align="center"><B>Required</B></TD>
</TR>
<TR>
<TD vAlign=top>chars</TD>
<TD vAlign=top>
The characters to delete. This attribute is
<a href="#backslash">backslash enabled</a>.
</TD>
<TD vAlign=top align="center">Yes</TD>
</TR>
</TABLE>
<P>
<H4>Examples:</H4>

Delete tabs and returns from the data.
<BLOCKQUOTE><PRE>
&lt;deletecharacters chars="\t\r"/&gt;
</PRE></BLOCKQUOTE>


<H3><a name="tokenfilter">TokenFilter</a></H3>
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.
<P><em>since Ant 1.6</em></p>
<P>
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.
<P>
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 <i>delimOutput</i>
attribute.
<P>
<a name="backslash"><em>blackslash interpretation</em></a>
A number of attributes (including <i>delimOutput</i>) interpret
backslash escapes. The following are understood: \n, \r, \f, \t
and \\.


<TABLE cellSpacing=0 cellPadding=2 border=1>
<TR>
<TD vAlign=top><B>Attribute</B></TD>
<TD vAlign=top><B>Description</B></TD>
<TD vAlign=top align="center"><B>Required</B></TD>
</TR>
<TR>
<TD vAlign=top>delimOutput</TD>
<TD vAlign=top>
This overrides the tokendelimiter
returned by the tokenizer if it is not empty. This
attribute is backslash enabled.
</TD>
<TD vAlign=top align="center">No</TD>
</TR>
</TABLE>
<P>

The following tokenizers are provided by the default distribution.
<p>
<a href="#linetokenizer">LineTokenizer</a><br>
<a href="#filetokenizer">FileTokenizer</a><br>
<a href="#stringtokenizer">StringTokenizer</a><br>
</p>

The following string filters are provided by the default distribution.
<p>
<a href="#replacestring">ReplaceString</a><br>
<a href="#containsstring">ContainsString</a><br>
<a href="#replaceregex">ReplaceRegex</a><br>
<a href="#containsregex">ContainsRegex</a><br>
<a href="#trim">Trim</a><br>
<a href="#ignoreblank">IgnoreBlank</a><br>
<a href="#filterdeletecharacters">DeleteCharacters</a><br>
</p>

The following string filters are provided by the optional distribution.
<p>
<a href="#scriptfilter">ScriptFilter</a><br>
</p>

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".
<P>

<p><b><em><a name="linetokenizer">LineTokenizer</a></em></b></p>
This tokenizer splits the input into lines.
The tokenizer delimits lines
by "\r", "\n" or "\r\n".
This is the default tokenizer.
<H4>Examples:</H4>

Convert input current line endings to unix style line endings.
<em>This currently has no effect when used in the copy task.</em>
<BLOCKQUOTE><PRE>
&lt;tokenfilter delimoutput=&quot;\n&quot;/&gt;
</PRE></BLOCKQUOTE>


Remove blank lines.
<BLOCKQUOTE><PRE>
&lt;tokenfilter&gt;
&lt;ignoreblank/&gt;
&lt;/tokenfilter&gt;

</PRE></BLOCKQUOTE>

<p><b><em><a name="filetokenizer">FileTokenizer</a></em></b></p>
This tokenizer treats <b>all</b> the input as a token. So be
careful not to use this on very large input.
<H4>Examples:</H4>

Replace the first occurance of package with //package.
<BLOCKQUOTE><PRE>
&lt;tokenfilter&gt;
&lt;filetokenizer/&gt;
&lt;replaceregex pattern="([\n\r]+[ \t]*|^[ \t]*)package"
flags="s"
replace="\1//package"/&gt;
&lt;/tokenfilter&gt;
</PRE></BLOCKQUOTE>

<p><b><em><a name="stringtokenizer">StringTokenizer</a></em></b></p>
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 <i>delimsaretokens</i>
attribute is used).

<TABLE cellSpacing=0 cellPadding=2 border=1>
<TR>
<TD vAlign=top><B>Attribute</B></TD>
<TD vAlign=top><B>Description</B></TD>
<TD vAlign=top align="center"><B>Required</B></TD>
</TR>
<TR>
<TD vAlign=top>delims</TD>
<TD vAlign=top>The delimiter characters. White space
is used if this is not set. (White space is defined
in this case by java.lang.Character.isWhitespace()).
</TD>
<TD vAlign=top align="center">No</TD>
</TR>
<tr>
<td valign="top">delimsaretokens</td>
<td valign="top">If this is true,
each delimiter character is returned as a token</td>
<td valign="top" align="center">No</td>
</tr>
<tr>
<td valign="top">suppressdelims</td>
<td valign="top">If this is true, delimiters are not returned. </td>
<td valign="top" align="center">No</td>
</tr>
</TABLE>

<H4>Examples:</H4>

Surround each non space token with a "[]".

<BLOCKQUOTE><PRE>
&lt;tokenfilter&gt;
&lt;stringtokenizer/&gt;
&lt;replaceregex pattern="(.+)" replace="[\1]"/&gt;
&lt;/tokenfilter&gt;

</PRE></BLOCKQUOTE>

<p><b><em><a name="replacestring">ReplaceString</a></em></b></p>
This is a simple filter to replace strings.
This filter may be used directly within a filterchain.

<TABLE cellSpacing=0 cellPadding=2 border=1>
<TR>
<TD vAlign=top><B>Attribute</B></TD>
<TD vAlign=top><B>Description</B></TD>
<TD vAlign=top align="center"><B>Required</B></TD>
</TR>
<TR>
<TD vAlign=top>from</TD>
<TD vAlign=top>The string that must be replaced.</TD>
<TD vAlign=top align="center">Yes</TD>
</TR>
<tr>
<td valign="top">to</td>
<td valign="top">The new value for the replaced string. When omitted
an empty string is used.
</td>
<td valign="top" align="center">No</td>
</tr>
</TABLE>

<H4>Examples:</H4>

Replace "sun" with "moon".

<BLOCKQUOTE><PRE>
&lt;tokenfilter&gt;
&lt;replacestring from="sun" to="moon"/&gt;
&lt;/tokenfilter&gt;
</PRE></BLOCKQUOTE>

<p><b><em><a name="containsstring">ContainsString</a></em></b></p>
This is a simple filter to filter tokens that contains
a specified string.

<TABLE cellSpacing=0 cellPadding=2 border=1>
<TR>
<TD vAlign=top><B>Attribute</B></TD>
<TD vAlign=top><B>Description</B></TD>
<TD vAlign=top align="center"><B>Required</B></TD>
</TR>
<TR>
<TD vAlign=top>contains</TD>
<TD vAlign=top>The string that the token must contain.</TD>
<TD vAlign=top align="center">Yes</TD>
</TR>
</TABLE>

<H4>Examples:</H4>

Include only lines that contain "foo";

<BLOCKQUOTE><PRE>
&lt;tokenfilter&gt;
&lt;containsstring contains="foo"/&gt;
&lt;/tokenfilter&gt;

</PRE></BLOCKQUOTE>

<p><b><em><a name="replaceregex">ReplaceRegex</a></em></b></p>
This string filter replaces regular expressions. See
<a href="../OptionalTasks/replaceregexp.html">ReplaceRegexp</a>
for an explanation on regular expressions.
This filter may be used directly within a filterchain.

<TABLE cellSpacing=0 cellPadding=2 border=1>
<TR>
<TD vAlign=top><B>Attribute</B></TD>
<TD vAlign=top><B>Description</B></TD>
<TD vAlign=top align="center"><B>Required</B></TD>
</TR>
<TR>
<TD vAlign=top>pattern</TD>
<TD vAlign=top>The regular expression pattern to match in
the token.</TD>
<TD vAlign=top align="center">Yes</TD>
</TR>
<TR>
<TD vAlign=top>replace</TD>
<TD vAlign=top>The substitution pattern to replace the matched
regular expression. When omitted an empty string is used.</TD>
<TD vAlign=top align="center">No</TD>
</TR>
<TR>
<TD vAlign=top>flags</TD>
<TD vAlign=top>See
<a href="../OptionalTasks/replaceregexp.html">ReplaceRegexp</a>
for an explanation of regex flags.</TD>
<TD vAlign=top align="center">No</TD>
</TR>
</TABLE>

<H4>Examples:</H4>

Replace all occurances of "hello" with "world", ignoring case.

<BLOCKQUOTE><PRE>
&lt;tokenfilter&gt;
&lt;replaceregex pattern="hello" replace="world" flags="gi"/&gt;
&lt;/tokenfilter&gt;

</PRE></BLOCKQUOTE>

<p><b><em><a name="containsregex">ContainsRegex</a></em></b></p>
This filters strings that match regular expressions.
The filter may optionally replace the matched regular expression.
See
<a href="../OptionalTasks/replaceregexp.html">ReplaceRegexp</a>
for an explanation on regular expressions.
This filter may be used directly within a filterchain.

<TABLE cellSpacing=0 cellPadding=2 border=1>
<TR>
<TD vAlign=top><B>Attribute</B></TD>
<TD vAlign=top><B>Description</B></TD>
<TD vAlign=top align="center"><B>Required</B></TD>
</TR>
<TR>
<TD vAlign=top>pattern</TD>
<TD vAlign=top>The regular expression pattern to match in
the token.</TD>
<TD vAlign=top align="center">Yes</TD>
</TR>
<TR>
<TD vAlign=top>replace</TD>
<TD vAlign=top>The substitution pattern to replace the matched
regular expression. When omitted the orignal token is returned.
</TD>
<TD vAlign=top align="center">No</TD>
</TR>
<TR>
<TD vAlign=top>flags</TD>
<TD vAlign=top>See
<a href="../OptionalTasks/replaceregexp.html">ReplaceRegexp</a>
for an explanation of regex flags.</TD>
<TD vAlign=top align="center">No</TD>
</TR>
</TABLE>

<H4>Examples:</H4>

Filter lines that contain "hello" or "world", ignoring case.

<BLOCKQUOTE><PRE>
&lt;tokenfilter&gt;
&lt;containsregex pattern="(hello|world)" flags="i"/&gt;
&lt;/tokenfilter&gt;

</PRE></BLOCKQUOTE>

This example replaces lines like "SUITE(TestSuite, bits);" with
"void register_bits();" and removes other lines.

<BLOCKQUOTE><PRE>
&lt;tokenfilter&gt;
&lt;containsregex
pattern="^ *SUITE\(.*,\s*(.*)\s*\).*"
replace="void register_\1();"/&gt;
&lt;/tokenfilter&gt;
</PRE></BLOCKQUOTE>

<p><b><em><a name="trim">Trim</a></em></b></p>
This filter trims whitespace from the start and end of
tokens.
This filter may be used directly within a filterchain.
<p><b><em><a name="ignoreblank">IgnoreBlank</a></em></b></p>
This filter removes empty tokens.
This filter may be used directly within a filterchain.
<p><b><em><a name="filterdeletecharacters">DeleteCharacters</a></em></b></p>
This filter deletes specified characters from tokens.

<TABLE cellSpacing=0 cellPadding=2 border=1>
<TR>
<TD vAlign=top><B>Attribute</B></TD>
<TD vAlign=top><B>Description</B></TD>
<TD vAlign=top align="center"><B>Required</B></TD>
</TR>
<TR>
<TD vAlign=top>chars</TD>
<TD vAlign=top>The characters to delete. This attribute
is backslash enabled.</TD>
<TD vAlign=top align="center">Yes</TD>
</TR>
</TABLE>

<H4>Examples:</H4>

Delete tabs from lines, trim the lines and removes empty lines.

<BLOCKQUOTE><PRE>
&lt;tokenfilter&gt;
&lt;deletecharacters chars="\t"/&gt;
&lt;trim/&gt;
&lt;ignoreblank/&gt;
&lt;/tokenfilter&gt;

</PRE></BLOCKQUOTE>

<p><b><em><a name="scriptfilter">ScriptFilter</a></em></b></p>
This is an optional filter that executes a script in a
<a href="http://jakarta.apache.org/bsf" target="_top">Apache BSF</a>
supported language.</p>
See the <a href="../OptionalTasks/script.html">Script</a> task for
an explanation of scripts and dependencies.
</p>
<p>
The script is provided with an object <i>self</i> that has
getToken() and setToken(String) methods.
</p>

This filter may be used directly within a filterchain.<p>
<TABLE cellSpacing=0 cellPadding=2 border=1>
<TR>
<TD vAlign=top><B>Attribute</B></TD>
<TD vAlign=top><B>Description</B></TD>
<TD vAlign=top align="center"><B>Required</B></TD>
</TR>
<TR>
<TD vAlign=top>language</TD>
<TD vAlign=top> The programming language the script is written in.
Must be a supported Apache BSF language</TD>
<TD vAlign=top align="center">Yes</TD>
</TR>
<TR>
<TD vAlign=top>src</TD>
<TD vAlign=top>The location of the script as a file, if not inline
</TD>
<TD vAlign=top align="center">No</TD>
</TR>
<TR>
</TABLE>

<H4>Examples:</H4>

Convert to uppercase.
<BLOCKQUOTE><PRE>
&lt;tokenfilter&gt;
&lt;scriptfilter language="javascript"&gt;
self.setToken(self.getToken().toUpperCase());
&lt;/scriptfilter&gt;
&lt;/tokenfilter&gt;

</PRE></BLOCKQUOTE>

<H4>Custom tokenizers and string filters</H4>

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 &lt;typedef/&gt;. For
example a string filter that capitalizes words may be declared as:
<blockquote><pre>
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);
}
}
</pre></blockquote>

This may be used as follows:
<blockquote><pre>
&lt;typedef type="capitalize" classname="my.customant.Capitalize"
classpath="my.customant.path"/&gt;
&lt;copy file="input" tofile="output"&gt;
&lt;filterchain&gt;
&lt;tokenfilter&gt;
&lt;stringtokenizer/&gt;
&lt;capitalize/&gt;
&lt;/tokenfilter&gt;
&lt;/filterchain&gt;
&lt;/copy&gt;
</pre></blockquote>

<HR> <HR>


<P align=center>Copyright &copy; 2002-2003 Apache Software Foundation. All rights <P align=center>Copyright &copy; 2002-2003 Apache Software Foundation. All rights


+ 34
- 0
src/etc/testcases/filters/dynamicfilter.xml View File

@@ -0,0 +1,34 @@
<?xml version="1.0"?>
<project default="cleanup" basedir=".">

<target name="init">
<mkdir dir="result" />
</target>

<target name="cleanup">
<delete dir="result"/>
</target>

<target name="dynamicfilter">
<path id="test-classes">
<pathelement location="../../../../build/testcases" />
<pathelement path="${java.class.path}" />
</path>
<typedef
name="customfilter"
classname="org.apache.tools.ant.filters.DynamicFilterTest$CustomFilter">
<classpath refid="test-classes"/>
</typedef>
<concat destfile="result/input">
hello world
</concat>

<copy file="result/input" tofile="result/dynamicfilter">
<filterchain>
<customfilter replace="o" with="O"/>
</filterchain>
</copy>
</target>

</project>

+ 314
- 0
src/etc/testcases/filters/tokenfilter.xml View File

@@ -0,0 +1,314 @@
<?xml version="1.0"?>
<project default="cleanup" basedir=".">

<target name="init">
<mkdir dir="result" />
</target>

<target name="cleanup">
<delete dir="result"/>
</target>

<target name="tokenfilter">
<copy file="input/linecontains.test" tofile="result/file1">
<filterchain>
<tokenfilter/>
</filterchain>
</copy>
</target>

<target name="trimignore">
<concat destfile="result/input">
Hello
World
</concat>
<copy file="result/input" tofile="result/output" overwrite="yes">
<filterchain>
<tokenfilter delimoutput="-">
<trim/>
<ignoreblank/>
</tokenfilter>
</filterchain>
</copy>
<concat>
<filelist dir="." files="result/output"/>
</concat>
</target>

<target name="trimfile">
<concat destfile="result/trimfile">
This is the contents of the trimmed file.
This is the second line.
<filterchain>
<trim byline="no"/>
</filterchain>
</concat>
</target>

<target name="trimfilebyline">
<concat destfile="result/trimfilebyline">
This is the contents of the trimmed file.
This is the second line.
<filterchain>
<trim/>
<tokenfilter delimoutput="\n"/>
</filterchain>
</concat>
</target>

<target name="filterreplacestring">
<concat destfile="result/filterreplacestring">
This is foo bar
<filterchain>
<replacestring from="foo" to="the"/>
<replacestring from="bar" to="moon"/>
</filterchain>
</concat>
</target>

<target name="stringtokenizer">
<concat destfile="result/input">
This is a number
of words
</concat>
<copy file="result/input" tofile="result/output" overwrite="yes">
<filterchain>
<tokenfilter delimoutput="#">
<stringtokenizer/>
</tokenfilter>
</filterchain>
</copy>
<concat>
<filelist dir="." files="result/output"/>
</concat>
</target>

<target name="unixlineoutput">
<concat destfile="result/unixlineoutput">
This is a number
of words
<filterchain>
<tokenfilter delimoutput="\n">
<stringtokenizer/>
</tokenfilter>
</filterchain>
</concat>
</target>

<target name="doslineoutput">
<concat destfile="result/doslineoutput">
This is a number
of words
<filterchain>
<tokenfilter delimoutput="\r\n">
<stringtokenizer/>
</tokenfilter>
</filterchain>
</concat>
</target>

<target name="filetokenizer">
<concat destfile="result/input">
This is a number
of words
</concat>
<copy file="result/input" tofile="result/filetokenizer">
<filterchain>
<tokenfilter>
<filetokenizer/>
<trim/>
</tokenfilter>
</filterchain>
</copy>
</target>

<target name="replacestring">
<concat destfile="result/replacestring">
this is the sun
<filterchain>
<tokenfilter>
<replacestring from="sun" to="moon"/>
</tokenfilter>
</filterchain>
</concat>
</target>

<target name="containsstring">
<concat destfile="result/input">
this is a line contains foo
this line does not
</concat>
<copy file="result/input" tofile="result/containsstring">
<filterchain>
<tokenfilter>
<containsstring contains="foo"/>
</tokenfilter>
</filterchain>
</copy>
</target>

<!-- need to check for existance of regex -->
<target name="replaceregex">
<concat destfile="result/input">
hello Hello HELLO hello
cat Cat cat
Sun Sun Sun
WhiteSpace tab
This is a line with digits - 1234 -- there
</concat>
<copy file="result/input" tofile="result/replaceregex">
<filterchain>
<tokenfilter>
<replaceregex pattern="hello" replace="world" flags="gi"/>
<replaceregex pattern="cat" replace="dog" flags="g"/>
<replaceregex pattern="sun" replace="moon" flags="i"/>
<replaceregex pattern="WhiteSpace[ \t]+tab"
replace="found WhiteSpace"/>
<replaceregex pattern="This is a line with dig.* ([0-9]+).*"
replace="Found digits [\1]"/>
</tokenfilter>
</filterchain>
</copy>
</target>

<target name="filterreplaceregex">
<concat destfile="result/filterreplaceregex">
hello Hello HELLO hello
<filterchain>
<replaceregex pattern="hello" replace="world" flags="gi"/>
</filterchain>
</concat>
</target>

<!-- need to check for existance of regex -->
<target name="containsregex">
<concat destfile="result/input">
hello world
this is the moon
World here
</concat>
<copy file="result/input" tofile="result/containsregex">
<filterchain>
<tokenfilter>
<containsregex pattern="(hello|world)" flags="i"/>
</tokenfilter>
</filterchain>
</copy>
</target>

<target name="filtercontainsregex">
<concat destfile="result/filtercontainsregex">
hello world
this is the moon
World here
<filterchain>
<tokenfilter>
<containsregex pattern="(hello|world)" flags="i"/>
</tokenfilter>
</filterchain>
</concat>
</target>


<!-- need to check for existance of regex -->
<target name="containsregex2">
<concat destfile="result/input">
SUITE(TestSuite, bits);
here
</concat>
<copy file="result/input" tofile="result/containsregex2">
<filterchain>
<tokenfilter>
<containsregex
pattern="^ *SUITE\(.*,\s*(.*)\s*\).*"
replace="void register_\1();"/>
</tokenfilter>
</filterchain>
</copy>
</target>

<target name="deletecharacters">
<concat destfile="result/deletechars">
This is some ### s
some ****
<filterchain>
<tokenfilter>
<deletecharacters chars="#"/>
</tokenfilter>
<deletecharacters chars="*"/>
</filterchain>
</concat>
</target>

<target name="scriptfilter">
<concat destfile="result/input">
hello world
</concat>
<copy file="result/input" tofile="result/scriptfilter">
<filterchain>
<tokenfilter>
<scriptfilter language="javascript">
self.setToken(self.getToken().toUpperCase());
</scriptfilter>
</tokenfilter>
</filterchain>
</copy>
</target>

<target name="scriptfilter2">
<concat destfile="result/input">
hello moon
</concat>
<copy file="result/input" tofile="result/scriptfilter2">
<filterchain>
<scriptfilter language="javascript">
self.setToken(self.getToken().toUpperCase());
</scriptfilter>
</filterchain>
</copy>
</target>

<target name="customtokenfilter">
<path id="test-classes">
<pathelement location="../../../../build/testcases" />
<pathelement path="${java.class.path}" />
</path>


<typedef
name="capitalize"
classname="org.apache.tools.ant.filters.TokenFilterTest$Capitalize">
<classpath refid="test-classes"/>
</typedef>
<concat destfile="result/input">
hello world
</concat>

<copy file="result/input" tofile="result/custom">
<filterchain>
<tokenfilter>
<stringtokenizer/>
<capitalize/>
</tokenfilter>
</filterchain>
</copy>
</target>

<target name="hasscript">
<script language="beanshell">
i = 1;
</script>
</target>
<target name="hasregex">
<concat destfile="result/replaceregexp">
hello world
</concat>
<replaceregexp file="result/replaceregexp"
match="hello( )world"
replace="bye\1world"/>
</target>

</project>

+ 918
- 0
src/main/org/apache/tools/ant/filters/TokenFilter.java View File

@@ -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
* <http://www.apache.org/>.
*/
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 <code>null</code>.
*/
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
* <dl>
* <li>g - Regexp.REPLACE_ALL</li>
* <li>i - Regexp.MATCH_CASE_INSENSITIVE</li>
* <li>m - Regexp.MATCH_MULTILINE</li>
* <li>s - Regexp.MATCH_SINGLELINE</li>
* </dl>
*/
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;
}
}

+ 110
- 1
src/main/org/apache/tools/ant/types/FilterChain.java View File

@@ -54,7 +54,13 @@
package org.apache.tools.ant.types; package org.apache.tools.ant.types;


import java.util.Vector; 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.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.ClassConstants;
import org.apache.tools.ant.filters.EscapeUnicode; import org.apache.tools.ant.filters.EscapeUnicode;
import org.apache.tools.ant.filters.ExpandProperties; 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.StripLineComments;
import org.apache.tools.ant.filters.TabsToSpaces; import org.apache.tools.ant.filters.TabsToSpaces;
import org.apache.tools.ant.filters.TailFilter; 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. * FilterChain may contain a chained set of filter readers.
* *
* @author Magesh Umasankar * @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(); private Vector filterReaders = new Vector();


@@ -145,6 +157,69 @@ public final class FilterChain extends DataType implements Cloneable {
filterReaders.addElement(escapeUnicode); 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 * Makes this instance in effect a reference to another FilterChain
* instance. * instance.
@@ -171,4 +246,38 @@ public final class FilterChain extends DataType implements Cloneable {


super.setRefid(r); 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);
}

} }

+ 2
- 0
src/main/org/apache/tools/ant/types/defaults.properties View File

@@ -16,3 +16,5 @@ extensionSet=org.apache.tools.ant.taskdefs.optional.extension.ExtensionSet
extension=org.apache.tools.ant.taskdefs.optional.extension.ExtensionAdapter extension=org.apache.tools.ant.taskdefs.optional.extension.ExtensionAdapter
libfileset=org.apache.tools.ant.taskdefs.optional.extension.LibFileSet libfileset=org.apache.tools.ant.taskdefs.optional.extension.LibFileSet
selector=org.apache.tools.ant.types.selectors.SelectSelector selector=org.apache.tools.ant.types.selectors.SelectSelector
scriptfilter=org.apache.tools.ant.types.optional.ScriptFilter


+ 269
- 0
src/main/org/apache/tools/ant/types/optional/ScriptFilter.java View File

@@ -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
* <http://www.apache.org/>.
*/
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, "<ANT>", 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;
}
}

+ 167
- 0
src/testcases/org/apache/tools/ant/filters/DynamicFilterTest.java View File

@@ -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
* <http://www.apache.org/>.
*/

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;
}
};
}
}
}

+ 319
- 0
src/testcases/org/apache/tools/ant/filters/TokenFilterTest.java View File

@@ -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
* <http://www.apache.org/>.
*/

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);
}
}

}

Loading…
Cancel
Save