Package com.softwaremill.common.cdi.autofactory.extension

Source Code of com.softwaremill.common.cdi.autofactory.extension.AutoFactoryFromCreatedWithCreator

package com.softwaremill.common.cdi.autofactory.extension;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.jboss.weld.manager.BeanManagerImpl;
import org.jboss.weld.manager.SimpleInjectionTarget;
import org.jboss.weld.resources.ClassTransformer;
import com.softwaremill.common.cdi.autofactory.CreatedWith;
import com.softwaremill.common.cdi.autofactory.extension.parameter.ParameterValue;
import com.softwaremill.common.cdi.autofactory.extension.parameter.converter.ConstructorToParameterValuesConverter;
import com.softwaremill.common.cdi.autofactory.extension.parameter.converter.FactoryParameterOnlyConstructorConverter;
import com.softwaremill.common.cdi.autofactory.extension.parameter.converter.MixedConstructorConverter;

import javax.annotation.Nullable;
import javax.enterprise.inject.spi.*;
import javax.inject.Inject;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Set;

import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Iterables.concat;

/**
* @author Adam Warski (adam at warski dot org)
*/
public class AutoFactoryFromCreatedWithCreator<T> {
    private final BeanManager beanManager;
    private final AnnotatedType<T> createdType;
    private final Class<?> factoryClass;

    public AutoFactoryFromCreatedWithCreator(BeanManager beanManager, CreatedWith createdWithAnnotation,
                                             ProcessAnnotatedType<T> event) {
        this.beanManager = beanManager;
        this.createdType = event.getAnnotatedType();
        this.factoryClass = createdWithAnnotation.value();
    }

    public Bean<T> create() {
        checkFactoryClassIsAnInterface();

        Method soleFactoryMethod = getSoleFactoryMethod();
        AnnotatedConstructor<T> soleCreatedTypeConstructor = getSoleAcceptableConstructorOfCreatedType();

        boolean constructorInjection = isConstructorInjection(soleCreatedTypeConstructor);

        MethodParameterIndexer methodParameterIndexer = new MethodParameterIndexer(soleFactoryMethod);
        ConstructorToParameterValuesConverter converter = getConverter(soleCreatedTypeConstructor,
                methodParameterIndexer, constructorInjection);
        ParameterValue[] createdTypeConstructorParameterValues = converter.convert();

        // We cannot use BeanManager.createInjectionTarget as this adds the injection target to validation
        InjectionTarget<T> injectionTarget = createInjectionTargetWithoutValidation();

        return new AutoFactoryBean<T>(beanManager, factoryClass,
                new CreatedTypeData<T>(createdTypeConstructorParameterValues,
                soleCreatedTypeConstructor, injectionTarget,
                constructorInjection));
    }

    private SimpleInjectionTarget<T> createInjectionTargetWithoutValidation() {
        BeanManagerImpl bmi = (BeanManagerImpl) beanManager;
        return new SimpleInjectionTarget<T>(bmi.getServices().get(ClassTransformer.class).loadClass(createdType), bmi);
    }

    private void checkFactoryClassIsAnInterface() {
        if (!factoryClass.isInterface()) {
            throw new RuntimeException("A factory class "  + factoryClass + "specified in the argument of @CreatedWith for " +
                    createdType + " is not an interface. ");
        }
    }

    private Method getSoleFactoryMethod() {
        if (factoryClass.getMethods().length != 1) {
            throw new RuntimeException("A factory class (" + factoryClass + ") can have only one method. " +
                    "Used through @CreatedWith by: " + createdType);
        }
       
        return factoryClass.getMethods()[0];
    }

    private AnnotatedConstructor<T> getSoleAcceptableConstructorOfCreatedType() {
        /* Yup, I know, imperatively it would've been shorter */
        final Predicate<AnnotatedConstructor<T>> diConstructorPredicate = new Predicate<AnnotatedConstructor<T>>() {
            @Override
            public boolean apply(@Nullable AnnotatedConstructor<T> input) {
                return input.getJavaMember().getAnnotation(Inject.class) != null;
            }
        };

        Set<? extends AnnotatedConstructor<T>> allConstructors = createdType.getConstructors();
        Collection<? extends AnnotatedConstructor<T>> diConstructors = filter(allConstructors,
                diConstructorPredicate);

        if (diConstructors.size() > 1 || (allConstructors.size() > 1 && diConstructors.size() == 0)) {
            throw new RuntimeException("A bean created with an auto-factory " +
                    "can have only one constructor: " +
                    createdType);
        }

        return Iterables.get(concat(diConstructors, allConstructors), 0);
    }

    private ConstructorToParameterValuesConverter getConverter(AnnotatedConstructor<?> soleCreatedTypeConstructor,
                                                               MethodParameterIndexer methodParameterIndexer,
                                                               boolean constructorInjection) {
        if (constructorInjection) {
            return new MixedConstructorConverter(new QualifierAnnotationsFilter(beanManager),
                    soleCreatedTypeConstructor, methodParameterIndexer);
        } else {
            // If the constructor isn't annotated with @Inject, the no dependencies are injected, all parameters
            // are taken from the factory method.
            return new FactoryParameterOnlyConstructorConverter(soleCreatedTypeConstructor, methodParameterIndexer);
        }
    }

    private boolean isConstructorInjection(AnnotatedConstructor<T> soleCreatedTypeConstructor) {
        return soleCreatedTypeConstructor.getAnnotation(Inject.class) != null;
    }
}
TOP

Related Classes of com.softwaremill.common.cdi.autofactory.extension.AutoFactoryFromCreatedWithCreator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.