/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package javax.el;
import java.util.
Iterator;
import java.lang.reflect.
Field;
import java.lang.reflect.
Method;
import java.lang.reflect.
Modifier;
import java.lang.reflect.
Constructor;
import java.beans.
FeatureDescriptor;
/**
* <p>An {@link ELResolver} for resolving static fields, enum constants and
* static methods. Also handles constructor calls as a special case.</p>
* <p>The resolver handles base objects of the type {@link ELClass}, which
* is usually generated by an EL implementation.</p>
*
* @see ELClass
* @since EL 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 (
Modifier.
isPublic(
mod) &&
Modifier.
isStatic(
mod)) {
return
field.
get(null);
}
} catch (
NoSuchFieldException ex) {
} catch (
IllegalAccessException ex) {
}
throw new
PropertyNotFoundException(
ELUtil.
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
*/
@
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(
ELUtil.
getExceptionMessageString(
context,
"staticFieldWriteError",
new
Object[] {
klass.
getName(),
fieldName}));
}
}
/**
* <p>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>
* <p>Invoke the public static method specified by <code>method</code>.</p>
* <p>The process involved in the method selection is
* the same as that used in {@link BeanELResolver}.</p>
*
* <p>As a special case, if the name of the method is "<init>", the
* constructor for the class will be invoked.</p>
*
* @param base An <code>ELClass</code>
* @param method 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 method,
Class<?>[]
paramTypes,
Object[]
params) {
if (
context == null) {
throw new
NullPointerException();
}
if (!(
base instanceof
ELClass &&
method instanceof
String)) {
return null;
}
Class<?>
klass = ((
ELClass)
base).
getKlass();
String name = (
String)
method;
Object ret;
if ("<init>".
equals(
name)) {
Constructor<?>
constructor =
ELUtil.
findConstructor(
klass,
paramTypes,
params);
ret =
ELUtil.
invokeConstructor(
context,
constructor,
params);
} else {
Method meth =
ELUtil.
findMethod(
klass,
name,
paramTypes,
params, true);
ret =
ELUtil.
invokeMethod(
context,
meth, null,
params);
}
context.
setPropertyResolved(
base,
method);
return
ret;
}
/**
* <p>Returns the type of a static field.</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>
*
* If the property string is a public static field of class specified in
* ELClass, return the type of the static field.</p>
* @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
* the type of the type of the field.
* @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 (
Modifier.
isPublic(
mod) &&
Modifier.
isStatic(
mod)) {
return
field.
getType();
}
} catch (
NoSuchFieldException ex) {
}
throw new
PropertyNotFoundException(
ELUtil.
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) {
Class<?>
klass = ((
ELClass)
base).
getKlass();
context.
setPropertyResolved(true);
}
return true;
}
/**
* Returns the properties that can be resolved.
* Always returns <code>null</code>, 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 <code>ELClass</code>.
* @return <code>null</code>.
*/
@
Override
public
Iterator<
FeatureDescriptor>
getFeatureDescriptors(
ELContext context,
Object base) {
return null;
}
/**
* 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;
}
}