diff --git a/WHATSNEW b/WHATSNEW index 55015e1b0..a3642ce49 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -132,6 +132,8 @@ Other changes: * New condition 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 ===================================================== diff --git a/docs/manual/CoreTasks/fixcrlf.html b/docs/manual/CoreTasks/fixcrlf.html index a33166497..c2383bb8d 100644 --- a/docs/manual/CoreTasks/fixcrlf.html +++ b/docs/manual/CoreTasks/fixcrlf.html @@ -9,170 +9,202 @@

FixCRLF

Description

-

- Adjusts a text file to local conventions. -

+

+ Adjusts a text file to local conventions. +

-

- The set of files to be adjusted can be refined with the - includes, includesfile, excludes, - excludesfile and defaultexcludes - attributes. Patterns provided through the includes or - includesfile attributes specify files to be - included. Patterns provided through the exclude or - excludesfile attribute specify files to be - excluded. Additionally, default exclusions can be specified with - the defaultexcludes attribute. See the section on directory based - tasks, for details of file inclusion/exclusion patterns - and their usage. -

+

+ The set of files to be adjusted can be refined with the + includes, includesfile, excludes, + excludesfile and defaultexcludes + attributes. Patterns provided through the includes or + includesfile attributes specify files to be + included. Patterns provided through the exclude or + excludesfile attribute specify files to be + excluded. Additionally, default exclusions can be specified with + the defaultexcludes attribute. See the section on directory-based + tasks, for details of file inclusion/exclusion patterns + and their usage. +

-

This task forms an implicit FileSet and -supports all attributes of <fileset> -(dir becomes srcdir) as well as the nested -<include>, <exclude> and -<patternset> elements.

+

+ This task forms an implicit + FileSet and + supports all attributes of <fileset> + (dir becomes srcdir) as well as the nested + <include>, <exclude> and + <patternset> elements. +

-

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

+

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

+ +

+ Since Ant 1.7, this task can be used in a + filterchain. +

Parameters

