package org.basex.index.path;
import static org.basex.util.Token.*;
import java.io.IOException;
import org.basex.data.Data;
import org.basex.data.MetaData;
import org.basex.index.Index;
import org.basex.index.IndexIterator;
import org.basex.index.IndexToken;
import org.basex.io.in.DataInput;
import org.basex.io.out.DataOutput;
import org.basex.util.Array;
import org.basex.util.Util;
import org.basex.util.hash.TokenIntMap;
import org.basex.util.list.ObjList;
import org.basex.util.list.TokenList;
/**
* This class stores the path summary of a database.
* It contains all unique location paths.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class PathSummary implements Index {
/** Node stack for building the summary. */
private final ObjList<PathNode> stack = new ObjList<PathNode>();
/** Data reference. */
private Data data;
/** Root node. */
private PathNode root;
/**
* Constructor, specifying a data reference.
* @param d data reference
*/
public PathSummary(final Data d) {
data = d;
}
/**
* Constructor, specifying an input file.
* @param d data reference
* @param in input stream
* @throws IOException I/O exception
*/
public PathSummary(final Data d, final DataInput in) throws IOException {
if(in.readBool()) root = new PathNode(in, null);
data = d;
}
/**
* Writes the path summary to the specified output.
* @param out output stream
* @throws IOException I/O exception
*/
public void write(final DataOutput out) throws IOException {
out.writeBool(root != null);
if(root != null) root.write(out);
}
/**
* Sets the data reference.
* @param d reference
*/
public void finish(final Data d) {
data = d;
}
@Override
public void close() {
root = null;
}
// Build Index ==============================================================
/**
* Adds an entry.
* @param n name reference
* @param k node kind
* @param l current level
*/
public void index(final int n, final byte k, final int l) {
index(n, k, l, null, null);
}
/**
* Adds an entry, including its value.
* @param n name reference
* @param k node kind
* @param l current level
* @param v value
* @param md meta data
*/
public void index(final int n, final byte k, final int l, final byte[] v,
final MetaData md) {
if(root == null) {
root = new PathNode(n, k, null);
stack.size(0);
stack.add(root);
} else if(l == 0) {
if(v != null) root.stats.add(v, md);
root.stats.count++;
} else {
stack.set(l, stack.get(l - 1).index(n, k, v, md));
}
}
// Traverse Index ===========================================================
/**
* Returns the root node.
* @return root node
*/
public ObjList<PathNode> root() {
final ObjList<PathNode> out = new ObjList<PathNode>();
out.add(root);
return out;
}
/**
* Returns all parents of the specified nodes.
* Used by the query optimizers.
* @param in input nodes
* @return parent nodes
*/
public static ObjList<PathNode> parent(final ObjList<PathNode> in) {
final ObjList<PathNode> out = new ObjList<PathNode>();
for(final PathNode n : in) if(!out.contains(n.par)) out.add(n.par);
return out;
}
/**
* Returns all children or descendants of the specified nodes.
* Called from the query parser and optimizer.
* @param in input nodes
* @param desc if false, return only children
* @return descendant nodes
*/
public static ObjList<PathNode> desc(final ObjList<PathNode> in,
final boolean desc) {
final ObjList<PathNode> out = new ObjList<PathNode>();
for(final PathNode n : in) {
for(final PathNode c : n.ch) {
if(desc) c.addDesc(out);
else if(!out.contains(c)) out.add(c);
}
}
return out;
}
/**
* Returns all children or descendants of the specified nodes with the
* specified tag or attribute value. Called from the query optimizer.
* @param n name reference
* @param k node kind
* @return descendant nodes
*/
public ObjList<PathNode> desc(final int n, final int k) {
final ObjList<PathNode> out = new ObjList<PathNode>();
for(final PathNode c : root.ch) c.addDesc(out, n, k);
return out;
}
/**
* Returns descendant tags and attributes for the specified start key.
* Used by the GUI.
* @param k input key
* @param d if false, return only children
* @param o true/false: sort by occurrence/lexicographically
* @return children
*/
public TokenList desc(final byte[] k, final boolean d, final boolean o) {
final TokenList tl = new TokenList();
if(k.length != 0) tl.add(k);
return desc(tl, d, o);
}
/**
* Returns descendant tags and attributes for the specified descendant path.
* Used by the GUI.
* @param tl input steps
* @param d if false, return only children
* @param o true/false: sort by occurrence/lexicographically
* @return children
*/
public TokenList desc(final TokenList tl, final boolean d, final boolean o) {
// follow the specified descendant/child steps
ObjList<PathNode> in = desc(root(), true);
for(final byte[] i : tl) {
final boolean att = startsWith(i, '@');
final byte kind = att ? Data.ATTR : Data.ELEM;
final int id = att ? data.atnindex.id(substring(i, 1)) :
data.tagindex.id(i);
final ObjList<PathNode> out = new ObjList<PathNode>();
for(final PathNode n : in) {
if(n.name != id || n.kind != kind) continue;
for(final PathNode c : n.ch) {
if(d) c.addDesc(out);
else out.add(c);
}
}
in = out;
}
// sort by number of occurrences
final double[] tmp = new double[in.size()];
for(int i = 0; i < in.size(); ++i) tmp[i] = in.get(i).stats.count;
final int[] occ = Array.createOrder(tmp, false);
// remove non-text/attribute nodes
final TokenList out = new TokenList();
for(int i = 0; i < in.size(); ++i) {
final PathNode r = in.get(o ? occ[i] : i);
final byte[] name = r.token(data);
if(name.length != 0 && !out.contains(name) && !contains(name, '(')) {
out.add(name);
}
}
if(!o) out.sort(false);
return out;
}
// Info =====================================================================
@Override
public byte[] info() {
return root != null ? chop(root.info(data, 0), 1 << 20) : EMPTY;
}
// Unsupported methods ======================================================
@Override
public IndexIterator iter(final IndexToken token) {
throw Util.notexpected();
}
@Override
public int count(final IndexToken token) {
throw Util.notexpected();
}
@Override
public TokenIntMap entries(final byte[] prefix) {
throw Util.notexpected();
}
@Override
public String toString() {
return string(info());
}
}