src/share/classes/com/sun/xml/internal/bind/v2/runtime/output/UTF8XmlOutput.java

Fri, 07 Aug 2009 11:27:00 -0700

author
asaha
date
Fri, 07 Aug 2009 11:27:00 -0700
changeset 78
860b95cc8d1d
parent 46
a88ad84027a0
permissions
-rw-r--r--

6813167: 6u14 JAX-WS audit mutable static bugs
6803688: Integrate latest JAX-WS (2.1.6) in to JDK 6u14
Reviewed-by: darcy, ramap

duke@1 1 /*
tbell@45 2 * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
duke@1 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1 4 *
duke@1 5 * This code is free software; you can redistribute it and/or modify it
duke@1 6 * under the terms of the GNU General Public License version 2 only, as
duke@1 7 * published by the Free Software Foundation. Sun designates this
duke@1 8 * particular file as subject to the "Classpath" exception as provided
duke@1 9 * by Sun in the LICENSE file that accompanied this code.
duke@1 10 *
duke@1 11 * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1 14 * version 2 for more details (a copy is included in the LICENSE file that
duke@1 15 * accompanied this code).
duke@1 16 *
duke@1 17 * You should have received a copy of the GNU General Public License version
duke@1 18 * 2 along with this work; if not, write to the Free Software Foundation,
duke@1 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1 20 *
duke@1 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
duke@1 22 * CA 95054 USA or visit www.sun.com if you need additional information or
duke@1 23 * have any questions.
duke@1 24 */
asaha@78 25
duke@1 26 package com.sun.xml.internal.bind.v2.runtime.output;
duke@1 27
duke@1 28 import java.io.IOException;
duke@1 29 import java.io.OutputStream;
duke@1 30
duke@1 31 import javax.xml.stream.XMLStreamException;
duke@1 32
duke@1 33 import com.sun.xml.internal.bind.DatatypeConverterImpl;
duke@1 34 import com.sun.xml.internal.bind.v2.runtime.Name;
duke@1 35 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
ramap@42 36 import com.sun.xml.internal.bind.v2.runtime.MarshallerImpl;
duke@1 37
duke@1 38 import org.xml.sax.SAXException;
duke@1 39
duke@1 40 /**
duke@1 41 * {@link XmlOutput} implementation specialized for UTF-8.
duke@1 42 *
duke@1 43 * @author Kohsuke Kawaguchi
duke@1 44 * @author Paul Sandoz
duke@1 45 */
duke@1 46 public class UTF8XmlOutput extends XmlOutputAbstractImpl {
duke@1 47 protected final OutputStream out;
duke@1 48
duke@1 49 /** prefixes encoded. */
duke@1 50 private Encoded[] prefixes = new Encoded[8];
duke@1 51
duke@1 52 /**
duke@1 53 * Of the {@link #prefixes}, number of filled entries.
duke@1 54 * This is almost the same as {@link NamespaceContextImpl#count()},
duke@1 55 * except that it allows us to handle contextual in-scope namespace bindings correctly.
duke@1 56 */
duke@1 57 private int prefixCount;
duke@1 58
duke@1 59 /** local names encoded in UTF-8. All entries are pre-filled. */
duke@1 60 private final Encoded[] localNames;
duke@1 61
duke@1 62 /** Temporary buffer used to encode text. */
duke@1 63 /*
duke@1 64 * TODO
duke@1 65 * The textBuffer could write directly to the _octetBuffer
duke@1 66 * when encoding a string if Encoder is modified.
duke@1 67 * This will avoid an additional memory copy.
duke@1 68 */
duke@1 69 private final Encoded textBuffer = new Encoded();
duke@1 70
duke@1 71 /** Buffer of octets for writing. */
duke@1 72 // TODO: Obtain buffer size from property on the JAXB context
duke@1 73 protected final byte[] octetBuffer = new byte[1024];
duke@1 74
duke@1 75 /** Index in buffer to write to. */
duke@1 76 protected int octetBufferIndex;
duke@1 77
duke@1 78 /**
duke@1 79 * Set to true to indicate that we need to write '>'
duke@1 80 * to close a start tag. Deferring the write of this char
duke@1 81 * allows us to write "/>" for empty elements.
duke@1 82 */
duke@1 83 protected boolean closeStartTagPending = false;
duke@1 84
duke@1 85 /**
ramap@42 86 * @see MarshallerImpl#header
ramap@42 87 */
ramap@42 88 private String header;
ramap@42 89
ramap@42 90 /**
duke@1 91 *
duke@1 92 * @param localNames
duke@1 93 * local names encoded in UTF-8.
duke@1 94 */
duke@1 95 public UTF8XmlOutput(OutputStream out, Encoded[] localNames) {
duke@1 96 this.out = out;
duke@1 97 this.localNames = localNames;
duke@1 98 for( int i=0; i<prefixes.length; i++ )
duke@1 99 prefixes[i] = new Encoded();
duke@1 100 }
duke@1 101
ramap@42 102 public void setHeader(String header) {
ramap@42 103 this.header = header;
ramap@42 104 }
ramap@42 105
duke@1 106 @Override
duke@1 107 public void startDocument(XMLSerializer serializer, boolean fragment, int[] nsUriIndex2prefixIndex, NamespaceContextImpl nsContext) throws IOException, SAXException, XMLStreamException {
duke@1 108 super.startDocument(serializer, fragment,nsUriIndex2prefixIndex,nsContext);
duke@1 109
duke@1 110 octetBufferIndex = 0;
duke@1 111 if(!fragment) {
duke@1 112 write(XML_DECL);
duke@1 113 }
ramap@42 114 if(header!=null) {
ramap@42 115 textBuffer.set(header);
ramap@42 116 textBuffer.write(this);
ramap@42 117 }
duke@1 118 }
duke@1 119
duke@1 120 public void endDocument(boolean fragment) throws IOException, SAXException, XMLStreamException {
duke@1 121 flushBuffer();
duke@1 122 super.endDocument(fragment);
duke@1 123 }
duke@1 124
duke@1 125 /**
duke@1 126 * Writes '>' to close the start tag, if necessary.
duke@1 127 */
duke@1 128 protected final void closeStartTag() throws IOException {
duke@1 129 if(closeStartTagPending) {
duke@1 130 write('>');
duke@1 131 closeStartTagPending = false;
duke@1 132 }
duke@1 133 }
duke@1 134
duke@1 135 public void beginStartTag(int prefix, String localName) throws IOException {
duke@1 136 closeStartTag();
duke@1 137 int base= pushNsDecls();
duke@1 138 write('<');
duke@1 139 writeName(prefix,localName);
duke@1 140 writeNsDecls(base);
duke@1 141 }
duke@1 142
duke@1 143 public void beginStartTag(Name name) throws IOException {
duke@1 144 closeStartTag();
duke@1 145 int base = pushNsDecls();
duke@1 146 write('<');
duke@1 147 writeName(name);
duke@1 148 writeNsDecls(base);
duke@1 149 }
duke@1 150
duke@1 151 private int pushNsDecls() {
duke@1 152 int total = nsContext.count();
duke@1 153 NamespaceContextImpl.Element ns = nsContext.getCurrent();
duke@1 154
duke@1 155 if(total > prefixes.length) {
duke@1 156 // reallocate
duke@1 157 int m = Math.max(total,prefixes.length*2);
duke@1 158 Encoded[] buf = new Encoded[m];
duke@1 159 System.arraycopy(prefixes,0,buf,0,prefixes.length);
duke@1 160 for( int i=prefixes.length; i<buf.length; i++ )
duke@1 161 buf[i] = new Encoded();
duke@1 162 prefixes = buf;
duke@1 163 }
duke@1 164
duke@1 165 int base = Math.min(prefixCount,ns.getBase());
duke@1 166 int size = nsContext.count();
duke@1 167 for( int i=base; i<size; i++ ) {
duke@1 168 String p = nsContext.getPrefix(i);
duke@1 169
duke@1 170 Encoded e = prefixes[i];
duke@1 171
duke@1 172 if(p.length()==0) {
duke@1 173 e.buf = EMPTY_BYTE_ARRAY;
duke@1 174 e.len = 0;
duke@1 175 } else {
duke@1 176 e.set(p);
duke@1 177 e.append(':');
duke@1 178 }
duke@1 179 }
duke@1 180 prefixCount = size;
duke@1 181 return base;
duke@1 182 }
duke@1 183
duke@1 184 protected void writeNsDecls(int base) throws IOException {
duke@1 185 NamespaceContextImpl.Element ns = nsContext.getCurrent();
duke@1 186 int size = nsContext.count();
duke@1 187
duke@1 188 for( int i=ns.getBase(); i<size; i++ )
duke@1 189 writeNsDecl(i);
duke@1 190 }
duke@1 191
duke@1 192 /**
duke@1 193 * Writes a single namespace declaration for the specified prefix.
duke@1 194 */
duke@1 195 protected final void writeNsDecl(int prefixIndex) throws IOException {
duke@1 196 String p = nsContext.getPrefix(prefixIndex);
duke@1 197
duke@1 198 if(p.length()==0) {
duke@1 199 if(nsContext.getCurrent().isRootElement()
duke@1 200 && nsContext.getNamespaceURI(prefixIndex).length()==0)
duke@1 201 return; // no point in declaring xmlns="" on the root element
duke@1 202 write(XMLNS_EQUALS);
duke@1 203 } else {
duke@1 204 Encoded e = prefixes[prefixIndex];
duke@1 205 write(XMLNS_COLON);
duke@1 206 write(e.buf,0,e.len-1); // skip the trailing ':'
duke@1 207 write(EQUALS);
duke@1 208 }
duke@1 209 doText(nsContext.getNamespaceURI(prefixIndex),true);
duke@1 210 write('\"');
duke@1 211 }
duke@1 212
duke@1 213 private void writePrefix(int prefix) throws IOException {
duke@1 214 prefixes[prefix].write(this);
duke@1 215 }
duke@1 216
duke@1 217 private void writeName(Name name) throws IOException {
duke@1 218 writePrefix(nsUriIndex2prefixIndex[name.nsUriIndex]);
duke@1 219 localNames[name.localNameIndex].write(this);
duke@1 220 }
duke@1 221
duke@1 222 private void writeName(int prefix, String localName) throws IOException {
duke@1 223 writePrefix(prefix);
duke@1 224 textBuffer.set(localName);
duke@1 225 textBuffer.write(this);
duke@1 226 }
duke@1 227
duke@1 228 @Override
duke@1 229 public void attribute(Name name, String value) throws IOException {
duke@1 230 write(' ');
duke@1 231 if(name.nsUriIndex==-1) {
duke@1 232 localNames[name.localNameIndex].write(this);
duke@1 233 } else
duke@1 234 writeName(name);
duke@1 235 write(EQUALS);
duke@1 236 doText(value,true);
duke@1 237 write('\"');
duke@1 238 }
duke@1 239
duke@1 240 public void attribute(int prefix, String localName, String value) throws IOException {
duke@1 241 write(' ');
duke@1 242 if(prefix==-1) {
duke@1 243 textBuffer.set(localName);
duke@1 244 textBuffer.write(this);
duke@1 245 } else
duke@1 246 writeName(prefix,localName);
duke@1 247 write(EQUALS);
duke@1 248 doText(value,true);
duke@1 249 write('\"');
duke@1 250 }
duke@1 251
duke@1 252 public void endStartTag() throws IOException {
duke@1 253 closeStartTagPending = true;
duke@1 254 }
duke@1 255
duke@1 256 @Override
duke@1 257 public void endTag(Name name) throws IOException {
duke@1 258 if(closeStartTagPending) {
duke@1 259 write(EMPTY_TAG);
duke@1 260 closeStartTagPending = false;
duke@1 261 } else {
duke@1 262 write(CLOSE_TAG);
duke@1 263 writeName(name);
duke@1 264 write('>');
duke@1 265 }
duke@1 266 }
duke@1 267
duke@1 268 public void endTag(int prefix, String localName) throws IOException {
duke@1 269 if(closeStartTagPending) {
duke@1 270 write(EMPTY_TAG);
duke@1 271 closeStartTagPending = false;
duke@1 272 } else {
duke@1 273 write(CLOSE_TAG);
duke@1 274 writeName(prefix,localName);
duke@1 275 write('>');
duke@1 276 }
duke@1 277 }
duke@1 278
duke@1 279 public void text(String value, boolean needSP) throws IOException {
duke@1 280 closeStartTag();
duke@1 281 if(needSP)
duke@1 282 write(' ');
duke@1 283 doText(value,false);
duke@1 284 }
duke@1 285
duke@1 286 public void text(Pcdata value, boolean needSP) throws IOException {
duke@1 287 closeStartTag();
duke@1 288 if(needSP)
duke@1 289 write(' ');
duke@1 290 value.writeTo(this);
duke@1 291 }
duke@1 292
duke@1 293 private void doText(String value,boolean isAttribute) throws IOException {
duke@1 294 textBuffer.setEscape(value,isAttribute);
duke@1 295 textBuffer.write(this);
duke@1 296 }
duke@1 297
duke@1 298 public final void text(int value) throws IOException {
duke@1 299 closeStartTag();
duke@1 300 /*
duke@1 301 * TODO
duke@1 302 * Change to use the octet buffer directly
duke@1 303 */
duke@1 304
duke@1 305 // max is -2147483648 and 11 digits
duke@1 306 boolean minus = (value<0);
duke@1 307 textBuffer.ensureSize(11);
duke@1 308 byte[] buf = textBuffer.buf;
duke@1 309 int idx = 11;
duke@1 310
duke@1 311 do {
duke@1 312 int r = value%10;
duke@1 313 if(r<0) r = -r;
duke@1 314 buf[--idx] = (byte)('0'|r); // really measn 0x30+r but 0<=r<10, so bit-OR would do.
duke@1 315 value /= 10;
duke@1 316 } while(value!=0);
duke@1 317
duke@1 318 if(minus) buf[--idx] = (byte)'-';
duke@1 319
duke@1 320 write(buf,idx,11-idx);
duke@1 321 }
duke@1 322
duke@1 323 /**
duke@1 324 * Writes the given byte[] as base64 encoded binary to the output.
duke@1 325 *
duke@1 326 * <p>
duke@1 327 * Being defined on this class allows this method to access the buffer directly,
duke@1 328 * which translates to a better performance.
duke@1 329 */
duke@1 330 public void text(byte[] data, int dataLen) throws IOException {
duke@1 331 closeStartTag();
duke@1 332
duke@1 333 int start = 0;
duke@1 334
duke@1 335 while(dataLen>0) {
duke@1 336 // how many bytes (in data) can we write without overflowing the buffer?
duke@1 337 int batchSize = Math.min(((octetBuffer.length-octetBufferIndex)/4)*3,dataLen);
duke@1 338
duke@1 339 // write the batch
duke@1 340 octetBufferIndex = DatatypeConverterImpl._printBase64Binary(data,start,batchSize,octetBuffer,octetBufferIndex);
duke@1 341
duke@1 342 if(batchSize<dataLen)
duke@1 343 flushBuffer();
duke@1 344
duke@1 345 start += batchSize;
duke@1 346 dataLen -= batchSize;
duke@1 347
duke@1 348 }
duke@1 349 }
duke@1 350
duke@1 351 //
duke@1 352 //
duke@1 353 // series of the write method that places bytes to the output
duke@1 354 // (by doing some buffering internal to this class)
duke@1 355 //
duke@1 356
duke@1 357 /**
duke@1 358 * Writes one byte directly into the buffer.
duke@1 359 *
duke@1 360 * <p>
duke@1 361 * This method can be used somewhat like the {@code text} method,
duke@1 362 * but it doesn't perform character escaping.
duke@1 363 */
duke@1 364 public final void write(int i) throws IOException {
duke@1 365 if (octetBufferIndex < octetBuffer.length) {
duke@1 366 octetBuffer[octetBufferIndex++] = (byte)i;
duke@1 367 } else {
duke@1 368 out.write(octetBuffer);
duke@1 369 octetBufferIndex = 1;
duke@1 370 octetBuffer[0] = (byte)i;
duke@1 371 }
duke@1 372 }
duke@1 373
duke@1 374 protected final void write(byte[] b) throws IOException {
duke@1 375 write(b, 0, b.length);
duke@1 376 }
duke@1 377
duke@1 378 protected final void write(byte[] b, int start, int length) throws IOException {
duke@1 379 if ((octetBufferIndex + length) < octetBuffer.length) {
duke@1 380 System.arraycopy(b, start, octetBuffer, octetBufferIndex, length);
duke@1 381 octetBufferIndex += length;
duke@1 382 } else {
duke@1 383 out.write(octetBuffer, 0, octetBufferIndex);
duke@1 384 out.write(b, start, length);
duke@1 385 octetBufferIndex = 0;
duke@1 386 }
duke@1 387 }
duke@1 388
duke@1 389 protected final void flushBuffer() throws IOException {
duke@1 390 out.write(octetBuffer, 0, octetBufferIndex);
duke@1 391 octetBufferIndex = 0;
duke@1 392 }
duke@1 393
duke@1 394 static byte[] toBytes(String s) {
duke@1 395 byte[] buf = new byte[s.length()];
duke@1 396 for( int i=s.length()-1; i>=0; i-- )
duke@1 397 buf[i] = (byte)s.charAt(i);
duke@1 398 return buf;
duke@1 399 }
duke@1 400
ramap@42 401 // per instance copy to prevent an attack where malicious OutputStream
ramap@42 402 // rewrites the byte array.
ramap@42 403 private final byte[] XMLNS_EQUALS = _XMLNS_EQUALS.clone();
ramap@42 404 private final byte[] XMLNS_COLON = _XMLNS_COLON.clone();
ramap@42 405 private final byte[] EQUALS = _EQUALS.clone();
ramap@42 406 private final byte[] CLOSE_TAG = _CLOSE_TAG.clone();
ramap@42 407 private final byte[] EMPTY_TAG = _EMPTY_TAG.clone();
ramap@42 408 private final byte[] XML_DECL = _XML_DECL.clone();
ramap@42 409
ramap@42 410 // masters
ramap@42 411 private static final byte[] _XMLNS_EQUALS = toBytes(" xmlns=\"");
ramap@42 412 private static final byte[] _XMLNS_COLON = toBytes(" xmlns:");
ramap@42 413 private static final byte[] _EQUALS = toBytes("=\"");
ramap@42 414 private static final byte[] _CLOSE_TAG = toBytes("</");
ramap@42 415 private static final byte[] _EMPTY_TAG = toBytes("/>");
ramap@42 416 private static final byte[] _XML_DECL = toBytes("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
ramap@42 417
ramap@42 418 // no need to copy
duke@1 419 private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
duke@1 420 }

mercurial