    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    }