use of org.exist.dom.persistent.VirtualNodeSet in project exist by eXist-db.
the class Predicate method selectByPosition.
/**
* @param outerSequence the outer sequence
* @param contextSequence the context sequence
* @param mode the mode
* @param innerSeq the inner sequence
*
* @return The result of the positional evaluation of the predicate.
*
* @throws XPathException if an error occurs
*/
private Sequence selectByPosition(final Sequence outerSequence, final Sequence contextSequence, final int mode, final Sequence innerSeq) throws XPathException {
if (outerSequence != null && !outerSequence.isEmpty() && Type.subTypeOf(contextSequence.getItemType(), Type.NODE) && contextSequence.isPersistentSet() && outerSequence.isPersistentSet()) {
final Sequence result = new NewArrayNodeSet();
final NodeSet contextSet = contextSequence.toNodeSet();
switch(mode) {
case Constants.CHILD_AXIS:
case Constants.ATTRIBUTE_AXIS:
case Constants.DESCENDANT_AXIS:
case Constants.DESCENDANT_SELF_AXIS:
case Constants.DESCENDANT_ATTRIBUTE_AXIS:
{
final NodeSet outerNodeSet = outerSequence.toNodeSet();
// TODO: in some cases, especially with in-memory nodes,
// outerSequence.toNodeSet() will generate a document
// which will be different from the one(s) in contextSet
// ancestors will thus be empty :-(
// A special treatment of VirtualNodeSet does not seem to be
// required anymore
final Sequence ancestors = outerNodeSet.selectAncestors(contextSet, true, getExpressionId());
if (contextSet.getDocumentSet().intersection(outerNodeSet.getDocumentSet()).getDocumentCount() == 0) {
LOG.info("contextSet and outerNodeSet don't share any document");
}
final NewArrayNodeSet temp = new NewArrayNodeSet();
for (final SequenceIterator i = ancestors.iterate(); i.hasNext(); ) {
NodeProxy p = (NodeProxy) i.nextItem();
ContextItem contextNode = p.getContext();
temp.reset();
while (contextNode != null) {
if (contextNode.getContextId() == getExpressionId()) {
temp.add(contextNode.getNode());
}
contextNode = contextNode.getNextDirect();
}
p.clearContext(getExpressionId());
// TODO : understand why we sort here...
temp.sortInDocumentOrder();
for (final SequenceIterator j = innerSeq.iterate(); j.hasNext(); ) {
final NumericValue v = (NumericValue) j.nextItem();
// Non integers return... nothing, not even an error !
if (!v.hasFractionalPart() && !v.isZero()) {
// ... whereas we don't want a sorted array here
// TODO : rename this method as getInDocumentOrder ? -pb
p = temp.get(v.getInt() - 1);
if (p != null) {
result.add(p);
}
// TODO : does null make sense here ? Well... sometimes ;-)
}
}
}
break;
}
default:
for (final SequenceIterator i = outerSequence.iterate(); i.hasNext(); ) {
NodeProxy p = (NodeProxy) i.nextItem();
Sequence temp;
boolean reverseAxis = true;
switch(mode) {
case Constants.ANCESTOR_AXIS:
temp = contextSet.selectAncestors(p, false, Expression.IGNORE_CONTEXT);
break;
case Constants.ANCESTOR_SELF_AXIS:
temp = contextSet.selectAncestors(p, true, Expression.IGNORE_CONTEXT);
break;
case Constants.PARENT_AXIS:
// TODO : understand why the contextSet is not involved
// here
// NodeProxy.getParent returns a *theoretical* parent
// which is *not* guaranteed to be in the context set !
temp = p.getParents(Expression.NO_CONTEXT_ID);
break;
case Constants.PRECEDING_AXIS:
temp = contextSet.selectPreceding(p, Expression.IGNORE_CONTEXT);
break;
case Constants.PRECEDING_SIBLING_AXIS:
temp = contextSet.selectPrecedingSiblings(p, Expression.IGNORE_CONTEXT);
break;
case Constants.FOLLOWING_SIBLING_AXIS:
temp = contextSet.selectFollowingSiblings(p, Expression.IGNORE_CONTEXT);
reverseAxis = false;
break;
case Constants.FOLLOWING_AXIS:
temp = contextSet.selectFollowing(p, Expression.IGNORE_CONTEXT);
reverseAxis = false;
break;
case Constants.SELF_AXIS:
temp = p;
reverseAxis = false;
break;
default:
throw new IllegalArgumentException("Tried to test unknown axis");
}
if (!temp.isEmpty()) {
for (final SequenceIterator j = innerSeq.iterate(); j.hasNext(); ) {
final NumericValue v = (NumericValue) j.nextItem();
// Non integers return... nothing, not even an error !
if (!v.hasFractionalPart() && !v.isZero()) {
final int pos = (reverseAxis ? temp.getItemCount() - v.getInt() : v.getInt() - 1);
// Other positions are ignored
if (pos >= 0 && pos < temp.getItemCount()) {
final NodeProxy t = (NodeProxy) temp.itemAt(pos);
// for the current context: filter out those
// context items not selected by the positional predicate
ContextItem ctx = t.getContext();
t.clearContext(Expression.IGNORE_CONTEXT);
while (ctx != null) {
if (ctx.getContextId() == outerContextId) {
if (ctx.getNode().getNodeId().equals(p.getNodeId())) {
t.addContextNode(outerContextId, ctx.getNode());
}
} else {
t.addContextNode(ctx.getContextId(), ctx.getNode());
}
ctx = ctx.getNextDirect();
}
result.add(t);
}
}
}
}
}
}
return result;
} else {
final boolean reverseAxis = Type.subTypeOf(contextSequence.getItemType(), Type.NODE) && (mode == Constants.ANCESTOR_AXIS || mode == Constants.ANCESTOR_SELF_AXIS || mode == Constants.PARENT_AXIS || mode == Constants.PRECEDING_AXIS || mode == Constants.PRECEDING_SIBLING_AXIS);
final Set<NumericValue> set = new TreeSet<>();
final ValueSequence result = new ValueSequence();
for (final SequenceIterator i = innerSeq.iterate(); i.hasNext(); ) {
final NumericValue v = (NumericValue) i.nextItem();
// Non integers return... nothing, not even an error !
if (!v.hasFractionalPart() && !v.isZero()) {
final int pos = (reverseAxis ? contextSequence.getItemCount() - v.getInt() : v.getInt() - 1);
// Other positions are ignored
if (pos >= 0 && pos < contextSequence.getItemCount() && !set.contains(v)) {
result.add(contextSequence.itemAt(pos));
set.add(v);
}
}
}
return result;
}
}
use of org.exist.dom.persistent.VirtualNodeSet in project exist by eXist-db.
the class PathExpr method eval.
@Override
public Sequence eval(Sequence contextSequence, final Item contextItem) throws XPathException {
if (context.getProfiler().isEnabled()) {
context.getProfiler().start(this);
context.getProfiler().message(this, Profiler.DEPENDENCIES, "DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies()));
if (contextSequence != null) {
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence);
}
if (contextItem != null) {
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT ITEM", contextItem.toSequence());
}
}
if (contextItem != null) {
contextSequence = contextItem.toSequence();
}
Sequence result = null;
if (steps.size() == 0) {
result = Sequence.EMPTY_SEQUENCE;
} else {
// we will filter out nodes from the contextSequence
result = contextSequence;
Sequence currentContext = contextSequence;
DocumentSet contextDocs = null;
Expression expr = steps.get(0);
if (expr instanceof VariableReference) {
final Variable var = ((VariableReference) expr).getVariable(new AnalyzeContextInfo(parent, 0));
// TOUNDERSTAND : how null could be possible here ? -pb
if (var != null) {
contextDocs = var.getContextDocs();
}
}
// contextDocs == null *is* significant
setContextDocSet(contextDocs);
// To prevent processing nodes after atomic values...
// TODO : let the parser do it ? -pb
boolean gotAtomicResult = false;
Expression prev = null;
for (Expression step : steps) {
prev = expr;
expr = step;
context.getWatchDog().proceed(expr);
// TODO : maybe this could be detected by the parser ? -pb
if (gotAtomicResult && !Type.subTypeOf(expr.returnsType(), Type.NODE) && // Ugly workaround to allow preceding *text* nodes.
!(expr instanceof EnclosedExpr)) {
throw new XPathException(this, ErrorCodes.XPTY0019, "left operand of '/' must be a node. Got '" + Type.getTypeName(result.getItemType()) + " " + result.getCardinality().getHumanDescription() + "'");
}
// contextDocs == null *is* significant
expr.setContextDocSet(contextDocs);
// switch into single step mode if we are processing in-memory nodes only
final boolean inMemProcessing = currentContext != null && Type.subTypeOf(currentContext.getItemType(), Type.NODE) && !currentContext.isPersistentSet();
// DESIGN : first test the dependency then the result
final int exprDeps = expr.getDependencies();
if (inMemProcessing || ((Dependency.dependsOn(exprDeps, Dependency.CONTEXT_ITEM) || Dependency.dependsOn(exprDeps, Dependency.CONTEXT_POSITION)) && // TODO : reconsider since that may be expensive (type evaluation)
!(this instanceof Predicate && Type.subTypeOfUnion(this.returnsType(), Type.NUMBER)) && currentContext != null && !currentContext.isEmpty())) {
Sequence exprResult = new ValueSequence(Type.subTypeOf(expr.returnsType(), Type.NODE));
((ValueSequence) exprResult).keepUnOrdered(unordered);
// Restore a position which may have been modified by inner expressions
int p = context.getContextPosition();
final Sequence seq = context.getContextSequence();
for (final SequenceIterator iterInner = currentContext.iterate(); iterInner.hasNext(); p++) {
context.setContextSequencePosition(p, seq);
context.getWatchDog().proceed(expr);
final Item current = iterInner.nextItem();
// 0 or 1 item
if (!currentContext.hasMany()) {
exprResult = expr.eval(currentContext, current);
} else {
exprResult.addAll(expr.eval(currentContext, current));
}
}
result = exprResult;
} else {
try {
result = expr.eval(currentContext);
} catch (XPathException ex) {
// enrich exception when information is available
if (ex.getLine() < 1 || ex.getColumn() < 1) {
ex.setLocation(expr.getLine(), expr.getColumn());
}
throw ex;
}
}
// well, no so stupid I think...
if (result != null) {
if (steps.size() > 1 && !(result instanceof VirtualNodeSet) && !(expr instanceof EnclosedExpr) && !result.isEmpty() && !Type.subTypeOf(result.getItemType(), Type.NODE)) {
gotAtomicResult = true;
}
if (steps.size() > 1 && getLastExpression() instanceof Step) {
// remove duplicate nodes if this is a path
// expression with more than one step
result.removeDuplicates();
}
}
if (!staticContext) {
currentContext = result;
}
}
final boolean allowMixedNodesInReturn;
if (prev != null) {
allowMixedNodesInReturn = prev.allowMixedNodesInReturn() | expr.allowMixedNodesInReturn();
} else {
allowMixedNodesInReturn = expr.allowMixedNodesInReturn();
}
if (gotAtomicResult && result != null && !allowMixedNodesInReturn && !Type.subTypeOf(result.getItemType(), Type.ATOMIC)) {
throw new XPathException(this, ErrorCodes.XPTY0018, "Cannot mix nodes and atomic values in the result of a path expression.");
}
}
if (context.getProfiler().isEnabled()) {
context.getProfiler().end(this, "", result);
}
return result;
}
use of org.exist.dom.persistent.VirtualNodeSet in project exist by eXist-db.
the class Query method eval.
public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
if (contextItem != null)
contextSequence = contextItem.toSequence();
if (contextSequence != null && !contextSequence.isPersistentSet())
// in-memory docs won't have an index
return Sequence.EMPTY_SEQUENCE;
NodeSet result;
if (preselectResult == null) {
long start = System.currentTimeMillis();
Sequence input = getArgument(0).eval(contextSequence);
if (!(input instanceof VirtualNodeSet) && input.isEmpty())
result = NodeSet.EMPTY_SET;
else {
NodeSet inNodes = input.toNodeSet();
DocumentSet docs = inNodes.getDocumentSet();
LuceneIndexWorker index = (LuceneIndexWorker) context.getBroker().getIndexController().getWorkerByIndexId(LuceneIndex.ID);
Item key = getKey(contextSequence, contextItem);
List<QName> qnames = null;
if (contextQName != null) {
qnames = new ArrayList<>(1);
qnames.add(contextQName);
}
QueryOptions options = parseOptions(this, contextSequence, contextItem, 3);
try {
if (key != null && Type.subTypeOf(key.getType(), Type.ELEMENT)) {
final Element queryXML = (Element) ((NodeValue) key).getNode();
result = index.query(getExpressionId(), docs, inNodes, qnames, queryXML, NodeSet.ANCESTOR, options);
} else {
final String query = key == null ? null : key.getStringValue();
result = index.query(getExpressionId(), docs, inNodes, qnames, query, NodeSet.ANCESTOR, options);
}
} catch (IOException | org.apache.lucene.queryparser.classic.ParseException e) {
throw new XPathException(this, e.getMessage());
}
}
if (context.getProfiler().traceFunctions()) {
context.getProfiler().traceIndexUsage(context, "lucene", this, PerformanceStats.BASIC_INDEX, System.currentTimeMillis() - start);
}
} else {
// DW: contextSequence can be null
contextStep.setPreloadedData(contextSequence.getDocumentSet(), preselectResult);
result = getArgument(0).eval(contextSequence).toNodeSet();
}
return result;
}
use of org.exist.dom.persistent.VirtualNodeSet in project exist by eXist-db.
the class Lookup method eval.
@Override
public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
if (!canOptimize && fallback != null) {
return fallback.eval(contextSequence, contextItem);
}
if (contextItem != null)
contextSequence = contextItem.toSequence();
if (contextSequence != null && !contextSequence.isPersistentSet()) {
// in-memory docs won't have an index
if (fallback == null) {
return Sequence.EMPTY_SEQUENCE;
} else {
return fallback.eval(contextSequence, contextItem);
}
}
NodeSet result;
if (preselectResult == null) {
long start = System.currentTimeMillis();
Sequence input = getArgument(0).eval(contextSequence);
if (!(input instanceof VirtualNodeSet) && input.isEmpty())
result = NodeSet.EMPTY_SET;
else {
RangeIndexWorker index = (RangeIndexWorker) context.getBroker().getIndexController().getWorkerByIndexId(RangeIndex.ID);
AtomicValue[] keys = getKeys(contextSequence);
if (keys.length == 0) {
return NodeSet.EMPTY_SET;
}
List<QName> qnames = null;
if (contextQName != null) {
qnames = new ArrayList<>(1);
qnames.add(contextQName);
}
final RangeIndex.Operator operator = getOperator();
// throw an exception if substring match operation is applied to collated index
if (usesCollation && !operator.supportsCollation()) {
throw new XPathException(this, RangeIndexModule.EXXQDYFT0001, "Index defines a collation which cannot be " + "used with the '" + operator + "' operation.");
}
try {
NodeSet inNodes = input.toNodeSet();
DocumentSet docs = inNodes.getDocumentSet();
result = index.query(getExpressionId(), docs, inNodes, qnames, keys, operator, NodeSet.ANCESTOR);
} catch (IOException e) {
throw new XPathException(this, e.getMessage());
}
}
if (context.getProfiler().traceFunctions()) {
context.getProfiler().traceIndexUsage(context, "new-range", this, PerformanceStats.BASIC_INDEX, System.currentTimeMillis() - start);
}
// LOG.info("eval plain took " + (System.currentTimeMillis() - start));
} else {
// long start = System.currentTimeMillis();
contextStep.setPreloadedData(preselectResult.getDocumentSet(), preselectResult);
result = getArgument(0).eval(contextSequence).toNodeSet();
// LOG.info("eval took " + (System.currentTimeMillis() - start));
}
return result;
}
use of org.exist.dom.persistent.VirtualNodeSet in project exist by eXist-db.
the class Predicate method selectByNodeSet.
/**
* @param contextSequence the context sequence
*
* @return The result of the node set evaluation of the predicate.
*
* @throws XPathException if an error occurs
*/
private Sequence selectByNodeSet(final Sequence contextSequence) throws XPathException {
final NewArrayNodeSet result = new NewArrayNodeSet();
final NodeSet contextSet = contextSequence.toNodeSet();
final boolean contextIsVirtual = contextSet instanceof VirtualNodeSet;
contextSet.setTrackMatches(false);
final NodeSet nodes = super.eval(contextSet, null).toNodeSet();
/*
* if the predicate expression returns results from the cache we can
* also return the cached result.
*/
if (cached != null && cached.isValid(contextSequence, null) && nodes.isCached()) {
if (context.getProfiler().isEnabled()) {
context.getProfiler().message(this, Profiler.OPTIMIZATIONS, "Using cached results", result);
}
return cached.getResult();
}
DocumentImpl lastDoc = null;
for (final NodeProxy currentNode : nodes) {
int sizeHint = Constants.NO_SIZE_HINT;
if (lastDoc == null || currentNode.getOwnerDocument() != lastDoc) {
lastDoc = currentNode.getOwnerDocument();
sizeHint = nodes.getSizeHint(lastDoc);
}
ContextItem contextItem = currentNode.getContext();
if (contextItem == null) {
throw new XPathException(this, "Internal evaluation error: context is missing for node " + currentNode.getNodeId() + " !");
}
// TODO : review to consider transverse context
while (contextItem != null) {
if (contextItem.getContextId() == getExpressionId()) {
final NodeProxy next = contextItem.getNode();
if (contextIsVirtual || contextSet.contains(next)) {
next.addMatches(currentNode);
result.add(next, sizeHint);
}
}
contextItem = contextItem.getNextDirect();
}
}
if (contextSequence.isCacheable()) {
cached = new CachedResult(contextSequence, null, result);
}
contextSet.setTrackMatches(true);
return result;
}
Aggregations