/* Fields.java
Purpose:
Description:
History:
Thu Oct 28 14:40:50 2004, Created by tomyeh
Copyright (C) 2004 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under LGPL Version 2.1 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.lang.reflect;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.AccessibleObject;
import java.util.Map;
import org.zkoss.lang.Classes;
import org.zkoss.lang.SystemException;
/**
* Utilities to access fields.
*
* @author tomyeh
*/
public class Fields {
/** Changes the accessibility without throwing any exception.
* @since 3.0.3
*/
public static final void setAccessible(AccessibleObject f, boolean accessible) {
try {
f.setAccessible(accessible);
} catch (Throwable t) {
}
}
/** Returns the value of the specified public field or public
* method of the object.
*
* <p>If getByCompound(obj, "a.b.c") is called and obj.getA() or
* obj.getA().getB() returns null, the result is null.
* However, NullPointerException is thrown if obj is null.
*
* @param name the field name. It can be in form of "a.b.c", but cannot
* be an expression.
* It must be a public field or public method (prefixed with set).
* @exception NoSuchMethodException if no corresponding field.
*/
public static final Object getByCompound(Object obj, String name)
throws NoSuchMethodException {
for (;;) {
final int j = name.indexOf('.');
if (j < 0)
return get(obj, name);
obj = get(obj, name.substring(0, j));
if (obj == null)
return obj;
name = name.substring(j + 1);
}
}
/** Sets the value of the specified public field or public
* method in the object.
*
* @param autoCoerce whether to automatically convert val to the proper
* class that matches the argument of method or field.
*/
public static final
void setByCompound(Object obj, String name, Object val,
boolean autoCoerce) throws NoSuchMethodException {
for (;;) {
final int j = name.indexOf('.');
if (j < 0) {
set(obj, name, val, autoCoerce);
return;
}
obj = get(obj, name.substring(0, j));
//Unlike getByCompound, obj==null is considered error here
name = name.substring(j + 1);
}
}
/** Sets the value of the specified public field or public method
* in the object, without converting the specified val.
*
* <p>It is a shortcut of setByCompound(obj, name, val, false).
*
* @param name the field name. It can be in form of "a.b.c", but cannot
* be an expression.
* It must be a public field or public method (prefixed with set).
*/
public static final
void setByCompound(Object obj, String name, Object val)
throws NoSuchMethodException {
setByCompound(obj, name, val, false);
}
/** Returns the value of the specified public field or public method
* in the object.
* Unlike {@link #getByCompound}, name cannot contain '.'.
* In other words, it must be the name of a public field
* or method (prefixed with get).
*/
public static final Object get(Object obj, String name)
throws NoSuchMethodException {
try {
final AccessibleObject acs = Classes.getAccessibleObject(
obj.getClass(), name, null,
Classes.B_GET|Classes.B_PUBLIC_ONLY);
return acs instanceof Method ?
((Method)acs).invoke(obj): ((Field)acs).get(obj);
} catch (NoSuchMethodException ex) {
if (obj instanceof Map) {
return ((Map) obj).get(name);
}
throw ex;
} catch (Exception ex) {
throw SystemException.Aide.wrap(ex, "Not found: " + name);
}
}
/** Sets the value of the specified public filed or public method
* in the object.
* Unlike {@link #setByCompound}, name cannot contain '.'.
* In other words, it must be the name of a public field
* or method (prefixed with set).
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static final void set(Object obj, String name, Object val,
boolean autoCoerce) throws NoSuchMethodException {
try {
AccessibleObject acs;
try {
acs = Classes.getAccessibleObject(
obj.getClass(),
name, new Class[] {val != null ? val.getClass(): null},
Classes.B_SET|Classes.B_PUBLIC_ONLY);
} catch (NoSuchMethodException ex) {
if (!autoCoerce || val == null)
throw ex;
//retry without specifying any argument type
acs = Classes.getAccessibleObject(
obj.getClass(), name, new Class[] {null},
Classes.B_SET|Classes.B_PUBLIC_ONLY);
}
if (acs instanceof Method) {
final Method mtd = (Method)acs;
mtd.invoke(obj,
autoCoerce ? Classes.coerce(mtd.getParameterTypes()[0], val): val);
} else {
final Field fld = (Field)acs;
fld.set(obj,
autoCoerce ? Classes.coerce(fld.getType(), val): val);
}
} catch (NoSuchMethodException ex) {
if (obj instanceof Map) {
((Map) obj).put(name, val);
return;
}
throw ex;
} catch (Exception ex) {
throw SystemException.Aide.wrap(ex, "Not found: " + name);
}
}
}