Mon, 20 Apr 2009 15:14:39 -0700
6831313: update jaxws in OpenJDK7 to 2.1 plus bug fixes from OpenJDK 6
6672868: Package javax.xml.ws.wsaddressing not included in make/docs/CORE_PKGS.gmk
Reviewed-by: darcy
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 | */ |
duke@1 | 25 | package com.sun.xml.internal.bind.v2.runtime.output; |
duke@1 | 26 | |
duke@1 | 27 | import java.io.IOException; |
duke@1 | 28 | import java.io.OutputStream; |
duke@1 | 29 | |
duke@1 | 30 | import javax.xml.stream.XMLStreamException; |
duke@1 | 31 | |
duke@1 | 32 | import com.sun.xml.internal.bind.DatatypeConverterImpl; |
duke@1 | 33 | import com.sun.xml.internal.bind.v2.runtime.Name; |
duke@1 | 34 | import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; |
duke@1 | 35 | |
duke@1 | 36 | import org.xml.sax.SAXException; |
duke@1 | 37 | |
duke@1 | 38 | /** |
duke@1 | 39 | * {@link XmlOutput} implementation specialized for UTF-8. |
duke@1 | 40 | * |
duke@1 | 41 | * @author Kohsuke Kawaguchi |
duke@1 | 42 | * @author Paul Sandoz |
duke@1 | 43 | */ |
duke@1 | 44 | public class UTF8XmlOutput extends XmlOutputAbstractImpl { |
duke@1 | 45 | protected final OutputStream out; |
duke@1 | 46 | |
duke@1 | 47 | /** prefixes encoded. */ |
duke@1 | 48 | private Encoded[] prefixes = new Encoded[8]; |
duke@1 | 49 | |
duke@1 | 50 | /** |
duke@1 | 51 | * Of the {@link #prefixes}, number of filled entries. |
duke@1 | 52 | * This is almost the same as {@link NamespaceContextImpl#count()}, |
duke@1 | 53 | * except that it allows us to handle contextual in-scope namespace bindings correctly. |
duke@1 | 54 | */ |
duke@1 | 55 | private int prefixCount; |
duke@1 | 56 | |
duke@1 | 57 | /** local names encoded in UTF-8. All entries are pre-filled. */ |
duke@1 | 58 | private final Encoded[] localNames; |
duke@1 | 59 | |
duke@1 | 60 | /** Temporary buffer used to encode text. */ |
duke@1 | 61 | /* |
duke@1 | 62 | * TODO |
duke@1 | 63 | * The textBuffer could write directly to the _octetBuffer |
duke@1 | 64 | * when encoding a string if Encoder is modified. |
duke@1 | 65 | * This will avoid an additional memory copy. |
duke@1 | 66 | */ |
duke@1 | 67 | private final Encoded textBuffer = new Encoded(); |
duke@1 | 68 | |
duke@1 | 69 | /** Buffer of octets for writing. */ |
duke@1 | 70 | // TODO: Obtain buffer size from property on the JAXB context |
duke@1 | 71 | protected final byte[] octetBuffer = new byte[1024]; |
duke@1 | 72 | |
duke@1 | 73 | /** Index in buffer to write to. */ |
duke@1 | 74 | protected int octetBufferIndex; |
duke@1 | 75 | |
duke@1 | 76 | /** |
duke@1 | 77 | * Set to true to indicate that we need to write '>' |
duke@1 | 78 | * to close a start tag. Deferring the write of this char |
duke@1 | 79 | * allows us to write "/>" for empty elements. |
duke@1 | 80 | */ |
duke@1 | 81 | protected boolean closeStartTagPending = false; |
duke@1 | 82 | |
duke@1 | 83 | /** |
duke@1 | 84 | * |
duke@1 | 85 | * @param localNames |
duke@1 | 86 | * local names encoded in UTF-8. |
duke@1 | 87 | */ |
duke@1 | 88 | public UTF8XmlOutput(OutputStream out, Encoded[] localNames) { |
duke@1 | 89 | this.out = out; |
duke@1 | 90 | this.localNames = localNames; |
duke@1 | 91 | for( int i=0; i<prefixes.length; i++ ) |
duke@1 | 92 | prefixes[i] = new Encoded(); |
duke@1 | 93 | } |
duke@1 | 94 | |
duke@1 | 95 | @Override |
duke@1 | 96 | public void startDocument(XMLSerializer serializer, boolean fragment, int[] nsUriIndex2prefixIndex, NamespaceContextImpl nsContext) throws IOException, SAXException, XMLStreamException { |
duke@1 | 97 | super.startDocument(serializer, fragment,nsUriIndex2prefixIndex,nsContext); |
duke@1 | 98 | |
duke@1 | 99 | octetBufferIndex = 0; |
duke@1 | 100 | if(!fragment) { |
duke@1 | 101 | write(XML_DECL); |
duke@1 | 102 | } |
duke@1 | 103 | } |
duke@1 | 104 | |
duke@1 | 105 | public void endDocument(boolean fragment) throws IOException, SAXException, XMLStreamException { |
duke@1 | 106 | flushBuffer(); |
duke@1 | 107 | super.endDocument(fragment); |
duke@1 | 108 | } |
duke@1 | 109 | |
duke@1 | 110 | /** |
duke@1 | 111 | * Writes '>' to close the start tag, if necessary. |
duke@1 | 112 | */ |
duke@1 | 113 | protected final void closeStartTag() throws IOException { |
duke@1 | 114 | if(closeStartTagPending) { |
duke@1 | 115 | write('>'); |
duke@1 | 116 | closeStartTagPending = false; |
duke@1 | 117 | } |
duke@1 | 118 | } |
duke@1 | 119 | |
duke@1 | 120 | public void beginStartTag(int prefix, String localName) throws IOException { |
duke@1 | 121 | closeStartTag(); |
duke@1 | 122 | int base= pushNsDecls(); |
duke@1 | 123 | write('<'); |
duke@1 | 124 | writeName(prefix,localName); |
duke@1 | 125 | writeNsDecls(base); |
duke@1 | 126 | } |
duke@1 | 127 | |
duke@1 | 128 | public void beginStartTag(Name name) throws IOException { |
duke@1 | 129 | closeStartTag(); |
duke@1 | 130 | int base = pushNsDecls(); |
duke@1 | 131 | write('<'); |
duke@1 | 132 | writeName(name); |
duke@1 | 133 | writeNsDecls(base); |
duke@1 | 134 | } |
duke@1 | 135 | |
duke@1 | 136 | private int pushNsDecls() { |
duke@1 | 137 | int total = nsContext.count(); |
duke@1 | 138 | NamespaceContextImpl.Element ns = nsContext.getCurrent(); |
duke@1 | 139 | |
duke@1 | 140 | if(total > prefixes.length) { |
duke@1 | 141 | // reallocate |
duke@1 | 142 | int m = Math.max(total,prefixes.length*2); |
duke@1 | 143 | Encoded[] buf = new Encoded[m]; |
duke@1 | 144 | System.arraycopy(prefixes,0,buf,0,prefixes.length); |
duke@1 | 145 | for( int i=prefixes.length; i<buf.length; i++ ) |
duke@1 | 146 | buf[i] = new Encoded(); |
duke@1 | 147 | prefixes = buf; |
duke@1 | 148 | } |
duke@1 | 149 | |
duke@1 | 150 | int base = Math.min(prefixCount,ns.getBase()); |
duke@1 | 151 | int size = nsContext.count(); |
duke@1 | 152 | for( int i=base; i<size; i++ ) { |
duke@1 | 153 | String p = nsContext.getPrefix(i); |
duke@1 | 154 | |
duke@1 | 155 | Encoded e = prefixes[i]; |
duke@1 | 156 | |
duke@1 | 157 | if(p.length()==0) { |
duke@1 | 158 | e.buf = EMPTY_BYTE_ARRAY; |
duke@1 | 159 | e.len = 0; |
duke@1 | 160 | } else { |
duke@1 | 161 | e.set(p); |
duke@1 | 162 | e.append(':'); |
duke@1 | 163 | } |
duke@1 | 164 | } |
duke@1 | 165 | prefixCount = size; |
duke@1 | 166 | return base; |
duke@1 | 167 | } |
duke@1 | 168 | |
duke@1 | 169 | protected void writeNsDecls(int base) throws IOException { |
duke@1 | 170 | NamespaceContextImpl.Element ns = nsContext.getCurrent(); |
duke@1 | 171 | int size = nsContext.count(); |
duke@1 | 172 | |
duke@1 | 173 | for( int i=ns.getBase(); i<size; i++ ) |
duke@1 | 174 | writeNsDecl(i); |
duke@1 | 175 | } |
duke@1 | 176 | |
duke@1 | 177 | /** |
duke@1 | 178 | * Writes a single namespace declaration for the specified prefix. |
duke@1 | 179 | */ |
duke@1 | 180 | protected final void writeNsDecl(int prefixIndex) throws IOException { |
duke@1 | 181 | String p = nsContext.getPrefix(prefixIndex); |
duke@1 | 182 | |
duke@1 | 183 | if(p.length()==0) { |
duke@1 | 184 | if(nsContext.getCurrent().isRootElement() |
duke@1 | 185 | && nsContext.getNamespaceURI(prefixIndex).length()==0) |
duke@1 | 186 | return; // no point in declaring xmlns="" on the root element |
duke@1 | 187 | write(XMLNS_EQUALS); |
duke@1 | 188 | } else { |
duke@1 | 189 | Encoded e = prefixes[prefixIndex]; |
duke@1 | 190 | write(XMLNS_COLON); |
duke@1 | 191 | write(e.buf,0,e.len-1); // skip the trailing ':' |
duke@1 | 192 | write(EQUALS); |
duke@1 | 193 | } |
duke@1 | 194 | doText(nsContext.getNamespaceURI(prefixIndex),true); |
duke@1 | 195 | write('\"'); |
duke@1 | 196 | } |
duke@1 | 197 | |
duke@1 | 198 | private void writePrefix(int prefix) throws IOException { |
duke@1 | 199 | prefixes[prefix].write(this); |
duke@1 | 200 | } |
duke@1 | 201 | |
duke@1 | 202 | private void writeName(Name name) throws IOException { |
duke@1 | 203 | writePrefix(nsUriIndex2prefixIndex[name.nsUriIndex]); |
duke@1 | 204 | localNames[name.localNameIndex].write(this); |
duke@1 | 205 | } |
duke@1 | 206 | |
duke@1 | 207 | private void writeName(int prefix, String localName) throws IOException { |
duke@1 | 208 | writePrefix(prefix); |
duke@1 | 209 | textBuffer.set(localName); |
duke@1 | 210 | textBuffer.write(this); |
duke@1 | 211 | } |
duke@1 | 212 | |
duke@1 | 213 | @Override |
duke@1 | 214 | public void attribute(Name name, String value) throws IOException { |
duke@1 | 215 | write(' '); |
duke@1 | 216 | if(name.nsUriIndex==-1) { |
duke@1 | 217 | localNames[name.localNameIndex].write(this); |
duke@1 | 218 | } else |
duke@1 | 219 | writeName(name); |
duke@1 | 220 | write(EQUALS); |
duke@1 | 221 | doText(value,true); |
duke@1 | 222 | write('\"'); |
duke@1 | 223 | } |
duke@1 | 224 | |
duke@1 | 225 | public void attribute(int prefix, String localName, String value) throws IOException { |
duke@1 | 226 | write(' '); |
duke@1 | 227 | if(prefix==-1) { |
duke@1 | 228 | textBuffer.set(localName); |
duke@1 | 229 | textBuffer.write(this); |
duke@1 | 230 | } else |
duke@1 | 231 | writeName(prefix,localName); |
duke@1 | 232 | write(EQUALS); |
duke@1 | 233 | doText(value,true); |
duke@1 | 234 | write('\"'); |
duke@1 | 235 | } |
duke@1 | 236 | |
duke@1 | 237 | public void endStartTag() throws IOException { |
duke@1 | 238 | closeStartTagPending = true; |
duke@1 | 239 | } |
duke@1 | 240 | |
duke@1 | 241 | @Override |
duke@1 | 242 | public void endTag(Name name) throws IOException { |
duke@1 | 243 | if(closeStartTagPending) { |
duke@1 | 244 | write(EMPTY_TAG); |
duke@1 | 245 | closeStartTagPending = false; |
duke@1 | 246 | } else { |
duke@1 | 247 | write(CLOSE_TAG); |
duke@1 | 248 | writeName(name); |
duke@1 | 249 | write('>'); |
duke@1 | 250 | } |
duke@1 | 251 | } |
duke@1 | 252 | |
duke@1 | 253 | public void endTag(int prefix, String localName) throws IOException { |
duke@1 | 254 | if(closeStartTagPending) { |
duke@1 | 255 | write(EMPTY_TAG); |
duke@1 | 256 | closeStartTagPending = false; |
duke@1 | 257 | } else { |
duke@1 | 258 | write(CLOSE_TAG); |
duke@1 | 259 | writeName(prefix,localName); |
duke@1 | 260 | write('>'); |
duke@1 | 261 | } |
duke@1 | 262 | } |
duke@1 | 263 | |
duke@1 | 264 | public void text(String value, boolean needSP) throws IOException { |
duke@1 | 265 | closeStartTag(); |
duke@1 | 266 | if(needSP) |
duke@1 | 267 | write(' '); |
duke@1 | 268 | doText(value,false); |
duke@1 | 269 | } |
duke@1 | 270 | |
duke@1 | 271 | public void text(Pcdata value, boolean needSP) throws IOException { |
duke@1 | 272 | closeStartTag(); |
duke@1 | 273 | if(needSP) |
duke@1 | 274 | write(' '); |
duke@1 | 275 | value.writeTo(this); |
duke@1 | 276 | } |
duke@1 | 277 | |
duke@1 | 278 | private void doText(String value,boolean isAttribute) throws IOException { |
duke@1 | 279 | textBuffer.setEscape(value,isAttribute); |
duke@1 | 280 | textBuffer.write(this); |
duke@1 | 281 | } |
duke@1 | 282 | |
duke@1 | 283 | public final void text(int value) throws IOException { |
duke@1 | 284 | closeStartTag(); |
duke@1 | 285 | /* |
duke@1 | 286 | * TODO |
duke@1 | 287 | * Change to use the octet buffer directly |
duke@1 | 288 | */ |
duke@1 | 289 | |
duke@1 | 290 | // max is -2147483648 and 11 digits |
duke@1 | 291 | boolean minus = (value<0); |
duke@1 | 292 | textBuffer.ensureSize(11); |
duke@1 | 293 | byte[] buf = textBuffer.buf; |
duke@1 | 294 | int idx = 11; |
duke@1 | 295 | |
duke@1 | 296 | do { |
duke@1 | 297 | int r = value%10; |
duke@1 | 298 | if(r<0) r = -r; |
duke@1 | 299 | buf[--idx] = (byte)('0'|r); // really measn 0x30+r but 0<=r<10, so bit-OR would do. |
duke@1 | 300 | value /= 10; |
duke@1 | 301 | } while(value!=0); |
duke@1 | 302 | |
duke@1 | 303 | if(minus) buf[--idx] = (byte)'-'; |
duke@1 | 304 | |
duke@1 | 305 | write(buf,idx,11-idx); |
duke@1 | 306 | } |
duke@1 | 307 | |
duke@1 | 308 | /** |
duke@1 | 309 | * Writes the given byte[] as base64 encoded binary to the output. |
duke@1 | 310 | * |
duke@1 | 311 | * <p> |
duke@1 | 312 | * Being defined on this class allows this method to access the buffer directly, |
duke@1 | 313 | * which translates to a better performance. |
duke@1 | 314 | */ |
duke@1 | 315 | public void text(byte[] data, int dataLen) throws IOException { |
duke@1 | 316 | closeStartTag(); |
duke@1 | 317 | |
duke@1 | 318 | int start = 0; |
duke@1 | 319 | |
duke@1 | 320 | while(dataLen>0) { |
duke@1 | 321 | // how many bytes (in data) can we write without overflowing the buffer? |
duke@1 | 322 | int batchSize = Math.min(((octetBuffer.length-octetBufferIndex)/4)*3,dataLen); |
duke@1 | 323 | |
duke@1 | 324 | // write the batch |
duke@1 | 325 | octetBufferIndex = DatatypeConverterImpl._printBase64Binary(data,start,batchSize,octetBuffer,octetBufferIndex); |
duke@1 | 326 | |
duke@1 | 327 | if(batchSize<dataLen) |
duke@1 | 328 | flushBuffer(); |
duke@1 | 329 | |
duke@1 | 330 | start += batchSize; |
duke@1 | 331 | dataLen -= batchSize; |
duke@1 | 332 | |
duke@1 | 333 | } |
duke@1 | 334 | } |
duke@1 | 335 | |
duke@1 | 336 | // |
duke@1 | 337 | // |
duke@1 | 338 | // series of the write method that places bytes to the output |
duke@1 | 339 | // (by doing some buffering internal to this class) |
duke@1 | 340 | // |
duke@1 | 341 | |
duke@1 | 342 | /** |
duke@1 | 343 | * Writes one byte directly into the buffer. |
duke@1 | 344 | * |
duke@1 | 345 | * <p> |
duke@1 | 346 | * This method can be used somewhat like the {@code text} method, |
duke@1 | 347 | * but it doesn't perform character escaping. |
duke@1 | 348 | */ |
duke@1 | 349 | public final void write(int i) throws IOException { |
duke@1 | 350 | if (octetBufferIndex < octetBuffer.length) { |
duke@1 | 351 | octetBuffer[octetBufferIndex++] = (byte)i; |
duke@1 | 352 | } else { |
duke@1 | 353 | out.write(octetBuffer); |
duke@1 | 354 | octetBufferIndex = 1; |
duke@1 | 355 | octetBuffer[0] = (byte)i; |
duke@1 | 356 | } |
duke@1 | 357 | } |
duke@1 | 358 | |
duke@1 | 359 | protected final void write(byte[] b) throws IOException { |
duke@1 | 360 | write(b, 0, b.length); |
duke@1 | 361 | } |
duke@1 | 362 | |
duke@1 | 363 | protected final void write(byte[] b, int start, int length) throws IOException { |
duke@1 | 364 | if ((octetBufferIndex + length) < octetBuffer.length) { |
duke@1 | 365 | System.arraycopy(b, start, octetBuffer, octetBufferIndex, length); |
duke@1 | 366 | octetBufferIndex += length; |
duke@1 | 367 | } else { |
duke@1 | 368 | out.write(octetBuffer, 0, octetBufferIndex); |
duke@1 | 369 | out.write(b, start, length); |
duke@1 | 370 | octetBufferIndex = 0; |
duke@1 | 371 | } |
duke@1 | 372 | } |
duke@1 | 373 | |
duke@1 | 374 | protected final void flushBuffer() throws IOException { |
duke@1 | 375 | out.write(octetBuffer, 0, octetBufferIndex); |
duke@1 | 376 | octetBufferIndex = 0; |
duke@1 | 377 | } |
duke@1 | 378 | |
duke@1 | 379 | static byte[] toBytes(String s) { |
duke@1 | 380 | byte[] buf = new byte[s.length()]; |
duke@1 | 381 | for( int i=s.length()-1; i>=0; i-- ) |
duke@1 | 382 | buf[i] = (byte)s.charAt(i); |
duke@1 | 383 | return buf; |
duke@1 | 384 | } |
duke@1 | 385 | |
duke@1 | 386 | private static final byte[] XMLNS_EQUALS = toBytes(" xmlns=\""); |
duke@1 | 387 | private static final byte[] XMLNS_COLON = toBytes(" xmlns:"); |
duke@1 | 388 | private static final byte[] EQUALS = toBytes("=\""); |
duke@1 | 389 | private static final byte[] CLOSE_TAG = toBytes("</"); |
duke@1 | 390 | private static final byte[] EMPTY_TAG = toBytes("/>"); |
duke@1 | 391 | private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; |
duke@1 | 392 | private static final byte[] XML_DECL = toBytes("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"); |
duke@1 | 393 | } |