jakarta.el.StaticFieldELResolver Maven / Gradle / Ivy
/*
* Copyright (c) 2012, 2019 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.beans.FeatureDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Iterator;
/**
* An {@link ELResolver} for resolving static fields, enum constants and static methods. Also handles constructor calls
* as a special case.
*
*
* 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 {
/**
*
* Returns the value of a static field.
*
*
* If the base object is an instance of ELClass
and the property is String, the
* propertyResolved
property of the ELContext
object must be set to true
by this
* resolver, before returning. If this property is not true
after this method is called, the caller should
* ignore the return value.
*
*
* If the property is a public static field of class specified in ELClass
, 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 ELClass
.
* @param property A static field name.
*
* @return If the propertyResolved
property of ELContext
was set to true
, then
* the static field value.
*
* @throws NullPointerException if context is null
.
* @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 (Modifier.isPublic(mod) && Modifier.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;
}
/**
*
* Attempts to write to a static field.
*
*
* If the base object is an instance of ELClass
and the property is String, a
* PropertyNotWritableException
will always be thrown, because writing to a static field is not allowed.
*
* @param context The context of this evaluation.
* @param base An ELClass
* @param property The name of the field
* @param value The value to set the field of the class to.
* @throws NullPointerException if context is null
* @throws PropertyNotWritableException if base object instance of ELClass
and property
* 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.
*
*
* If the base object is an instance of ELClass
and the method is a String, the
* propertyResolved
property of the ELContext
object must be set to true
by the
* resolver, before returning. If this property is not true
after this method is called, the caller should
* ignore the return value.
*
*
* Invoke the public static method specified by method
.
*
*
* The process involved in the method selection is the same as that used in {@link BeanELResolver}.
*
*
* As a special case, if the name of the method is "<init>", the constructor for the class will be invoked.
*
* @param base An ELClass
* @param methodName When coerced to a String
, 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 null
, in which case the method's formal parameter
* types are assumed to be unknown.
* @param params The parameters to pass to the method, or null
if no parameters.
* @return The result of the method invocation (null
if the method has a void
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
* InvocationTargetException
, extract its cause
and pass it to the ELException
* 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 ("".equals(name)) {
Constructor> constructor = ELUtil.findConstructor(klass, paramTypes, params);
ret = ELUtil.invokeConstructor(context, constructor, params);
} else {
Method method = ELUtil.findMethod(klass, name, paramTypes, params, true);
ret = ELUtil.invokeMethod(context, method, null, params);
}
context.setPropertyResolved(base, methodName);
return ret;
}
/**
* Returns the type of a static field.
*
*
* If the base object is an instance of ELClass
and the property is a String, the
* propertyResolved
property of the ELContext
object must be set to true
by the
* resolver, before returning. If this property is not true
after this method is called, the caller can
* safely assume no value has been set.
*
*
* If the property string is a public static field of class specified in ELClass, return the type of the static field.
*
* @param context The context of this evaluation.
* @param base An ELClass
.
* @param property The name of the field.
* @return If the propertyResolved
property of ELContext
was set to true
, then
* the type of the type of the field.
* @throws NullPointerException if context is null
.
* @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)) {
return field.getType();
}
} catch (NoSuchFieldException ex) {
}
throw new PropertyNotFoundException(getExceptionMessageString(context, "staticFieldReadError", new Object[] { klass.getName(), fieldName }));
}
return null;
}
/**
*
* Inquires whether the static field is writable.
*
*
* If the base object is an instance of ELClass
and the property is a String, the
* propertyResolved
property of the ELContext
object must be set to true
by the
* resolver, before returning. If this property is not true
after this method is called, the caller can
* safely assume no value has been set.
*
*
*
* Always returns a true
because writing to a static field is not allowed.
*
*
* @param context The context of this evaluation.
* @param base An ELClass
.
* @param property The name of the bean.
* @return true
* @throws NullPointerException if context is null
.
*/
@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 properties that can be resolved. Always returns null
, since there is no reason to iterate
* through a list of one element: field name.
*
* @param context The context of this evaluation.
* @param base An ELClass
.
* @return null
.
*/
@Override
public Iterator getFeatureDescriptors(ELContext context, Object base) {
return null;
}
/**
* Returns the type of the property. Always returns String.class
, since a field name is a String.
*
* @param context The context of this evaluation.
* @param base An ELClass
.
* @return String.class
.
*/
@Override
public Class> getCommonPropertyType(ELContext context, Object base) {
return String.class;
}
}