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 }