| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 2000-2002 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 |
| * <http://www.apache.org/>. |
| */ |
| |
| package org.apache.tools.ant.taskdefs; |
| |
| 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.types.EnumeratedAttribute; |
| import org.apache.tools.ant.util.FileUtils; |
| |
| import java.io.BufferedReader; |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.FileReader; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.OutputStreamWriter; |
| import java.io.Reader; |
| import java.io.Writer; |
| import java.util.Enumeration; |
| import java.util.NoSuchElementException; |
| |
| /** |
| * Converts text source files to local OS formatting conventions, as |
| * well as repair text files damaged by misconfigured or misguided editors or |
| * file transfer programs. |
| * <p> |
| * This task can take the following arguments: |
| * <ul> |
| * <li>srcdir |
| * <li>destdir |
| * <li>include |
| * <li>exclude |
| * <li>cr |
| * <li>eol |
| * <li>tab |
| * <li>eof |
| * <li>encoding |
| * </ul> |
| * Of these arguments, only <b>sourcedir</b> is required. |
| * <p> |
| * When this task executes, it will scan the srcdir based on the include |
| * and exclude properties. |
| * <p> |
| * This version generalises the handling of EOL characters, and allows |
| * for CR-only line endings (which I suspect is the standard on Macs.) |
| * Tab handling has also been generalised to accommodate any tabwidth |
| * from 2 to 80, inclusive. Importantly, it will leave untouched any |
| * literal TAB characters embedded within string or character constants. |
| * <p> |
| * <em>Warning:</em> do not run on binary files. |
| * <em>Caution:</em> run with care on carefully formatted files. |
| * This may sound obvious, but if you don't specify asis, presume that |
| * your files are going to be modified. If "tabs" is "add" or "remove", |
| * whitespace characters may be added or removed as necessary. Similarly, |
| * for 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 <a href="mailto:rubys@us.ibm.com">rubys@us.ibm.com</a> |
| * @author <a href="mailto:pbwest@powerup.com.au">Peter B. West</a> |
| * @version $Revision$ $Name$ |
| * @since Ant 1.1 |
| * |
| * @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; |
| |
| 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; |
| |
| private FileUtils fileUtils = FileUtils.newFileUtils(); |
| |
| /** |
| * Encoding to assume for the files |
| */ |
| private String encoding = null; |
| |
| /** |
| * Defaults the properties based on the system type. |
| * <ul><li>Unix: eol="LF" tab="asis" eof="remove" |
| * <li>Mac: eol="CR" tab="asis" eof="remove" |
| * <li>DOS: eol="CRLF" tab="asis" eof="asis"</ul> |
| */ |
| public FixCRLF () { |
| tabs = ASIS; |
| if (Os.isFamily("mac")) { |
| ctrlz = REMOVE; |
| eol = CR; |
| eolstr = "\r"; |
| } else if (Os.isFamily("dos")) { |
| ctrlz = ASIS; |
| eol = CRLF; |
| eolstr = "\r\n"; |
| } else { |
| ctrlz = REMOVE; |
| eol = LF; |
| eolstr = "\n"; |
| } |
| } |
| |
| /** |
| * Set the source dir to find the source text files. |
| */ |
| public void setSrcdir(File srcDir) { |
| this.srcDir = srcDir; |
| } |
| |
| /** |
| * Set the destination where the fixed files should be placed. |
| * Default is to replace the original file. |
| */ |
| public void setDestdir(File destDir) { |
| this.destDir = destDir; |
| } |
| |
| /** |
| * Set to true if modifying Java source files. |
| */ |
| public void setJavafiles(boolean javafiles) { |
| this.javafiles = javafiles; |
| } |
| |
| |
| /** |
| * Specify how EndOfLine characters are to be handled. |
| * |
| * @param attr valid values: |
| * <ul> |
| * <li>asis: leave line endings alone |
| * <li>cr: convert line endings to CR |
| * <li>lf: convert line endings to LF |
| * <li>crlf: convert line endings to CRLF |
| * </ul> |
| */ |
| public void setEol(CrLf attr) { |
| 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: |
| * <ul> |
| * <li>add: ensure that there is a CR before every LF |
| * <li>asis: leave CR characters alone |
| * <li>remove: remove all CR characters |
| * </ul> |
| * |
| * @deprecated use {@link #setEol setEol} instead. |
| */ |
| public void setCr(AddAsisRemove attr) { |
| log("DEPRECATED: The cr attribute has been deprecated,", |
| Project.MSG_WARN); |
| log("Please use the eol attribute instead", Project.MSG_WARN); |
| String option = attr.getValue(); |
| CrLf c = new CrLf(); |
| if (option.equals("remove")) { |
| c.setValue("lf"); |
| } else if (option.equals("asis")) { |
| c.setValue("asis"); |
| } else { |
| // must be "add" |
| c.setValue("crlf"); |
| } |
| setEol(c); |
| } |
| |
| /** |
| * Specify how tab characters are to be handled. |
| * |
| * @param attr valid values: |
| * <ul> |
| * <li>add: convert sequences of spaces which span a tab stop to tabs |
| * <li>asis: leave tab and space characters alone |
| * <li>remove: convert tabs to spaces |
| * </ul> |
| */ |
| public void setTab(AddAsisRemove attr) { |
| String option = attr.getValue(); |
| if (option.equals("remove")) { |
| tabs = SPACES; |
| } else if (option.equals("asis")) { |
| tabs = ASIS; |
| } else { |
| // must be "add" |
| tabs = TABS; |
| } |
| } |
| |
| /** |
| * Specify tab length in characters. |
| * |
| * @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", |
| location); |
| } |
| tablength = tlength; |
| StringBuffer sp = new StringBuffer(); |
| for (int i = 0; i < tablength; i++) { |
| sp.append(' '); |
| } |
| spaces = sp.toString(); |
| } |
| |
| /** |
| * Specify how DOS EOF (control-z) characters are to be handled. |
| * |
| * @param attr valid values: |
| * <ul> |
| * <li>add: ensure that there is an eof at the end of the file |
| * <li>asis: leave eof characters alone |
| * <li>remove: remove any eof character found at the end |
| * </ul> |
| */ |
| public void setEof(AddAsisRemove attr) { |
| String option = attr.getValue(); |
| if (option.equals("remove")) { |
| ctrlz = REMOVE; |
| } else if (option.equals("asis")) { |
| ctrlz = ASIS; |
| } else { |
| // must be "add" |
| ctrlz = ADD; |
| } |
| } |
| |
| /** |
| * Specifies the encoding Ant expects the files to be in - |
| * defaults to the platforms default encoding. |
| */ |
| public void setEncoding(String encoding) { |
| this.encoding = encoding; |
| } |
| |
| /** |
| * Executes the task. |
| */ |
| public void execute() throws BuildException { |
| // first off, make sure that we've got a srcdir and destdir |
| |
| if (srcDir == null) { |
| throw new BuildException("srcdir attribute must be set!"); |
| } |
| if (!srcDir.exists()) { |
| throw new BuildException("srcdir does not exist!"); |
| } |
| if (!srcDir.isDirectory()) { |
| throw new BuildException("srcdir is not a directory!"); |
| } |
| if (destDir != null) { |
| if (!destDir.exists()) { |
| throw new BuildException("destdir does not exist!"); |
| } |
| if (!destDir.isDirectory()) { |
| 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 + |
| " encoding=" + (encoding == null ? "default" : encoding), |
| Project.MSG_VERBOSE); |
| |
| DirectoryScanner ds = super.getDirectoryScanner(srcDir); |
| String[] files = ds.getIncludedFiles(); |
| |
| for (int i = 0; i < files.length; i++) { |
| processFile(files[i]); |
| } |
| } |
| |
| /** |
| * 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); |
| 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); |
| |
| try { |
| // Set up the output Writer |
| try { |
| tmpFile = fileUtils.createTempFile("fixcrlf", "", destD); |
| 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) |
| int ptr; |
| |
| while ((ptr = 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) |
| |
| try { |
| outWriter.write(eolstr); |
| } catch (IOException e) { |
| throw new BuildException(e); |
| } // end of try-catch |
| |
| } // 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 destFile = new File(destD, file); |
| |
| if (destFile.exists()) { |
| // Compare the destination with the temp file |
| log("destFile exists", Project.MSG_DEBUG); |
| if (!fileUtils.contentEquals(destFile, tmpFile)) { |
| log(destFile + " is being written", Project.MSG_DEBUG); |
| if (!destFile.delete()) { |
| throw new BuildException("Unable to delete " |
| + destFile); |
| } |
| if (!tmpFile.renameTo(destFile)) { |
| throw new BuildException( |
| "Failed to transform " + srcFile |
| + " to " + destFile |
| + ". Couldn't rename temporary file: " |
| + tmpFile); |
| } |
| |
| } else { // destination is equal to temp file |
| log(destFile + |
| " is not written, as the contents are identical", |
| Project.MSG_DEBUG); |
| if (!tmpFile.delete()) { |
| throw new BuildException("Unable to delete " |
| + tmpFile); |
| } |
| } |
| } else { // destFile does not exist - write the temp file |
| log("destFile does not exist", Project.MSG_DEBUG); |
| if (!tmpFile.renameTo(destFile)) { |
| throw new BuildException( |
| "Failed to transform " + srcFile |
| + " to " + destFile |
| + ". Couldn't rename temporary file: " |
| + tmpFile); |
| } |
| } |
| |
| 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 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); |
| } |
| |
| |
| /** |
| * 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 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"); |
| } |
| |
| |
| /** |
| * 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 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.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 StringBuffer line = new StringBuffer(); |
| private boolean reachedEof = false; |
| |
| public OneLiner(File srcFile) |
| throws BuildException { |
| try { |
| reader = new BufferedReader |
| (getReader(srcFile), INBUFLEN); |
| nextLine(); |
| } catch (IOException e) { |
| throw new BuildException(e); |
| } |
| } |
| |
| protected void nextLine() |
| throws BuildException { |
| int ch = -1; |
| int eolcount = 0; |
| |
| eolStr.setLength(0); |
| line.setLength(0); |
| |
| try { |
| ch = reader.read(); |
| while (ch != -1 && ch != '\r' && ch != '\n') { |
| line.append((char) ch); |
| ch = reader.read(); |
| } |
| |
| if (ch == -1 && line.length() == 0) { |
| // Eof has been reached |
| reachedEof = true; |
| return; |
| } |
| |
| 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'); |
| reader.mark(2); |
| switch ((ch = reader.read())) { |
| case '\r': |
| if ((char) (ch = reader.read()) == '\n') { |
| eolcount += 2; |
| eolStr.append("\r\n"); |
| } else { |
| reader.reset(); |
| } |
| break; |
| case '\n': |
| ++eolcount; |
| eolStr.append('\n'); |
| break; |
| case -1: |
| // don't reposition when we've reached the end |
| // of the stream |
| break; |
| default: |
| reader.reset(); |
| break; |
| } // end of switch ((char)(ch = reader.read())) |
| break; |
| |
| case '\n': |
| ++eolcount; |
| eolStr.append('\n'); |
| break; |
| |
| } // end of switch ((char) ch) |
| |
| // if at eolcount == 0 and trailing characters of string |
| // are CTRL-Zs, set eofStr |
| if (eolcount == 0) { |
| int i = line.length(); |
| while (--i >= 0 && line.charAt(i) == CTRLZ) { |
| // keep searching for the first ^Z |
| } |
| if (i < line.length() - 1) { |
| // Trailing characters are ^Zs |
| // Construct new line and eofStr |
| eofStr.append(line.toString().substring(i + 1)); |
| if (i < 0) { |
| line.setLength(0); |
| reachedEof = true; |
| } else { |
| line.setLength(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 !reachedEof; |
| } |
| |
| public Object nextElement() |
| throws NoSuchElementException { |
| if (!hasMoreElements()) { |
| throw new NoSuchElementException("OneLiner"); |
| } |
| BufferLine tmpLine = |
| new BufferLine(line.toString(), eolStr.toString()); |
| nextLine(); |
| return tmpLine; |
| } |
| |
| public void close() throws IOException { |
| if (reader != null) { |
| reader.close(); |
| } |
| } |
| |
| 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(); |
| } |
| } |
| } |
| |
| /** |
| * Enumerated attribute with the values "asis", "add" and "remove". |
| */ |
| public static class AddAsisRemove extends EnumeratedAttribute { |
| public String[] getValues() { |
| return new String[] {"add", "asis", "remove"}; |
| } |
| } |
| |
| /** |
| * Enumerated attribute with the values "asis", "cr", "lf" and "crlf". |
| */ |
| public static class CrLf extends EnumeratedAttribute { |
| /** |
| * @see EnumeratedAttribute#getValues |
| */ |
| public String[] getValues() { |
| return new String[] {"asis", "cr", "lf", "crlf"}; |
| } |
| } |
| |
| } |