net.sf.saxon.evpull.EventToStaxBridge Maven / Gradle / Ivy
Show all versions of saxon-he Show documentation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 Saxonica Limited.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.evpull;
import net.sf.saxon.event.LocationProvider;
import net.sf.saxon.event.PipelineConfiguration;
import net.sf.saxon.lib.NamespaceConstant;
import net.sf.saxon.om.*;
import net.sf.saxon.pull.NamespaceContextImpl;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.util.FastStringBuffer;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.AtomicValue;
import net.sf.saxon.value.Whitespace;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Stack;
/**
* This class bridges EventIterator events to XMLStreamReader (Stax) events. That is, it acts
* as an XMLStreamReader, fetching the underlying data from an EventIterator.
*
* An EventIterator may provide access to any XDM sequence, whereas an XMLStreamReader always
* reads a document. The conversion of a sequence to a document follows the rules for
* "normalizing" a sequence in the Serialization specification: for example, atomic values are
* converted into text nodes, with adjacent atomic values being space-separated.
*/
public class EventToStaxBridge implements XMLStreamReader {
private EventIterator provider;
private StartElementEvent startElementEvent;
private Item currentItem;
private Stack stack; // holds instances of StartElementEvent; needed because namespace information
// (though not attributes) must be available at EndElement time
//private NamePool namePool;
private boolean previousAtomic;
private FastStringBuffer currentTextNode = new FastStringBuffer(FastStringBuffer.SMALL);
private int currentStaxEvent = XMLStreamConstants.START_DOCUMENT;
private XPathException pendingException = null;
/**
* Create a EventToStaxBridge instance, which wraps a Saxon EventIterator as a Stax XMLStreamReader
* @param provider the Saxon EventIterator from which the events will be read. This must return
* a fully decomposed event stream, that is, document and element nodes must be presented as separate
* events for the start, content, and end.
* @param pipe the PipelineConfiguration
*/
public EventToStaxBridge(EventIterator provider, PipelineConfiguration pipe) {
EventIterator flatIterator = EventStackIterator.flatten(provider);
if (flatIterator instanceof LocationProvider) {
pipe.setLocationProvider((LocationProvider)flatIterator);
}
this.provider = new NamespaceMaintainer(flatIterator);
this.stack = new Stack();
}
public int getAttributeCount() {
if (currentStaxEvent != START_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
return startElementEvent.getAttributeCount();
}
public boolean isAttributeSpecified(int i) {
if (currentStaxEvent != START_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
return true;
}
public QName getAttributeName(int i) {
if (currentStaxEvent != START_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
NodeInfo att = startElementEvent.getAttribute(i);
return new QName(att.getURI(), att.getLocalPart(), att.getPrefix());
}
public String getAttributeLocalName(int i) {
if (currentStaxEvent != START_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
return startElementEvent.getAttribute(i).getLocalPart();
}
public String getAttributeNamespace(int i) {
if (currentStaxEvent != START_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
return startElementEvent.getAttribute(i).getURI();
}
public String getAttributePrefix(int i) {
if (currentStaxEvent != START_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
return startElementEvent.getAttribute(i).getPrefix();
}
public String getAttributeType(int i) {
if (currentStaxEvent != START_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
int type = startElementEvent.getAttribute(i).getSchemaType().getFingerprint();
if (type == StandardNames.XS_ID) {
return "ID";
} else if (type == StandardNames.XS_IDREF) {
return "IDREF";
} else if (type == StandardNames.XS_IDREFS) {
return "IDREFS";
} else if (type == StandardNames.XS_NMTOKEN) {
return "NMTOKEN";
} else if (type == StandardNames.XS_NMTOKENS) {
return "NMTOKENS";
} else if (type == StandardNames.XS_ENTITY) {
return "ENTITY";
} else if (type == StandardNames.XS_ENTITIES) {
return "ENTITIES";
}
return "CDATA";
}
public String getAttributeValue(int i) {
if (currentStaxEvent != START_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
return startElementEvent.getAttribute(i).getStringValue();
}
/*@Nullable*/ public String getAttributeValue(String uri, String local) {
for (Iterator iter = startElementEvent.iterateAttributes(); iter.hasNext(); ) {
NodeInfo att = (NodeInfo)iter.next();
if (att.getURI().equals(uri) && att.getLocalPart().equals(local)) {
return att.getStringValue();
}
}
return null;
}
public int getEventType() {
return currentStaxEvent;
}
public int getNamespaceCount() {
if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
NamespaceBinding[] nscodes = startElementEvent.getLocalNamespaces();
for (int i=0; i target.length) {
throw new IndexOutOfBoundsException("targetStart");
}
if (length < 0 || targetStart + length > target.length) {
throw new IndexOutOfBoundsException("length");
}
String value = getText();
if (sourceStart >= value.length()) {
return 0;
}
int sourceEnd = sourceStart + length;
if (sourceEnd > value.length()) {
sourceEnd = value.length();
}
value.getChars(sourceStart, sourceEnd, target, targetStart);
return sourceEnd - sourceStart;
}
// Diagnostic version of next() method
// public int next() throws XMLStreamException {
// System.err.println("calling next: ");
// try {
// int x = nextx();
// System.err.println("next: " + x);
// return x;
// } catch (XMLStreamException e) {
// System.err.println("end of stream ");
// throw e;
// } catch (IllegalStateException e) {
// System.err.println("illegal end of stream ");
// throw e;
// }
// }
public int next() throws XMLStreamException {
if (pendingException != null) {
throw new XMLStreamException(pendingException);
}
PullEvent p;
try {
p = provider.next();
} catch (XPathException e) {
throw new XMLStreamException(e);
}
if (p == null) {
// The spec is ambivalent here; it also says IllegalStateException is appropriate
throw new NoSuchElementException("end of stream");
}
startElementEvent = null;
if (p instanceof StartDocumentEvent) {
// STAX doesn't actually report START_DOCUMENT: it's the initial state before reading any events
currentStaxEvent = XMLStreamConstants.START_DOCUMENT;
return next();
} else if (p instanceof StartElementEvent) {
startElementEvent = (StartElementEvent)p;
currentStaxEvent = XMLStreamConstants.START_ELEMENT;
stack.push(p);
return currentStaxEvent;
} else if (p instanceof EndElementEvent) {
currentStaxEvent = XMLStreamConstants.END_ELEMENT;
startElementEvent = (StartElementEvent)stack.pop();
return currentStaxEvent;
} else if (p instanceof EndDocumentEvent) {
currentStaxEvent = XMLStreamConstants.END_DOCUMENT;
return currentStaxEvent;
} else if (p instanceof NodeInfo) {
currentItem = (NodeInfo)p;
switch (((NodeInfo)p).getNodeKind()) {
case Type.COMMENT:
currentStaxEvent = XMLStreamConstants.COMMENT;
return currentStaxEvent;
case Type.PROCESSING_INSTRUCTION:
currentStaxEvent = XMLStreamConstants.PROCESSING_INSTRUCTION;
return currentStaxEvent;
case Type.TEXT:
currentStaxEvent = XMLStreamConstants.CHARACTERS;
return currentStaxEvent;
case Type.ATTRIBUTE:
throw new XMLStreamException("Encountered top-level attribute in sequence");
default:
throw new AssertionError("Unexpected node kind (sequence not decomposed?)");
}
} else if (p instanceof AtomicValue) {
currentItem = (AtomicValue)p;
currentStaxEvent = XMLStreamConstants.CHARACTERS;
previousAtomic = true;
return currentStaxEvent;
} else if (p instanceof EventIterator) {
throw new AssertionError("EventToStaxBridge requires a flattened event sequence");
} else {
throw new AssertionError("Unhandled pull event: " + p.getClass().getName());
}
}
public int nextTag() throws XMLStreamException {
if (pendingException != null) {
throw new XMLStreamException(pendingException);
}
int eventType = next();
while ((eventType == XMLStreamConstants.CHARACTERS && isWhiteSpace()) // skip whitespace
|| (eventType == XMLStreamConstants.CDATA && isWhiteSpace())
// skip whitespace
|| eventType == XMLStreamConstants.SPACE
|| eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
|| eventType == XMLStreamConstants.COMMENT
) {
eventType = next();
}
if (eventType != XMLStreamConstants.START_ELEMENT && eventType != XMLStreamConstants.END_ELEMENT) {
throw new XMLStreamException("expected start or end tag", getLocation());
}
return eventType;
}
public void close() throws XMLStreamException {
//System.err.println("close");
if (pendingException != null) {
throw new XMLStreamException(pendingException);
}
}
public boolean hasName() {
return currentStaxEvent == XMLStreamConstants.START_ELEMENT ||
currentStaxEvent == XMLStreamConstants.END_ELEMENT;
}
public boolean hasNext() throws XMLStreamException {
//System.err.println("hasNext()? " + (currentStaxEvent != XMLStreamConstants.END_DOCUMENT));
if (pendingException != null) {
throw new XMLStreamException(pendingException);
}
return currentStaxEvent != XMLStreamConstants.END_DOCUMENT;
}
public boolean hasText() {
return currentStaxEvent == XMLStreamConstants.CHARACTERS || currentStaxEvent == XMLStreamConstants.COMMENT;
}
public boolean isCharacters() {
return currentStaxEvent == XMLStreamConstants.CHARACTERS;
}
public boolean isEndElement() {
return currentStaxEvent == XMLStreamConstants.END_ELEMENT;
}
public boolean isStandalone() {
return false;
}
public boolean isStartElement() {
return currentStaxEvent == XMLStreamConstants.START_ELEMENT;
}
public boolean isWhiteSpace() {
return currentStaxEvent == XMLStreamConstants.CHARACTERS && Whitespace.isWhite(getText());
}
public boolean standaloneSet() {
return false;
}
public String getCharacterEncodingScheme() {
return null;
}
public String getElementText() throws XMLStreamException {
if (pendingException != null) {
throw new XMLStreamException(pendingException);
}
if (getEventType() != XMLStreamConstants.START_ELEMENT) {
throw new XMLStreamException("parser must be on START_ELEMENT to read next text", getLocation());
}
int eventType = next();
StringBuffer content = new StringBuffer();
while (eventType != XMLStreamConstants.END_ELEMENT) {
if (eventType == XMLStreamConstants.CHARACTERS
|| eventType == XMLStreamConstants.CDATA
|| eventType == XMLStreamConstants.SPACE
|| eventType == XMLStreamConstants.ENTITY_REFERENCE) {
content.append(getText());
} else if (eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
|| eventType == XMLStreamConstants.COMMENT) {
// skipping
} else if (eventType == XMLStreamConstants.END_DOCUMENT) {
throw new XMLStreamException("unexpected end of document when reading element text content", getLocation());
} else if (eventType == XMLStreamConstants.START_ELEMENT) {
throw new XMLStreamException("element text content may not contain START_ELEMENT", getLocation());
} else {
throw new XMLStreamException("Unexpected event type " + eventType, getLocation());
}
eventType = next();
}
return content.toString();
}
public String getEncoding() {
return null;
}
public String getLocalName() {
if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
return startElementEvent.getElementName().getLocalPart();
}
public String getNamespaceURI() {
if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
return null;
}
return startElementEvent.getElementName().getURI();
}
public String getPIData() {
if (currentStaxEvent != XMLStreamConstants.PROCESSING_INSTRUCTION) {
throw new IllegalStateException("Not positioned at a processing instruction");
}
return currentItem.getStringValue();
}
public String getPITarget() {
if (currentStaxEvent != XMLStreamConstants.PROCESSING_INSTRUCTION) {
throw new IllegalStateException("Not positioned at a processing instruction");
}
return ((NodeInfo)currentItem).getLocalPart();
}
public String getPrefix() {
if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
return null;
}
return startElementEvent.getElementName().getPrefix();
}
public String getVersion() {
return "1.0";
}
public String getNamespacePrefix(int i) {
if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
NamespaceBinding nscode = startElementEvent.getLocalNamespaces()[i];
return nscode.getPrefix();
}
public String getNamespaceURI(int i) {
if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
NamespaceBinding nscode = startElementEvent.getLocalNamespaces()[i];
return nscode.getURI();
}
public NamespaceContext getNamespaceContext() {
return new NamespaceContextImpl((NamespaceResolver)provider);
}
public QName getName() {
if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
throw new IllegalStateException(""+currentStaxEvent);
}
NodeName name = startElementEvent.getElementName();
return new QName(name.getURI(), name.getLocalPart(), name.getPrefix());
}
public Location getLocation() {
if (currentItem instanceof NodeInfo) {
final NodeInfo node = (NodeInfo)currentItem;
return new Location() {
public int getCharacterOffset() {
return -1;
}
public int getColumnNumber() {
return node.getColumnNumber();
}
public int getLineNumber() {
return node.getLineNumber();
}
public String getPublicId() {
return null;
}
public String getSystemId() {
return node.getSystemId();
}
};
} else if (startElementEvent != null) {
PipelineConfiguration pipe = startElementEvent.getPipelineConfiguration();
final LocationProvider provider = pipe.getLocationProvider();
final int locationId = startElementEvent.getLocationId();
if (provider != null) {
return new Location() {
public int getCharacterOffset() {
return -1;
}
public int getColumnNumber() {
return provider.getColumnNumber(locationId);
}
public int getLineNumber() {
return provider.getLineNumber(locationId);
}
public String getPublicId() {
return null;
}
public String getSystemId() {
return provider.getSystemId(locationId);
}
};
}
}
return DummyLocation.THE_INSTANCE;
}
public Object getProperty(String s) throws IllegalArgumentException {
return null;
}
public void require(int event, String uri, String local) throws XMLStreamException {
if (pendingException != null) {
throw new XMLStreamException(pendingException);
}
if (currentStaxEvent != event) {
throw new XMLStreamException("Required event type is " + event + ", actual event is " + currentStaxEvent);
}
if (uri != null && !uri.equals(getNamespaceURI())) {
throw new XMLStreamException("Required namespace is " + uri + ", actual is " + getNamespaceURI());
}
if (local != null && !local.equals(getLocalName())) {
throw new XMLStreamException("Required local name is " + local + ", actual is " + getLocalName());
}
}
public String getNamespaceURI(String prefix) {
if (prefix.equals("xmlns")) {
return NamespaceConstant.XMLNS;
}
return ((NamespaceResolver)provider).getURIForPrefix(prefix, true);
}
/**
* Get the underlying event stream
*/
public EventIterator getProvider() {
return provider;
}
private static class DummyLocation implements Location{
public static final Location THE_INSTANCE = new DummyLocation();
private DummyLocation() {}
public int getCharacterOffset() {
return -1;
}
public int getColumnNumber() {
return -1;
}
public int getLineNumber() {
return -1;
}
public java.lang.String getPublicId() {
return null;
}
public java.lang.String getSystemId() {
return null;
}
}
/**
* Temporary test program
* @param args command line arguments. First argument is a file containing an XML document
* @throws Exception
*/
// public static void main(String[] args) throws Exception {
// Configuration config = new Configuration();
// DocumentInfo doc = config.buildDocument(new StreamSource(new File(args[0])));
// PipelineConfiguration pipe = config.makePipelineConfiguration();
// pipe.setHostLanguage(Configuration.XQUERY);
// EventIterator ei = new Decomposer(doc, pipe);
// XMLStreamReader sr = new EventToStaxBridge(ei, config.getNamePool());
// StaxBridge bridge = new StaxBridge();
// bridge.setXMLStreamReader(sr);
//
// bridge.setPipelineConfiguration(pipe);
// Receiver out = config.getSerializerFactory().getReceiver(new StreamResult(System.out), pipe, new Properties());
// PullPushCopier copier = new PullPushCopier(bridge, out);
// copier.copy();
// }
}