001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jxpath.ri.compiler;
017:
018: import org.apache.commons.jxpath.ri.EvalContext;
019: import org.apache.commons.jxpath.ri.axes.InitialContext;
020: import org.apache.commons.jxpath.ri.axes.NodeSetContext;
021: import org.apache.commons.jxpath.ri.axes.PredicateContext;
022: import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
023: import org.apache.commons.jxpath.ri.axes.UnionContext;
024: import org.apache.commons.jxpath.ri.model.NodePointer;
025:
026: /**
027: * An element of the parse tree that represents an expression path, which is a
028: * path that starts with an expression like a function call: <code>getFoo(.)
029: * /bar</code>.
030: *
031: * @author Dmitri Plotnikov
032: * @version $Revision: 1.11 $ $Date: 2004/02/29 14:17:39 $
033: */
034: public class ExpressionPath extends Path {
035:
036: private Expression expression;
037: private Expression predicates[];
038:
039: private boolean basicKnown = false;
040: private boolean basic;
041:
042: public ExpressionPath(Expression expression,
043: Expression[] predicates, Step[] steps) {
044: super (steps);
045: this .expression = expression;
046: this .predicates = predicates;
047: }
048:
049: public Expression getExpression() {
050: return expression;
051: }
052:
053: /**
054: * Predicates are the expressions in brackets that may follow
055: * the root expression of the path.
056: */
057: public Expression[] getPredicates() {
058: return predicates;
059: }
060:
061: /**
062: * Returns true if the root expression or any of the
063: * predicates or the path steps are context dependent.
064: */
065: public boolean computeContextDependent() {
066: if (expression.isContextDependent()) {
067: return true;
068: }
069: if (predicates != null) {
070: for (int i = 0; i < predicates.length; i++) {
071: if (predicates[i].isContextDependent()) {
072: return true;
073: }
074: }
075: }
076: return super .computeContextDependent();
077: }
078:
079: /**
080: * Recognized paths formatted as <code>$x[3]/foo[2]</code>. The
081: * evaluation of such "simple" paths is optimized and streamlined.
082: */
083: public boolean isSimpleExpressionPath() {
084: if (!basicKnown) {
085: basicKnown = true;
086: basic = isSimplePath()
087: && areBasicPredicates(getPredicates());
088: }
089: return basic;
090: }
091:
092: public String toString() {
093: StringBuffer buffer = new StringBuffer();
094: if (expression instanceof CoreOperation
095: || expression instanceof ExpressionPath
096: || expression instanceof LocationPath) {
097: buffer.append('(');
098: buffer.append(expression);
099: buffer.append(')');
100: } else {
101: buffer.append(expression);
102: }
103: if (predicates != null) {
104: for (int i = 0; i < predicates.length; i++) {
105: buffer.append('[');
106: buffer.append(predicates[i]);
107: buffer.append(']');
108: }
109: }
110:
111: Step steps[] = getSteps();
112: if (steps != null) {
113: for (int i = 0; i < steps.length; i++) {
114: buffer.append("/");
115: buffer.append(steps[i]);
116: }
117: }
118: return buffer.toString();
119: }
120:
121: public Object compute(EvalContext context) {
122: return expressionPath(context, false);
123: }
124:
125: public Object computeValue(EvalContext context) {
126: return expressionPath(context, true);
127: }
128:
129: /**
130: * Walks an expression path (a path that starts with an expression)
131: */
132: protected Object expressionPath(EvalContext evalContext,
133: boolean firstMatch) {
134: Object value = expression.compute(evalContext);
135: EvalContext context;
136: if (value instanceof InitialContext) {
137: // This is an optimization. We can avoid iterating through a
138: // collection if the context bean is in fact one.
139: context = (InitialContext) value;
140: } else if (value instanceof EvalContext) {
141: // UnionContext will collect all values from the "value" context
142: // and treat the whole thing as a big collection.
143: context = new UnionContext(evalContext,
144: new EvalContext[] { (EvalContext) value });
145: } else {
146: context = evalContext.getRootContext().getConstantContext(
147: value);
148: }
149:
150: if (firstMatch && isSimpleExpressionPath()
151: && !(context instanceof NodeSetContext)) {
152: EvalContext ctx = context;
153: NodePointer ptr = (NodePointer) ctx.getSingleNodePointer();
154: if (ptr != null
155: && (ptr.getIndex() == NodePointer.WHOLE_COLLECTION
156: || predicates == null || predicates.length == 0)) {
157: return SimplePathInterpreter
158: .interpretSimpleExpressionPath(evalContext,
159: ptr, predicates, getSteps());
160: }
161: }
162: if (predicates != null) {
163: for (int j = 0; j < predicates.length; j++) {
164: context = new PredicateContext(context, predicates[j]);
165: }
166: }
167: if (firstMatch) {
168: return getSingleNodePointerForSteps(context);
169: } else {
170: return evalSteps(context);
171: }
172: }
173: }
|