View Javadoc
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  
18  package org.apache.commons.jxpath.ri.compiler;
19  
20  import java.util.Collections;
21  import java.util.Iterator;
22  import java.util.Locale;
23  
24  import org.apache.commons.jxpath.NodeSet;
25  import org.apache.commons.jxpath.Pointer;
26  import org.apache.commons.jxpath.ri.EvalContext;
27  import org.apache.commons.jxpath.ri.QName;
28  import org.apache.commons.jxpath.ri.model.NodePointer;
29  import org.apache.commons.jxpath.util.ValueUtils;
30  
31  /**
32   * Common superclass for several types of nodes in the parse tree. Provides APIs for optimization of evaluation of expressions. Specifically, an expression only
33   * needs to executed once during the evaluation of an XPath if that expression is context-independent. Expression.isContextDependent() provides that hint.
34   */
35  public abstract class Expression {
36  
37      /**
38       * Pointer iterator
39       */
40      public static class PointerIterator implements Iterator {
41  
42          private final Iterator iterator;
43          private final QName qname;
44          private final Locale locale;
45  
46          // to what method does the following comment refer?
47          /**
48           * Constructs a new PointerIterator
49           *
50           * @param it     underlying Iterator
51           * @param qname  name
52           * @param locale Locale
53           * @deprecated Use the method that takes a NamespaceManager
54           */
55          @Deprecated
56          public PointerIterator(final Iterator it, final QName qname, final Locale locale) {
57              this.iterator = it;
58              this.qname = qname;
59              this.locale = locale;
60          }
61  
62          @Override
63          public boolean hasNext() {
64              return iterator.hasNext();
65          }
66  
67          @Override
68          public Object next() {
69              final Object o = iterator.next();
70              return o instanceof Pointer ? o : NodePointer.newNodePointer(qname, o, locale);
71          }
72  
73          /**
74           * Unsupported.
75           */
76          @Override
77          public void remove() {
78              throw new UnsupportedOperationException();
79          }
80      }
81  
82      /**
83       * Value Iterator
84       */
85      public static class ValueIterator implements Iterator {
86  
87          private final Iterator iterator;
88  
89          /**
90           * Constructs a new ValueIterator.
91           *
92           * @param it underlying Iterator, may contain pointers
93           */
94          public ValueIterator(final Iterator it) {
95              this.iterator = it;
96          }
97  
98          @Override
99          public boolean hasNext() {
100             return iterator.hasNext();
101         }
102 
103         @Override
104         public Object next() {
105             final Object o = iterator.next();
106             return o instanceof Pointer ? ((Pointer) o).getValue() : o;
107         }
108 
109         /**
110          * Unsupported.
111          */
112         @Override
113         public void remove() {
114             throw new UnsupportedOperationException();
115         }
116     }
117 
118     /** Zero */
119     protected static final Double ZERO = Double.valueOf(0);
120     /** One */
121     protected static final Double ONE = Double.valueOf(1);
122     /** NaN */
123     protected static final Double NOT_A_NUMBER = Double.valueOf(Double.NaN);
124     private boolean contextDependencyKnown;
125     private boolean contextDependent;
126 
127     /**
128      * Constructs a new instance.
129      */
130     public Expression() {
131         // empty
132     }
133 
134     /**
135      * Evaluates the expression. If the result is a node set, returns the first element of the node set.
136      *
137      * @param context evaluation context
138      * @return Object
139      */
140     public abstract Object compute(EvalContext context);
141 
142     /**
143      * Implemented by subclasses and result is cached by isContextDependent()
144      *
145      * @return calculated context-dependentness as boolean
146      */
147     public abstract boolean computeContextDependent();
148 
149     /**
150      * Evaluates the expression. If the result is a node set, returns the first element of the node set.
151      *
152      * @param context evaluation context
153      * @return Object
154      */
155     public abstract Object computeValue(EvalContext context);
156 
157     /**
158      * Returns true if this expression should be re-evaluated each time the current position in the context changes.
159      *
160      * @return boolean
161      */
162     public synchronized boolean isContextDependent() {
163         if (!contextDependencyKnown) {
164             contextDependent = computeContextDependent();
165             contextDependencyKnown = true;
166         }
167         return contextDependent;
168     }
169 
170     /**
171      * Iterate over the values from the specified context.
172      *
173      * @param context evaluation context
174      * @return value Iterator
175      */
176     public Iterator iterate(final EvalContext context) {
177         final Object result = compute(context);
178         if (result instanceof EvalContext) {
179             return new ValueIterator((EvalContext) result);
180         }
181         if (result instanceof NodeSet) {
182             return new ValueIterator(((NodeSet) result).getPointers().iterator());
183         }
184         return ValueUtils.iterate(result);
185     }
186 
187     /**
188      * Iterate over the pointers from the specified context.
189      *
190      * @param context evaluation context
191      * @return pointer Iterator
192      */
193     public Iterator iteratePointers(final EvalContext context) {
194         final Object result = compute(context);
195         if (result == null) {
196             return Collections.EMPTY_LIST.iterator();
197         }
198         if (result instanceof EvalContext) {
199             return (EvalContext) result;
200         }
201         if (result instanceof NodeSet) {
202             return new PointerIterator(((NodeSet) result).getPointers().iterator(), new QName(null, "value"),
203                     context.getRootContext().getCurrentNodePointer().getLocale());
204         }
205         return new PointerIterator(ValueUtils.iterate(result), new QName(null, "value"), context.getRootContext().getCurrentNodePointer().getLocale());
206     }
207 }