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.compiler;
018
019import org.apache.commons.jxpath.NodeSet;
020import org.apache.commons.jxpath.Pointer;
021import org.apache.commons.jxpath.ri.EvalContext;
022import org.apache.commons.jxpath.ri.model.NodePointer;
023import org.apache.commons.jxpath.ri.QName;
024import org.apache.commons.jxpath.util.ValueUtils;
025
026import java.util.Collections;
027import java.util.Iterator;
028import 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 19:46:46 +0200 (Fr, 02 Mai 2008) $
039 */
040public 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}