diff --git a/src/main/org/apache/tools/ant/util/LineOrientedOutputStream.java b/src/main/org/apache/tools/ant/util/LineOrientedOutputStream.java new file mode 100644 index 000000000..86dfe1dc2 --- /dev/null +++ b/src/main/org/apache/tools/ant/util/LineOrientedOutputStream.java @@ -0,0 +1,134 @@ +/* + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Invokes {@link #processLine processLine} whenever a full line has + * been written to this stream. + * + *
Tries to be smart about line separators.
+ */ +public abstract class LineOrientedOutputStream extends OutputStream { + + /** Initial buffer size. */ + private static final int INTIAL_SIZE = 132; + + /** Carriage return */ + private static final int CR = 0x0d; + + /** Linefeed */ + private static final int LF = 0x0a; + + private ByteArrayOutputStream buffer + = new ByteArrayOutputStream(INTIAL_SIZE); + private boolean skip = false; + + /** + * Write the data to the buffer and flush the buffer, if a line + * separator is detected. + * + * @param cc data to log (byte). + */ + public final void write(int cc) throws IOException { + final byte c = (byte) cc; + if ((c == LF) || (c == CR)) { + if (!skip) { + processBuffer(); + } + } else { + buffer.write(cc); + } + skip = (c == CR); + } + + /** + * Flush this log stream + */ + public final void flush() throws IOException { + if (buffer.size() > 0) { + processBuffer(); + } + } + + /** + * Converts the buffer to a string and sends it to + *processLine
+ */
+ private void processBuffer() throws IOException {
+ try {
+ processLine(buffer.toString());
+ } finally {
+ buffer.reset();
+ }
+ }
+
+ /**
+ * Processes a line.
+ *
+ * @param line the line to log.
+ */
+ protected abstract void processLine(String line) throws IOException;
+
+ /**
+ * Writes all remaining
+ */
+ public final void close() throws IOException {
+ if (buffer.size() > 0) {
+ processBuffer();
+ }
+ super.close();
+ }
+
+ /**
+ * Write a block of characters to the output stream
+ *
+ * @param b the array containing the data
+ * @param off the offset into the array where data starts
+ * @param len the length of block
+ *
+ * @throws IOException if the data cannot be written into the stream.
+ */
+ public final void write(byte[] b, int off, int len) throws IOException {
+ // find the line breaks and pass other chars through in blocks
+ int offset = off;
+ int blockStartOffset = offset;
+ int remaining = len;
+ while (remaining > 0) {
+ while (remaining > 0 && b[offset] != LF && b[offset] != CR) {
+ offset++;
+ remaining--;
+ }
+ // either end of buffer or a line separator char
+ int blockLength = offset - blockStartOffset;
+ if (blockLength > 0) {
+ buffer.write(b, blockStartOffset, blockLength);
+ }
+ while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) {
+ write(b[offset]);
+ offset++;
+ remaining--;
+ }
+ blockStartOffset = offset;
+ }
+ }
+
+}
diff --git a/src/testcases/org/apache/tools/ant/util/LineOrientedOutputStreamTest.java b/src/testcases/org/apache/tools/ant/util/LineOrientedOutputStreamTest.java
new file mode 100644
index 000000000..9c08aec67
--- /dev/null
+++ b/src/testcases/org/apache/tools/ant/util/LineOrientedOutputStreamTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.io.IOException;
+import junit.framework.TestCase;
+
+/**
+ */
+
+public class LineOrientedOutputStreamTest extends TestCase {
+
+ private static String LINE = "This is a line";
+ private DummyStream stream;
+
+ public LineOrientedOutputStreamTest(String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ stream = new DummyStream();
+ }
+
+ public void tearDown() throws IOException {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+
+ public void testLineWithLinefeedArray() throws IOException {
+ writeByteArray();
+ writeAsArray('\n');
+ stream.assertInvoked();
+ }
+
+ public void testLineWithLinefeedSingleBytes() throws IOException {
+ writeSingleBytes();
+ stream.write('\n');
+ stream.assertInvoked();
+ }
+
+ public void testLineWithCariagereturnArray() throws IOException {
+ writeByteArray();
+ writeAsArray('\r');
+ stream.assertInvoked();
+ }
+
+ public void testLineWithCariagereturnSingleBytes() throws IOException {
+ writeSingleBytes();
+ stream.write('\r');
+ stream.assertInvoked();
+ }
+
+ public void testLineWithCariagereturnLinefeedArray() throws IOException {
+ writeByteArray();
+ writeAsArray('\r');
+ writeAsArray('\n');
+ stream.assertInvoked();
+ }
+
+ public void testLineWithCariagereturnLinefeedSingleBytes() throws IOException {
+ writeSingleBytes();
+ stream.write('\r');
+ stream.write('\n');
+ stream.assertInvoked();
+ }
+
+ public void testFlushArray() throws IOException {
+ writeByteArray();
+ stream.flush();
+ stream.assertInvoked();
+ }
+
+ public void testFlushSingleBytes() throws IOException {
+ writeSingleBytes();
+ stream.flush();
+ stream.assertInvoked();
+ }
+
+ public void testCloseArray() throws IOException {
+ writeByteArray();
+ stream.close();
+ stream.assertInvoked();
+ stream = null;
+ }
+
+ public void testCloseSingleBytes() throws IOException {
+ writeSingleBytes();
+ stream.close();
+ stream.assertInvoked();
+ stream = null;
+ }
+
+ private void writeByteArray() throws IOException {
+ stream.write(LINE.getBytes(), 0, LINE.length());
+ }
+
+ private void writeSingleBytes() throws IOException {
+ byte[] b = LINE.getBytes();
+ for (int i = 0; i < b.length; i++) {
+ stream.write(b[i]);
+ }
+ }
+
+ private void writeAsArray(char c) throws IOException {
+ stream.write(new byte[] {(byte) c}, 0, 1);
+ }
+
+ private class DummyStream extends LineOrientedOutputStream {
+ private boolean invoked;
+ protected void processLine(String line) {
+ LineOrientedOutputStreamTest.this.assertFalse("Only one line",
+ invoked);
+ LineOrientedOutputStreamTest.this.assertEquals(LINE, line);
+ invoked = true;
+ }
+
+ private void assertInvoked() {
+ LineOrientedOutputStreamTest.this.assertTrue("At least one line",
+ invoked);
+ }
+ }
+}// LineOrientedOutputStreamTest