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

org.unitils.objectvalidation.objectcreator.ObjectCreatorImpl Maven / Gradle / Ivy

There is a newer version: 1.1.9
Show newest version
/**
 * Copyright (C) 2010 Osman Shoukry
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see .
 */
package org.unitils.objectvalidation.objectcreator;

import static java.lang.reflect.Modifier.isStatic;
import static java.util.logging.Level.WARNING;
import static org.unitils.util.ReflectionUtils.getFieldValue;
import static org.unitils.util.ReflectionUtils.setFieldValue;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import org.objenesis.ObjenesisHelper;
import org.unitils.core.UnitilsException;
import org.unitils.objectvalidation.ObjectCreator;
import org.unitils.objectvalidation.objectcreator.generator.Generator;
import org.unitils.objectvalidation.utils.TreeNode;
import org.unitils.objectvalidation.utils.TreeNodeCreator;
import org.unitils.util.ReflectionUtils;


/**
 * {@link ObjectCreator} can create an object, but not just in the easy way.
 * It will interpret the constructor to create the fullest possible object with the help of the Generators given.
 *
 * @author Jeroen Horemans
 * @since Feb 20, 2012
 */
class ObjectCreatorImpl extends BaseObjectCreator implements ObjectCreator {

    private static final Logger LOGGER = Logger.getLogger("ObjectCreator");


    public ObjectCreatorImpl() {
        super();
    }

    /**
     * First we will determine the object tree we will have to create. After the tree is completely constructed we will recursively run
     * trough it and start creating the entire object graph needed from bottom to top.
     *
     * @param generators
     */
    ObjectCreatorImpl(Generator... generators) {
        super(generators);
    }

    /**
     * @param type the class object to be created.
     * @return a random created object.
     */
    @Override
    public Object createRandomObject(final Type type) {
        TreeNode parentNode = TreeNodeCreator.createTreeNodeFor(type);
        if (parentNode.getType() == null) {
            parentNode.setType(type);
        }
        return createRandomObject(parentNode);
    }

    /**
     * @see org.unitils.objectvalidation.ObjectCreator#createRandomObject(org.unitils.objectvalidation.utils.TreeNode)
     */
    @Override
    public Object createRandomObject(TreeNode node) {
        Object randomObject = createFromTree(node);

        if (!isPrimitive(randomObject)) {

            replaceFieldsWithRandom(randomObject);

        }

        return randomObject;
    }


    private boolean isPrimitive(Object object) {

        if (object == null) {
            return true;
        }

        Class clazz = object.getClass();
        return clazz.isPrimitive()
            || clazz == Boolean.class
            || clazz == Integer.class
            || clazz == BigInteger.class
            || clazz == Float.class
            || clazz == Double.class
            || clazz == BigDecimal.class
            || clazz == Long.class
            || clazz == Short.class
            || clazz == Byte.class
            || clazz == Character.class
            || clazz == String.class
            || clazz == java.util.Date.class
            || clazz == java.sql.Date.class
            || clazz == Timestamp.class
            || clazz == Calendar.class
            || clazz.isAssignableFrom(Collection.class)
            || Arrays.asList(clazz.getInterfaces()).contains(List.class)
            || Exception.class.isAssignableFrom(clazz);
    }

    private void replaceFieldsWithRandom(Object randomObject) {
        if (randomObject != null) {

            Set fields = ReflectionUtils.getAllFields(randomObject.getClass());

            for (Field field : fields) {

                try {
                    if (!isStatic(field.getModifiers())) {

                        if (field.getType().isPrimitive()) {

                            Object fieldValue = getGenerator().generateObject(field.getType(), null, null, null);
                            setFieldValue(randomObject, field, fieldValue);

                        } else if (getFieldValue(randomObject, field) == null) {
                                Object fieldValue = null;
                                if (field.getType().equals(randomObject.getClass())) {
                                    fieldValue = ObjenesisHelper.newInstance(field.getType());
                                } else {
                                    fieldValue = createRandomObject(field.getGenericType());
                                }
                                setFieldValue(randomObject, field, fieldValue);
                        }
                    }
                } catch (IllegalArgumentException e) {
                    throw new UnitilsException("Field " + field.getName() + " value could not be retrieved.", e);
                } catch (Exception e) {
                    throw new UnitilsException("Field " + field.getName() + " value could not be generated.", e);
                }
            }

        }
    }

    protected Object createFromTree(TreeNode parentNode) {
        List input = new ArrayList(parentNode.getChilds().size());
        List> inputClasses = new ArrayList>(parentNode.getChilds().size());

        for (TreeNode child : parentNode.getChilds()) {
            input.add(createFromTree(child));
            inputClasses.add(child.getValue());
        }

        try {
            return getGenerator().generateObject(parentNode.getValue(), input, inputClasses, parentNode.getGenericSubtype());
        } catch (Exception e) {
            LOGGER.log(WARNING, "Invocation on the generator has made a mistake. Try adding a custom generator for the object '" + parentNode.getValue().getName() + "' the factory has problems with.", e);
            // We log the exception but the ObjectCreator cannot throw an exception. It should always 'work'.
        }
        return null;
    }

}