001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: SerializerTraceWriter.java 468654 2006-10-28 07:09:23Z minchau $ 020 */ 021 package org.apache.xml.serializer; 022 023 import java.io.IOException; 024 import java.io.OutputStream; 025 import java.io.Writer; 026 027 /** 028 * This class wraps the real writer, it only purpose is to send 029 * CHARACTERTOSTREAM events to the trace listener. 030 * Each method immediately sends the call to the wrapped writer unchanged, but 031 * in addition it collects characters to be issued to a trace listener. 032 * 033 * In this way the trace 034 * listener knows what characters have been written to the output Writer. 035 * 036 * There may still be differences in what the trace events say is going to the 037 * output writer and what is really going there. These differences will be due 038 * to the fact that this class is UTF-8 encoding before emiting the trace event 039 * and the underlying writer may not be UTF-8 encoding. There may also be 040 * encoding differences. So the main pupose of this class is to provide a 041 * resonable facsimile of the true output. 042 * 043 * @xsl.usage internal 044 */ 045 final class SerializerTraceWriter extends Writer implements WriterChain 046 { 047 048 /** The real writer to immediately write to. 049 * This reference may be null, in which case nothing is written out, but 050 * only the trace events are fired for output. 051 */ 052 private final java.io.Writer m_writer; 053 054 /** The tracer to send events to */ 055 private final SerializerTrace m_tracer; 056 057 /** The size of the internal buffer, just to keep too many 058 * events from being sent to the tracer 059 */ 060 private int buf_length; 061 062 /** 063 * Internal buffer to collect the characters to go to the trace listener. 064 * 065 */ 066 private byte buf[]; 067 068 /** 069 * How many bytes have been collected and still need to go to trace 070 * listener. 071 */ 072 private int count; 073 074 /** 075 * Creates or replaces the internal buffer, and makes sure it has a few 076 * extra bytes slight overflow of the last UTF8 encoded character. 077 * @param size 078 */ 079 private void setBufferSize(int size) 080 { 081 buf = new byte[size + 3]; 082 buf_length = size; 083 count = 0; 084 } 085 086 /** 087 * Constructor. 088 * If the writer passed in is null, then this SerializerTraceWriter will 089 * only signal trace events of what would have been written to that writer. 090 * If the writer passed in is not null then the trace events will mirror 091 * what is going to that writer. In this way tools, such as a debugger, can 092 * gather information on what is being written out. 093 * 094 * @param out the Writer to write to (possibly null) 095 * @param tracer the tracer to inform that characters are being written 096 */ 097 public SerializerTraceWriter(Writer out, SerializerTrace tracer) 098 { 099 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 }