net.sf.saxon.evpull.ComplexContentProcessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of saxon-he Show documentation
Show all versions of saxon-he Show documentation
An OSGi bundle for Saxon-HE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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.Configuration;
import net.sf.saxon.om.NamespaceBinding;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.util.FastStringBuffer;
import net.sf.saxon.tree.util.Orphan;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.AtomicValue;
/**
* The ComplexContentProcessor is an EventIterator that deals with the events occurring between
* a startElement and endElement (or startDocument and endDocument) according to the XSLT/XQuery
* rules for constructing complex content. This includes:
*
*
* - Converting atomic values to text nodes (inserting space as a separator between adjacent nodes)
* - Replacing nested document nodes by their children
* - Merging adjacent text nodes and dropping zero-length text nodes
* - Detecting mispositioned or duplicated attribute and namespace nodes
*
*
*
* Note that if the content includes nodes such as element nodes, these will not be decomposed into
* a sequence of tree events, they will simply be returned as nodes.
*/
public class ComplexContentProcessor implements EventIterator {
private Configuration config;
private EventIterator base;
private PullEvent[] startEventStack; // contains either startElement or startDocument events
private int depth;
private NodeInfo pendingTextNode;
private boolean pendingTextNodeIsMutable;
private boolean prevAtomic = false;
private PullEvent pendingOutput = null;
/**
* Create the ComplexContentProcessor
* @param config the Saxon Configuration
* @param base the EventIterator that delivers the content of the element or document node
*/
public ComplexContentProcessor(Configuration config, EventIterator base) {
this.config = config;
this.base = EventStackIterator.flatten(base);
startEventStack = new PullEvent[20];
depth = 0;
}
/**
* Get the next event in the sequence. This will never be an EventIterator.
*
* @return the next event, or null when the sequence is exhausted
* @throws net.sf.saxon.trans.XPathException
* if a dynamic evaluation error occurs
*/
public PullEvent next() throws XPathException {
if (pendingOutput != null) {
PullEvent next = pendingOutput;
pendingOutput = null;
return next;
} else {
return advance();
}
}
/*@Nullable*/ private PullEvent advance() throws XPathException {
while (true) {
if (depth == 0) {
PullEvent e = base.next();
if (e instanceof StartElementEvent) {
push(e);
} else if (e instanceof StartDocumentEvent) {
push(e);
}
return e;
} else {
PullEvent e = base.next();
if (e instanceof StartElementEvent) {
prevAtomic = false;
push(e);
if (pendingTextNode != null) {
pendingOutput = e;
PullEvent next = pendingTextNode;
pendingTextNode = null;
return next;
} else {
return e;
}
} else if (e instanceof StartDocumentEvent) {
prevAtomic = false;
push(e);
if (pendingTextNode != null) {
pendingOutput = e;
PullEvent next = pendingTextNode;
pendingTextNode = null;
return next;
} else {
//continue;
}
} else if (e instanceof EndElementEvent) {
prevAtomic = false;
pop();
if (pendingTextNode != null) {
pendingOutput = e;
PullEvent next = pendingTextNode;
pendingTextNode = null;
return next;
} else {
return e;
}
} else if (e instanceof EndDocumentEvent) {
prevAtomic = false;
pop();
if (pendingTextNode != null) {
pendingOutput = e;
PullEvent next = pendingTextNode;
pendingTextNode = null;
return next;
} else {
return e;
}
} else if (e instanceof NodeInfo) {
prevAtomic = false;
switch (((NodeInfo)e).getNodeKind()) {
case Type.TEXT:
if (pendingTextNode == null) {
pendingTextNode = (NodeInfo)e;
pendingTextNodeIsMutable = false;
} else if (pendingTextNodeIsMutable) {
FastStringBuffer sb = (FastStringBuffer)((Orphan)pendingTextNode).getStringValueCS();
sb.append(((NodeInfo)e).getStringValueCS());
} else {
Orphan o = new Orphan(config);
o.setNodeKind(Type.TEXT);
FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
sb.append(pendingTextNode.getStringValueCS());
sb.append(((NodeInfo)e).getStringValueCS());
o.setStringValue(sb);
pendingTextNode = o;
pendingTextNodeIsMutable = true;
}
continue;
default:
if (pendingTextNode != null) {
pendingOutput = e;
PullEvent next = pendingTextNode;
pendingTextNode = null;
return next;
} else {
return e;
}
}
} else if (e instanceof AtomicValue) {
if (prevAtomic) {
FastStringBuffer sb = (FastStringBuffer)((Orphan)pendingTextNode).getStringValueCS();
sb.append(' ');
sb.append(((AtomicValue)e).getStringValueCS());
} else if (pendingTextNode != null) {
prevAtomic = true;
if (pendingTextNodeIsMutable) {
FastStringBuffer sb = (FastStringBuffer)((Orphan)pendingTextNode).getStringValueCS();
sb.append(((AtomicValue)e).getStringValueCS());
} else {
Orphan o = new Orphan(config);
o.setNodeKind(Type.TEXT);
FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
sb.append(pendingTextNode.getStringValueCS());
sb.append(((AtomicValue)e).getStringValueCS());
o.setStringValue(sb);
pendingTextNode = o;
pendingTextNodeIsMutable = true;
}
} else {
prevAtomic = true;
Orphan o = new Orphan(config);
o.setNodeKind(Type.TEXT);
FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
sb.append(((AtomicValue)e).getStringValueCS());
o.setStringValue(sb);
pendingTextNode = o;
pendingTextNodeIsMutable = true;
}
//continue;
} else {
throw new AssertionError("Unknown event");
}
}
}
}
/**
* Push a startElement or startDocument event onto the stack. At the same time, if it is a startElement
* event, remove any redundant namespace declarations
* @param p the startElement or startDocument event
*/
private void push(PullEvent p) {
if (depth >= startEventStack.length - 1) {
PullEvent[] b2 = new PullEvent[depth*2];
System.arraycopy(startEventStack, 0, b2, 0, startEventStack.length);
startEventStack = b2;
}
if (p instanceof StartElementEvent) {
int retained = 0;
NamespaceBinding[] nsp = ((StartElementEvent)p).getLocalNamespaces();
for (int nspi = 0; nspi < nsp.length; nspi++) {
if (nsp[nspi] == null) {
break;
}
retained++;
outer:
for (int i=depth-1; i>=0; i--) {
PullEvent q = startEventStack[i];
if (q instanceof StartElementEvent) {
NamespaceBinding[] nsq = ((StartElementEvent)q).getLocalNamespaces();
for (int nsqi = 0; nsqi < nsq.length; nsqi++) {
if (nsp[nspi] == nsq[nsqi]) {
nsp[nspi] = null;
retained--;
break outer;
} else if (nsp[nspi].getPrefix().equals(nsq[nsqi].getPrefix())) {
break outer;
}
}
}
}
}
if (retained < nsp.length) {
NamespaceBinding[] nsr = new NamespaceBinding[retained];
int nsri = 0;
for (int nspi=0; nspi