
net.sf.saxon.tree.tiny.TinyBuilder 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.tree.tiny;
import net.sf.saxon.event.*;
import net.sf.saxon.om.NamespaceBinding;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.NodeName;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.util.FastStringBuffer;
import net.sf.saxon.type.SchemaType;
import net.sf.saxon.type.SimpleType;
import net.sf.saxon.type.Type;
/**
* The TinyBuilder class is responsible for taking a stream of SAX events and constructing
* a Document tree, using the "TinyTree" implementation.
*
* @author Michael H. Kay
*/
public class TinyBuilder extends Builder {
public static final int PARENT_POINTER_INTERVAL = 10;
// a lower value allocates more parent pointers which takes more space but reduces
// the length of parent searches
/*@Nullable*/ private TinyTree tree;
private int currentDepth = 0;
private int nodeNr = 0; // this is the local sequence within this document
private boolean ended = false;
/*@Nullable*/ private int[] sizeParams; // estimate of number of nodes, attributes, namespaces, characters
/**
* Create a TinyTree builder
* @param pipe information about the pipeline leading up to this Builder
*/
public TinyBuilder(/*@NotNull*/ PipelineConfiguration pipe) {
super(pipe);
//System.err.println("TinyBuilder " + this);
}
/**
* Set the size parameters for the tree
* @param params an array of four integers giving the expected number of non-attribute nodes, the expected
* number of attributes, the expected number of namespace declarations, and the expected total length of
* character data
*/
public void setSizeParameters(/*@Nullable*/ int[] params) {
sizeParams = params;
}
/**
* Get the size parameters for the tree
* @return an array of four integers giving the actual number of non-attribute nodes, the actual
* number of attributes, the actual number of namespace declarations, and the actual total length of
* character data. Return null if and only if the current tree is null.
*/
/*@Nullable*/ public int[] getSizeParameters() {
if (tree != null) {
return new int[] {tree.numberOfNodes, tree.numberOfAttributes, tree.numberOfNamespaces,
tree.getCharacterBuffer().length()};
} else {
return null;
}
}
/*@NotNull*/ private int[] prevAtDepth = new int[100];
// this array is scaffolding used while constructing the tree, it is
// not present in the final tree. For each level of the tree, it records the
// node number of the most recent node at that level.
/*@NotNull*/ private int[] siblingsAtDepth = new int[100];
// more scaffolding. For each level of the tree, this array records the
// number of siblings processed at that level. When this exceeds a threshold value,
// a dummy node is inserted into the arrays to contain a parent pointer: this it to
// prevent excessively long searches for a parent node, which is normally found by
// scanning the siblings. The value is then reset to zero.
private boolean isIDElement = false;
/**
* Get the tree being built by this builder
* @return the TinyTree
*/
/*@Nullable*/ public TinyTree getTree() {
return tree;
}
/**
* Get the current depth in the tree
* @return the current depth
*/
public int getCurrentDepth() {
return currentDepth;
}
/**
* Open the event stream
*/
public void open() {
//System.err.println("TinyBuilder " + this + " open; " + started);
if (started) {
// this happens when using an IdentityTransformer
return;
}
if (tree == null) {
if (sizeParams ==null) {
tree = new TinyTree(config);
} else {
tree = new TinyTree(config,
sizeParams[0], sizeParams[1], sizeParams[2], sizeParams[3]);
}
currentDepth = 0;
if (lineNumbering) {
tree.setLineNumbering();
}
}
super.open();
}
/**
* Write a document node to the tree
*/
public void startDocument (int properties) throws XPathException {
// if (currentDepth == 0 && tree.numberOfNodes != 0) {
// System.err.println("**** FOREST DOCUMENT **** " + tree.numberOfNodes);
// }
// System.err.println("New doc: free memory: " + Runtime.getRuntime().freeMemory());
// if (sizeParams != null) {
// System.err.println("Size params: " + sizeParams[0] + ", " + sizeParams[1] + ", " + sizeParams[2] + ", " + sizeParams[3]);
// }
if ((started && !ended) || currentDepth > 0) {
// this happens when using an IdentityTransformer, or when copying a document node to form
// the content of an element
return;
}
started = true;
ended = false;
TinyTree tt = tree;
assert tt != null;
currentRoot = new TinyDocumentImpl(tt);
TinyDocumentImpl doc = (TinyDocumentImpl)currentRoot;
doc.setSystemId(getSystemId());
doc.setBaseURI(getBaseURI());
currentDepth = 0;
int nodeNr = tt.addDocumentNode((TinyDocumentImpl)currentRoot);
prevAtDepth[0] = nodeNr;
prevAtDepth[1] = -1;
siblingsAtDepth[0] = 0;
siblingsAtDepth[1] = 0;
tt.next[nodeNr] = -1;
currentDepth++;
}
/**
* Callback interface for SAX: not for application use
*/
public void endDocument () throws XPathException {
// System.err.println("TinyBuilder: " + this + " End document");
// Add a stopper node to ensure no-one walks off the end of the array; but
// decrement numberOfNodes so the next node will overwrite it
tree.addNode(Type.STOPPER, 0, 0, 0, -1);
tree.numberOfNodes--;
if (currentDepth > 1) return;
// happens when copying a document node as the child of an element
if (ended) return; // happens when using an IdentityTransformer
ended = true;
prevAtDepth[currentDepth] = -1;
currentDepth--;
}
public void reset() {
super.reset();
tree = null;
currentDepth = 0;
nodeNr = 0;
ended = false;
sizeParams = null;
}
public void close() throws XPathException {
TinyTree tt = tree;
if (tt != null) {
tt.addNode(Type.STOPPER, 0, 0, 0, -1);
tt.condense();
}
super.close();
}
/**
* Notify the start tag of an element
*/
public void startElement (/*@NotNull*/ NodeName elemName, SchemaType type, int locationId, int properties) throws XPathException
{
// if (currentDepth == 0 && tree.numberOfNodes != 0) {
// System.err.println("**** FOREST ELEMENT **** trees=" + tree.rootIndexUsed );
// }
// if the number of siblings exceeds a certain threshold, add a parent pointer, in the form
// of a pseudo-node
TinyTree tt = tree;
assert tt != null;
if (siblingsAtDepth[currentDepth] > PARENT_POINTER_INTERVAL) {
nodeNr = tt.addNode(Type.PARENT_POINTER, currentDepth, prevAtDepth[currentDepth-1], 0, 0);
int prev = prevAtDepth[currentDepth];
if (prev > 0) {
tt.next[prev] = nodeNr;
}
tt.next[nodeNr] = prevAtDepth[currentDepth-1];
prevAtDepth[currentDepth] = nodeNr;
siblingsAtDepth[currentDepth] = 0;
}
// now add the element node itself
int nameCode = elemName.allocateNameCode(namePool);
nodeNr = tt.addNode(Type.ELEMENT, currentDepth, -1, -1, nameCode);
isIDElement = ((properties & ReceiverOptions.IS_ID) != 0);
int typeCode = type.getFingerprint();
if (typeCode != StandardNames.XS_UNTYPED) {
if ((properties & ReceiverOptions.NILLED_ELEMENT) != 0) {
typeCode |= NodeInfo.IS_NILLED;
}
tt.setElementAnnotation(nodeNr, typeCode);
if (!isIDElement && config.getTypeHierarchy().isIdCode(typeCode)) {
isIDElement = true;
}
}
if (currentDepth == 0) {
prevAtDepth[0] = nodeNr;
prevAtDepth[1] = -1;
//tree.next[0] = -1;
currentRoot = tt.getNode(nodeNr);
} else {
int prev = prevAtDepth[currentDepth];
if (prev > 0) {
tt.next[prev] = nodeNr;
}
tt.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
prevAtDepth[currentDepth] = nodeNr;
siblingsAtDepth[currentDepth]++;
}
currentDepth++;
if (currentDepth == prevAtDepth.length) {
int[] p2 = new int[currentDepth*2];
System.arraycopy(prevAtDepth, 0, p2, 0, currentDepth);
prevAtDepth = p2;
p2 = new int[currentDepth*2];
System.arraycopy(siblingsAtDepth, 0, p2, 0, currentDepth);
siblingsAtDepth = p2;
}
prevAtDepth[currentDepth] = -1;
siblingsAtDepth[currentDepth] = 0;
LocationProvider locator = pipe.getLocationProvider();
if (locator instanceof SourceLocationProvider) {
tt.setSystemId(nodeNr, locator.getSystemId(locationId));
} else if (currentDepth == 1) {
tt.setSystemId(nodeNr, systemId);
}
if (lineNumbering) {
tt.setLineNumber(nodeNr, locator.getLineNumber(locationId), locator.getColumnNumber(locationId));
}
}
public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
assert tree != null;
tree.addNamespace(nodeNr, namespaceBinding);
}
public void attribute(/*@NotNull*/ NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties)
throws XPathException {
// System.err.println("attribute " + nameCode + "=" + value);
int nameCode = attName.allocateNameCode(namePool);
assert tree != null;
assert currentRoot != null;
tree.addAttribute(currentRoot, nodeNr, nameCode, typeCode.getFingerprint(), value, properties);
}
public void startContent() {
nodeNr++;
}
/**
* Callback interface for SAX: not for application use
*/
public void endElement() throws XPathException {
TinyTree tt = tree;
assert tt != null;
// System.err.println("End element");
prevAtDepth[currentDepth] = -1;
siblingsAtDepth[currentDepth] = 0;
currentDepth--;
if (isIDElement) {
// we're relying on the fact that an ID element has no element children!
tt.indexIDElement(currentRoot, prevAtDepth[currentDepth], config.getNameChecker());
isIDElement = false;
}
}
/**
* Get the last completed element node. This is used during checking of schema assertions,
* which happens while the tree is still under construction. This method is called immediately after
* a call on endElement(), and it returns the element that has just ended.
* @return the last completed element node, that is, the element whose endElement event is the most recent
* endElement event to be reported, or null if there is no such element
*/
public NodeInfo getLastCompletedElement() {
if (tree == null) {
return null;
}
return tree.getNode((currentDepth>=0 ? prevAtDepth[currentDepth] : 0));
// Note: reading an incomplete tree needs care if it constructs a prior index, etc.
}
/**
* Callback interface for SAX: not for application use
*/
public void characters(/*@NotNull*/ CharSequence chars, int locationId, int properties) throws XPathException
{
if (chars instanceof CompressedWhitespace &&
(properties & ReceiverOptions.WHOLE_TEXT_NODE) != 0) {
TinyTree tt = tree;
assert tt != null;
long lvalue = ((CompressedWhitespace)chars).getCompressedValue();
nodeNr = tt.addNode(Type.WHITESPACE_TEXT, currentDepth, (int)(lvalue>>32), (int)(lvalue), -1);
int prev = prevAtDepth[currentDepth];
if (prev > 0) {
tt.next[prev] = nodeNr;
}
tt.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
prevAtDepth[currentDepth] = nodeNr;
siblingsAtDepth[currentDepth]++;
return;
}
final int len = chars.length();
if (len>0) {
nodeNr = makeTextNode(chars, len);
}
}
/**
* Create a text node. Separate method so it can be overridden. If the current node
* on the tree is already a text node, the new text will be appended to it.
* @param chars the contents of the text node
* @param len the length of the text node
* @return the node number of the created text node, or the text node to which
* this text has been appended.
*/
protected int makeTextNode(CharSequence chars, int len) {
TinyTree tt = tree;
assert tt != null;
int bufferStart = tt.getCharacterBuffer().length();
tt.appendChars(chars);
int n=tt.numberOfNodes-1;
if (tt.nodeKind[n] == Type.TEXT && tt.depth[n] == currentDepth) {
// merge this text node with the previous text node
tt.beta[n] += len;
} else {
nodeNr = tt.addNode(Type.TEXT, currentDepth, bufferStart, len, -1);
int prev = prevAtDepth[currentDepth];
if (prev > 0) {
tt.next[prev] = nodeNr;
}
tt.next[nodeNr] = prevAtDepth[currentDepth - 1];
prevAtDepth[currentDepth] = nodeNr;
siblingsAtDepth[currentDepth]++;
}
return nodeNr;
}
/**
* Callback interface for SAX: not for application use
*/
public void processingInstruction (String piname, /*@NotNull*/ CharSequence remainder, int locationId, int properties) throws XPathException
{
TinyTree tt = tree;
assert tt != null;
if (tt.commentBuffer==null) {
tt.commentBuffer = new FastStringBuffer(FastStringBuffer.MEDIUM);
}
int s = tt.commentBuffer.length();
tt.commentBuffer.append(remainder.toString());
int nameCode = namePool.allocate("", "", piname);
nodeNr = tt.addNode(Type.PROCESSING_INSTRUCTION, currentDepth, s, remainder.length(),
nameCode);
int prev = prevAtDepth[currentDepth];
if (prev > 0) {
tt.next[prev] = nodeNr;
}
tt.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
prevAtDepth[currentDepth] = nodeNr;
siblingsAtDepth[currentDepth]++;
LocationProvider locator = pipe.getLocationProvider();
if (locator instanceof SourceLocationProvider) {
tt.setSystemId(nodeNr, locator.getSystemId(locationId));
if (lineNumbering) {
tt.setLineNumber(nodeNr, locator.getLineNumber(locationId), locator.getColumnNumber(locationId));
}
}
}
/**
* Callback interface for SAX: not for application use
*/
public void comment (/*@NotNull*/ CharSequence chars, int locationId, int properties) throws XPathException {
TinyTree tt = tree;
assert tt != null;
if (tt.commentBuffer==null) {
tt.commentBuffer = new FastStringBuffer(FastStringBuffer.MEDIUM);
}
int s = tt.commentBuffer.length();
tt.commentBuffer.append(chars.toString());
nodeNr = tt.addNode(Type.COMMENT, currentDepth, s, chars.length(), -1);
int prev = prevAtDepth[currentDepth];
if (prev > 0) {
tt.next[prev] = nodeNr;
}
tt.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
prevAtDepth[currentDepth] = nodeNr;
siblingsAtDepth[currentDepth]++;
}
/**
* Set an unparsed entity in the document
*/
public void setUnparsedEntity(String name, String uri, String publicId) {
assert currentRoot != null;
((TinyDocumentImpl)currentRoot).setUnparsedEntity(name, uri, publicId);
}
/*@NotNull*/ public BuilderMonitor getBuilderMonitor() {
return new TinyBuilderMonitor(this);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy