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