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

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

There is a newer version: 2.3.1
Show newest version
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.BuildStrategy;
import com.jparams.object.builder.Context;
import com.jparams.object.builder.type.Type;
import com.jparams.object.builder.type.TypeMap;
import com.jparams.object.builder.type.TypeResolver;
import com.jparams.object.builder.utils.ObjectUtils;

public class ObjectProvider implements Provider
{
    private final BuildStrategy defaultStrategy;
    private final TypeMap strategyMap;

    public ObjectProvider(final BuildStrategy defaultStrategy, final TypeMap strategyMap)
    {
        this.defaultStrategy = defaultStrategy;
        this.strategyMap = strategyMap;
    }

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

    @Override
    public Object provide(final Context context)
    {
        final BuildStrategy strategy = strategyMap.findMatch(context.getPath().getType()).orElse(defaultStrategy);

        switch (strategy)
        {
            case CONSTRUCTOR:
                return createInstanceWithConstructor(context, true);
            case FIELD_INJECTION:
                return createInstanceWithFieldInjection(context);
            case AUTO:
                return createInstanceWithFallback(context);
            default:
                context.logError("Unknown injection strategy " + strategy);
                return null;
        }
    }

    private Object createInstanceWithFallback(final Context context)
    {
        final Object created = createInstanceWithConstructor(context, false);
        return created == null ? createInstanceWithFieldInjection(context) : created;
    }

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

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

        if (logOnFailure)
        {
            context.logError("No constructor found");
        }

        return null;
    }

    private Object createInstanceWithConstructor(final Context context, final Constructor constructor, final boolean logOnFailure)
    {
        final String name = String.format("%s(%s)", context.getPath().getType().getJavaType().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 Type type = TypeResolver.resolve(parameter);
            arguments[i] = context.createChild(name + "[" + i + "]", type);
        }

        try
        {
            return constructor.newInstance(arguments);
        }
        catch (final Exception e)
        {
            if (logOnFailure)
            {
                context.logError("Failed to build an instance using the constructor build 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().getType().getJavaType();

        try
        {
            final Object instance = ObjectUtils.createInstance(type);
            injectFields(instance, context);
            return instance;
        }
        catch (final Exception e)
        {
            context.logError("Failed to build an instance using the field injection build strategy", e);
            return null;
        }
    }

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy