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.ri.EvalContext;
20  import org.apache.commons.jxpath.ri.axes.InitialContext;
21  import org.apache.commons.jxpath.ri.axes.NodeSetContext;
22  import org.apache.commons.jxpath.ri.axes.PredicateContext;
23  import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
24  import org.apache.commons.jxpath.ri.axes.UnionContext;
25  import org.apache.commons.jxpath.ri.model.NodePointer;
26  
27  /**
28   * An  element of the parse tree that represents an expression path, which is a
29   * path that starts with an expression like a function call: <code>getFoo(.)
30   * /bar</code>.
31   *
32   * @author Dmitri Plotnikov
33   * @version $Revision: 652845 $ $Date: 2008-05-02 19:46:46 +0200 (Fr, 02 Mai 2008) $
34   */
35  public class ExpressionPath extends Path {
36  
37      private Expression expression;
38      private Expression[] predicates;
39  
40      private boolean basicKnown = false;
41      private boolean basic;
42  
43      /**
44       * Create a new ExpressionPath.
45       * @param expression Expression
46       * @param predicates to execute
47       * @param steps navigation
48       */
49      public ExpressionPath(Expression expression, Expression[] predicates,
50              Step[] steps) {
51          super(steps);
52          this.expression = expression;
53          this.predicates = predicates;
54      }
55  
56      /**
57       * Get the expression.
58       * @return Expression
59       */
60      public Expression getExpression() {
61          return expression;
62      }
63  
64      /**
65       * Predicates are the expressions in brackets that may follow
66       * the root expression of the path.
67       * @return Expression[]
68       */
69      public Expression[] getPredicates() {
70          return predicates;
71      }
72  
73      /**
74       * Returns true if the root expression or any of the
75       * predicates or the path steps are context dependent.
76       * @return boolean
77       */
78      public boolean computeContextDependent() {
79          if (expression.isContextDependent()) {
80              return true;
81          }
82          if (predicates != null) {
83              for (int i = 0; i < predicates.length; i++) {
84                  if (predicates[i].isContextDependent()) {
85                      return true;
86                  }
87              }
88          }
89          return super.computeContextDependent();
90      }
91  
92      /**
93       * Recognized paths formatted as <code>$x[3]/foo[2]</code>.  The
94       * evaluation of such "simple" paths is optimized and streamlined.
95       * @return boolean
96       */
97      public synchronized boolean isSimpleExpressionPath() {
98          if (!basicKnown) {
99              basicKnown = true;
100             basic = isSimplePath() && areBasicPredicates(getPredicates());
101         }
102         return basic;
103     }
104 
105     public String toString() {
106         StringBuffer buffer = new StringBuffer();
107         if (expression instanceof CoreOperation
108             || expression instanceof ExpressionPath
109             || expression instanceof LocationPath) {
110             buffer.append('(');
111             buffer.append(expression);
112             buffer.append(')');
113         }
114         else {
115             buffer.append(expression);
116         }
117         if (predicates != null) {
118             for (int i = 0; i < predicates.length; i++) {
119                 buffer.append('[');
120                 buffer.append(predicates[i]);
121                 buffer.append(']');
122             }
123         }
124 
125         Step[] steps = getSteps();
126         if (steps != null) {
127             for (int i = 0; i < steps.length; i++) {
128                 buffer.append("/");
129                 buffer.append(steps[i]);
130             }
131         }
132         return buffer.toString();
133     }
134 
135     public Object compute(EvalContext context) {
136         return expressionPath(context, false);
137     }
138 
139     public Object computeValue(EvalContext context) {
140         return expressionPath(context, true);
141     }
142 
143     /**
144      * Walks an expression path (a path that starts with an expression)
145      * @param evalContext base context
146      * @param firstMatch whether to return the first match found
147      * @return Object found
148      */
149     protected Object expressionPath(EvalContext evalContext, boolean firstMatch) {
150         Object value = expression.compute(evalContext);
151         EvalContext context;
152         if (value instanceof InitialContext) {
153             // This is an optimization. We can avoid iterating through a
154             // collection if the context bean is in fact one.
155             context = (InitialContext) value;
156         }
157         else if (value instanceof EvalContext) {
158             // UnionContext will collect all values from the "value" context
159             // and treat the whole thing as a big collection.
160             context =
161                 new UnionContext(
162                     evalContext,
163                     new EvalContext[] {(EvalContext) value });
164         }
165         else {
166             context = evalContext.getRootContext().getConstantContext(value);
167         }
168 
169         if (firstMatch
170             && isSimpleExpressionPath()
171             && !(context instanceof NodeSetContext)) {
172             EvalContext ctx = context;
173             NodePointer ptr = (NodePointer) ctx.getSingleNodePointer();
174             if (ptr != null
175                 && (ptr.getIndex() == NodePointer.WHOLE_COLLECTION
176                     || predicates == null
177                     || predicates.length == 0)) {
178                 return SimplePathInterpreter.interpretSimpleExpressionPath(
179                     evalContext,
180                     ptr,
181                     predicates,
182                     getSteps());
183             }
184         }
185         if (predicates != null) {
186             for (int j = 0; j < predicates.length; j++) {
187                 if (j != 0) {
188                     context = new UnionContext(context, new EvalContext[]{context});
189                 }
190                 context = new PredicateContext(context, predicates[j]);
191             }
192         }
193         return firstMatch ? (Object) getSingleNodePointerForSteps(context)
194                 : evalSteps(context);
195     }
196 }