- - - + + + + + + + - + + - - + + - + srcDir (replacing the original file). + + + taken to be an include pattern. + + + taken to be an exclude pattern. + + ("yes"/"no"). Default excludes are used when omitted. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + - + N.B.: When the attribute + "javafiles" (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. +

+

+ N.B.: There is an incompatibility between this + and the previous version in the handling of white + space at the end of lines. This version does + not remove trailing whitespace on lines. +

+ + - + 2 and 80 inclusive. The default for this parameter is 8. + @@ -218,76 +250,52 @@ supports all attributes of <fileset> For Unix platforms, the default is remove. For DOS based systems (including Windows), the default is asis. - - - - - - + - - - - - - + of a processed file. Since Ant 1.6.1 +
AttributeDescriptionRequiredAttributeDescriptionRequired
As TaskAs Filter
srcDir Where to find the files to be fixed up.Either file or srcDirOne of these
fileName of a single file to fix. Since Ant1.7Either file or srcDirName of a single file to fix. Since Ant 1.7
destDir Where to place the corrected files. Defaults to - srcDir (replacing the original file) No
includes comma- or space-separated list of patterns of files that must be included. All files are included when omitted. No
includesfile the name of a file. Each line of this file is - taken to be an include pattern No
excludes comma- or space-separated list of patterns of files that must be excluded. No files (except default excludes) are excluded when omitted. No
excludesfile the name of a file. Each line of this file is - taken to be an exclude pattern No
defaultexcludes indicates whether default excludes should be used or not - ("yes"/"no"). Default excludes are used when omitted. No
encodingThe encoding of the files.No; defaults to default JVM encoding.
preservelastmodifiedWhether to preserve the last modified + date of source files. Since Ant 1.6.3No; default is false
eol + 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: +
    +
  • asis: leave EOL characters alone
  • +
  • cr: convert all EOLs to a single CR
  • +
  • lf: convert all EOLs to a single LF
  • +
  • crlf: convert all EOLs to the pair CRLF
  • +
  • mac: convert all EOLs to a single CR
  • +
  • unix: convert all EOLs to a single LF
  • +
  • dos: convert all EOLs to the pair CRLF
  • +
+ 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". +

+ This is the preferred method for specifying EOL. The + "cr" attribute (see below) is + now deprecated. +

+

+ N.B.: 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. +

+
No
cr + Deprecated. Specifies how CR characters are + to be handled at end-of-line (EOL). Valid values for this + property are: +
    +
  • asis: leave EOL characters alone.
  • +
  • + add: add a CR before any single LF characters. The + intent is to convert all EOLs to the pair CRLF. +
  • +
  • + remove: remove all CRs from the file. The intent is + to convert all EOLs to a single LF. +
  • +
+ 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". +

+ N.B.: 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. +

+
No
javafiles + Used only in association with the + "tab" 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". + No
eol - 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: -
    -
  • asis: leave EOL characters alone
  • -
  • cr: convert all EOLs to a single CR
  • -
  • lf: convert all EOLs to a single LF
  • -
  • crlf: convert all EOLs to the pair CRLF
  • -
  • mac: convert all EOLs to a single CR
  • -
  • unix: convert all EOLs to a single LF
  • -
  • dos: convert all EOLs to the pair CRLF
  • -
- 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". -

- This is the preferred method for specifying EOL. The - "cr" attribute (see below) is - now deprecated. -

-

- N.B.: 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. -

-
No
cr - Deprecated. Specifies how CR characters are - to be handled at end-of-line (EOL). Valid values for this - property are: -
    -
  • asis: leave EOL characters alone.
  • -
  • - add: add a CR before any single LF characters. The - intent is to convert all EOLs to the pair CRLF. -
  • -
  • - remove: remove all CRs from the file. The intent is - to convert all EOLs to a single LF. -
  • -
- 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". -

- N.B.: 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. -

-
No
javafiles - Used only in association with the - "tab" 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". - No
tab Specifies how tab characters are to be handled. Valid values for this property are: @@ -182,28 +214,28 @@ supports all attributes of <fileset>
  • remove: convert tabs to spaces
  • Default for this parameter is "asis". -

    - N.B.: When the attribute - "javafiles" (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. -

    - N.B.: There is an incompatibility between this - and the previous version in the handling of white - space at the end of lines. This version does - not remove trailing whitespace on lines. -

    -
    NoNo
    tablength TAB character interval. Valid values are between - 2 and 80 inclusive. The default for this parameter is 8.NoNo
    eofNo
    encodingThe encoding of the filesNo - defaults to default JVM encodingNo
    fixlast Whether to add a missing EOL to the last line - of a processed file. (Since ant 1.6.1)No - default is true
    preservelastmodifiedWhether to preserve the last modified - date of source files. Since ant 1.6.3No; default is falseNo; default is true

    Examples

    -
      <fixcrlf srcdir="${src}"
    -       eol="lf" 
    -       eof="remove"
    -       includes="**/*.sh"
    -  />
    +
    <fixcrlf srcdir="${src}" includes="**/*.sh"
    +         eol="lf" eof="remove" />

    Replaces EOLs with LF characters and removes eof characters from - the shell scripts. Tabs and spaces are left as is.

    -
      <fixcrlf srcdir="${src}"
    -       eol="crlf"
    -       includes="**/*.bat"
    -  />
    + the shell scripts. Tabs and spaces are left as is.

    +
    <fixcrlf srcdir="${src}"
    +         includes="**/*.bat" eol="crlf" />

    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.

    -
      <fixcrlf srcdir="${src}"
    -       tab="add"
    -       includes="**/Makefile"
    -  />
    + 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.

    +
    <fixcrlf srcdir="${src}"
    +         includes="**/Makefile" tab="add" />

    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.

    -
      <fixcrlf srcdir="${src}"
    -       tab="remove"
    -       tablength="3"
    -       eol="lf"
    -       javafiles="yes"
    -       includes="**/*.java"
    -  />
    -

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

    -
      <fixcrlf srcdir="${src}"
    -       tab="remove"
    -       includes="**/README*"
    -  />
    + 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.

    +
    <fixcrlf srcdir="${src}" includes="**/*.java"
    +         tab="remove" tablength="3"
    +         eol="lf" javafiles="yes" />
    +

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

    +
    <fixcrlf srcdir="${src}"
    +         includes="**/README*" tab="remove" />

    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.

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


    Copyright © 2000-2005 The Apache Software Foundation. All rights Reserved.

    diff --git a/src/etc/testcases/taskdefs/fixcrlf/build.xml b/src/etc/testcases/taskdefs/fixcrlf/build.xml index 8fdf45795..57c6272b4 100644 --- a/src/etc/testcases/taskdefs/fixcrlf/build.xml +++ b/src/etc/testcases/taskdefs/fixcrlf/build.xml @@ -194,4 +194,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/org/apache/tools/ant/filters/FixCrLfFilter.java b/src/main/org/apache/tools/ant/filters/FixCrLfFilter.java new file mode 100755 index 000000000..04fc7ff9a --- /dev/null +++ b/src/main/org/apache/tools/ant/filters/FixCrLfFilter.java @@ -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. + *

    + * This filter can take the following arguments: + *

    + * None of which are required. + *

    + * 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. + *

    + * Caution: 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. + * + *

    Example: + *

    <
    + *
    + * Or:
    + *
    + * 
    <filterreader classname="org.apache.tools.ant.filters.FixCrLfFilter">
    + *  <param eol="crlf" tab="asis"/>
    + * </filterreader>
    + * + */ +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 null. + */ + 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 null. + * + * @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: + *
      + *
    • add: ensure that there is an eof at the end of the file + *
    • asis: leave eof characters alone + *
    • remove: remove any eof character found at the end + *
    + */ + 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: + *
      + *
    • asis: convert line endings to your O/S convention + *
    • cr: convert line endings to CR + *
    • lf: convert line endings to LF + *
    • crlf: convert line endings to CRLF + *
    + */ + 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. + *

    + * This attribute is only used in assocation with the + * "tab" 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: + *

      + *
    • add: convert sequences of spaces which span a tab stop to tabs + *
    • asis: leave tab and space characters alone + *
    • remove: convert tabs to spaces + *
    + */ + 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: + *
      + *
    • add: ensure that there is an eof at the end of the file + *
    • asis: leave eof characters alone + *
    • remove: remove any eof character found at the end + *
    + */ + public void setEof(AddAsisRemove attr) { + ctrlz = attr.resolve(); + } + + /** + * Specify how end of line (EOL) characters are to be handled. + * + * @param attr valid values: + *
      + *
    • asis: convert line endings to your O/S convention + *
    • cr: convert line endings to CR + *
    • lf: convert line endings to LF + *
    • crlf: convert line endings to CRLF + *
    + */ + 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 + * "tab" 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: + *
      + *
    • add: convert sequences of spaces which span a tab stop to tabs + *
    • asis: leave tab and space characters alone + *
    • remove: convert tabs to spaces + *
    + */ + 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. + * + *

    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.

    + */ + 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; + } + } +} diff --git a/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java b/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java index db6df3fdd..732521f31 100644 --- a/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java +++ b/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java @@ -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 { *
  • DOS: eol="CRLF" tab="asis" eof="asis" */ 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 { * */ 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 { * */ 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 { * */ 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(), diff --git a/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java b/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java index 2653bbedf..1847d72a5 100644 --- a/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java @@ -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()) {