1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
198
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 prefix = qname.getPrefix();
260 if (prefix != null) {
261 String 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;
296 }
297 }