/*******************************************************************************
* Copyright (c) 2008 Hallvard Traetteberg.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Hallvard Traetteberg - initial API and implementation
******************************************************************************/
package org.eclipse.e4.tm.builder;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.e4.tm.stringconverters.ClassStringConverter;
public class ReflectionSupport {
private IBinderContext context;
public ReflectionSupport(IBinderContext context) {
this.context = context;
}
static boolean hasSingleParameterForValue(Class<?>[] parameterTypes, Object value, boolean ignoreType) {
return (parameterTypes.length == 1 && (ignoreType || ClassStringConverter.getObjectClass(parameterTypes[0]).isInstance(value)));
}
private Map<String, Method> methodMap = new HashMap<String, Method>();
public Method getMethod(Object object, String signature) {
String key = object.getClass().getName() + '.' + signature;
Method method = methodMap.get(key);
if (method != null) {
return method;
}
Class<?>[] types = null;
int pos = signature.indexOf('(');
try {
if (pos >= 0) {
StringTokenizer parameters = new StringTokenizer(signature.substring(pos + 1, signature.length() - 1), ",");
signature = signature.substring(0, pos);
types = new Class<?>[parameters.countTokens()];
for (int i = 0; parameters.hasMoreTokens(); i++) {
String typeSpec = parameters.nextToken();
boolean isArray = false;
if (typeSpec.endsWith("[]")) {
isArray = true;
typeSpec = typeSpec.substring(0, typeSpec.length() - 2);
} else if (typeSpec.endsWith("*")) {
isArray = true;
typeSpec = typeSpec.substring(0, typeSpec.length() - 1);
}
Class<?> type = context.convert(typeSpec, Class.class);
if (isArray) {
type = Array.newInstance(type, 0).getClass();
}
types[i] = type;
}
}
method = object.getClass().getMethod(signature, types);
methodMap.put(key, method);
} catch (Exception e1) {
}
return method;
}
private Map<Object, Method> getterMap = new HashMap<Object, Method>();
public Method getGetterMethod(Object object, String name) {
String key = object.getClass().getName() + '.' + name;
Method method = getterMap.get(key);
if (method != null) {
return method;
}
Method[] methods = object.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
method = methods[i];
if (isPrefixedName(method.getName(), "get", name)) {
if (method.getParameterTypes().length == 0) {
getterMap.put(key, method);
return method;
}
}
}
return null;
}
private boolean isPrefixedName(String name, String prefix, String propertyName) {
return name.length() == 3 + propertyName.length() &&
name.startsWith(prefix) &&
name.charAt(prefix.length()) == Character.toUpperCase(propertyName.charAt(0)) &&
name.endsWith(propertyName.substring(1));
}
private Map<Object, Method> setterMap = new HashMap<Object, Method>();
public Method getSetterMethod(Object object, String name, Object value, boolean ignoreType) {
String key = object.getClass().getName() + '.' + name;
Method method = setterMap.get(key);
if (method != null) {
return method;
}
Method[] methods = object.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
method = methods[i];
if (isPrefixedName(method.getName(), "set", name)) {
if (ReflectionSupport.hasSingleParameterForValue(method.getParameterTypes(), value, ignoreType)) {
setterMap.put(key, method);
return method;
}
}
}
return null;
}
private Map<Object, Field> fieldMap = new HashMap<Object, Field>();
private Field getField(Object object, String name, Object value, boolean ignoreType) {
String key = object.getClass().getName() + '.' + name;
Field field = fieldMap.get(key);
if (field != null) {
return field;
}
try {
field = object.getClass().getField(name);
if (ignoreType || value == null || ClassStringConverter.getObjectClass(field.getType()).isInstance(value)) {
fieldMap.put(key, field);
return field;
}
} catch (Exception e) {
}
return null;
}
public Object getGetterProperty(Object object, String name) {
Method getterMethod = getGetterMethod(object, name);
Exception ex = null;
if (getterMethod != null) {
try {
return getterMethod.invoke(object, (Object[])null);
} catch (Exception e) {
ex = e;
}
}
throw new RuntimeException("Couldn't invoke getter for " + name, ex);
}
public Object getMethodProperty(Object object, String methodSpec, Object[] args) {
Method method = getMethod(object, methodSpec);
Exception ex = null;
if (method != null) {
try {
return method.invoke(object, args);
} catch (Exception e) {
ex = e;
}
}
throw new RuntimeException("Couldn't invoke method for " + methodSpec, ex);
}
public Object getFieldProperty(Object object, String name) {
Field field = getField(object, name, null, true);
Exception ex = null;
if (field != null) {
try {
return field.get(object);
} catch (Exception e) {
ex = e;
}
}
throw new RuntimeException("Couldn't get field for " + name, ex);
}
//
public Exception setSetterProperty(Object object, String name, Object value) {
Method setterMethod = getSetterMethod(object, name, value, true);
if (setterMethod != null) {
try {
value = context.adapt(value, setterMethod.getParameterTypes()[0]);
setterMethod.invoke(object, new Object[]{value});
return null;
} catch (Exception e) {
return e;
}
}
return new NoSuchMethodException("Couldn't invoke setter for " + name);
}
public Class<?> getSetterPropertyType(Object object, String name) {
Method setterMethod = getSetterMethod(object, name, null, true);
return (setterMethod != null ? setterMethod.getParameterTypes()[0] : null);
}
public Exception setMethodProperty(Object object, String methodSpec, Object[] args) {
Method method = getMethod(object, methodSpec);
if (method != null) {
try {
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < args.length; i++) {
args[i] = context.adapt(args[i], parameterTypes[i]);
}
method.invoke(object, args);
return null;
} catch (Exception e) {
return e;
}
}
return new NoSuchMethodException("Couldn't invoke method for " + methodSpec);
}
public Class<?> getMethodPropertyType(Object object, String methodSpec) {
Method method = getMethod(object, methodSpec);
return (method != null ? method.getParameterTypes()[0] : null);
}
public Exception setFieldProperty(Object object, String name, Object value) {
Field field = getField(object, name, value, true);
if (field != null) {
try {
value = context.adapt(value, field.getType());
field.set(object, value);
return null;
} catch (Exception e) {
return e;
}
}
return new NoSuchFieldException("No field for " + name);
}
public Class<?> getFieldPropertyType(Object object, String name) {
Field field = getField(object, name, null, true);
return (field != null ? field.getType() : null);
}
public void setProperty(Object object, String name, Object value) throws Exception {
Exception ex = setSetterProperty(object, name, value);
if (ex != null) {
ex = setFieldProperty(object, name, value);
}
if (ex != null) {
throw ex;
}
}
public Class<?> getPropertyType(Object object, String name) {
Class<?> c = getSetterPropertyType(object, name);
if (c == null) {
c = getFieldPropertyType(object, name);
}
return c;
}
}