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 | |||
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 | |||
===================================================== | |||
@@ -9,170 +9,202 @@ | |||
<h2><a name="fixcrlf">FixCRLF</a></h2> | |||
<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> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<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> | |||
<td valign="top">srcDir</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> | |||
<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> | |||
<td valign="top">destDir</td> | |||
<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 bgcolor="#CCCCCC"><nbsp /></td> | |||
</tr> | |||
<tr> | |||
<td valign="top">includes</td> | |||
<td valign="top">comma- or space-separated list of patterns of files that must be | |||
included. All files are included when omitted.</td> | |||
<td valign="top" align="center">No</td> | |||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||
</tr> | |||
<tr> | |||
<td valign="top">includesfile</td> | |||
<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 bgcolor="#CCCCCC"><nbsp /></td> | |||
</tr> | |||
<tr> | |||
<td valign="top">excludes</td> | |||
<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> | |||
<td valign="top" align="center">No</td> | |||
<td bgcolor="#CCCCCC"><nbsp /></td> | |||
</tr> | |||
<tr> | |||
<td valign="top">excludesfile</td> | |||
<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 bgcolor="#CCCCCC"><nbsp /></td> | |||
</tr> | |||
<tr> | |||
<td valign="top">defaultexcludes</td> | |||
<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 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> | |||
<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">Specifies how tab characters are to be handled. Valid | |||
values for this property are: | |||
@@ -182,28 +214,28 @@ supports all attributes of <code><fileset></code> | |||
<li>remove: convert tabs to spaces</li> | |||
</ul> | |||
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> | |||
<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> | |||
<td valign="top">tablength</td> | |||
<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> | |||
<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 | |||
(including Windows), the default is asis. | |||
</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> | |||
<td valign="top">fixlast</td> | |||
<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> | |||
</table> | |||
<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 | |||
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. | |||
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 | |||
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 | |||
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> | |||
<p align="center">Copyright © 2000-2005 The Apache Software Foundation. All rights | |||
Reserved.</p> | |||
@@ -194,4 +194,100 @@ | |||
<fail unless="fs" /> | |||
</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> |
@@ -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"); | |||
* you may not use this file except in compliance with the License. | |||
@@ -17,24 +17,22 @@ | |||
package org.apache.tools.ant.taskdefs; | |||
import java.io.BufferedReader; | |||
import java.io.BufferedWriter; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.FileOutputStream; | |||
import java.io.Reader; | |||
import java.io.FileReader; | |||
import java.io.FileWriter; | |||
import java.io.IOException; | |||
import java.io.BufferedReader; | |||
import java.io.FileInputStream; | |||
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.NoSuchElementException; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.BuildException; | |||
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.util.FileUtils; | |||
@@ -82,53 +80,23 @@ import org.apache.tools.ant.util.FileUtils; | |||
* @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 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 File srcDir; | |||
private File destDir = null; | |||
private File file; | |||
private FixCrLfFilter filter = new FixCrLfFilter(); | |||
/** | |||
* Encoding to assume for the files | |||
*/ | |||
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. | |||
@@ -137,24 +105,21 @@ public class FixCRLF extends MatchingTask { | |||
* <li>DOS: eol="CRLF" tab="asis" eof="asis"</ul> | |||
*/ | |||
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. | |||
* @param srcDir the source directory. | |||
*/ | |||
public void setSrcdir(File srcDir) { | |||
this.srcDir = srcDir; | |||
@@ -163,6 +128,7 @@ public class FixCRLF extends MatchingTask { | |||
/** | |||
* Set the destination where the fixed files should be placed. | |||
* Default is to replace the original file. | |||
* @param destDir the destination directory. | |||
*/ | |||
public void setDestdir(File destDir) { | |||
this.destDir = destDir; | |||
@@ -170,15 +136,16 @@ public class FixCRLF extends MatchingTask { | |||
/** | |||
* Set to true if modifying Java source files. | |||
* @param javafiles whether modifying Java files. | |||
*/ | |||
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) { | |||
this.file = file; | |||
@@ -196,20 +163,7 @@ public class FixCRLF extends MatchingTask { | |||
* </ul> | |||
*/ | |||
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> | |||
*/ | |||
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, | |||
*/ | |||
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> | |||
*/ | |||
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) { | |||
this.encoding = encoding; | |||
@@ -314,13 +248,15 @@ public class FixCRLF extends MatchingTask { | |||
/** | |||
* Specify whether a missing EOL will be added | |||
* to the final line of a file. | |||
* @param fixlast whether to fix the last line. | |||
*/ | |||
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 | |||
*/ | |||
public void setPreserveLastModified(boolean preserve) { | |||
@@ -333,14 +269,14 @@ public class FixCRLF extends MatchingTask { | |||
public void execute() throws BuildException { | |||
// 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); | |||
} | |||
//patch file into the fileset | |||
fileset.setFile(file); | |||
//set our parent dir | |||
srcDir=file.getParentFile(); | |||
srcDir = file.getParentFile(); | |||
} | |||
if (srcDir == null) { | |||
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!"); | |||
} | |||
} | |||
// log options used | |||
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), | |||
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 { | |||
File srcFile = new File(srcDir, file); | |||
long lastModified = srcFile.lastModified(); | |||
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 { | |||
// 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); | |||
@@ -556,15 +334,11 @@ public class FixCRLF extends MatchingTask { | |||
if (destFile.exists()) { | |||
// Compare the destination with the temp file | |||
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) { | |||
FILE_UTILS.rename(tmpFile, destFile); | |||
if (preserveLastModified) { | |||
@@ -573,214 +347,20 @@ public class FixCRLF extends MatchingTask { | |||
} | |||
tmpFile = null; | |||
} | |||
} catch (IOException 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 { | |||
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 eofStr = new StringBuffer(); | |||
@@ -794,8 +374,11 @@ public class FixCRLF extends MatchingTask { | |||
throws BuildException { | |||
this.srcFile = srcFile; | |||
try { | |||
reader = new BufferedReader | |||
(getReader(srcFile), INBUFLEN); | |||
reader = new BufferedReader( | |||
((encoding == null) ? new FileReader(srcFile) | |||
: new InputStreamReader( | |||
new FileInputStream(srcFile), encoding)), INBUFLEN); | |||
nextLine(); | |||
} catch (IOException e) { | |||
throw new BuildException(srcFile + ": " + e.getMessage(), | |||
@@ -201,6 +201,42 @@ public class FixCrLfTest extends BuildFileTest { | |||
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) | |||
throws AssertionFailedError, IOException { | |||
if (!result.exists()) { | |||