Underp the Accessor reflection framework.

This commit is contained in:
Olof Larsson 2014-11-13 09:08:51 +01:00
parent d986f2ba31
commit 81e4aae209
17 changed files with 194 additions and 512 deletions

View File

@ -1,20 +1,154 @@
package com.massivecraft.massivecore.store.accessor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public final class Accessor
public class Accessor
{
private static Map<Class<?>, EntityAccessor> class2EntityAccessor = new HashMap<Class<?>, EntityAccessor>();
// -------------------------------------------- //
// FIELDS
// -------------------------------------------- //
public static EntityAccessor get(Class<?> clazz)
private final Class<?> clazz;
public Class<?> getClazz() { return this.clazz; }
private Map<String, FieldAccessor> fieldToAccessor = new LinkedHashMap<String, FieldAccessor>();
public Map<String, FieldAccessor> getFieldToAccessor() { return this.fieldToAccessor; }
public FieldAccessor getFieldAccessor(String fieldName)
{
EntityAccessor ret = class2EntityAccessor.get(clazz);
if (ret == null)
{
ret = new EntityAccessorPerProperty(clazz);
class2EntityAccessor.put(clazz, ret);
FieldAccessor ret = this.fieldToAccessor.get(fieldName);
if (ret == null) throw new IllegalArgumentException("The field \""+fieldName+"\" is not supported.");
return ret;
}
public void setFieldAccessor(String fieldName, FieldAccessor fieldAccessor)
{
this.fieldToAccessor.put(fieldName, fieldAccessor);
}
public Collection<String> getFieldNames()
{
return this.fieldToAccessor.keySet();
}
// -------------------------------------------- //
// CONSTRUCT / FACTORY
// -------------------------------------------- //
private static Map<Class<?>, Accessor> classToAccessor = new HashMap<Class<?>, Accessor>();
public static Accessor get(Class<?> clazz)
{
Accessor ret = classToAccessor.get(clazz);
if (ret != null) return ret;
return new Accessor(clazz);
}
private Accessor(Class<?> clazz)
{
this.clazz = clazz;
this.populate();
classToAccessor.put(clazz, this);
}
// -------------------------------------------- //
// POPULATE: REFLECTION
// -------------------------------------------- //
public void populate()
{
Map<String, Field> map = getFieldMap(this.clazz);
for (Entry<String, Field> entry : map.entrySet())
{
String fieldName = entry.getKey();
Field field = entry.getValue();
FieldAccessor fieldAccessor = new FieldAccessor(field);
this.setFieldAccessor(fieldName, fieldAccessor);
}
}
// -------------------------------------------- //
// GET & SET & COPY
// -------------------------------------------- //
public Object get(Object object, String fieldName)
{
FieldAccessor fieldAccessor = this.getFieldAccessor(fieldName);
return fieldAccessor.get(object);
}
public void set(Object object, String fieldName, Object val)
{
FieldAccessor fieldAccessor = this.getFieldAccessor(fieldName);
fieldAccessor.set(object, val);
}
// Copy one only!
public void copy(Object from, Object to, String fieldName)
{
FieldAccessor fieldAccessor = this.getFieldAccessor(fieldName);
Object val = fieldAccessor.get(from);
fieldAccessor.set(to, val);
}
// Copy a few!
public void copy(Object from, Object to, Collection<String> fieldNames)
{
for (String fieldName : fieldNames)
{
this.copy(from, to, fieldName);
}
}
// Copy them all!
public void copy(Object from, Object to)
{
for (FieldAccessor fieldAccessor : this.getFieldToAccessor().values())
{
Object val = fieldAccessor.get(from);
fieldAccessor.set(to, val);
}
}
// -------------------------------------------- //
// UTIL
// -------------------------------------------- //
public static List<Field> getFieldList(Class<?> clazz)
{
List<Field> fields = new ArrayList<Field>();
for (Class<?> c = clazz; c != null; c = c.getSuperclass())
{
fields.addAll(Arrays.asList(c.getDeclaredFields()));
}
return fields;
}
public static Map<String, Field> getFieldMap(Class<?> clazz)
{
Map<String, Field> ret = new LinkedHashMap<String, Field>();
for (Field field : getFieldList(clazz))
{
if (Modifier.isTransient(field.getModifiers())) continue;
if (Modifier.isFinal(field.getModifiers())) continue;
String fieldName = field.getName();
if (ret.containsKey(fieldName)) continue;
field.setAccessible(true);
ret.put(fieldName, field);
}
return ret;
}

View File

@ -1,173 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AccessorUtil
{
// -------------------------------------------- //
// MAKE ACCESSIBLE
// -------------------------------------------- //
public static void makeAccessible(Field field)
{
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier.isFinal(field.getModifiers())) && !field.isAccessible())
{
field.setAccessible(true);
}
}
public static void makeAccessible(Method method)
{
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible())
{
method.setAccessible(true);
}
}
// -------------------------------------------- //
// FIND
// -------------------------------------------- //
public static List<Method> findMethod(Class<?> clazz, String name)
{
List<Method> ret = new ArrayList<Method>();
for (Class<?> c = clazz; c != null; c = c.getSuperclass())
{
Method[] methods = (c.isInterface() ? c.getMethods() : c.getDeclaredMethods());
for (Method method : methods)
{
if (name.equals(method.getName())) ret.add(method);
}
}
return ret;
}
public static Field findField(Class<?> clazz, String name)
{
for (Class<?> c = clazz; c != null && !Object.class.equals(c); c = c.getSuperclass())
{
Field[] fields = c.getDeclaredFields();
for (Field field : fields)
{
if (name.equals(field.getName())) return field;
}
}
return null;
}
public static List<Field> findAllFields(Class<?> clazz)
{
List<Field> fields = new ArrayList<Field>();
for (Class<?> c = clazz; c != null; c = c.getSuperclass())
{
fields.addAll(Arrays.asList(c.getDeclaredFields()));
}
return fields;
}
/**
* Must be non-transient.
* Must be non-final.
* Will be set accessible if possible.
*/
public static Map<String, Field> getFieldMap(Class<?> clazz)
{
Map<String, Field> ret = new HashMap<String, Field>();
for (Field field : findAllFields(clazz))
{
if (Modifier.isTransient(field.getModifiers())) continue;
if (Modifier.isFinal(field.getModifiers())) continue;
makeAccessible(field);
ret.put(field.getName(), field);
}
return ret;
}
// -------------------------------------------- //
// FIND GETTERS AND SETTERS
// -------------------------------------------- //
public static String ucfirst(String str)
{
return Character.toUpperCase(str.charAt(0))+str.substring(1);
}
public static String calcGetterNameBool(String fieldName) { return "is"+ucfirst(fieldName); }
public static String calcGetterName(String fieldName) { return "get"+ucfirst(fieldName); }
public static String calcSetterName(String fieldName) { return "set"+ucfirst(fieldName); }
// TODO: Use a predictate?
public static Method findGetter(Class<?> clazz, String fieldName)
{
for (Method method : findMethod(clazz, calcGetterName(fieldName)))
{
if (method.getParameterTypes().length == 0 && method.getReturnType() != null) return method;
}
for (Method method : findMethod(clazz, calcGetterNameBool(fieldName)))
{
if (method.getParameterTypes().length == 0 && method.getReturnType() == Boolean.class) return method;
}
return null;
}
public static Method findSetter(Class<?> clazz, String fieldName)
{
List<Method> methods = findMethod(clazz, calcSetterName(fieldName));
methods.addAll(findMethod(clazz, fieldName));
for (Method method : methods)
{
if (method != null && method.getParameterTypes().length == 1) return method;
}
return null;
}
// -------------------------------------------- //
// CREATE PROPERTY ACCESS
// -------------------------------------------- //
public static PropertyGetter createPropertyGetter(Class<?> clazz, String name)
{
Method method = findGetter(clazz, name);
if (method != null) return new PropertyGetterMethodReflection(method);
Field field = findField(clazz, name);
if (field != null) return new PropertyGetterFieldReflection(field);
return null;
}
public static PropertySetter createPropertySetter(Class<?> clazz, String name)
{
Method method = findSetter(clazz, name);
if (method != null) return new PropertySetterMethodReflection(method);
Field field = findField(clazz, name);
if (field != null) return new PropertySetterFieldReflection(field);
return null;
}
public static PropertyAccessor createPropertyAccessor(Class<?> clazz, String name)
{
PropertyGetter getter = createPropertyGetter(clazz, name);
if (getter == null) return null;
PropertySetter setter = createPropertySetter(clazz, name);
if (setter == null) return null;
return new PropertyAccessorComposite(getter, setter);
}
}

View File

@ -1,6 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
public interface EntityAccessor extends EntitySetter, EntityGetter, EntityGlue
{
}

View File

@ -1,52 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
import java.util.Collection;
public abstract class EntityAccessorAbstract implements EntityAccessor
{
protected final Class<?> clazz;
public Class<?> getClazz() { return this.clazz; }
public EntityAccessorAbstract(Class<?> clazz)
{
this.clazz = clazz;
}
private static final boolean DEFAULT_TRANSPARENT = false;
@Override
public void copy(Object from, Object to, String property, boolean transparent)
{
Object val = this.get(from, property);
if (transparent && val == null) return;
this.set(to, property, val);
}
@Override
public void copy(Object from, Object to, String property)
{
this.copy(from, to, property, DEFAULT_TRANSPARENT);
}
@Override
public void copy(Object from, Object to, Collection<String> properties, boolean transparent)
{
for (String property : properties)
{
this.copy(from, to, property, transparent);
}
}
@Override
public void copy(Object from, Object to, Collection<String> properties)
{
this.copy(from, to, properties, DEFAULT_TRANSPARENT);
}
@Override
public void copy(Object from, Object to, boolean transparent)
{
this.copy(from, to, this.getPropertyNames(), transparent);
}
@Override
public void copy(Object from, Object to)
{
this.copy(from, to, DEFAULT_TRANSPARENT);
}
}

View File

@ -1,86 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class EntityAccessorPerProperty extends EntityAccessorAbstract
{
protected Map<String, PropertyAccessor> propertyAccessors;
public Map<String, PropertyAccessor> getPropertyAccessors() { return this.propertyAccessors; }
public PropertyAccessor getPropertyAccessor(String name)
{
PropertyAccessor ret = this.propertyAccessors.get(name);
if (ret == null)
{
throw new IllegalArgumentException("The property \""+name+"\" is not supported.");
}
return ret;
}
public void setPropertyAccessor(String name, PropertyAccessor val)
{
this.propertyAccessors.put(name, val);
}
//----------------------------------------------//
// CONSTRUCTORS
//----------------------------------------------//
public EntityAccessorPerProperty(Class<?> clazz)
{
super(clazz);
this.propertyAccessors = new HashMap<String, PropertyAccessor>();
this.populateAI1();
}
//----------------------------------------------//
// AI
//----------------------------------------------//
public void populateAI1()
{
this.propertyAccessorAI(AccessorUtil.getFieldMap(this.clazz).keySet());
}
public void propertyAccessorAI(String name)
{
this.propertyAccessors.put(name, AccessorUtil.createPropertyAccessor(this.clazz, name));
}
public void propertyAccessorAI(String... names)
{
for (String name : names)
{
this.propertyAccessorAI(name);
}
}
public void propertyAccessorAI(Collection<? extends String> names)
{
this.propertyAccessorAI(names.toArray(new String[0]));
}
//----------------------------------------------//
// IMPLEMENTATION
//----------------------------------------------//
@Override
public void set(Object entity, String property, Object val)
{
PropertyAccessor pa = this.getPropertyAccessor(property);
pa.set(entity, val);
}
@Override
public Object get(Object entity, String property)
{
PropertyAccessor pa = this.getPropertyAccessor(property);
return pa.get(entity);
}
@Override
public Collection<String> getPropertyNames()
{
return this.propertyAccessors.keySet();
}
}

