    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    package org.apache.commons.jxpath.ri.axes;
    018
    019    import java.util.Iterator;
    020
    021    import org.apache.commons.jxpath.ri.EvalContext;
    022    import org.apache.commons.jxpath.ri.InfoSetUtil;
    023    import org.apache.commons.jxpath.ri.compiler.Expression;
    024    import org.apache.commons.jxpath.ri.compiler.NameAttributeTest;
    025    import org.apache.commons.jxpath.ri.model.NodePointer;
    026    import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
    027    import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
    028
    029    /**
    030     * EvalContext that checks predicates.
    031     *
    032     * @author Dmitri Plotnikov
    033     * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
    034     */
    035    public class PredicateContext extends EvalContext {
    036        private Expression expression;
    037        private boolean done = false;
    038        private Expression nameTestExpression;
    039        private PropertyPointer dynamicPropertyPointer;
    040
    041        /**
    042         * Create a new PredicateContext.
    043         * @param parentContext parent context
    044         * @param expression compiled Expression
    045         */
    046        public PredicateContext(EvalContext parentContext, Expression expression) {
    047            super(parentContext);
    048            this.expression = expression;
    049            if (expression instanceof NameAttributeTest) {
    050                nameTestExpression =
    051                    ((NameAttributeTest) expression).getNameTestExpression();
    052            }
    053        }
    054
    055        public boolean nextNode() {
    056            if (done) {
    057                return false;
    058            }
    059            while (parentContext.nextNode()) {
    060                if (setupDynamicPropertyPointer()) {
    061                    Object pred = nameTestExpression.computeValue(parentContext);
    062                    String propertyName = InfoSetUtil.stringValue(pred);
    063
    064                    // At this point it would be nice to say:
    065                    // dynamicPropertyPointer.setPropertyName(propertyName)
    066                    // and then: dynamicPropertyPointer.isActual().
    067                    // However some PropertyPointers, e.g. DynamicPropertyPointer
    068                    // will declare that any property you ask for is actual.
    069                    // That's not acceptable for us: we really need to know
    070                    // if the property is currently declared. Thus,
    071                    // we'll need to perform a search.
    072                    boolean ok = false;
    073                    String[] names = dynamicPropertyPointer.getPropertyNames();
    074                    for (int i = 0; i < names.length; i++) {
    075                        if (names[i].equals(propertyName)) {
    076                            ok = true;
    077                            break;
    078                        }
    079                    }
    080                    if (ok) {
    081                        dynamicPropertyPointer.setPropertyName(propertyName);
    082                        position++;
    083                        return true;
    084                    }
    085                }
    086                else {
    087                    Object pred = expression.computeValue(parentContext);
    088                    if (pred instanceof Iterator) {
    089                        if (!((Iterator) pred).hasNext()) {
    090                            return false;
    091                        }
    092                        pred = ((Iterator) pred).next();
    093                    }
    094
    095                    if (pred instanceof NodePointer) {
    096                        pred = ((NodePointer) pred).getNode();
    097                    }
    098
    099                    if (pred instanceof Number) {
    100                        int pos = (int) InfoSetUtil.doubleValue(pred);
    101                        position++;
    102                        done = true;
    103                        return parentContext.setPosition(pos);
    104                    }
    105                    if (InfoSetUtil.booleanValue(pred)) {
    106                        position++;
    107                        return true;
    108                    }
    109                }
    110            }
    111            return false;
    112        }
    113
    114        /**
    115         * Used for an optimized access to dynamic properties using the
    116         * "map[@name = 'name']" syntax
    117         * @return whether valid
    118         */
    119        private boolean setupDynamicPropertyPointer() {
    120            if (nameTestExpression == null) {
    121                return false;
    122            }
    123
    124            NodePointer parent = parentContext.getCurrentNodePointer();
    125            if (parent == null) {
    126                return false;
    127            }
    128            parent = parent.getValuePointer();
    129            if (!(parent instanceof PropertyOwnerPointer)) {
    130                return false;
    131            }
    132            dynamicPropertyPointer =
    133                (PropertyPointer) ((PropertyOwnerPointer) parent)
    134                    .getPropertyPointer()
    135                    .clone();
    136            return true;
    137        }
    138
    139        public boolean setPosition(int position) {
    140            if (nameTestExpression == null) {
    141                return setPositionStandard(position);
    142            }
    143            else {
    144                if (dynamicPropertyPointer == null && !setupDynamicPropertyPointer()) {
    145                    return setPositionStandard(position);
    146                }
    147                if (position < 1
    148                    || position > dynamicPropertyPointer.getLength()) {
    149                    return false;
    150                }
    151                dynamicPropertyPointer.setIndex(position - 1);
    152                return true;
    153            }
    154        }
    155
    156        public NodePointer getCurrentNodePointer() {
    157            if (position == 0 && !setPosition(1)) {
    158                return null;
    159            }
    160            if (dynamicPropertyPointer != null) {
    161                return dynamicPropertyPointer.getValuePointer();
    162            }
    163            return parentContext.getCurrentNodePointer();
    164        }
    165
    166        public void reset() {
    167            super.reset();
    168            parentContext.reset();
    169            done = false;
    170        }
    171
    172        public boolean nextSet() {
    173            reset();
    174            return parentContext.nextSet();
    175        }
    176
    177        /**
    178         * Basic setPosition
    179         * @param position to set
    180         * @return whether valid
    181         */
    182        private boolean setPositionStandard(int position) {
    183            if (this.position > position) {
    184                reset();
    185            }
    186
    187            while (this.position < position) {
    188                if (!nextNode()) {
    189                    return false;
    190                }
    191            }
    192            return true;
    193        }
    194    }