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.compiler;
019
020import java.util.Collections;
021import java.util.Iterator;
022import java.util.Locale;
023
024import org.apache.commons.jxpath.NodeSet;
025import org.apache.commons.jxpath.Pointer;
026import org.apache.commons.jxpath.ri.EvalContext;
027import org.apache.commons.jxpath.ri.QName;
028import org.apache.commons.jxpath.ri.model.NodePointer;
029import org.apache.commons.jxpath.util.ValueUtils;
030
031/**
032 * Common superclass for several types of nodes in the parse tree. Provides APIs for optimization of evaluation of expressions. Specifically, an expression only
033 * needs to executed once during the evaluation of an XPath if that expression is context-independent. Expression.isContextDependent() provides that hint.
034 */
035public abstract class Expression {
036
037    /**
038     * Pointer iterator
039     */
040    public static class PointerIterator implements Iterator {
041
042        private final Iterator iterator;
043        private final QName qname;
044        private final Locale locale;
045
046        // to what method does the following comment refer?
047        /**
048         * Constructs a new PointerIterator
049         *
050         * @param it     underlying Iterator
051         * @param qname  name
052         * @param locale Locale
053         * @deprecated Use the method that takes a NamespaceManager
054         */
055        @Deprecated
056        public PointerIterator(final Iterator it, final QName qname, final Locale locale) {
057            this.iterator = it;
058            this.qname = qname;
059            this.locale = locale;
060        }
061
062        @Override
063        public boolean hasNext() {
064            return iterator.hasNext();
065        }
066
067        @Override
068        public Object next() {
069            final Object o = iterator.next();
070            return o instanceof Pointer ? o : NodePointer.newNodePointer(qname, o, locale);
071        }
072
073        /**
074         * Unsupported.
075         */
076        @Override
077        public void remove() {
078            throw new UnsupportedOperationException();
079        }
080    }
081
082    /**
083     * Value Iterator
084     */
085    public static class ValueIterator implements Iterator {
086
087        private final Iterator iterator;
088
089        /**
090         * Constructs a new ValueIterator.
091         *
092         * @param it underlying Iterator, may contain pointers
093         */
094        public ValueIterator(final Iterator it) {
095            this.iterator = it;
096        }
097
098        @Override
099        public boolean hasNext() {
100            return iterator.hasNext();
101        }
102
103        @Override
104        public Object next() {
105            final Object o = iterator.next();
106            return o instanceof Pointer ? ((Pointer) o).getValue() : o;
107        }
108
109        /**
110         * Unsupported.
111         */
112        @Override
113        public void remove() {
114            throw new UnsupportedOperationException();
115        }
116    }
117
118    /** Zero */
119    protected static final Double ZERO = Double.valueOf(0);
120    /** One */
121    protected static final Double ONE = Double.valueOf(1);
122    /** NaN */
123    protected static final Double NOT_A_NUMBER = Double.valueOf(Double.NaN);
124    private boolean contextDependencyKnown;
125    private boolean contextDependent;
126
127    /**
128     * Constructs a new instance.
129     */
130    public Expression() {
131        // empty
132    }
133
134    /**
135     * Evaluates the expression. If the result is a node set, returns the first element of the node set.
136     *
137     * @param context evaluation context
138     * @return Object
139     */
140    public abstract Object compute(EvalContext context);
141
142    /**
143     * Implemented by subclasses and result is cached by isContextDependent()
144     *
145     * @return calculated context-dependentness as boolean
146     */
147    public abstract boolean computeContextDependent();
148
149    /**
150     * Evaluates the expression. If the result is a node set, returns the first element of the node set.
151     *
152     * @param context evaluation context
153     * @return Object
154     */
155    public abstract Object computeValue(EvalContext context);
156
157    /**
158     * Returns true if this expression should be re-evaluated each time the current position in the context changes.
159     *
160     * @return boolean
161     */
162    public synchronized boolean isContextDependent() {
163        if (!contextDependencyKnown) {
164            contextDependent = computeContextDependent();
165            contextDependencyKnown = true;
166        }
167        return contextDependent;
168    }
169
170    /**
171     * Iterate over the values from the specified context.
172     *
173     * @param context evaluation context
174     * @return value Iterator
175     */
176    public Iterator iterate(final EvalContext context) {
177        final Object result = compute(context);
178        if (result instanceof EvalContext) {
179            return new ValueIterator((EvalContext) result);
180        }
181        if (result instanceof NodeSet) {
182            return new ValueIterator(((NodeSet) result).getPointers().iterator());
183        }
184        return ValueUtils.iterate(result);
185    }
186
187    /**
188     * Iterate over the pointers from the specified context.
189     *
190     * @param context evaluation context
191     * @return pointer Iterator
192     */
193    public Iterator iteratePointers(final EvalContext context) {
194        final Object result = compute(context);
195        if (result == null) {
196            return Collections.EMPTY_LIST.iterator();
197        }
198        if (result instanceof EvalContext) {
199            return (EvalContext) result;
200        }
201        if (result instanceof NodeSet) {
202            return new PointerIterator(((NodeSet) result).getPointers().iterator(), new QName(null, "value"),
203                    context.getRootContext().getCurrentNodePointer().getLocale());
204        }
205        return new PointerIterator(ValueUtils.iterate(result), new QName(null, "value"), context.getRootContext().getCurrentNodePointer().getLocale());
206    }
207}