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