All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.unitils.objectvalidation.rules.GetterAndSetterComplientRule Maven / Gradle / Ivy

There is a newer version: 1.1.9
Show newest version
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;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy