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

com.coditory.quark.context.ContextBuilder Maven / Gradle / Ivy

There is a newer version: 0.1.22
Show newest version
package com.coditory.quark.context;

import com.coditory.quark.context.annotations.Bean;
import com.coditory.quark.context.annotations.Configuration;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.StreamSupport;

import static com.coditory.quark.context.Args.checkNonNull;
import static com.coditory.quark.context.BeanDescriptor.descriptor;
import static com.coditory.quark.context.BeanHolder.holder;
import static com.coditory.quark.context.ConstructorBasedBeanCreator.fromConstructor;

public final class ContextBuilder {
    private final Set> beanHolders = new LinkedHashSet<>();
    private final Map properties = new LinkedHashMap<>();

    public ContextBuilder scanPackage(Class type) {
        checkNonNull(type, "type");
        return scanPackage(type.getPackageName(), name -> true);
    }

    public ContextBuilder scanPackage(Class type, Predicate canonicalNameFilter) {
        checkNonNull(type, "type");
        checkNonNull(canonicalNameFilter, "canonicalNameFilter");
        return scanPackage(type.getPackageName(), canonicalNameFilter);
    }

    public ContextBuilder scanPackage(String packageName) {
        checkNonNull(packageName, "packageName");
        return scanPackage(packageName, name -> true);
    }

    public ContextBuilder scanPackage(String packageName, Predicate canonicalNameFilter) {
        checkNonNull(packageName, "packageName");
        checkNonNull(canonicalNameFilter, "canonicalNameFilter");
        Iterable> iterable = () -> ClasspathScanner.scanPackageAndSubPackages(packageName, canonicalNameFilter);
        StreamSupport.stream(iterable.spliterator(), false)
                .forEach(this::scanClass);
        return this;
    }

    public  ContextBuilder scanClass(Class type) {
        checkNonNull(type, "type");
        Configuration configuration = type.getAnnotation(Configuration.class);
        if (configuration != null) {
            if (type.isAnnotationPresent(Bean.class)) {
                throw new ContextException("Detected incompatible annotations on class: " + type.getCanonicalName()
                        + ". Class should have one annotation: @Configuration or @Bean.");
            }
            addBeansFromConfiguration(configuration, type);
        } else if (type.isAnnotationPresent(Bean.class)) {
            addAnnotatedBeanClass(type);
        }
        return this;
    }

    private  void addAnnotatedBeanClass(Class type) {
        Bean annotation = type.getAnnotation(Bean.class);
        BeanCreator creator = fromConstructor(type);
        addAnnotatedCreator(annotation, type, creator);
    }

    private  void addBeansFromConfiguration(Configuration configuration, Class type) {
        String name = configuration.name().isBlank()
                ? configuration.value()
                : configuration.name();
        BeanHolder holder = holder(descriptor(type, name), fromConstructor(type), configuration.eager());
        addBeanHolder(holder);
        for (Method method : type.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Bean.class)) {
                method.setAccessible(true);
                addFromAnnotatedConfiguration(holder, method, method.getReturnType());
            }
        }
    }

    private  void addFromAnnotatedConfiguration(BeanHolder holder, Method method, Class returnType) {
        BeanCreator creator = new MethodBasedBeanCreator<>(holder, method);
        Bean annotation = method.getAnnotation(Bean.class);
        addAnnotatedCreator(annotation, returnType, creator);
    }

    private  void addAnnotatedCreator(Bean annotation, Class type, BeanCreator creator) {
        String name = annotation.name().isBlank()
                ? annotation.value()
                : annotation.name();
        BeanHolder holder = holder(descriptor(type, name), creator, annotation.eager());
        addBeanHolder(holder);
    }

    public  ContextBuilder setProperty(String name, Object value) {
        checkNonNull(name, "name");
        checkNonNull(value, "value");
        properties.put(name, value);
        return this;
    }

    public  ContextBuilder setProperties(Map properties) {
        checkNonNull(properties, "properties");
        this.properties.putAll(properties);
        return this;
    }

    @SuppressWarnings("unchecked")
    public  ContextBuilder add(T bean) {
        checkNonNull(bean, "bean");
        BeanDescriptor descriptor = descriptor((Class) bean.getClass());
        BeanHolder holder = holder(descriptor, (ctx) -> bean, true);
        addBeanHolder(holder);
        return this;
    }

    @SuppressWarnings("unchecked")
    public  ContextBuilder add(T bean, String name) {
        checkNonNull(bean, "bean");
        checkNonNull(name, "name");
        BeanDescriptor descriptor = descriptor((Class) bean.getClass(), name);
        BeanHolder holder = holder(descriptor, (ctx) -> bean, true);
        addBeanHolder(holder);
        return this;
    }

    public  ContextBuilder add(Class type, Predicate condition, BeanCreator beanCreator) {
        checkNonNull(condition, "condition");
        checkNonNull(type, "type");
        checkNonNull(beanCreator, "beanCreator");
        return add(type, wrapBeanCreator(condition, beanCreator));
    }

    public  ContextBuilder add(Class type, String name, Predicate condition, BeanCreator beanCreator) {
        checkNonNull(condition, "condition");
        checkNonNull(type, "type");
        checkNonNull(name, "name");
        checkNonNull(beanCreator, "beanCreator");
        return add(type, name, wrapBeanCreator(condition, beanCreator));
    }

    private  BeanCreator wrapBeanCreator(Predicate condition, BeanCreator beanCreator) {
        return new BeanCreator() {
            @Override
            public T create(ResolutionContext context) {
                return beanCreator.create(context);
            }

            @Override
            public boolean isActive(ConditionContext context) {
                return beanCreator.isActive(context) && condition.test(context);
            }
        };
    }

    public  ContextBuilder add(Class type, BeanCreator beanCreator) {
        checkNonNull(beanCreator, "beanCreator");
        addBeanHolder(holder(descriptor(type), beanCreator));
        return this;
    }

    public  ContextBuilder add(Class type, String name, BeanCreator beanCreator) {
        checkNonNull(name, "name");
        checkNonNull(beanCreator, "beanCreator");
        addBeanHolder(holder(descriptor(type, name), beanCreator));
        return this;
    }

    private void addBeanHolder(BeanHolder holder) {
        beanHolders.add(holder);
    }

    public Context buildEager() {
        Map, List>> holders = resolveConditionalBeans();
        Context context = Context.create(holders, properties);
        holders.forEach((descriptor, creator) -> {
            if (descriptor.getName() != null) {
                context.get(descriptor.getType(), descriptor.getName());
            } else {
                context.get(descriptor.getType());
            }
        });
        return context;
    }

    public Context build() {
        return Context.create(resolveConditionalBeans(), properties);
    }

    private Map, List>> resolveConditionalBeans() {
        return ContextResolver.resolve(beanHolders, properties);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy