Fri, 14 Feb 2014 11:13:45 +0100
8026188: Enhance envelope factory
Summary: Avoiding caching data initialized via TCCL in static context; fix also reviewed by Alexander Fomin
Reviewed-by: ahgross, mgrebac, skoivu
1 /*
2 * Copyright (c) 2005, 2014, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.xml.internal.stream.buffer;
28 import com.sun.xml.internal.stream.buffer.sax.SAXBufferProcessor;
29 import com.sun.xml.internal.stream.buffer.stax.StreamReaderBufferProcessor;
30 import com.sun.xml.internal.stream.buffer.stax.StreamWriterBufferProcessor;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.util.Collections;
34 import java.util.Map;
35 import javax.xml.stream.XMLStreamException;
36 import javax.xml.stream.XMLStreamReader;
37 import javax.xml.stream.XMLStreamWriter;
38 import javax.xml.transform.TransformerFactory;
39 import javax.xml.transform.Transformer;
40 import javax.xml.transform.TransformerException;
41 import javax.xml.transform.dom.DOMResult;
43 import org.xml.sax.ContentHandler;
44 import org.xml.sax.DTDHandler;
45 import org.xml.sax.ErrorHandler;
46 import org.xml.sax.SAXException;
47 import org.xml.sax.XMLReader;
48 import org.xml.sax.ext.LexicalHandler;
49 import org.w3c.dom.Node;
51 /**
52 * An immutable stream-based buffer of an XML infoset.
53 *
54 * <p>
55 * A XMLStreamBuffer is an abstract class. It is immutable with
56 * respect to the methods on the class, which are non-modifying in terms
57 * of state.
58 *
59 * <p>
60 * A XMLStreamBuffer can be processed using specific SAX and StAX-based
61 * processors. Utility methods on XMLStreamBuffer are provided for
62 * such functionality that utilize SAX and StAX-based processors.
63 * The same instance of a XMLStreamBuffer may be processed
64 * multiple times and concurrently by more than one processor.
65 *
66 * <p>
67 * There are two concrete implementations of XMLStreamBuffer.
68 * The first, {@link MutableXMLStreamBuffer}, can be instantiated for the creation
69 * of a buffer using SAX and StAX-based creators, and from which may be
70 * processed as an XMLStreamBuffer. The second,
71 * {@link XMLStreamBufferMark}, can be instantiated to mark into an existing
72 * buffer that is being created or processed. This allows a subtree of
73 * {@link XMLStreamBuffer} to be treated as its own {@link XMLStreamBuffer}.
74 *
75 * <p>
76 * A XMLStreamBuffer can represent a complete XML infoset or a subtree
77 * of an XML infoset. It is also capable of representing a "forest",
78 * where the buffer represents multiple adjacent XML elements, although
79 * in this mode there are restrictions about how you can consume such
80 * forest, because not all XML APIs handle forests very well.
81 */
82 public abstract class XMLStreamBuffer {
84 /**
85 * In scope namespaces on a fragment
86 */
87 protected Map<String,String> _inscopeNamespaces = Collections.emptyMap();
89 /**
90 * True if the buffer was created from a parser that interns Strings
91 * as specified by the SAX interning features
92 */
93 protected boolean _hasInternedStrings;
95 /**
96 * Fragmented array to hold structural information
97 */
98 protected FragmentedArray<byte[]> _structure;
99 protected int _structurePtr;
101 /**
102 * Fragmented array to hold structural information as strings
103 */
104 protected FragmentedArray<String[]> _structureStrings;
105 protected int _structureStringsPtr;
107 /**
108 * Fragmented array to hold content information in a shared char[]
109 */
110 protected FragmentedArray<char[]> _contentCharactersBuffer;
111 protected int _contentCharactersBufferPtr;
113 /**
114 * Fragmented array to hold content information as objects
115 */
116 protected FragmentedArray<Object[]> _contentObjects;
117 protected int _contentObjectsPtr;
119 /**
120 * Number of trees in this stream buffer.
121 *
122 * <p>
123 * 1 if there's only one, which is the normal case. When the buffer
124 * holds a forest, this value is greater than 1. If the buffer is empty, then 0.
125 *
126 * <p>
127 * Notice that we cannot infer this value by looking at the {@link FragmentedArray}s,
128 * because this {@link XMLStreamBuffer} maybe a view of a portion of another bigger
129 * {@link XMLStreamBuffer}.
130 */
131 protected int treeCount;
133 /**
134 * The system identifier associated with the buffer
135 */
136 protected String systemId;
138 /**
139 * Is the buffer created by creator.
140 *
141 * @return
142 * <code>true</code> if the buffer has been created.
143 */
144 public final boolean isCreated() {
145 return _structure.getArray()[0] != AbstractCreatorProcessor.T_END;
146 }
148 /**
149 * Is the buffer a representation of a fragment of an XML infoset.
150 *
151 * @return
152 * <code>true</code> if the buffer is a representation of a fragment
153 * of an XML infoset.
154 */
155 public final boolean isFragment() {
156 return (isCreated() && (_structure.getArray()[_structurePtr] & AbstractCreatorProcessor.TYPE_MASK)
157 != AbstractCreatorProcessor.T_DOCUMENT);
158 }
160 /**
161 * Is the buffer a representation of a fragment of an XML infoset
162 * that is an element (and its contents).
163 *
164 * @return
165 * <code>true</code> if the buffer a representation
166 * of a fragment of an XML infoset that is an element (and its contents).
167 */
168 public final boolean isElementFragment() {
169 return (isCreated() && (_structure.getArray()[_structurePtr] & AbstractCreatorProcessor.TYPE_MASK)
170 == AbstractCreatorProcessor.T_ELEMENT);
171 }
173 /**
174 * Returns ture if this buffer represents a forest, which is
175 * are more than one adjacent XML elements.
176 */
177 public final boolean isForest() {
178 return isCreated() && treeCount>1;
179 }
181 /**
182 * Get the system identifier associated with the buffer.
183 * @return The system identifier.
184 */
185 public final String getSystemId() {
186 return systemId;
187 }
189 /**
190 * Get the in-scope namespaces.
191 *
192 * <p>
193 *
194 * The in-scope namespaces will be empty if the buffer is not a
195 * fragment ({@link #isFragment} returns <code>false</code>).
196 *
197 * The in-scope namespace will correspond to the in-scope namespaces of the
198 * fragment if the buffer is a fragment ({@link #isFragment}
199 * returns <code>false</code>). The in-scope namespaces will include any
200 * namespace delcarations on an element if the fragment correspond to that
201 * of an element ({@link #isElementFragment} returns <code>false</code>).
202 *
203 * @return
204 * The in-scope namespaces of the XMLStreamBuffer.
205 * Prefix to namespace URI.
206 */
207 public final Map<String,String> getInscopeNamespaces() {
208 return _inscopeNamespaces;
209 }
211 /**
212 * Has the buffer been created using Strings that have been interned
213 * for certain properties of information items. The Strings that are interned
214 * are those that correspond to Strings that are specified by the SAX API
215 * "string-interning" property
216 * (see <a href="http://java.sun.com/j2se/1.5.0/docs/api/org/xml/sax/package-summary.html#package_description">here</a>).
217 *
218 * <p>
219 * An buffer may have been created, for example, from an XML document parsed
220 * using the Xerces SAX parser. The Xerces SAX parser will have interned certain Strings
221 * according to the SAX string interning property.
222 * This method enables processors to avoid the duplication of
223 * String interning if such a feature is required by a procesing application and the
224 * buffer being processed was created using Strings that have been interned.
225 *
226 * @return
227 * <code>true</code> if the buffer has been created using Strings that
228 * have been interned.
229 */
230 public final boolean hasInternedStrings() {
231 return _hasInternedStrings;
232 }
234 /**
235 * Read the contents of the buffer as a {@link XMLStreamReader}.
236 *
237 * @return
238 * A an instance of a {@link StreamReaderBufferProcessor}. Always non-null.
239 */
240 public final StreamReaderBufferProcessor readAsXMLStreamReader() throws XMLStreamException {
241 return new StreamReaderBufferProcessor(this);
242 }
244 /**
245 * Write the contents of the buffer to an XMLStreamWriter.
246 *
247 * <p>
248 * The XMLStreamBuffer will be written out to the XMLStreamWriter using
249 * an instance of {@link StreamWriterBufferProcessor}.
250 *
251 * @param writer
252 * A XMLStreamWriter to write to.
253 * @param writeAsFragment
254 * If true, {@link XMLStreamWriter} will not receive {@link XMLStreamWriter#writeStartDocument()}
255 * nor {@link XMLStreamWriter#writeEndDocument()}. This is desirable behavior when
256 * you are writing the contents of a buffer into a bigger document.
257 */
258 public final void writeToXMLStreamWriter(XMLStreamWriter writer, boolean writeAsFragment) throws XMLStreamException {
259 StreamWriterBufferProcessor p = new StreamWriterBufferProcessor(this,writeAsFragment);
260 p.process(writer);
261 }
263 /**
264 * @deprecated
265 * Use {@link #writeToXMLStreamWriter(XMLStreamWriter, boolean)}
266 */
267 public final void writeToXMLStreamWriter(XMLStreamWriter writer) throws XMLStreamException {
268 writeToXMLStreamWriter(writer, this.isFragment());
269 }
271 /**
272 * Reads the contents of the buffer from a {@link XMLReader}.
273 *
274 * @return
275 * A an instance of a {@link SAXBufferProcessor}.
276 * @deprecated
277 * Use {@link #readAsXMLReader(boolean)}
278 */
279 public final SAXBufferProcessor readAsXMLReader() {
280 return new SAXBufferProcessor(this,isFragment());
281 }
283 /**
284 * Reads the contents of the buffer from a {@link XMLReader}.
285 *
286 * @param produceFragmentEvent
287 * True to generate fragment SAX events without start/endDocument.
288 * False to generate a full document SAX events.
289 * @return
290 * A an instance of a {@link SAXBufferProcessor}.
291 */
292 public final SAXBufferProcessor readAsXMLReader(boolean produceFragmentEvent) {
293 return new SAXBufferProcessor(this,produceFragmentEvent);
294 }
296 /**
297 * Write the contents of the buffer to a {@link ContentHandler}.
298 *
299 * <p>
300 * If the <code>handler</code> is also an instance of other SAX-based
301 * handlers, such as {@link LexicalHandler}, than corresponding SAX events
302 * will be reported to those handlers.
303 *
304 * @param handler
305 * The ContentHandler to receive SAX events.
306 * @param produceFragmentEvent
307 * True to generate fragment SAX events without start/endDocument.
308 * False to generate a full document SAX events.
309 *
310 * @throws SAXException
311 * if a parsing fails, or if {@link ContentHandler} throws a {@link SAXException}.
312 */
313 public final void writeTo(ContentHandler handler, boolean produceFragmentEvent) throws SAXException {
314 SAXBufferProcessor p = readAsXMLReader(produceFragmentEvent);
315 p.setContentHandler(handler);
316 if (p instanceof LexicalHandler) {
317 p.setLexicalHandler((LexicalHandler)handler);
318 }
319 if (p instanceof DTDHandler) {
320 p.setDTDHandler((DTDHandler)handler);
321 }
322 if (p instanceof ErrorHandler) {
323 p.setErrorHandler((ErrorHandler)handler);
324 }
325 p.process();
326 }
328 /**
329 * @deprecated
330 * Use {@link #writeTo(ContentHandler,boolean)}
331 */
332 public final void writeTo(ContentHandler handler) throws SAXException {
333 writeTo(handler,isFragment());
334 }
336 /**
337 * Write the contents of the buffer to a {@link ContentHandler} with errors
338 * report to a {@link ErrorHandler}.
339 *
340 * <p>
341 * If the <code>handler</code> is also an instance of other SAX-based
342 * handlers, such as {@link LexicalHandler}, than corresponding SAX events
343 * will be reported to those handlers.
344 *
345 * @param handler
346 * The ContentHandler to receive SAX events.
347 * @param errorHandler
348 * The ErrorHandler to receive error events.
349 *
350 * @throws SAXException
351 * if a parsing fails and {@link ErrorHandler} throws a {@link SAXException},
352 * or if {@link ContentHandler} throws a {@link SAXException}.
353 */
354 public final void writeTo(ContentHandler handler, ErrorHandler errorHandler, boolean produceFragmentEvent) throws SAXException {
355 SAXBufferProcessor p = readAsXMLReader(produceFragmentEvent);
356 p.setContentHandler(handler);
357 if (p instanceof LexicalHandler) {
358 p.setLexicalHandler((LexicalHandler)handler);
359 }
360 if (p instanceof DTDHandler) {
361 p.setDTDHandler((DTDHandler)handler);
362 }
364 p.setErrorHandler(errorHandler);
366 p.process();
367 }
369 public final void writeTo(ContentHandler handler, ErrorHandler errorHandler) throws SAXException {
370 writeTo(handler, errorHandler, isFragment());
371 }
373 private static final ContextClassloaderLocal<TransformerFactory> trnsformerFactory = new ContextClassloaderLocal<TransformerFactory>() {
374 @Override
375 protected TransformerFactory initialValue() throws Exception {
376 return TransformerFactory.newInstance();
377 }
378 };
380 /**
381 * Writes out the contents of this buffer as DOM node and append that to the given node.
382 *
383 * Faster implementation would be desirable.
384 *
385 * @return
386 * The newly added child node.
387 */
388 public final Node writeTo(Node n) throws XMLStreamBufferException {
389 try {
390 Transformer t = trnsformerFactory.get().newTransformer();
391 t.transform(new XMLStreamBufferSource(this), new DOMResult(n));
392 return n.getLastChild();
393 } catch (TransformerException e) {
394 throw new XMLStreamBufferException(e);
395 }
396 }
398 /**
399 * Create a new buffer from a XMLStreamReader.
400 *
401 * @param reader
402 * A XMLStreamReader to read from to create.
403 * @return XMLStreamBuffer the created buffer
404 * @see MutableXMLStreamBuffer#createFromXMLStreamReader(XMLStreamReader)
405 */
406 public static XMLStreamBuffer createNewBufferFromXMLStreamReader(XMLStreamReader reader)
407 throws XMLStreamException {
408 MutableXMLStreamBuffer b = new MutableXMLStreamBuffer();
409 b.createFromXMLStreamReader(reader);
410 return b;
411 }
413 /**
414 * Create a new buffer from a {@link XMLReader} and {@link InputStream}.
415 *
416 * @param reader
417 * The {@link XMLReader} to use for parsing.
418 * @param in
419 * The {@link InputStream} to be parsed.
420 * @return XMLStreamBuffer the created buffer
421 * @see MutableXMLStreamBuffer#createFromXMLReader(XMLReader, InputStream)
422 */
423 public static XMLStreamBuffer createNewBufferFromXMLReader(XMLReader reader, InputStream in) throws SAXException, IOException {
424 MutableXMLStreamBuffer b = new MutableXMLStreamBuffer();
425 b.createFromXMLReader(reader, in);
426 return b;
427 }
429 /**
430 * Create a new buffer from a {@link XMLReader} and {@link InputStream}.
431 *
432 * @param reader
433 * The {@link XMLReader} to use for parsing.
434 * @param in
435 * The {@link InputStream} to be parsed.
436 * @param systemId
437 * The system ID of the input stream.
438 * @return XMLStreamBuffer the created buffer
439 * @see MutableXMLStreamBuffer#createFromXMLReader(XMLReader, InputStream, String)
440 */
441 public static XMLStreamBuffer createNewBufferFromXMLReader(XMLReader reader, InputStream in,
442 String systemId) throws SAXException, IOException {
443 MutableXMLStreamBuffer b = new MutableXMLStreamBuffer();
444 b.createFromXMLReader(reader, in, systemId);
445 return b;
446 }
448 protected final FragmentedArray<byte[]> getStructure() {
449 return _structure;
450 }
452 protected final int getStructurePtr() {
453 return _structurePtr;
454 }
456 protected final FragmentedArray<String[]> getStructureStrings() {
457 return _structureStrings;
458 }
460 protected final int getStructureStringsPtr() {
461 return _structureStringsPtr;
462 }
464 protected final FragmentedArray<char[]> getContentCharactersBuffer() {
465 return _contentCharactersBuffer;
466 }
468 protected final int getContentCharactersBufferPtr() {
469 return _contentCharactersBufferPtr;
470 }
472 protected final FragmentedArray<Object[]> getContentObjects() {
473 return _contentObjects;
474 }
476 protected final int getContentObjectsPtr() {
477 return _contentObjectsPtr;
478 }
479 }