001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: SerializerTraceWriter.java 468654 2006-10-28 07:09:23Z minchau $
020 */
021 package org.apache.xml.serializer;
022
023 import java.io.IOException;
024 import java.io.OutputStream;
025 import java.io.Writer;
026
027 /**
028 * This class wraps the real writer, it only purpose is to send
029 * CHARACTERTOSTREAM events to the trace listener.
030 * Each method immediately sends the call to the wrapped writer unchanged, but
031 * in addition it collects characters to be issued to a trace listener.
032 *
033 * In this way the trace
034 * listener knows what characters have been written to the output Writer.
035 *
036 * There may still be differences in what the trace events say is going to the
037 * output writer and what is really going there. These differences will be due
038 * to the fact that this class is UTF-8 encoding before emiting the trace event
039 * and the underlying writer may not be UTF-8 encoding. There may also be
040 * encoding differences. So the main pupose of this class is to provide a
041 * resonable facsimile of the true output.
042 *
043 * @xsl.usage internal
044 */
045 final class SerializerTraceWriter extends Writer implements WriterChain
046 {
047
048 /** The real writer to immediately write to.
049 * This reference may be null, in which case nothing is written out, but
050 * only the trace events are fired for output.
051 */
052 private final java.io.Writer m_writer;
053
054 /** The tracer to send events to */
055 private final SerializerTrace m_tracer;
056
057 /** The size of the internal buffer, just to keep too many
058 * events from being sent to the tracer
059 */
060 private int buf_length;
061
062 /**
063 * Internal buffer to collect the characters to go to the trace listener.
064 *
065 */
066 private byte buf[];
067
068 /**
069 * How many bytes have been collected and still need to go to trace
070 * listener.
071 */
072 private int count;
073
074 /**
075 * Creates or replaces the internal buffer, and makes sure it has a few
076 * extra bytes slight overflow of the last UTF8 encoded character.
077 * @param size
078 */
079 private void setBufferSize(int size)
080 {
081 buf = new byte[size + 3];
082 buf_length = size;
083 count = 0;
084 }
085
086 /**
087 * Constructor.
088 * If the writer passed in is null, then this SerializerTraceWriter will
089 * only signal trace events of what would have been written to that writer.
090 * If the writer passed in is not null then the trace events will mirror
091 * what is going to that writer. In this way tools, such as a debugger, can
092 * gather information on what is being written out.
093 *
094 * @param out the Writer to write to (possibly null)
095 * @param tracer the tracer to inform that characters are being written
096 */
097 public SerializerTraceWriter(Writer out, SerializerTrace tracer)
098 {
099 m_writer = out;
100 m_tracer = tracer;
101 setBufferSize(1024);
102 }
103
104 /**
105 * Flush out the collected characters by sending them to the trace
106 * listener. These characters are never written to the real writer
107 * (m_writer) because that has already happened with every method
108 * call. This method simple informs the listener of what has already
109 * happened.
110 * @throws IOException
111 */
112 private void flushBuffer() throws IOException
113 {
114
115 // Just for tracing purposes
116 if (count > 0)
117 {
118 char[] chars = new char[count];
119 for(int i=0; i<count; i++)
120 chars[i] = (char) buf[i];
121
122 if (m_tracer != null)
123 m_tracer.fireGenerateEvent(
124 SerializerTrace.EVENTTYPE_OUTPUT_CHARACTERS,
125 chars,
126 0,
127 chars.length);
128
129 count = 0;
130 }
131 }
132
133 /**
134 * Flush the internal buffer and flush the Writer
135 * @see java.io.Writer#flush()
136 */
137 public void flush() throws java.io.IOException
138 {
139 // send to the real writer
140 if (m_writer != null)
141 m_writer.flush();
142
143 // from here on just for tracing purposes
144 flushBuffer();
145 }
146
147 /**
148 * Flush the internal buffer and close the Writer
149 * @see java.io.Writer#close()
150 */
151 public void close() throws java.io.IOException
152 {
153 // send to the real writer
154 if (m_writer != null)
155 m_writer.close();
156
157 // from here on just for tracing purposes
158 flushBuffer();
159 }
160
161
162 /**
163 * Write a single character. The character to be written is contained in
164 * the 16 low-order bits of the given integer value; the 16 high-order bits
165 * are ignored.
166 *
167 * <p> Subclasses that intend to support efficient single-character output
168 * should override this method.
169 *
170 * @param c int specifying a character to be written.
171 * @exception IOException If an I/O error occurs
172 */
173 public void write(final int c) throws IOException
174 {
175 // send to the real writer
176 if (m_writer != null)
177 m_writer.write(c);
178
179 // ---------- from here on just collect for tracing purposes
180
181 /* If we are close to the end of the buffer then flush it.
182 * Remember the buffer can hold a few more characters than buf_length
183 */
184 if (count >= buf_length)
185 flushBuffer();
186
187 if (c < 0x80)
188 {
189 buf[count++] = (byte) (c);
190 }
191 else if (c < 0x800)
192 {
193 buf[count++] = (byte) (0xc0 + (c >> 6));
194 buf[count++] = (byte) (0x80 + (c & 0x3f));
195 }
196 else
197 {
198 buf[count++] = (byte) (0xe0 + (c >> 12));
199 buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
200 buf[count++] = (byte) (0x80 + (c & 0x3f));
201 }
202 }
203
204 /**
205 * Write a portion of an array of characters.
206 *
207 * @param chars Array of characters
208 * @param start Offset from which to start writing characters
209 * @param length Number of characters to write
210 *
211 * @exception IOException If an I/O error occurs
212 *
213 * @throws java.io.IOException
214 */
215 public void write(final char chars[], final int start, final int length)
216 throws java.io.IOException
217 {
218 // send to the real writer
219 if (m_writer != null)
220 m_writer.write(chars, start, length);
221
222 // from here on just collect for tracing purposes
223 int lengthx3 = (length << 1) + length;
224
225 if (lengthx3 >= buf_length)
226 {
227
228 /* If the request length exceeds the size of the output buffer,
229 * flush the output buffer and make the buffer bigger to handle.
230 */
231
232 flushBuffer();
233 setBufferSize(2 * lengthx3);
234
235 }
236
237 if (lengthx3 > buf_length - count)
238 {
239 flushBuffer();
240 }
241
242 final int n = length + start;
243 for (int i = start; i < n; i++)
244 {
245 final char c = chars[i];
246
247 if (c < 0x80)
248 buf[count++] = (byte) (c);
249 else if (c < 0x800)
250 {
251 buf[count++] = (byte) (0xc0 + (c >> 6));
252 buf[count++] = (byte) (0x80 + (c & 0x3f));
253 }
254 else
255 {
256 buf[count++] = (byte) (0xe0 + (c >> 12));
257 buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
258 buf[count++] = (byte) (0x80 + (c & 0x3f));
259 }
260 }
261
262 }
263
264 /**
265 * Write a string.
266 *
267 * @param s String to be written
268 *
269 * @exception IOException If an I/O error occurs
270 */
271 public void write(final String s) throws IOException
272 {
273 // send to the real writer
274 if (m_writer != null)
275 m_writer.write(s);
276
277 // from here on just collect for tracing purposes
278 final int length = s.length();
279
280 // We multiply the length by three since this is the maximum length
281 // of the characters that we can put into the buffer. It is possible
282 // for each Unicode character to expand to three bytes.
283
284 int lengthx3 = (length << 1) + length;
285
286 if (lengthx3 >= buf_length)
287 {
288
289 /* If the request length exceeds the size of the output buffer,
290 * flush the output buffer and make the buffer bigger to handle.
291 */
292
293 flushBuffer();
294 setBufferSize(2 * lengthx3);
295 }
296
297 if (lengthx3 > buf_length - count)
298 {
299 flushBuffer();
300 }
301
302 for (int i = 0; i < length; i++)
303 {
304 final char c = s.charAt(i);
305
306 if (c < 0x80)
307 buf[count++] = (byte) (c);
308 else if (c < 0x800)
309 {
310 buf[count++] = (byte) (0xc0 + (c >> 6));
311 buf[count++] = (byte) (0x80 + (c & 0x3f));
312 }
313 else
314 {
315 buf[count++] = (byte) (0xe0 + (c >> 12));
316 buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
317 buf[count++] = (byte) (0x80 + (c & 0x3f));
318 }
319 }
320 }
321
322 /**
323 * Get the writer that this one directly wraps.
324 */
325 public Writer getWriter()
326 {
327 return m_writer;
328 }
329
330 /**
331 * Get the OutputStream that is the at the end of the
332 * chain of writers.
333 */
334 public OutputStream getOutputStream()
335 {
336 OutputStream retval = null;
337 if (m_writer instanceof WriterChain)
338 retval = ((WriterChain) m_writer).getOutputStream();
339 return retval;
340 }
341 }