1 /*
2  * reserved comment block
3  * DO NOT REMOVE OR ALTER!
4  */
5 /*
6  * Licensed to the Apache Software Foundation (ASF) under one or more
7  * contributor license agreements.  See the NOTICE file distributed with
8  * this work for additional information regarding copyright ownership.
9  * The ASF licenses this file to You under the Apache License, Version 2.0
10  * (the "License"); you may not use this file except in compliance with
11  * the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 package com.sun.org.apache.xml.internal.serializer;
23 
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.io.Writer;
27 
28 /**
29  * This class wraps the real writer, it only purpose is to send
30  * CHARACTERTOSTREAM events to the trace listener.
31  * Each method immediately sends the call to the wrapped writer unchanged, but
32  * in addition it collects characters to be issued to a trace listener.
33  *
34  * In this way the trace
35  * listener knows what characters have been written to the output Writer.
36  *
37  * There may still be differences in what the trace events say is going to the
38  * output writer and what is really going there. These differences will be due
39  * to the fact that this class is UTF-8 encoding before emiting the trace event
40  * and the underlying writer may not be UTF-8 encoding. There may also be
41  * encoding differences.  So the main pupose of this class is to provide a
42  * resonable facsimile of the true output.
43  *
44  * @xsl.usage internal
45  */
46 final class SerializerTraceWriter extends Writer implements WriterChain
47 {
48 
49     /** The real writer to immediately write to.
50      * This reference may be null, in which case nothing is written out, but
51      * only the trace events are fired for output.
52      */
53     private final java.io.Writer m_writer;
54 
55     /** The tracer to send events to */
56     private final SerializerTrace m_tracer;
57 
58     /** The size of the internal buffer, just to keep too many
59      * events from being sent to the tracer
60      */
61     private int buf_length;
62 
63     /**
64      * Internal buffer to collect the characters to go to the trace listener.
65      *
66      */
67     private byte buf[];
68 
69     /**
70      * How many bytes have been collected and still need to go to trace
71      * listener.
72      */
73     private int count;
74 
75     /**
76      * Creates or replaces the internal buffer, and makes sure it has a few
77      * extra bytes slight overflow of the last UTF8 encoded character.
78      * @param size
79      */
setBufferSize(int size)80     private void setBufferSize(int size)
81     {
82         buf = new byte[size + 3];
83         buf_length = size;
84         count = 0;
85     }
86 
87     /**
88      * Constructor.
89      * If the writer passed in is null, then this SerializerTraceWriter will
90      * only signal trace events of what would have been written to that writer.
91      * If the writer passed in is not null then the trace events will mirror
92      * what is going to that writer. In this way tools, such as a debugger, can
93      * gather information on what is being written out.
94      *
95      * @param out the Writer to write to (possibly null)
96      * @param tracer the tracer to inform that characters are being written
97      */
SerializerTraceWriter(Writer out, SerializerTrace tracer)98     public SerializerTraceWriter(Writer out, SerializerTrace tracer)
99     {
100         m_writer = out;
101         m_tracer = tracer;
102         setBufferSize(1024);
103     }
104 
105     /**
106      * Flush out the collected characters by sending them to the trace
107      * listener.  These characters are never written to the real writer
108      * (m_writer) because that has already happened with every method
109      * call. This method simple informs the listener of what has already
110      * happened.
111      * @throws IOException
112      */
flushBuffer()113     private void flushBuffer() throws IOException
114     {
115 
116         // Just for tracing purposes
117         if (count > 0)
118         {
119             char[] chars = new char[count];
120             for(int i=0; i<count; i++)
121                 chars[i] = (char) buf[i];
122 
123             if (m_tracer != null)
124                 m_tracer.fireGenerateEvent(
125                     SerializerTrace.EVENTTYPE_OUTPUT_CHARACTERS,
126                     chars,
127                     0,
128                     chars.length);
129 
130             count = 0;
131         }
132     }
133 
134     /**
135      * Flush the internal buffer and flush the Writer
136      * @see java.io.Writer#flush()
137      */
flush()138     public void flush() throws java.io.IOException
139     {
140         // send to the real writer
141         if (m_writer != null)
142             m_writer.flush();
143 
144         // from here on just for tracing purposes
145         flushBuffer();
146     }
147 
148     /**
149      * Flush the internal buffer and close the Writer
150      * @see java.io.Writer#close()
151      */
close()152     public void close() throws java.io.IOException
153     {
154         // send to the real writer
155         if (m_writer != null)
156             m_writer.close();
157 
158         // from here on just for tracing purposes
159         flushBuffer();
160     }
161 
162 
163     /**
164      * Write a single character.  The character to be written is contained in
165      * the 16 low-order bits of the given integer value; the 16 high-order bits
166      * are ignored.
167      *
168      * <p> Subclasses that intend to support efficient single-character output
169      * should override this method.
170      *
171      * @param c  int specifying a character to be written.
172      * @exception  IOException  If an I/O error occurs
173      */
write(final int c)174     public void write(final int c) throws IOException
175     {
176         // send to the real writer
177         if (m_writer != null)
178             m_writer.write(c);
179 
180         // ---------- from here on just collect for tracing purposes
181 
182         /* If we are close to the end of the buffer then flush it.
183          * Remember the buffer can hold a few more characters than buf_length
184          */
185         if (count >= buf_length)
186             flushBuffer();
187 
188         if (c < 0x80)
189         {
190             buf[count++] = (byte) (c);
191         }
192         else if (c < 0x800)
193         {
194             buf[count++] = (byte) (0xc0 + (c >> 6));
195             buf[count++] = (byte) (0x80 + (c & 0x3f));
196         }
197         else
198         {
199             buf[count++] = (byte) (0xe0 + (c >> 12));
200             buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
201             buf[count++] = (byte) (0x80 + (c & 0x3f));
202         }
203     }
204 
205     /**
206      * Write a portion of an array of characters.
207      *
208      * @param  chars  Array of characters
209      * @param  start   Offset from which to start writing characters
210      * @param  length   Number of characters to write
211      *
212      * @exception  IOException  If an I/O error occurs
213      *
214      * @throws java.io.IOException
215      */
write(final char chars[], final int start, final int length)216     public void write(final char chars[], final int start, final int length)
217         throws java.io.IOException
218     {
219         // send to the real writer
220         if (m_writer != null)
221             m_writer.write(chars, start, length);
222 
223         // from here on just collect for tracing purposes
224         int lengthx3 = (length << 1) + length;
225 
226         if (lengthx3 >= buf_length)
227         {
228 
229             /* If the request length exceeds the size of the output buffer,
230               * flush the output buffer and make the buffer bigger to handle.
231               */
232 
233             flushBuffer();
234             setBufferSize(2 * lengthx3);
235 
236         }
237 
238         if (lengthx3 > buf_length - count)
239         {
240             flushBuffer();
241         }
242 
243         final int n = length + start;
244         for (int i = start; i < n; i++)
245         {
246             final char c = chars[i];
247 
248             if (c < 0x80)
249                 buf[count++] = (byte) (c);
250             else if (c < 0x800)
251             {
252                 buf[count++] = (byte) (0xc0 + (c >> 6));
253                 buf[count++] = (byte) (0x80 + (c & 0x3f));
254             }
255             else
256             {
257                 buf[count++] = (byte) (0xe0 + (c >> 12));
258                 buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
259                 buf[count++] = (byte) (0x80 + (c & 0x3f));
260             }
261         }
262 
263     }
264 
265     /**
266      * Write a string.
267      *
268      * @param  s  String to be written
269      *
270      * @exception  IOException  If an I/O error occurs
271      */
write(final String s)272     public void write(final String s) throws IOException
273     {
274         // send to the real writer
275         if (m_writer != null)
276             m_writer.write(s);
277 
278         // from here on just collect for tracing purposes
279         final int length = s.length();
280 
281         // We multiply the length by three since this is the maximum length
282         // of the characters that we can put into the buffer.  It is possible
283         // for each Unicode character to expand to three bytes.
284 
285         int lengthx3 = (length << 1) + length;
286 
287         if (lengthx3 >= buf_length)
288         {
289 
290             /* If the request length exceeds the size of the output buffer,
291               * flush the output buffer and make the buffer bigger to handle.
292               */
293 
294             flushBuffer();
295             setBufferSize(2 * lengthx3);
296         }
297 
298         if (lengthx3 > buf_length - count)
299         {
300             flushBuffer();
301         }
302 
303         for (int i = 0; i < length; i++)
304         {
305             final char c = s.charAt(i);
306 
307             if (c < 0x80)
308                 buf[count++] = (byte) (c);
309             else if (c < 0x800)
310             {
311                 buf[count++] = (byte) (0xc0 + (c >> 6));
312                 buf[count++] = (byte) (0x80 + (c & 0x3f));
313             }
314             else
315             {
316                 buf[count++] = (byte) (0xe0 + (c >> 12));
317                 buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
318                 buf[count++] = (byte) (0x80 + (c & 0x3f));
319             }
320         }
321     }
322 
323     /**
324      * Get the writer that this one directly wraps.
325      */
getWriter()326     public Writer getWriter()
327     {
328         return m_writer;
329     }
330 
331     /**
332      * Get the OutputStream that is the at the end of the
333      * chain of writers.
334      */
getOutputStream()335     public OutputStream getOutputStream()
336     {
337         OutputStream retval = null;
338         if (m_writer instanceof WriterChain)
339             retval = ((WriterChain) m_writer).getOutputStream();
340         return retval;
341     }
342 }
343