1 /* 2 * Copyright (c) 2005, 2010, 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 */ 25 26 package com.sun.xml.internal.stream.buffer; 27 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; 42 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; 50 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 { 83 84 /** 85 * In scope namespaces on a fragment 86 */ 87 protected Map<String,String> _inscopeNamespaces = Collections.emptyMap(); 88 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; 94 95 /** 96 * Fragmented array to hold structural information 97 */ 98 protected FragmentedArray<byte[]> _structure; 99 protected int _structurePtr; 100 101 /** 102 * Fragmented array to hold structural information as strings 103 */ 104 protected FragmentedArray<String[]> _structureStrings; 105 protected int _structureStringsPtr; 106 107 /** 108 * Fragmented array to hold content information in a shared char[] 109 */ 110 protected FragmentedArray<char[]> _contentCharactersBuffer; 111 protected int _contentCharactersBufferPtr; 112 113 /** 114 * Fragmented array to hold content information as objects 115 */ 116 protected FragmentedArray<Object[]> _contentObjects; 117 protected int _contentObjectsPtr; 118 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; 132 133 /** 134 * The system identifier associated with the buffer 135 */ 136 protected String systemId; 137 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 } 147 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 } 159 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 } 172 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 } 180 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 } 188 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 } 210 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 } 233 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 } 243 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 } 262 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 } 270 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 } 282 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 } 295 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 } 327 328 /** 329 * @deprecated 330 * Use {@link #writeTo(ContentHandler,boolean)} 331 */ 332 public final void writeTo(ContentHandler handler) throws SAXException { 333 writeTo(handler,isFragment()); 334 } 335 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 } 363 364 p.setErrorHandler(errorHandler); 365 366 p.process(); 367 } 368 369 public final void writeTo(ContentHandler handler, ErrorHandler errorHandler) throws SAXException { 370 writeTo(handler, errorHandler, isFragment()); 371 } 372 373 private static final TransformerFactory trnsformerFactory = TransformerFactory.newInstance(); 374 375 /** 376 * Writes out the contents of this buffer as DOM node and append that to the given node. 377 * 378 * Faster implementation would be desirable. 379 * 380 * @return 381 * The newly added child node. 382 */ 383 public final Node writeTo(Node n) throws XMLStreamBufferException { 384 try { 385 Transformer t = trnsformerFactory.newTransformer(); 386 t.transform(new XMLStreamBufferSource(this), new DOMResult(n)); 387 return n.getLastChild(); 388 } catch (TransformerException e) { 389 throw new XMLStreamBufferException(e); 390 } 391 } 392 393 /** 394 * Create a new buffer from a XMLStreamReader. 395 * 396 * @param reader 397 * A XMLStreamReader to read from to create. 398 * @return XMLStreamBuffer the created buffer 399 * @see MutableXMLStreamBuffer#createFromXMLStreamReader(XMLStreamReader) 400 */ 401 public static XMLStreamBuffer createNewBufferFromXMLStreamReader(XMLStreamReader reader) 402 throws XMLStreamException { 403 MutableXMLStreamBuffer b = new MutableXMLStreamBuffer(); 404 b.createFromXMLStreamReader(reader); 405 return b; 406 } 407 408 /** 409 * Create a new buffer from a {@link XMLReader} and {@link InputStream}. 410 * 411 * @param reader 412 * The {@link XMLReader} to use for parsing. 413 * @param in 414 * The {@link InputStream} to be parsed. 415 * @return XMLStreamBuffer the created buffer 416 * @see MutableXMLStreamBuffer#createFromXMLReader(XMLReader, InputStream) 417 */ 418 public static XMLStreamBuffer createNewBufferFromXMLReader(XMLReader reader, InputStream in) throws SAXException, IOException { 419 MutableXMLStreamBuffer b = new MutableXMLStreamBuffer(); 420 b.createFromXMLReader(reader, in); 421 return b; 422 } 423 424 /** 425 * Create a new buffer from a {@link XMLReader} and {@link InputStream}. 426 * 427 * @param reader 428 * The {@link XMLReader} to use for parsing. 429 * @param in 430 * The {@link InputStream} to be parsed. 431 * @param systemId 432 * The system ID of the input stream. 433 * @return XMLStreamBuffer the created buffer 434 * @see MutableXMLStreamBuffer#createFromXMLReader(XMLReader, InputStream, String) 435 */ 436 public static XMLStreamBuffer createNewBufferFromXMLReader(XMLReader reader, InputStream in, 437 String systemId) throws SAXException, IOException { 438 MutableXMLStreamBuffer b = new MutableXMLStreamBuffer(); 439 b.createFromXMLReader(reader, in, systemId); 440 return b; 441 } 442 443 protected final FragmentedArray<byte[]> getStructure() { 444 return _structure; 445 } 446 447 protected final int getStructurePtr() { 448 return _structurePtr; 449 } 450 451 protected final FragmentedArray<String[]> getStructureStrings() { 452 return _structureStrings; 453 } 454 455 protected final int getStructureStringsPtr() { 456 return _structureStringsPtr; 457 } 458 459 protected final FragmentedArray<char[]> getContentCharactersBuffer() { 460 return _contentCharactersBuffer; 461 } 462 463 protected final int getContentCharactersBufferPtr() { 464 return _contentCharactersBufferPtr; 465 } 466 467 protected final FragmentedArray<Object[]> getContentObjects() { 468 return _contentObjects; 469 } 470 471 protected final int getContentObjectsPtr() { 472 return _contentObjectsPtr; 473 } 474 }