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

com.jparams.object.builder.provider.ObjectProvider Maven / Gradle / Ivy

package com.jparams.object.builder.provider;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Optional;

import com.jparams.object.builder.Context;
import com.jparams.object.builder.type.MemberType;
import com.jparams.object.builder.type.MemberTypeResolver;
import com.jparams.object.builder.utils.ObjectUtils;

public class ObjectProvider implements Provider
{
    private final InjectionStrategy injectionStrategy;

    public ObjectProvider(final InjectionStrategy injectionStrategy)
    {
        if (injectionStrategy == null)
        {
            throw new IllegalArgumentException("Injection Strategy is null");
        }

        this.injectionStrategy = injectionStrategy;
    }

    @Override
    public boolean supports(final Class clazz)
    {
        return !clazz.isPrimitive() && !clazz.isEnum() && !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers());
    }

    @Override
    public Object provide(final Context context)
    {
        switch (injectionStrategy)
        {
            case FIELD_INJECTION:
                return createInstanceWithFieldInjection(context);
            case CONSTRUCTOR_INJECTION:
                return createInstanceWithConstructor(context);
            default:
                context.logError("Unknown injection strategy " + injectionStrategy);
                return null;
        }
    }

    private Object createInstanceWithConstructor(final Context context)
    {
        final Optional> constructor = Arrays.stream(context.getPath().getMemberType().getType().getDeclaredConstructors())
                                                           .sorted(Comparator.comparingInt(Constructor::getParameterCount))
                                                           .peek(c -> c.setAccessible(true))
                                                           .findFirst();

        if (constructor.isPresent())
        {
            return createInstanceWithConstructor(context, constructor.get());
        }

        context.logError("No constructor found");
        return null;
    }

    private Object createInstanceWithConstructor(final Context context, final Constructor constructor)
    {
        final String name = String.format("%s(%s)", context.getPath().getMemberType().getType().getSimpleName(), getParametersString(constructor));
        final Object[] arguments = new Object[constructor.getParameters().length];

        for (int i = 0; i < constructor.getParameters().length; i++)
        {
            final Parameter parameter = constructor.getParameters()[i];
            final MemberType memberType = MemberTypeResolver.resolve(parameter);
            arguments[i] = context.createChild(name + "[" + i + "]", memberType);
        }

        try
        {
            return constructor.newInstance(arguments);
        }
        catch (final Exception e)
        {
            context.logError("Failed to construct an instance. Consider using Field Injection strategy", e);
            return null;
        }
    }

    private String getParametersString(final Constructor constructor)
    {
        return Arrays.stream(constructor.getParameters())
                     .map(param -> param.getType().getSimpleName() + " " + param.getName())
                     .reduce((item1, item2) -> item1 + ", " + item2)
                     .orElse("");
    }

    private Object createInstanceWithFieldInjection(final Context context)
    {
        final Class type = context.getPath().getMemberType().getType();

        try
        {
            final Object instance = ObjectUtils.createInstance(type);
            injectFields(instance, context);
            return instance;
        }
        catch (final Exception e)
        {
            context.logError("Field Injection strategy failed with error. Consider using Constructor Injection strategy.", e);
            return null;
        }
    }

    private void injectFields(final Object object, final Context context)
    {
        for (final Field field : ObjectUtils.getFields(object.getClass()))
        {
            try
            {
                final MemberType memberType = MemberTypeResolver.resolve(field);
                final Object instance = context.createChild(field.getName(), memberType);
                field.setAccessible(true);
                field.set(object, instance);
            }
            catch (final Exception e)
            {
                context.logError("Failed to inject field [" + field.getName() + "]", e);
            }
        }
    }

    public enum InjectionStrategy
    {
        /**
         * Constructs an instance of an object using the best possible constructor
         */
        CONSTRUCTOR_INJECTION,

        /**
         * Constructs an instance using field injection.
         */
        FIELD_INJECTION
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy