diff --git a/WHATSNEW b/WHATSNEW index e56b1b100..7624549a1 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -138,6 +138,9 @@ Other changes: * Two new supported compilers for javac: kjc for kopi and gcj for the gcc frontend. +* can now with CR only line-ends and can use an arbitraty + between 2 and 80. + Fixed bugs: ----------- @@ -211,6 +214,8 @@ Fixed bugs: * directory based tasks and fileset could miss some included files in directories that have been excluded +* failed for large files. + Changes from Ant 1.2 to Ant 1.3 =========================================== diff --git a/build.xml b/build.xml index f6ebd94a9..dc96ef117 100644 --- a/build.xml +++ b/build.xml @@ -228,6 +228,7 @@ + @@ -304,8 +305,8 @@ - - + + @@ -433,8 +434,8 @@ - - + + diff --git a/src/etc/testcases/taskdefs/fixcrlf/build.xml b/src/etc/testcases/taskdefs/fixcrlf/build.xml new file mode 100644 index 000000000..66a3369b5 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/build.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/etc/testcases/taskdefs/fixcrlf/expected/Junk1.java b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk1.java new file mode 100644 index 000000000..7321b8dc3 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk1.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/expected/Junk2.java b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk2.java new file mode 100644 index 000000000..7321b8dc3 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk2.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/expected/Junk3.java b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk3.java new file mode 100644 index 000000000..3427166e6 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk3.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/expected/Junk4.java b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk4.java new file mode 100644 index 000000000..3427166e6 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk4.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/expected/Junk5.java b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk5.java new file mode 100644 index 000000000..bba01a5d6 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk5.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/expected/Junk6.java b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk6.java new file mode 100644 index 000000000..7321b8dc3 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk6.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/expected/Junk7.java b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk7.java new file mode 100644 index 000000000..7321b8dc3 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk7.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/expected/Junk8.java b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk8.java new file mode 100644 index 000000000..82a8e84bc --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk8.java @@ -0,0 +1,13 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} + \ No newline at end of file diff --git a/src/etc/testcases/taskdefs/fixcrlf/expected/Junk9.java b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk9.java new file mode 100644 index 000000000..3427166e6 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/expected/Junk9.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/input/Junk1.java b/src/etc/testcases/taskdefs/fixcrlf/input/Junk1.java new file mode 100644 index 000000000..ad697473f --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/input/Junk1.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/input/Junk2.java b/src/etc/testcases/taskdefs/fixcrlf/input/Junk2.java new file mode 100644 index 000000000..ad697473f --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/input/Junk2.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/input/Junk3.java b/src/etc/testcases/taskdefs/fixcrlf/input/Junk3.java new file mode 100644 index 000000000..ad697473f --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/input/Junk3.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/input/Junk4.java b/src/etc/testcases/taskdefs/fixcrlf/input/Junk4.java new file mode 100644 index 000000000..3427166e6 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/input/Junk4.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/input/Junk5.java b/src/etc/testcases/taskdefs/fixcrlf/input/Junk5.java new file mode 100644 index 000000000..3427166e6 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/input/Junk5.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/input/Junk6.java b/src/etc/testcases/taskdefs/fixcrlf/input/Junk6.java new file mode 100644 index 000000000..3427166e6 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/input/Junk6.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/input/Junk7.java b/src/etc/testcases/taskdefs/fixcrlf/input/Junk7.java new file mode 100644 index 000000000..3427166e6 --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/input/Junk7.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/input/Junk8.java b/src/etc/testcases/taskdefs/fixcrlf/input/Junk8.java new file mode 100644 index 000000000..ad697473f --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/input/Junk8.java @@ -0,0 +1,12 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} diff --git a/src/etc/testcases/taskdefs/fixcrlf/input/Junk9.java b/src/etc/testcases/taskdefs/fixcrlf/input/Junk9.java new file mode 100644 index 000000000..82a8e84bc --- /dev/null +++ b/src/etc/testcases/taskdefs/fixcrlf/input/Junk9.java @@ -0,0 +1,13 @@ +public class Junk1 { + public boolean mybool; + public static void main (String[] args) { + System.out.println("I have a tab"); + // Here is a comment with an embedded tab + if (mybool) { /* Here is a multi-line + (with embedded' 'tab) + Comment */char mychar = ' '; //<-tab-> <- + } // end of if (mybool) + + } // end of main () +} + \ No newline at end of file diff --git a/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java b/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java index 36d880f54..13f595a8f 100644 --- a/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java +++ b/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java @@ -61,7 +61,6 @@ import org.apache.tools.ant.types.EnumeratedAttribute; import java.io.*; import java.util.*; -import java.text.*; /** * Task to convert text source files to local OS formatting conventions, as @@ -75,6 +74,7 @@ import java.text.*; *
  • include *
  • exclude *
  • cr + *
  • eol *
  • tab *
  • eof * @@ -83,39 +83,87 @@ import java.text.*; * When this task executes, it will scan the srcdir based on the include * and exclude properties. *

    - * Warning: do not run on binary or carefully formatted files. - * this may sound obvious, but if you don't specify asis, presume that - * your files are going to be modified. If you want tabs to be fixed, + * 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 will leave untouched any + * literal TAB characters embedded within string or character constants. + *

    + * Warning: do not run on binary files. + * 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 CR's - in fact cr="add" can result in cr characters being removed. - * (to handle cases where other programs have converted CRLF into CRCRLF). + * for CR's - in fact "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. * * @author Sam Ruby rubys@us.ibm.com + * @author Peter B. West + * @version $Revision$ $Name$ */ public class FixCRLF extends MatchingTask { - private int addcr; // cr: -1 => remove, 0 => asis, +1 => add - private int addtab; // tab: -1 => remove, 0 => asis, +1 => add - private int ctrlz; // eof: -1 => remove, 0 => asis, +1 => add - private int tablength = 8; // length of tab in spaces + 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; + + private static final char CTRLZ = '\u001A'; + + 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 File srcDir; private File destDir = null; /** * Defaults the properties based on the system type. - *

    • Unix: cr="remove" tab="asis" eof="remove" - *
    • DOS: cr="add" tab="asis" eof="asis"
    + *
    • Unix: eol="LF" tab="asis" eof="remove" + *
    • Mac: eol="CR" tab="asis" eof="remove" + *
    • DOS: eol="CRLF" tab="asis" eof="asis"
    */ - public FixCRLF() { - if (System.getProperty("path.separator").equals(":")) { - addcr = -1; // remove - ctrlz = -1; // remove - } else { - addcr = +1; // add - ctrlz = 0; // asis - } + public FixCRLF () { + tabs = ASIS; + if (System.getProperty("path.separator").equals(":")) { + ctrlz = REMOVE; + if (System.getProperty("os.name").indexOf("Mac") > -1) { + eol = CR; + eolstr = "\r"; + } else { + eol = LF; + eolstr = "\n"; + } + } + else { + ctrlz = ASIS; + eol = CRLF; + eolstr = "\r\n"; + } } /** @@ -134,7 +182,43 @@ public class FixCRLF extends MatchingTask { } /** - * Specify how carriage return (CR) charaters are to be handled + * Fixing Java source files? + */ + public void setJavafiles(boolean javafiles) { + this.javafiles = javafiles; + } + + + /** + * Specify how EndOfLine characters are to be handled + * + * @param option valid values: + *
      + *
    • asis: leave line endings alone + *
    • cr: convert line endings to CR + *
    • lf: convert line endings to LF + *
    • crlf: convert line endings to CRLF + *
    + */ + public void setEol(CrLf attr) { + String option = attr.getValue(); + if (option.equals("asis")) { + eol = ASIS; + } else if (option.equals("cr")) { + eol = CR; + eolstr = "\r"; + } else if (option.equals("lf")) { + eol = LF; + eolstr = "\n"; + } else { + // Must be "crlf" + eol = CRLF; + eolstr = "\r\n"; + } + } + + /** + * Specify how carriage return (CR) characters are to be handled * * @param option valid values: *
      @@ -142,21 +226,28 @@ public class FixCRLF extends MatchingTask { *
    • asis: leave CR characters alone *
    • remove: remove all CR characters *
    + * + * @deprecated use {@link #setEol setEol} instead. */ public void setCr(AddAsisRemove attr) { + log("DEPRECATED: The cr attribute has been deprecated,", + Project.MSG_WARN); + log("Please us the eol attribute instead", Project.MSG_WARN); String option = attr.getValue(); + CrLf c = new CrLf(); if (option.equals("remove")) { - addcr = -1; + c.setValue("lf"); } else if (option.equals("asis")) { - addcr = 0; + c.setValue("asis"); } else { // must be "add" - addcr = +1; + c.setValue("crlf"); } + setEol(c); } /** - * Specify how tab charaters are to be handled + * Specify how tab characters are to be handled * * @param option valid values: *
      @@ -168,26 +259,31 @@ public class FixCRLF extends MatchingTask { public void setTab(AddAsisRemove attr) { String option = attr.getValue(); if (option.equals("remove")) { - addtab = -1; + tabs = SPACES; } else if (option.equals("asis")) { - addtab = 0; + tabs = ASIS; } else { // must be "add" - addtab = +1; + tabs = TABS; } } /** * Specify tab length in characters * - * @param tlength specify the length of tab in spaces, has to be a power of 2 + * @param tlength specify the length of tab in spaces, */ public void setTablength(int tlength) throws BuildException { - if (tlength < 2 || (tlength & (tlength-1)) != 0) { - throw new BuildException("tablength must be a positive power of 2", - location); + if (tlength < 2 || tlength >80) { + throw new BuildException("tablength must be between 2 and 80", + location); } tablength = tlength; + StringBuffer sp = new StringBuffer(); + for (int i = 0; i < tablength; i++) { + sp.append(' '); + } + spaces = sp.toString(); } /** @@ -203,12 +299,12 @@ public class FixCRLF extends MatchingTask { public void setEof(AddAsisRemove attr) { String option = attr.getValue(); if (option.equals("remove")) { - ctrlz = -1; + ctrlz = REMOVE; } else if (option.equals("asis")) { - ctrlz = 0; + ctrlz = ASIS; } else { // must be "add" - ctrlz = +1; + ctrlz = ADD; } } @@ -238,9 +334,10 @@ public class FixCRLF extends MatchingTask { // log options used log("options:" + - " cr=" + (addcr==1 ? "add" : addcr==0 ? "asis" : "remove") + - " tab=" + (addtab==1 ? "add" : addtab==0 ? "asis" : "remove") + - " eof=" + (ctrlz==1 ? "add" : ctrlz==0 ? "asis" : "remove") + + " 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, Project.MSG_VERBOSE); @@ -248,174 +345,610 @@ public class FixCRLF extends MatchingTask { String[] files = ds.getIncludedFiles(); for (int i = 0; i < files.length; i++) { - File srcFile = new File(srcDir, files[i]); + processFile(files[i]); + } + } + + /** + * Creates a temporary file. + */ + private File createTempFile() { + String name = "fixcrlf" + + (new Random(System.currentTimeMillis())).nextLong(); + if (destDir == null) { + return new File(srcDir, name); + } else { + return new File(destDir, name); + } + } + + private void processFile(String file) throws BuildException { + File srcFile = new File(srcDir, file); + File tmpFile = null; + BufferedWriter outWriter; + OneLiner.BufferLine line; - // read the contents of the file - int count = (int)srcFile.length(); - byte indata[] = new byte[count]; + // read the contents of the file + OneLiner lines = new OneLiner(srcFile); + + try { + // Set up the output Writer try { - FileInputStream inStream = new FileInputStream(srcFile); - inStream.read(indata); - inStream.close(); + tmpFile = createTempFile(); + FileWriter writer = new FileWriter(tmpFile); + 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) + int ptr; + + while ((ptr = line.getNext()) < linelen) { + + switch (lines.getState()) { + + case NOTJAVA: + notInConstant(line, line.length(), outWriter); + break; + + case IN_MULTI_COMMENT: + if ((endComment = + lineString.indexOf("*/", line.getNext()) + ) >= 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) + + try { + outWriter.write(eolstr); + } catch (IOException e) { + throw new BuildException(e); + } // end of try-catch + + } // end of while (lines.hasNext()) - // count the number of cr, lf, and tab characters - int cr = 0; - int lf = 0; - int tab = 0; - - for (int k=0; k0) && (indata[count-1] == 0x1A)); - - // log stats (before fixes) - log(srcFile + ": size=" + count + " cr=" + cr + - " lf=" + lf + " tab=" + tab + " eof=" + eof, - Project.MSG_VERBOSE); - - // determine the output buffer size (slightly pessimisticly) - int outsize = count; - if (addcr != 0) outsize-=cr; - if (addcr == +1) outsize+=lf; - if (addtab == -1) outsize+=tab*(tablength-1); - if (ctrlz == +1) outsize+=1; - - // copy the data - byte outdata[] = new byte[outsize]; - int o = 0; // output offset - int line = o; // beginning of line - int col = 0; // desired column - - for (int k=0; k0 && o+12 && outdata[o-1]==0x0A && outdata[o-2]==0x1A) o--; - if (o>1 && outdata[o-1]==0x1A) o--; + } finally { + try { + lines.close(); + } catch (IOException io) { + log("Error closing "+srcFile, Project.MSG_ERR); + } // end of catch + + if (tmpFile != null) { + tmpFile.delete(); } + } // end of finally + } - // output the data - try { - // Determine whether it should be written, - // that is if it is different than the potentially already existing file - boolean write = false; - byte[] existingdata = indata; - File destFile = srcFile; - if (destDir != null) { - destFile = new File(destDir, files[i]); - if(destFile.isFile()) { - int len = (int)destFile.length(); - if(len != o) { - write = true; - } else { - existingdata = new byte[len]; - try { - FileInputStream in = new FileInputStream(destFile); - in.read(existingdata); - in.close(); - } catch (IOException e) { - throw new BuildException(e); - } - } - } else { - write = true; + /** + * 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 BufferLine 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 eol = bufline.length(); + int ptr = bufline.getNext(); + + + // Look for next single or double quote, double slash or slash star + while (ptr < eol) { + 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 < eol) { + 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 < eol) + // Eol is the next token + bufline.setLookahead(ptr); + } - if(!write) { - if(existingdata.length != o) { - write = true; - } else { - for(int j = 0; j < o; ++j) { - if(existingdata[j] != outdata[j]) { - write = true; - break; - } - } - } + + /** + * 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 BufferLine bufline BufferLine containing the string + * to be processed + * @param char 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 eol = bufline.length(); + char c; + ptr++; // skip past initial quote + while (ptr < eol) { + if ((c = bufline.getChar(ptr++)) == '\\') { + ptr++; + } + else { + if (c == terminator) { + bufline.setLookahead(ptr); + return; } + } + } // end of while (ptr < eol) + // Must have fallen through to the end of the line + throw new BuildException("endOfCharConst: unterminated char constant"); + } + - if(write) { - log(destFile + " is being written", Project.MSG_VERBOSE); - FileOutputStream outStream = new FileOutputStream(destFile); - outStream.write(outdata,0,o); - outStream.close(); - } else { - log(destFile + " is not written, as the contents are identical", - Project.MSG_VERBOSE); + /** + * Process a BufferLine string which is not part of 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 BufferLine bufline BufferLine containing the string + * to be processed + * @param int end Index just past the end of the + * string + * @param BufferedWriter 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.setLength(0); + 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.toString()); + if (tabs == REMOVE) { + try { + outWriter.write(linestring); + } catch (IOException e) { + throw new BuildException(e); + } // end of try-catch + } + else { // tabs == ADD + int tabCol; + linebuf2.setLength(0); + 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.toString()); } 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); + + } + + + class OneLiner implements Enumeration { + + private int state = javafiles ? LOOKING : NOTJAVA; + + private StringBuffer eolStr = new StringBuffer(LINEBUFLEN); + private StringBuffer eofStr = new StringBuffer(); + + private BufferedReader reader; + private String line; + + public OneLiner(File srcFile) + throws BuildException + { + try { + reader = new BufferedReader + (new FileReader(srcFile), INBUFLEN); + nextLine(); + } catch (IOException e) { + throw new BuildException(e); + } + } + + protected void nextLine() + throws BuildException { + int ch; + int eolcount = 0; + + eolStr.setLength(0); + + try { + int linelen; + + reader.mark(INBUFLEN); + line = reader.readLine(); + if (line == null) { + // Eof has been reached + linelen = 0; + } + else { + linelen = line.length(); + } + + + // Find the EOL character(s) + + reader.reset(); + + // an IOException will be thrown + reader.skip((long)linelen); + reader.mark(INBUFLEN); + ch = reader.read(); + switch ((char) ch) { + case '\r': + // Check for \r, \r\n and \r\r\n + // Regard \r\r not followed by \n as two lines + ++eolcount; + eolStr.append('\r'); + switch ((char)(ch = reader.read())) { + case '\r': + if ((char)(ch = reader.read()) == '\n') { + eolcount += 2; + eolStr.append("\r\n"); + } + break; + case '\n': + ++eolcount; + eolStr.append('\n'); + break; + } // end of switch ((char)(ch = reader.read())) + break; + + case '\n': + ++eolcount; + eolStr.append('\n'); + break; + + } // end of switch ((char) ch) + + // Reset the position of the file reader + reader.reset(); + reader.skip((long)eolcount); + + // if at eolcount == 0 and trailing characters of string + // are CTRL-Zs, set eofStr + if (line != null && eolcount == 0) { + int i = linelen; + while (--i >= 0 && line.charAt(i) == CTRLZ) {} + if (i < linelen - 1) { + // Trailing characters are ^Zs + // Construct new line and eofStr + eofStr.append(line.substring(i + 1)); + line = i < 0 ? null : line.substring(0, i + 1); + } + + } // end of if (eolcount == 0) + + } catch (IOException e) { + throw new BuildException(e); + } + } + + public String getEofStr() { + return eofStr.toString(); + } + + public int getState() { + return state; + } + + public void setState(int state) { + this.state = state; + } + + public boolean hasMoreElements() + { + return line != null; + } + + public Object nextElement() + throws NoSuchElementException + { + if (! hasMoreElements()) { + throw new NoSuchElementException("OneLiner"); + } + BufferLine tmpLine = + new BufferLine(line, eolStr.toString()); + nextLine(); + return tmpLine; + } + + public void close() throws IOException { + if (reader != null) { + reader.close(); } + } - } /* end for */ + class BufferLine { + private int next = 0; + private int column = 0; + private int lookahead = UNDEF; + private String line; + private String eolStr; + + public BufferLine(String line, String eolStr) + throws BuildException + { + next = 0; + column = 0; + this.line = line; + this.eolStr = eolStr; + } + + public int getNext() { + return next; + } + + public void setNext(int next) { + this.next = next; + } + + public int getLookahead() { + return lookahead; + } + + public void setLookahead(int lookahead) { + this.lookahead = lookahead; + } + + public char getChar(int i) { + return line.charAt(i); + } + + public char getNextChar() { + return getChar(next); + } + + public char getNextCharInc() { + return getChar(next++); + } + + public int getColumn() { + return column; + } + + public void setColumn(int col) { + column = col; + } + + public int incColumn() { + return column++; + } + + public int length() { + return line.length(); + } + + public int getEolLength() { + return eolStr.length(); + } + + public String getLineString() { + return line; + } + + public String getEol() { + return eolStr; + } + + public String substring(int begin) { + return line.substring(begin); + } + + public String substring(int begin, int end) { + return line.substring(begin, end); + } + + public void setState(int state) { + OneLiner.this.setState(state); + } + + public int getState() { + return OneLiner.this.getState(); + } + } } /** @@ -426,4 +959,14 @@ public class FixCRLF extends MatchingTask { return new String[] {"add", "asis", "remove"}; } } + + /** + * Enumerated attribute with the values "asis", "cr", "lf" and "crlf". + */ + public static class CrLf extends EnumeratedAttribute { + public String[] getValues() { + return new String[] {"asis", "cr", "lf", "crlf"}; + } + } + } diff --git a/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java b/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java new file mode 100644 index 000000000..63e7bf15f --- /dev/null +++ b/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java @@ -0,0 +1,161 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant.taskdefs; + +import java.io.*; + +import junit.framework.AssertionFailedError; + +/** + * @author Peter B. West + * @author Stefan Bodewig + */ +public class FixCrLfTest extends TaskdefsTest { + + public FixCrLfTest(String name) { + super(name); + } + + public void setUp() { + configureProject("src/etc/testcases/taskdefs/fixcrlf/build.xml"); + } + + public void tearDown() { + executeTarget("cleanup"); + } + + public void test1() throws IOException { + executeTarget("test1"); + assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk1.java"), + new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk1.java")); + } + + public void test2() throws IOException { + executeTarget("test2"); + assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk2.java"), + new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk2.java")); + } + + public void test3() throws IOException { + executeTarget("test3"); + assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk3.java"), + new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk3.java")); + } + + public void test4() throws IOException { + executeTarget("test4"); + assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk4.java"), + new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk4.java")); + } + + public void test5() throws IOException { + executeTarget("test5"); + assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk5.java"), + new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk5.java")); + } + + public void test6() throws IOException { + executeTarget("test6"); + assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk6.java"), + new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk6.java")); + } + + public void test7() throws IOException { + executeTarget("test7"); + assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk7.java"), + new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk7.java")); + } + + public void test8() throws IOException { + executeTarget("test8"); + assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk8.java"), + new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk8.java")); + } + + public void test9() throws IOException { + executeTarget("test9"); + assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk9.java"), + new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk9.java")); + } + + public void assertEqualContent(File expect, File result) + throws AssertionFailedError, IOException { + if (!result.exists()) { + fail("Expected file "+result+" doesn\'t exist"); + } + + InputStream inExpect = null; + InputStream inResult = null; + try { + inExpect = new BufferedInputStream(new FileInputStream(expect)); + inResult = new BufferedInputStream(new FileInputStream(result)); + + int expectedByte = inExpect.read(); + while (expectedByte != -1) { + assertEquals(expectedByte, inResult.read()); + expectedByte = inExpect.read(); + } + assertEquals("End of file", -1, inResult.read()); + } finally { + if (inResult != null) { + inResult.close(); + } + if (inExpect != null) { + inExpect.close(); + } + } + } + +}