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    }