package org.basex.index.path; import static org.basex.data.DataText.*; import java.io.IOException; import org.basex.core.Text; import org.basex.data.Data; import org.basex.data.MetaData; import org.basex.index.Stats; import org.basex.io.in.DataInput; import org.basex.io.out.DataOutput; import org.basex.util.Token; import org.basex.util.TokenBuilder; import org.basex.util.list.ObjList; /** * This class represents a node of the path summary. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen * @author Andreas Weiler */ public final class PathNode { /** Tag/attribute name id. */ public final short name; /** Node kind, defined in the {@link Data} class. */ public final byte kind; /** Parent. */ public final PathNode par; /** Children. */ public PathNode[] ch; /** Node kind. */ public Stats stats; /** * Default constructor. * @param n node name * @param k node kind * @param p parent node */ PathNode(final int n, final byte k, final PathNode p) { ch = new PathNode[0]; name = (short) n; kind = k; par = p; stats = new Stats(); stats.count = 1; } /** * Constructor, specifying an input stream. * @param in input stream * @param p parent node * @throws IOException I/O exception */ PathNode(final DataInput in, final PathNode p) throws IOException { name = (short) in.readNum(); kind = (byte) in.read(); final int s = in.readNum(); ch = new PathNode[in.readNum()]; if(in.readDouble() == 1) { // "1" indicates the format introduced with Version 7.1 stats = new Stats(in); } else { // create old format stats = new Stats(); stats.count = s; } par = p; for(int i = 0; i < ch.length; ++i) ch[i] = new PathNode(in, this); } /** * Indexes the specified name along with its kind. * @param n name id * @param k node kind * @param v value * @param md meta data * @return node reference */ PathNode index(final int n, final byte k, final byte[] v, final MetaData md) { for(final PathNode c : ch) { if(c.kind == k && c.name == n) { if(v != null) c.stats.add(v, md); c.stats.count++; return c; } } final PathNode pn = new PathNode(n, k, this); if(v != null) pn.stats.add(v, md); final int cs = ch.length; final PathNode[] tmp = new PathNode[cs + 1]; System.arraycopy(ch, 0, tmp, 0, cs); tmp[cs] = pn; ch = tmp; return pn; } /** * Writes the node to the specified output stream. * @param out output stream * @throws IOException I/O exception */ void write(final DataOutput out) throws IOException { out.writeNum(name); out.write1(kind); out.writeNum(0); out.writeNum(ch.length); out.writeDouble(1); // update leaf flag boolean leaf = stats.isLeaf(); for(final PathNode c : ch) { leaf &= c.kind == Data.TEXT || c.kind == Data.ATTR; } stats.setLeaf(leaf); stats.write(out); for(final PathNode c : ch) c.write(out); } /** * Recursively adds the node and its descendants to the specified list. * @param nodes node list */ void addDesc(final ObjList<PathNode> nodes) { nodes.add(this); for(final PathNode n : ch) n.addDesc(nodes); } /** * Recursively adds the node and its descendants to the specified list * with the specified name. * @param nodes node list * @param n name id * @param k node kind */ void addDesc(final ObjList<PathNode> nodes, final int n, final int k) { if(n == name && k == kind) nodes.add(this); for(final PathNode pn : ch) pn.addDesc(nodes, n, k); } /** * Returns a readable representation of this node. * @param data data reference * @return completions */ public byte[] token(final Data data) { switch(kind) { case Data.ELEM: return data.tagindex.key(name); case Data.ATTR: return Token.concat(ATT, data.atnindex.key(name)); case Data.TEXT: return TEXT; case Data.COMM: return COMM; case Data.PI: return PI; default: return Token.EMPTY; } } /** * Returns the level of the path node. * @return level */ public int level() { PathNode pn = par; int c = 0; while(pn != null) { pn = pn.par; ++c; } return c; } /** * Prints a path summary node. * @param data data reference * @param l level * @return string representation */ byte[] info(final Data data, final int l) { final TokenBuilder tb = new TokenBuilder(); if(l != 0) tb.add(Text.NL); for(int i = 0; i < l << 1; ++i) tb.add(' '); switch(kind) { case Data.DOC: tb.add(DOC); break; case Data.ELEM: tb.add(data.tagindex.key(name)); break; case Data.TEXT: tb.add(TEXT); break; case Data.ATTR: tb.add(ATT); tb.add(data.atnindex.key(name)); break; case Data.COMM: tb.add(COMM); break; case Data.PI: tb.add(PI); break; } tb.add(": " + stats); for(final PathNode p : ch) tb.add(p.info(data, l + 1)); return tb.finish(); } }