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    }