Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 1,054   Methods: 45
NCLOC: 652   Classes: 5
 
 Source file Conditionals Statements Methods TOTAL
FixCRLF.java 69% 82.9% 86.7% 79.9%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant.taskdefs;
 56   
 
 57   
 import java.io.BufferedReader;
 58   
 import java.io.BufferedWriter;
 59   
 import java.io.File;
 60   
 import java.io.FileInputStream;
 61   
 import java.io.FileOutputStream;
 62   
 import java.io.FileReader;
 63   
 import java.io.FileWriter;
 64   
 import java.io.IOException;
 65   
 import java.io.InputStreamReader;
 66   
 import java.io.OutputStreamWriter;
 67   
 import java.io.Reader;
 68   
 import java.io.Writer;
 69   
 import java.util.Enumeration;
 70   
 import java.util.NoSuchElementException;
 71   
 import org.apache.tools.ant.BuildException;
 72   
 import org.apache.tools.ant.DirectoryScanner;
 73   
 import org.apache.tools.ant.Project;
 74   
 import org.apache.tools.ant.taskdefs.condition.Os;
 75   
 import org.apache.tools.ant.types.EnumeratedAttribute;
 76   
 import org.apache.tools.ant.util.FileUtils;
 77   
 
 78   
 /**
 79   
  * Converts text source files to local OS formatting conventions, as
 80   
  * well as repair text files damaged by misconfigured or misguided editors or
 81   
  * file transfer programs.
 82   
  * <p>
 83   
  * This task can take the following arguments:
 84   
  * <ul>
 85   
  * <li>srcdir
 86   
  * <li>destdir
 87   
  * <li>include
 88   
  * <li>exclude
 89   
  * <li>cr
 90   
  * <li>eol
 91   
  * <li>tab
 92   
  * <li>eof
 93   
  * <li>encoding
 94   
  * </ul>
 95   
  * Of these arguments, only <b>sourcedir</b> is required.
 96   
  * <p>
 97   
  * When this task executes, it will scan the srcdir based on the include
 98   
  * and exclude properties.
 99   
  * <p>
 100   
  * This version generalises the handling of EOL characters, and allows
 101   
  * for CR-only line endings (which I suspect is the standard on Macs.)
 102   
  * Tab handling has also been generalised to accommodate any tabwidth
 103   
  * from 2 to 80, inclusive.  Importantly, it will leave untouched any
 104   
  * literal TAB characters embedded within string or character constants.
 105   
  * <p>
 106   
  * <em>Warning:</em> do not run on binary files.
 107   
  * <em>Caution:</em> run with care on carefully formatted files.
 108   
  * This may sound obvious, but if you don't specify asis, presume that
 109   
  * your files are going to be modified.  If "tabs" is "add" or "remove",
 110   
  * whitespace characters may be added or removed as necessary.  Similarly,
 111   
  * for CR's - in fact "eol"="crlf" or cr="add" can result in cr
 112   
  * characters being removed in one special case accommodated, i.e.,
 113   
  * CRCRLF is regarded as a single EOL to handle cases where other
 114   
  * programs have converted CRLF into CRCRLF.
 115   
  *
 116   
  * @author Sam Ruby <a href="mailto:rubys@us.ibm.com">rubys@us.ibm.com</a>
 117   
  * @author <a href="mailto:pbwest@powerup.com.au">Peter B. West</a>
 118   
  * @version $Revision: 1.45 $ $Name:  $
 119   
  * @since Ant 1.1
 120   
  *
 121   
  * @ant.task category="filesystem"
 122   
  */
 123   
 
 124   
 public class FixCRLF extends MatchingTask {
 125   
 
 126   
     private static final int UNDEF = -1;
 127   
     private static final int NOTJAVA = 0;
 128   
     private static final int LOOKING = 1;
 129   
     private static final int IN_CHAR_CONST = 2;
 130   
     private static final int IN_STR_CONST = 3;
 131   
     private static final int IN_SINGLE_COMMENT = 4;
 132   
     private static final int IN_MULTI_COMMENT = 5;
 133   
 
 134   
     private static final int ASIS = 0;
 135   
     private static final int CR = 1;
 136   
     private static final int LF = 2;
 137   
     private static final int CRLF = 3;
 138   
     private static final int ADD = 1;
 139   
     private static final int REMOVE = -1;
 140   
     private static final int SPACES = -1;
 141   
     private static final int TABS = 1;
 142   
 
 143   
     private static final int INBUFLEN = 8192;
 144   
     private static final int LINEBUFLEN = 200;
 145   
 
 146   
     private static final char CTRLZ = '\u001A';
 147   
 
 148   
     private int tablength = 8;
 149   
     private String spaces = "        ";
 150   
     private StringBuffer linebuf = new StringBuffer(1024);
 151   
     private StringBuffer linebuf2 = new StringBuffer(1024);
 152   
     private int eol;
 153   
     private String eolstr;
 154   
     private int ctrlz;
 155   
     private int tabs;
 156   
     private boolean javafiles = false;
 157   
 
 158   
     private File srcDir;
 159   
     private File destDir = null;
 160   
 
 161   
     private FileUtils fileUtils = FileUtils.newFileUtils();
 162   
 
 163   
     /**
 164   
      * Encoding to assume for the files
 165   
      */
 166   
     private String encoding = null;
 167   
 
 168   
     /**
 169   
      * Defaults the properties based on the system type.
 170   
      * <ul><li>Unix: eol="LF" tab="asis" eof="remove"
 171   
      *     <li>Mac: eol="CR" tab="asis" eof="remove"
 172   
      *     <li>DOS: eol="CRLF" tab="asis" eof="asis"</ul>
 173   
      */
 174  25
     public FixCRLF () {
 175  25
         tabs = ASIS;
 176  25
         if (Os.isFamily("mac")) {
 177  0
             ctrlz = REMOVE;
 178  0
             eol = CR;
 179  0
             eolstr = "\r";
 180  25
         } else if (Os.isFamily("dos")) {
 181  0
             ctrlz = ASIS;
 182  0
             eol = CRLF;
 183  0
             eolstr = "\r\n";
 184   
         } else {
 185  25
             ctrlz = REMOVE;
 186  25
             eol = LF;
 187  25
             eolstr = "\n";
 188   
         }
 189   
     }
 190   
 
 191   
     /**
 192   
      * Set the source dir to find the source text files.
 193   
      */
 194  25
     public void setSrcdir(File srcDir) {
 195  25
         this.srcDir = srcDir;
 196   
     }
 197   
 
 198   
     /**
 199   
      * Set the destination where the fixed files should be placed.
 200   
      * Default is to replace the original file.
 201   
      */
 202  17
     public void setDestdir(File destDir) {
 203  17
         this.destDir = destDir;
 204   
     }
 205   
 
 206   
     /**
 207   
      * Set to true if modifying Java source files.
 208   
      */
 209  10
     public void setJavafiles(boolean javafiles) {
 210  10
         this.javafiles = javafiles;
 211   
     }
 212   
 
 213   
 
 214   
     /**
 215   
      * Specify how EndOfLine characters are to be handled.
 216   
      *
 217   
      * @param attr valid values:
 218   
      * <ul>
 219   
      * <li>asis: leave line endings alone
 220   
      * <li>cr: convert line endings to CR
 221   
      * <li>lf: convert line endings to LF
 222   
      * <li>crlf: convert line endings to CRLF
 223   
      * </ul>
 224   
      */
 225  27
     public void setEol(CrLf attr) {
 226  27
         String option = attr.getValue();
 227  27
         if (option.equals("asis")) {
 228  0
             eol = ASIS;
 229  27
         } else if (option.equals("cr") || option.equals("mac")) {
 230  1
             eol = CR;
 231  1
             eolstr = "\r";
 232  26
         } else if (option.equals("lf") || option.equals("unix")) {
 233  16
             eol = LF;
 234  16
             eolstr = "\n";
 235   
         } else {
 236   
             // Must be "crlf"
 237  10
             eol = CRLF;
 238  10
             eolstr = "\r\n";
 239   
         }
 240   
     }
 241   
 
 242   
     /**
 243   
      * Specify how carriage return (CR) characters are to be handled.
 244   
      *
 245   
      * @param option valid values:
 246   
      * <ul>
 247   
      * <li>add: ensure that there is a CR before every LF
 248   
      * <li>asis: leave CR characters alone
 249   
      * <li>remove: remove all CR characters
 250   
      * </ul>
 251   
      *
 252   
      * @deprecated use {@link #setEol setEol} instead.
 253   
      */
 254  7
     public void setCr(AddAsisRemove attr) {
 255  7
         log("DEPRECATED: The cr attribute has been deprecated,",
 256   
             Project.MSG_WARN);
 257  7
         log("Please use the eol attribute instead", Project.MSG_WARN);
 258  7
         String option = attr.getValue();
 259  7
         CrLf c = new CrLf();
 260  7
         if (option.equals("remove")) {
 261  4
             c.setValue("lf");
 262  3
         } else if (option.equals("asis")) {
 263  0
             c.setValue("asis");
 264   
         } else {
 265   
             // must be "add"
 266  3
             c.setValue("crlf");
 267   
         }
 268  7
         setEol(c);
 269   
     }
 270   
 
 271   
     /**
 272   
      * Specify how tab characters are to be handled.
 273   
      *
 274   
      * @param attr valid values:
 275   
      * <ul>
 276   
      * <li>add: convert sequences of spaces which span a tab stop to tabs
 277   
      * <li>asis: leave tab and space characters alone
 278   
      * <li>remove: convert tabs to spaces
 279   
      * </ul>
 280   
      */
 281  11
     public void setTab(AddAsisRemove attr) {
 282  11
         String option = attr.getValue();
 283  11
         if (option.equals("remove")) {
 284  4
             tabs = SPACES;
 285  7
         } else if (option.equals("asis")) {
 286  0
             tabs = ASIS;
 287   
         } else {
 288   
             // must be "add"
 289  7
             tabs = TABS;
 290   
         }
 291   
     }
 292   
 
 293   
     /**
 294   
      * Specify tab length in characters.
 295   
      *
 296   
      * @param tlength specify the length of tab in spaces,
 297   
      */
 298  0
     public void setTablength(int tlength) throws BuildException {
 299  0
         if (tlength < 2 || tlength > 80) {
 300  0
             throw new BuildException("tablength must be between 2 and 80",
 301   
                                      getLocation());
 302   
         }
 303  0
         tablength = tlength;
 304  0
         StringBuffer sp = new StringBuffer();
 305  0
         for (int i = 0; i < tablength; i++) {
 306  0
             sp.append(' ');
 307   
         }
 308  0
         spaces = sp.toString();
 309   
     }
 310   
 
 311   
     /**
 312   
      * Specify how DOS EOF (control-z) characters are to be handled.
 313   
      *
 314   
      * @param attr valid values:
 315   
      * <ul>
 316   
      * <li>add: ensure that there is an eof at the end of the file
 317   
      * <li>asis: leave eof characters alone
 318   
      * <li>remove: remove any eof character found at the end
 319   
      * </ul>
 320   
      */
 321  11
     public void setEof(AddAsisRemove attr) {
 322  11
         String option = attr.getValue();
 323  11
         if (option.equals("remove")) {
 324  1
             ctrlz = REMOVE;
 325  10
         } else if (option.equals("asis")) {
 326  9
             ctrlz = ASIS;
 327   
         } else {
 328   
             // must be "add"
 329  1
             ctrlz = ADD;
 330   
         }
 331   
     }
 332   
 
 333   
     /**
 334   
      * Specifies the encoding Ant expects the files to be in -
 335   
      * defaults to the platforms default encoding.
 336   
      */
 337  1
     public void setEncoding(String encoding) {
 338  1
         this.encoding = encoding;
 339   
     }
 340   
 
 341   
     /**
 342   
      * Executes the task.
 343   
      */
 344  25
     public void execute() throws BuildException {
 345   
         // first off, make sure that we've got a srcdir and destdir
 346   
 
 347  25
         if (srcDir == null) {
 348  0
             throw new BuildException("srcdir attribute must be set!");
 349   
         }
 350  25
         if (!srcDir.exists()) {
 351  0
             throw new BuildException("srcdir does not exist!");
 352   
         }
 353  25
         if (!srcDir.isDirectory()) {
 354  0
             throw new BuildException("srcdir is not a directory!");
 355   
         }
 356  25
         if (destDir != null) {
 357  17
             if (!destDir.exists()) {
 358  0
                 throw new BuildException("destdir does not exist!");
 359   
             }
 360  17
             if (!destDir.isDirectory()) {
 361  0
                 throw new BuildException("destdir is not a directory!");
 362   
             }
 363   
         }
 364   
 
 365   
         // log options used
 366  25
         log("options:" +
 367   
             " eol=" +
 368   
             (eol == ASIS ? "asis" : eol == CR ? "cr" : eol == LF ? "lf" : "crlf") +
 369   
             " tab=" + (tabs == TABS ? "add" : tabs == ASIS ? "asis" : "remove") +
 370   
             " eof=" + (ctrlz == ADD ? "add" : ctrlz == ASIS ? "asis" : "remove") +
 371   
             " tablength=" + tablength +
 372   
             " encoding=" + (encoding == null ? "default" : encoding),
 373   
             Project.MSG_VERBOSE);
 374   
 
 375  25
         DirectoryScanner ds = super.getDirectoryScanner(srcDir);
 376  25
         String[] files = ds.getIncludedFiles();
 377   
 
 378  25
         for (int i = 0; i < files.length; i++) {
 379  25
             processFile(files[i]);
 380   
         }
 381   
     }
 382   
 
 383   
     /**
 384   
      * Creates a Reader reading from a given file an taking the user
 385   
      * defined encoding into account.
 386   
      */
 387  25
     private Reader getReader(File f) throws IOException {
 388  25
         return (encoding == null) ? new FileReader(f)
 389   
             : new InputStreamReader(new FileInputStream(f), encoding);
 390   
     }
 391   
 
 392   
 
 393  25
     private void processFile(String file) throws BuildException {
 394  25
         File srcFile = new File(srcDir, file);
 395  25
         File destD = destDir == null ? srcDir : destDir;
 396  25
         File tmpFile = null;
 397  25
         BufferedWriter outWriter;
 398  25
         OneLiner.BufferLine line;
 399   
 
 400   
         // read the contents of the file
 401  25
         OneLiner lines = new OneLiner(srcFile);
 402   
 
 403  25
         try {
 404   
             // Set up the output Writer
 405  25
             try {
 406  25
                 tmpFile = fileUtils.createTempFile("fixcrlf", "", destD);
 407  25
                 Writer writer = (encoding == null) ? new FileWriter(tmpFile)
 408   
                     : new OutputStreamWriter(new FileOutputStream(tmpFile),
 409   
                                              encoding);
 410  25
                 outWriter = new BufferedWriter(writer);
 411   
             } catch (IOException e) {
 412  0
                 throw new BuildException(e);
 413   
             }
 414   
 
 415  25
             while (lines.hasMoreElements()) {
 416   
                 // In-line states
 417  162
                 int endComment;
 418   
 
 419  162
                 try {
 420  162
                     line = (OneLiner.BufferLine) lines.nextElement();
 421   
                 } catch (NoSuchElementException e) {
 422  0
                     throw new BuildException(e);
 423   
                 }
 424   
 
 425  162
                 String lineString = line.getLineString();
 426  162
                 int linelen = line.length();
 427   
 
 428   
                 // Note - all of the following processing NOT done for
 429   
                 // tabs ASIS
 430   
 
 431  162
                 if (tabs == ASIS) {
 432   
                     // Just copy the body of the line across
 433  30
                     try {
 434  30
                         outWriter.write(lineString);
 435   
                     } catch (IOException e) {
 436  0
                         throw new BuildException(e);
 437   
                     } // end of try-catch
 438   
 
 439   
                 } else { // (tabs != ASIS)
 440  132
                     int ptr;
 441   
 
 442  ?
                     while ((ptr = line.getNext()) < linelen) {
 443   
 
 444  212
                         switch (lines.getState()) {
 445   
 
 446   
                         case NOTJAVA:
 447  36
                             notInConstant(line, line.length(), outWriter);
 448  36
                             break;
 449   
 
 450   
                         case IN_MULTI_COMMENT:
 451  24
                             endComment
 452   
                                 = lineString.indexOf("*/", line.getNext());
 453  24
                             if (endComment >= 0) {
 454   
                                 // End of multiLineComment on this line
 455  8
                                 endComment += 2;  // Include the end token
 456  8
                                 lines.setState(LOOKING);
 457   
                             } else {
 458  16
                                 endComment = linelen;
 459   
                             }
 460   
 
 461  24
                             notInConstant(line, endComment, outWriter);
 462  24
                             break;
 463   
 
 464   
                         case IN_SINGLE_COMMENT:
 465  32
                             notInConstant(line, line.length(), outWriter);
 466  32
                             lines.setState(LOOKING);
 467  32
                             break;
 468   
 
 469   
                         case IN_CHAR_CONST:
 470   
                         case IN_STR_CONST:
 471   
                             // Got here from LOOKING by finding an
 472   
                             // opening "\'" next points to that quote
 473   
                             // character.
 474   
                             // Find the end of the constant.  Watch
 475   
                             // out for backslashes.  Literal tabs are
 476   
                             // left unchanged, and the column is
 477   
                             // adjusted accordingly.
 478   
 
 479  16
                             int begin = line.getNext();
 480  16
                             char terminator = (lines.getState() == IN_STR_CONST
 481   
                                                ? '\"'
 482   
                                                : '\'');
 483  16
                             endOfCharConst(line, terminator);
 484  16
                             while (line.getNext() < line.getLookahead()) {
 485  136
                                 if (line.getNextCharInc() == '\t') {
 486  16
                                     line.setColumn(line.getColumn() +
 487   
                                                    tablength -
 488   
                                                    (line.getColumn()
 489   
                                                     % tablength));
 490   
                                 } else {
 491  120
                                     line.incColumn();
 492   
                                 }
 493   
                             }
 494   
 
 495   
                             // Now output the substring
 496  16
                             try {
 497  16
                                 outWriter.write(line.substring(begin,
 498   
                                                                line.getNext()));
 499   
                             } catch (IOException e) {
 500  0
                                 throw new BuildException(e);
 501   
                             }
 502   
 
 503  16
                             lines.setState(LOOKING);
 504   
 
 505  16
                             break;
 506   
 
 507   
 
 508   
                         case LOOKING:
 509  104
                             nextStateChange(line);
 510  104
                             notInConstant(line, line.getLookahead(), outWriter);
 511  104
                             break;
 512   
 
 513   
                         } // end of switch (state)
 514   
 
 515   
                     } // end of while (line.getNext() < linelen)
 516   
 
 517   
                 } // end of else (tabs != ASIS)
 518   
 
 519  162
                 try {
 520  162
                     outWriter.write(eolstr);
 521   
                 } catch (IOException e) {
 522  0
                     throw new BuildException(e);
 523   
                 } // end of try-catch
 524   
 
 525   
             } // end of while (lines.hasNext())
 526   
 
 527  25
             try {
 528   
                 // Handle CTRLZ
 529  25
                 if (ctrlz == ASIS) {
 530  9
                     outWriter.write(lines.getEofStr());
 531  16
                 } else if (ctrlz == ADD){
 532  1
                     outWriter.write(CTRLZ);
 533   
                 }
 534   
             } catch (IOException e) {
 535  0
                 throw new BuildException(e);
 536   
             } finally {
 537  25
                 try {
 538  25
                     outWriter.close();
 539   
                 } catch (IOException e) {
 540  0
                     throw new BuildException(e);
 541   
                 }
 542   
             }
 543   
 
 544   
 
 545  25
             try {
 546  25
                 lines.close();
 547  25
                 lines = null;
 548   
             } catch (IOException e) {
 549  0
                 throw new BuildException("Unable to close source file "
 550   
                                          + srcFile);
 551   
             }
 552   
 
 553  25
             File destFile = new File(destD, file);
 554   
 
 555  25
             if (destFile.exists()) {
 556   
                 // Compare the destination with the temp file
 557  9
                 log("destFile exists", Project.MSG_DEBUG);
 558  9
                 if (!fileUtils.contentEquals(destFile, tmpFile)) {
 559  1
                     log(destFile + " is being written", Project.MSG_DEBUG);
 560  1
                     if (!destFile.delete()) {
 561  0
                         throw new BuildException("Unable to delete "
 562   
                                                  + destFile);
 563   
                     }
 564  1
                     if (!tmpFile.renameTo(destFile)) {
 565  0
                         throw new BuildException(
 566   
                                 "Failed to transform " + srcFile
 567   
                                 + " to " + destFile
 568   
                                 + ". Couldn't rename temporary file: "
 569   
                                 + tmpFile);
 570   
                     }
 571   
 
 572   
                 } else { // destination is equal to temp file
 573  8
                     log(destFile +
 574   
                         " is not written, as the contents are identical",
 575   
                         Project.MSG_DEBUG);
 576  8
                     if (!tmpFile.delete()) {
 577  0
                         throw new BuildException("Unable to delete "
 578   
                                                  + tmpFile);
 579   
                     }
 580   
                 }
 581   
             } else { // destFile does not exist - write the temp file
 582  16
                 log("destFile does not exist", Project.MSG_DEBUG);
 583  16
                 if (!tmpFile.renameTo(destFile)) {
 584  0
                     throw new BuildException(
 585   
                             "Failed to transform " + srcFile
 586   
                             + " to " + destFile
 587   
                             + ". Couldn't rename temporary file: "
 588   
                             + tmpFile);
 589   
                 }
 590   
             }
 591   
 
 592  25
             tmpFile = null;
 593   
 
 594   
         } catch (IOException e) {
 595  0
             throw new BuildException(e);
 596   
         } finally {
 597  25
             try {
 598  25
                 if (lines != null) {
 599  0
                     lines.close();
 600   
                 }
 601   
             } catch (IOException io) {
 602  0
                 log("Error closing " + srcFile, Project.MSG_ERR);
 603   
             } // end of catch
 604   
 
 605  25
             if (tmpFile != null) {
 606  0
                 tmpFile.delete();
 607   
             }
 608   
         } // end of finally
 609   
     }
 610   
 
 611   
     /**
 612   
      * Scan a BufferLine for the next state changing token: the beginning
 613   
      * of a single or multi-line comment, a character or a string constant.
 614   
      *
 615   
      * As a side-effect, sets the buffer state to the next state, and sets
 616   
      * field lookahead to the first character of the state-changing token, or
 617   
      * to the next eol character.
 618   
      *
 619   
      * @param bufline       BufferLine containing the string
 620   
      *                                 to be processed
 621   
      * @exception org.apache.tools.ant.BuildException
 622   
      *                                 Thrown when end of line is reached
 623   
      *                                 before the terminator is found.
 624   
      */
 625  104
     private void nextStateChange(OneLiner.BufferLine bufline)
 626   
         throws BuildException {
 627  104
         int eol = bufline.length();
 628  104
         int ptr = bufline.getNext();
 629   
 
 630   
 
 631   
         //  Look for next single or double quote, double slash or slash star
 632  104
         while (ptr < eol) {
 633  1346
             switch (bufline.getChar(ptr++)) {
 634   
             case '\'':
 635  8
                 bufline.setState(IN_CHAR_CONST);
 636  8
                 bufline.setLookahead(--ptr);
 637  8
                 return;
 638   
             case '\"':
 639  8
                 bufline.setState(IN_STR_CONST);
 640  8
                 bufline.setLookahead(--ptr);
 641  8
                 return;
 642   
             case '/':
 643  40
                 if (ptr < eol) {
 644  40
                     if (bufline.getChar(ptr) == '*') {
 645  8
                         bufline.setState(IN_MULTI_COMMENT);
 646  8
                         bufline.setLookahead(--ptr);
 647  8
                         return;
 648  32
                     } else if (bufline.getChar(ptr) == '/') {
 649  32
                         bufline.setState(IN_SINGLE_COMMENT);
 650  32
                         bufline.setLookahead(--ptr);
 651  32
                         return;
 652   
                     }
 653   
                 }
 654  0
                 break;
 655   
             } // end of switch (bufline.getChar(ptr++))
 656   
 
 657   
         } // end of while (ptr < eol)
 658   
         // Eol is the next token
 659  48
         bufline.setLookahead(ptr);
 660   
     }
 661   
 
 662   
 
 663   
     /**
 664   
      * Scan a BufferLine forward from the 'next' pointer
 665   
      * for the end of a character constant.  Set 'lookahead' pointer to the
 666   
      * character following the terminating quote.
 667   
      *
 668   
      * @param bufline       BufferLine containing the string
 669   
      *                                 to be processed
 670   
      * @param terminator          The constant terminator
 671   
      *
 672   
      * @exception org.apache.tools.ant.BuildException
 673   
      *                                 Thrown when end of line is reached
 674   
      *                                 before the terminator is found.
 675   
      */
 676  16
     private void endOfCharConst(OneLiner.BufferLine bufline, char terminator)
 677   
         throws BuildException {
 678  16
         int ptr = bufline.getNext();
 679  16
         int eol = bufline.length();
 680  16
         char c;
 681  16
         ptr++;          // skip past initial quote
 682  120
         while (ptr < eol) {
 683  ?
             if ((c = bufline.getChar(ptr++)) == '\\') {
 684  0
                 ptr++;
 685   
             } else {
 686  120
                 if (c == terminator) {
 687  16
                     bufline.setLookahead(ptr);
 688  16
                     return;
 689   
                 }
 690   
             }
 691   
         } // end of while (ptr < eol)
 692   
         // Must have fallen through to the end of the line
 693  0
         throw new BuildException("endOfCharConst: unterminated char constant");
 694   
     }
 695   
 
 696   
 
 697   
     /**
 698   
      * Process a BufferLine string which is not part of of a string constant.
 699   
      * The start position of the string is given by the 'next' field.
 700   
      * Sets the 'next' and 'column' fields in the BufferLine.
 701   
      *
 702   
      * @param bufline       BufferLine containing the string
 703   
      *                                 to be processed
 704   
      * @param end                  Index just past the end of the
 705   
      *                                 string
 706   
      * @param outWriter Sink for the processed string
 707   
      */
 708  196
     private void notInConstant(OneLiner.BufferLine bufline, int end,
 709   
                                 BufferedWriter outWriter) {
 710   
         // N.B. both column and string index are zero-based
 711   
         // Process a string not part of a constant;
 712   
         // i.e. convert tabs<->spaces as required
 713   
         // This is NOT called for ASIS tab handling
 714  196
         int nextTab;
 715  196
         int nextStop;
 716  196
         int tabspaces;
 717  196
         String line = bufline.substring(bufline.getNext(), end);
 718  196
         int place = 0;          // Zero-based
 719  196
         int col = bufline.getColumn();  // Zero-based
 720   
 
 721   
         // process sequences of white space
 722   
         // first convert all tabs to spaces
 723  196
         linebuf = new StringBuffer();
 724  ?
         while ((nextTab = line.indexOf((int) '\t', place)) >= 0) {
 725  104
             linebuf.append(line.substring(place, nextTab)); // copy to the TAB
 726  104
             col += nextTab - place;
 727  104
             tabspaces = tablength - (col % tablength);
 728  104
             linebuf.append(spaces.substring(0, tabspaces));
 729  104
             col += tabspaces;
 730  104
             place = nextTab + 1;
 731   
         } // end of while
 732  196
         linebuf.append(line.substring(place, line.length()));
 733   
         // if converting to spaces, all finished
 734  196
         String linestring = new String(linebuf.substring(0));
 735  196
         if (tabs == REMOVE) {
 736  72
             try {
 737  72
                 outWriter.write(linestring);
 738   
             } catch (IOException e) {
 739  0
                 throw new BuildException(e);
 740   
             } // end of try-catch
 741   
         } else { // tabs == ADD
 742  124
             int tabCol;
 743  124
             linebuf2 = new StringBuffer();
 744  124
             place = 0;
 745  124
             col = bufline.getColumn();
 746  124
             int placediff = col - 0;
 747   
             // for the length of the string, cycle through the tab stop
 748   
             // positions, checking for a space preceded by at least one
 749   
             // other space at the tab stop.  if so replace the longest possible
 750   
             // preceding sequence of spaces with a tab.
 751  124
             nextStop = col + (tablength - col % tablength);
 752  124
             if (nextStop - col < 2) {
 753  0
                 linebuf2.append(linestring.substring(
 754   
                                         place, nextStop - placediff));
 755  0
                 place = nextStop - placediff;
 756  0
                 nextStop += tablength;
 757   
             }
 758   
 
 759  124
             for (; nextStop - placediff <= linestring.length()
 760   
                           ; nextStop += tablength) {
 761  314
                 for (tabCol = nextStop;
 762  1053
                              --tabCol - placediff >= place
 763   
                              && linestring.charAt(tabCol - placediff) == ' '
 764   
                              ;) {
 765   
                     ; // Loop for the side-effects
 766   
                 }
 767   
                 // tabCol is column index of the last non-space character
 768   
                 // before the next tab stop
 769  314
                 if (nextStop - tabCol > 2) {
 770  102
                     linebuf2.append(linestring.substring(
 771   
                                     place, ++tabCol - placediff));
 772  102
                     linebuf2.append('\t');
 773   
                 } else {
 774  212
                     linebuf2.append(linestring.substring(
 775   
                                     place, nextStop - placediff));
 776   
                 } // end of else
 777   
 
 778  314
                 place = nextStop - placediff;
 779   
             } // end of for (nextStop ... )
 780   
 
 781   
             // pick up that last bit, if any
 782  124
             linebuf2.append(linestring.substring(place, linestring.length()));
 783   
 
 784  124
             try {
 785  124
                 outWriter.write(linebuf2.substring(0));
 786   
             } catch (IOException e) {
 787  0
                 throw new BuildException(e);
 788   
             } // end of try-catch
 789   
 
 790   
         } // end of else tabs == ADD
 791   
 
 792   
         // Set column position as modified by this method
 793  196
         bufline.setColumn(bufline.getColumn() + linestring.length());
 794  196
         bufline.setNext(end);
 795   
 
 796   
     }
 797   
 
 798   
 
 799   
     class OneLiner implements Enumeration {
 800   
 
 801   
         private int state = javafiles ? LOOKING : NOTJAVA;
 802   
 
 803   
         private StringBuffer eolStr = new StringBuffer(LINEBUFLEN);
 804   
         private StringBuffer eofStr = new StringBuffer();
 805   
 
 806   
         private BufferedReader reader;
 807   
         private StringBuffer line = new StringBuffer();
 808   
         private boolean reachedEof = false;
 809   
         private File srcFile;
 810   
 
 811  25
         public OneLiner(File srcFile)
 812   
             throws BuildException {
 813  25
             this.srcFile = srcFile;
 814  25
             try {
 815  25
                 reader = new BufferedReader
 816   
                         (getReader(srcFile), INBUFLEN);
 817  25
                 nextLine();
 818   
             } catch (IOException e) {
 819  0
                 throw new BuildException(srcFile + ": "+ e.getMessage(),
 820   
                                          e, getLocation());
 821   
             }
 822   
         }
 823   
 
 824  187
         protected void nextLine()
 825   
             throws BuildException {
 826  187
             int ch = -1;
 827  187
             int eolcount = 0;
 828   
 
 829  187
             eolStr = new StringBuffer();
 830  187
             line = new StringBuffer();
 831   
 
 832  187
             try {
 833  187
                 ch = reader.read();
 834  187
                 while (ch != -1 && ch != '\r' && ch != '\n') {
 835  70572
                     line.append((char) ch);
 836  70572
                     ch = reader.read();
 837   
                 }
 838   
 
 839  187
                 if (ch == -1 && line.length() == 0) {
 840   
                     // Eof has been reached
 841  24
                     reachedEof = true;
 842  24
                     return;
 843   
                 }
 844   
 
 845  163
                 switch ((char) ch) {
 846   
                 case '\r':
 847   
                     // Check for \r, \r\n and \r\r\n
 848   
                     // Regard \r\r not followed by \n as two lines
 849  23
                     ++eolcount;
 850  23
                     eolStr.append('\r');
 851  23
                     reader.mark(2);
 852  23
                     switch ((ch = reader.read())) {
 853   
                     case '\r':
 854  ?
                         if ((char) (ch = reader.read()) == '\n') {
 855  3
                             eolcount += 2;
 856  3
                             eolStr.append("\r\n");
 857   
                         } else {
 858  1
                             reader.reset();
 859   
                         }
 860  4
                         break;
 861   
                     case '\n':
 862  17
                         ++eolcount;
 863  17
                         eolStr.append('\n');
 864  17
                         break;
 865   
                     case -1:
 866   
                         // don't reposition when we've reached the end
 867   
                         // of the stream
 868  0
                         break;
 869   
                     default:
 870  2
                         reader.reset();
 871  2
                         break;
 872   
                     } // end of switch ((char)(ch = reader.read()))
 873  23
                     break;
 874   
 
 875   
                 case '\n':
 876  139
                     ++eolcount;
 877  139
                     eolStr.append('\n');
 878  139
                     break;
 879   
 
 880   
                 } // end of switch ((char) ch)
 881   
 
 882   
                 // if at eolcount == 0 and trailing characters of string
 883   
                 // are CTRL-Zs, set eofStr
 884  163
                 if (eolcount == 0) {
 885  1
                     int i = line.length();
 886  1
                     while (--i >= 0 && line.charAt(i) == CTRLZ) {
 887   
                         // keep searching for the first ^Z
 888   
                     }
 889  1
                     if (i < line.length() - 1) {
 890   
                         // Trailing characters are ^Zs
 891   
                         // Construct new line and eofStr
 892  1
                         eofStr.append(line.toString().substring(i + 1));
 893  1
                         if (i < 0) {
 894  1
                             line.setLength(0);
 895  1
                             reachedEof = true;
 896   
                         } else {
 897  0
                             line.setLength(i + 1);
 898   
                         }
 899   
                     }
 900   
 
 901   
                 } // end of if (eolcount == 0)
 902   
 
 903   
             } catch (IOException e) {
 904  0
                 throw new BuildException(srcFile + ": "+ e.getMessage(),
 905   
                                          e, getLocation());
 906   
             }
 907   
         }
 908   
 
 909  9
         public String getEofStr() {
 910  9
             return eofStr.substring(0);
 911   
         }
 912   
 
 913  228
         public int getState() {
 914  228
             return state;
 915   
         }
 916   
 
 917  112
         public void setState(int state) {
 918  112
             this.state = state;
 919   
         }
 920   
 
 921  349
         public boolean hasMoreElements() {
 922  349
             return !reachedEof;
 923   
         }
 924   
 
 925  162
         public Object nextElement()
 926   
             throws NoSuchElementException {
 927  162
             if (!hasMoreElements()) {
 928  0
                 throw new NoSuchElementException("OneLiner");
 929   
             }
 930  162
             BufferLine tmpLine =
 931   
                     new BufferLine(line.toString(), eolStr.substring(0));
 932  162
             nextLine();
 933  162
             return tmpLine;
 934   
         }
 935   
 
 936  25
         public void close() throws IOException {
 937  25
             if (reader != null) {
 938  25
                 reader.close();
 939   
             }
 940   
         }
 941   
 
 942   
         class BufferLine {
 943   
             private int next = 0;
 944   
             private int column = 0;
 945   
             private int lookahead = UNDEF;
 946   
             private String line;
 947   
             private String eolStr;
 948   
 
 949  162
             public BufferLine(String line, String eolStr)
 950   
                 throws BuildException {
 951  162
                 next = 0;
 952  162
                 column = 0;
 953  162
                 this.line = line;
 954  162
                 this.eolStr = eolStr;
 955   
             }
 956   
 
 957  868
             public int getNext() {
 958  868
                 return next;
 959   
             }
 960   
 
 961  196
             public void setNext(int next) {
 962  196
                 this.next = next;
 963   
             }
 964   
 
 965  256
             public int getLookahead() {
 966  256
                 return lookahead;
 967   
             }
 968   
 
 969  120
             public void setLookahead(int lookahead) {
 970  120
                 this.lookahead = lookahead;
 971   
             }
 972   
 
 973  1674
             public char getChar(int i) {
 974  1674
                 return line.charAt(i);
 975   
             }
 976   
 
 977  0
             public char getNextChar() {
 978  0
                 return getChar(next);
 979   
             }
 980   
 
 981  136
             public char getNextCharInc() {
 982  136
                 return getChar(next++);
 983   
             }
 984   
 
 985  548
             public int getColumn() {
 986  548
                 return column;
 987   
             }
 988   
 
 989  212
             public void setColumn(int col) {
 990  212
                 column = col;
 991   
             }
 992   
 
 993  120
             public int incColumn() {
 994  120
                 return column++;
 995   
             }
 996   
 
 997  350
             public int length() {
 998  350
                 return line.length();
 999   
             }
 1000   
 
 1001  0
             public int getEolLength() {
 1002  0
                 return eolStr.length();
 1003   
             }
 1004   
 
 1005  162
             public String getLineString() {
 1006  162
                 return line;
 1007   
             }
 1008   
 
 1009  0
             public String getEol() {
 1010  0
                 return eolStr;
 1011   
             }
 1012   
 
 1013  0
             public String substring(int begin) {
 1014  0
                 return line.substring(begin);
 1015   
             }
 1016   
 
 1017  212
             public String substring(int begin, int end) {
 1018  212
                 return line.substring(begin, end);
 1019   
             }
 1020   
 
 1021  56
             public void setState(int state) {
 1022  56
                 OneLiner.this.setState(state);
 1023   
             }
 1024   
 
 1025  0
             public int getState() {
 1026  0
                 return OneLiner.this.getState();
 1027   
             }
 1028   
         }
 1029   
     }
 1030   
 
 1031   
     /**
 1032   
      * Enumerated attribute with the values "asis", "add" and "remove".
 1033   
      */
 1034   
     public static class AddAsisRemove extends EnumeratedAttribute {
 1035  29
         public String[] getValues() {
 1036  29
             return new String[] {"add", "asis", "remove"};
 1037   
         }
 1038   
     }
 1039   
 
 1040   
     /**
 1041   
      * Enumerated attribute with the values "asis", "cr", "lf" and "crlf".
 1042   
      */
 1043   
     public static class CrLf extends EnumeratedAttribute {
 1044   
         /**
 1045   
          * @see EnumeratedAttribute#getValues
 1046   
          */
 1047  27
         public String[] getValues() {
 1048  27
             return new String[] {"asis", "cr", "lf", "crlf", 
 1049   
                                  "mac", "unix", "dos"};
 1050   
         }
 1051   
     }
 1052   
 
 1053   
 }
 1054