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.ri.EvalContext; 020import org.apache.commons.jxpath.ri.axes.InitialContext; 021import org.apache.commons.jxpath.ri.axes.NodeSetContext; 022import org.apache.commons.jxpath.ri.axes.PredicateContext; 023import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter; 024import org.apache.commons.jxpath.ri.axes.UnionContext; 025import 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 19:46:46 +0200 (Fr, 02 Mai 2008) $ 034 */ 035public 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}