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