    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}