1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.jxpath.ri.compiler;
18
19 import org.apache.commons.jxpath.NodeSet;
20 import org.apache.commons.jxpath.Pointer;
21 import org.apache.commons.jxpath.ri.EvalContext;
22 import org.apache.commons.jxpath.ri.model.NodePointer;
23 import org.apache.commons.jxpath.ri.QName;
24 import org.apache.commons.jxpath.util.ValueUtils;
25
26 import java.util.Collections;
27 import java.util.Iterator;
28 import java.util.Locale;
29
30 /**
31 * Common superclass for several types of nodes in the parse tree. Provides
32 * APIs for optimization of evaluation of expressions. Specifically, an
33 * expression only needs to executed once during the evaluation of an xpath
34 * if that expression is context-independent. Expression.isContextDependent()
35 * provides that hint.
36 *
37 * @author Dmitri Plotnikov
38 * @version $Revision: 652845 $ $Date: 2008-05-02 13:46:46 -0400 (Fri, 02 May 2008) $
39 */
40 public abstract class Expression {
41
42 /** zero */
43 protected static final Double ZERO = new Double(0);
44
45 /** one */
46 protected static final Double ONE = new Double(1);
47
48 /** NaN */
49 protected static final Double NOT_A_NUMBER = new Double(Double.NaN);
50
51 private boolean contextDependencyKnown = false;
52 private boolean contextDependent;
53
54 /**
55 * Returns true if this expression should be re-evaluated
56 * each time the current position in the context changes.
57 * @return boolean
58 */
59 public synchronized boolean isContextDependent() {
60 if (!contextDependencyKnown) {
61 contextDependent = computeContextDependent();
62 contextDependencyKnown = true;
63 }
64 return contextDependent;
65 }
66
67 /**
68 * Implemented by subclasses and result is cached by isContextDependent()
69 * @return calculated context-dependentness as boolean
70 */
71 public abstract boolean computeContextDependent();
72
73 /**
74 * Evaluates the expression. If the result is a node set, returns
75 * the first element of the node set.
76 * @param context evaluation context
77 * @return Object
78 */
79 public abstract Object computeValue(EvalContext context);
80
81 /**
82 * Evaluates the expression. If the result is a node set, returns
83 * the first element of the node set.
84 * @param context evaluation context
85 * @return Object
86 */
87 public abstract Object compute(EvalContext context);
88
89 /**
90 * Iterate over the values from the specified context.
91 * @param context evaluation context
92 * @return value Iterator
93 */
94 public Iterator iterate(EvalContext context) {
95 Object result = compute(context);
96 if (result instanceof EvalContext) {
97 return new ValueIterator((EvalContext) result);
98 }
99 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 }