package org.test4j.datafilling.common; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.Set; import org.test4j.datafilling.annotations.FillExclude; import org.test4j.module.core.utility.MessageHelper; /** * This class wraps fields and setters information about a given class * <p> * The purpose of this class is to work as a sort of cache which stores the list * of declared fields and setter methods of a given class. These information * will then be analysed to compose the list of setters which can be invoked to * create the state of a given POJO. * </p> */ @SuppressWarnings({ "serial" }) public class ClassFieldInfo implements Serializable { /** The Set of fields belonging to this class */ private final Set<String> classFields; /** The Set of setters belonging to this class */ private final Set<Method> classSetters; /** * Full constructor * * @param className The class name * @param classFields The set of fields belonging to this class * @param classSetters The set of setters belonging to this class */ public ClassFieldInfo(Set<String> classFields, Set<Method> classSetters) { this.classFields = classFields; this.classSetters = classSetters; } /** * @return the classSetters */ public Set<Method> getClassSetters() { return classSetters; } public Set<String> getClassFields() { return classFields; } /** * It returns a {@link ClassFieldInfo} object for the given class * * @param clazz The class to retrieve info from * @return a {@link ClassFieldInfo} object for the given class */ public static ClassFieldInfo getClassInfo(Class<?> clazz) { Set<String> classFields = getDeclaredInstanceFields(clazz); Set<Method> classSetters = getPojoSetters(clazz, classFields); return new ClassFieldInfo(classFields, classSetters); } /** * Given a class, it returns a Set of its declared instance field names. * * @param clazz The class to analyse to retrieve declared fields * @return Set of a class declared field names. */ public static Set<String> getDeclaredInstanceFields(Class<?> clazz) { Set<String> classFields = new HashSet<String>(); while (clazz != null) { Field[] declaredFields = clazz.getDeclaredFields(); for (Field field : declaredFields) { // If users wanted to skip this field, we grant their wishes if (field.getAnnotation(FillExclude.class) != null) { continue; } int modifiers = field.getModifiers(); if (!Modifier.isStatic(modifiers)) { classFields.add(field.getName()); } } clazz = clazz.getSuperclass(); } return classFields; } /** * Given a class and a set of class declared fields it returns a Set of * setters matching the declared fields * <p> * If present, a setter method is considered if and only if the * {@code classFields} argument contains an attribute whose name matches the * setter, according to JavaBean standards. * </p> * * @param clazz The class to analyse for setters * @param classFields A Set of field names for which setters are to be found * @return A Set of setters matching the class declared field names */ public static Set<Method> getPojoSetters(Class<?> clazz, Set<String> classFields) { Set<Method> classSetters = new HashSet<Method>(); while (clazz != null) { Method[] declaredMethods = clazz.getDeclaredMethods(); String candidateField = null; for (Method method : declaredMethods) { if (!method.getName().startsWith("set")) { continue; } //if (!method.getReturnType().equals(void.class)) { if (method.getParameterTypes().length != 1) { continue; } candidateField = extractFieldNameFromSetterMethod(method); if (!classFields.contains(candidateField)) { continue; } classSetters.add(method); } clazz = clazz.getSuperclass(); } return classSetters; } /** * Given a setter {@link Method}, it extracts the field name, according to * JavaBean standards * <p> * This method, given a setter method, it returns the corresponding * attribute name. For example: given setIntField the method would return * intField. The correctness of the return value depends on the adherence to * JavaBean standards. * </p> * * @param method The setter method from which the field name is required * @return The field name corresponding to the setter */ public static String extractFieldNameFromSetterMethod(Method method) { String candidateField = null; candidateField = method.getName().substring(3); if (!candidateField.equals("")) { candidateField = Character.toLowerCase(candidateField.charAt(0)) + candidateField.substring(1); } else { MessageHelper.warn("Encountered method set. This will be ignored."); } return candidateField; } }