| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 2003 The Apache Software Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xalan" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 2003, International Business |
| * Machines, Inc., http://www.ibm.com. For more information on the Apache |
| * Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| package org.apache.xml.serializer; |
| |
| import java.io.IOException; |
| import java.io.Writer; |
| |
| /** |
| * This class wraps the real writer, it only purpose is to send |
| * CHARACTERTOSTREAM events to the trace listener. |
| * Each method immediately sends the call to the wrapped writer unchanged, but |
| * in addition it collects characters to be issued to a trace listener. |
| * |
| * In this way the trace |
| * listener knows what characters have been written to the output Writer. |
| * |
| * There may still be differences in what the trace events say is going to the |
| * output writer and what is really going there. These differences will be due |
| * to the fact that this class is UTF-8 encoding before emiting the trace event |
| * and the underlying writer may not be UTF-8 encoding. There may also be |
| * encoding differences. So the main pupose of this class is to provide a |
| * resonable facsimile of the true output. |
| * |
| */ |
| public class SerializerTraceWriter extends Writer |
| { |
| |
| /** The real writer to immediately write to. |
| * This reference may be null, in which case nothing is written out, but |
| * only the trace events are fired for output. |
| */ |
| private final java.io.Writer m_writer; |
| |
| /** The tracer to send events to */ |
| private final SerializerTrace m_tracer; |
| |
| /** The size of the internal buffer, just to keep too many |
| * events from being sent to the tracer |
| */ |
| private int buf_length; |
| |
| /** |
| * Internal buffer to collect the characters to go to the trace listener. |
| * |
| */ |
| private byte buf[]; |
| |
| /** |
| * How many bytes have been collected and still need to go to trace |
| * listener. |
| */ |
| private int count; |
| |
| /** |
| * Creates or replaces the internal buffer, and makes sure it has a few |
| * extra bytes slight overflow of the last UTF8 encoded character. |
| * @param size |
| */ |
| private void setBufferSize(int size) |
| { |
| buf = new byte[size + 3]; |
| buf_length = size; |
| count = 0; |
| } |
| |
| /** |
| * Constructor. |
| * If the writer passed in is null, then this SerializerTraceWriter will |
| * only signal trace events of what would have been written to that writer. |
| * If the writer passed in is not null then the trace events will mirror |
| * what is going to that writer. In this way tools, such as a debugger, can |
| * gather information on what is being written out. |
| * |
| * @param out the Writer to write to (possibly null) |
| * @param tracer the tracer to inform that characters are being written |
| */ |
| public SerializerTraceWriter(Writer out, SerializerTrace tracer) |
| { |
| m_writer = out; |
| m_tracer = tracer; |
| setBufferSize(1024); |
| } |
| |
| /** |
| * Flush out the collected characters by sending them to the trace |
| * listener. These characters are never written to the real writer |
| * (m_writer) because that has already happened with every method |
| * call. This method simple informs the listener of what has already |
| * happened. |
| * @throws IOException |
| */ |
| private void flushBuffer() throws IOException |
| { |
| |
| // Just for tracing purposes |
| if (count > 0) |
| { |
| char[] chars = new char[count]; |
| for(int i=0; i<count; i++) |
| chars[i] = (char) buf[i]; |
| |
| if (m_tracer != null) |
| m_tracer.fireGenerateEvent( |
| SerializerTrace.EVENTTYPE_OUTPUT_CHARACTERS, |
| chars, |
| 0, |
| chars.length); |
| |
| count = 0; |
| } |
| } |
| |
| /** |
| * Flush the internal buffer and flush the Writer |
| * @see java.io.Writer#flush() |
| */ |
| public void flush() throws java.io.IOException |
| { |
| // send to the real writer |
| if (m_writer != null) |
| m_writer.flush(); |
| |
| // from here on just for tracing purposes |
| flushBuffer(); |
| } |
| |
| /** |
| * Flush the internal buffer and close the Writer |
| * @see java.io.Writer#close() |
| */ |
| public void close() throws java.io.IOException |
| { |
| // send to the real writer |
| if (m_writer != null) |
| m_writer.close(); |
| |
| // from here on just for tracing purposes |
| flushBuffer(); |
| } |
| |
| |
| /** |
| * Write a single character. The character to be written is contained in |
| * the 16 low-order bits of the given integer value; the 16 high-order bits |
| * are ignored. |
| * |
| * <p> Subclasses that intend to support efficient single-character output |
| * should override this method. |
| * |
| * @param c int specifying a character to be written. |
| * @exception IOException If an I/O error occurs |
| */ |
| public void write(final int c) throws IOException |
| { |
| // send to the real writer |
| if (m_writer != null) |
| m_writer.write(c); |
| |
| // ---------- from here on just collect for tracing purposes |
| |
| /* If we are close to the end of the buffer then flush it. |
| * Remember the buffer can hold a few more characters than buf_length |
| */ |
| if (count >= buf_length) |
| flushBuffer(); |
| |
| if (c < 0x80) |
| { |
| buf[count++] = (byte) (c); |
| } |
| else if (c < 0x800) |
| { |
| buf[count++] = (byte) (0xc0 + (c >> 6)); |
| buf[count++] = (byte) (0x80 + (c & 0x3f)); |
| } |
| else |
| { |
| buf[count++] = (byte) (0xe0 + (c >> 12)); |
| buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f)); |
| buf[count++] = (byte) (0x80 + (c & 0x3f)); |
| } |
| } |
| |
| /** |
| * Write a portion of an array of characters. |
| * |
| * @param chars Array of characters |
| * @param start Offset from which to start writing characters |
| * @param length Number of characters to write |
| * |
| * @exception IOException If an I/O error occurs |
| * |
| * @throws java.io.IOException |
| */ |
| public void write(final char chars[], final int start, final int length) |
| throws java.io.IOException |
| { |
| // send to the real writer |
| if (m_writer != null) |
| m_writer.write(chars, start, length); |
| |
| // from here on just collect for tracing purposes |
| int lengthx3 = (length << 1) + length; |
| |
| if (lengthx3 >= buf_length) |
| { |
| |
| /* If the request length exceeds the size of the output buffer, |
| * flush the output buffer and make the buffer bigger to handle. |
| */ |
| |
| flushBuffer(); |
| setBufferSize(2 * lengthx3); |
| |
| } |
| |
| if (lengthx3 > buf_length - count) |
| { |
| flushBuffer(); |
| } |
| |
| final int n = length + start; |
| for (int i = start; i < n; i++) |
| { |
| final char c = chars[i]; |
| |
| if (c < 0x80) |
| buf[count++] = (byte) (c); |
| else if (c < 0x800) |
| { |
| buf[count++] = (byte) (0xc0 + (c >> 6)); |
| buf[count++] = (byte) (0x80 + (c & 0x3f)); |
| } |
| else |
| { |
| buf[count++] = (byte) (0xe0 + (c >> 12)); |
| buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f)); |
| buf[count++] = (byte) (0x80 + (c & 0x3f)); |
| } |
| } |
| |
| } |
| |
| /** |
| * Write a string. |
| * |
| * @param s String to be written |
| * |
| * @exception IOException If an I/O error occurs |
| */ |
| public void write(final String s) throws IOException |
| { |
| // send to the real writer |
| if (m_writer != null) |
| m_writer.write(s); |
| |
| // from here on just collect for tracing purposes |
| final int length = s.length(); |
| |
| // We multiply the length by three since this is the maximum length |
| // of the characters that we can put into the buffer. It is possible |
| // for each Unicode character to expand to three bytes. |
| |
| int lengthx3 = (length << 1) + length; |
| |
| if (lengthx3 >= buf_length) |
| { |
| |
| /* If the request length exceeds the size of the output buffer, |
| * flush the output buffer and make the buffer bigger to handle. |
| */ |
| |
| flushBuffer(); |
| setBufferSize(2 * lengthx3); |
| } |
| |
| if (lengthx3 > buf_length - count) |
| { |
| flushBuffer(); |
| } |
| |
| for (int i = 0; i < length; i++) |
| { |
| final char c = s.charAt(i); |
| |
| if (c < 0x80) |
| buf[count++] = (byte) (c); |
| else if (c < 0x800) |
| { |
| buf[count++] = (byte) (0xc0 + (c >> 6)); |
| buf[count++] = (byte) (0x80 + (c & 0x3f)); |
| } |
| else |
| { |
| buf[count++] = (byte) (0xe0 + (c >> 12)); |
| buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f)); |
| buf[count++] = (byte) (0x80 + (c & 0x3f)); |
| } |
| } |
| } |
| |
| } |