net.sf.saxon.pull.PullToStax Maven / Gradle / Ivy
Show all versions of saxon Show documentation
package net.sf.saxon.pull;
import net.sf.saxon.expr.ExpressionLocation;
import net.sf.saxon.om.AttributeCollection;
import net.sf.saxon.om.FastStringBuffer;
import net.sf.saxon.om.NamePool;
import net.sf.saxon.trans.XPathException;
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 javax.xml.transform.SourceLocator;
/**
* This class bridges PullProvider events to XMLStreamReader (Stax) events. That is, it acts
* as an XMLStreamReader, fetching the underlying data from a PullProvider.
*
* A PullProvider 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 PullToStax implements XMLStreamReader {
private PullNamespaceReducer provider;
private NamePool namePool;
private boolean previousAtomic;
private FastStringBuffer currentTextNode = new FastStringBuffer(100);
private int currentStaxEvent = XMLStreamReader.START_DOCUMENT;
private XPathException pendingException = null;
/**
* Create a PullToStax instance, which wraps a Saxon PullProvider as a Stax XMLStreamReader
* @param provider the Saxon PullProvider from which the events will be read
*/
public PullToStax(PullProvider provider) {
if (provider instanceof PullNamespaceReducer) {
this.provider = (PullNamespaceReducer)provider;
} else {
this.provider = new PullNamespaceReducer(provider);
}
namePool = provider.getPipelineConfiguration().getConfiguration().getNamePool();
}
public int getAttributeCount() {
try {
return provider.getAttributes().getLength();
} catch (XPathException e) {
pendingException = e;
return 0;
}
}
public boolean isAttributeSpecified(int i) {
return true;
}
public QName getAttributeName(int i) {
try {
AttributeCollection atts = provider.getAttributes();
return new QName(atts.getURI(i), atts.getLocalName(i), atts.getPrefix(i));
} catch (XPathException e) {
pendingException = e;
return new QName("http://saxon.sf.net/error", "error", "");
}
}
public String getAttributeLocalName(int i) {
try {
return provider.getAttributes().getLocalName(i);
} catch (XPathException e) {
pendingException = e;
return "error";
}
}
public String getAttributeNamespace(int i) {
try {
return provider.getAttributes().getURI(i);
} catch (XPathException e) {
pendingException = e;
return "http://saxon.sf.net/error";
}
}
public String getAttributePrefix(int i) {
try {
return provider.getAttributes().getPrefix(i);
} catch (XPathException e) {
pendingException = e;
return "";
}
}
public String getAttributeType(int i) {
try {
AttributeCollection ac = provider.getAttributes();
if (ac.isId(i)) {
return "ID";
} else if (ac.isIdref(i)) {
return "IDREFS";
} else {
return "CDATA";
}
} catch (XPathException e) {
pendingException = e;
return "CDATA";
}
}
public String getAttributeValue(int i) {
try {
return provider.getAttributes().getValue(i);
} catch (XPathException e) {
pendingException = e;
return "error";
}
}
public String getAttributeValue(String uri, String local) {
try {
return provider.getAttributes().getValue(uri, local);
} catch (XPathException e) {
pendingException = e;
return "";
}
}
public int getEventType() {
return currentStaxEvent;
}
public int getNamespaceCount() {
try {
return provider.getNamespaceDeclarations().getNumberOfNamespaces();
} catch (XPathException e) {
pendingException = e;
return 0;
}
}
public String getText() {
if (previousAtomic) {
return currentTextNode.toString();
} else {
try {
return provider.getStringValue().toString();
} catch (XPathException e) {
pendingException = e;
return "";
}
}
}
public int getTextLength() {
if (previousAtomic) {
return currentTextNode.length();
} else {
try {
return provider.getStringValue().length();
} catch (XPathException e) {
pendingException = e;
return 0;
}
}
}
public int getTextStart() {
return 0;
}
public char[] getTextCharacters() {
if (previousAtomic) {
return currentTextNode.getCharArray();
} else {
try {
String stringValue = provider.getStringValue().toString();
char[] chars = new char[stringValue.length()];
stringValue.getChars(0, chars.length, chars, 0);
return chars;
} catch (XPathException e) {
pendingException = e;
return new char[0];
}
}
}
public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException {
if (pendingException != null) {
throw new XMLStreamException(pendingException);
}
if (previousAtomic) {
int end = sourceStart + length;
if (end > currentTextNode.length()) {
end = currentTextNode.length();
}
currentTextNode.getChars(sourceStart, end, target, targetStart);
return end - sourceStart;
} else {
try {
String stringValue = provider.getStringValue().subSequence(sourceStart, sourceStart + length).toString();
stringValue.getChars(0, length, target, targetStart);
return length;
} catch (XPathException e) {
pendingException = e;
return 0;
}
}
}
public int next() throws XMLStreamException {
if (pendingException != null) {
throw new XMLStreamException(pendingException);
}
int p;
try {
p = provider.next();
} catch (XPathException e) {
throw new XMLStreamException(e);
}
switch (p) {
case PullProvider.START_OF_INPUT:
return next();
case PullProvider.ATOMIC_VALUE:
currentTextNode.setLength(0);
if (previousAtomic) {
currentTextNode.append(' ');
}
currentTextNode.append(provider.getAtomicValue().getStringValue());
currentStaxEvent = XMLStreamConstants.CHARACTERS;
break;
case PullProvider.START_DOCUMENT:
currentStaxEvent = XMLStreamConstants.START_DOCUMENT;
return next();
case PullProvider.END_DOCUMENT:
currentStaxEvent = XMLStreamConstants.END_DOCUMENT;
break;
case PullProvider.START_ELEMENT:
currentStaxEvent = XMLStreamConstants.START_ELEMENT;
break;
case PullProvider.END_ELEMENT:
currentStaxEvent = XMLStreamConstants.END_ELEMENT;
break;
case PullProvider.TEXT:
currentStaxEvent = XMLStreamConstants.CHARACTERS;
break;
case PullProvider.COMMENT:
currentStaxEvent = XMLStreamConstants.COMMENT;
break;
case PullProvider.PROCESSING_INSTRUCTION:
currentStaxEvent = XMLStreamConstants.PROCESSING_INSTRUCTION;
break;
case PullProvider.END_OF_INPUT:
currentStaxEvent = XMLStreamConstants.END_DOCUMENT;
break;
case PullProvider.ATTRIBUTE:
throw new XMLStreamException("Free-standing attributes cannot be serialized");
case PullProvider.NAMESPACE:
throw new XMLStreamException("Free-standing namespace nodes cannot be serialized");
default:
throw new IllegalStateException("Unknown Stax event " + p);
}
previousAtomic = (p == PullProvider.ATOMIC_VALUE);
return currentStaxEvent;
}
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 {
if (pendingException != null) {
throw new XMLStreamException(pendingException);
}
provider.close();
}
public boolean hasName() {
return currentStaxEvent == XMLStreamConstants.START_ELEMENT ||
currentStaxEvent == XMLStreamConstants.END_ELEMENT;
}
public boolean hasNext() throws XMLStreamException {
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() {
try {
return currentStaxEvent == XMLStreamConstants.CHARACTERS && Whitespace.isWhite(provider.getStringValue());
} catch (XPathException e) {
pendingException = e;
return false;
}
}
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() {
return namePool.getLocalName(provider.getNameCode());
}
public String getNamespaceURI() {
return namePool.getURI(provider.getNameCode());
}
public String getPIData() {
if (currentStaxEvent != XMLStreamConstants.PROCESSING_INSTRUCTION) {
throw new IllegalStateException("Not positioned at a processing instruction");
}
try {
return provider.getStringValue().toString();
} catch (XPathException e) {
pendingException = e;
return "";
}
}
public String getPITarget() {
if (currentStaxEvent != XMLStreamConstants.PROCESSING_INSTRUCTION) {
throw new IllegalStateException("Not positioned at a processing instruction");
}
return namePool.getLocalName(provider.getNameCode());
}
public String getPrefix() {
return namePool.getPrefix(provider.getNameCode());
}
public String getVersion() {
return "1.0";
}
public String getNamespacePrefix(int i) {
try {
return provider.getNamespaceDeclarations().getPrefix(i);
} catch (XPathException e) {
pendingException = e;
return "";
}
}
public String getNamespaceURI(int i) {
try {
return provider.getNamespaceDeclarations().getURI(i);
} catch (XPathException e) {
pendingException = e;
return "";
}
}
public NamespaceContext getNamespaceContext() {
return new NamespaceContextImpl(provider);
}
public QName getName() {
int nc = provider.getNameCode();
return new QName(namePool.getURI(nc), namePool.getLocalName(nc), namePool.getPrefix(nc));
}
public Location getLocation() {
SourceLocator sourceLocator = provider.getSourceLocator();
if (sourceLocator == null) {
sourceLocator = new ExpressionLocation();
}
return new SourceStreamLocation(sourceLocator);
}
public Object getProperty(String s) throws IllegalArgumentException {
return null; //TODO: implement this method
}
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 s) {
return provider.getURIForPrefix(s, true);
}
/**
* Bridge a SAX SourceLocator to a javax.xml.stream.Location
*/
public class SourceStreamLocation implements javax.xml.stream.Location {
private SourceLocator locator;
/**
* Create a StAX SourceStreamLocation object based on a given SAX SourceLocator
* @param locator the SAX SourceLocator
*/
public SourceStreamLocation(SourceLocator locator) {
this.locator = locator;
}
public int getCharacterOffset() {
return -1;
}
public int getColumnNumber() {
return locator.getColumnNumber();
}
public int getLineNumber() {
return locator.getLineNumber();
}
public String getPublicId() {
return locator.getPublicId();
}
public String getSystemId() {
return locator.getSystemId();
}
}
}
//
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the
// License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is: all this file
//
// The Initial Developer of the Original Code is Michael H. Kay.
//
// Contributor(s):
//