KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > DemuxOutputStream


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18
19 package org.apache.tools.ant;
20
21 import java.io.ByteArrayOutputStream JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.io.OutputStream JavaDoc;
24 import java.util.WeakHashMap JavaDoc;
25
26 /**
27  * Logs content written by a thread and forwards the buffers onto the
28  * project object which will forward the content to the appropriate
29  * task.
30  *
31  * @since 1.4
32  */

33 public class DemuxOutputStream extends OutputStream JavaDoc {
34
35     /**
36      * A data class to store information about a buffer. Such information
37      * is stored on a per-thread basis.
38      */

39     private static class BufferInfo {
40         /**
41          * The per-thread output stream.
42          */

43         private ByteArrayOutputStream JavaDoc buffer;
44
45         /**
46          * Indicates we have just seen a carriage return. It may be part of
47          * a crlf pair or a single cr invoking processBuffer twice.
48          */

49          private boolean crSeen = false;
50     }
51
52     /** Maximum buffer size. */
53     private static final int MAX_SIZE = 1024;
54
55     /** Initial buffer size. */
56     private static final int INTIAL_SIZE = 132;
57
58     /** Carriage return */
59     private static final int CR = 0x0d;
60
61     /** Linefeed */
62     private static final int LF = 0x0a;
63
64     /** Mapping from thread to buffer (Thread to BufferInfo). */
65     private WeakHashMap JavaDoc buffers = new WeakHashMap JavaDoc();
66
67     /**
68      * The project to send output to.
69      */

70     private Project project;
71
72     /**
73      * Whether or not this stream represents an error stream.
74      */

75     private boolean isErrorStream;
76
77     /**
78      * Creates a new instance of this class.
79      *
80      * @param project The project instance for which output is being
81      * demultiplexed. Must not be <code>null</code>.
82      * @param isErrorStream <code>true</code> if this is the error string,
83      * otherwise a normal output stream. This is
84      * passed to the project so it knows
85      * which stream it is receiving.
86      */

87     public DemuxOutputStream(Project project, boolean isErrorStream) {
88         this.project = project;
89         this.isErrorStream = isErrorStream;
90     }
91
92     /**
93      * Returns the buffer associated with the current thread.
94      *
95      * @return a BufferInfo for the current thread to write data to
96      */

97     private BufferInfo getBufferInfo() {
98         Thread JavaDoc current = Thread.currentThread();
99         BufferInfo bufferInfo = (BufferInfo) buffers.get(current);
100         if (bufferInfo == null) {
101             bufferInfo = new BufferInfo();
102             bufferInfo.buffer = new ByteArrayOutputStream JavaDoc(INTIAL_SIZE);
103             bufferInfo.crSeen = false;
104             buffers.put(current, bufferInfo);
105         }
106         return bufferInfo;
107     }
108
109     /**
110      * Resets the buffer for the current thread.
111      */

112     private void resetBufferInfo() {
113         Thread JavaDoc current = Thread.currentThread();
114         BufferInfo bufferInfo = (BufferInfo) buffers.get(current);
115         try {
116             bufferInfo.buffer.close();
117         } catch (IOException JavaDoc e) {
118             // Shouldn't happen
119
}
120         bufferInfo.buffer = new ByteArrayOutputStream JavaDoc();
121         bufferInfo.crSeen = false;
122     }
123
124     /**
125      * Removes the buffer for the current thread.
126      */

127     private void removeBuffer() {
128         Thread JavaDoc current = Thread.currentThread();
129         buffers.remove (current);
130     }
131
132     /**
133      * Writes the data to the buffer and flushes the buffer if a line
134      * separator is detected or if the buffer has reached its maximum size.
135      *
136      * @param cc data to log (byte).
137      * @exception IOException if the data cannot be written to the stream
138      */

139     public void write(int cc) throws IOException JavaDoc {
140         final byte c = (byte) cc;
141
142         BufferInfo bufferInfo = getBufferInfo();
143
144         if (c == '\n') {
145             // LF is always end of line (i.e. CRLF or single LF)
146
bufferInfo.buffer.write(cc);
147             processBuffer(bufferInfo.buffer);
148         } else {
149             if (bufferInfo.crSeen) {
150                 // CR without LF - send buffer then add char
151
processBuffer(bufferInfo.buffer);
152             }
153             // add into buffer
154
bufferInfo.buffer.write(cc);
155         }
156         bufferInfo.crSeen = (c == '\r');
157         if (!bufferInfo.crSeen && bufferInfo.buffer.size() > MAX_SIZE) {
158             processBuffer(bufferInfo.buffer);
159         }
160     }
161
162     /**
163      * Converts the buffer to a string and sends it to the project.
164      *
165      * @param buffer the ByteArrayOutputStream used to collect the output
166      * until a line separator is seen.
167      *
168      * @see Project#demuxOutput(String,boolean)
169      */

170     protected void processBuffer(ByteArrayOutputStream JavaDoc buffer) {
171         String JavaDoc output = buffer.toString();
172         project.demuxOutput(output, isErrorStream);
173         resetBufferInfo();
174     }
175
176     /**
177      * Converts the buffer to a string and sends it to the project.
178      *
179      * @param buffer the ByteArrayOutputStream used to collect the output
180      * until a line separator is seen.
181      *
182      * @see Project#demuxOutput(String,boolean)
183      */

184     protected void processFlush(ByteArrayOutputStream JavaDoc buffer) {
185         String JavaDoc output = buffer.toString();
186         project.demuxFlush(output, isErrorStream);
187         resetBufferInfo();
188     }
189
190     /**
191      * Equivalent to flushing the stream.
192      *
193      * @exception IOException if there is a problem closing the stream.
194      *
195      * @see #flush
196      */

197     public void close() throws IOException JavaDoc {
198         flush();
199         removeBuffer();
200     }
201
202     /**
203      * Writes all remaining data in the buffer associated
204      * with the current thread to the project.
205      *
206      * @exception IOException if there is a problem flushing the stream.
207      */

208     public void flush() throws IOException JavaDoc {
209         BufferInfo bufferInfo = getBufferInfo();
210         if (bufferInfo.buffer.size() > 0) {
211             processFlush(bufferInfo.buffer);
212         }
213     }
214
215     /**
216      * Write a block of characters to the output stream
217      *
218      * @param b the array containing the data
219      * @param off the offset into the array where data starts
220      * @param len the length of block
221      *
222      * @throws IOException if the data cannot be written into the stream.
223      */

224     public void write(byte[] b, int off, int len) throws IOException JavaDoc {
225         // find the line breaks and pass other chars through in blocks
226
int offset = off;
227         int blockStartOffset = offset;
228         int remaining = len;
229         BufferInfo bufferInfo = getBufferInfo();
230         while (remaining > 0) {
231             while (remaining > 0 && b[offset] != LF && b[offset] != CR) {
232                 offset++;
233                 remaining--;
234             }
235             // either end of buffer or a line separator char
236
int blockLength = offset - blockStartOffset;
237             if (blockLength > 0) {
238                 bufferInfo.buffer.write(b, blockStartOffset, blockLength);
239             }
240             while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) {
241                 write(b[offset]);
242                 offset++;
243                 remaining--;
244             }
245             blockStartOffset = offset;
246         }
247     }
248 }
249
Popular Tags