Home | History | Annotate | Download | only in serializer
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: SerializerTraceWriter.java 468654 2006-10-28 07:09:23Z minchau $
     20  */
     21 package org.apache.xml.serializer;
     22 
     23 import java.io.IOException;
     24 import java.io.OutputStream;
     25 import java.io.Writer;
     26 
     27 /**
     28  * This class wraps the real writer, it only purpose is to send
     29  * CHARACTERTOSTREAM events to the trace listener.
     30  * Each method immediately sends the call to the wrapped writer unchanged, but
     31  * in addition it collects characters to be issued to a trace listener.
     32  *
     33  * In this way the trace
     34  * listener knows what characters have been written to the output Writer.
     35  *
     36  * There may still be differences in what the trace events say is going to the
     37  * output writer and what is really going there. These differences will be due
     38  * to the fact that this class is UTF-8 encoding before emiting the trace event
     39  * and the underlying writer may not be UTF-8 encoding. There may also be
     40  * encoding differences.  So the main pupose of this class is to provide a
     41  * resonable facsimile of the true output.
     42  *
     43  * @xsl.usage internal
     44  */
     45 final class SerializerTraceWriter extends Writer implements WriterChain
     46 {
     47 
     48     /** The real writer to immediately write to.
     49      * This reference may be null, in which case nothing is written out, but
     50      * only the trace events are fired for output.
     51      */
     52     private final java.io.Writer m_writer;
     53 
     54     /** The tracer to send events to */
     55     private final SerializerTrace m_tracer;
     56 
     57     /** The size of the internal buffer, just to keep too many
     58      * events from being sent to the tracer
     59      */
     60     private int buf_length;
     61 
     62     /**
     63      * Internal buffer to collect the characters to go to the trace listener.
     64      *
     65      */
     66     private byte buf[];
     67 
     68     /**
     69      * How many bytes have been collected and still need to go to trace
     70      * listener.
     71      */
     72     private int count;
     73 
     74     /**
     75      * Creates or replaces the internal buffer, and makes sure it has a few
     76      * extra bytes slight overflow of the last UTF8 encoded character.
     77      * @param size
     78      */
     79     private void setBufferSize(int size)
     80     {
     81         buf = new byte[size + 3];
     82         buf_length = size;
     83         count = 0;
     84     }
     85 
     86     /**
     87      * Constructor.
     88      * If the writer passed in is null, then this SerializerTraceWriter will
     89      * only signal trace events of what would have been written to that writer.
     90      * If the writer passed in is not null then the trace events will mirror
     91      * what is going to that writer. In this way tools, such as a debugger, can
     92      * gather information on what is being written out.
     93      *
     94      * @param out the Writer to write to (possibly null)
     95      * @param tracer the tracer to inform that characters are being written
     96      */
     97     public SerializerTraceWriter(Writer out, SerializerTrace tracer)
     98     {
     99         m_writer = out;
    100         m_tracer = tracer;
    101         setBufferSize(1024);
    102     }
    103 
    104     /**
    105      * Flush out the collected characters by sending them to the trace
    106      * listener.  These characters are never written to the real writer
    107      * (m_writer) because that has already happened with every method
    108      * call. This method simple informs the listener of what has already
    109      * happened.
    110      * @throws IOException
    111      */
    112     private void flushBuffer() throws IOException
    113     {
    114 
    115         // Just for tracing purposes
    116         if (count > 0)
    117         {
    118             char[] chars = new char[count];
    119             for(int i=0; i<count; i++)
    120                 chars[i] = (char) buf[i];
    121 
    122             if (m_tracer != null)
    123                 m_tracer.fireGenerateEvent(
    124                     SerializerTrace.EVENTTYPE_OUTPUT_CHARACTERS,
    125                     chars,
    126                     0,
    127                     chars.length);
    128 
    129             count = 0;
    130         }
    131     }
    132 
    133     /**
    134      * Flush the internal buffer and flush the Writer
    135      * @see java.io.Writer#flush()
    136      */
    137     public void flush() throws java.io.IOException
    138     {
    139         // send to the real writer
    140         if (m_writer != null)
    141             m_writer.flush();
    142 
    143         // from here on just for tracing purposes
    144         flushBuffer();
    145     }
    146 
    147     /**
    148      * Flush the internal buffer and close the Writer
    149      * @see java.io.Writer#close()
    150      */
    151     public void close() throws java.io.IOException
    152     {
    153         // send to the real writer
    154         if (m_writer != null)
    155             m_writer.close();
    156 
    157         // from here on just for tracing purposes
    158         flushBuffer();
    159     }
    160 
    161 
    162     /**
    163      * Write a single character.  The character to be written is contained in
    164      * the 16 low-order bits of the given integer value; the 16 high-order bits
    165      * are ignored.
    166      *
    167      * <p> Subclasses that intend to support efficient single-character output
    168      * should override this method.
    169      *
    170      * @param c  int specifying a character to be written.
    171      * @exception  IOException  If an I/O error occurs
    172      */
    173     public void write(final int c) throws IOException
    174     {
    175         // send to the real writer
    176         if (m_writer != null)
    177             m_writer.write(c);
    178 
    179         // ---------- from here on just collect for tracing purposes
    180 
    181         /* If we are close to the end of the buffer then flush it.
    182          * Remember the buffer can hold a few more characters than buf_length
    183          */
    184         if (count >= buf_length)
    185             flushBuffer();
    186 
    187         if (c < 0x80)
    188         {
    189             buf[count++] = (byte) (c);
    190         }
    191         else if (c < 0x800)
    192         {
    193             buf[count++] = (byte) (0xc0 + (c >> 6));
    194             buf[count++] = (byte) (0x80 + (c & 0x3f));
    195         }
    196         else
    197         {
    198             buf[count++] = (byte) (0xe0 + (c >> 12));
    199             buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
    200             buf[count++] = (byte) (0x80 + (c & 0x3f));
    201         }
    202     }
    203 
    204     /**
    205      * Write a portion of an array of characters.
    206      *
    207      * @param  chars  Array of characters
    208      * @param  start   Offset from which to start writing characters
    209      * @param  length   Number of characters to write
    210      *
    211      * @exception  IOException  If an I/O error occurs
    212      *
    213      * @throws java.io.IOException
    214      */
    215     public void write(final char chars[], final int start, final int length)
    216         throws java.io.IOException
    217     {
    218         // send to the real writer
    219         if (m_writer != null)
    220             m_writer.write(chars, start, length);
    221 
    222         // from here on just collect for tracing purposes
    223         int lengthx3 = (length << 1) + length;
    224 
    225         if (lengthx3 >= buf_length)
    226         {
    227 
    228             /* If the request length exceeds the size of the output buffer,
    229               * flush the output buffer and make the buffer bigger to handle.
    230               */
    231 
    232             flushBuffer();
    233             setBufferSize(2 * lengthx3);
    234 
    235         }
    236 
    237         if (lengthx3 > buf_length - count)
    238         {
    239             flushBuffer();
    240         }
    241 
    242         final int n = length + start;
    243         for (int i = start; i < n; i++)
    244         {
    245             final char c = chars[i];
    246 
    247             if (c < 0x80)
    248                 buf[count++] = (byte) (c);
    249             else if (c < 0x800)
    250             {
    251                 buf[count++] = (byte) (0xc0 + (c >> 6));
    252                 buf[count++] = (byte) (0x80 + (c & 0x3f));
    253             }
    254             else
    255             {
    256                 buf[count++] = (byte) (0xe0 + (c >> 12));
    257                 buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
    258                 buf[count++] = (byte) (0x80 + (c & 0x3f));
    259             }
    260         }
    261 
    262     }
    263 
    264     /**
    265      * Write a string.
    266      *
    267      * @param  s  String to be written
    268      *
    269      * @exception  IOException  If an I/O error occurs
    270      */
    271     public void write(final String s) throws IOException
    272     {
    273         // send to the real writer
    274         if (m_writer != null)
    275             m_writer.write(s);
    276 
    277         // from here on just collect for tracing purposes
    278         final int length = s.length();
    279 
    280         // We multiply the length by three since this is the maximum length
    281         // of the characters that we can put into the buffer.  It is possible
    282         // for each Unicode character to expand to three bytes.
    283 
    284         int lengthx3 = (length << 1) + length;
    285 
    286         if (lengthx3 >= buf_length)
    287         {
    288 
    289             /* If the request length exceeds the size of the output buffer,
    290               * flush the output buffer and make the buffer bigger to handle.
    291               */
    292 
    293             flushBuffer();
    294             setBufferSize(2 * lengthx3);
    295         }
    296 
    297         if (lengthx3 > buf_length - count)
    298         {
    299             flushBuffer();
    300         }
    301 
    302         for (int i = 0; i < length; i++)
    303         {
    304             final char c = s.charAt(i);
    305 
    306             if (c < 0x80)
    307                 buf[count++] = (byte) (c);
    308             else if (c < 0x800)
    309             {
    310                 buf[count++] = (byte) (0xc0 + (c >> 6));
    311                 buf[count++] = (byte) (0x80 + (c & 0x3f));
    312             }
    313             else
    314             {
    315                 buf[count++] = (byte) (0xe0 + (c >> 12));
    316                 buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
    317                 buf[count++] = (byte) (0x80 + (c & 0x3f));
    318             }
    319         }
    320     }
    321 
    322     /**
    323      * Get the writer that this one directly wraps.
    324      */
    325     public Writer getWriter()
    326     {
    327         return m_writer;
    328     }
    329 
    330     /**
    331      * Get the OutputStream that is the at the end of the
    332      * chain of writers.
    333      */
    334     public OutputStream getOutputStream()
    335     {
    336         OutputStream retval = null;
    337         if (m_writer instanceof WriterChain)
    338             retval = ((WriterChain) m_writer).getOutputStream();
    339         return retval;
    340     }
    341 }
    342