
org.codemonkey.javareflection.FieldUtils Maven / Gradle / Ivy
Show all versions of java-reflection Show documentation
package org.codemonkey.javareflection;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
/**
* A {@link Field} shorthand utility class mainly used to collect fields from classes meeting certain restrictions/requirements.
*
* With this utility class you can perform field lookups, by combining lookup restriction criteria. A trivial example is find all fields on
* a class Apple, not looking at its super classes, which should be protected, have a getter method, but not a setter method:
*
*
* FieldUtils.collectFields(Apple.class, Apple.class, EnumSet.of(Visibility.PROTECTED),
* EnumSet.of(BeanRestriction.YES_GETTER, BeanRestriction.NO_SETTER));
*
*
* @author Benny Bottema
* @see #collectFields(Class, Class, EnumSet, EnumSet)
*/
public final class FieldUtils {
private FieldUtils() {
}
/**
* Determines what visibility modifiers a field is allowed to have in {@link FieldUtils#collectFields(Class, Class, EnumSet, EnumSet)}.
*
* @author Benny Bottema
*/
public enum Visibility {
/**
* Visibility flag that corresponds with java's keyword private
.
*/
PRIVATE(Modifier.PRIVATE),
/**
* Visibility flag that corresponds with java's visibility modifier default
(package protected).
*/
DEFAULT(-1), // no Java equivalent
/**
* Visibility flag that corresponds with java's keyword protected
.
*/
PROTECTED(Modifier.PROTECTED),
/**
* Visibility flag that corresponds with java's keyword public
.
*/
PUBLIC(Modifier.PUBLIC);
private int modifierFlag;
private Visibility(final int modifierFlag) {
this.modifierFlag = modifierFlag;
}
}
/**
* Indicates whether a field needs a Bean setter or getter, exactly none or any combination thereof. Determines what kind of fields are
* potential collection candidates.
*
* @author Benny Bottema
*/
public enum BeanRestriction {
/**
* Restriction flag that indicates a getter method is required.
*/
YES_GETTER,
/**
* Restriction flag that indicates a setter method is required.
*/
YES_SETTER,
/**
* Restriction flag that indicates no setter must be available.
*/
NO_SETTER,
/**
* Restriction flag that indicates a getter must be available.
*/
NO_GETTER;
}
/**
* Returns a pool of {@link Field} wrappers including optional relevant setter/getter methods, collected from the given class tested
* against the given visibility and Bean restriction requirements.
*
* The returned fields are mapped against the classes they were found on, since field names can be declared multiple times with the same
* name.
*
* @param _class The class (and chain) to harvest fields from.
* @param boundaryMarker The last class>
or interface
implementing class that fields are collected from. Can
* be used to prevent finding fields on a supper class.
* @param visibility A set of visibility requirements (ie. {@link Visibility#PROTECTED} indicates a field is allowed to have
* protected
visibility).
* @param beanRestrictions A set of Bean restriction requirements indicating a field should or shouldn't have a setter, getter or both.
* @return A Map per class in the chain with the fields declared by that class.
* @see #meetsVisibilityRequirements(Field, EnumSet)
* @see #resolveBeanProperty(Field, EnumSet)
*/
public static Map, List> collectFields(final Class> _class, final Class> boundaryMarker,
final EnumSet visibility, final EnumSet beanRestrictions) {
final Map, List> fields = new HashMap, List>();
final Field[] allFields = _class.getDeclaredFields();
final List filteredFields = new LinkedList();
for (final Field field : allFields) {
if (meetsVisibilityRequirements(field, visibility)) {
final FieldWrapper property = resolveBeanProperty(field, beanRestrictions);
if (property != null) {
filteredFields.add(property);
}
}
fields.put(_class, filteredFields);
}
// determine if we need to look deeper
final List> interfaces = Arrays.asList(_class.getInterfaces());
if (_class.equals(boundaryMarker) || interfaces.contains(boundaryMarker)) {
return fields;
} else {
fields.putAll(collectFields(_class.getSuperclass(), boundaryMarker, visibility, beanRestrictions));
return fields;
}
}
/**
* Determines if the visibility modifiers of a given {@link Field} is included in the set of flags.
*
* @param field The field who's visibility modifiers we want to test.
* @param visibility List of {@link Visibility} flags to test against.
* @return Whether a given field has one of the specified visibility flags.
*/
static boolean meetsVisibilityRequirements(final Field field, final EnumSet visibility) {
for (final Visibility visibilityModifier : visibility) {
final int m = field.getModifiers();
if (!visibilityModifier.equals(Visibility.DEFAULT)) {
if ((m & visibilityModifier.modifierFlag) != 0) {
return true;
}
} else {
if (!Modifier.isPrivate(m) && !Modifier.isProtected(m) && !Modifier.isPublic(m)) {
return true;
}
}
}
return false;
}
/**
* Determines if a given Field
meets the specified Bean restriction requirements and returns the field as a BeanProperty
* with optional getter/setter.
*
* @param field The field to test.
* @param beanRestrictions The Bean restrictions to apply (should/shouldn't have setter/getter).
* @return Whether the field fits the restrictions.
*/
static FieldWrapper resolveBeanProperty(final Field field, final EnumSet beanRestrictions) {
if (beanRestrictions.containsAll(EnumSet.of(BeanRestriction.NO_GETTER, BeanRestriction.YES_GETTER)) //
|| beanRestrictions.containsAll(EnumSet.of(BeanRestriction.NO_SETTER, BeanRestriction.YES_SETTER))) {
throw new IllegalArgumentException("cannot both include and exclude a setter/getter requirement");
}
// since PropertyUtilsBean#getPropertyDescriptors(...) doesn't detect setters without getters (Bean convention)
// we'll just find setter/getters manually
final String setterName = "set" + StringUtils.capitalize(field.getName());
final String getterName;
if (field.getType().equals(boolean.class)) {
getterName = "is" + StringUtils.capitalize(field.getName());
} else {
getterName = "get" + StringUtils.capitalize(field.getName());
}
final Method writeMethod = JReflect.findSimpleCompatibleMethod(field.getDeclaringClass(), setterName, field.getType());
final Method readMethod = JReflect.findSimpleCompatibleMethod(field.getDeclaringClass(), getterName);
if (!((readMethod != null && beanRestrictions.contains(BeanRestriction.NO_GETTER)) //
|| (!(readMethod != null) && beanRestrictions.contains(BeanRestriction.YES_GETTER)) //
|| (writeMethod != null && beanRestrictions.contains(BeanRestriction.NO_SETTER)) //
|| (!(writeMethod != null) && beanRestrictions.contains(BeanRestriction.YES_SETTER)))) {
return new FieldWrapper(field, readMethod, writeMethod);
} else {
return null;
}
}
}