Skip to contentMethod: StaticFieldELResolver()
1: /*
2: * Copyright (c) 2012, 2022 Oracle and/or its affiliates and others.
3: * All rights reserved.
4: *
5: * This program and the accompanying materials are made available under the
6: * terms of the Eclipse Public License v. 2.0, which is available at
7: * http://www.eclipse.org/legal/epl-2.0.
8: *
9: * This Source Code may also be made available under the following Secondary
10: * Licenses when the conditions for such availability set forth in the
11: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
12: * version 2 with the GNU Classpath Exception, which is available at
13: * https://www.gnu.org/software/classpath/license.html.
14: *
15: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
16: */
17:
18: package jakarta.el;
19:
20: import static java.lang.reflect.Modifier.isPublic;
21: import static java.lang.reflect.Modifier.isStatic;
22: import static jakarta.el.ELUtil.getExceptionMessageString;
23:
24: import java.lang.reflect.Constructor;
25: import java.lang.reflect.Field;
26: import java.lang.reflect.Method;
27:
28: /**
29: * An {@link ELResolver} for resolving static fields, enum constants and static methods. Also handles constructor calls
30: * as a special case.
31: *
32: * <p>
33: * The resolver handles base objects of the type {@link ELClass}, which is usually generated by a Jakarta Expression
34: * Language implementation.
35: *
36: * @see ELClass
37: * @since Jakarta Expression Language 3.0
38: */
39: public class StaticFieldELResolver extends ELResolver {
40:
41: /**
42: * <p>
43: * Returns the value of a static field.
44: * </p>
45: * <p>
46: * If the base object is an instance of <code>ELClass</code> and the property is String, the
47: * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by this
48: * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller should
49: * ignore the return value.
50: * </p>
51: *
52: * If the property is a public static field of class specified in <code>ELClass</code>, return the value of the static
53: * field. An Enum constant is a public static field of an Enum object, and is a special case of this.
54: *
55: * @param context The context of this evaluation.
56: * @param base An <code>ELClass</code>.
57: * @param property A static field name.
58: *
59: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
60: * the static field value.
61: *
62: * @throws NullPointerException if context is <code>null</code>.
63: * @throws PropertyNotFoundException if the specified class does not exist, or if the field is not a public static filed
64: * of the class, or if the field is inaccessible.
65: */
66: @Override
67: public Object getValue(ELContext context, Object base, Object property) {
68: if (context == null) {
69: throw new NullPointerException();
70: }
71:
72: if (base instanceof ELClass && property instanceof String) {
73: Class<?> klass = ((ELClass) base).getKlass();
74: String fieldName = (String) property;
75: try {
76: context.setPropertyResolved(base, property);
77: Field field = klass.getField(fieldName);
78:
79: int mod = field.getModifiers();
80: if (isPublic(mod) && isStatic(mod)) {
81: return field.get(null);
82: }
83: } catch (NoSuchFieldException ex) {
84: } catch (IllegalAccessException ex) {
85: }
86:
87: throw new PropertyNotFoundException(getExceptionMessageString(context, "staticFieldReadError", new Object[] { klass.getName(), fieldName }));
88: }
89:
90: return null;
91: }
92:
93: /**
94: * <p>
95: * Attempts to write to a static field.
96: * </p>
97: * <p>
98: * If the base object is an instance of <code>ELClass</code>and the property is String, a
99: * <code>PropertyNotWritableException</code> will always be thrown, because writing to a static field is not allowed.
100: *
101: * @param context The context of this evaluation.
102: * @param base An <code>ELClass</code>
103: * @param property The name of the field
104: * @param value The value to set the field of the class to.
105: * @throws NullPointerException if context is <code>null</code>
106: * @throws PropertyNotWritableException if base object instance of <code>ELClass</code>and <code>property</code>
107: * instance of String
108: */
109: @Override
110: public void setValue(ELContext context, Object base, Object property, Object value) {
111: if (context == null) {
112: throw new NullPointerException();
113: }
114:
115: if (base instanceof ELClass && property instanceof String) {
116: Class<?> klass = ((ELClass) base).getKlass();
117: String fieldName = (String) property;
118: throw new PropertyNotWritableException(
119: getExceptionMessageString(context, "staticFieldWriteError", new Object[] { klass.getName(), fieldName }));
120: }
121: }
122:
123: /**
124: * Invokes a public static method or the constructor for a class.
125: *
126: * <p>
127: * If the base object is an instance of <code>ELClass</code> and the method is a String, the
128: * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by the
129: * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller should
130: * ignore the return value.
131: *
132: * <p>
133: * Invoke the public static method specified by <code>method</code>.
134: *
135: * <p>
136: * The process involved in the method selection is the same as that used in {@link BeanELResolver}.
137: *
138: * <p>
139: * As a special case, if the name of the method is "<init>", the constructor for the class will be invoked.
140: *
141: * @param base An <code>ELClass</code>
142: * @param methodName When coerced to a <code>String</code>, the simple name of the method.
143: * @param paramTypes An array of Class objects identifying the method's formal parameter types, in declared order. Use
144: * an empty array if the method has no parameters. Can be <code>null</code>, in which case the method's formal parameter
145: * types are assumed to be unknown.
146: * @param params The parameters to pass to the method, or <code>null</code> if no parameters.
147: * @return The result of the method invocation (<code>null</code> if the method has a <code>void</code> return type).
148: * @throws MethodNotFoundException if no suitable method can be found.
149: * @throws ELException if an exception was thrown while performing (base, method) resolution. The thrown exception must
150: * be included as the cause property of this exception, if available. If the exception thrown is an
151: * <code>InvocationTargetException</code>, extract its <code>cause</code> and pass it to the <code>ELException</code>
152: * constructor.
153: */
154: @Override
155: public Object invoke(ELContext context, Object base, Object methodName, Class<?>[] paramTypes, Object[] params) {
156: if (context == null) {
157: throw new NullPointerException();
158: }
159:
160: if (!(base instanceof ELClass && methodName instanceof String)) {
161: return null;
162: }
163:
164: Class<?> klass = ((ELClass) base).getKlass();
165: String name = (String) methodName;
166:
167: Object ret;
168: if ("<init>".equals(name)) {
169: Constructor<?> constructor = ELUtil.findConstructor(klass, paramTypes, params);
170: ret = ELUtil.invokeConstructor(context, constructor, params);
171: } else {
172: Method method = ELUtil.findMethod(klass, base, name, paramTypes, params, true);
173: ret = ELUtil.invokeMethod(context, method, null, params);
174: }
175: context.setPropertyResolved(base, methodName);
176:
177: return ret;
178: }
179:
180: /**
181: * Returns the type of a static field.
182: *
183: * <p>
184: * If the base object is an instance of <code>ELClass</code>and the property is a String, the
185: * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by the
186: * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller can
187: * safely assume no value has been set.
188: *
189: * @param context The context of this evaluation.
190: * @param base An <code>ELClass</code>.
191: * @param property The name of the field.
192: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
193: * <code>null</code> otherwise undefined.
194: * @throws NullPointerException if context is <code>null</code>.
195: * @throws PropertyNotFoundException if field is not a public static filed of the class, or if the field is
196: * inaccessible.
197: */
198: @Override
199: public Class<?> getType(ELContext context, Object base, Object property) {
200: if (context == null) {
201: throw new NullPointerException();
202: }
203:
204: if (base instanceof ELClass && property instanceof String) {
205: Class<?> klass = ((ELClass) base).getKlass();
206: String fieldName = (String) property;
207: try {
208: context.setPropertyResolved(true);
209: Field field = klass.getField(fieldName);
210:
211: int mod = field.getModifiers();
212: if (isPublic(mod) && isStatic(mod)) {
213: // Resolver is read-only so need to return null if field is
214: // resolved.
215: return null;
216: }
217: } catch (NoSuchFieldException ex) {
218: }
219: throw new PropertyNotFoundException(getExceptionMessageString(context, "staticFieldReadError", new Object[] { klass.getName(), fieldName }));
220: }
221:
222: return null;
223: }
224:
225: /**
226: * <p>
227: * Inquires whether the static field is writable.
228: * </p>
229: * <p>
230: * If the base object is an instance of <code>ELClass</code>and the property is a String, the
231: * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by the
232: * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller can
233: * safely assume no value has been set.
234: * </p>
235: *
236: * <p>
237: * Always returns a <code>true</code> because writing to a static field is not allowed.
238: * </p>
239: *
240: * @param context The context of this evaluation.
241: * @param base An <code>ELClass</code>.
242: * @param property The name of the bean.
243: * @return <code>true</code>
244: * @throws NullPointerException if context is <code>null</code>.
245: */
246: @Override
247: public boolean isReadOnly(ELContext context, Object base, Object property) {
248: if (context == null) {
249: throw new NullPointerException();
250: }
251:
252: if (base instanceof ELClass && property instanceof String) {
253: ((ELClass) base).getKlass();
254: context.setPropertyResolved(true);
255: }
256:
257: return true;
258: }
259:
260: /**
261: * Returns the type of the property. Always returns <code>String.class</code>, since a field name is a String.
262: *
263: * @param context The context of this evaluation.
264: * @param base An <code>ELClass</code>.
265: * @return <code>String.class</code>.
266: */
267: @Override
268: public Class<?> getCommonPropertyType(ELContext context, Object base) {
269: return String.class;
270: }
271: }