org.apache.xml.serializer.SerializerTraceWriter Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id: SerializerTraceWriter.java 468654 2006-10-28 07:09:23Z minchau $
*/
package org.apache.xml.serializer;
import java.io.IOException;
import java.io.OutputStream;
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.
*
* @xsl.usage internal
*/
final class SerializerTraceWriter extends Writer implements WriterChain
{
/** 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 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));
}
}
}
/**
* Get the writer that this one directly wraps.
*/
public Writer getWriter()
{
return m_writer;
}
/**
* Get the OutputStream that is the at the end of the
* chain of writers.
*/
public OutputStream getOutputStream()
{
OutputStream retval = null;
if (m_writer instanceof WriterChain)
retval = ((WriterChain) m_writer).getOutputStream();
return retval;
}
}