blob: 85166778d73dee0fd227cfb5c754d9bc5cb11fa1 [file] [log] [blame]
/*
* 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;
}
}