blob: 3462dad47f1cbb134ce7b5f8bbdda2936cd83bae [file] [log] [blame]
/*
* 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));
}
}
}
}