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  package org.apache.commons.jxpath.ri.compiler;
18  
19  import org.apache.commons.jxpath.Pointer;
20  import org.apache.commons.jxpath.ri.Compiler;
21  import org.apache.commons.jxpath.ri.EvalContext;
22  import org.apache.commons.jxpath.ri.QName;
23  import org.apache.commons.jxpath.ri.axes.AncestorContext;
24  import org.apache.commons.jxpath.ri.axes.AttributeContext;
25  import org.apache.commons.jxpath.ri.axes.ChildContext;
26  import org.apache.commons.jxpath.ri.axes.DescendantContext;
27  import org.apache.commons.jxpath.ri.axes.InitialContext;
28  import org.apache.commons.jxpath.ri.axes.NamespaceContext;
29  import org.apache.commons.jxpath.ri.axes.ParentContext;
30  import org.apache.commons.jxpath.ri.axes.PrecedingOrFollowingContext;
31  import org.apache.commons.jxpath.ri.axes.PredicateContext;
32  import org.apache.commons.jxpath.ri.axes.SelfContext;
33  import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
34  import org.apache.commons.jxpath.ri.axes.UnionContext;
35  import org.apache.commons.jxpath.ri.model.NodePointer;
36  
37  /**
38   * @author Dmitri Plotnikov
39   * @version $Revision: 679912 $ $Date: 2008-07-25 18:23:10 -0400 (Fri, 25 Jul 2008) $
40   */
41  public abstract class Path extends Expression {
42  
43      private Step[] steps;
44      private boolean basicKnown = false;
45      private boolean basic;
46  
47      /**
48       * Create a new Path.
49       * @param steps that compose the Path
50       */
51      public Path(Step[] steps) {
52          this.steps = steps;
53      }
54  
55      /**
56       * Get the steps.
57       * @return Step[]
58       */
59      public Step[] getSteps() {
60          return steps;
61      }
62  
63      public boolean computeContextDependent() {
64          if (steps != null) {
65              for (int i = 0; i < steps.length; i++) {
66                  if (steps[i].isContextDependent()) {
67                      return true;
68                  }
69              }
70          }
71          return false;
72      }
73  
74      /**
75       * Recognizes paths formatted as <code>foo/bar[3]/baz[@name = 'biz']</code>.
76       * The evaluation of such "simple" paths is optimized and
77       * streamlined.
78       * @return <code>true</code> if this path is simple
79       */
80      public synchronized boolean isSimplePath() {
81          if (!basicKnown) {
82              basicKnown = true;
83              basic = true;
84              Step[] steps = getSteps();
85              for (int i = 0; i < steps.length; i++) {
86                  if (!isSimpleStep(steps[i])) {
87                      basic = false;
88                      break;
89                  }
90              }
91          }
92          return basic;
93      }
94  
95      /**
96       * A Step is "simple" if it takes one of these forms: ".", "/foo",
97       * "@bar", "/foo[3]". If there are predicates, they should be
98       * context independent for the step to still be considered simple.
99       * @param step the step to check
100      * @return boolean
101      */
102     protected boolean isSimpleStep(Step step) {
103         if (step.getAxis() == Compiler.AXIS_SELF) {
104             NodeTest nodeTest = step.getNodeTest();
105             if (!(nodeTest instanceof NodeTypeTest)) {
106                 return false;
107             }
108             int nodeType = ((NodeTypeTest) nodeTest).getNodeType();
109             if (nodeType != Compiler.NODE_TYPE_NODE) {
110                 return false;
111             }
112             return areBasicPredicates(step.getPredicates());
113         }
114         if (step.getAxis() == Compiler.AXIS_CHILD
115                 || step.getAxis() == Compiler.AXIS_ATTRIBUTE) {
116             NodeTest nodeTest = step.getNodeTest();
117             if (!(nodeTest instanceof NodeNameTest)) {
118                 return false;
119             }
120             if (((NodeNameTest) nodeTest).isWildcard()) {
121                 return false;
122             }
123             return areBasicPredicates(step.getPredicates());
124         }
125         return false;
126     }
127 
128     /**
129      * Learn whether the elements of the specified array are "basic" predicates.
130      * @param predicates the Expression[] to check
131      * @return boolean
132      */
133     protected boolean areBasicPredicates(Expression[] predicates) {
134         if (predicates != null && predicates.length != 0) {
135             boolean firstIndex = true;
136             for (int i = 0; i < predicates.length; i++) {
137                 if (predicates[i] instanceof NameAttributeTest) {
138                     if (((NameAttributeTest) predicates[i])
139                         .getNameTestExpression()
140                         .isContextDependent()) {
141                         return false;
142                     }
143                 }
144                 else if (predicates[i].isContextDependent()) {
145                     return false;
146                 }
147                 else {
148                     if (!firstIndex) {
149                         return false;
150                     }
151                     firstIndex = false;
152                 }
153             }
154         }
155         return true;
156     }
157 
158     /**
159      * Given a root context, walks a path therefrom and finds the
160      * pointer to the first element matching the path.
161      * @param context evaluation context
162      * @return Pointer
163      */
164     protected Pointer getSingleNodePointerForSteps(EvalContext context) {
165         if (steps.length == 0) {
166             return context.getSingleNodePointer();
167         }
168 
169         if (isSimplePath()) {
170             NodePointer ptr = (NodePointer) context.getSingleNodePointer();
171             return SimplePathInterpreter.interpretSimpleLocationPath(
172                 context,
173                 ptr,
174                 steps);
175         }
176         return searchForPath(context);
177     }
178 
179     /**
180      * The idea here is to return a NullPointer rather than null if that's at
181      * all possible. Take for example this path: "//map/key". Let's say, "map"
182      * is an existing node, but "key" is not there. We will create a
183      * NullPointer that can be used to set/create the "key" property.
184      * <p>
185      * However, a path like "//key" would still produce null, because we have
186      * no way of knowing where "key" would be if it existed.
187      * </p>
188      * <p>
189      * To accomplish this, we first try the path itself. If it does not find
190      * anything, we chop off last step of the path, as long as it is a simple
191      * one like child:: or attribute:: and try to evaluate the truncated path.
192      * If it finds exactly one node - create a NullPointer and return. If it
193      * fails, chop off another step and repeat. If it finds more than one
194      * location - return null.
195      * </p>
196      * @param context evaluation context
197      * @return Pointer
198      */
199     protected Pointer searchForPath(EvalContext context) {
200         EvalContext ctx = buildContextChain(context, steps.length, true);
201         Pointer pointer = ctx.getSingleNodePointer();
202 
203         if (pointer != null) {
204             return pointer;
205         }
206 
207         for (int i = steps.length; --i > 0;) {
208             if (!isSimpleStep(steps[i])) {
209                 return null;
210             }
211             ctx = buildContextChain(context, i, true);
212             if (ctx.hasNext()) {
213                 Pointer partial = (Pointer) ctx.next();
214                 if (ctx.hasNext()) {
215                     // If we find another location - the search is
216                     // ambiguous, so we report failure
217                     return null;
218                 }
219                 if (partial instanceof NodePointer) {
220                     return SimplePathInterpreter.createNullPointer(
221                             context,
222                             (NodePointer) partial,
223                             steps,
224                             i);
225                 }
226             }
227         }
228         return null;
229     }
230 
231     /**
232      * Given a root context, walks a path therefrom and builds a context
233      * that contains all nodes matching the path.
234      * @param context evaluation context
235      * @return EvaluationContext
236      */
237     protected EvalContext evalSteps(EvalContext context) {
238         return buildContextChain(context, steps.length, false);
239     }
240 
241     /**
242      * Build a context from a chain of contexts.
243      * @param context evaluation context
244      * @param stepCount number of steps to descend
245      * @param createInitialContext whether to create the initial context
246      * @return created context
247      */
248     protected EvalContext buildContextChain(
249             EvalContext context,
250             int stepCount,
251             boolean createInitialContext) {
252         if (createInitialContext) {
253             context = new InitialContext(context);
254         }
255         if (steps.length == 0) {
256             return context;
257         }
258         for (int i = 0; i < stepCount; i++) {
259             context =
260                 createContextForStep(
261                     context,
262                     steps[i].getAxis(),
263                     steps[i].getNodeTest());
264             Expression[] predicates = steps[i].getPredicates();
265             if (predicates != null) {
266                 for (int j = 0; j < predicates.length; j++) {
267                     if (j != 0) {
268                         context = new UnionContext(context, new EvalContext[]{context});
269                     }
270                     context = new PredicateContext(context, predicates[j]);
271                 }
272             }
273         }
274         return context;
275     }
276 
277     /**
278      * Different axes are serviced by different contexts. This method
279      * allocates the right context for the supplied step.
280      * @param context evaluation context
281      * @param axis code
282      * @param nodeTest node test
283      * @return EvalContext
284      */
285     protected EvalContext createContextForStep(
286         EvalContext context,
287         int axis,
288         NodeTest nodeTest) {
289         if (nodeTest instanceof NodeNameTest) {
290             QName qname = ((NodeNameTest) nodeTest).getNodeName();
291             String prefix = qname.getPrefix();
292             if (prefix != null) {
293                 String namespaceURI = context.getJXPathContext()
294                         .getNamespaceURI(prefix);
295                 nodeTest = new NodeNameTest(qname, namespaceURI);
296             }
297         }
298 
299         switch (axis) {
300         case Compiler.AXIS_ANCESTOR :
301             return new AncestorContext(context, false, nodeTest);
302         case Compiler.AXIS_ANCESTOR_OR_SELF :
303             return new AncestorContext(context, true, nodeTest);
304         case Compiler.AXIS_ATTRIBUTE :
305             return new AttributeContext(context, nodeTest);
306         case Compiler.AXIS_CHILD :
307             return new ChildContext(context, nodeTest, false, false);
308         case Compiler.AXIS_DESCENDANT :
309             return new DescendantContext(context, false, nodeTest);
310         case Compiler.AXIS_DESCENDANT_OR_SELF :
311             return new DescendantContext(context, true, nodeTest);
312         case Compiler.AXIS_FOLLOWING :
313             return new PrecedingOrFollowingContext(context, nodeTest, false);
314         case Compiler.AXIS_FOLLOWING_SIBLING :
315             return new ChildContext(context, nodeTest, true, false);
316         case Compiler.AXIS_NAMESPACE :
317             return new NamespaceContext(context, nodeTest);
318         case Compiler.AXIS_PARENT :
319             return new ParentContext(context, nodeTest);
320         case Compiler.AXIS_PRECEDING :
321             return new PrecedingOrFollowingContext(context, nodeTest, true);
322         case Compiler.AXIS_PRECEDING_SIBLING :
323             return new ChildContext(context, nodeTest, true, true);
324         case Compiler.AXIS_SELF :
325             return new SelfContext(context, nodeTest);
326         default:
327             return null; // Never happens
328         }
329     }
330 }