Package org.exist.xquery

Source Code of org.exist.xquery.Predicate

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2001-04,  Wolfgang M. Meier (meier@ifs.tu-darmstadt.de)
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Library General Public License
*  as published by the Free Software Foundation; either version 2
*  of the License, or (at your option) any later version.
*
*  This library is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Library General Public License for more details.
*
*  You should have received a copy of the GNU Library General Public
*  License along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
*  $Id$
*/
package org.exist.xquery;

import org.exist.dom.ContextItem;
import org.exist.dom.DocumentImpl;
import org.exist.dom.DocumentSet;
import org.exist.dom.NewArrayNodeSet;
import org.exist.dom.NodeProxy;
import org.exist.dom.NodeSet;
import org.exist.dom.VirtualNodeSet;
import org.exist.xquery.util.ExpressionDumper;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.NumericValue;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceIterator;
import org.exist.xquery.value.Type;
import org.exist.xquery.value.ValueSequence;

import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

/**
* Handles predicate expressions.
*
*@author Wolfgang Meier
*/
public class Predicate extends PathExpr {

    public final static int UNKNOWN = -1;
    public final static int NODE = 0;
    public final static int BOOLEAN = 1;
    public final static int POSITIONAL = 2;

    private CachedResult cached = null;

    private int executionMode = UNKNOWN;

    private int outerContextId;

    private Expression parent;

    public Predicate(XQueryContext context) {
        super(context);
    }

