| /* |
| * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.xml.internal.stream.buffer; |
| |
| import com.sun.xml.internal.stream.buffer.sax.SAXBufferProcessor; |
| import com.sun.xml.internal.stream.buffer.stax.StreamReaderBufferProcessor; |
| import com.sun.xml.internal.stream.buffer.stax.StreamWriterBufferProcessor; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.Collections; |
| import java.util.Map; |
| import javax.xml.stream.XMLStreamException; |
| import javax.xml.stream.XMLStreamReader; |
| import javax.xml.stream.XMLStreamWriter; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.dom.DOMResult; |
| |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.DTDHandler; |
| import org.xml.sax.ErrorHandler; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.XMLReader; |
| import org.xml.sax.ext.LexicalHandler; |
| import org.w3c.dom.Node; |
| |
| /** |
| * An immutable stream-based buffer of an XML infoset. |
| * |
| * <p> |
| * A XMLStreamBuffer is an abstract class. It is immutable with |
| * respect to the methods on the class, which are non-modifying in terms |
| * of state. |
| * |
| * <p> |
| * A XMLStreamBuffer can be processed using specific SAX and StAX-based |
| * processors. Utility methods on XMLStreamBuffer are provided for |
| * such functionality that utilize SAX and StAX-based processors. |
| * The same instance of a XMLStreamBuffer may be processed |
| * multiple times and concurrently by more than one processor. |
| * |
| * <p> |
| * There are two concrete implementations of XMLStreamBuffer. |
| * The first, {@link MutableXMLStreamBuffer}, can be instantiated for the creation |
| * of a buffer using SAX and StAX-based creators, and from which may be |
| * processed as an XMLStreamBuffer. The second, |
| * {@link XMLStreamBufferMark}, can be instantiated to mark into an existing |
| * buffer that is being created or processed. This allows a subtree of |
| * {@link XMLStreamBuffer} to be treated as its own {@link XMLStreamBuffer}. |
| * |
| * <p> |
| * A XMLStreamBuffer can represent a complete XML infoset or a subtree |
| * of an XML infoset. It is also capable of representing a "forest", |
| * where the buffer represents multiple adjacent XML elements, although |
| * in this mode there are restrictions about how you can consume such |
| * forest, because not all XML APIs handle forests very well. |
| */ |
| public abstract class XMLStreamBuffer { |
| |
| /** |
| * In scope namespaces on a fragment |
| */ |
| protected Map<String,String> _inscopeNamespaces = Collections.emptyMap(); |
| |
| /** |
| * True if the buffer was created from a parser that interns Strings |
| * as specified by the SAX interning features |
| */ |
| protected boolean _hasInternedStrings; |
| |
| /** |
| * Fragmented array to hold structural information |
| */ |
| protected FragmentedArray<byte[]> _structure; |
| protected int _structurePtr; |
| |
| /** |
| * Fragmented array to hold structural information as strings |
| */ |
| protected FragmentedArray<String[]> _structureStrings; |
| protected int _structureStringsPtr; |
| |
| /** |
| * Fragmented array to hold content information in a shared char[] |
| */ |
| protected FragmentedArray<char[]> _contentCharactersBuffer; |
| protected int _contentCharactersBufferPtr; |
| |
| /** |
| * Fragmented array to hold content information as objects |
| */ |
| protected FragmentedArray<Object[]> _contentObjects; |
| protected int _contentObjectsPtr; |
| |
| /** |
| * Number of trees in this stream buffer. |
| * |
| * <p> |
| * 1 if there's only one, which is the normal case. When the buffer |
| * holds a forest, this value is greater than 1. If the buffer is empty, then 0. |
| * |
| * <p> |
| * Notice that we cannot infer this value by looking at the {@link FragmentedArray}s, |
| * because this {@link XMLStreamBuffer} maybe a view of a portion of another bigger |
| * {@link XMLStreamBuffer}. |
| */ |
| protected int treeCount; |
| |
| /** |
| * The system identifier associated with the buffer |
| */ |
| protected String systemId; |
| |
| /** |
| * Is the buffer created by creator. |
| * |
| * @return |
| * <code>true</code> if the buffer has been created. |
| */ |
| public final boolean isCreated() { |
| return _structure.getArray()[0] != AbstractCreatorProcessor.T_END; |
| } |
| |
| /** |
| * Is the buffer a representation of a fragment of an XML infoset. |
| * |
| * @return |
| * <code>true</code> if the buffer is a representation of a fragment |
| * of an XML infoset. |
| */ |
| public final boolean isFragment() { |
| return (isCreated() && (_structure.getArray()[_structurePtr] & AbstractCreatorProcessor.TYPE_MASK) |
| != AbstractCreatorProcessor.T_DOCUMENT); |
| } |
| |
| /** |
| * Is the buffer a representation of a fragment of an XML infoset |
| * that is an element (and its contents). |
| * |
| * @return |
| * <code>true</code> if the buffer a representation |
| * of a fragment of an XML infoset that is an element (and its contents). |
| */ |
| public final boolean isElementFragment() { |
| return (isCreated() && (_structure.getArray()[_structurePtr] & AbstractCreatorProcessor.TYPE_MASK) |
| == AbstractCreatorProcessor.T_ELEMENT); |
| } |
| |
| /** |
| * Returns ture if this buffer represents a forest, which is |
| * are more than one adjacent XML elements. |
| */ |
| public final boolean isForest() { |
| return isCreated() && treeCount>1; |
| } |
| |
| /** |
| * Get the system identifier associated with the buffer. |
| * @return The system identifier. |
| */ |
| public final String getSystemId() { |
| return systemId; |
| } |
| |
| /** |
| * Get the in-scope namespaces. |
| * |
| * <p> |
| * |
| * The in-scope namespaces will be empty if the buffer is not a |
| * fragment ({@link #isFragment} returns <code>false</code>). |
| * |
| * The in-scope namespace will correspond to the in-scope namespaces of the |
| * fragment if the buffer is a fragment ({@link #isFragment} |
| * returns <code>false</code>). The in-scope namespaces will include any |
| * namespace delcarations on an element if the fragment correspond to that |
| * of an element ({@link #isElementFragment} returns <code>false</code>). |
| * |
| * @return |
| * The in-scope namespaces of the XMLStreamBuffer. |
| * Prefix to namespace URI. |
| */ |
| public final Map<String,String> getInscopeNamespaces() { |
| return _inscopeNamespaces; |
| } |
| |
| /** |
| * Has the buffer been created using Strings that have been interned |
| * for certain properties of information items. The Strings that are interned |
| * are those that correspond to Strings that are specified by the SAX API |
| * "string-interning" property |
| * (see <a href="http://java.sun.com/j2se/1.5.0/docs/api/org/xml/sax/package-summary.html#package_description">here</a>). |
| * |
| * <p> |
| * An buffer may have been created, for example, from an XML document parsed |
| * using the Xerces SAX parser. The Xerces SAX parser will have interned certain Strings |
| * according to the SAX string interning property. |
| * This method enables processors to avoid the duplication of |
| * String interning if such a feature is required by a procesing application and the |
| * buffer being processed was created using Strings that have been interned. |
| * |
| * @return |
| * <code>true</code> if the buffer has been created using Strings that |
| * have been interned. |
| */ |
| public final boolean hasInternedStrings() { |
| return _hasInternedStrings; |
| } |
| |
| /** |
| * Read the contents of the buffer as a {@link XMLStreamReader}. |
| * |
| * @return |
| * A an instance of a {@link StreamReaderBufferProcessor}. Always non-null. |
| */ |
| public final StreamReaderBufferProcessor readAsXMLStreamReader() throws XMLStreamException { |
| return new StreamReaderBufferProcessor(this); |
| } |
| |
| /** |
| * Write the contents of the buffer to an XMLStreamWriter. |
| * |
| * <p> |
| * The XMLStreamBuffer will be written out to the XMLStreamWriter using |
| * an instance of {@link StreamWriterBufferProcessor}. |
| * |
| * @param writer |
| * A XMLStreamWriter to write to. |
| * @param writeAsFragment |
| * If true, {@link XMLStreamWriter} will not receive {@link XMLStreamWriter#writeStartDocument()} |
| * nor {@link XMLStreamWriter#writeEndDocument()}. This is desirable behavior when |
| * you are writing the contents of a buffer into a bigger document. |
| */ |
| public final void writeToXMLStreamWriter(XMLStreamWriter writer, boolean writeAsFragment) throws XMLStreamException { |
| StreamWriterBufferProcessor p = new StreamWriterBufferProcessor(this,writeAsFragment); |
| p.process(writer); |
| } |
| |
| /** |
| * @deprecated |
| * Use {@link #writeToXMLStreamWriter(XMLStreamWriter, boolean)} |
| */ |
| public final void writeToXMLStreamWriter(XMLStreamWriter writer) throws XMLStreamException { |
| writeToXMLStreamWriter(writer, this.isFragment()); |
| } |
| |
| /** |
| * Reads the contents of the buffer from a {@link XMLReader}. |
| * |
| * @return |
| * A an instance of a {@link SAXBufferProcessor}. |
| * @deprecated |
| * Use {@link #readAsXMLReader(boolean)} |
| */ |
| public final SAXBufferProcessor readAsXMLReader() { |
| return new SAXBufferProcessor(this,isFragment()); |
| } |
| |
| /** |
| * Reads the contents of the buffer from a {@link XMLReader}. |
| * |
| * @param produceFragmentEvent |
| * True to generate fragment SAX events without start/endDocument. |
| * False to generate a full document SAX events. |
| * @return |
| * A an instance of a {@link SAXBufferProcessor}. |
| */ |
| public final SAXBufferProcessor readAsXMLReader(boolean produceFragmentEvent) { |
| return new SAXBufferProcessor(this,produceFragmentEvent); |
| } |
| |
| /** |
| * Write the contents of the buffer to a {@link ContentHandler}. |
| * |
| * <p> |
| * If the <code>handler</code> is also an instance of other SAX-based |
| * handlers, such as {@link LexicalHandler}, than corresponding SAX events |
| * will be reported to those handlers. |
| * |
| * @param handler |
| * The ContentHandler to receive SAX events. |
| * @param produceFragmentEvent |
| * True to generate fragment SAX events without start/endDocument. |
| * False to generate a full document SAX events. |
| * |
| * @throws SAXException |
| * if a parsing fails, or if {@link ContentHandler} throws a {@link SAXException}. |
| */ |
| public final void writeTo(ContentHandler handler, boolean produceFragmentEvent) throws SAXException { |
| SAXBufferProcessor p = readAsXMLReader(produceFragmentEvent); |
| p.setContentHandler(handler); |
| if (p instanceof LexicalHandler) { |
| p.setLexicalHandler((LexicalHandler)handler); |
| } |
| if (p instanceof DTDHandler) { |
| p.setDTDHandler((DTDHandler)handler); |
| } |
| if (p instanceof ErrorHandler) { |
| p.setErrorHandler((ErrorHandler)handler); |
| } |
| p.process(); |
| } |
| |
| /** |
| * @deprecated |
| * Use {@link #writeTo(ContentHandler,boolean)} |
| */ |
| public final void writeTo(ContentHandler handler) throws SAXException { |
| writeTo(handler,isFragment()); |
| } |
| |
| /** |
| * Write the contents of the buffer to a {@link ContentHandler} with errors |
| * report to a {@link ErrorHandler}. |
| * |
| * <p> |
| * If the <code>handler</code> is also an instance of other SAX-based |
| * handlers, such as {@link LexicalHandler}, than corresponding SAX events |
| * will be reported to those handlers. |
| * |
| * @param handler |
| * The ContentHandler to receive SAX events. |
| * @param errorHandler |
| * The ErrorHandler to receive error events. |
| * |
| * @throws SAXException |
| * if a parsing fails and {@link ErrorHandler} throws a {@link SAXException}, |
| * or if {@link ContentHandler} throws a {@link SAXException}. |
| */ |
| public final void writeTo(ContentHandler handler, ErrorHandler errorHandler, boolean produceFragmentEvent) throws SAXException { |
| SAXBufferProcessor p = readAsXMLReader(produceFragmentEvent); |
| p.setContentHandler(handler); |
| if (p instanceof LexicalHandler) { |
| p.setLexicalHandler((LexicalHandler)handler); |
| } |
| if (p instanceof DTDHandler) { |
| p.setDTDHandler((DTDHandler)handler); |
| } |
| |
| p.setErrorHandler(errorHandler); |
| |
| p.process(); |
| } |
| |
| public final void writeTo(ContentHandler handler, ErrorHandler errorHandler) throws SAXException { |
| writeTo(handler, errorHandler, isFragment()); |
| } |
| |
| private static final ContextClassloaderLocal<TransformerFactory> trnsformerFactory = new ContextClassloaderLocal<TransformerFactory>() { |
| @Override |
| protected TransformerFactory initialValue() throws Exception { |
| return TransformerFactory.newInstance(); |
| } |
| }; |
| |
| /** |
| * Writes out the contents of this buffer as DOM node and append that to the given node. |
| * |
| * Faster implementation would be desirable. |
| * |
| * @return |
| * The newly added child node. |
| */ |
| public final Node writeTo(Node n) throws XMLStreamBufferException { |
| try { |
| Transformer t = trnsformerFactory.get().newTransformer(); |
| t.transform(new XMLStreamBufferSource(this), new DOMResult(n)); |
| return n.getLastChild(); |
| } catch (TransformerException e) { |
| throw new XMLStreamBufferException(e); |
| } |
| } |
| |
| /** |
| * Create a new buffer from a XMLStreamReader. |
| * |
| * @param reader |
| * A XMLStreamReader to read from to create. |
| * @return XMLStreamBuffer the created buffer |
| * @see MutableXMLStreamBuffer#createFromXMLStreamReader(XMLStreamReader) |
| */ |
| public static XMLStreamBuffer createNewBufferFromXMLStreamReader(XMLStreamReader reader) |
| throws XMLStreamException { |
| MutableXMLStreamBuffer b = new MutableXMLStreamBuffer(); |
| b.createFromXMLStreamReader(reader); |
| return b; |
| } |
| |
| /** |
| * Create a new buffer from a {@link XMLReader} and {@link InputStream}. |
| * |
| * @param reader |
| * The {@link XMLReader} to use for parsing. |
| * @param in |
| * The {@link InputStream} to be parsed. |
| * @return XMLStreamBuffer the created buffer |
| * @see MutableXMLStreamBuffer#createFromXMLReader(XMLReader, InputStream) |
| */ |
| public static XMLStreamBuffer createNewBufferFromXMLReader(XMLReader reader, InputStream in) throws SAXException, IOException { |
| MutableXMLStreamBuffer b = new MutableXMLStreamBuffer(); |
| b.createFromXMLReader(reader, in); |
| return b; |
| } |
| |
| /** |
| * Create a new buffer from a {@link XMLReader} and {@link InputStream}. |
| * |
| * @param reader |
| * The {@link XMLReader} to use for parsing. |
| * @param in |
| * The {@link InputStream} to be parsed. |
| * @param systemId |
| * The system ID of the input stream. |
| * @return XMLStreamBuffer the created buffer |
| * @see MutableXMLStreamBuffer#createFromXMLReader(XMLReader, InputStream, String) |
| */ |
| public static XMLStreamBuffer createNewBufferFromXMLReader(XMLReader reader, InputStream in, |
| String systemId) throws SAXException, IOException { |
| MutableXMLStreamBuffer b = new MutableXMLStreamBuffer(); |
| b.createFromXMLReader(reader, in, systemId); |
| return b; |
| } |
| |
| protected final FragmentedArray<byte[]> getStructure() { |
| return _structure; |
| } |
| |
| protected final int getStructurePtr() { |
| return _structurePtr; |
| } |
| |
| protected final FragmentedArray<String[]> getStructureStrings() { |
| return _structureStrings; |
| } |
| |
| protected final int getStructureStringsPtr() { |
| return _structureStringsPtr; |
| } |
| |
| protected final FragmentedArray<char[]> getContentCharactersBuffer() { |
| return _contentCharactersBuffer; |
| } |
| |
| protected final int getContentCharactersBufferPtr() { |
| return _contentCharactersBufferPtr; |
| } |
| |
| protected final FragmentedArray<Object[]> getContentObjects() { |
| return _contentObjects; |
| } |
| |
| protected final int getContentObjectsPtr() { |
| return _contentObjectsPtr; |
| } |
| } |