View File

@ -1,6 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
public interface EntityGetter
{
public Object get(Object entity, String property);
}

View File

@ -1,17 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
import java.util.Collection;
public interface EntityGlue
{
public void copy(Object from, Object to, String property, boolean transparent);
public void copy(Object from, Object to, String property);
public void copy(Object from, Object to, Collection<String> properties, boolean transparent);
public void copy(Object from, Object to, Collection<String> properties);
public void copy(Object from, Object to, boolean transparent);
public void copy(Object from, Object to);
public Collection<String> getPropertyNames();
}

View File

@ -1,6 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
public interface EntitySetter
{
public void set(Object entity, String property, Object val);
}

View File

@ -0,0 +1,52 @@
package com.massivecraft.massivecore.store.accessor;
import java.lang.reflect.Field;
public class FieldAccessor
{
// -------------------------------------------- //
// FIELDS
// -------------------------------------------- //
private final Field field;
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public FieldAccessor(Field field)
{
field.setAccessible(true);
this.field = field;
}
// -------------------------------------------- //
// CORE
// -------------------------------------------- //
public Object get(Object entity)
{
try
{
return this.field.get(entity);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
public void set(Object entity, Object val)
{
try
{
this.field.set(entity, val);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

View File

@ -1,6 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
public interface PropertyAccessor extends PropertySetter, PropertyGetter
{
}

View File

@ -1,26 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
public class PropertyAccessorComposite implements PropertyAccessor
{
private final PropertyGetter getter;
private final PropertySetter setter;
public PropertyAccessorComposite(PropertyGetter getter, PropertySetter setter)
{
this.getter = getter;
this.setter = setter;
}
@Override
public void set(Object entity, Object val)
{
this.setter.set(entity, val);
}
@Override
public Object get(Object entity)
{
return this.getter.get(entity);
}
}

View File

@ -1,6 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
public interface PropertyGetter
{
public Object get(Object entity);
}

View File

@ -1,29 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
import java.lang.reflect.Field;
public class PropertyGetterFieldReflection implements PropertyGetter
{
private final Field field;
public PropertyGetterFieldReflection(Field field)
{
AccessorUtil.makeAccessible(field);
this.field = field;
}
@Override
public Object get(Object entity)
{
try
{
return this.field.get(entity);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
}

View File

@ -1,29 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
import java.lang.reflect.Method;
public class PropertyGetterMethodReflection implements PropertyGetter
{
private final Method method;
public PropertyGetterMethodReflection(Method method)
{
AccessorUtil.makeAccessible(method);
this.method = method;
}
@Override
public Object get(Object entity)
{
try
{
return this.method.invoke(entity, new Object[0]);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
}

View File

@ -1,6 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
public interface PropertySetter
{
public void set(Object entity, Object val);
}

View File

@ -1,28 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
import java.lang.reflect.Field;
public class PropertySetterFieldReflection implements PropertySetter
{
private final Field field;
public PropertySetterFieldReflection(Field field)
{
AccessorUtil.makeAccessible(field);
this.field = field;
}
@Override
public void set(Object entity, Object val)
{
try
{
this.field.set(entity, val);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

View File

@ -1,28 +0,0 @@
package com.massivecraft.massivecore.store.accessor;
import java.lang.reflect.Method;
public class PropertySetterMethodReflection implements PropertySetter
{
private final Method method;
public PropertySetterMethodReflection(Method method)
{
AccessorUtil.makeAccessible(method);
this.method = method;
}
@Override
public void set(Object entity, Object val)
{
try
{
this.method.invoke(entity, val);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}