/* | 
|
 * reserved comment block | 
|
 * DO NOT REMOVE OR ALTER! | 
|
*/  | 
|
/*  | 
|
* 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.  | 
|
*/  | 
|
package com.sun.org.apache.xml.internal.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<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));  | 
|
}  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * 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;  | 
|
}  | 
|
}  |