Return-Path: Delivered-To: apmail-ant-dev-archive@www.apache.org Received: (qmail 62342 invoked from network); 12 Dec 2004 21:08:16 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur-2.apache.org with SMTP; 12 Dec 2004 21:08:16 -0000 Received: (qmail 40308 invoked by uid 500); 12 Dec 2004 21:08:14 -0000 Delivered-To: apmail-ant-dev-archive@ant.apache.org Received: (qmail 40240 invoked by uid 500); 12 Dec 2004 21:08:14 -0000 Mailing-List: contact dev-help@ant.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Ant Developers List" Reply-To: "Ant Developers List" Delivered-To: mailing list dev@ant.apache.org Received: (qmail 40227 invoked by uid 500); 12 Dec 2004 21:08:14 -0000 Received: (qmail 40222 invoked by uid 99); 12 Dec 2004 21:08:14 -0000 X-ASF-Spam-Status: No, hits=-10.0 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from minotaur.apache.org (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.28) with SMTP; Sun, 12 Dec 2004 13:08:13 -0800 Received: (qmail 62312 invoked by uid 1985); 12 Dec 2004 21:08:12 -0000 Date: 12 Dec 2004 21:08:12 -0000 Message-ID: <20041212210812.62311.qmail@minotaur.apache.org> From: jkf@apache.org To: ant-cvs@apache.org Subject: cvs commit: ant/src/main/org/apache/tools/ant/taskdefs Replace.java X-Virus-Checked: Checked X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N 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 dir 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: dev-unsubscribe@ant.apache.org For additional commands, e-mail: dev-help@ant.apache.org