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

Thu, 12 Oct 2017 19:44:07 +0800

author
aoqi
date
Thu, 12 Oct 2017 19:44:07 +0800
changeset 760
e530533619ec
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

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

mercurial