jkf         2004/12/12 13:08:12

  Modified:    src/main/org/apache/tools/ant/taskdefs Replace.java
  Log:
  PR: 32566
  Real solution to allow big files in a replace action.
  
  Revision  Changes    Path
  1.56      +265 -55   ant/src/main/org/apache/tools/ant/taskdefs/Replace.java
  
  Index: Replace.java
  ===================================================================
  RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/Replace.java,v
  retrieving revision 1.55
  retrieving revision 1.56
  diff -u -r1.55 -r1.56
  --- Replace.java      11 Dec 2004 21:16:47 -0000      1.55
  +++ Replace.java      12 Dec 2004 21:08:12 -0000      1.56
  @@ -102,8 +102,12 @@
       public class Replacefilter {
           private String token;
           private String value;
  +        private String replaceValue;
           private String property;
   
  +        private StringBuffer inputBuffer;
  +        private StringBuffer outputBuffer = new StringBuffer();
  +
           /**
            * validate the filter's configuration
            * @throws BuildException if any part is invalid
  @@ -147,6 +151,8 @@
                       throw new BuildException(message);
                   }
               }
  +
  +            replaceValue = getReplaceValue();
           }
   
           /**
  @@ -216,6 +222,200 @@
           public String getProperty() {
               return property;
           }
  +
  +        /**
  +         * Retrieves the output buffer of this filter. The filter guarantees
  +         * that data is only appended to the end of this StringBuffer.
  +         * @return The StringBuffer containing the output of this filter.
  +         */
  +        StringBuffer getOutputBuffer() {
  +            return outputBuffer;
  +        }
  +
  +        /**
  +         * Sets the input buffer for this filter.
  +         * The filter expects from the component providing the input that 
data
  +         * is only addded by that component to the end of this StringBuffer.
  +         * This StringBuffer will be modified by this filter, and expects 
that
  +         * another component will only apped to this StringBuffer.
  +         * @param input The input for this filter.
  +         */
  +        void setInputBuffer(StringBuffer input) {
  +            inputBuffer = input;
  +        }
  +
  +        /**
  +         * Processes the buffer as far as possible. Takes into account that
  +         * appended data may make it possible to replace the end of the 
already
  +         * received data, when the token is split over the "old" and the 
"new"
  +         * part.
  +         * @return true if some data has been made available in the
  +         *         outputBuffer.
  +         */
  +        boolean process() {
  +            if (inputBuffer.length() > token.length()) {
  +                int pos = replace();
  +                pos = Math.max((inputBuffer.length() - token.length()), pos);
  +                outputBuffer.append(inputBuffer.substring(0, pos));
  +                inputBuffer.delete(0, pos);
  +                return true;
  +            }
  +            return false;
  +        }
  +
  +        /**
  +         * Processes the buffer to the end. Does not take into account that
  +         * appended data may make it possible to replace the end of the 
already
  +         * received data.
  +         */
  +        void flush() {
  +            int pos = replace();
  +            outputBuffer.append(inputBuffer);
  +            inputBuffer.delete(0, inputBuffer.length());
  +        }
  +
  +        /**
  +         * Performs the replace operation.
  +         * @return The position of the last character that was inserted as
  +         *         replacement.
  +         */
  +        private int replace() {
  +            int found = inputBuffer.toString().indexOf(token);
  +            int pos = -1;
  +            while (found >= 0) {
  +                inputBuffer.replace(found, found + token.length(),
  +                        replaceValue);
  +                pos = found + replaceValue.length();
  +                found = inputBuffer.toString().indexOf(token, pos);
  +                ++replaceCount;
  +            }
  +            return pos;
  +        }
  +    }
  +
  +    /**
  +     * Class reading a file in small chuncks, and presenting these chuncks in
  +     * a StringBuffer. Compatible with the Replacefilter.
  +     * @since 1.7
  +     */
  +    private class FileInput {
  +        private StringBuffer outputBuffer;
  +        private Reader reader;
  +        private char[] buffer;
  +        private static final int BUFF_SIZE = 4096;
  +
  +        /**
  +         * Constructs the input component. Opens the file for reading.
  +         * @param source The file to read from.
  +         * @throws IOException When the file cannot be read from.
  +         */
  +        FileInput(File source) throws IOException {
  +            outputBuffer = new StringBuffer();
  +            buffer = new char[BUFF_SIZE];
  +            if (encoding == null) {
  +                reader = new BufferedReader(new FileReader(source));
  +            } else {
  +                reader = new BufferedReader(new InputStreamReader(
  +                        new FileInputStream(source), encoding));
  +            }
  +        }
  +
  +        /**
  +         * Retrieves the output buffer of this filter. The component 
guarantees
  +         * that data is only appended to the end of this StringBuffer.
  +         * @return The StringBuffer containing the output of this filter.
  +         */
  +        StringBuffer getOutputBuffer() {
  +            return outputBuffer;
  +        }
  +
  +        /**
  +         * Reads some data from the file.
  +         * @return true when the end of the file has not been reached.
  +         * @throws IOException When the file cannot be read from.
  +         */
  +        boolean readChunck() throws IOException {
  +            int bufferLength = 0;
  +            bufferLength = reader.read(buffer);
  +            if (bufferLength < 0) {
  +                return false;
  +            }
  +            outputBuffer.append(new String(buffer, 0, bufferLength));
  +            return true;
  +        }
  +
  +        /**
  +         * Closes the file.
  +         * @throws IOException When the file cannot be closed.
  +         */
  +        void close() throws IOException {
  +            reader.close();
  +        }
  +
  +    }
  +
  +    /**
  +     * Component writing a file in chuncks, taking the chunks from the
  +     * Replacefilter.
  +     * @since 1.7
  +     */
  +    private class FileOutput {
  +        private StringBuffer inputBuffer;
  +        private Writer writer;
  +
  +        /**
  +         * Constructs the output component. Opens the file for writing.
  +         * @param source The file to read from.
  +         * @throws IOException When the file cannot be read from.
  +         */
  +        FileOutput(File out) throws IOException {
  +                if (encoding == null) {
  +                    writer = new BufferedWriter(new FileWriter(out));
  +                } else {
  +                    writer = new BufferedWriter(new OutputStreamWriter
  +                            (new FileOutputStream(out), encoding));
  +                }
  +        }
  +
  +        /**
  +         * Sets the input buffer for this component.
  +         * The filter expects from the component providing the input that 
data
  +         * is only addded by that component to the end of this StringBuffer.
  +         * This StringBuffer will be modified by this filter, and expects 
that
  +         * another component will only apped to this StringBuffer.
  +         * @param input The input for this filter.
  +         */
  +        void setInputBuffer(StringBuffer input) {
  +            inputBuffer = input;
  +        }
  +
  +        /**
  +         * Writes the buffer as far as possible.
  +         * @return false to be inline with the Replacefilter.
  +         * (Yes defining an interface crossed my mind, but would publish the
  +         * internal behavior.)
  +         */
  +        boolean process() throws IOException {
  +            writer.write(inputBuffer.toString());
  +            inputBuffer.delete(0, inputBuffer.length());
  +            return false;
  +        }
  +
  +        /**
  +         * Processes the buffer to the end.
  +         */
  +        void flush() throws IOException {
  +            process();
  +            writer.flush();
  +        }
  +
  +        /**
  +         * Closes the file.
  +         * @throws IOException When the file cannot be closed.
  +         */
  +        void close() throws IOException {
  +            writer.close();
  +        }
       }
   
       /**
  @@ -233,11 +433,11 @@
               // in order to compare with the file contents, replace them
               // as needed
               StringBuffer val = new StringBuffer(value.getText());
  -            stringReplace(val, "\r\n", "\n", false);
  -            stringReplace(val, "\n", StringUtils.LINE_SEP, false);
  +            stringReplace(val, "\r\n", "\n");
  +            stringReplace(val, "\n", StringUtils.LINE_SEP);
               StringBuffer tok = new StringBuffer(token.getText());
  -            stringReplace(tok, "\r\n", "\n", false);
  -            stringReplace(tok, "\n", StringUtils.LINE_SEP, false);
  +            stringReplace(tok, "\r\n", "\n");
  +            stringReplace(tok, "\n", StringUtils.LINE_SEP);
               Replacefilter firstFilter = createPrimaryfilter();
               firstFilter.setToken(tok.toString());
               firstFilter.setValue(val.toString());
  @@ -383,89 +583,103 @@
           }
   
           File temp = null;
  -        Reader reader = null;
  -        Writer writer = null;
  +        FileInput in = null;
  +        FileOutput out = null;
           try {
  -            reader = encoding == null ? new FileReader(src)
  -                : new InputStreamReader(new FileInputStream(src), encoding);
  +            in = new FileInput(src);
   
  -            BufferedReader br = new BufferedReader(reader);
  +            temp = fileUtils.createTempFile("rep", ".tmp",
  +                    src.getParentFile());
  +            out = new FileOutput(temp);
   
  -            String buf = FileUtils.readFully(br);
  -            br.close();
  -            reader = null;
  +            int repCountStart = replaceCount;
   
  -            if (buf == null) {
  -                buf = "";
  -            }
  +            out.setInputBuffer(buildFilterChain(in.getOutputBuffer()));
   
  -            StringBuffer buffer = new StringBuffer(buf);
  -            buf = null;
  +            while (in.readChunck()) {
  +                if (processFilterChain()) {
  +                    out.process();
  +                }
  +            }
   
  -            int repCountStart = replaceCount;
  +            flushFilterChain();
   
  -            processReplacefilters(buffer, src.getPath());
  +            out.flush();
  +            in.close();
  +            in = null;
  +            out.close();
  +            out = null;
   
               boolean changes = (replaceCount != repCountStart);
               if (changes) {
  -                String out = buffer.toString();
  -                temp = fileUtils.createTempFile("rep", ".tmp",
  -                        src.getParentFile());
  -                temp.deleteOnExit();
  -                writer = encoding == null ? new FileWriter(temp)
  -                        : new OutputStreamWriter(new FileOutputStream(temp), 
encoding);
  -                BufferedWriter bw = new BufferedWriter(writer);
  -                bw.write(out, 0, out.length());
  -                bw.flush();
  -                bw.close();
  -                writer = null;
  -                ++fileCount;
                   fileUtils.rename(temp, src);
                   temp = null;
               }
           } catch (IOException ioe) {
               throw new BuildException("IOException in " + src + " - "
  -                                    + ioe.getClass().getName() + ":"
  -                                    + ioe.getMessage(), ioe, getLocation());
  +                    + ioe.getClass().getName() + ":"
  +                    + ioe.getMessage(), ioe, getLocation());
           } finally {
  -            if (reader != null) {
  +            if (in != null) {
                   try {
  -                    reader.close();
  +                    in.close();
                   } catch (IOException e) {
                       // ignore
                   }
               }
  -            if (writer != null) {
  +            if (out != null) {
                   try {
  -                    writer.close();
  +                    out.close();
                   } catch (IOException e) {
                       // ignore
                   }
               }
               if (temp != null) {
  -                temp.delete();
  +                if (!temp.delete()) {
  +                    temp.deleteOnExit();
  +                }
               }
           }
  -
       }
   
       /**
  -     * apply all replace filters to a buffer
  -     * @param buffer stringbuffer to filter
  -     * @param filename filename for logging purposes
  +     * Flushes all filters.
        */
  -    private void processReplacefilters(StringBuffer buffer, String filename) 
{
  +    private void flushFilterChain() {
           for (int i = 0; i < replacefilters.size(); i++) {
               Replacefilter filter = (Replacefilter) 
replacefilters.elementAt(i);
  +            filter.flush();
  +        }
  +    }
   
  -            //for each found token, replace with value
  -            log("Replacing in " + filename + ": " + filter.getToken()
  -                + " --> " + filter.getReplaceValue(), Project.MSG_VERBOSE);
  -            stringReplace(buffer, filter.getToken(),
  -                                      filter.getReplaceValue(), true);
  +    /**
  +     * Performs the normal processing of the filters.
  +     * @return true if the filter chain produced new output.
  +     */
  +    private boolean processFilterChain() {
  +        for (int i = 0; i < replacefilters.size(); i++) {
  +            Replacefilter filter = (Replacefilter) 
replacefilters.elementAt(i);
  +            if (!filter.process()) {
  +                return false;
  +            }
           }
  +        return true;
       }
   
  +    /**
  +     * Creates the chain of filters to operate.
  +     * @param inputBuffer The buffer that contains the input for the first 
filter
  +     * @return The StringBuffer that cointains the output of the last filter.
  +     */
  +    private StringBuffer buildFilterChain(StringBuffer inputBuffer) {
  +        StringBuffer buf = inputBuffer;
  +        for (int i = 0; i < replacefilters.size(); i++) {
  +            Replacefilter filter = (Replacefilter) 
replacefilters.elementAt(i);
  +            filter.setInputBuffer(buf);
  +            buf = filter.getOutputBuffer();
  +        }
  +        return buf;
  +    }
   
       /**
        * Set the source file; required unless <code>dir</code> is set.
  @@ -591,15 +805,11 @@
       /**
        * Replace occurrences of str1 in stringbuffer str with str2
        */
  -    private void stringReplace(StringBuffer str, String str1, String str2,
  -                                 boolean countReplaces) {
  -        int found = str.indexOf(str1);
  +    private void stringReplace(StringBuffer str, String str1, String str2) {
  +        int found = str.toString().indexOf(str1);
           while (found >= 0) {
               str.replace(found, found + str1.length(), str2);
  -            found = str.indexOf(str1, found + str2.length());
  -            if (countReplaces) {
  -                ++replaceCount;
  -            }
  +            found = str.toString().indexOf(str1, found + str2.length());
           }
       }
   
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to