KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > jxpath > ri > compiler > Path


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.commons.jxpath.ri.compiler;
17
18 import org.apache.commons.jxpath.Pointer;
19 import org.apache.commons.jxpath.ri.Compiler;
20 import org.apache.commons.jxpath.ri.EvalContext;
21 import org.apache.commons.jxpath.ri.QName;
22 import org.apache.commons.jxpath.ri.axes.AncestorContext;
23 import org.apache.commons.jxpath.ri.axes.AttributeContext;
24 import org.apache.commons.jxpath.ri.axes.ChildContext;
25 import org.apache.commons.jxpath.ri.axes.DescendantContext;
26 import org.apache.commons.jxpath.ri.axes.InitialContext;
27 import org.apache.commons.jxpath.ri.axes.NamespaceContext;
28 import org.apache.commons.jxpath.ri.axes.ParentContext;
29 import org.apache.commons.jxpath.ri.axes.PrecedingOrFollowingContext;
30 import org.apache.commons.jxpath.ri.axes.PredicateContext;
31 import org.apache.commons.jxpath.ri.axes.SelfContext;
32 import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
33 import org.apache.commons.jxpath.ri.model.NodePointer;
34
35 /**
36  * @author Dmitri Plotnikov
37  * @version $Revision: 1.14 $ $Date: 2004/04/01 02:55:32 $
38  */

