Skip to content

Method: getArguments(Node)

1: /*
2: * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package com.sun.el.parser;
18:
19: import java.lang.reflect.Method;
20:
21: import jakarta.el.ELClass;
22: import jakarta.el.ELException;
23: import jakarta.el.ELResolver;
24: import jakarta.el.ImportHandler;
25: import jakarta.el.MethodInfo;
26: import jakarta.el.PropertyNotFoundException;
27: import jakarta.el.PropertyNotWritableException;
28: import jakarta.el.ValueReference;
29:
30: import com.sun.el.lang.ELSupport;
31: import com.sun.el.lang.EvaluationContext;
32: import com.sun.el.util.MessageFactory;
33: import com.sun.el.util.ReflectionUtil;
34:
35: /**
36: * @author Jacob Hookom [jacob@hookom.net]
37: * @author Kin-man Chung
38: * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
39: */
40: public final class AstValue extends SimpleNode {
41:
42: protected static class Target {
43: protected Object base;
44: protected Node suffixNode;
45:
46: Target(Object base, Node suffixNode) {
47: this.base = base;
48: this.suffixNode = suffixNode;
49: }
50:
51: boolean isMethodCall() {
52: return getArguments(suffixNode) != null;
53: }
54: }
55:
56: public AstValue(int id) {
57: super(id);
58: }
59:
60: @Override
61: public Class getType(EvaluationContext ctx) throws ELException {
62: Target t = getTarget(ctx);
63: if (t.isMethodCall()) {
64: return null;
65: }
66: Object property = t.suffixNode.getValue(ctx);
67: ctx.setPropertyResolved(false);
68: Class ret = ctx.getELResolver().getType(ctx, t.base, property);
69: if (!ctx.isPropertyResolved()) {
70: ELSupport.throwUnhandled(t.base, property);
71: }
72: return ret;
73: }
74:
75: @Override
76: public ValueReference getValueReference(EvaluationContext ctx) throws ELException {
77: Target t = getTarget(ctx);
78: if (t.isMethodCall()) {
79: return null;
80: }
81: Object property = t.suffixNode.getValue(ctx);
82: return new ValueReference(t.base, property);
83: }
84:
85: private static AstMethodArguments getArguments(Node n) {
86:• if (n instanceof AstDotSuffix && n.jjtGetNumChildren() > 0) {
87: return (AstMethodArguments) n.jjtGetChild(0);
88: }
89:• if (n instanceof AstBracketSuffix && n.jjtGetNumChildren() > 1) {
90: return (AstMethodArguments) n.jjtGetChild(1);
91: }
92: return null;
93: }
94:
95: private Object getValue(Object base, Node child, EvaluationContext ctx) throws ELException {
96:
97: Object value = null;
98: ELResolver resolver = ctx.getELResolver();
99: Object property = child.getValue(ctx);
100: AstMethodArguments args = getArguments(child);
101: if (args != null) {
102: // This is a method call
103: if (!(property instanceof String)) {
104: throw new ELException(MessageFactory.get("error.method.name", property));
105: }
106: Class<?>[] paramTypes = args.getParamTypes();
107: Object[] params = args.getParameters(ctx);
108:
109: ctx.setPropertyResolved(false);
110: value = resolver.invoke(ctx, base, property, paramTypes, params);
111: } else {
112: if (property != null) {
113: ctx.setPropertyResolved(false);
114: value = resolver.getValue(ctx, base, property);
115: if (!ctx.isPropertyResolved()) {
116: ELSupport.throwUnhandled(base, property);
117: }
118: }
119: }
120: return value;
121: }
122:
123: private Object getBase(EvaluationContext ctx) {
124: try {
125: return this.children[0].getValue(ctx);
126: } catch (PropertyNotFoundException ex) {
127: // Next check if the base is an imported class
128: if (this.children[0] instanceof AstIdentifier) {
129: String name = ((AstIdentifier) this.children[0]).image;
130: ImportHandler importHandler = ctx.getImportHandler();
131: if (importHandler != null) {
132: Class<?> c = importHandler.resolveClass(name);
133: if (c != null) {
134: return new ELClass(c);
135: }
136: }
137: }
138: throw ex;
139: }
140: }
141:
142: private Target getTarget(EvaluationContext ctx) throws ELException {
143: // evaluate expr-a to value-a
144: Object base = getBase(ctx);
145:
146: // if our base is null (we know there are more properites to evaluate)
147: if (base == null) {
148: throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.base", this.children[0].getImage()));
149: }
150:
151: // set up our start/end
152: Object property = null;
153: int propCount = this.jjtGetNumChildren() - 1;
154: int i = 1;
155:
156: // evaluate any properties before our target
157: if (propCount > 1) {
158: while (base != null && i < propCount) {
159: base = getValue(base, this.children[i], ctx);
160: i++;
161: }
162: // if we are in this block, we have more properties to resolve,
163: // but our base was null
164: if (base == null) {
165: throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.property", property));
166: }
167: }
168: return new Target(base, this.children[propCount]);
169: }
170:
171: @Override
172: public Object getValue(EvaluationContext ctx) throws ELException {
173: Object base = getBase(ctx);
174: int propCount = this.jjtGetNumChildren();
175: int i = 1;
176: while (base != null && i < propCount) {
177: base = getValue(base, this.children[i], ctx);
178: i++;
179: }
180: return base;
181: }
182:
183: @Override
184: public boolean isReadOnly(EvaluationContext ctx) throws ELException {
185: Target t = getTarget(ctx);
186: if (t.isMethodCall()) {
187: return true;
188: }
189: Object property = t.suffixNode.getValue(ctx);
190: ctx.setPropertyResolved(false);
191: boolean ret = ctx.getELResolver().isReadOnly(ctx, t.base, property);
192: if (!ctx.isPropertyResolved()) {
193: ELSupport.throwUnhandled(t.base, property);
194: }
195: return ret;
196: }
197:
198: @Override
199: public void setValue(EvaluationContext ctx, Object value) throws ELException {
200: Target t = getTarget(ctx);
201: if (t.isMethodCall()) {
202: throw new PropertyNotWritableException(MessageFactory.get("error.syntax.set"));
203: }
204: Object property = t.suffixNode.getValue(ctx);
205: ELResolver elResolver = ctx.getELResolver();
206:
207: /*
208: * Note by kchung 10/2013 The spec does not say if the value should be cocerced to the target type before setting the
209: * value to the target. The conversion is kept here to be backward compatible.
210: */
211: ctx.setPropertyResolved(false);
212: Class<?> targetType = elResolver.getType(ctx, t.base, property);
213: if (ctx.isPropertyResolved()) {
214: ctx.setPropertyResolved(false);
215: Object targetValue = elResolver.convertToType(ctx, value, targetType);
216:
217: if (ctx.isPropertyResolved()) {
218: value = targetValue;
219: } else {
220: if (value != null || (targetType != null && targetType.isPrimitive())) {
221: value = ELSupport.coerceToType(value, targetType);
222: }
223: }
224: }
225:
226: ctx.setPropertyResolved(false);
227: elResolver.setValue(ctx, t.base, property, value);
228: if (!ctx.isPropertyResolved()) {
229: ELSupport.throwUnhandled(t.base, property);
230: }
231: }
232:
233: @Override
234: public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException {
235: Target t = getTarget(ctx);
236: if (t.isMethodCall()) {
237: return null;
238: }
239: Object property = t.suffixNode.getValue(ctx);
240: Method m = ReflectionUtil.findMethod(t.base.getClass(), property.toString(), paramTypes, null);
241: return new MethodInfo(m.getName(), m.getReturnType(), m.getParameterTypes());
242: }
243:
244: @Override
245: public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException {
246: Target t = getTarget(ctx);
247: if (t.isMethodCall()) {
248: AstMethodArguments args = getArguments(t.suffixNode);
249: // Always use the param types in the expression, and ignore those
250: // specified elsewhere, such as TLD
251: paramTypes = args.getParamTypes();
252: Object[] params = args.getParameters(ctx);
253: String method = (String) t.suffixNode.getValue(ctx);
254:
255: ctx.setPropertyResolved(false);
256: ELResolver resolver = ctx.getELResolver();
257: return resolver.invoke(ctx, t.base, method, paramTypes, params);
258: }
259: Object property = t.suffixNode.getValue(ctx);
260: Method m = ReflectionUtil.findMethod(t.base.getClass(), property.toString(), paramTypes, paramValues);
261: return ReflectionUtil.invokeMethod(ctx, m, t.base, paramValues);
262: }
263:
264: @Override
265: public boolean isParametersProvided() {
266: return getArguments(this.children[this.jjtGetNumChildren() - 1]) != null;
267: }
268: }