001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jxpath.ri.compiler;
017:
018: import org.apache.commons.jxpath.Pointer;
019: import org.apache.commons.jxpath.ri.Compiler;
020: import org.apache.commons.jxpath.ri.EvalContext;
021: import org.apache.commons.jxpath.ri.QName;
022: import org.apache.commons.jxpath.ri.axes.AncestorContext;
023: import org.apache.commons.jxpath.ri.axes.AttributeContext;
024: import org.apache.commons.jxpath.ri.axes.ChildContext;
025: import org.apache.commons.jxpath.ri.axes.DescendantContext;
026: import org.apache.commons.jxpath.ri.axes.InitialContext;
027: import org.apache.commons.jxpath.ri.axes.NamespaceContext;
028: import org.apache.commons.jxpath.ri.axes.ParentContext;
029: import org.apache.commons.jxpath.ri.axes.PrecedingOrFollowingContext;
030: import org.apache.commons.jxpath.ri.axes.PredicateContext;
031: import org.apache.commons.jxpath.ri.axes.SelfContext;
032: import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
033: import org.apache.commons.jxpath.ri.model.NodePointer;
034:
035: /**
036: * @author Dmitri Plotnikov
037: * @version $Revision: 1.14 $ $Date: 2004/04/01 02:55:32 $
038: */
039: public abstract class Path extends Expression {
040:
041: private Step[] steps;
042: private boolean basicKnown = false;
043: private boolean basic;
044:
045: public Path(Step[] steps) {
046: this .steps = steps;
047: }
048:
049: public Step[] getSteps() {
050: return steps;
051: }
052:
053: public boolean computeContextDependent() {
054: if (steps != null) {
055: for (int i = 0; i < steps.length; i++) {
056: if (steps[i].isContextDependent()) {
057: return true;
058: }
059: }
060: }
061:
062: return false;
063: }
064:
065: /**
066: * Recognized paths formatted as <code>foo/bar[3]/baz[@name = 'biz']
067: * </code>. The evaluation of such "simple" paths is optimized and
068: * streamlined.
069: */
070: public boolean isSimplePath() {
071: if (!basicKnown) {
072: basicKnown = true;
073: basic = true;
074: Step[] steps = getSteps();
075: for (int i = 0; i < steps.length; i++) {
076: if (!isSimpleStep(steps[i])) {
077: basic = false;
078: break;
079: }
080: }
081: }
082: return basic;
083: }
084:
085: /**
086: * A Step is "simple" if it takes one of these forms: ".", "/foo",
087: * "@bar", "/foo[3]". If there are predicates, they should be
088: * context independent for the step to still be considered simple.
089: */
090: protected boolean isSimpleStep(Step step) {
091: if (step.getAxis() == Compiler.AXIS_SELF) {
092: NodeTest nodeTest = step.getNodeTest();
093: if (!(nodeTest instanceof NodeTypeTest)) {
094: return false;
095: }
096: int nodeType = ((NodeTypeTest) nodeTest).getNodeType();
097: if (nodeType != Compiler.NODE_TYPE_NODE) {
098: return false;
099: }
100: return areBasicPredicates(step.getPredicates());
101: } else if (step.getAxis() == Compiler.AXIS_CHILD
102: || step.getAxis() == Compiler.AXIS_ATTRIBUTE) {
103: NodeTest nodeTest = step.getNodeTest();
104: if (!(nodeTest instanceof NodeNameTest)) {
105: return false;
106: }
107:
108: if (((NodeNameTest) nodeTest).isWildcard()) {
109: return false;
110: }
111: return areBasicPredicates(step.getPredicates());
112: }
113: return false;
114: }
115:
116: protected boolean areBasicPredicates(Expression predicates[]) {
117: if (predicates != null && predicates.length != 0) {
118: boolean firstIndex = true;
119: for (int i = 0; i < predicates.length; i++) {
120: if (predicates[i] instanceof NameAttributeTest) {
121: if (((NameAttributeTest) predicates[i])
122: .getNameTestExpression()
123: .isContextDependent()) {
124: return false;
125: }
126: } else if (predicates[i].isContextDependent()) {
127: return false;
128: } else {
129: if (!firstIndex) {
130: return false;
131: }
132: firstIndex = false;
133: }
134: }
135: }
136: return true;
137: }
138:
139: /**
140: * Given a root context, walks a path therefrom and finds the
141: * pointer to the first element matching the path.
142: */
143: protected Pointer getSingleNodePointerForSteps(EvalContext context) {
144: if (steps.length == 0) {
145: return context.getSingleNodePointer();
146: }
147:
148: if (isSimplePath()) {
149: NodePointer ptr = (NodePointer) context
150: .getSingleNodePointer();
151: return SimplePathInterpreter.interpretSimpleLocationPath(
152: context, ptr, steps);
153: } else {
154: return searchForPath(context);
155: }
156: }
157:
158: /**
159: * The idea here is to return a NullPointer rather than null if that's at
160: * all possible. Take for example this path: "//map/key". Let's say, "map"
161: * is an existing node, but "key" is not there. We will create a
162: * NullPointer that can be used to set/create the "key" property.
163: * <p>
164: * However, a path like "//key" would still produce null, because we have
165: * no way of knowing where "key" would be if it existed.
166: * </p>
167: * <p>
168: * To accomplish this, we first try the path itself. If it does not find
169: * anything, we chop off last step of the path, as long as it is a simple
170: * one like child:: or attribute:: and try to evaluate the truncated path.
171: * If it finds exactly one node - create a NullPointer and return. If it
172: * fails, chop off another step and repeat. If it finds more than one
173: * location - return null.
174: * </p>
175: */
176: private Pointer searchForPath(EvalContext context) {
177: EvalContext ctx = buildContextChain(context, steps.length, true);
178: Pointer pointer = ctx.getSingleNodePointer();
179:
180: if (pointer != null) {
181: return pointer;
182: }
183:
184: for (int i = steps.length; --i > 0;) {
185: if (!isSimpleStep(steps[i])) {
186: return null;
187: }
188: ctx = buildContextChain(context, i, true);
189: if (ctx.hasNext()) {
190: Pointer partial = (Pointer) ctx.next();
191: if (ctx.hasNext()) {
192: // If we find another location - the search is
193: // ambiguous, so we report failure
194: return null;
195: }
196: if (partial instanceof NodePointer) {
197: return SimplePathInterpreter.createNullPointer(
198: context, (NodePointer) partial, steps, i);
199: }
200: }
201: }
202: return null;
203: }
204:
205: /**
206: * Given a root context, walks a path therefrom and builds a context
207: * that contains all nodes matching the path.
208: */
209: protected EvalContext evalSteps(EvalContext context) {
210: return buildContextChain(context, steps.length, false);
211: }
212:
213: private EvalContext buildContextChain(EvalContext context,
214: int stepCount, boolean createInitialContext) {
215: if (createInitialContext) {
216: context = new InitialContext(context);
217: }
218: if (steps.length == 0) {
219: return context;
220: }
221: for (int i = 0; i < stepCount; i++) {
222: context = createContextForStep(context, steps[i].getAxis(),
223: steps[i].getNodeTest());
224: Expression predicates[] = steps[i].getPredicates();
225: if (predicates != null) {
226: for (int j = 0; j < predicates.length; j++) {
227: context = new PredicateContext(context,
228: predicates[j]);
229: }
230: }
231: }
232: return context;
233: }
234:
235: /**
236: * Different axes are serviced by different contexts. This method
237: * allocates the right context for the supplied step.
238: */
239: protected EvalContext createContextForStep(EvalContext context,
240: int axis, NodeTest nodeTest) {
241: if (nodeTest instanceof NodeNameTest) {
242: QName qname = ((NodeNameTest) nodeTest).getNodeName();
243: String prefix = qname.getPrefix();
244: if (prefix != null) {
245: String namespaceURI = context.getJXPathContext()
246: .getNamespaceURI(prefix);
247: nodeTest = new NodeNameTest(qname, namespaceURI);
248: }
249: }
250:
251: switch (axis) {
252: case Compiler.AXIS_ANCESTOR:
253: return new AncestorContext(context, false, nodeTest);
254: case Compiler.AXIS_ANCESTOR_OR_SELF:
255: return new AncestorContext(context, true, nodeTest);
256: case Compiler.AXIS_ATTRIBUTE:
257: return new AttributeContext(context, nodeTest);
258: case Compiler.AXIS_CHILD:
259: return new ChildContext(context, nodeTest, false, false);
260: case Compiler.AXIS_DESCENDANT:
261: return new DescendantContext(context, false, nodeTest);
262: case Compiler.AXIS_DESCENDANT_OR_SELF:
263: return new DescendantContext(context, true, nodeTest);
264: case Compiler.AXIS_FOLLOWING:
265: return new PrecedingOrFollowingContext(context, nodeTest,
266: false);
267: case Compiler.AXIS_FOLLOWING_SIBLING:
268: return new ChildContext(context, nodeTest, true, false);
269: case Compiler.AXIS_NAMESPACE:
270: return new NamespaceContext(context, nodeTest);
271: case Compiler.AXIS_PARENT:
272: return new ParentContext(context, nodeTest);
273: case Compiler.AXIS_PRECEDING:
274: return new PrecedingOrFollowingContext(context, nodeTest,
275: true);
276: case Compiler.AXIS_PRECEDING_SIBLING:
277: return new ChildContext(context, nodeTest, true, true);
278: case Compiler.AXIS_SELF:
279: return new SelfContext(context, nodeTest);
280: }
281: return null; // Never happens
282: }
283: }
|