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}