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