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 }