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

org.osgl.inject.loader.AnnotatedElementLoader Maven / Gradle / Ivy

There is a newer version: 1.13.2
Show newest version
package org.osgl.inject.loader;

/*-
 * #%L
 * OSGL Genie
 * %%
 * Copyright (C) 2017 OSGL (Open Source General Library)
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import org.osgl.$;
import org.osgl.inject.BeanSpec;
import org.osgl.inject.ElementType;
import org.osgl.inject.Genie;
import org.osgl.util.E;

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;

/**
 * Implementation of `AnnotatedElementLoader` shall load beans whose class has
 * been annotated by a certain annotation class
 */
public abstract class AnnotatedElementLoader extends ElementLoaderBase {


    /**
     * This method will load instances of all public and non-abstract classes that
     * has been annotated by annotation class specified as `value` in `options`
     *
     * @param options   optional parameters specified to refine the loading process
     * @param container the bean spec about the container into which the bean to be loaded
     * @param genie     the dependency injector
     * @return the list of bean instances
     */
    @Override
    public Iterable load(Map options, BeanSpec container, final Genie genie) {
        Object hint = options.get("value");
        E.illegalArgumentIf(!Annotation.class.isAssignableFrom((Class) hint));
        boolean loadNonPublic = (Boolean)options.get("loadNonPublic");
        ElementType elementType = elementType(options, container);
        boolean loadAbstract = elementType.loadAbstract() && (Boolean) options.get("loadAbstract");
        List> classes = load(annoClassFromHint(hint), loadNonPublic, loadAbstract);
        return elementType.transform(classes, genie);
    }

    /**
     * Implementation shall load list of beans whose class is annotated with `annoClass`
     * specified. The class of all beans returned must have `public` access
     *
     * @param annoClass the annotation class
     * @param loadNonPublic specify if it should load non public classes
     * @param loadAbstract specify if it should load abstract classes
     * @return a list of classes that are annotated with `annoClass`
     */
    protected abstract List> load(
            Class annoClass,
            boolean loadNonPublic,
            boolean loadAbstract);

    /**
     * Returns a predicate check if an object has annotation as specified as `hint`
     *
     * @param options   not used
     * @param container the bean spec of the container into which the element will be loaded
     * @return a predicate function as described above
     */
    @Override
    public $.Function filter(Map options, BeanSpec container) {
        Object hint = options.get("value");
        E.illegalArgumentIf(!Annotation.class.isAssignableFrom((Class) hint));
        final Class annoClass = annoClassFromHint(hint);
        final ElementType elementType = elementType(options, container);
        final boolean loadNonPublic = (Boolean)options.get("loadNonPublic");
        final boolean loadAbstract = elementType.loadAbstract() && (Boolean) options.get("loadAbstract");
        return new $.Predicate() {
            @Override
            public boolean test(Object o) {
                if (elementType == ElementType.BEAN) {
                    Class c = o.getClass();
                    return (loadNonPublic || Modifier.isPublic(c.getModifiers())) && c.isAnnotationPresent(annoClass);
                } else {
                    if (o instanceof Class) {
                        Class c = (Class) o;
                        int modifiers = c.getModifiers();
                        boolean yes = loadNonPublic || Modifier.isPublic(modifiers);
                        yes = yes && (loadAbstract || !Modifier.isAbstract(modifiers));
                        yes = yes && c.isAnnotationPresent(annoClass);
                        return yes;
                    }
                    return false;
                }
            }
        };
    }

    private static Class annoClassFromHint(Object hint) {
        return $.cast(hint);
    }

    private ElementType elementType(Map options, BeanSpec container) {
        ElementType type = (ElementType) options.get("elementType");
        if ((type == ElementType.CLASS)) {
            return type;
        }
        // check container element generic type
        $.Var typeVar = $.var(type);
        LoaderUtil.targetClass(typeVar, container);
        return typeVar.get();
    }


}