org.unitils.objectvalidation.rules.GetterAndSetterComplientRule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of unitils-objectvalidation Show documentation
Show all versions of unitils-objectvalidation Show documentation
Unitils module to validate objects.
package org.unitils.objectvalidation.rules;
import static org.junit.Assert.assertNotNull;
import static org.unitils.reflectionassert.ReflectionAssert.assertReflectionEquals;
import static org.unitils.util.ReflectionUtils.getAllFields;
import static org.unitils.util.ReflectionUtils.getGetter;
import static org.unitils.util.ReflectionUtils.getSetter;
import static org.unitils.util.ReflectionUtils.setFieldValue;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.List;
import junit.framework.AssertionFailedError;
import org.unitils.objectvalidation.ObjectCloner;
import org.unitils.objectvalidation.ObjectCreator;
import org.unitils.objectvalidation.Rule;
import org.unitils.util.ReflectionUtils;
/**
* Rule to validate that every field is accessible with a getter and a setter.
* Also that if we set an object to the field that the getter will return it.
*
* @author Jeroen Horemans
* @author Matthieu Mestrez
* @since Oct 22, 2013
*/
public class GetterAndSetterComplientRule implements Rule {
private static final List FIELDS_NAME_TO_IGNORE = Arrays.asList("serialVersionUID", "$jacocoData");
private ObjectCreator objectCreator;
private ObjectCloner objectCloner;
public GetterAndSetterComplientRule(ObjectCreator objectCreator, ObjectCloner objectCloner) {
this.objectCreator = objectCreator;
this.objectCloner = objectCloner;
}
@Override
public void validate(Class> classToValidate) {
Object object = objectCreator.createRandomObject(classToValidate);
if (object != null) {
loopOverFieldsAndCheckRules(object);
} else {
throw new AssertionError("Could not create object of type " + classToValidate.getName());
}
}
private void loopOverFieldsAndCheckRules(Object object) {
for (Field field : getAllFields(object.getClass())) {
if (!FIELDS_NAME_TO_IGNORE.contains(field.getName())) {
Method getter = getGetterFor(field);
Method setter = getSetterFor(field);
Object objectValue = getValueIfFinal(field, object);
assertNotNull("No getter for the field " + field.getName(), getter);
assertNotNull("No setter for the field " + field.getName(), setter);
handleGetter(object, field, objectValue);
assertGetterIsComplient(field, objectValue, object, getter);
handleSetter(object, setter, objectValue);
assertSetterIsComplient(field, objectValue, object);
}
}
}
private Method getGetterFor(Field field) {
return getGetter(field.getDeclaringClass(), field.getName(), false);
}
private Method getSetterFor(Field field) {
return getSetter(field.getDeclaringClass(), field.getName(), false);
}
private Object getValueIfFinal(Field field, Object object) {
if (Modifier.isFinal(field.getModifiers())) {
try {
field.setAccessible(true);
return field.get(object);
} catch (IllegalArgumentException illegalArgumentException) {
throw new AssertionError("field " + field.getName() + " had a wrong argument.\n" + illegalArgumentException.getMessage());
} catch (IllegalAccessException illegalAccessException) {
throw new AssertionError("field " + field.getName() + " could not be accessed.\n" + illegalAccessException.getMessage());
}
} else {
return objectCreator.createRandomObject(field.getGenericType());
}
}
private Object handleGetter(Object simpleBean, Field field, Object value) {
setFieldValue(simpleBean, field, value);
return clone(value);
}
private Object handleSetter(Object simpleBean, Method method, Object value) {
try {
method.invoke(simpleBean, value);
} catch (IllegalArgumentException exception) {
throwAssertionError(simpleBean, method, exception);
} catch (IllegalAccessException exception) {
throwAssertionError(simpleBean, method, exception);
} catch (InvocationTargetException exception) {
throwAssertionError(simpleBean, method, exception);
}
return clone(value);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void assertGetterIsComplient(Field field, Object value, Object simpleBean, final Method method) {
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Object run() {
method.setAccessible(true);
return null;
}
});
try {
assertReflectionEquals("Getter returned non equal value for field=[" + field + "]", value, method.invoke(simpleBean));
assertReflectionEquals("Getter returned non equal value for field the second time=[" + field + "]", value, method.invoke(simpleBean));
} catch (AssertionFailedError e) {
throwAssertionError(field, simpleBean, method, e);
} catch (IllegalArgumentException e) {
throwAssertionError(field, simpleBean, method, e);
} catch (IllegalAccessException e) {
throwAssertionError(field, simpleBean, method, e);
} catch (InvocationTargetException e) {
throwAssertionError(field, simpleBean, method, e);
}
}
protected void assertSetterIsComplient(Field fieldEntry, Object actual, Object simpleBean) {
assertReflectionEquals("Setter setted non equal value for field=[" + fieldEntry + "]", actual, ReflectionUtils.getFieldValue(simpleBean, fieldEntry));
}
private void throwAssertionError(Field field, Object simpleBean, final Method method, Throwable exception) throws AssertionError {
throw new AssertionError("The field " + field.getName() +
" has a compliancy problem with his method " + method.getName() +
".\n" + exception.getMessage());
}
private void throwAssertionError(Object simpleBean, final Method method, Throwable exception) throws AssertionError {
throw new AssertionError("The method " + method.getName() +
" had a compliancy problem.\n" + exception.getMessage());
}
/*
* This workaround is needed because the reflection assert on two classes from the type java.lang.Object always failes. The object can
* only be checked on memory adress. So we need to return the same Object instead of a clone
*/
private Object clone(Object value) {
if (!Object.class.equals(value.getClass())) {
return objectCloner.deepClone(value);
} else {
return value;
}
}
}