2. Adapt FixCRLF the task to use the filter (single set of fixcrlf code) 3. Implement ChainableReader in FixCRLF so that the existing taskdef will be recognized as a nested element in a filterchain. PR: 33155 Submitted by: Kevin Greiner git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277787 13f79535-47bb-0310-9956-ffa450edef68master
@@ -132,6 +132,8 @@ Other changes: | |||||
* New condition <parsersupports> which can look for XML parser feature or | * New condition <parsersupports> which can look for XML parser feature or | ||||
property support in the parser Ant is using. | property support in the parser Ant is using. | ||||
* fixcrlf can be used in a filterchain. | |||||
Changes from Ant 1.6.2 to current Ant 1.6 CVS version | Changes from Ant 1.6.2 to current Ant 1.6 CVS version | ||||
===================================================== | ===================================================== | ||||
@@ -9,170 +9,202 @@ | |||||
<h2><a name="fixcrlf">FixCRLF</a></h2> | <h2><a name="fixcrlf">FixCRLF</a></h2> | ||||
<h3>Description</h3> | <h3>Description</h3> | ||||
<p> | |||||
Adjusts a text file to local conventions. | |||||
</p> | |||||
<p> | |||||
Adjusts a text file to local conventions. | |||||
</p> | |||||
<p> | |||||
The set of files to be adjusted can be refined with the | |||||
<i>includes</i>, <i>includesfile</i>, <i>excludes</i>, | |||||
<i>excludesfile</i> and <i>defaultexcludes</i> | |||||
attributes. Patterns provided through the <i>includes</i> or | |||||
<i>includesfile</i> attributes specify files to be | |||||
included. Patterns provided through the <i>exclude</i> or | |||||
<i>excludesfile</i> attribute specify files to be | |||||
excluded. Additionally, default exclusions can be specified with | |||||
the <i>defaultexcludes</i> attribute. See the section on <a | |||||
href="../dirtasks.html#directorybasedtasks">directory based | |||||
tasks</a>, for details of file inclusion/exclusion patterns | |||||
and their usage. | |||||
</p> | |||||
<p> | |||||
The set of files to be adjusted can be refined with the | |||||
<i>includes</i>, <i>includesfile</i>, <i>excludes</i>, | |||||
<i>excludesfile</i> and <i>defaultexcludes</i> | |||||
attributes. Patterns provided through the <i>includes</i> or | |||||
<i>includesfile</i> attributes specify files to be | |||||
included. Patterns provided through the <i>exclude</i> or | |||||
<i>excludesfile</i> attribute specify files to be | |||||
excluded. Additionally, default exclusions can be specified with | |||||
the <i>defaultexcludes</i> attribute. See the section on <a | |||||
href="../dirtasks.html#directorybasedtasks">directory-based | |||||
tasks</a>, for details of file inclusion/exclusion patterns | |||||
and their usage. | |||||
</p> | |||||
<p>This task forms an implicit <a href="../CoreTypes/fileset.html">FileSet</a> and | |||||
supports all attributes of <code><fileset></code> | |||||
(<code>dir</code> becomes <code>srcdir</code>) as well as the nested | |||||
<code><include></code>, <code><exclude></code> and | |||||
<code><patternset></code> elements.</p> | |||||
<p> | |||||
This task forms an implicit | |||||
<a href="../CoreTypes/fileset.html">FileSet</a> and | |||||
supports all attributes of <code><fileset></code> | |||||
(<code>dir</code> becomes <code>srcdir</code>) as well as the nested | |||||
<code><include></code>, <code><exclude></code> and | |||||
<code><patternset></code> elements. | |||||
</p> | |||||
<p> | |||||
The output file is only written if it is a new file, or if it | |||||
differs from the existing file. This prevents spurious | |||||
rebuilds based on unchanged files which have been regenerated | |||||
by this task. | |||||
</p> | |||||
<p> | |||||
The output file is only written if it is a new file, or if it | |||||
differs from the existing file. This prevents spurious | |||||
rebuilds based on unchanged files which have been regenerated | |||||
by this task. | |||||
</p> | |||||
<p> | |||||
Since <b>Ant 1.7</b>, this task can be used in a | |||||
<a href="../CoreTypes/filterchain.html">filterchain</a>. | |||||
</p> | |||||
<h3>Parameters</h3> | <h3>Parameters</h3> | ||||
<table border="1" cellpadding="2" cellspacing="0"> | <table border="1" cellpadding="2" cellspacing="0"> | ||||
<tr> | <tr> | ||||
<td valign="top"><b>Attribute</b></td> | |||||
<td valign="top"><b>Description</b></td> | |||||
<td align="center" valign="top"><b>Required</b></td> | |||||
<td valign="center" rowspan="2"><b>Attribute</b></td> | |||||
<td valign="center" rowspan="2"><b>Description</b></td> | |||||
<td align="center" valign="top" colspan="2"><b>Required</b></td> | |||||
</tr> | |||||
<tr> | |||||
<td valign="center"><b>As Task</b></td> | |||||
<td valign="center"><b>As Filter</b></td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">srcDir</td> | <td valign="top">srcDir</td> | ||||
<td valign="top">Where to find the files to be fixed up.</td> | <td valign="top">Where to find the files to be fixed up.</td> | ||||
<td valign="top" align="center">Either file or srcDir</td> | |||||
<td valign="top" align="center" rowspan="2">One of these</td> | |||||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">file</td> | <td valign="top">file</td> | ||||
<td valign="top">Name of a single file to fix. Since Ant1.7</td> | |||||
<td valign="top" align="center">Either file or srcDir</td> | |||||
<td valign="top">Name of a single file to fix. <b>Since Ant 1.7</b></td> | |||||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">destDir</td> | <td valign="top">destDir</td> | ||||
<td valign="top">Where to place the corrected files. Defaults to | <td valign="top">Where to place the corrected files. Defaults to | ||||
srcDir (replacing the original file)</td> | |||||
srcDir (replacing the original file).</td> | |||||
<td valign="top" align="center">No</td> | <td valign="top" align="center">No</td> | ||||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">includes</td> | <td valign="top">includes</td> | ||||
<td valign="top">comma- or space-separated list of patterns of files that must be | <td valign="top">comma- or space-separated list of patterns of files that must be | ||||
included. All files are included when omitted.</td> | included. All files are included when omitted.</td> | ||||
<td valign="top" align="center">No</td> | <td valign="top" align="center">No</td> | ||||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">includesfile</td> | <td valign="top">includesfile</td> | ||||
<td valign="top">the name of a file. Each line of this file is | <td valign="top">the name of a file. Each line of this file is | ||||
taken to be an include pattern</td> | |||||
taken to be an include pattern.</td> | |||||
<td valign="top" align="center">No</td> | <td valign="top" align="center">No</td> | ||||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">excludes</td> | <td valign="top">excludes</td> | ||||
<td valign="top">comma- or space-separated list of patterns of files that must be | <td valign="top">comma- or space-separated list of patterns of files that must be | ||||
excluded. No files (except default excludes) are excluded when omitted.</td> | excluded. No files (except default excludes) are excluded when omitted.</td> | ||||
<td valign="top" align="center">No</td> | <td valign="top" align="center">No</td> | ||||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">excludesfile</td> | <td valign="top">excludesfile</td> | ||||
<td valign="top">the name of a file. Each line of this file is | <td valign="top">the name of a file. Each line of this file is | ||||
taken to be an exclude pattern</td> | |||||
taken to be an exclude pattern.</td> | |||||
<td valign="top" align="center">No</td> | <td valign="top" align="center">No</td> | ||||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">defaultexcludes</td> | <td valign="top">defaultexcludes</td> | ||||
<td valign="top">indicates whether default excludes should be used or not | <td valign="top">indicates whether default excludes should be used or not | ||||
("yes"/"no"). Default excludes are used when omitted.</td> | |||||
("yes"/"no"). Default excludes are used when omitted. | |||||
</td> | |||||
<td valign="top" align="center">No</td> | <td valign="top" align="center">No</td> | ||||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||||
</tr> | |||||
<tr> | |||||
<td valign="top">encoding</td> | |||||
<td valign="top">The encoding of the files.</td> | |||||
<td align="center">No; defaults to default JVM encoding.</td> | |||||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||||
</tr> | |||||
<tr> | |||||
<td valign="top">preservelastmodified</td> | |||||
<td valign="top">Whether to preserve the last modified | |||||
date of source files. <b>Since Ant 1.6.3</b></td> | |||||
<td align="center">No; default is <i>false</i></td> | |||||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||||
</tr> | |||||
<tr> | |||||
<td valign="top">eol</td> | |||||
<td valign="top"> | |||||
Specifies how end-of-line (EOL) characters are to be | |||||
handled. The EOL characters are CR, LF and the pair CRLF. | |||||
Valid values for this property are: | |||||
<ul> | |||||
<li>asis: leave EOL characters alone</li> | |||||
<li>cr: convert all EOLs to a single CR</li> | |||||
<li>lf: convert all EOLs to a single LF</li> | |||||
<li>crlf: convert all EOLs to the pair CRLF</li> | |||||
<li>mac: convert all EOLs to a single CR</li> | |||||
<li>unix: convert all EOLs to a single LF</li> | |||||
<li>dos: convert all EOLs to the pair CRLF</li> | |||||
</ul> | |||||
Default is based on the platform on which you are running | |||||
this task. For Unix platforms, the default is "lf". | |||||
For DOS based systems (including Windows), the default is | |||||
"crlf". For Mac OS, the default is "cr". | |||||
<p> | |||||
This is the preferred method for specifying EOL. The | |||||
"<i><b>cr</b></i>" attribute (see below) is | |||||
now deprecated. | |||||
</p> | |||||
<p> | |||||
<i>N.B.</i>: One special case is recognized. The three | |||||
characters CR-CR-LF are regarded as a single EOL. | |||||
Unless this property is specified as "asis", | |||||
this sequence will be converted into the specified EOL | |||||
type. | |||||
</p> | |||||
</td> | |||||
<td valign="top" align="center" colspan="2">No</td> | |||||
</tr> | |||||
<tr> | |||||
<td valign="top">cr</td> | |||||
<td valign="top"> | |||||
<i><b>Deprecated.</b></i> Specifies how CR characters are | |||||
to be handled at end-of-line (EOL). Valid values for this | |||||
property are: | |||||
<ul> | |||||
<li>asis: leave EOL characters alone.</li> | |||||
<li> | |||||
add: add a CR before any single LF characters. The | |||||
intent is to convert all EOLs to the pair CRLF. | |||||
</li> | |||||
<li> | |||||
remove: remove all CRs from the file. The intent is | |||||
to convert all EOLs to a single LF. | |||||
</li> | |||||
</ul> | |||||
Default is based on the platform on which you are running | |||||
this task. For Unix platforms, the default is "remove". | |||||
For DOS based systems (including Windows), the default is | |||||
"add". | |||||
<p> | |||||
<i>N.B.</i>: One special case is recognized. The three | |||||
characters CR-CR-LF are regarded as a single EOL. | |||||
Unless this property is specified as "asis", | |||||
this sequence will be converted into the specified EOL | |||||
type. | |||||
</p> | |||||
</td> | |||||
<td valign="top" align="center" colspan="2">No</td> | |||||
</tr> | |||||
<tr> | |||||
<td valign="top">javafiles</td> | |||||
<td valign="top"> | |||||
Used only in association with the | |||||
"<i><b>tab</b></i>" attribute (see below), this | |||||
boolean attribute indicates whether the fileset is a set | |||||
of java source files | |||||
("yes"/"no"). Defaults to | |||||
"no". See notes in section on "tab". | |||||
</td> | |||||
<td valign="top" align="center" colspan="2">No</td> | |||||
</tr> | </tr> | ||||
<tr> | |||||
<td valign="top">eol</td> | |||||
<td valign="top"> | |||||
Specifies how end-of-line (EOL) characters are to be | |||||
handled. The EOL characters are CR, LF and the pair CRLF. | |||||
Valid values for this property are: | |||||
<ul> | |||||
<li>asis: leave EOL characters alone</li> | |||||
<li>cr: convert all EOLs to a single CR</li> | |||||
<li>lf: convert all EOLs to a single LF</li> | |||||
<li>crlf: convert all EOLs to the pair CRLF</li> | |||||
<li>mac: convert all EOLs to a single CR</li> | |||||
<li>unix: convert all EOLs to a single LF</li> | |||||
<li>dos: convert all EOLs to the pair CRLF</li> | |||||
</ul> | |||||
Default is based on the platform on which you are running | |||||
this task. For Unix platforms, the default is "lf". | |||||
For DOS based systems (including Windows), the default is | |||||
"crlf". For Mac OS, the default is "cr". | |||||
<p> | |||||
This is the preferred method for specifying EOL. The | |||||
"<i><b>cr</b></i>" attribute (see below) is | |||||
now deprecated. | |||||
</p> | |||||
<p> | |||||
<i>N.B.</i>: One special case is recognized. The three | |||||
characters CR-CR-LF are regarded as a single EOL. | |||||
Unless this property is specified as "asis", | |||||
this sequence will be converted into the specified EOL | |||||
type. | |||||
</p> | |||||
</td> | |||||
<td valign="top" align="center">No</td> | |||||
</tr> | |||||
<tr> | |||||
<td valign="top">cr</td> | |||||
<td valign="top"> | |||||
<i><b>Deprecated.</b></i> Specifies how CR characters are | |||||
to be handled at end-of-line (EOL). Valid values for this | |||||
property are: | |||||
<ul> | |||||
<li>asis: leave EOL characters alone.</li> | |||||
<li> | |||||
add: add a CR before any single LF characters. The | |||||
intent is to convert all EOLs to the pair CRLF. | |||||
</li> | |||||
<li> | |||||
remove: remove all CRs from the file. The intent is | |||||
to convert all EOLs to a single LF. | |||||
</li> | |||||
</ul> | |||||
Default is based on the platform on which you are running | |||||
this task. For Unix platforms, the default is "remove". | |||||
For DOS based systems (including Windows), the default is | |||||
"add". | |||||
<p> | |||||
<i>N.B.</i>: One special case is recognized. The three | |||||
characters CR-CR-LF are regarded as a single EOL. | |||||
Unless this property is specified as "asis", | |||||
this sequence will be converted into the specified EOL | |||||
type. | |||||
</p> | |||||
</td> | |||||
<td valign="top" align="center">No</td> | |||||
</tr> | |||||
<tr> | |||||
<td valign="top">javafiles</td> | |||||
<td valign="top"> | |||||
Used only in association with the | |||||
"<i><b>tab</b></i>" attribute (see below), this | |||||
boolean attribute indicates whether the fileset is a set | |||||
of java source files | |||||
("yes"/"no"). Defaults to | |||||
"no". See notes in section on "tab". | |||||
</td> | |||||
<td valign="top" align="center">No</td> | |||||
</tr> | |||||
<tr> | |||||
<tr> | |||||
<td valign="top">tab</td> | <td valign="top">tab</td> | ||||
<td valign="top">Specifies how tab characters are to be handled. Valid | <td valign="top">Specifies how tab characters are to be handled. Valid | ||||
values for this property are: | values for this property are: | ||||
@@ -182,28 +214,28 @@ supports all attributes of <code><fileset></code> | |||||
<li>remove: convert tabs to spaces</li> | <li>remove: convert tabs to spaces</li> | ||||
</ul> | </ul> | ||||
Default for this parameter is "asis". | Default for this parameter is "asis". | ||||
<p> | |||||
<i>N.B.</i>: When the attribute | |||||
"<i><b>javafiles</b></i>" (see above) is | |||||
"true", literal TAB characters occurring | |||||
within Java string or character constants are never | |||||
modified. This functionality also requires the | |||||
recognition of Java-style comments. | |||||
</p> | |||||
<p> | <p> | ||||
<i>N.B.</i>: There is an incompatibility between this | |||||
and the previous version in the handling of white | |||||
space at the end of lines. This version does | |||||
<i><b>not</b></i> remove trailing whitespace on lines. | |||||
</p> | |||||
</td> | |||||
<td valign="top" align="center">No</td> | |||||
<i>N.B.</i>: When the attribute | |||||
"<i><b>javafiles</b></i>" (see above) is | |||||
"true", literal TAB characters occurring | |||||
within Java string or character constants are never | |||||
modified. This functionality also requires the | |||||
recognition of Java-style comments. | |||||
</p> | |||||
<p> | |||||
<i>N.B.</i>: There is an incompatibility between this | |||||
and the previous version in the handling of white | |||||
space at the end of lines. This version does | |||||
<i><b>not</b></i> remove trailing whitespace on lines. | |||||
</p> | |||||
</td> | |||||
<td valign="top" align="center" colspan="2">No</td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">tablength</td> | <td valign="top">tablength</td> | ||||
<td valign="top">TAB character interval. Valid values are between | <td valign="top">TAB character interval. Valid values are between | ||||
2 and 80 inclusive. The default for this parameter is 8.</td> | |||||
<td valign="top" align="center">No</td> | |||||
2 and 80 inclusive. The default for this parameter is 8.</td> | |||||
<td valign="top" align="center" colspan="2">No</td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">eof</td> | <td valign="top">eof</td> | ||||
@@ -218,76 +250,52 @@ supports all attributes of <code><fileset></code> | |||||
For Unix platforms, the default is remove. For DOS based systems | For Unix platforms, the default is remove. For DOS based systems | ||||
(including Windows), the default is asis. | (including Windows), the default is asis. | ||||
</td> | </td> | ||||
<td valign="top" align="center">No</td> | |||||
</tr> | |||||
<tr> | |||||
<td valign="top">encoding</td> | |||||
<td valign="top">The encoding of the files</td> | |||||
<td align="center">No - defaults to default JVM encoding</td> | |||||
<td valign="top" align="center" colspan="2">No</td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">fixlast</td> | <td valign="top">fixlast</td> | ||||
<td valign="top">Whether to add a missing EOL to the last line | <td valign="top">Whether to add a missing EOL to the last line | ||||
of a processed file. (Since ant 1.6.1)</td> | |||||
<td align="center">No - default is <i>true</i></td> | |||||
</tr> | |||||
<tr> | |||||
<td valign="top">preservelastmodified</td> | |||||
<td valign="top">Whether to preserve the last modified | |||||
date of source files. <b>Since ant 1.6.3</b></td> | |||||
<td align="center">No; default is <i>false</i></td> | |||||
of a processed file. <b>Since Ant 1.6.1</b></td> | |||||
<td align="center" colspan="2">No; default is <i>true</i></td> | |||||
</tr> | </tr> | ||||
</table> | </table> | ||||
<h3>Examples</h3> | <h3>Examples</h3> | ||||
<pre> <fixcrlf srcdir="${src}" | |||||
eol="lf" | |||||
eof="remove" | |||||
includes="**/*.sh" | |||||
/></pre> | |||||
<pre><fixcrlf srcdir="${src}" includes="**/*.sh" | |||||
eol="lf" eof="remove" /></pre> | |||||
<p>Replaces EOLs with LF characters and removes eof characters from | <p>Replaces EOLs with LF characters and removes eof characters from | ||||
the shell scripts. Tabs and spaces are left as is.</p> | |||||
<pre> <fixcrlf srcdir="${src}" | |||||
eol="crlf" | |||||
includes="**/*.bat" | |||||
/></pre> | |||||
the shell scripts. Tabs and spaces are left as is.</p> | |||||
<pre><fixcrlf srcdir="${src}" | |||||
includes="**/*.bat" eol="crlf" /></pre> | |||||
<p>Replaces all EOLs with cr-lf pairs in the batch files. | <p>Replaces all EOLs with cr-lf pairs in the batch files. | ||||
Tabs and spaces are left as is. | |||||
EOF characters are left alone if run on | |||||
DOS systems, and are removed if run on Unix systems.</p> | |||||
<pre> <fixcrlf srcdir="${src}" | |||||
tab="add" | |||||
includes="**/Makefile" | |||||
/></pre> | |||||
Tabs and spaces are left as is. | |||||
EOF characters are left alone if run on | |||||
DOS systems, and are removed if run on Unix systems.</p> | |||||
<pre><fixcrlf srcdir="${src}" | |||||
includes="**/Makefile" tab="add" /></pre> | |||||
<p>Sets EOLs according to local OS conventions, and | <p>Sets EOLs according to local OS conventions, and | ||||
converts sequences of spaces and tabs to the minimal set of spaces and | |||||
tabs which will maintain spacing within the line. Tabs are | |||||
set at 8 character intervals. EOF characters are left alone if | |||||
run on DOS systems, and are removed if run on Unix systems. | |||||
Many versions of make require tabs prior to commands.</p> | |||||
<pre> <fixcrlf srcdir="${src}" | |||||
tab="remove" | |||||
tablength="3" | |||||
eol="lf" | |||||
javafiles="yes" | |||||
includes="**/*.java" | |||||
/></pre> | |||||
<p> | |||||
Converts all EOLs in the included java source files to a | |||||
single LF. Replace all TAB characters except those in string | |||||
or character constants with spaces, assuming a tab width of 3. | |||||
If run on a unix system, any CTRL-Z EOF characters at the end | |||||
of the file are removed. On DOS/Windows, any such EOF | |||||
characters will be left untouched. | |||||
</p> | |||||
<pre> <fixcrlf srcdir="${src}" | |||||
tab="remove" | |||||
includes="**/README*" | |||||
/></pre> | |||||
converts sequences of spaces and tabs to the minimal set of spaces and | |||||
tabs which will maintain spacing within the line. Tabs are | |||||
set at 8 character intervals. EOF characters are left alone if | |||||
run on DOS systems, and are removed if run on Unix systems. | |||||
Many versions of make require tabs prior to commands.</p> | |||||
<pre><fixcrlf srcdir="${src}" includes="**/*.java" | |||||
tab="remove" tablength="3" | |||||
eol="lf" javafiles="yes" /></pre> | |||||
<p> | |||||
Converts all EOLs in the included java source files to a | |||||
single LF. Replace all TAB characters except those in string | |||||
or character constants with spaces, assuming a tab width of 3. | |||||
If run on a unix system, any CTRL-Z EOF characters at the end | |||||
of the file are removed. On DOS/Windows, any such EOF | |||||
characters will be left untouched. | |||||
</p> | |||||
<pre><fixcrlf srcdir="${src}" | |||||
includes="**/README*" tab="remove" /></pre> | |||||
<p>Sets EOLs according to local OS conventions, and | <p>Sets EOLs according to local OS conventions, and | ||||
converts all tabs to spaces, assuming a tab width of 8. | |||||
EOF characters are left alone if run on | |||||
DOS systems, and are removed if run on Unix systems. | |||||
You never know what editor a user will use to browse README's.</p> | |||||
converts all tabs to spaces, assuming a tab width of 8. | |||||
EOF characters are left alone if run on | |||||
DOS systems, and are removed if run on Unix systems. | |||||
You never know what editor a user will use to browse READMEs.</p> | |||||
<hr> | <hr> | ||||
<p align="center">Copyright © 2000-2005 The Apache Software Foundation. All rights | <p align="center">Copyright © 2000-2005 The Apache Software Foundation. All rights | ||||
Reserved.</p> | Reserved.</p> | ||||
@@ -194,4 +194,100 @@ | |||||
<fail unless="fs" /> | <fail unless="fs" /> | ||||
</target> | </target> | ||||
<macrodef name="testjunk"> | |||||
<attribute name="n" /> | |||||
<sequential> | |||||
<fail> | |||||
<condition> | |||||
<not> | |||||
<filesmatch file1="result/Junk@{n}.java" | |||||
file2="expected/Junk@{n}.java" /> | |||||
</not> | |||||
</condition> | |||||
</fail> | |||||
</sequential> | |||||
</macrodef> | |||||
<target name="testFilter1" depends="init"> | |||||
<copy file="input/Junk1.java" todir="result" overwrite="true"> | |||||
<filterchain> | |||||
<fixcrlf javafiles="true" tab="add" | |||||
eol="crlf" eof="asis" /> | |||||
</filterchain> | |||||
</copy> | |||||
<testjunk n="1" /> | |||||
</target> | |||||
<target name="testFilter2" depends="init"> | |||||
<copy file="input/Junk2.java" todir="result" overwrite="true"> | |||||
<filterchain> | |||||
<fixcrlf javafiles="true" tab="add" cr="add" eol="crlf" eof="asis" /> | |||||
</filterchain> | |||||
</copy> | |||||
<testjunk n="2" /> | |||||
</target> | |||||
<target name="testFilter3" depends="init"> | |||||
<copy file="input/Junk3.java" todir="result" overwrite="true"> | |||||
<filterchain> | |||||
<fixcrlf javafiles="true" tab="remove" eol="lf" eof="asis" /> | |||||
</filterchain> | |||||
</copy> | |||||
<testjunk n="3" /> | |||||
</target> | |||||
<target name="testFilter4" depends="init"> | |||||
<copy file="input/Junk4.java" todir="result" overwrite="true"> | |||||
<filterchain> | |||||
<fixcrlf javafiles="true" tab="remove" eol="lf" eof="asis" /> | |||||
</filterchain> | |||||
</copy> | |||||
<testjunk n="4" /> | |||||
</target> | |||||
<target name="testFilter5" depends="init"> | |||||
<copy file="input/Junk5.java" todir="result" overwrite="true"> | |||||
<filterchain> | |||||
<fixcrlf tab="remove" eol="lf" eof="asis" /> | |||||
</filterchain> | |||||
</copy> | |||||
<testjunk n="5" /> | |||||
</target> | |||||
<target name="testFilter6" depends="init"> | |||||
<copy file="input/Junk6.java" todir="result" overwrite="true"> | |||||
<filterchain> | |||||
<fixcrlf tab="add" cr="remove" eol="crlf" eof="asis" /> | |||||
</filterchain> | |||||
</copy> | |||||
<testjunk n="6" /> | |||||
</target> | |||||
<target name="testFilter7" depends="init"> | |||||
<copy file="input/Junk7.java" todir="result" overwrite="true"> | |||||
<filterchain> | |||||
<fixcrlf tab="add" cr="add" eof="asis" /> | |||||
</filterchain> | |||||
</copy> | |||||
<testjunk n="7" /> | |||||
</target> | |||||
<target name="testFilter8" depends="init"> | |||||
<copy file="input/Junk8.java" todir="result" overwrite="true"> | |||||
<filterchain> | |||||
<fixcrlf javafiles="true" tab="add" cr="add" eof="add" /> | |||||
</filterchain> | |||||
</copy> | |||||
<testjunk n="8" /> | |||||
</target> | |||||
<target name="testFilter9" depends="init"> | |||||
<copy file="input/Junk9.java" todir="result" overwrite="true"> | |||||
<filterchain> | |||||
<fixcrlf javafiles="true" tab="remove" cr="remove" eof="remove" /> | |||||
</filterchain> | |||||
</copy> | |||||
<testjunk n="9" /> | |||||
</target> | |||||
</project> | </project> |
@@ -0,0 +1,899 @@ | |||||
/* | |||||
* Copyright 2005 The Apache Software Foundation | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
* | |||||
*/ | |||||
package org.apache.tools.ant.filters; | |||||
import java.io.BufferedReader; | |||||
import java.io.CharArrayWriter; | |||||
import java.io.IOException; | |||||
import java.io.InvalidObjectException; | |||||
import java.io.ObjectStreamException; | |||||
import java.io.Reader; | |||||
import java.io.Writer; | |||||
import java.util.NoSuchElementException; | |||||
import org.apache.tools.ant.taskdefs.condition.Os; | |||||
import org.apache.tools.ant.types.EnumeratedAttribute; | |||||
/** | |||||
* Converts text to local OS formatting conventions, as | |||||
* well as repair text damaged by misconfigured or misguided editors or | |||||
* file transfer programs. | |||||
* <p> | |||||
* This filter can take the following arguments: | |||||
* <ul> | |||||
* <li>eof | |||||
* <li>eol | |||||
* <li>fixlast | |||||
* <li>javafiles | |||||
* <li>tab | |||||
* <li>tablength | |||||
* </ul> | |||||
* None of which are required. | |||||
* <p> | |||||
* This version generalises the handling of EOL characters, and allows | |||||
* for CR-only line endings (which I suspect is the standard on Macs.) | |||||
* Tab handling has also been generalised to accommodate any tabwidth | |||||
* from 2 to 80, inclusive. Importantly, it can leave untouched any | |||||
* literal TAB characters embedded within Java string or character constants. | |||||
* <p> | |||||
* <em>Caution:</em> run with care on carefully formatted files. This may | |||||
* sound obvious, but if you don't specify asis, presume that your files are | |||||
* going to be modified. If "tabs" is "add" or "remove", whitespace | |||||
* characters may be added or removed as necessary. Similarly, for EOL's - | |||||
* eol="asis" actually means convert to your native O/S EOL convention while | |||||
* eol="crlf" or cr="add" can result in CR characters being removed in one | |||||
* special case accommodated, i.e., CRCRLF is regarded as a single EOL to | |||||
* handle cases where other programs have converted CRLF into CRCRLF. | |||||
* | |||||
* <P>Example: | |||||
* <pre><<fixcrlf tab="add" eol="crlf" eof="asis"/></pre> | |||||
* | |||||
* Or: | |||||
* | |||||
* <pre><filterreader classname="org.apache.tools.ant.filters.FixCrLfFilter"> | |||||
* <param eol="crlf" tab="asis"/> | |||||
* </filterreader></pre> | |||||
* | |||||
*/ | |||||
public final class FixCrLfFilter | |||||
extends BaseParamFilterReader | |||||
implements ChainableReader { | |||||
private static final char CTRLZ = '\u001A'; | |||||
private int tabLength = 8; | |||||
private CrLf eol; | |||||
private AddAsisRemove ctrlz; | |||||
private AddAsisRemove tabs; | |||||
private boolean javafiles = false; | |||||
private boolean fixlast = true; | |||||
/** | |||||
* Constructor for "dummy" instances. | |||||
* | |||||
* @see BaseFilterReader#BaseFilterReader() | |||||
*/ | |||||
public FixCrLfFilter() { | |||||
super(); | |||||
} | |||||
/** | |||||
* Create a new filtered reader. | |||||
* | |||||
* @param in A Reader object providing the underlying stream. | |||||
* Must not be <code>null</code>. | |||||
*/ | |||||
public FixCrLfFilter(final Reader in) throws IOException { | |||||
super(in); | |||||
} | |||||
// Instance initializer: Executes just after the super() call in this class's constructor. | |||||
{ | |||||
tabs = AddAsisRemove.ASIS; | |||||
if (Os.isFamily("mac")) { | |||||
ctrlz = AddAsisRemove.REMOVE; | |||||
setEol(CrLf.MAC); | |||||
} else if (Os.isFamily("dos")) { | |||||
ctrlz = AddAsisRemove.ASIS; | |||||
setEol(CrLf.DOS); | |||||
} else { | |||||
ctrlz = AddAsisRemove.REMOVE; | |||||
setEol(CrLf.UNIX); | |||||
} | |||||
} | |||||
/** | |||||
* Create a new FixCrLfFilter using the passed in | |||||
* Reader for instantiation. | |||||
* | |||||
* @param rdr A Reader object providing the underlying stream. | |||||
* Must not be <code>null</code>. | |||||
* | |||||
* @return a new filter based on this configuration, but filtering | |||||
* the specified reader. | |||||
*/ | |||||
public final Reader chain(final Reader rdr) { | |||||
try { | |||||
FixCrLfFilter newFilter = new FixCrLfFilter(rdr); | |||||
newFilter.setJavafiles(getJavafiles()); | |||||
newFilter.setEol(getEol()); | |||||
newFilter.setTab(getTab()); | |||||
newFilter.setTablength(getTablength()); | |||||
newFilter.setEof(getEof()); | |||||
newFilter.setFixlast(getFixlast()); | |||||
newFilter.initInternalFilters(); | |||||
return newFilter; | |||||
} catch (IOException e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
/** | |||||
* Get how DOS EOF (control-z) characters are being handled. | |||||
* | |||||
* @return values: | |||||
* <ul> | |||||
* <li>add: ensure that there is an eof at the end of the file | |||||
* <li>asis: leave eof characters alone | |||||
* <li>remove: remove any eof character found at the end | |||||
* </ul> | |||||
*/ | |||||
public AddAsisRemove getEof() { | |||||
// Return copy so that the call must call setEof() to change the state of fixCRLF | |||||
return ctrlz.newInstance(); | |||||
} | |||||
/** | |||||
* Get how EndOfLine characters are being handled. | |||||
* | |||||
* @return values: | |||||
* <ul> | |||||
* <li>asis: convert line endings to your O/S convention | |||||
* <li>cr: convert line endings to CR | |||||
* <li>lf: convert line endings to LF | |||||
* <li>crlf: convert line endings to CRLF | |||||
* </ul> | |||||
*/ | |||||
public CrLf getEol() { | |||||
// Return copy so that the call must call setEol() to change the state of fixCRLF | |||||
return eol.newInstance(); | |||||
} | |||||
/** | |||||
* Get whether a missing EOL be added to the final line of the stream. | |||||
* | |||||
* @return true if a filtered file will always end with an EOL | |||||
*/ | |||||
public boolean getFixlast() { | |||||
return fixlast; | |||||
} | |||||
/** | |||||
* Get whether the stream is to be treated as though it contains Java source. | |||||
* <P> | |||||
* This attribute is only used in assocation with the | |||||
* "<i><b>tab</b></i>" attribute. Tabs found in Java literals | |||||
* are protected from changes by this filter. | |||||
* | |||||
* @return true if whitespace in Java character and string literals is | |||||
* ignored. | |||||
*/ | |||||
public boolean getJavafiles() { | |||||
return javafiles; | |||||
} | |||||
/** | |||||
* Return how tab characters are being handled. | |||||
* | |||||
* @return values: | |||||
* <ul> | |||||
* <li>add: convert sequences of spaces which span a tab stop to tabs | |||||
* <li>asis: leave tab and space characters alone | |||||
* <li>remove: convert tabs to spaces | |||||
* </ul> | |||||
*/ | |||||
public AddAsisRemove getTab() { | |||||
// Return copy so that the caller must call setTab() to change the state of fixCRLF. | |||||
return tabs.newInstance(); | |||||
} | |||||
/** | |||||
* Get the tab length to use. | |||||
* | |||||
* @return the length of tab in spaces | |||||
*/ | |||||
public int getTablength(){ | |||||
return tabLength; | |||||
} | |||||
private static String calculateEolString(CrLf eol) { | |||||
// Calculate the EOL string per the current config | |||||
if (eol == CrLf.ASIS) { | |||||
return System.getProperty("line.separator"); | |||||
} | |||||
if (eol == CrLf.CR || eol == CrLf.MAC) { | |||||
return "\r"; | |||||
} | |||||
if (eol == CrLf.CRLF || eol == CrLf.DOS) { | |||||
return "\r\n"; | |||||
} | |||||
//assume (eol == CrLf.LF || eol == CrLf.UNIX) | |||||
return "\n"; | |||||
} | |||||
/** | |||||
* Wrap the input stream with the internal filters necessary to perform | |||||
* the configuration settings. | |||||
*/ | |||||
private void initInternalFilters() { | |||||
// If I'm removing an EOF character, do so first so that the other | |||||
// filters don't see that character. | |||||
in = (ctrlz == AddAsisRemove.REMOVE) ? new RemoveEofFilter(in) : in; | |||||
// Change all EOL characters to match the calculated EOL string. If | |||||
// configured to do so, append a trailing EOL so that the file ends on | |||||
// a EOL. | |||||
in = new NormalizeEolFilter(in, calculateEolString(eol), getFixlast()); | |||||
if (tabs != AddAsisRemove.ASIS) { | |||||
// If filtering Java source, prevent changes to whitespace in | |||||
// character and string literals. | |||||
if (getJavafiles()) { | |||||
in = new MaskJavaTabLiteralsFilter(in); | |||||
} | |||||
// Add/Remove tabs | |||||
in = (tabs == AddAsisRemove.ADD) | |||||
? (Reader) new AddTabFilter(in, getTablength()) | |||||
: (Reader) new RemoveTabFilter(in, getTablength()); | |||||
} | |||||
// Add missing EOF character | |||||
in = (ctrlz == AddAsisRemove.ADD) ? new AddEofFilter(in) : in; | |||||
} | |||||
/** | |||||
* Return the next character in the filtered stream. | |||||
* | |||||
* @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 final int read() throws IOException { | |||||
return in.read(); | |||||
} | |||||
/** | |||||
* Specify how DOS EOF (control-z) characters are to be handled. | |||||
* | |||||
* @param attr valid values: | |||||
* <ul> | |||||
* <li>add: ensure that there is an eof at the end of the file | |||||
* <li>asis: leave eof characters alone | |||||
* <li>remove: remove any eof character found at the end | |||||
* </ul> | |||||
*/ | |||||
public void setEof(AddAsisRemove attr) { | |||||
ctrlz = attr.resolve(); | |||||
} | |||||
/** | |||||
* Specify how end of line (EOL) characters are to be handled. | |||||
* | |||||
* @param attr valid values: | |||||
* <ul> | |||||
* <li>asis: convert line endings to your O/S convention | |||||
* <li>cr: convert line endings to CR | |||||
* <li>lf: convert line endings to LF | |||||
* <li>crlf: convert line endings to CRLF | |||||
* </ul> | |||||
*/ | |||||
public void setEol(CrLf attr) { | |||||
eol = attr.resolve(); | |||||
} | |||||
/** | |||||
* Specify whether a missing EOL will be added | |||||
* to the final line of input. | |||||
* | |||||
* @param fixlast if true a missing EOL will be appended. | |||||
*/ | |||||
public void setFixlast(boolean fixlast) { | |||||
this.fixlast = fixlast; | |||||
} | |||||
/** | |||||
* Indicate whether this stream contains Java source. | |||||
* | |||||
* This attribute is only used in assocation with the | |||||
* "<i><b>tab</b></i>" attribute. | |||||
* | |||||
* @param javafiles set to true to prevent this filter from changing tabs | |||||
* found in Java literals. | |||||
*/ | |||||
public void setJavafiles(boolean javafiles) { | |||||
this.javafiles = javafiles; | |||||
} | |||||
/** | |||||
* Specify how tab characters are to be handled. | |||||
* | |||||
* @param attr valid values: | |||||
* <ul> | |||||
* <li>add: convert sequences of spaces which span a tab stop to tabs | |||||
* <li>asis: leave tab and space characters alone | |||||
* <li>remove: convert tabs to spaces | |||||
* </ul> | |||||
*/ | |||||
public void setTab(AddAsisRemove attr) { | |||||
tabs = attr.resolve(); | |||||
} | |||||
/** | |||||
* Specify tab length in characters. | |||||
* | |||||
* @param tabLength specify the length of tab in spaces. | |||||
* Valid values are between 2 and 80 | |||||
* inclusive. The default for this parameter is 8. | |||||
*/ | |||||
public void setTablength(int tabLength) throws IOException { | |||||
if (tabLength < 2 || tabLength > 80) { | |||||
throw new IOException("tablength must be between 2 and 80"); | |||||
} | |||||
this.tabLength = tabLength; | |||||
} | |||||
/** | |||||
* This filter reader redirects all read I/O methods through its own read() method. | |||||
* | |||||
* <P>The input stream is already buffered by the copy task so this doesn't significantly | |||||
* impact performance while it makes writing the individual fix filters much easier.</P> | |||||
*/ | |||||
private static class SimpleFilterReader extends Reader { | |||||
private Reader in; | |||||
int[] preempt = new int[16]; | |||||
int preemptIndex = 0; | |||||
public SimpleFilterReader(Reader in) { | |||||
this.in = in; | |||||
} | |||||
public void push(char c) { | |||||
push((int) c); | |||||
} | |||||
public void push(int c) { | |||||
try { | |||||
preempt[preemptIndex++] = c; | |||||
} catch (ArrayIndexOutOfBoundsException e) { | |||||
int[] p2 = new int[preempt.length * 2]; | |||||
System.arraycopy(preempt, 0, p2, 0, preempt.length); | |||||
preempt = p2; | |||||
push(c); | |||||
} | |||||
} | |||||
public void push(char[] cs, int start, int length) { | |||||
for (int i = start + length - 1; i >= start;) { | |||||
push(cs [i--]); | |||||
} | |||||
} | |||||
public void push(char[] cs) { | |||||
push(cs, 0, cs.length); | |||||
} | |||||
public void push(String s) { | |||||
push(s.toCharArray()); | |||||
} | |||||
/** | |||||
* Does this filter want to block edits on the last character returned by read()? | |||||
*/ | |||||
public boolean editsBlocked() { | |||||
if (in instanceof SimpleFilterReader) { | |||||
return ((SimpleFilterReader) in).editsBlocked(); | |||||
} | |||||
return false; | |||||
} | |||||
public int read() throws java.io.IOException { | |||||
if (preemptIndex > 0) { | |||||
return preempt[--preemptIndex]; | |||||
} | |||||
return in.read(); | |||||
} | |||||
public void close() throws java.io.IOException { | |||||
in.close(); | |||||
} | |||||
public void reset() throws IOException { | |||||
in.reset(); | |||||
} | |||||
public boolean markSupported() { | |||||
return in.markSupported(); | |||||
} | |||||
public boolean ready() throws java.io.IOException { | |||||
return in.ready(); | |||||
} | |||||
public void mark(int i) throws java.io.IOException { | |||||
in.mark(i); | |||||
} | |||||
public long skip(long i) throws java.io.IOException { | |||||
return in.skip(i); | |||||
} | |||||
public int read(char[] buf) throws java.io.IOException { | |||||
return read(buf, 0, buf.length); | |||||
} | |||||
public int read(char[] buf, int start, int length) throws java.io.IOException { | |||||
int count = 0; | |||||
int c = 0; | |||||
while (length-- > 0 && (c = this.read()) != -1) { | |||||
buf[start++] = (char) c; | |||||
count++; | |||||
} | |||||
// if at EOF with no characters in the buffer, return EOF | |||||
if (count == 0 && c == -1) { | |||||
return -1; | |||||
} | |||||
return count; | |||||
} | |||||
} | |||||
private static class MaskJavaTabLiteralsFilter extends SimpleFilterReader { | |||||
boolean editsBlocked = false; | |||||
private static final int JAVA = 1; | |||||
private static final int IN_CHAR_CONST = 2; | |||||
private static final int IN_STR_CONST = 3; | |||||
private static final int IN_SINGLE_COMMENT = 4; | |||||
private static final int IN_MULTI_COMMENT = 5; | |||||
private static final int TRANS_TO_COMMENT = 6; | |||||
private static final int TRANS_FROM_MULTI = 8; | |||||
private int state; | |||||
public MaskJavaTabLiteralsFilter(Reader in) { | |||||
super(in); | |||||
state = JAVA; | |||||
} | |||||
public boolean editsBlocked () { | |||||
return editsBlocked || super.editsBlocked(); | |||||
} | |||||
public int read() throws IOException { | |||||
int thisChar = super.read(); | |||||
// Mask, block from being edited, all characters in constants. | |||||
editsBlocked = (state == IN_CHAR_CONST || state == IN_STR_CONST); | |||||
switch (state) { | |||||
case JAVA: | |||||
// The current character is always emitted. | |||||
switch(thisChar) { | |||||
case '\'': state = IN_CHAR_CONST; break; | |||||
case '"' : state = IN_STR_CONST; break; | |||||
case '/' : state = TRANS_TO_COMMENT; break; | |||||
} | |||||
break; | |||||
case IN_CHAR_CONST: | |||||
switch (thisChar) { | |||||
case '\'': state = JAVA; break; | |||||
} | |||||
break; | |||||
case IN_STR_CONST: | |||||
switch (thisChar) { | |||||
case '"' : state = JAVA; break; | |||||
} | |||||
break; | |||||
case IN_SINGLE_COMMENT: | |||||
// The current character is always emitted. | |||||
switch (thisChar) { | |||||
case '\n': | |||||
case '\r': // EOL | |||||
state = JAVA; | |||||
break; | |||||
} | |||||
break; | |||||
case IN_MULTI_COMMENT: | |||||
// The current character is always emitted. | |||||
switch (thisChar) { | |||||
case '*': state = TRANS_FROM_MULTI; break; | |||||
} | |||||
break; | |||||
case TRANS_TO_COMMENT: | |||||
// The current character is always emitted. | |||||
switch (thisChar) { | |||||
case '*' : state = IN_MULTI_COMMENT; break; | |||||
case '/' : state = IN_SINGLE_COMMENT; break; | |||||
case '\'': state = IN_CHAR_CONST; break; | |||||
case '"' : state = IN_STR_CONST; break; | |||||
default : state = JAVA; | |||||
} | |||||
case TRANS_FROM_MULTI: | |||||
// The current character is always emitted. | |||||
switch (thisChar) { | |||||
case '/': state = JAVA; break; | |||||
} | |||||
} | |||||
return thisChar; | |||||
} | |||||
} | |||||
private static class NormalizeEolFilter extends SimpleFilterReader { | |||||
boolean previousWasEOL; | |||||
boolean fixLast; | |||||
int normalizedEOL = 0; | |||||
char[] eol = null; | |||||
public NormalizeEolFilter(Reader in, String eolString, boolean fixLast) { | |||||
super(in); | |||||
eol = eolString.toCharArray(); | |||||
this.fixLast = fixLast; | |||||
} | |||||
public int read() throws IOException { | |||||
int thisChar = super.read(); | |||||
if (normalizedEOL == 0) { | |||||
int numEOL = 0; | |||||
switch (thisChar) { | |||||
case CTRLZ: | |||||
case -1: | |||||
if (fixLast && !previousWasEOL) { | |||||
numEOL = 1; | |||||
if (thisChar == CTRLZ) { | |||||
push(thisChar); | |||||
} | |||||
} | |||||
break; | |||||
case '\n': | |||||
// EOL was "\n" | |||||
numEOL = 1; | |||||
break; | |||||
case '\r': | |||||
numEOL = 1; | |||||
int c1 = super.read(); | |||||
int c2 = super.read(); | |||||
if (c1 == '\r' && c2 == '\n') { | |||||
// EOL was "\r\r\n" | |||||
} else if (c1 == '\r') { | |||||
// EOL was "\r\r" - handle as two consecutive "\r" and "\r" | |||||
numEOL = 2; | |||||
push(c2); | |||||
} else if (c1 == '\n') { | |||||
// EOL was "\r\n" | |||||
push(c2); | |||||
} else { | |||||
// EOL was "\r" | |||||
push(c2); | |||||
push(c1); | |||||
} | |||||
} | |||||
if (numEOL > 0) { | |||||
while (numEOL-- > 0) { | |||||
push(eol); | |||||
normalizedEOL += eol.length; | |||||
} | |||||
previousWasEOL = true; | |||||
thisChar = read(); | |||||
} else if (thisChar != -1) { | |||||
previousWasEOL = false; | |||||
} | |||||
} else { | |||||
normalizedEOL--; | |||||
} | |||||
return thisChar; | |||||
} | |||||
} | |||||
private static class FixLastFilter extends SimpleFilterReader { | |||||
int lastChar = -1; | |||||
char[] eol = null; | |||||
public FixLastFilter(Reader in, String eolString) { | |||||
super(in); | |||||
eol = eolString.toCharArray(); | |||||
} | |||||
public int read() throws IOException { | |||||
int thisChar = super.read(); | |||||
// if source is EOF but last character was NOT eol, return eol | |||||
if (thisChar == -1) { | |||||
switch (lastChar) { | |||||
case '\r': | |||||
case '\n': | |||||
// Return first character of EOL | |||||
thisChar = eol[0]; | |||||
// Push remaining characters onto input stream | |||||
push(eol, 1, eol.length - 1); | |||||
} | |||||
} | |||||
lastChar = thisChar; | |||||
return thisChar; | |||||
} | |||||
} | |||||
private static class AddEofFilter extends SimpleFilterReader { | |||||
int lastChar = -1; | |||||
public AddEofFilter(Reader in) { | |||||
super(in); | |||||
} | |||||
public int read() throws IOException { | |||||
int thisChar = super.read(); | |||||
// if source is EOF but last character was NOT ctrl-z, return ctrl-z | |||||
if (thisChar == -1) { | |||||
if (lastChar != CTRLZ) { | |||||
lastChar = CTRLZ; | |||||
thisChar = CTRLZ; | |||||
} | |||||
} else { | |||||
lastChar = thisChar; | |||||
} | |||||
return thisChar; | |||||
} | |||||
} | |||||
private static class RemoveEofFilter extends SimpleFilterReader { | |||||
int lookAhead = -1; | |||||
public RemoveEofFilter(Reader in) { | |||||
super(in); | |||||
try { | |||||
lookAhead = in.read(); | |||||
} catch (IOException e) { | |||||
lookAhead = -1; | |||||
} | |||||
} | |||||
public int read() throws IOException { | |||||
int lookAhead2 = super.read(); | |||||
// If source at EOF and lookAhead is ctrl-z, return EOF (NOT ctrl-z) | |||||
if (lookAhead2 == -1 && lookAhead == CTRLZ) { | |||||
return -1; | |||||
} | |||||
// Return current look-ahead | |||||
int i = lookAhead; | |||||
lookAhead = lookAhead2; | |||||
return i; | |||||
} | |||||
} | |||||
private static class AddTabFilter extends SimpleFilterReader { | |||||
int columnNumber = 0; | |||||
int tabLength = 0; | |||||
public AddTabFilter(Reader in, int tabLength) { | |||||
super(in); | |||||
this.tabLength = tabLength; | |||||
} | |||||
public int read() throws IOException { | |||||
int c = super.read(); | |||||
switch (c) { | |||||
case '\r': | |||||
case '\n': | |||||
columnNumber = 0; | |||||
break; | |||||
case ' ': | |||||
columnNumber++; | |||||
if (!editsBlocked()) { | |||||
int colNextTab = ((columnNumber + tabLength - 1) / tabLength) * tabLength; | |||||
int countSpaces = 1; | |||||
int numTabs = 0; | |||||
scanWhitespace: | |||||
while ((c = super.read()) != -1) { | |||||
switch (c) { | |||||
case ' ': | |||||
if (++columnNumber == colNextTab) { | |||||
numTabs++; | |||||
countSpaces = 0; | |||||
colNextTab += tabLength; | |||||
} else { | |||||
countSpaces++; | |||||
} | |||||
break; | |||||
case '\t': | |||||
columnNumber = colNextTab; | |||||
numTabs++; | |||||
countSpaces = 0; | |||||
colNextTab += tabLength; | |||||
break; | |||||
default: | |||||
push(c); | |||||
break scanWhitespace; | |||||
} | |||||
} | |||||
while (countSpaces-- > 0) { | |||||
push(' '); | |||||
columnNumber--; | |||||
} | |||||
while (numTabs-- > 0) { | |||||
push('\t'); | |||||
columnNumber -= tabLength; | |||||
} | |||||
c = super.read(); | |||||
switch (c) { | |||||
case ' ': columnNumber ++; break; | |||||
case '\t': columnNumber += tabLength; break; | |||||
} | |||||
} | |||||
break; | |||||
case '\t': | |||||
columnNumber = ((columnNumber + tabLength - 1) / tabLength) * tabLength; | |||||
break; | |||||
default: | |||||
columnNumber++; | |||||
} | |||||
return c; | |||||
} | |||||
} | |||||
private static class RemoveTabFilter extends SimpleFilterReader { | |||||
int columnNumber = 0; | |||||
int tabLength = 0; | |||||
public RemoveTabFilter(Reader in, int tabLength) { | |||||
super(in); | |||||
this.tabLength = tabLength; | |||||
} | |||||
public int read() throws IOException { | |||||
int c = super.read(); | |||||
switch (c) { | |||||
case '\r': | |||||
case '\n': | |||||
columnNumber = 0; | |||||
break; | |||||
case '\t': | |||||
int width = tabLength - columnNumber % tabLength; | |||||
if (!editsBlocked()) { | |||||
for (;width > 1; width--) { | |||||
push(' '); | |||||
} | |||||
c = ' '; | |||||
} | |||||
columnNumber += width; | |||||
break; | |||||
default: | |||||
columnNumber++; | |||||
} | |||||
return c; | |||||
} | |||||
} | |||||
/** | |||||
* Enumerated attribute with the values "asis", "add" and "remove". | |||||
*/ | |||||
public static class AddAsisRemove extends EnumeratedAttribute { | |||||
private static final AddAsisRemove ASIS = newInstance("asis"); | |||||
private static final AddAsisRemove ADD = newInstance("add"); | |||||
private static final AddAsisRemove REMOVE = newInstance("remove"); | |||||
public String[] getValues() { | |||||
return new String[] {"add", "asis", "remove"}; | |||||
} | |||||
public boolean equals(Object other) { | |||||
return other instanceof AddAsisRemove | |||||
&& getIndex() == ((AddAsisRemove) other).getIndex(); | |||||
} | |||||
AddAsisRemove resolve() throws IllegalStateException { | |||||
if (this.equals(ASIS)) { | |||||
return ASIS; | |||||
} | |||||
if (this.equals(ADD)) { | |||||
return ADD; | |||||
} | |||||
if (this.equals(REMOVE)) { | |||||
return REMOVE; | |||||
} | |||||
throw new IllegalStateException("No replacement for " + this); | |||||
} | |||||
// Works like clone() but doesn't show up in the Javadocs | |||||
private AddAsisRemove newInstance() { | |||||
return newInstance(getValue()); | |||||
} | |||||
public static AddAsisRemove newInstance(String value) { | |||||
AddAsisRemove a = new AddAsisRemove(); | |||||
a.setValue(value); | |||||
return a; | |||||
} | |||||
} | |||||
/** | |||||
* Enumerated attribute with the values "asis", "cr", "lf" and "crlf". | |||||
*/ | |||||
public static class CrLf extends EnumeratedAttribute { | |||||
private static final CrLf ASIS = newInstance("asis"); | |||||
private static final CrLf CR = newInstance("cr"); | |||||
private static final CrLf CRLF = newInstance("crlf"); | |||||
private static final CrLf DOS = newInstance("dos"); | |||||
private static final CrLf LF = newInstance("lf"); | |||||
private static final CrLf MAC = newInstance("mac"); | |||||
private static final CrLf UNIX = newInstance("unix"); | |||||
/** | |||||
* @see EnumeratedAttribute#getValues | |||||
*/ | |||||
public String[] getValues() { | |||||
return new String[] {"asis", "cr", "lf", "crlf", | |||||
"mac", "unix", "dos"}; | |||||
} | |||||
public boolean equals(Object other) { | |||||
return other instanceof CrLf | |||||
&& getIndex() == ((CrLf) other).getIndex(); | |||||
} | |||||
CrLf resolve() { | |||||
if (this.equals(ASIS)) { | |||||
return ASIS; | |||||
} | |||||
if (this.equals(CR) || this.equals(UNIX)) { | |||||
return CR; | |||||
} | |||||
if (this.equals(CRLF) || this.equals(DOS)) { | |||||
return CRLF; | |||||
} | |||||
if (this.equals(LF) || this.equals(MAC)) { | |||||
return LF; | |||||
} | |||||
throw new IllegalStateException("No replacement for " + this); | |||||
} | |||||
// Works like clone() but doesn't show up in the Javadocs | |||||
private CrLf newInstance() { | |||||
return newInstance(getValue()); | |||||
} | |||||
public static CrLf newInstance(String value) { | |||||
CrLf c = new CrLf(); | |||||
c.setValue(value); | |||||
return c; | |||||
} | |||||
} | |||||
} |
@@ -1,5 +1,5 @@ | |||||
/* | /* | ||||
* Copyright 2000-2005 The Apache Software Foundation | |||||
* Copyright 2000-2005 The Apache Software Foundation | |||||
* | * | ||||
* Licensed under the Apache License, Version 2.0 (the "License"); | * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
* you may not use this file except in compliance with the License. | * you may not use this file except in compliance with the License. | ||||
@@ -17,24 +17,22 @@ | |||||
package org.apache.tools.ant.taskdefs; | package org.apache.tools.ant.taskdefs; | ||||
import java.io.BufferedReader; | |||||
import java.io.BufferedWriter; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileInputStream; | |||||
import java.io.FileOutputStream; | |||||
import java.io.Reader; | |||||
import java.io.FileReader; | import java.io.FileReader; | ||||
import java.io.FileWriter; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.BufferedReader; | |||||
import java.io.FileInputStream; | |||||
import java.io.InputStreamReader; | import java.io.InputStreamReader; | ||||
import java.io.OutputStreamWriter; | |||||
import java.io.Reader; | |||||
import java.io.Writer; | |||||
import java.util.Vector; | |||||
import java.util.Enumeration; | import java.util.Enumeration; | ||||
import java.util.NoSuchElementException; | import java.util.NoSuchElementException; | ||||
import org.apache.tools.ant.Project; | |||||
import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
import org.apache.tools.ant.DirectoryScanner; | import org.apache.tools.ant.DirectoryScanner; | ||||
import org.apache.tools.ant.Project; | |||||
import org.apache.tools.ant.taskdefs.condition.Os; | |||||
import org.apache.tools.ant.filters.FixCrLfFilter; | |||||
import org.apache.tools.ant.filters.ChainableReader; | |||||
import org.apache.tools.ant.types.FilterChain; | |||||
import org.apache.tools.ant.types.EnumeratedAttribute; | import org.apache.tools.ant.types.EnumeratedAttribute; | ||||
import org.apache.tools.ant.util.FileUtils; | import org.apache.tools.ant.util.FileUtils; | ||||
@@ -82,53 +80,23 @@ import org.apache.tools.ant.util.FileUtils; | |||||
* @ant.task category="filesystem" | * @ant.task category="filesystem" | ||||
*/ | */ | ||||
public class FixCRLF extends MatchingTask { | |||||
private static final int UNDEF = -1; | |||||
private static final int NOTJAVA = 0; | |||||
private static final int LOOKING = 1; | |||||
private static final int IN_CHAR_CONST = 2; | |||||
private static final int IN_STR_CONST = 3; | |||||
private static final int IN_SINGLE_COMMENT = 4; | |||||
private static final int IN_MULTI_COMMENT = 5; | |||||
private static final int ASIS = 0; | |||||
private static final int CR = 1; | |||||
private static final int LF = 2; | |||||
private static final int CRLF = 3; | |||||
private static final int ADD = 1; | |||||
private static final int REMOVE = -1; | |||||
private static final int SPACES = -1; | |||||
private static final int TABS = 1; | |||||
private static final int INBUFLEN = 8192; | |||||
private static final int LINEBUFLEN = 200; | |||||
public class FixCRLF extends MatchingTask implements ChainableReader { | |||||
private static final char CTRLZ = '\u001A'; | |||||
public static final String ERROR_FILE_AND_SRCDIR | |||||
= "srcdir and file are mutually exclusive"; | |||||
private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); | private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); | ||||
private int tablength = 8; | |||||
private String spaces = " "; | |||||
private StringBuffer linebuf = new StringBuffer(1024); | |||||
private StringBuffer linebuf2 = new StringBuffer(1024); | |||||
private int eol; | |||||
private String eolstr; | |||||
private int ctrlz; | |||||
private int tabs; | |||||
private boolean javafiles = false; | |||||
private boolean fixlast = true; | |||||
private boolean preserveLastModified = false; | private boolean preserveLastModified = false; | ||||
private File srcDir; | private File srcDir; | ||||
private File destDir = null; | private File destDir = null; | ||||
private File file; | private File file; | ||||
private FixCrLfFilter filter = new FixCrLfFilter(); | |||||
/** | /** | ||||
* Encoding to assume for the files | * Encoding to assume for the files | ||||
*/ | */ | ||||
private String encoding = null; | private String encoding = null; | ||||
public static final String ERROR_FILE_AND_SRCDIR = "srcdir and file are mutually exclusive"; | |||||
/** | /** | ||||
* Defaults the properties based on the system type. | * Defaults the properties based on the system type. | ||||
@@ -137,24 +105,21 @@ public class FixCRLF extends MatchingTask { | |||||
* <li>DOS: eol="CRLF" tab="asis" eof="asis"</ul> | * <li>DOS: eol="CRLF" tab="asis" eof="asis"</ul> | ||||
*/ | */ | ||||
public FixCRLF () { | public FixCRLF () { | ||||
tabs = ASIS; | |||||
if (Os.isFamily("mac")) { | |||||
ctrlz = REMOVE; | |||||
eol = CR; | |||||
eolstr = "\r"; | |||||
} else if (Os.isFamily("dos")) { | |||||
ctrlz = ASIS; | |||||
eol = CRLF; | |||||
eolstr = "\r\n"; | |||||
} else { | |||||
ctrlz = REMOVE; | |||||
eol = LF; | |||||
eolstr = "\n"; | |||||
} | |||||
} | |||||
/** | |||||
* Chain this task as a reader. | |||||
* @param rdr Reader to chain. | |||||
* @return a Reader. | |||||
* @since Ant 1.7? | |||||
*/ | |||||
public final Reader chain(final Reader rdr) { | |||||
return filter.chain(rdr); | |||||
} | } | ||||
/** | /** | ||||
* Set the source dir to find the source text files. | * Set the source dir to find the source text files. | ||||
* @param srcDir the source directory. | |||||
*/ | */ | ||||
public void setSrcdir(File srcDir) { | public void setSrcdir(File srcDir) { | ||||
this.srcDir = srcDir; | this.srcDir = srcDir; | ||||
@@ -163,6 +128,7 @@ public class FixCRLF extends MatchingTask { | |||||
/** | /** | ||||
* Set the destination where the fixed files should be placed. | * Set the destination where the fixed files should be placed. | ||||
* Default is to replace the original file. | * Default is to replace the original file. | ||||
* @param destDir the destination directory. | |||||
*/ | */ | ||||
public void setDestdir(File destDir) { | public void setDestdir(File destDir) { | ||||
this.destDir = destDir; | this.destDir = destDir; | ||||
@@ -170,15 +136,16 @@ public class FixCRLF extends MatchingTask { | |||||
/** | /** | ||||
* Set to true if modifying Java source files. | * Set to true if modifying Java source files. | ||||
* @param javafiles whether modifying Java files. | |||||
*/ | */ | ||||
public void setJavafiles(boolean javafiles) { | public void setJavafiles(boolean javafiles) { | ||||
this.javafiles = javafiles; | |||||
filter.setJavafiles(javafiles); | |||||
} | } | ||||
/** | /** | ||||
* set a single file to convert | |||||
* @since Ant1.7 | |||||
* @param file | |||||
* Set a single file to convert. | |||||
* @since Ant 1.6.3 | |||||
* @param file the file to convert. | |||||
*/ | */ | ||||
public void setFile(File file) { | public void setFile(File file) { | ||||
this.file = file; | this.file = file; | ||||
@@ -196,20 +163,7 @@ public class FixCRLF extends MatchingTask { | |||||
* </ul> | * </ul> | ||||
*/ | */ | ||||
public void setEol(CrLf attr) { | public void setEol(CrLf attr) { | ||||
String option = attr.getValue(); | |||||
if (option.equals("asis")) { | |||||
eol = ASIS; | |||||
} else if (option.equals("cr") || option.equals("mac")) { | |||||
eol = CR; | |||||
eolstr = "\r"; | |||||
} else if (option.equals("lf") || option.equals("unix")) { | |||||
eol = LF; | |||||
eolstr = "\n"; | |||||
} else { | |||||
// Must be "crlf" | |||||
eol = CRLF; | |||||
eolstr = "\r\n"; | |||||
} | |||||
filter.setEol(FixCrLfFilter.CrLf.newInstance(attr.getValue())); | |||||
} | } | ||||
/** | /** | ||||
@@ -252,15 +206,7 @@ public class FixCRLF extends MatchingTask { | |||||
* </ul> | * </ul> | ||||
*/ | */ | ||||
public void setTab(AddAsisRemove attr) { | public void setTab(AddAsisRemove attr) { | ||||
String option = attr.getValue(); | |||||
if (option.equals("remove")) { | |||||
tabs = SPACES; | |||||
} else if (option.equals("asis")) { | |||||
tabs = ASIS; | |||||
} else { | |||||
// must be "add" | |||||
tabs = TABS; | |||||
} | |||||
filter.setTab(FixCrLfFilter.AddAsisRemove.newInstance(attr.getValue())); | |||||
} | } | ||||
/** | /** | ||||
@@ -269,16 +215,11 @@ public class FixCRLF extends MatchingTask { | |||||
* @param tlength specify the length of tab in spaces, | * @param tlength specify the length of tab in spaces, | ||||
*/ | */ | ||||
public void setTablength(int tlength) throws BuildException { | public void setTablength(int tlength) throws BuildException { | ||||
if (tlength < 2 || tlength > 80) { | |||||
throw new BuildException("tablength must be between 2 and 80", | |||||
getLocation()); | |||||
} | |||||
tablength = tlength; | |||||
StringBuffer sp = new StringBuffer(); | |||||
for (int i = 0; i < tablength; i++) { | |||||
sp.append(' '); | |||||
try { | |||||
filter.setTablength(tlength); | |||||
} catch (IOException e) { | |||||
throw new BuildException(e); | |||||
} | } | ||||
spaces = sp.toString(); | |||||
} | } | ||||
/** | /** | ||||
@@ -292,20 +233,13 @@ public class FixCRLF extends MatchingTask { | |||||
* </ul> | * </ul> | ||||
*/ | */ | ||||
public void setEof(AddAsisRemove attr) { | public void setEof(AddAsisRemove attr) { | ||||
String option = attr.getValue(); | |||||
if (option.equals("remove")) { | |||||
ctrlz = REMOVE; | |||||
} else if (option.equals("asis")) { | |||||
ctrlz = ASIS; | |||||
} else { | |||||
// must be "add" | |||||
ctrlz = ADD; | |||||
} | |||||
filter.setEof(FixCrLfFilter.AddAsisRemove.newInstance(attr.getValue())); | |||||
} | } | ||||
/** | /** | ||||
* Specifies the encoding Ant expects the files to be in - | |||||
* defaults to the platforms default encoding. | |||||
* Specifies the encoding Ant expects the files to be | |||||
* in--defaults to the platforms default encoding. | |||||
* @param encoding String encoding name. | |||||
*/ | */ | ||||
public void setEncoding(String encoding) { | public void setEncoding(String encoding) { | ||||
this.encoding = encoding; | this.encoding = encoding; | ||||
@@ -314,13 +248,15 @@ public class FixCRLF extends MatchingTask { | |||||
/** | /** | ||||
* Specify whether a missing EOL will be added | * Specify whether a missing EOL will be added | ||||
* to the final line of a file. | * to the final line of a file. | ||||
* @param fixlast whether to fix the last line. | |||||
*/ | */ | ||||
public void setFixlast(boolean fixlast) { | public void setFixlast(boolean fixlast) { | ||||
this.fixlast = fixlast; | |||||
filter.setFixlast(fixlast); | |||||
} | } | ||||
/** | /** | ||||
* Set to true if keeping the last modified time as the original files. | |||||
* Set whether to preserve the last modified time as the original files. | |||||
* @param preserve true if timestamps should be preserved. | |||||
* @since Ant 1.6.3 | * @since Ant 1.6.3 | ||||
*/ | */ | ||||
public void setPreserveLastModified(boolean preserve) { | public void setPreserveLastModified(boolean preserve) { | ||||
@@ -333,14 +269,14 @@ public class FixCRLF extends MatchingTask { | |||||
public void execute() throws BuildException { | public void execute() throws BuildException { | ||||
// first off, make sure that we've got a srcdir and destdir | // first off, make sure that we've got a srcdir and destdir | ||||
if(file!=null) { | |||||
if(srcDir!=null) { | |||||
if (file != null) { | |||||
if (srcDir != null) { | |||||
throw new BuildException(ERROR_FILE_AND_SRCDIR); | throw new BuildException(ERROR_FILE_AND_SRCDIR); | ||||
} | } | ||||
//patch file into the fileset | //patch file into the fileset | ||||
fileset.setFile(file); | fileset.setFile(file); | ||||
//set our parent dir | //set our parent dir | ||||
srcDir=file.getParentFile(); | |||||
srcDir = file.getParentFile(); | |||||
} | } | ||||
if (srcDir == null) { | if (srcDir == null) { | ||||
throw new BuildException("srcdir attribute must be set!"); | throw new BuildException("srcdir attribute must be set!"); | ||||
@@ -359,14 +295,12 @@ public class FixCRLF extends MatchingTask { | |||||
throw new BuildException("destdir is not a directory!"); | throw new BuildException("destdir is not a directory!"); | ||||
} | } | ||||
} | } | ||||
// log options used | // log options used | ||||
log("options:" | log("options:" | ||||
+ " eol=" | |||||
+ (eol == ASIS ? "asis" : eol == CR ? "cr" : eol == LF ? "lf" : "crlf") | |||||
+ " tab=" + (tabs == TABS ? "add" : tabs == ASIS ? "asis" : "remove") | |||||
+ " eof=" + (ctrlz == ADD ? "add" : ctrlz == ASIS ? "asis" : "remove") | |||||
+ " tablength=" + tablength | |||||
+ " eol=" + filter.getEol().getValue() | |||||
+ " tab=" + filter.getTab().getValue() | |||||
+ " eof=" + filter.getEof().getValue() | |||||
+ " tablength=" + filter.getTablength() | |||||
+ " encoding=" + (encoding == null ? "default" : encoding), | + " encoding=" + (encoding == null ? "default" : encoding), | ||||
Project.MSG_VERBOSE); | Project.MSG_VERBOSE); | ||||
@@ -378,177 +312,21 @@ public class FixCRLF extends MatchingTask { | |||||
} | } | ||||
} | } | ||||
/** | |||||
* Creates a Reader reading from a given file an taking the user | |||||
* defined encoding into account. | |||||
*/ | |||||
private Reader getReader(File f) throws IOException { | |||||
return (encoding == null) ? new FileReader(f) | |||||
: new InputStreamReader(new FileInputStream(f), encoding); | |||||
} | |||||
private void processFile(String file) throws BuildException { | private void processFile(String file) throws BuildException { | ||||
File srcFile = new File(srcDir, file); | File srcFile = new File(srcDir, file); | ||||
long lastModified = srcFile.lastModified(); | long lastModified = srcFile.lastModified(); | ||||
File destD = destDir == null ? srcDir : destDir; | File destD = destDir == null ? srcDir : destDir; | ||||
File tmpFile = null; | |||||
BufferedWriter outWriter; | |||||
OneLiner.BufferLine line; | |||||
// read the contents of the file | |||||
OneLiner lines = new OneLiner(srcFile); | |||||
FilterChain fc = new FilterChain(); | |||||
fc.add(filter); | |||||
Vector fcv = new Vector(1); | |||||
fcv.add(fc); | |||||
File tmpFile = FILE_UTILS.createTempFile("fixcrlf", "", null); | |||||
tmpFile.deleteOnExit(); | |||||
try { | try { | ||||
// Set up the output Writer | |||||
try { | |||||
tmpFile = FILE_UTILS.createTempFile("fixcrlf", "", null); | |||||
tmpFile.deleteOnExit(); | |||||
Writer writer = (encoding == null) ? new FileWriter(tmpFile) | |||||
: new OutputStreamWriter(new FileOutputStream(tmpFile), | |||||
encoding); | |||||
outWriter = new BufferedWriter(writer); | |||||
} catch (IOException e) { | |||||
throw new BuildException(e); | |||||
} | |||||
while (lines.hasMoreElements()) { | |||||
// In-line states | |||||
int endComment; | |||||
try { | |||||
line = (OneLiner.BufferLine) lines.nextElement(); | |||||
} catch (NoSuchElementException e) { | |||||
throw new BuildException(e); | |||||
} | |||||
String lineString = line.getLineString(); | |||||
int linelen = line.length(); | |||||
// Note - all of the following processing NOT done for | |||||
// tabs ASIS | |||||
if (tabs == ASIS) { | |||||
// Just copy the body of the line across | |||||
try { | |||||
outWriter.write(lineString); | |||||
} catch (IOException e) { | |||||
throw new BuildException(e); | |||||
} // end of try-catch | |||||
} else { // (tabs != ASIS) | |||||
while (line.getNext() < linelen) { | |||||
switch (lines.getState()) { | |||||
case NOTJAVA: | |||||
notInConstant(line, line.length(), outWriter); | |||||
break; | |||||
case IN_MULTI_COMMENT: | |||||
endComment | |||||
= lineString.indexOf("*/", line.getNext()); | |||||
if (endComment >= 0) { | |||||
// End of multiLineComment on this line | |||||
endComment += 2; // Include the end token | |||||
lines.setState(LOOKING); | |||||
} else { | |||||
endComment = linelen; | |||||
} | |||||
notInConstant(line, endComment, outWriter); | |||||
break; | |||||
case IN_SINGLE_COMMENT: | |||||
notInConstant(line, line.length(), outWriter); | |||||
lines.setState(LOOKING); | |||||
break; | |||||
case IN_CHAR_CONST: | |||||
case IN_STR_CONST: | |||||
// Got here from LOOKING by finding an | |||||
// opening "\'" next points to that quote | |||||
// character. | |||||
// Find the end of the constant. Watch | |||||
// out for backslashes. Literal tabs are | |||||
// left unchanged, and the column is | |||||
// adjusted accordingly. | |||||
int begin = line.getNext(); | |||||
char terminator = (lines.getState() == IN_STR_CONST | |||||
? '\"' | |||||
: '\''); | |||||
endOfCharConst(line, terminator); | |||||
while (line.getNext() < line.getLookahead()) { | |||||
if (line.getNextCharInc() == '\t') { | |||||
line.setColumn(line.getColumn() | |||||
+ tablength | |||||
- (line.getColumn() % tablength)); | |||||
} else { | |||||
line.incColumn(); | |||||
} | |||||
} | |||||
// Now output the substring | |||||
try { | |||||
outWriter.write(line.substring(begin, | |||||
line.getNext())); | |||||
} catch (IOException e) { | |||||
throw new BuildException(e); | |||||
} | |||||
lines.setState(LOOKING); | |||||
break; | |||||
case LOOKING: | |||||
nextStateChange(line); | |||||
notInConstant(line, line.getLookahead(), outWriter); | |||||
break; | |||||
} // end of switch (state) | |||||
} // end of while (line.getNext() < linelen) | |||||
} // end of else (tabs != ASIS) | |||||
if (!("".equals(line.getEol())) || fixlast) { | |||||
try { | |||||
outWriter.write(eolstr); | |||||
} catch (IOException e) { | |||||
throw new BuildException(e); | |||||
} // end of try-catch | |||||
} //end if non-blank original eol or fixlast | |||||
} // end of while (lines.hasNext()) | |||||
try { | |||||
// Handle CTRLZ | |||||
if (ctrlz == ASIS) { | |||||
outWriter.write(lines.getEofStr()); | |||||
} else if (ctrlz == ADD) { | |||||
outWriter.write(CTRLZ); | |||||
} | |||||
} catch (IOException e) { | |||||
throw new BuildException(e); | |||||
} finally { | |||||
try { | |||||
outWriter.close(); | |||||
} catch (IOException e) { | |||||
throw new BuildException(e); | |||||
} | |||||
} | |||||
try { | |||||
lines.close(); | |||||
lines = null; | |||||
} catch (IOException e) { | |||||
throw new BuildException("Unable to close source file " | |||||
+ srcFile); | |||||
} | |||||
FILE_UTILS.copyFile(srcFile, tmpFile, null, fcv, false, | |||||
false, encoding, getProject()); | |||||
File destFile = new File(destD, file); | File destFile = new File(destD, file); | ||||
@@ -556,15 +334,11 @@ public class FixCRLF extends MatchingTask { | |||||
if (destFile.exists()) { | if (destFile.exists()) { | ||||
// Compare the destination with the temp file | // Compare the destination with the temp file | ||||
log("destFile exists", Project.MSG_DEBUG); | log("destFile exists", Project.MSG_DEBUG); | ||||
if (!FILE_UTILS.contentEquals(destFile, tmpFile)) { | |||||
log(destFile + " is being written", Project.MSG_DEBUG); | |||||
} else { | |||||
log(destFile + " is not written, as the contents " | |||||
+ "are identical", Project.MSG_DEBUG); | |||||
destIsWrong = false; | |||||
} | |||||
destIsWrong = !FILE_UTILS.contentEquals(destFile, tmpFile); | |||||
log(destFile + (destIsWrong ? " is being written" | |||||
: " is not written, as the contents are identical"), | |||||
Project.MSG_DEBUG); | |||||
} | } | ||||
if (destIsWrong) { | if (destIsWrong) { | ||||
FILE_UTILS.rename(tmpFile, destFile); | FILE_UTILS.rename(tmpFile, destFile); | ||||
if (preserveLastModified) { | if (preserveLastModified) { | ||||
@@ -573,214 +347,20 @@ public class FixCRLF extends MatchingTask { | |||||
} | } | ||||
tmpFile = null; | tmpFile = null; | ||||
} | } | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new BuildException(e); | throw new BuildException(e); | ||||
} finally { | |||||
try { | |||||
if (lines != null) { | |||||
lines.close(); | |||||
} | |||||
} catch (IOException io) { | |||||
log("Error closing " + srcFile, Project.MSG_ERR); | |||||
} // end of catch | |||||
if (tmpFile != null) { | |||||
tmpFile.delete(); | |||||
} | |||||
} // end of finally | |||||
} | |||||
/** | |||||
* Scan a BufferLine for the next state changing token: the beginning | |||||
* of a single or multi-line comment, a character or a string constant. | |||||
* | |||||
* As a side-effect, sets the buffer state to the next state, and sets | |||||
* field lookahead to the first character of the state-changing token, or | |||||
* to the next eol character. | |||||
* | |||||
* @param bufline BufferLine containing the string | |||||
* to be processed | |||||
* @exception org.apache.tools.ant.BuildException | |||||
* Thrown when end of line is reached | |||||
* before the terminator is found. | |||||
*/ | |||||
private void nextStateChange(OneLiner.BufferLine bufline) | |||||
throws BuildException { | |||||
int eofl = bufline.length(); | |||||
int ptr = bufline.getNext(); | |||||
// Look for next single or double quote, double slash or slash star | |||||
while (ptr < eofl) { | |||||
switch (bufline.getChar(ptr++)) { | |||||
case '\'': | |||||
bufline.setState(IN_CHAR_CONST); | |||||
bufline.setLookahead(--ptr); | |||||
return; | |||||
case '\"': | |||||
bufline.setState(IN_STR_CONST); | |||||
bufline.setLookahead(--ptr); | |||||
return; | |||||
case '/': | |||||
if (ptr < eofl) { | |||||
if (bufline.getChar(ptr) == '*') { | |||||
bufline.setState(IN_MULTI_COMMENT); | |||||
bufline.setLookahead(--ptr); | |||||
return; | |||||
} else if (bufline.getChar(ptr) == '/') { | |||||
bufline.setState(IN_SINGLE_COMMENT); | |||||
bufline.setLookahead(--ptr); | |||||
return; | |||||
} | |||||
} | |||||
break; | |||||
} // end of switch (bufline.getChar(ptr++)) | |||||
} // end of while (ptr < eofl) | |||||
// Eol is the next token | |||||
bufline.setLookahead(ptr); | |||||
} | |||||
/** | |||||
* Scan a BufferLine forward from the 'next' pointer | |||||
* for the end of a character constant. Set 'lookahead' pointer to the | |||||
* character following the terminating quote. | |||||
* | |||||
* @param bufline BufferLine containing the string | |||||
* to be processed | |||||
* @param terminator The constant terminator | |||||
* | |||||
* @exception org.apache.tools.ant.BuildException | |||||
* Thrown when end of line is reached | |||||
* before the terminator is found. | |||||
*/ | |||||
private void endOfCharConst(OneLiner.BufferLine bufline, char terminator) | |||||
throws BuildException { | |||||
int ptr = bufline.getNext(); | |||||
int eofl = bufline.length(); | |||||
char c; | |||||
ptr++; // skip past initial quote | |||||
while (ptr < eofl) { | |||||
if ((c = bufline.getChar(ptr++)) == '\\') { | |||||
ptr++; | |||||
} else { | |||||
if (c == terminator) { | |||||
bufline.setLookahead(ptr); | |||||
return; | |||||
} | |||||
} | |||||
} // end of while (ptr < eofl) | |||||
// Must have fallen through to the end of the line | |||||
throw new BuildException("endOfCharConst: unterminated char constant"); | |||||
} | |||||
/** | |||||
* Process a BufferLine string which is not part of a string constant. | |||||
* The start position of the string is given by the 'next' field. | |||||
* Sets the 'next' and 'column' fields in the BufferLine. | |||||
* | |||||
* @param bufline BufferLine containing the string | |||||
* to be processed | |||||
* @param end Index just past the end of the | |||||
* string | |||||
* @param outWriter Sink for the processed string | |||||
*/ | |||||
private void notInConstant(OneLiner.BufferLine bufline, int end, | |||||
BufferedWriter outWriter) { | |||||
// N.B. both column and string index are zero-based | |||||
// Process a string not part of a constant; | |||||
// i.e. convert tabs<->spaces as required | |||||
// This is NOT called for ASIS tab handling | |||||
int nextTab; | |||||
int nextStop; | |||||
int tabspaces; | |||||
String line = bufline.substring(bufline.getNext(), end); | |||||
int place = 0; // Zero-based | |||||
int col = bufline.getColumn(); // Zero-based | |||||
// process sequences of white space | |||||
// first convert all tabs to spaces | |||||
linebuf = new StringBuffer(); | |||||
while ((nextTab = line.indexOf((int) '\t', place)) >= 0) { | |||||
linebuf.append(line.substring(place, nextTab)); // copy to the TAB | |||||
col += nextTab - place; | |||||
tabspaces = tablength - (col % tablength); | |||||
linebuf.append(spaces.substring(0, tabspaces)); | |||||
col += tabspaces; | |||||
place = nextTab + 1; | |||||
} // end of while | |||||
linebuf.append(line.substring(place, line.length())); | |||||
// if converting to spaces, all finished | |||||
String linestring = new String(linebuf.substring(0)); | |||||
if (tabs == REMOVE) { | |||||
try { | |||||
outWriter.write(linestring); | |||||
} catch (IOException e) { | |||||
throw new BuildException(e); | |||||
} // end of try-catch | |||||
} else { // tabs == ADD | |||||
int tabCol; | |||||
linebuf2 = new StringBuffer(); | |||||
place = 0; | |||||
col = bufline.getColumn(); | |||||
int placediff = col - 0; | |||||
// for the length of the string, cycle through the tab stop | |||||
// positions, checking for a space preceded by at least one | |||||
// other space at the tab stop. if so replace the longest possible | |||||
// preceding sequence of spaces with a tab. | |||||
nextStop = col + (tablength - col % tablength); | |||||
if (nextStop - col < 2) { | |||||
linebuf2.append(linestring.substring( | |||||
place, nextStop - placediff)); | |||||
place = nextStop - placediff; | |||||
nextStop += tablength; | |||||
} | |||||
for (; nextStop - placediff <= linestring.length(); | |||||
nextStop += tablength) { | |||||
for (tabCol = nextStop; | |||||
--tabCol - placediff >= place | |||||
&& linestring.charAt(tabCol - placediff) == ' ';) { | |||||
; // Loop for the side-effects | |||||
} | |||||
// tabCol is column index of the last non-space character | |||||
// before the next tab stop | |||||
if (nextStop - tabCol > 2) { | |||||
linebuf2.append(linestring.substring( | |||||
place, ++tabCol - placediff)); | |||||
linebuf2.append('\t'); | |||||
} else { | |||||
linebuf2.append(linestring.substring( | |||||
place, nextStop - placediff)); | |||||
} // end of else | |||||
place = nextStop - placediff; | |||||
} // end of for (nextStop ... ) | |||||
// pick up that last bit, if any | |||||
linebuf2.append(linestring.substring(place, linestring.length())); | |||||
try { | |||||
outWriter.write(linebuf2.substring(0)); | |||||
} catch (IOException e) { | |||||
throw new BuildException(e); | |||||
} // end of try-catch | |||||
} // end of else tabs == ADD | |||||
// Set column position as modified by this method | |||||
bufline.setColumn(bufline.getColumn() + linestring.length()); | |||||
bufline.setNext(end); | |||||
} | |||||
} | } | ||||
protected class OneLiner implements Enumeration { | protected class OneLiner implements Enumeration { | ||||
private static final int UNDEF = -1; | |||||
private static final int NOTJAVA = 0; | |||||
private static final int LOOKING = 1; | |||||
private static final int INBUFLEN = 8192; | |||||
private static final int LINEBUFLEN = 200; | |||||
private static final char CTRLZ = '\u001A'; | |||||
private int state = javafiles ? LOOKING : NOTJAVA; | |||||
private int state = filter.getJavafiles() ? LOOKING : NOTJAVA; | |||||
private StringBuffer eolStr = new StringBuffer(LINEBUFLEN); | private StringBuffer eolStr = new StringBuffer(LINEBUFLEN); | ||||
private StringBuffer eofStr = new StringBuffer(); | private StringBuffer eofStr = new StringBuffer(); | ||||
@@ -794,8 +374,11 @@ public class FixCRLF extends MatchingTask { | |||||
throws BuildException { | throws BuildException { | ||||
this.srcFile = srcFile; | this.srcFile = srcFile; | ||||
try { | try { | ||||
reader = new BufferedReader | |||||
(getReader(srcFile), INBUFLEN); | |||||
reader = new BufferedReader( | |||||
((encoding == null) ? new FileReader(srcFile) | |||||
: new InputStreamReader( | |||||
new FileInputStream(srcFile), encoding)), INBUFLEN); | |||||
nextLine(); | nextLine(); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new BuildException(srcFile + ": " + e.getMessage(), | throw new BuildException(srcFile + ": " + e.getMessage(), | ||||
@@ -201,6 +201,42 @@ public class FixCrLfTest extends BuildFileTest { | |||||
executeTarget("testPreserveLastModified"); | executeTarget("testPreserveLastModified"); | ||||
} | } | ||||
public void testFilter1() { | |||||
executeTarget("testFilter1"); | |||||
} | |||||
public void testFilter2() { | |||||
executeTarget("testFilter2"); | |||||
} | |||||
public void testFilter3() { | |||||
executeTarget("testFilter3"); | |||||
} | |||||
public void testFilter4() { | |||||
executeTarget("testFilter4"); | |||||
} | |||||
public void testFilter5() { | |||||
executeTarget("testFilter5"); | |||||
} | |||||
public void testFilter6() { | |||||
executeTarget("testFilter6"); | |||||
} | |||||
public void testFilter7() { | |||||
executeTarget("testFilter7"); | |||||
} | |||||
public void testFilter8() { | |||||
executeTarget("testFilter8"); | |||||
} | |||||
public void testFilter9() { | |||||
executeTarget("testFilter9"); | |||||
} | |||||
public void assertEqualContent(File expect, File result) | public void assertEqualContent(File expect, File result) | ||||
throws AssertionFailedError, IOException { | throws AssertionFailedError, IOException { | ||||
if (!result.exists()) { | if (!result.exists()) { | ||||