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