source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/DemuxOutputStream.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

File size: 7.9 KB
Line 
1/*
2 * Copyright 2001-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant;
19
20import java.io.ByteArrayOutputStream;
21import java.io.IOException;
22import java.io.OutputStream;
23import java.util.Hashtable;
24
25/**
26 * Logs content written by a thread and forwards the buffers onto the
27 * project object which will forward the content to the appropriate
28 * task.
29 *
30 * @since 1.4
31 */
32public class DemuxOutputStream extends OutputStream {
33
34 /**
35 * A data class to store information about a buffer. Such information
36 * is stored on a per-thread basis.
37 */
38 private static class BufferInfo {
39 /**
40 * The per-thread output stream.
41 */
42 private ByteArrayOutputStream buffer;
43
44 /**
45 * Indicates we have just seen a carriage return. It may be part of
46 * a crlf pair or a single cr invoking processBuffer twice.
47 */
48 private boolean crSeen = false;
49 }
50
51 /** Maximum buffer size. */
52 private static final int MAX_SIZE = 1024;
53
54 /** Initial buffer size. */
55 private static final int INTIAL_SIZE = 132;
56
57 /** Carriage return */
58 private static final int CR = 0x0d;
59
60 /** Linefeed */
61 private static final int LF = 0x0a;
62
63 /** Mapping from thread to buffer (Thread to BufferInfo). */
64 private Hashtable buffers = new Hashtable();
65
66 /**
67 * The project to send output to.
68 */
69 private Project project;
70
71 /**
72 * Whether or not this stream represents an error stream.
73 */
74 private boolean isErrorStream;
75
76 /**
77 * Creates a new instance of this class.
78 *
79 * @param project The project instance for which output is being
80 * demultiplexed. Must not be <code>null</code>.
81 * @param isErrorStream <code>true</code> if this is the error string,
82 * otherwise a normal output stream. This is
83 * passed to the project so it knows
84 * which stream it is receiving.
85 */
86 public DemuxOutputStream(Project project, boolean isErrorStream) {
87 this.project = project;
88 this.isErrorStream = isErrorStream;
89 }
90
91 /**
92 * Returns the buffer associated with the current thread.
93 *
94 * @return a BufferInfo for the current thread to write data to
95 */
96 private BufferInfo getBufferInfo() {
97 Thread current = Thread.currentThread();
98 BufferInfo bufferInfo = (BufferInfo) buffers.get(current);
99 if (bufferInfo == null) {
100 bufferInfo = new BufferInfo();
101 bufferInfo.buffer = new ByteArrayOutputStream(INTIAL_SIZE);
102 bufferInfo.crSeen = false;
103 buffers.put(current, bufferInfo);
104 }
105 return bufferInfo;
106 }
107
108 /**
109 * Resets the buffer for the current thread.
110 */
111 private void resetBufferInfo() {
112 Thread current = Thread.currentThread();
113 BufferInfo bufferInfo = (BufferInfo) buffers.get(current);
114 try {
115 bufferInfo.buffer.close();
116 } catch (IOException e) {
117 // Shouldn't happen
118 }
119 bufferInfo.buffer = new ByteArrayOutputStream();
120 bufferInfo.crSeen = false;
121 }
122
123 /**
124 * Removes the buffer for the current thread.
125 */
126 private void removeBuffer() {
127 Thread current = Thread.currentThread();
128 buffers.remove (current);
129 }
130
131 /**
132 * Writes the data to the buffer and flushes the buffer if a line
133 * separator is detected or if the buffer has reached its maximum size.
134 *
135 * @param cc data to log (byte).
136 * @exception IOException if the data cannot be written to the stream
137 */
138 public void write(int cc) throws IOException {
139 final byte c = (byte) cc;
140
141 BufferInfo bufferInfo = getBufferInfo();
142
143 if (c == '\n') {
144 // LF is always end of line (i.e. CRLF or single LF)
145 bufferInfo.buffer.write(cc);
146 processBuffer(bufferInfo.buffer);
147 } else {
148 if (bufferInfo.crSeen) {
149 // CR without LF - send buffer then add char
150 processBuffer(bufferInfo.buffer);
151 }
152 // add into buffer
153 bufferInfo.buffer.write(cc);
154 }
155 bufferInfo.crSeen = (c == '\r');
156 if (!bufferInfo.crSeen && bufferInfo.buffer.size() > MAX_SIZE) {
157 processBuffer(bufferInfo.buffer);
158 }
159 }
160
161 /**
162 * Converts the buffer to a string and sends it to the project.
163 *
164 * @param buffer the ByteArrayOutputStream used to collect the output
165 * until a line separator is seen.
166 *
167 * @see Project#demuxOutput(String,boolean)
168 */
169 protected void processBuffer(ByteArrayOutputStream buffer) {
170 String output = buffer.toString();
171 project.demuxOutput(output, isErrorStream);
172 resetBufferInfo();
173 }
174
175 /**
176 * Converts the buffer to a string and sends it to the project.
177 *
178 * @param buffer the ByteArrayOutputStream used to collect the output
179 * until a line separator is seen.
180 *
181 * @see Project#demuxOutput(String,boolean)
182 */
183 protected void processFlush(ByteArrayOutputStream buffer) {
184 String output = buffer.toString();
185 project.demuxFlush(output, isErrorStream);
186 resetBufferInfo();
187 }
188
189 /**
190 * Equivalent to flushing the stream.
191 *
192 * @exception IOException if there is a problem closing the stream.
193 *
194 * @see #flush
195 */
196 public void close() throws IOException {
197 flush();
198 removeBuffer();
199 }
200
201 /**
202 * Writes all remaining data in the buffer associated
203 * with the current thread to the project.
204 *
205 * @exception IOException if there is a problem flushing the stream.
206 */
207 public void flush() throws IOException {
208 BufferInfo bufferInfo = getBufferInfo();
209 if (bufferInfo.buffer.size() > 0) {
210 processFlush(bufferInfo.buffer);
211 }
212 }
213
214 /**
215 * Write a block of characters to the output stream
216 *
217 * @param b the array containing the data
218 * @param off the offset into the array where data starts
219 * @param len the length of block
220 *
221 * @throws IOException if the data cannot be written into the stream.
222 */
223 public void write(byte[] b, int off, int len) throws IOException {
224 // find the line breaks and pass other chars through in blocks
225 int offset = off;
226 int blockStartOffset = offset;
227 int remaining = len;
228 BufferInfo bufferInfo = getBufferInfo();
229 while (remaining > 0) {
230 while (remaining > 0 && b[offset] != LF && b[offset] != CR) {
231 offset++;
232 remaining--;
233 }
234 // either end of buffer or a line separator char
235 int blockLength = offset - blockStartOffset;
236 if (blockLength > 0) {
237 bufferInfo.buffer.write(b, blockStartOffset, blockLength);
238 }
239 while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) {
240 write(b[offset]);
241 offset++;
242 remaining--;
243 }
244 blockStartOffset = offset;
245 }
246 }
247}
Note: See TracBrowser for help on using the repository browser.