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.ri.EvalContext;
020    import org.apache.commons.jxpath.ri.axes.InitialContext;
021    import org.apache.commons.jxpath.ri.axes.NodeSetContext;
022    import org.apache.commons.jxpath.ri.axes.PredicateContext;
023    import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
024    import org.apache.commons.jxpath.ri.axes.UnionContext;
025    import org.apache.commons.jxpath.ri.model.NodePointer;
026    
027    /**
028     * An  element of the parse tree that represents an expression path, which is a
029     * path that starts with an expression like a function call: <code>getFoo(.)
030     * /bar</code>.
031     *
032     * @author Dmitri Plotnikov
033     * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
034     */
035    public class ExpressionPath extends Path {
036    
037        private Expression expression;
038        private Expression[] predicates;
039    
040        private boolean basicKnown = false;
041        private boolean basic;
042    
043        /**
044         * Create a new ExpressionPath.
045         * @param expression Expression
046         * @param predicates to execute
047         * @param steps navigation
048         */
049        public ExpressionPath(Expression expression, Expression[] predicates,
050                Step[] steps) {
051            super(steps);
052            this.expression = expression;
053            this.predicates = predicates;
054        }
055    
056        /**
057         * Get the expression.
058         * @return Expression
059         */
060        public Expression getExpression() {
061            return expression;
062        }
063    
064        /**
065         * Predicates are the expressions in brackets that may follow
066         * the root expression of the path.
067         * @return Expression[]
068         */
069        public Expression[] getPredicates() {
070            return predicates;
071        }
072    
073        /**
074         * Returns true if the root expression or any of the
075         * predicates or the path steps are context dependent.
076         * @return boolean
077         */
078        public boolean computeContextDependent() {
079            if (expression.isContextDependent()) {
080                return true;
081            }
082            if (predicates != null) {
083                for (int i = 0; i < predicates.length; i++) {
084                    if (predicates[i].isContextDependent()) {
085                        return true;
086                    }
087                }
088            }
089            return super.computeContextDependent();
090        }
091    
092        /**
093         * Recognized paths formatted as <code>$x[3]/foo[2]</code>.  The
094         * evaluation of such "simple" paths is optimized and streamlined.
095         * @return boolean
096         */
097        public synchronized boolean isSimpleExpressionPath() {
098            if (!basicKnown) {
099                basicKnown = true;
100                basic = isSimplePath() && areBasicPredicates(getPredicates());
101            }
102            return basic;
103        }
104    
105        public String toString() {
106            StringBuffer buffer = new StringBuffer();
107            if (expression instanceof CoreOperation
108                || expression instanceof ExpressionPath
109                || expression instanceof LocationPath) {
110                buffer.append('(');
111                buffer.append(expression);
112                buffer.append(')');
113            }
114            else {
115                buffer.append(expression);
116            }
117            if (predicates != null) {
118                for (int i = 0; i < predicates.length; i++) {
119                    buffer.append('[');
120                    buffer.append(predicates[i]);
121                    buffer.append(']');
122                }
123            }
124    
125            Step[] steps = getSteps();
126            if (steps != null) {
127                for (int i = 0; i < steps.length; i++) {
128                    buffer.append("/");
129                    buffer.append(steps[i]);
130                }
131            }
132            return buffer.toString();
133        }
134    
135        public Object compute(EvalContext context) {
136            return expressionPath(context, false);
137        }
138    
139        public Object computeValue(EvalContext context) {
140            return expressionPath(context, true);
141        }
142    
143        /**
144         * Walks an expression path (a path that starts with an expression)
145         * @param evalContext base context
146         * @param firstMatch whether to return the first match found
147         * @return Object found
148         */
149        protected Object expressionPath(EvalContext evalContext, boolean firstMatch) {
150            Object value = expression.compute(evalContext);
151            EvalContext context;
152            if (value instanceof InitialContext) {
153                // This is an optimization. We can avoid iterating through a
154                // collection if the context bean is in fact one.
155                context = (InitialContext) value;
156            }
157            else if (value instanceof EvalContext) {
158                // UnionContext will collect all values from the "value" context
159                // and treat the whole thing as a big collection.
160                context =
161                    new UnionContext(
162                        evalContext,
163                        new EvalContext[] {(EvalContext) value });
164            }
165            else {
166                context = evalContext.getRootContext().getConstantContext(value);
167            }
168    
169            if (firstMatch
170                && isSimpleExpressionPath()
171                && !(context instanceof NodeSetContext)) {
172                EvalContext ctx = context;
173                NodePointer ptr = (NodePointer) ctx.getSingleNodePointer();
174                if (ptr != null
175                    && (ptr.getIndex() == NodePointer.WHOLE_COLLECTION
176                        || predicates == null
177                        || predicates.length == 0)) {
178                    return SimplePathInterpreter.interpretSimpleExpressionPath(
179                        evalContext,
180                        ptr,
181                        predicates,
182                        getSteps());
183                }
184            }
185            if (predicates != null) {
186                for (int j = 0; j < predicates.length; j++) {
187                    if (j != 0) {
188                        context = new UnionContext(context, new EvalContext[]{context});
189                    }
190                    context = new PredicateContext(context, predicates[j]);
191                }
192            }
193            return firstMatch ? (Object) getSingleNodePointerForSteps(context)
194                    : evalSteps(context);
195        }
196    }