39 public abstract class Path extends Expression {
40
41     private Step[] steps;
42     private boolean basicKnown = false;
43     private boolean basic;
44
45     public Path(Step[] steps) {
46         this.steps = steps;
47     }
48
49     public Step[] getSteps() {
50         return steps;
51     }
52
53     public boolean computeContextDependent() {
54         if (steps != null) {
55             for (int i = 0; i < steps.length; i++) {
56                 if (steps[i].isContextDependent()) {
57                     return true;
58                 }
59             }
60         }
61
62         return false;
63     }
64
65     /**
66      * Recognized paths formatted as <code>foo/bar[3]/baz[@name = 'biz']
67      * </code>. The evaluation of such "simple" paths is optimized and
68      * streamlined.
69      */

70     public boolean isSimplePath() {
71         if (!basicKnown) {
72             basicKnown = true;
73             basic = true;
74             Step[] steps = getSteps();
75             for (int i = 0; i < steps.length; i++) {
76                 if (!isSimpleStep(steps[i])){
77                     basic = false;
78                     break;
79                 }
80             }
81         }
82         return basic;
83     }
84
85     /**
86      * A Step is "simple" if it takes one of these forms: ".", "/foo",
87      * "@bar", "/foo[3]". If there are predicates, they should be
88      * context independent for the step to still be considered simple.
89      */

90     protected boolean isSimpleStep(Step step) {
91         if (step.getAxis() == Compiler.AXIS_SELF) {
92             NodeTest nodeTest = step.getNodeTest();
93             if (!(nodeTest instanceof NodeTypeTest)) {
94                 return false;
95             }
96             int nodeType = ((NodeTypeTest) nodeTest).getNodeType();
97             if (nodeType != Compiler.NODE_TYPE_NODE) {
98                 return false;
99             }
100             return areBasicPredicates(step.getPredicates());
101         }
102         else if (step.getAxis() == Compiler.AXIS_CHILD
103                 || step.getAxis() == Compiler.AXIS_ATTRIBUTE) {
104             NodeTest nodeTest = step.getNodeTest();
105             if (!(nodeTest instanceof NodeNameTest)){
106                 return false;
107             }
108             
109             if (((NodeNameTest) nodeTest).isWildcard()) {
110                 return false;
111             }
112             return areBasicPredicates(step.getPredicates());
113         }
114         return false;
115     }
116
117     protected boolean areBasicPredicates(Expression predicates[]) {
118         if (predicates != null && predicates.length != 0) {
119             boolean firstIndex = true;
120             for (int i = 0; i < predicates.length; i++) {
121                 if (predicates[i] instanceof NameAttributeTest) {
122                     if (((NameAttributeTest) predicates[i])
123                         .getNameTestExpression()
124                         .isContextDependent()) {
125                         return false;
126                     }
127                 }
128                 else if (predicates[i].isContextDependent()) {
129                     return false;
130                 }
131                 else {
132                     if (!firstIndex) {
133                         return false;
134                     }
135                     firstIndex = false;
136                 }
137             }
138         }
139         return true;
140     }
141
142     /**
143      * Given a root context, walks a path therefrom and finds the
144      * pointer to the first element matching the path.
145      */

146     protected Pointer getSingleNodePointerForSteps(EvalContext context) {
147         if (steps.length == 0) {
148             return context.getSingleNodePointer();
149         }
150
151         if (isSimplePath()) {
152             NodePointer ptr = (NodePointer) context.getSingleNodePointer();
153             return SimplePathInterpreter.interpretSimpleLocationPath(
154                 context,
155                 ptr,
156                 steps);
157         }
158         else {
159             return searchForPath(context);
160         }
161     }
162
163     /**
164      * The idea here is to return a NullPointer rather than null if that's at
165      * all possible. Take for example this path: "//map/key". Let's say, "map"
166      * is an existing node, but "key" is not there. We will create a
167      * NullPointer that can be used to set/create the "key" property.
168      * <p>
169      * However, a path like "//key" would still produce null, because we have
170      * no way of knowing where "key" would be if it existed.
171      * </p>
172      * <p>
173      * To accomplish this, we first try the path itself. If it does not find
174      * anything, we chop off last step of the path, as long as it is a simple
175      * one like child:: or attribute:: and try to evaluate the truncated path.
176      * If it finds exactly one node - create a NullPointer and return. If it
177      * fails, chop off another step and repeat. If it finds more than one
178      * location - return null.
179      * </p>
180      */

181     private Pointer searchForPath(EvalContext context) {
182         EvalContext ctx = buildContextChain(context, steps.length, true);
183         Pointer pointer = ctx.getSingleNodePointer();
184         
185         if (pointer != null) {
186             return pointer;
187         }
188         
189         for (int i = steps.length; --i > 0;) {
190             if (!isSimpleStep(steps[i])) {
191                 return null;
192             }
193             ctx = buildContextChain(context, i, true);
194             if (ctx.hasNext()) {
195                 Pointer partial = (Pointer) ctx.next();
196                 if (ctx.hasNext()) {
197                     // If we find another location - the search is
198
// ambiguous, so we report failure
199
return null;
200                 }
201                 if (partial instanceof NodePointer) {
202                     return SimplePathInterpreter.createNullPointer(
203                             context,
204                             (NodePointer) partial,
205                             steps,
206                             i);
207                 }
208             }
209         }
210         return null;
211     }
212
213     /**
214      * Given a root context, walks a path therefrom and builds a context
215      * that contains all nodes matching the path.
216      */

217     protected EvalContext evalSteps(EvalContext context) {
218         return buildContextChain(context, steps.length, false);
219     }
220
221     private EvalContext buildContextChain(
222             EvalContext context,
223             int stepCount,
224             boolean createInitialContext)
225     {
226         if (createInitialContext) {
227             context = new InitialContext(context);
228         }
229         if (steps.length == 0) {
230             return context;
231         }
232         for (int i = 0; i < stepCount; i++) {
233             context =
234                 createContextForStep(
235                     context,
236                     steps[i].getAxis(),
237                     steps[i].getNodeTest());
238             Expression predicates[] = steps[i].getPredicates();
239             if (predicates != null) {
240                 for (int j = 0; j < predicates.length; j++) {
241                     context = new PredicateContext(context, predicates[j]);
242                 }
243             }
244         }
245         return context;
246     }
247     
248     /**
249      * Different axes are serviced by different contexts. This method
250      * allocates the right context for the supplied step.
251      */

252     protected EvalContext createContextForStep(
253         EvalContext context,
254         int axis,
255         NodeTest nodeTest)
256     {
257         if (nodeTest instanceof NodeNameTest) {
258             QName qname = ((NodeNameTest) nodeTest).getNodeName();
259             String JavaDoc prefix = qname.getPrefix();
260             if (prefix != null) {
261                 String JavaDoc namespaceURI = context.getJXPathContext()
262                         .getNamespaceURI(prefix);
263                 nodeTest = new NodeNameTest(qname, namespaceURI);
264             }
265         }
266         
267         switch (axis) {
268         case Compiler.AXIS_ANCESTOR :
269             return new AncestorContext(context, false, nodeTest);
270         case Compiler.AXIS_ANCESTOR_OR_SELF :
271             return new AncestorContext(context, true, nodeTest);
272         case Compiler.AXIS_ATTRIBUTE :
273             return new AttributeContext(context, nodeTest);
274         case Compiler.AXIS_CHILD :
275             return new ChildContext(context, nodeTest, false, false);
276         case Compiler.AXIS_DESCENDANT :
277             return new DescendantContext(context, false, nodeTest);
278         case Compiler.AXIS_DESCENDANT_OR_SELF :
279             return new DescendantContext(context, true, nodeTest);
280         case Compiler.AXIS_FOLLOWING :
281             return new PrecedingOrFollowingContext(context, nodeTest, false);
282         case Compiler.AXIS_FOLLOWING_SIBLING :
283             return new ChildContext(context, nodeTest, true, false);
284         case Compiler.AXIS_NAMESPACE :
285             return new NamespaceContext(context, nodeTest);
286         case Compiler.AXIS_PARENT :
287             return new ParentContext(context, nodeTest);
288         case Compiler.AXIS_PRECEDING :
289             return new PrecedingOrFollowingContext(context, nodeTest, true);
290         case Compiler.AXIS_PRECEDING_SIBLING :
291             return new ChildContext(context, nodeTest, true, true);
292         case Compiler.AXIS_SELF :
293             return new SelfContext(context, nodeTest);
294         }
295         return null; // Never happens
296
}
297 }
Popular Tags