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.Pointer;
020 import org.apache.commons.jxpath.ri.Compiler;
021 import org.apache.commons.jxpath.ri.EvalContext;
022 import org.apache.commons.jxpath.ri.QName;
023 import org.apache.commons.jxpath.ri.axes.AncestorContext;
024 import org.apache.commons.jxpath.ri.axes.AttributeContext;
025 import org.apache.commons.jxpath.ri.axes.ChildContext;
026 import org.apache.commons.jxpath.ri.axes.DescendantContext;
027 import org.apache.commons.jxpath.ri.axes.InitialContext;
028 import org.apache.commons.jxpath.ri.axes.NamespaceContext;
029 import org.apache.commons.jxpath.ri.axes.ParentContext;
030 import org.apache.commons.jxpath.ri.axes.PrecedingOrFollowingContext;
031 import org.apache.commons.jxpath.ri.axes.PredicateContext;
032 import org.apache.commons.jxpath.ri.axes.SelfContext;
033 import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
034 import org.apache.commons.jxpath.ri.axes.UnionContext;
035 import org.apache.commons.jxpath.ri.model.NodePointer;
036
037 /**
038 * @author Dmitri Plotnikov
039 * @version $Revision: 681111 $ $Date: 2008-07-30 11:30:29 -0500 (Wed, 30 Jul 2008) $
040 */
041 public abstract class Path extends Expression {
042
043 private Step[] steps;
044 private boolean basicKnown = false;
045 private boolean basic;
046
047 /**
048 * Create a new Path.
049 * @param steps that compose the Path
050 */
051 public Path(Step[] steps) {
052 this.steps = steps;
053 }
054
055 /**
056 * Get the steps.
057 * @return Step[]
058 */
059 public Step[] getSteps() {
060 return steps;
061 }
062
063 public boolean computeContextDependent() {
064 if (steps != null) {
065 for (int i = 0; i < steps.length; i++) {
066 if (steps[i].isContextDependent()) {
067 return true;
068 }
069 }
070 }
071 return false;
072 }
073
074 /**
075 * Recognizes paths formatted as <code>foo/bar[3]/baz[@name = 'biz']</code>.
076 * The evaluation of such "simple" paths is optimized and
077 * streamlined.
078 * @return <code>true</code> if this path is simple
079 */
080 public synchronized boolean isSimplePath() {
081 if (!basicKnown) {
082 basicKnown = true;
083 basic = true;
084 Step[] steps = getSteps();
085 for (int i = 0; i < steps.length; i++) {
086 if (!isSimpleStep(steps[i])) {
087 basic = false;
088 break;
089 }
090 }
091 }
092 return basic;
093 }
094
095 /**
096 * A Step is "simple" if it takes one of these forms: ".", "/foo",
097 * "@bar", "/foo[3]". If there are predicates, they should be
098 * context independent for the step to still be considered simple.
099 * @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 }