package org.basex.query.item; import org.basex.api.dom.*; import org.basex.data.Data; import org.basex.io.serial.Serializer; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.iter.AxisIter; import org.basex.query.iter.AxisMoreIter; import org.basex.query.iter.NodeCache; import org.basex.util.Atts; import org.basex.util.InputInfo; import org.basex.util.Token; import org.basex.util.Util; import java.io.IOException; /** * Abstract node type. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public abstract class ANode extends Item { /** Node Types. */ private static final NodeType[] TYPES = { NodeType.DOC, NodeType.ELM, NodeType.TXT, NodeType.ATT, NodeType.COM, NodeType.PI }; /** Static node counter. */ // [CG] XQuery/ID: // - move to query context to reduce chance of overflow // - move to FNode to reduce memory usage of DBNode instances private static int sid; /** Unique node id. */ public final int id = ++sid; /** Cached string value. */ byte[] val; /** Parent node. */ ANode par; /** * Constructor. * @param t data type */ ANode(final NodeType t) { super(t); } @Override public final boolean bool(final InputInfo ii) { return true; } @Override public final byte[] string(final InputInfo ii) { return string(); } /** * Returns the string value. * @return string value */ public abstract byte[] string(); @Override public final boolean eq(final InputInfo ii, final Item it) throws QueryException { return !it.type.isUntyped() ? it.eq(ii, this) : Token.eq(string(), it.string(ii)); } @Override public final int diff(final InputInfo ii, final Item it) throws QueryException { return !it.type.isUntyped() ? -it.diff(ii, this) : Token.diff(string(), it.string(ii)); } /** * Returns a copy of the node. * @return copy */ public abstract ANode copy(); /** * Returns a deep copy of the node. * @return node copy */ public abstract ANode deepCopy(); /** * Returns the name of the node, composed of an optional prefix * and the local name. * This function must only be called for element and attribute nodes. * It is more efficient than calling {@link #qname}, as no {@link QNm} * instance is created. * @return name */ public byte[] name() { return null; } /** * Returns the QName of the node. * This function must only be called for elements, attributes and pi's. * @return name */ public QNm qname() { return null; } /** * Updates the specified with the information of the current node. * This is more efficient than calling {@link #qname}, as an existing * {@link QNm} instance is reused. * This function must only be called for elements, attributes and pi's. * @param nm temporary qname * @return name */ public abstract QNm update(final QNm nm); /** * Minimizes the memory consumption of the node. * @return self reference */ public ANode optimize() { return this; } /** * Returns all namespaces defined for the nodes. * Overwritten by {@link FElem} and {@link DBNode}. * @return namespace array */ public Atts namespaces() { return null; } /** * Returns a copy of the namespace hierarchy. * @return namespaces */ public final Atts nsScope() { final Atts ns = new Atts(); ANode node = this; do { final Atts n = node.namespaces(); if(n != null) { for(int a = n.size() - 1; a >= 0; a--) { final byte[] key = n.name(a); if(!ns.contains(key)) ns.add(key, n.string(a)); } } node = node.parent(); } while(node != null && node.type == NodeType.ELM); return ns; } /** * Recursively finds the uri for the specified prefix. * @param pref prefix * @param ctx query context * @return uri */ public final byte[] uri(final byte[] pref, final QueryContext ctx) { final Atts at = namespaces(); if(at != null) { final int i = at.get(pref); if(i != -1) return at.string(i); final ANode n = parent(); if(n != null) return n.uri(pref, ctx); } return pref.length == 0 ? Token.EMPTY : null; } /** * Returns the base URI of the node. * @return base URI */ public byte[] baseURI() { return Token.EMPTY; } @Override public abstract void serialize(final Serializer ser) throws IOException; /** * Compares the identity of two nodes. * @param node node to be compared * @return result of check */ public abstract boolean is(final ANode node); /** * Compares two nodes for their unique order. * @param node node to be compared * @return 0 if the nodes are equal or a positive/negative value * if the node appears after/before the argument */ public abstract int diff(final ANode node); /** * Returns a final node representation. This method is called by the * step expressions, before it is passed on as result. * @return node */ public ANode finish() { return this; } /** * Returns the parent node. * @return parent node */ public abstract ANode parent(); /** * Sets the parent node. * @param p parent node * @return self reference */ public abstract ANode parent(final ANode p); /** * Returns true if the node has children. * @return result of test */ public abstract boolean hasChildren(); /** * Returns the value of the specified attribute, or {@code null}. * @param name attribute to be found * @return attribute value */ public byte[] attribute(final QNm name) { final AxisIter ai = attributes(); while(true) { final ANode node = ai.next(); if(node == null) return null; if(node.qname().eq(name)) return node.string(); } } /** * Returns an ancestor axis iterator. * @return iterator */ public abstract AxisIter ancestor(); /** * Returns an ancestor-or-self axis iterator. * @return iterator */ public abstract AxisIter ancestorOrSelf(); /** * Returns an attribute axis iterator. * @return iterator */ public abstract AxisMoreIter attributes(); /** * Returns a child axis iterator. * @return iterator */ public abstract AxisMoreIter children(); /** * Returns a descendant axis iterator. * @return iterator */ public abstract AxisIter descendant(); /** * Returns a descendant-or-self axis iterator. * @return iterator */ public abstract AxisIter descendantOrSelf(); /** * Returns a following axis iterator. * @return iterator */ public abstract AxisIter following(); /** * Returns a following-sibling axis iterator. * @return iterator */ public abstract AxisIter followingSibling(); /** * Returns a parent axis iterator. * @return iterator */ public abstract AxisIter parentIter(); /** * Returns a preceding axis iterator. * @return iterator */ public final AxisIter preceding() { return new AxisIter() { /** Iterator. */ private NodeCache nc; @Override public ANode next() { if(nc == null) { nc = new NodeCache(); ANode n = ANode.this; ANode p = n.parent(); while(p != null) { if(n.type != NodeType.ATT) { final NodeCache tmp = new NodeCache(); final AxisIter ai = p.children(); for(ANode c; (c = ai.next()) != null && !c.is(n);) { tmp.add(c.finish()); addDesc(c.children(), tmp); } for(long t = tmp.size() - 1; t >= 0; t--) nc.add(tmp.get(t)); } n = p; p = p.parent(); } } return nc.next(); } }; } /** * Returns a preceding-sibling axis iterator. * @return iterator */ public final AxisIter precedingSibling() { return new AxisIter() { /** Child nodes. */ private NodeCache nc; /** Counter. */ private long c; @Override public ANode next() { if(nc == null) { final ANode r = parent(); if(r == null) return null; nc = new NodeCache(); final AxisIter ai = r.children(); for(ANode n; (n = ai.next()) != null && !n.is(ANode.this);) { nc.add(n.finish()); } c = nc.size(); } return c > 0 ? nc.get(--c) : null; } }; } /** * Returns an self axis iterator. * @return iterator */ public final AxisMoreIter self() { return new AxisMoreIter() { /** First call. */ private boolean more = true; @Override public boolean more() { return more; } @Override public ANode next() { return (more ^= true) ? null : ANode.this; } }; } /** * Adds children of a sub node. * @param ch child nodes * @param nc node cache */ static final void addDesc(final AxisMoreIter ch, final NodeCache nc) { for(ANode n; (n = ch.next()) != null;) { nc.add(n.finish()); addDesc(n.children(), nc); } } /** * Returns a database kind for the specified node type. * @return node kind */ public int kind() { return kind(nodeType()); } /** * Returns a database kind for the specified node type. * @param t node type * @return node kind */ public static int kind(final NodeType t) { switch(t) { case DOC: return Data.DOC; case ELM: return Data.ELEM; case TXT: return Data.TEXT; case ATT: return Data.ATTR; case COM: return Data.COMM; case PI : return Data.PI; default : throw Util.notexpected(); } } /** * Returns a node type for the specified database kind. * @param k database kind * @return node type */ public static NodeType type(final int k) { return TYPES[k]; } @Override public final BXNode toJava() { switch(nodeType()) { case DOC: return new BXDoc(this); case ELM: return new BXElem(this); case TXT: return new BXText(this); case ATT: return new BXAttr(this); case COM: return new BXComm(this); case PI : return new BXPI(this); default : return null; } } /** * Returns this Node's node type. * @return node type */ public final NodeType nodeType() { return (NodeType) type; } }