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.util.Arrays.asList;
import static java.util.logging.Level.WARNING;
import static org.unitils.util.ReflectionUtils.getFieldValue;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import org.unitils.core.UnitilsException;
import org.unitils.objectvalidation.ObjectCreator;
import org.unitils.objectvalidation.objectcreator.generator.CompositeGenerator;
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 implements ObjectCreator {
    
    private static final Logger LOGGER = Logger.getLogger("ObjectCreator");
    private static final List FIELD_NAMES_TO_BE_IGNORED = asList("serialVersionUID", "$jacocoData");

    
    private Generator generator;

    /**
     * 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
     * @param type
     */
    ObjectCreatorImpl(Generator... generators) {
        CompositeGenerator gen = new CompositeGenerator();
        gen.addGenerators(generators);

        this.generator = gen;
    }

    /**
     * @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);
        Object randomObject = createFromTree(parentNode);
        
        fillInNullFields(randomObject);
        
        return randomObject;
    }

    private void fillInNullFields(Object randomObject) {
        if (randomObject != null) {
            
            Set fields = ReflectionUtils.getAllFields(randomObject.getClass());
            
            for (Field field : fields) {
                
                try {
                    if (!FIELD_NAMES_TO_BE_IGNORED.contains(field.getName()) && getFieldValue(randomObject, field) == null) {
                        
                        Object fieldValue = createRandomObject(field.getGenericType());
                        ReflectionUtils.setFieldValue(randomObject, field, fieldValue);
                        
                    }
                } catch (IllegalArgumentException e) {
                    throw new UnitilsException("Field " + field.getName() + " value could not be retrieved.", 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 generator.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;
    }

}