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