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 */
017package org.apache.commons.jxpath.ri.axes;
018
019import java.util.Iterator;
020
021import org.apache.commons.jxpath.ri.EvalContext;
022import org.apache.commons.jxpath.ri.InfoSetUtil;
023import org.apache.commons.jxpath.ri.compiler.Expression;
024import org.apache.commons.jxpath.ri.compiler.NameAttributeTest;
025import org.apache.commons.jxpath.ri.model.NodePointer;
026import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
027import 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 19:46:46 +0200 (Fr, 02 Mai 2008) $
034 */
035public 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}