    @Override
    public void addPath(PathExpr path) {
        if (path.getLength() == 1) {
            add(path.getExpression(0));
        } else {
            super.addPath(path);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.exist.xquery.PathExpr#getDependencies()
     */
    public int getDependencies() {
        int deps = 0;
        if (getLength() == 1) {
            deps = getExpression(0).getDependencies();
        } else {
            deps = super.getDependencies();
        }
        return deps;
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.exist.xquery.PathExpr#analyze(org.exist.xquery.AnalyzeContextInfo)
     */
    public void analyze(AnalyzeContextInfo contextInfo) throws XPathException {
        parent = contextInfo.getParent();
        AnalyzeContextInfo newContextInfo = createContext(contextInfo);
        super.analyze(newContextInfo);
        final Expression inner = getExpression(0);
        final int staticReturnType = newContextInfo.getStaticReturnType();
        final int innerType = staticReturnType != Type.ITEM ?
            staticReturnType : inner.returnsType();
        // Case 1: predicate expression returns a node set.
        // Check the returned node set against the context set
        // and return all nodes from the context, for which the
        // predicate expression returns a non-empty sequence.
        if (Type.subTypeOf(innerType, Type.NODE)
                && !Dependency.dependsOn(inner, Dependency.CONTEXT_ITEM)) {
            executionMode = NODE;
        // Case 2: predicate expression returns a unique number and has no
        // dependency with the context item.
        } else if (Type.subTypeOf(innerType, Type.NUMBER) &&
            !Dependency.dependsOn(inner, Dependency.CONTEXT_ITEM) &&
            Cardinality.checkCardinality(inner.getCardinality(), Cardinality.EXACTLY_ONE))
            {executionMode = POSITIONAL;}
        // Case 3: all other cases, boolean evaluation (that can be "promoted" later)
        else
            {executionMode = BOOLEAN;}
        if (executionMode == BOOLEAN) {
            // need to re-analyze:
            newContextInfo = createContext(contextInfo);
            newContextInfo.addFlag(SINGLE_STEP_EXECUTION);
            super.analyze(newContextInfo);
        }
        if (executionMode == POSITIONAL && staticReturnType != Type.ITEM
                && !Dependency.dependsOn(inner, Dependency.CONTEXT_ITEM)) {
            contextInfo.addFlag(POSITIONAL_PREDICATE);
        }
    }

    private AnalyzeContextInfo createContext(AnalyzeContextInfo contextInfo) {
        final AnalyzeContextInfo newContextInfo = new AnalyzeContextInfo(contextInfo);
        // set flag to signal subexpression that we are in a predicate
        newContextInfo.addFlag(IN_PREDICATE);
        newContextInfo.removeFlag(IN_WHERE_CLAUSE); // remove where clause flag
        newContextInfo.removeFlag(DOT_TEST);
        outerContextId = newContextInfo.getContextId();
        newContextInfo.setContextId(getExpressionId());
        newContextInfo.setStaticType(contextInfo.getStaticType());
        newContextInfo.setParent(this);
        return newContextInfo;
    }

    public Sequence preprocess() throws XPathException {
        final Expression inner = steps.size() == 1 ? getExpression(0) : this;
        return inner.eval(null);
    }

    public Boolean matchPredicate(Sequence contextSequence,
            Item contextItem, int mode) 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);}
        }
        boolean result = false;
        final Expression inner = steps.size() == 1 ? getExpression(0) : this;
        if (inner == null)
            {result = false;}
        else {
            int recomputedExecutionMode = executionMode;
            Sequence innerSeq = null;
            // Atomic context sequences :
            if (Type.subTypeOf(contextSequence.getItemType(), Type.ATOMIC)) {
                // We can't have a node set operation : reconsider depending of
                // the inner sequence
                if (executionMode == NODE && !(contextSequence instanceof VirtualNodeSet)) {
                    // (1,2,2,4)[.]
                    if (Type.subTypeOf(contextSequence.getItemType(), Type.NUMBER)) {
                        recomputedExecutionMode = POSITIONAL;
                    } else {
                        recomputedExecutionMode = BOOLEAN;
                    }
                }
                // If there is no dependency on the context item, try a positional promotion
                if (executionMode == BOOLEAN && !Dependency.dependsOn(inner, Dependency.CONTEXT_ITEM) &&
                        // Hack : GeneralComparison lies on its dependencies
                        // TODO : try to remove this since our dependency
                        // computation should now be better
                        !((inner instanceof GeneralComparison) &&
                        ((GeneralComparison) inner).invalidNodeEvaluation)) {
                    innerSeq = inner.eval(contextSequence);
                    // Only if we have an actual *singleton* of numeric items
                    if (innerSeq.hasOne()
                            && Type.subTypeOf(innerSeq.getItemType(), Type.NUMBER)) {
                        recomputedExecutionMode = POSITIONAL;
                    }
                }
            } else if (executionMode == NODE && !contextSequence.isPersistentSet()) {
                recomputedExecutionMode = BOOLEAN;
            } else {
                if (executionMode == BOOLEAN && !Dependency.dependsOn(inner, Dependency.CONTEXT_ITEM)) {
                    /*
                     *
                     * WARNING : this sequence will be evaluated with
                     * preloadable nodesets !
                     */
                    innerSeq = inner.eval(contextSequence);
                    // Try to promote a boolean evaluation to a nodeset one
                    // We are now sure of the inner sequence return type
                    if (Type.subTypeOf(innerSeq.getItemType(), Type.NODE)
                            && innerSeq.isPersistentSet()) {
                        recomputedExecutionMode = NODE;
                    // Try to promote a boolean evaluation to a positional one
                    // Only if we have an actual *singleton* of numeric items
                    } else if (innerSeq.hasOne()
                            && Type.subTypeOf(innerSeq.getItemType(), Type.NUMBER)) {
                        recomputedExecutionMode = POSITIONAL;
                    }
                }
            }
            switch (recomputedExecutionMode) {
            case NODE:
                if (context.getProfiler().isEnabled())
                    {context.getProfiler().message(this,
                        Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION CHOICE", "Node selection");}
                    //TODO: result = selectByNodeSet(contextSequence);
                break;
            case BOOLEAN:
                if (context.getProfiler().isEnabled())
                    {context.getProfiler().message(this,
                        Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION CHOICE", "Boolean evaluation");}
                //TODO: result = evalBoolean(contextSequence, inner);
                break;
            case POSITIONAL:
                if (context.getProfiler().isEnabled())
                    {context.getProfiler().message(this,
                        Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION CHOICE",
                        "Positional evaluation");}
                // In case it hasn't been evaluated above
                if (innerSeq == null) {
                    innerSeq = inner.eval(contextSequence);
                }
                //TODO: result = selectByPosition(outerSequence, contextSequence, mode, innerSeq);
                break;
            default:
                throw new IllegalArgumentException(
                    "Unsupported execution mode: '" + recomputedExecutionMode + "'");
            }
        }
        if (context.getProfiler().isEnabled())
            {context.getProfiler().end(this, "", null);}
        return result;
    }

    public Sequence evalPredicate(Sequence outerSequence,
            Sequence contextSequence, int mode) 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);}
        }
        Sequence result;
        final Expression inner = steps.size() == 1 ? getExpression(0) : this;
        if (inner == null)
            {result = Sequence.EMPTY_SEQUENCE;}
        else {
            if (executionMode == UNKNOWN)
                {executionMode = BOOLEAN;}
            int recomputedExecutionMode = executionMode;
            Sequence innerSeq = null;
            // Atomic context sequences :
            if (Type.subTypeOf(contextSequence.getItemType(), Type.ATOMIC)) {
                // We can't have a node set operation : reconsider depending of
                // the inner sequence
                if (executionMode == NODE && !(contextSequence instanceof VirtualNodeSet)) {
                    // (1,2,2,4)[.]
                    if (Type.subTypeOf(contextSequence.getItemType(), Type.NUMBER)) {
                        recomputedExecutionMode = POSITIONAL;
                    } else {
                        recomputedExecutionMode = BOOLEAN;
                    }
                }
                // If there is no dependency on the context item, try a
                // positional promotion
                if (executionMode == BOOLEAN &&
                        !Dependency.dependsOn(inner, Dependency.CONTEXT_ITEM) &&
                        // Hack : GeneralComparison lies on its dependencies
                        // TODO : try to remove this since our dependency
                        // computation should now be better
                        !((inner instanceof GeneralComparison) &&
                        ((GeneralComparison) inner).invalidNodeEvaluation)) {
                    innerSeq = inner.eval(contextSequence);
                    // Only if we have an actual *singleton* of numeric items
                    if (innerSeq.hasOne()
                        && Type.subTypeOf(innerSeq.getItemType(), Type.NUMBER)) {
                        recomputedExecutionMode = POSITIONAL;
                    }
                }
            } else if (executionMode == NODE && !contextSequence.isPersistentSet()) {
                recomputedExecutionMode = BOOLEAN;
            } else {
                if (executionMode == BOOLEAN &&
                        !Dependency.dependsOn(inner, Dependency.CONTEXT_ITEM)) {
                    /*
                     *
                     * WARNING : this sequence will be evaluated with
                     * preloadable nodesets !
                     */
                    innerSeq = inner.eval(contextSequence);
                    // Try to promote a boolean evaluation to a nodeset one
                    // We are now sure of the inner sequence return type
                    if (Type.subTypeOf(innerSeq.getItemType(), Type.NODE)
                            && innerSeq.isPersistentSet()) {
                        recomputedExecutionMode = NODE;
                        // Try to promote a boolean evaluation to a positional one
                        // Only if we have an actual *singleton* of numeric items
                    } else if (innerSeq.hasOne()
                            && Type.subTypeOf(innerSeq.getItemType(), Type.NUMBER)) {
                        recomputedExecutionMode = POSITIONAL;
                    }
                }
            }
            switch (recomputedExecutionMode) {
            case NODE:
                if (context.getProfiler().isEnabled())
                    {context.getProfiler().message(this,
                        Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION CHOICE", "Node selection");}
                result = selectByNodeSet(contextSequence);
                break;
            case BOOLEAN:
                if (context.getProfiler().isEnabled())
                    {context.getProfiler().message(this,
                        Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION CHOICE", "Boolean evaluation");}
                result = evalBoolean(contextSequence, inner, mode);
                break;
            case POSITIONAL:
                if (context.getProfiler().isEnabled())
                    {context.getProfiler().message(this,
                        Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION CHOICE", "Positional evaluation");}
                // In case it hasn't been evaluated above
                if (innerSeq == null) {
                    innerSeq = inner.eval(contextSequence);
                }
                result = selectByPosition(outerSequence, contextSequence, mode, innerSeq);
                break;
            default:
                throw new IllegalArgumentException(
                    "Unsupported execution mode: '" + recomputedExecutionMode + "'");
            }
        }
        if (context.getProfiler().isEnabled())
            {context.getProfiler().end(this, "", result);}
        return result;
    }

    /**
     * @param contextSequence
     * @param inner
     * @return The result of the boolean evaluation of the predicate.
     * @throws XPathException
     */
    private Sequence evalBoolean(Sequence contextSequence, Expression inner, int mode)
            throws XPathException {
        final Sequence result = new ValueSequence();
        int p;
        if (contextSequence instanceof NodeSet
                && ((NodeSet) contextSequence).getProcessInReverseOrder()) {
            // This one may be expensive...
            p = contextSequence.getItemCount();
            for (final SequenceIterator i = contextSequence.iterate(); i.hasNext(); p--) {
                // 0-based
                context.setContextSequencePosition(p - 1, contextSequence);
                final Item item = i.nextItem();
                final Sequence innerSeq = inner.eval(contextSequence, item);
                if (innerSeq.effectiveBooleanValue())
                    {result.add(item);}
            }
        } else {
            // 0-based
            p = 0;

            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);

            // TODO : is this block also accurate in reverse-order processing ?
            // Compute each position in the boolean-like way...
            // ... but grab some context positions ! -<8-P
            if (Type.subTypeOf(inner.returnsType(), Type.NUMBER)
                    && Dependency.dependsOn(inner, Dependency.CONTEXT_ITEM)) {
                final Set<NumericValue> positions = new TreeSet<NumericValue>();
                for (final SequenceIterator i = contextSequence.iterate(); i.hasNext(); p++) {
                    context.setContextSequencePosition(p, contextSequence);
                    final Item item = i.nextItem();
                    final Sequence innerSeq = inner.eval(contextSequence, item);
                    if (innerSeq.hasOne()) {
                      final NumericValue nv = (NumericValue) innerSeq.itemAt(0);
                      // Non integers return... nothing, not even an error !
                      if (!nv.hasFractionalPart() && !nv.isZero())
                          {positions.add(nv);}
                    }
                    //XXX: else error or nothing?
                }
                for (final NumericValue pos : positions) {
                    final int position = (reverseAxis ? contextSequence.getItemCount() - pos.getInt() : pos.getInt() - 1);
                    // TODO : move this test above ?
                    if (position <= contextSequence.getItemCount())
                        {result.add(contextSequence.itemAt(position));}
                }
            } else {
                final Set<NumericValue> positions = new TreeSet<NumericValue>();
                for (final SequenceIterator i = contextSequence.iterate(); i.hasNext(); p++) {
                    context.setContextSequencePosition(p, contextSequence);
                    final Item item = i.nextItem();
                    final Sequence innerSeq = inner.eval(contextSequence, item);
                    if (innerSeq.hasOne()
                            && Type.subTypeOf(innerSeq.getItemType(), Type.NUMBER)) {
                        // TODO : introduce a check in innerSeq.hasOne() ?
                        final NumericValue nv = (NumericValue) innerSeq;
                        // Non integers return... nothing, not even an error !
                        if (!nv.hasFractionalPart() && !nv.isZero())
                            {positions.add(nv);}
                    } else if (innerSeq.effectiveBooleanValue())
                        {result.add(item);}
                }
                for (final NumericValue pos : positions) {
                    final int position = (reverseAxis ? contextSequence.getItemCount() - pos.getInt() : pos.getInt() - 1);
                    // TODO : move this test above ?
                    if (position <= contextSequence.getItemCount())
                        {result.add(contextSequence.itemAt(position));}
                }
            }
        }
        return result;
    }

    /**
     * @param contextSequence
     * @return The result of the node set evaluation of the predicate.
     * @throws XPathException
     */
    private Sequence selectByNodeSet(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 Iterator<NodeProxy> i = nodes.iterator(); i.hasNext();) {
            final NodeProxy currentNode = i.next();
            int sizeHint = Constants.NO_SIZE_HINT;
            if (lastDoc == null || currentNode.getDocument() != lastDoc) {
                lastDoc = currentNode.getDocument();
                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;
    }

    /**
     * @param outerSequence
     * @param contextSequence
     * @param mode
     * @param innerSeq
     * @return The result of the positional evaluation of the predicate.
     * @throws XPathException
     */
    private Sequence selectByPosition(Sequence outerSequence,
            Sequence contextSequence, int mode, Sequence innerSeq)
            throws XPathException {
        if (outerSequence != null && !outerSequence.isEmpty()
                && Type.subTypeOf(contextSequence.getItemType(), Type.NODE)
                && contextSequence.isPersistentSet()
                && outerSequence.isPersistentSet()) {
            final Sequence result = new NewArrayNodeSet(100);
            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(100);
                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<NumericValue>();
            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;
        }
    }

    public void setContextDocSet(DocumentSet contextSet) {
        super.setContextDocSet(contextSet);
        if (getLength() > 0)
            {getExpression(0).setContextDocSet(contextSet);}
    }

    public int getExecutionMode() {
        return executionMode;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.exist.xquery.PathExpr#resetState()
     */
    public void resetState(boolean postOptimization) {
        super.resetState(postOptimization);
        if (!postOptimization)
            {cached = null;}
    }

    public Expression getParent() {
        return parent;
    }

    public void accept(ExpressionVisitor visitor) {
        visitor.visitPredicate(this);
    }

    public void dump(ExpressionDumper dumper) {
        dumper.display("[");
        super.dump(dumper);
        dumper.display("]");
    }

    public String toString() {
        return "[" + super.toString() + "]";
    }

    @Override
    public Expression simplify() {
        return this;
    }
}
TOP

Related Classes of org.exist.xquery.Predicate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.