com.sun.xml.stream.buffer.stax.StreamWriterBufferProcessor Maven / Gradle / Ivy
Show all versions of streambuffer Show documentation
/*
* Copyright (c) 2005, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package com.sun.xml.stream.buffer.stax;
import com.sun.xml.stream.buffer.AbstractProcessor;
import com.sun.xml.stream.buffer.XMLStreamBuffer;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.jvnet.staxex.Base64Data;
import org.jvnet.staxex.XMLStreamWriterEx;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
/**
* A processor of a {@link XMLStreamBuffer} that writes the XML infoset to a
* {@link XMLStreamWriter}.
*
* @author [email protected]
* @author [email protected]
*/
public class StreamWriterBufferProcessor extends AbstractProcessor {
public StreamWriterBufferProcessor() {
super();
}
/**
* @deprecated
* Use {@link #StreamWriterBufferProcessor(XMLStreamBuffer, boolean)}
*/
@Deprecated
public StreamWriterBufferProcessor(XMLStreamBuffer buffer) {
setXMLStreamBuffer(buffer,buffer.isFragment());
}
/**
* @param produceFragmentEvent
* True to generate fragment SAX events without start/endDocument.
* False to generate a full document SAX events.
*/
public StreamWriterBufferProcessor(XMLStreamBuffer buffer,boolean produceFragmentEvent) {
setXMLStreamBuffer(buffer,produceFragmentEvent);
}
public final void process(XMLStreamBuffer buffer, XMLStreamWriter writer) throws XMLStreamException {
setXMLStreamBuffer(buffer,buffer.isFragment());
process(writer);
}
public void process(XMLStreamWriter writer) throws XMLStreamException {
if(_fragmentMode){
writeFragment(writer);
}else{
write(writer);
}
}
/**
* @deprecated
* Use {@link #setXMLStreamBuffer(XMLStreamBuffer, boolean)}
*/
@Deprecated
public void setXMLStreamBuffer(XMLStreamBuffer buffer) {
setBuffer(buffer);
}
/**
* @param produceFragmentEvent
* True to generate fragment SAX events without start/endDocument.
* False to generate a full document SAX events.
*/
public void setXMLStreamBuffer(XMLStreamBuffer buffer, boolean produceFragmentEvent) {
setBuffer(buffer,produceFragmentEvent);
}
/**
* Writes a full XML infoset event to the given writer,
* including start/end document.
* Any inscope namespaces present will be written as namespace
* delcarations on each top-level element.
*/
public void write(XMLStreamWriter writer) throws XMLStreamException{
if(!_fragmentMode) {
if(_treeCount>1)
throw new IllegalStateException("forest cannot be written as a full infoset");
writer.writeStartDocument();
}
while(true) {
int item = getEIIState(peekStructure());
writer.flush();
switch(item) {
case STATE_DOCUMENT:
readStructure(); //skip
break;
case STATE_ELEMENT_U_LN_QN:
case STATE_ELEMENT_P_U_LN:
case STATE_ELEMENT_U_LN:
case STATE_ELEMENT_LN:
writeFragment(writer);
break;
case STATE_COMMENT_AS_CHAR_ARRAY_SMALL: {
readStructure();
final int length = readStructure();
final int start = readContentCharactersBuffer(length);
final String comment = new String(_contentCharactersBuffer, start, length);
writer.writeComment(comment);
break;
}
case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM: {
readStructure();
final int length = readStructure16();
final int start = readContentCharactersBuffer(length);
final String comment = new String(_contentCharactersBuffer, start, length);
writer.writeComment(comment);
break;
}
case STATE_COMMENT_AS_CHAR_ARRAY_COPY: {
readStructure();
final char[] ch = readContentCharactersCopy();
writer.writeComment(new String(ch));
break;
}
case STATE_PROCESSING_INSTRUCTION:
readStructure();
writer.writeProcessingInstruction(readStructureString(), readStructureString());
break;
case STATE_END: // done
readStructure();
writer.writeEndDocument();
return;
default:
throw new XMLStreamException("Invalid State "+item);
}
}
}
/**
* Writes the buffer as a fragment, meaning
* the writer will not receive start/endDocument events.
* Any inscope namespaces present will be written as namespace
* delcarations on each top-level element.
*
* If {@link XMLStreamBuffer} has a forest, this method will write all the forests.
*/
public void writeFragment(XMLStreamWriter writer) throws XMLStreamException {
if (writer instanceof XMLStreamWriterEx) {
writeFragmentEx((XMLStreamWriterEx)writer);
} else {
writeFragmentNoEx(writer);
}
}
public void writeFragmentEx(XMLStreamWriterEx writer) throws XMLStreamException {
int depth = 0; // used to determine when we are done with a tree.
int item = getEIIState(peekStructure());
if(item==STATE_DOCUMENT)
readStructure(); // skip STATE_DOCUMENT
do {
item = readEiiState();
switch(item) {
case STATE_DOCUMENT:
throw new AssertionError();
case STATE_ELEMENT_U_LN_QN: {
depth ++;
final String uri = readStructureString();
final String localName = readStructureString();
final String prefix = getPrefixFromQName(readStructureString());
writer.writeStartElement(prefix,localName,uri);
writeAttributes(writer, isInscope(depth));
break;
}
case STATE_ELEMENT_P_U_LN: {
depth ++;
final String prefix = readStructureString();
final String uri = readStructureString();
final String localName = readStructureString();
writer.writeStartElement(prefix,localName,uri);
writeAttributes(writer, isInscope(depth));
break;
}
case STATE_ELEMENT_U_LN: {
depth ++;
final String uri = readStructureString();
final String localName = readStructureString();
writer.writeStartElement("",localName,uri);
writeAttributes(writer, isInscope(depth));
break;
}
case STATE_ELEMENT_LN: {
depth ++;
final String localName = readStructureString();
writer.writeStartElement(localName);
writeAttributes(writer, isInscope(depth));
break;
}
case STATE_TEXT_AS_CHAR_ARRAY_SMALL: {
final int length = readStructure();
final int start = readContentCharactersBuffer(length);
writer.writeCharacters(_contentCharactersBuffer,start,length);
break;
}
case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM: {
final int length = readStructure16();
final int start = readContentCharactersBuffer(length);
writer.writeCharacters(_contentCharactersBuffer,start,length);
break;
}
case STATE_TEXT_AS_CHAR_ARRAY_COPY: {
char[] c = readContentCharactersCopy();
writer.writeCharacters(c,0,c.length);
break;
}
case STATE_TEXT_AS_STRING: {
final String s = readContentString();
writer.writeCharacters(s);
break;
}
case STATE_TEXT_AS_OBJECT: {
final CharSequence c = (CharSequence)readContentObject();
writer.writePCDATA(c);
break;
}
case STATE_COMMENT_AS_CHAR_ARRAY_SMALL: {
final int length = readStructure();
final int start = readContentCharactersBuffer(length);
final String comment = new String(_contentCharactersBuffer, start, length);
writer.writeComment(comment);
break;
}
case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM: {
final int length = readStructure16();
final int start = readContentCharactersBuffer(length);
final String comment = new String(_contentCharactersBuffer, start, length);
writer.writeComment(comment);
break;
}
case STATE_COMMENT_AS_CHAR_ARRAY_COPY: {
final char[] ch = readContentCharactersCopy();
writer.writeComment(new String(ch));
break;
}
case STATE_PROCESSING_INSTRUCTION:
writer.writeProcessingInstruction(readStructureString(), readStructureString());
break;
case STATE_END:
writer.writeEndElement();
depth --;
if(depth==0)
_treeCount--;
break;
default:
throw new XMLStreamException("Invalid State "+item);
}
} while(depth>0 || _treeCount>0);
}
public void writeFragmentNoEx(XMLStreamWriter writer) throws XMLStreamException {
int depth = 0;
int item = getEIIState(peekStructure());
if(item==STATE_DOCUMENT)
readStructure(); // skip STATE_DOCUMENT
do {
item = readEiiState();
switch(item) {
case STATE_DOCUMENT:
throw new AssertionError();
case STATE_ELEMENT_U_LN_QN: {
depth ++;
final String uri = readStructureString();
final String localName = readStructureString();
final String prefix = getPrefixFromQName(readStructureString());
writer.writeStartElement(prefix,localName,uri);
writeAttributes(writer, isInscope(depth));
break;
}
case STATE_ELEMENT_P_U_LN: {
depth ++;
final String prefix = readStructureString();
final String uri = readStructureString();
final String localName = readStructureString();
writer.writeStartElement(prefix,localName,uri);
writeAttributes(writer, isInscope(depth));
break;
}
case STATE_ELEMENT_U_LN: {
depth ++;
final String uri = readStructureString();
final String localName = readStructureString();
writer.writeStartElement("",localName,uri);
writeAttributes(writer, isInscope(depth));
break;
}
case STATE_ELEMENT_LN: {
depth ++;
final String localName = readStructureString();
writer.writeStartElement(localName);
writeAttributes(writer, isInscope(depth));
break;
}
case STATE_TEXT_AS_CHAR_ARRAY_SMALL: {
final int length = readStructure();
final int start = readContentCharactersBuffer(length);
writer.writeCharacters(_contentCharactersBuffer,start,length);
break;
}
case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM: {
final int length = readStructure16();
final int start = readContentCharactersBuffer(length);
writer.writeCharacters(_contentCharactersBuffer,start,length);
break;
}
case STATE_TEXT_AS_CHAR_ARRAY_COPY: {
char[] c = readContentCharactersCopy();
writer.writeCharacters(c,0,c.length);
break;
}
case STATE_TEXT_AS_STRING: {
final String s = readContentString();
writer.writeCharacters(s);
break;
}
case STATE_TEXT_AS_OBJECT: {
final CharSequence c = (CharSequence)readContentObject();
if (c instanceof Base64Data) {
try {
Base64Data bd = (Base64Data)c;
bd.writeTo(writer);
} catch (IOException e) {
throw new XMLStreamException(e);
}
} else {
writer.writeCharacters(c.toString());
}
break;
}
case STATE_COMMENT_AS_CHAR_ARRAY_SMALL: {
final int length = readStructure();
final int start = readContentCharactersBuffer(length);
final String comment = new String(_contentCharactersBuffer, start, length);
writer.writeComment(comment);
break;
}
case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM: {
final int length = readStructure16();
final int start = readContentCharactersBuffer(length);
final String comment = new String(_contentCharactersBuffer, start, length);
writer.writeComment(comment);
break;
}
case STATE_COMMENT_AS_CHAR_ARRAY_COPY: {
final char[] ch = readContentCharactersCopy();
writer.writeComment(new String(ch));
break;
}
case STATE_PROCESSING_INSTRUCTION:
writer.writeProcessingInstruction(readStructureString(), readStructureString());
break;
case STATE_END:
writer.writeEndElement();
depth --;
if(depth==0)
_treeCount--;
break;
default:
throw new XMLStreamException("Invalid State "+item);
}
} while(depth > 0 || _treeCount>0);
}
private boolean isInscope(int depth) {
return _buffer.getInscopeNamespaces().size() > 0 && depth ==1;
}
/*
* @param inscope: true means write inscope namespaces
*/
private void writeAttributes(XMLStreamWriter writer, boolean inscope) throws XMLStreamException {
// prefixSet to collect prefixes that are written before writing inscope namespaces
Set prefixSet = inscope ? new HashSet<>() : Collections.emptySet();
int item = peekStructure();
if ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE) {
// Skip the namespace declarations on the element
// they will have been added already
item = writeNamespaceAttributes(item, writer, inscope, prefixSet);
}
if (inscope) {
writeInscopeNamespaces(writer, prefixSet);
}
if ((item & TYPE_MASK) == T_ATTRIBUTE) {
writeAttributes(item, writer);
}
}
private static String fixNull(String s) {
if (s == null) return "";
else return s;
}
/*
* @param prefixSet: already written prefixes
*/
private void writeInscopeNamespaces(XMLStreamWriter writer, Set prefixSet) throws XMLStreamException {
for (Map.Entry e : _buffer.getInscopeNamespaces().entrySet()) {
String key = fixNull(e.getKey());
// If the prefix is already written, do not write the prefix
if (!prefixSet.contains(key)) {
writer.writeNamespace(key, e.getValue());
}
}
}
private int writeNamespaceAttributes(int item, XMLStreamWriter writer, boolean collectPrefixes, Set prefixSet) throws XMLStreamException {
do {
switch(getNIIState(item)){
case STATE_NAMESPACE_ATTRIBUTE:
// Undeclaration of default namespace
writer.writeDefaultNamespace("");
if (collectPrefixes) {
prefixSet.add("");
}
break;
case STATE_NAMESPACE_ATTRIBUTE_P:
// Undeclaration of namespace
// Declaration with prefix
String prefix = readStructureString();
writer.writeNamespace(prefix, "");
if (collectPrefixes) {
prefixSet.add(prefix);
}
break;
case STATE_NAMESPACE_ATTRIBUTE_P_U:
// Declaration with prefix
prefix = readStructureString();
writer.writeNamespace(prefix, readStructureString());
if (collectPrefixes) {
prefixSet.add(prefix);
}
break;
case STATE_NAMESPACE_ATTRIBUTE_U:
// Default declaration
writer.writeDefaultNamespace(readStructureString());
if (collectPrefixes) {
prefixSet.add("");
}
break;
}
readStructure();
item = peekStructure();
} while((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE);
return item;
}
private void writeAttributes(int item, XMLStreamWriter writer) throws XMLStreamException {
do {
switch(getAIIState(item)) {
case STATE_ATTRIBUTE_U_LN_QN: {
final String uri = readStructureString();
final String localName = readStructureString();
final String prefix = getPrefixFromQName(readStructureString());
writer.writeAttribute(prefix,uri,localName,readContentString());
break;
}
case STATE_ATTRIBUTE_P_U_LN:
writer.writeAttribute(readStructureString(), readStructureString(),
readStructureString(), readContentString());
break;
case STATE_ATTRIBUTE_U_LN:
writer.writeAttribute(readStructureString(), readStructureString(), readContentString());
break;
case STATE_ATTRIBUTE_LN:
writer.writeAttribute(readStructureString(), readContentString());
break;
}
// Ignore the attribute type
readStructureString();
readStructure();
item = peekStructure();
} while((item & TYPE_MASK) == T_ATTRIBUTE);
}
}