    1   /*
    2    * Copyright 1999-2004 The Apache Software Foundation
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.jxpath.ri.compiler;
    17
    18  import org.apache.commons.jxpath.ri.EvalContext;
    19  import org.apache.commons.jxpath.ri.axes.InitialContext;
    20  import org.apache.commons.jxpath.ri.axes.NodeSetContext;
    21  import org.apache.commons.jxpath.ri.axes.PredicateContext;
    22  import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
    23  import org.apache.commons.jxpath.ri.axes.UnionContext;
    24  import org.apache.commons.jxpath.ri.model.NodePointer;
    25
    26  /***
    27   * An  element of the parse tree that represents an expression path, which is a
    28   * path that starts with an expression like a function call: <code>getFoo(.)
    29   * /bar</code>.
    30   *
    31   * @author Dmitri Plotnikov
    32   * @version $Revision: 1.11 $ $Date: 2004/02/29 14:17:39 $
    33   */
    34  public class ExpressionPath extends Path {
    35
    36      private Expression expression;
    37      private Expression predicates[];
    38
    39      private boolean basicKnown = false;
    40      private boolean basic;
    41
    42      public ExpressionPath(
    43          Expression expression,
    44          Expression[] predicates,
    45          Step[] steps)
    46      {
    47          super(steps);
    48          this.expression = expression;
    49          this.predicates = predicates;
    50      }
    51
    52      public Expression getExpression() {
    53          return expression;
    54      }
    55
    56      /***
    57       * Predicates are the expressions in brackets that may follow
    58       * the root expression of the path.
    59       */
    60      public Expression[] getPredicates() {
    61          return predicates;
    62      }
    63
    64      /***
    65       * Returns true if the root expression or any of the
    66       * predicates or the path steps are context dependent.
    67       */
    68      public boolean computeContextDependent() {
    69          if (expression.isContextDependent()) {
    70              return true;
    71          }
    72          if (predicates != null) {
    73              for (int i = 0; i < predicates.length; i++) {
    74                  if (predicates[i].isContextDependent()) {
    75                      return true;
    76                  }
    77              }
    78          }
    79          return super.computeContextDependent();
    80      }
    81
    82      /***
    83       * Recognized paths formatted as <code>$x[3]/foo[2]</code>.  The
    84       * evaluation of such "simple" paths is optimized and streamlined.
    85       */
    86      public boolean isSimpleExpressionPath() {
    87          if (!basicKnown) {
    88              basicKnown = true;
    89              basic = isSimplePath() && areBasicPredicates(getPredicates());
    90          }
    91          return basic;
    92      }
    93
    94      public String toString() {
    95          StringBuffer buffer = new StringBuffer();
    96          if (expression instanceof CoreOperation
    97              || expression instanceof ExpressionPath
    98              || expression instanceof LocationPath) {
    99              buffer.append('(');
    100             buffer.append(expression);
    101             buffer.append(')');
    102         }
    103         else {
    104             buffer.append(expression);
    105         }
    106         if (predicates != null) {
    107             for (int i = 0; i < predicates.length; i++) {
    108                 buffer.append('[');
    109                 buffer.append(predicates[i]);
    110                 buffer.append(']');
    111             }
    112         }
    113
    114         Step steps[] = getSteps();
    115         if (steps != null) {
    116             for (int i = 0; i < steps.length; i++) {
    117                 buffer.append("/");
    118                 buffer.append(steps[i]);
    119             }
    120         }
    121         return buffer.toString();
    122     }
    123
    124     public Object compute(EvalContext context) {
    125         return expressionPath(context, false);
    126     }
    127
    128     public Object computeValue(EvalContext context) {
    129         return expressionPath(context, true);
    130     }
    131
    132     /***
    133      * Walks an expression path (a path that starts with an expression)
    134      */
    135     protected Object expressionPath(
    136         EvalContext evalContext,
    137         boolean firstMatch)
    138     {
    139         Object value = expression.compute(evalContext);
    140         EvalContext context;
    141         if (value instanceof InitialContext) {
    142             // This is an optimization. We can avoid iterating through a
    143             // collection if the context bean is in fact one.
    144             context = (InitialContext) value;
    145         }
    146         else if (value instanceof EvalContext) {
    147             // UnionContext will collect all values from the "value" context
    148             // and treat the whole thing as a big collection.
    149             context =
    150                 new UnionContext(
    151                     evalContext,
    152                     new EvalContext[] {(EvalContext) value });
    153         }
    154         else {
    155             context = evalContext.getRootContext().getConstantContext(value);
    156         }
    157
    158         if (firstMatch
    159             && isSimpleExpressionPath()
    160             && !(context instanceof NodeSetContext)) {
    161             EvalContext ctx = context;
    162             NodePointer ptr = (NodePointer) ctx.getSingleNodePointer();
    163             if (ptr != null
    164                 && (ptr.getIndex() == NodePointer.WHOLE_COLLECTION
    165                     || predicates == null
    166                     || predicates.length == 0)) {
    167                 return SimplePathInterpreter.interpretSimpleExpressionPath(
    168                     evalContext,
    169                     ptr,
    170                     predicates,
    171                     getSteps());
    172             }
    173         }
    174         if (predicates != null) {
    175             for (int j = 0; j < predicates.length; j++) {
    176                 context = new PredicateContext(context, predicates[j]);
    177             }
    178         }
    179         if (firstMatch) {
    180             return getSingleNodePointerForSteps(context);
    181         }
    182         else {
    183             return evalSteps(context);
    184         }
    185     }
    186 }