package org.basex.query.func;
import static org.basex.query.QueryText.*;
import static org.basex.query.util.Err.*;
import static org.basex.util.Token.*;
import java.util.Locale;
import org.basex.data.Data;
import org.basex.index.Index;
import org.basex.index.IndexToken.IndexType;
import org.basex.index.Names;
import org.basex.index.Stats;
import org.basex.index.path.PathNode;
import org.basex.query.QueryContext;
import org.basex.query.QueryException;
import org.basex.query.expr.Expr;
import org.basex.query.item.ANode;
import org.basex.query.item.FAttr;
import org.basex.query.item.FDoc;
import org.basex.query.item.FElem;
import org.basex.query.item.FTxt;
import org.basex.query.item.Item;
import org.basex.query.item.NodeType;
import org.basex.query.item.QNm;
import org.basex.query.iter.Iter;
import org.basex.query.iter.NodeCache;
import org.basex.query.iter.ValueIter;
import org.basex.util.InputInfo;
import org.basex.util.hash.TokenIntMap;
/**
* Index functions.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
* @author Andreas Weiler
*/
public final class FNIndex extends StandardFunc {
/** Name: name. */
static final QNm Q_NAME = new QNm(NAM);
/** Name: count. */
static final QNm Q_COUNT = new QNm(COUNT);
/** Name: type. */
static final QNm Q_TYPE = new QNm(TYP);
/** Name: value. */
static final QNm Q_VALUE = new QNm(VAL);
/** Name: min. */
static final QNm Q_MIN = new QNm(MIN);
/** Name: max. */
static final QNm Q_MAX = new QNm(MAX);
/** Name: elements. */
static final QNm Q_ELM = new QNm(NodeType.ELM.string());
/** Name: attributes. */
static final QNm Q_ATT = new QNm(NodeType.ATT.string());
/** Flag: flat output. */
static final byte[] FLAT = token("flat");
/**
* Constructor.
* @param ii input info
* @param f function definition
* @param e arguments
*/
public FNIndex(final InputInfo ii, final Function f, final Expr... e) {
super(ii, f, e);
}
@Override
public Item item(final QueryContext ctx, final InputInfo ii)
throws QueryException {
switch(sig) {
case _INDEX_FACETS: return facets(ctx);
default: return super.item(ctx, ii);
}
}
@Override
public Iter iter(final QueryContext ctx) throws QueryException {
switch(sig) {
case _INDEX_TEXTS: return values(ctx, IndexType.TEXT);
case _INDEX_ATTRIBUTES: return values(ctx, IndexType.ATTRIBUTE);
case _INDEX_ELEMENT_NAMES: return names(ctx, IndexType.TAG);
case _INDEX_ATTRIBUTE_NAMES: return names(ctx, IndexType.ATTNAME);
default: return super.iter(ctx);
}
}
/**
* Returns facet information about a database.
* @param ctx query context
* @return facet information
* @throws QueryException query exception
*/
private Item facets(final QueryContext ctx) throws QueryException {
final Data data = data(0, ctx);
if(!data.meta.pathindex)
NOINDEX.thrw(input, data.meta.name, IndexType.PATH);
final boolean flat = expr.length == 2 && eq(checkStr(expr[1], ctx), FLAT);
final NodeCache nc = new NodeCache();
nc.add(flat ? flat(data) : tree(data, data.paths.root().get(0)));
return new FDoc(nc, EMPTY);
}
/**
* Returns all entries of the specified value index.
* @param ctx query context
* @param it index type
* @return text entries
* @throws QueryException query exception
*/
private Iter values(final QueryContext ctx, final IndexType it)
throws QueryException {
final Data data = data(0, ctx);
final byte[] prefix = expr.length < 2 ? EMPTY : checkStr(expr[1], ctx);
return entries(data, prefix, it, this);
}
/**
* Returns all entries of the specified value index.
* @param data data reference
* @param prefix prefix
* @param it index type
* @param call calling function
* @return text entries
* @throws QueryException query exception
*/
static Iter entries(final Data data, final byte[] prefix, final IndexType it,
final StandardFunc call) throws QueryException {
final Index index;
final boolean avl;
if(it == IndexType.TEXT) {
index = data.txtindex;
avl = data.meta.textindex;
} else if(it == IndexType.ATTRIBUTE) {
index = data.atvindex;
avl = data.meta.attrindex;
} else {
index = data.ftxindex;
avl = data.meta.ftxtindex;
}
if(!avl) NOINDEX.thrw(call.input, data.meta.name, it);
final TokenIntMap entries = index.entries(prefix);
return new ValueIter() {
final int es = entries.size();
int pos;
@Override
public ANode get(final long i) {
final FElem elem = new FElem(Q_VALUE);
elem.add(new FAttr(Q_COUNT, token(entries.value((int) i + 1))));
elem.add(new FTxt(entries.key((int) i + 1)));
return elem;
}
@Override
public ANode next() { return pos < size() ? get(pos++) : null; }
@Override
public boolean reset() { pos = 0; return true; }
@Override
public long size() { return es; }
};
}
/**
* Returns all entries of the specified name index.
* @param ctx query context
* @param it index type
* @return text entries
* @throws QueryException query exception
*/
private Iter names(final QueryContext ctx, final IndexType it)
throws QueryException {
final Data data = data(0, ctx);
final Index index = it == IndexType.TAG ? data.tagindex : data.atnindex;
final TokenIntMap entries = index.entries(EMPTY);
return new ValueIter() {
final int es = entries.size();
int pos;
@Override
public ANode get(final long i) {
final FElem elem = new FElem(Q_VALUE);
elem.add(new FAttr(Q_COUNT, token(entries.value((int) i + 1))));
elem.add(new FTxt(entries.key((int) i + 1)));
return elem;
}
@Override
public ANode next() { return pos < size() ? get(pos++) : null; }
@Override
public boolean reset() { pos = 0; return true; }
@Override
public long size() { return es; }
};
}
/**
* Returns a flat facet representation.
* @param data data reference
* @return element
*/
private static FElem flat(final Data data) {
final FElem elem = new FElem(new QNm(NodeType.DOC.string()));
index(data.tagindex, Q_ELM, elem);
index(data.atnindex, Q_ATT, elem);
return elem;
}
/**
* Evaluates name index information.
* @param names name index
* @param name element name
* @param root root node
*/
private static void index(final Names names, final QNm name,
final FElem root) {
for(int i = 0; i < names.size(); ++i) {
final FElem sub = new FElem(name);
sub.add(new FAttr(Q_NAME, names.key(i + 1)));
stats(names.stat(i + 1), sub);
root.add(sub);
}
}
/**
* Returns tree facet representation.
* @param data data reference
* @param root root node
* @return element
*/
private static FElem tree(final Data data, final PathNode root) {
final FElem elem = new FElem(new QNm(ANode.type(root.kind).string()));
final boolean elm = root.kind == Data.ELEM;
final Names names = elm ? data.tagindex : data.atnindex;
if(root.kind == Data.ATTR || elm) {
elem.add(new FAttr(Q_NAME, names.key(root.name)));
}
stats(root.stats, elem);
for(final PathNode p : root.ch) elem.add(tree(data, p));
return elem;
}
/**
* Attaches statistical information to the specified element.
* @param stats statistics
* @param elem element
*/
private static void stats(final Stats stats, final FElem elem) {
final String k = stats.type.toString().toLowerCase(Locale.ENGLISH);
elem.add(new FAttr(Q_TYPE, token(k)));
elem.add(new FAttr(Q_COUNT, token(stats.count)));
switch(stats.type) {
case CATEGORY:
for(final byte[] c : stats.cats) {
final FElem sub = new FElem(Q_VALUE);
sub.add(new FAttr(Q_COUNT, token(stats.cats.value(c))));
sub.add(new FTxt(c));
elem.add(sub);
}
break;
case DOUBLE:
case INTEGER:
elem.add(new FAttr(Q_MIN, token(stats.min)));
elem.add(new FAttr(Q_MAX, token(stats.max)));
break;
default:
break;
}
}
@Override
public boolean uses(final Use u) {
return
// skip evaluation at compile time
u == Use.CTX && (sig == Function._INDEX_TEXTS ||
sig == Function._INDEX_ATTRIBUTES) || super.uses(u);
}
}