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 */
017package org.apache.commons.jxpath.ri.compiler;
018
019import org.apache.commons.jxpath.Pointer;
020import org.apache.commons.jxpath.ri.Compiler;
021import org.apache.commons.jxpath.ri.EvalContext;
022import org.apache.commons.jxpath.ri.QName;
023import org.apache.commons.jxpath.ri.axes.AncestorContext;
024import org.apache.commons.jxpath.ri.axes.AttributeContext;
025import org.apache.commons.jxpath.ri.axes.ChildContext;
026import org.apache.commons.jxpath.ri.axes.DescendantContext;
027import org.apache.commons.jxpath.ri.axes.InitialContext;
028import org.apache.commons.jxpath.ri.axes.NamespaceContext;
029import org.apache.commons.jxpath.ri.axes.ParentContext;
030import org.apache.commons.jxpath.ri.axes.PrecedingOrFollowingContext;
031import org.apache.commons.jxpath.ri.axes.PredicateContext;
032import org.apache.commons.jxpath.ri.axes.SelfContext;
033import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
034import org.apache.commons.jxpath.ri.axes.UnionContext;
035import org.apache.commons.jxpath.ri.model.NodePointer;
036
037/**
038 * @author Dmitri Plotnikov
039 * @version $Revision: 679912 $ $Date: 2008-07-26 00:23:10 +0200 (Sa, 26 Jul 2008) $
040 */
041public 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}