/* * 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; } }