package net.sf.saxon.tinytree;
import net.sf.saxon.Configuration;
import net.sf.saxon.om.*;
import net.sf.saxon.sort.IntHashSet;
import net.sf.saxon.style.StandardNames;
import net.sf.saxon.tree.LineNumberMap;
import net.sf.saxon.tree.SystemIdMap;
import net.sf.saxon.type.*;
import net.sf.saxon.value.UntypedAtomicValue;
import java.util.ArrayList;
/**
* A data structure to hold the contents of a tree. As the name implies, this implementation
* of the data model is optimized for size, and for speed of creation: it minimizes the number
* of Java objects used.
*
* <p>It can be used to represent a tree that is rooted at a document node, or one that is rooted
* at an element node.</p>
*/
public final class TinyTree {
private static final int[] EMPTY_INT_ARRAY = new int[0];
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private Configuration config;
// List of top-level document nodes.
private ArrayList documentList = new ArrayList(5);
// The document number (really a tree number: it can identify a non-document root node
protected int documentNumber;
// the contents of the document
protected char[] charBuffer;
protected int charBufferLength = 0;
protected FastStringBuffer commentBuffer = null; // created when needed
protected int numberOfNodes = 0; // excluding attributes and namespaces
// The following arrays contain one entry for each node other than attribute
// and namespace nodes, arranged in document order.
// nodeKind indicates the kind of node, e.g. element, text, or comment
public byte[] nodeKind;
// depth is the depth of the node in the hierarchy, i.e. the number of ancestors
protected short[] depth;
// next is the node number of the next sibling
// - unless it points backwards, in which case it is the node number of the parent
protected int[] next;
// alpha holds a value that depends on the node kind. For text nodes, it is the offset
// into the text buffer. For comments and processing instructions, it is the offset into
// the comment buffer. For elements, it is the index of the first attribute node, or -1
// if this element has no attributes.
protected int[] alpha;
// beta holds a value that depends on the node kind. For text nodes, it is the length
// of the text. For comments and processing instructions, it is the length of the text.
// For elements, it is the index of the first namespace node, or -1
// if this element has no namespaces.
protected int[] beta;
// nameCode holds the name of the node, as an identifier resolved using the name pool
protected int[] nameCode;
// the prior array indexes preceding-siblings; it is constructed only when required
protected int[] prior = null;
// the typeCode array holds type codes for element nodes; it is constructed only
// if at least one element has a type other than untyped (-1)
protected int[] typeCodeArray = null;
// the owner array gives fast access from a node to its parent; it is constructed
// only when required
// protected int[] parentIndex = null;
// the following arrays have one entry for each attribute.
protected int numberOfAttributes = 0;
// attParent is the index of the parent element node
protected int[] attParent;
// attCode is the nameCode representing the attribute name
protected int[] attCode;
// attValue is the string value of the attribute
protected CharSequence[] attValue;
// attTypeCode holds type annotations. The array is created only if any nodes have a type annotation
// or are marked as IDREF/IDREFS attributes. The bit 1<<30 represents the is-idref property, and 1<<29
// represents the is-id property
protected int[] attTypeCode;
// The following arrays have one entry for each namespace declaration
protected int numberOfNamespaces = 0;
// namespaceParent is the index of the element node owning the namespace declaration
protected int[] namespaceParent;
// namespaceCode is the namespace code used by the name pool: the top half is the prefix
// code, the bottom half the URI code
protected int[] namespaceCode;
// an array holding the offsets of all the level-0 (root) nodes, so that the root of a given
// node can be found efficiently
private int[] rootIndex = new int[5];
private int rootIndexUsed = 0;
private LineNumberMap lineNumberMap;
private SystemIdMap systemIdMap = null;
// a boolean that is set to true if the document declares a namespace other than the XML namespace
protected boolean usesNamespaces = false;
private IntHashSet nonIDs; // a set of type annotation codes appearing in this document that are known
// not to be subtypes of xs:ID
//private int IDtype;
public TinyTree() {
this(4000, 100, 20, 4000);
}
public TinyTree(int nodes, int attributes, int namespaces, int characters) {
//System.err.println("TinyTree.new() " + this);
nodeKind = new byte[nodes];
depth = new short[nodes];
next = new int[nodes];
alpha = new int[nodes];
beta = new int[nodes];
nameCode = new int[nodes];
numberOfAttributes = 0;
attParent = new int[attributes];
attCode = new int[attributes];
attValue = new String[attributes];
numberOfNamespaces = 0;
namespaceParent = new int[namespaces];
namespaceCode = new int[namespaces];
charBuffer = new char[characters];
charBufferLength = 0;
}
/**
* Set the Configuration that contains this document
*/
public void setConfiguration(Configuration config) {
this.config = config;
addNamespace(0, NamespaceConstant.XML_NAMESPACE_CODE);
}
/**
* Get the configuration previously set using setConfiguration
*/
public Configuration getConfiguration() {
return config;
}
/**
* Get the name pool used for the names in this document
*/
public NamePool getNamePool() {
return config.getNamePool();
}
private void ensureNodeCapacity(short kind) {
if (nodeKind.length < numberOfNodes+1) {
//System.err.println("Number of nodes = " + numberOfNodes);
int k = (kind == Type.STOPPER ? numberOfNodes+1 : numberOfNodes*2);
byte[] nodeKind2 = new byte[k];
int[] next2 = new int[k];
short[] depth2 = new short[k];
int[] alpha2 = new int[k];
int[] beta2 = new int[k];
int[] nameCode2 = new int[k];
System.arraycopy(nodeKind, 0, nodeKind2, 0, numberOfNodes);
System.arraycopy(next, 0, next2, 0, numberOfNodes);
System.arraycopy(depth, 0, depth2, 0, numberOfNodes);
System.arraycopy(alpha, 0, alpha2, 0, numberOfNodes);
System.arraycopy(beta, 0, beta2, 0, numberOfNodes);
System.arraycopy(nameCode, 0, nameCode2, 0, numberOfNodes);
nodeKind = nodeKind2;
next = next2;
depth = depth2;
alpha = alpha2;
beta = beta2;
nameCode = nameCode2;
if (typeCodeArray != null) {
int[] typeCodeArray2 = new int[k];
System.arraycopy(typeCodeArray, 0, typeCodeArray2, 0, numberOfNodes);
typeCodeArray = typeCodeArray2;
}
}
}
private void ensureAttributeCapacity() {
if (attParent.length < numberOfAttributes+1) {
int k = numberOfAttributes*2;
if (k==0) {
k = 10;
}
int[] attParent2 = new int[k];
int[] attCode2 = new int[k];
String[] attValue2 = new String[k];
System.arraycopy(attParent, 0, attParent2, 0, numberOfAttributes);
System.arraycopy(attCode, 0, attCode2, 0, numberOfAttributes);
System.arraycopy(attValue, 0, attValue2, 0, numberOfAttributes);
attParent = attParent2;
attCode = attCode2;
attValue = attValue2;
if (attTypeCode != null) {
int[] attTypeCode2 = new int[k];
System.arraycopy(attTypeCode, 0, attTypeCode2, 0, numberOfAttributes);
attTypeCode = attTypeCode2;
}
}
}
private void ensureNamespaceCapacity() {
if (namespaceParent.length < numberOfNamespaces+1) {
int k = numberOfNamespaces*2;
int[] namespaceParent2 = new int[k];
int[] namespaceCode2 = new int[k];
System.arraycopy(namespaceParent, 0, namespaceParent2, 0, numberOfNamespaces);
System.arraycopy(namespaceCode, 0, namespaceCode2, 0, numberOfNamespaces);
namespaceParent = namespaceParent2;
namespaceCode = namespaceCode2;
}
}
/**
* Add a document node to the tree. The data structure can contain any number of document (or element) nodes
* as top-level nodes. The document node is retained in the documentList list, and its offset in that list
* is held in the alpha array for the relevant node number.
*/
void addDocumentNode(TinyDocumentImpl doc) {
documentList.add(doc);
addNode(Type.DOCUMENT, 0, documentList.size()-1, 0, -1);
}
/**
* Add a node to the tree
* @param kind The kind of the node. This must be a document, element, text, comment,
* or processing-instruction node (not an attribute or namespace)
* @param depth The depth in the tree
* @param alpha Pointer to attributes or text
* @param beta Pointer to namespaces or text
* @param nameCode The name of the node
* @return the node number of the node that was added
*/
int addNode(short kind, int depth, int alpha, int beta, int nameCode) {
ensureNodeCapacity(kind);
this.nodeKind[numberOfNodes] = (byte)kind;
this.depth[numberOfNodes] = (short)depth;
this.alpha[numberOfNodes] = alpha;
this.beta[numberOfNodes] = beta;
this.nameCode[numberOfNodes] = nameCode;
this.next[numberOfNodes] = -1; // safety precaution
if (typeCodeArray != null) {
typeCodeArray[numberOfNodes] = StandardNames.XDT_UNTYPED;
}
if (numberOfNodes == 0) {
documentNumber = config.getDocumentNumberAllocator().allocateDocumentNumber();
}
if (depth == 0) {
if (rootIndexUsed == rootIndex.length) {
int[] r2 = new int[rootIndexUsed * 2];
System.arraycopy(rootIndex, 0, r2, 0, rootIndexUsed);
rootIndex = r2;
}
rootIndex[rootIndexUsed++] = numberOfNodes;
}
return numberOfNodes++;
}
void appendChars(CharSequence chars) {
if (charBuffer.length < charBufferLength + chars.length()) {
char[] ch2 = new char[Math.max(charBuffer.length * 2, charBufferLength + chars.length() + 100)];
System.arraycopy(charBuffer, 0, ch2, 0, charBufferLength);
charBuffer = ch2;
}
if (chars instanceof CharSlice) {
((CharSlice)chars).copyTo(charBuffer, charBufferLength);
} else if (chars instanceof FastStringBuffer) {
((FastStringBuffer)chars).getChars(0, chars.length(), charBuffer, charBufferLength);
} else {
char[] newchars = chars.toString().toCharArray();
System.arraycopy(newchars, 0, charBuffer, charBufferLength, chars.length());
// TODO: this is inefficient. There is no absolute reason that the
// character data for the whole document needs to be in one array.
}
charBufferLength += chars.length();
}
/**
* Condense the tree: release unused memory. This is done after the full tree has been built.
* The method makes a pragmatic judgement as to whether it is worth reclaiming space; this is
* only done when the constructed tree is very small compared with the space allocated.
*/
protected void condense() {
//System.err.println("TinyTree.condense() " + this + " roots " + rootIndexUsed + " nodes " + numberOfNodes + " capacity " + nodeKind.length);
// If there are already two trees in this forest, the chances are that more will be added. In this
// case we don't want to condense the arrays because we will only have to expand them again, which gets
// increasingly expensive as they grow larger.
if (rootIndexUsed > 1) {
return;
}
if (numberOfNodes * 3 < nodeKind.length ||
(nodeKind.length - numberOfNodes > 20000)) {
//System.err.println("-- copying node arrays");
int k = numberOfNodes + 1;
byte[] nodeKind2 = new byte[k];
int[] next2 = new int[k];
short[] depth2 = new short[k];
int[] alpha2 = new int[k];
int[] beta2 = new int[k];
int[] nameCode2 = new int[k];
System.arraycopy(nodeKind, 0, nodeKind2, 0, numberOfNodes);
System.arraycopy(next, 0, next2, 0, numberOfNodes);
System.arraycopy(depth, 0, depth2, 0, numberOfNodes);
System.arraycopy(alpha, 0, alpha2, 0, numberOfNodes);
System.arraycopy(beta, 0, beta2, 0, numberOfNodes);
System.arraycopy(nameCode, 0, nameCode2, 0, numberOfNodes);
if (typeCodeArray != null) {
int[] type2 = new int[k];
System.arraycopy(typeCodeArray, 0, type2, 0, numberOfNodes);
typeCodeArray = type2;
}
nodeKind = nodeKind2;
next = next2;
depth = depth2;
alpha = alpha2;
beta = beta2;
nameCode = nameCode2;
}
if ((numberOfAttributes * 3 < attParent.length) ||
(attParent.length - numberOfAttributes > 1000)) {
int k = numberOfAttributes;
//System.err.println("-- copying attribute arrays");
if (k==0) {
attParent = EMPTY_INT_ARRAY;
attCode = EMPTY_INT_ARRAY;
attValue = EMPTY_STRING_ARRAY;
attTypeCode = null;
}
int[] attParent2 = new int[k];
int[] attCode2 = new int[k];
String[] attValue2 = new String[k];
System.arraycopy(attParent, 0, attParent2, 0, numberOfAttributes);
System.arraycopy(attCode, 0, attCode2, 0, numberOfAttributes);
System.arraycopy(attValue, 0, attValue2, 0, numberOfAttributes);
attParent = attParent2;
attCode = attCode2;
attValue = attValue2;
if (attTypeCode != null) {
int[] attTypeCode2 = new int[k];
System.arraycopy(attTypeCode, 0, attTypeCode2, 0, numberOfAttributes);
attTypeCode = attTypeCode2;
}
}
if (numberOfNamespaces * 3 < namespaceParent.length) {
int k = numberOfNamespaces;
int[] namespaceParent2 = new int[k];
int[] namespaceCode2 = new int[k];
//System.err.println("-- copying namespace arrays");
System.arraycopy(namespaceParent, 0, namespaceParent2, 0, numberOfNamespaces);
System.arraycopy(namespaceCode, 0, namespaceCode2, 0, numberOfNamespaces);
namespaceParent = namespaceParent2;
namespaceCode = namespaceCode2;
}
if (charBufferLength * 3 < charBuffer.length ||
charBuffer.length - charBufferLength > 10000) {
char[] c2 = new char[charBufferLength];
System.arraycopy(charBuffer, 0, c2, 0, charBufferLength);
charBuffer = c2;
}
}
/**
* Set the type annotation of an element node
*/
void setElementAnnotation(int nodeNr, int typeCode) {
if (typeCode != StandardNames.XDT_UNTYPED) {
if (typeCodeArray == null) {
typeCodeArray = new int[nodeKind.length];
for (int i=0; i<nodeKind.length; i++) {
typeCodeArray[i] = StandardNames.XDT_UNTYPED;
}
}
typeCodeArray[nodeNr] = typeCode;
}
}
/**
* Get the type annotation of a node. Applies only to document, element, text,
* processing instruction, and comment nodes.
* @return -1 if the annotation is xdt:untyped or if the node is not an element.
*/
public int getTypeAnnotation(int nodeNr) {
if (typeCodeArray == null) {
return StandardNames.XDT_UNTYPED;
}
return typeCodeArray[nodeNr];
}
/**
* Get the node kind of a given node, which must be a document, element,
* text, comment, or processing instruction node
* @param nodeNr the node number
* @return the node kind
*/
public int getNodeKind(int nodeNr) {
return nodeKind[nodeNr];
}
/**
* Get the nameCode for a given node, which must be a document, element,
* text, comment, or processing instruction node
* @param nodeNr the node number
* @return the name code
*/
public int getNameCode(int nodeNr) {
return nameCode[nodeNr];
}
/**
* On demand, make an index for quick access to preceding-sibling nodes
*/
void ensurePriorIndex() {
if (prior==null) {
makePriorIndex();
}
}
private synchronized void makePriorIndex() {
prior = new int[numberOfNodes];
for (int i=0; i<numberOfNodes; i++) {
prior[i] = -1;
}
for (int i=0; i<numberOfNodes; i++) {
int nextNode = next[i];
if (nextNode > i) {
prior[nextNode] = i;
}
}
}
void addAttribute(NodeInfo root, int parent, int nameCode, int typeCode, CharSequence attValue, int properties) {
ensureAttributeCapacity();
attParent[numberOfAttributes] = parent;
attCode[numberOfAttributes] = nameCode;
this.attValue[numberOfAttributes] = attValue;
if (typeCode == -1) {
// this shouldn't happen any more
typeCode = StandardNames.XDT_UNTYPED_ATOMIC;
}
if (typeCode != StandardNames.XDT_UNTYPED_ATOMIC) {
if (attTypeCode==null) {
// this is the first typed attribute;
// create an array for the types, and set all previous attributes to untyped
attTypeCode = new int[attParent.length];
for (int i=0; i<numberOfAttributes; i++) {
attTypeCode[i] = StandardNames.XDT_UNTYPED_ATOMIC;
}
}
}
if (attTypeCode != null) {
attTypeCode[numberOfAttributes] = typeCode;
}
if (alpha[parent] == -1) {
alpha[parent] = numberOfAttributes;
}
if (root instanceof TinyDocumentImpl && (isIDCode(typeCode) ||
((nameCode & NamePool.FP_MASK) == StandardNames.XML_ID))) {
// The attribute is marked as being an ID. But we don't trust it - it
// might come from a non-validating parser. Before adding it to the index, we
// check that it really is an ID.
String id = attValue.toString().trim();
if (root.getConfiguration().getNameChecker().isValidNCName(id)) {
NodeInfo e = getNode(parent);
((TinyDocumentImpl)root).registerID(e, id);
} else if (attTypeCode != null) {
attTypeCode[numberOfAttributes] = Type.UNTYPED_ATOMIC;
}
}
// Note: IDREF attributes are not indexed at this stage; that happens only if and when
// the idref() function is called.
// Note that an attTypes array will be created for all attributes if any ID or IDREF is reported.
numberOfAttributes++;
}
/**
* Index an element of type xs:ID
*/
public void indexIDElement(NodeInfo root, int nodeNr, NameChecker checker) {
String id = TinyParentNodeImpl.getStringValue(this, nodeNr).toString().trim();
if (root.getNodeKind() == Type.DOCUMENT && checker.isValidNCName(id)) {
NodeInfo e = getNode(nodeNr);
((TinyDocumentImpl)root).registerID(e, id);
}
}
/**
* Test whether a type annotation code represents the type xs:ID or one of its subtypes
*/
public boolean isIDCode(int typeCode) {
typeCode &= NamePool.FP_MASK;
if (typeCode == StandardNames.XS_ID) {
return true;
} else if (typeCode < 1024) {
// No other built-in type is an ID
return false;
} else {
if (nonIDs == null) {
nonIDs = new IntHashSet(20);
}
//Integer key = new Integer(typeCode);
if (nonIDs.contains(typeCode)) {
return false;
}
SchemaType type = getConfiguration().getSchemaType(typeCode);
if (type instanceof AtomicType) {
final TypeHierarchy th = getConfiguration().getNamePool().getTypeHierarchy();
if (th.isSubType((AtomicType)type, Type.ID_TYPE)) {
return true;
} else {
nonIDs.add(typeCode);
return false;
}
} else {
return false;
}
}
}
/**
* Add a namespace node to the current element
* @param parent the node number of the element
* @param nscode namespace code identifying the prefix and uri
*/
void addNamespace(int parent, int nscode ) {
ensureNamespaceCapacity();
namespaceParent[numberOfNamespaces] = parent;
namespaceCode[numberOfNamespaces] = nscode;
if (beta[parent] == -1) {
beta[parent] = numberOfNamespaces;
}
numberOfNamespaces++;
if (nscode != NamespaceConstant.XML_NAMESPACE_CODE) {
usesNamespaces = true;
}
}
public TinyNodeImpl getNode(int nr) {
switch ((short)nodeKind[nr]) {
case Type.DOCUMENT:
return (TinyDocumentImpl)documentList.get(alpha[nr]);
case Type.ELEMENT:
return new TinyElementImpl(this, nr);
case Type.TEXT:
return new TinyTextImpl(this, nr);
case Type.COMMENT:
return new TinyCommentImpl(this, nr);
case Type.PROCESSING_INSTRUCTION:
return new TinyProcInstImpl(this, nr);
case Type.PARENT_POINTER:
throw new IllegalArgumentException("Attempting to treat a parent pointer as a node");
}
return null;
}
/**
* Get the typed value of a node whose type is known to be untypedAtomic.
* The node must be a document, element, text,
* comment, or processing-instruction node, and it must have no type annotation.
* This method gets the typed value
* of a numbered node without actually instantiating the NodeInfo object, as
* a performance optimization.
*/
UntypedAtomicValue getUntypedAtomicValue(int nodeNr) {
switch (nodeKind[nodeNr]) {
case Type.ELEMENT:
case Type.DOCUMENT:
int level = depth[nodeNr];
int next = nodeNr+1;
// we optimize two special cases: firstly, where the node has no children, and secondly,
// where it has a single text node as a child.
if (depth[next] <= level) {
return UntypedAtomicValue.ZERO_LENGTH_UNTYPED;
} else if (nodeKind[next] == Type.TEXT && depth[next+1] <= level) {
int length = beta[next];
int start = alpha[next];
return new UntypedAtomicValue(new CharSlice(charBuffer, start, length));
}
// Now handle the general case
StringBuffer sb = null;
while (next < numberOfNodes && depth[next] > level) {
if (nodeKind[next]==Type.TEXT) {
if (sb==null) {
sb = new StringBuffer(1024);
}
int length = beta[next];
int start = alpha[next];
sb.append(charBuffer, start, length);
}
next++;
}
if (sb==null) {
return UntypedAtomicValue.ZERO_LENGTH_UNTYPED;
} else {
//sb.trimToSize(); // TODO: reinstate this in JDK 1.5
char[] buff = new char[sb.length()];
sb.getChars(0, sb.length(), buff, 0);
return new UntypedAtomicValue(new CharSlice(buff));
}
case Type.TEXT:
int start = alpha[nodeNr];
int len = beta[nodeNr];
return new UntypedAtomicValue(
new CharSlice(charBuffer, start, len));
case Type.COMMENT:
case Type.PROCESSING_INSTRUCTION:
int start2 = alpha[nodeNr];
int len2 = beta[nodeNr];
if (len2==0) return UntypedAtomicValue.ZERO_LENGTH_UNTYPED;
char[] dest = new char[len2];
commentBuffer.getChars(start2, start2+len2, dest, 0);
return new UntypedAtomicValue(new CharSlice(dest, 0, len2));
default:
throw new IllegalStateException("Unknown node kind");
}
}
/**
* Make a (transient) attribute node from the array of attributes
*/
TinyAttributeImpl getAttributeNode(int nr) {
return new TinyAttributeImpl(this, nr);
}
/**
* Get the type annotation of an attribute node.
* The bit {@link NodeInfo#IS_DTD_TYPE} (1<<30) will be set in the case of an attribute node if the type annotation
* is one of ID, IDREF, or IDREFS and this is derived from DTD rather than schema validation.
* @return Type.UNTYPED_ATOMIC if there is no annotation
*/
int getAttributeAnnotation(int nr) {
if (attTypeCode == null) {
return StandardNames.XDT_UNTYPED_ATOMIC;
} else {
return attTypeCode[nr] & (NamePool.FP_MASK | NodeInfo.IS_DTD_TYPE);
}
}
/**
* Determine whether an attribute is an IDREF/IDREFS attribute. (The represents the
* is-IDREF property in the data model
*/
boolean isIdref(int nr) {
if (attTypeCode == null) {
return false;
} else {
int tc = attTypeCode[nr];
if ((attTypeCode[nr] & (1<<30)) != 0) {
return true;
} else if (tc == Type.UNTYPED_ATOMIC) {
return false;
} else if (tc == StandardNames.XS_IDREF) {
return true;
} else if (tc == StandardNames.XS_IDREFS) {
return true;
} else if (tc < 1024) {
return false;
} else {
final SchemaType type = getConfiguration().getSchemaType(tc);
final TypeHierarchy th = getConfiguration().getNamePool().getTypeHierarchy();
if (type instanceof AtomicType) {
return th.isSubType((AtomicType)type,
(AtomicType)BuiltInSchemaFactory.getSchemaType(StandardNames.XS_IDREF));
} else if (type instanceof ListType) {
SimpleType itemType = ((ListType)type).getItemType();
if (!(itemType instanceof AtomicType)) {
return false;
}
return th.isSubType((AtomicType)itemType,
(AtomicType)BuiltInSchemaFactory.getSchemaType(StandardNames.XS_IDREF));
}
}
}
return false;
}
/**
* Set the system id of an element in the document. This identifies the external entity containing
* the node - this is not necessarily the same as the base URI.
* @param seq the node number
* @param uri the system ID
*/
void setSystemId(int seq, String uri) {
if (uri==null) {
uri = "";
}
if (systemIdMap==null) {
systemIdMap = new SystemIdMap();
}
systemIdMap.setSystemId(seq, uri);
}
/**
* Get the system id of an element in the document
*/
String getSystemId(int seq) {
if (systemIdMap==null) {
return null;
}
return systemIdMap.getSystemId(seq);
}
/**
* Get the root node for a given node
*/
int getRootNode(int nodeNr) {
for (int i=rootIndexUsed-1; i>=0; i--) {
if (rootIndex[i] <= nodeNr) {
return rootIndex[i];
}
}
return 0;
}
/**
* Set line numbering on
*/
public void setLineNumbering() {
lineNumberMap = new LineNumberMap();
lineNumberMap.setLineNumber(0, 0);
}
/**
* Set the line number for an element. Ignored if line numbering is off.
*/
void setLineNumber(int sequence, int line) {
if (lineNumberMap != null) {
lineNumberMap.setLineNumber(sequence, line);
}
}
/**
* Get the line number for an element. Return -1 if line numbering is off.
*/
int getLineNumber(int sequence) {
if (lineNumberMap != null) {
return lineNumberMap.getLineNumber(sequence);
}
return -1;
}
/**
* Get the document number (actually, the tree number)
*/
public int getDocumentNumber() {
return documentNumber;
}
/**
* Determine whether a given node is nilled
*/
public boolean isNilled(int nodeNr) {
if (nodeKind[nodeNr] != Type.ELEMENT) {
return false;
}
if (typeCodeArray == null || typeCodeArray[nodeNr] == -1 || typeCodeArray[nodeNr] == StandardNames.XDT_UNTYPED) {
return false;
}
int index = alpha[nodeNr];
if (index > 0) {
while (attParent[index] == nodeNr && index < numberOfAttributes) {
if (attCode[index] == StandardNames.XSI_NIL) {
String val = attValue[index].toString().trim();
if (val.equals("1") || val.equals("true")) {
return true;
}
}
index++;
}
}
return false;
}
/**
* Produce diagnostic print of main tree arrays
*/
public void diagnosticDump() {
System.err.println(" node type depth next alpha beta name");
for (int i=0; i<numberOfNodes; i++) {
System.err.println(n8(i) + n8(nodeKind[i]) + n8(depth[i]) + n8(next[i]) +
n8(alpha[i]) + n8(beta[i]) + n8(nameCode[i]));
}
System.err.println(" attr parent name value");
for (int i=0; i<numberOfAttributes; i++) {
System.err.println(n8(i) + n8(attParent[i]) + n8(attCode[i]) + " " + attValue[i]);
}
System.err.println(" ns parent prefix uri");
for (int i=0; i<numberOfNamespaces; i++) {
System.err.println(n8(i) + n8(namespaceParent[i]) + n8(namespaceCode[i]>>16) + n8(namespaceCode[i]&0xffff));
}
}
/**
* Output a number as a string of 8 characters
*/
private String n8(int val) {
String s = " " + val;
return s.substring(s.length()-8);
}
public void showSize() {
System.err.println("Tree size: " + numberOfNodes + " nodes, " + charBufferLength + " characters, " +
numberOfAttributes + " attributes");
}
/**
* Get the number of nodes in the tree, excluding attributes and namespace nodes
* @return the number of nodes.
*/
public int getNumberOfNodes() {
return numberOfNodes;
}
public int getNumberOfAttributes() {
return numberOfAttributes;
}
public int getNumberOfNamespaces() {
return numberOfNamespaces;
}
public byte[] getNodeKindArray() {
return nodeKind;
}
public short[] getNodeDepthArray() {
return depth;
}
public int[] getNameCodeArray() {
return nameCode;
}
public int[] getTypeCodeArray() {
return typeCodeArray;
}
public int[] getNextPointerArray() {
return next;
}
public int[] getAlphaArray() {
return alpha;
}
public int[] getBetaArray() {
return beta;
}
public CharSequence getCharacterBuffer() {
return new CharSlice(charBuffer, 0, charBufferLength);
}
public CharSequence getCommentBuffer() {
return commentBuffer;
}
public int[] getAttributeNameCodeArray() {
return attCode;
}
public int[] getAttributeTypeCodeArray() {
return attTypeCode;
}
public int[] getAttributeParentArray() {
return attParent;
}
public CharSequence[] getAttributeValueArray() {
return attValue;
}
public int[] getNamespaceCodeArray() {
return namespaceCode;
}
public int[] getNamespaceParentArray() {
return namespaceParent;
}
}
//
// 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):
//
|