    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.jxpath.ri.axes;
    019
    020import java.util.Iterator;
    021
    022import org.apache.commons.jxpath.ri.EvalContext;
    023import org.apache.commons.jxpath.ri.InfoSetUtil;
    024import org.apache.commons.jxpath.ri.compiler.Expression;
    025import org.apache.commons.jxpath.ri.compiler.NameAttributeTest;
    026import org.apache.commons.jxpath.ri.model.NodePointer;
    027import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
    028import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
    029
    030/**
    031 * EvalContext that checks predicates.
    032 */
    033public class PredicateContext extends EvalContext {
    034
    035    private final Expression expression;
    036    private boolean done;
    037    private Expression nameTestExpression;
    038    private PropertyPointer dynamicPropertyPointer;
    039
    040    /**
    041     * Constructs a new PredicateContext.
    042     *
    043     * @param parentContext parent context
    044     * @param expression    compiled Expression
    045     */
    046    public PredicateContext(final EvalContext parentContext, final Expression expression) {
    047        super(parentContext);
    048        this.expression = expression;
    049        if (expression instanceof NameAttributeTest) {
    050            nameTestExpression = ((NameAttributeTest) expression).getNameTestExpression();
    051        }
    052    }
    053
    054    @Override
    055    public NodePointer getCurrentNodePointer() {
    056        if (position == 0 && !setPosition(1)) {
    057            return null;
    058        }
    059        if (dynamicPropertyPointer != null) {
    060            return dynamicPropertyPointer.getValuePointer();
    061        }
    062        return parentContext.getCurrentNodePointer();
    063    }
    064
    065    @Override
    066    public boolean nextNode() {
    067        if (done) {
    068            return false;
    069        }
    070        while (parentContext.nextNode()) {
    071            if (setupDynamicPropertyPointer()) {
    072                final Object pred = nameTestExpression.computeValue(parentContext);
    073                final String propertyName = InfoSetUtil.stringValue(pred);
    074                // At this point it would be nice to say:
    075                // dynamicPropertyPointer.setPropertyName(propertyName)
    076                // and then: dynamicPropertyPointer.isActual().
    077                // However some PropertyPointers, e.g. DynamicPropertyPointer
    078                // will declare that any property you ask for is actual.
    079                // That's not acceptable for us: we really need to know
    080                // if the property is currently declared. Thus,
    081                // we'll need to perform a search.
    082                boolean ok = false;
    083                final String[] names = dynamicPropertyPointer.getPropertyNames();
    084                for (final String name : names) {
    085                    if (name.equals(propertyName)) {
    086                        ok = true;
    087                        break;
    088                    }
    089                }
    090                if (ok) {
    091                    dynamicPropertyPointer.setPropertyName(propertyName);
    092                    position++;
    093                    return true;
    094                }
    095            } else {
    096                Object pred = expression.computeValue(parentContext);
    097                if (pred instanceof Iterator) {
    098                    if (!((Iterator) pred).hasNext()) {
    099                        return false;
    100                    }
    101                    pred = ((Iterator) pred).next();
    102                }
    103                if (pred instanceof NodePointer) {
    104                    pred = ((NodePointer) pred).getNode();
    105                }
    106                if (pred instanceof Number) {
    107                    final int pos = (int) InfoSetUtil.doubleValue(pred);
    108                    position++;
    109                    done = true;
    110                    return parentContext.setPosition(pos);
    111                }
    112                if (InfoSetUtil.booleanValue(pred)) {
    113                    position++;
    114                    return true;
    115                }
    116            }
    117        }
    118        return false;
    119    }
    120
    121    @Override
    122    public boolean nextSet() {
    123        reset();
    124        return parentContext.nextSet();
    125    }
    126
    127    @Override
    128    public void reset() {
    129        super.reset();
    130        parentContext.reset();
    131        done = false;
    132    }
    133
    134    @Override
    135    public boolean setPosition(final int position) {
    136        if (nameTestExpression == null) {
    137            return setPositionStandard(position);
    138        }
    139        if (dynamicPropertyPointer == null && !setupDynamicPropertyPointer()) {
    140            return setPositionStandard(position);
    141        }
    142        if (position < 1 || position > dynamicPropertyPointer.getLength()) {
    143            return false;
    144        }
    145        dynamicPropertyPointer.setIndex(position - 1);
    146        return true;
    147    }
    148
    149    /**
    150     * Basic setPosition
    151     *
    152     * @param position to set
    153     * @return whether valid
    154     */
    155    private boolean setPositionStandard(final int position) {
    156        if (this.position > position) {
    157            reset();
    158        }
    159        while (this.position < position) {
    160            if (!nextNode()) {
    161                return false;
    162            }
    163        }
    164        return true;
    165    }
    166
    167    /**
    168     * Used for an optimized access to dynamic properties using the "map[@name = 'name']" syntax
    169     *
    170     * @return whether valid
    171     */
    172    private boolean setupDynamicPropertyPointer() {
    173        if (nameTestExpression == null) {
    174            return false;
    175        }
    176        NodePointer parent = parentContext.getCurrentNodePointer();
    177        if (parent == null) {
    178            return false;
    179        }
    180        parent = parent.getValuePointer();
    181        if (!(parent instanceof PropertyOwnerPointer)) {
    182            return false;
    183        }
    184        dynamicPropertyPointer = (PropertyPointer) ((PropertyOwnerPointer) parent).getPropertyPointer().clone();
    185        return true;
    186    }
    187}