package org.basex.query.expr;
import static org.basex.query.QueryText.*;
import static org.basex.util.Token.*;
import java.io.IOException;
import org.basex.data.Data;
import org.basex.data.MemData;
import org.basex.index.IndexIterator;
import org.basex.index.IndexToken.IndexType;
import org.basex.index.ValuesToken;
import org.basex.io.serial.Serializer;
import org.basex.query.QueryContext;
import org.basex.query.QueryException;
import org.basex.query.func.Function;
import org.basex.query.item.DBNode;
import org.basex.query.item.Item;
import org.basex.query.item.ANode;
import org.basex.query.item.SeqType;
import org.basex.query.item.Str;
import org.basex.query.iter.AxisIter;
import org.basex.query.iter.Iter;
import org.basex.query.iter.NodeCache;
import org.basex.query.iter.NodeIter;
import org.basex.query.util.IndexContext;
import org.basex.util.InputInfo;
/**
* This index class retrieves texts and attribute values from the index.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class IndexAccess extends Single {
/** Index context. */
final IndexContext ictx;
/** Index type. */
final IndexType itype;
/**
* Constructor.
* @param ii input info
* @param e index expression
* @param t access type
* @param ic index context
*/
public IndexAccess(final InputInfo ii, final Expr e, final IndexType t,
final IndexContext ic) {
super(ii, e);
itype = t;
ictx = ic;
type = SeqType.NOD_ZM;
}
@Override
public NodeIter iter(final QueryContext ctx) throws QueryException {
NodeIter[] iter = {};
final Iter ir = ctx.iter(expr);
for(Item it; (it = ir.next()) != null;) {
final int s = iter.length;
final NodeIter[] tmp = new NodeIter[s + 1];
System.arraycopy(iter, 0, tmp, 0, s);
iter = tmp;
iter[s] = index(it.string(input));
}
return iter.length == 0 ? new NodeCache() : iter.length == 1 ? iter[0] :
new Union(input, expr).eval(iter);
}
/**
* Returns an index iterator.
* @param term term to be found
* @return iterator
*/
private AxisIter index(final byte[] term) {
final Data data = ictx.data;
// access index if term is not too long, and if index exists.
// otherwise, scan data sequentially
final IndexIterator ii = term.length <= data.meta.maxlen &&
(itype == IndexType.TEXT ? data.meta.textindex : data.meta.attrindex) ?
data.iter(new ValuesToken(itype, term)) : scan(term);
return new AxisIter() {
final byte kind = itype == IndexType.TEXT ? Data.TEXT : Data.ATTR;
final boolean mem = data instanceof MemData;
@Override
public ANode next() {
while(ii.more()) {
final int p = ii.next();
// main memory instance: check if text is no comment, etc.
if(!mem || data.kind(p) == kind) return new DBNode(data, p, kind);
}
return null;
}
};
}
/**
* Returns scan-based iterator.
* @param val value to be found
* @return node iterator
*/
private IndexIterator scan(final byte[] val) {
return new IndexIterator() {
final byte kind = itype == IndexType.TEXT ? Data.TEXT : Data.ATTR;
final boolean text = itype == IndexType.TEXT;
final Data data = ictx.data;
int pre = -1;
@Override
public double score() {
return -1;
}
@Override
public int next() {
return pre;
}
@Override
public boolean more() {
while(++pre < data.meta.size) {
if(data.kind(pre) == kind && eq(data.text(pre, text), val))
return true;
}
return false;
}
};
}
@Override
public boolean iterable() {
return ictx.iterable;
}
@Override
public void plan(final Serializer ser) throws IOException {
ser.openElement(this, DATA, token(ictx.data.meta.name),
TYP, token(itype.toString()));
expr.plan(ser);
ser.closeElement();
}
@Override
public String toString() {
return (itype == IndexType.TEXT ?
Function._DB_TEXT : Function._DB_ATTRIBUTE).get(input,
Str.get(ictx.data.meta.name), expr).toString();
}
}