com.github.jeluard.extend.PluginLoader.java Source code

Java tutorial

Introduction

Here is the source code for com.github.jeluard.extend.PluginLoader.java

Source

/**
 * Copyright 2012 Julien Eluard
 * This project includes software developed by Julien Eluard: https://github.com/jeluard/
 *
 * 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.
 */
package com.github.jeluard.extend;

import com.github.jeluard.extend.spi.Loader;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class PluginLoader {

    /**
     * {@link Predicate} filtering default plugin implementation (i.e. type defining {@link Plugin.Default} annotation). 
     * @param <T> 
     */
    private static final class DefaultPluginPredicate<T> implements Predicate<T> {
        @Override
        public boolean apply(final T input) {
            Preconditions.checkNotNull(input, "null input");

            return input.getClass().getAnnotation(Plugin.Default.class) != null;//Class#getAnnotation returns null if annotation is not defined.
        }
    }

    private final Loader loader;

    public PluginLoader(final Loader loader) {
        Preconditions.checkNotNull(loader, "null loader");

        this.loader = loader;
    }

    /**
     * Load the implementation of T.
     * Expects to detect a single {@link Plugin.Default} and a single alternative implementation.
     *
     * @param <T>
     * @param type
     * @return the implementation of T
     * @throws IllegalArgumentException if does not detect a single {@link Plugin.Default} and a single alternative implementation
     */
    public <T extends Plugin.Alternative> T load(final Class<T> type) {
        Preconditions.checkNotNull(type, "null type");

        final List<T> plugins = Lists.newArrayList(this.loader.load(type));
        if (plugins.isEmpty()) {
            throw new IllegalArgumentException("No implementation for " + type);
        }
        final Predicate<T> defautPluginPredicate = new PluginLoader.DefaultPluginPredicate<T>();
        final Collection<T> defaults = Collections2.filter(plugins, defautPluginPredicate);
        final Collection<T> alternatives = Collections2.filter(plugins, Predicates.not(defautPluginPredicate));
        if (defaults.isEmpty()) {
            throw new IllegalArgumentException(
                    "No default implementation for " + type + " among <" + Iterables.toString(plugins) + ">");
        }
        if (defaults.size() > 1) {
            throw new IllegalArgumentException("Found multiple default implementation for " + type + ": <"
                    + Iterables.toString(defaults) + ">");
        }

        if (plugins.size() == 1) {
            //Plugins consist of a single default
            return defaults.iterator().next();
        } else if (alternatives.size() > 1) {
            throw new IllegalArgumentException("Found multiple non-default implementation for " + type + ": <"
                    + Iterables.toString(alternatives) + ">");
        } else {
            //Only one alternative
            return alternatives.iterator().next();
        }
    }

    private Optional<Plugin.Priority> loadPriorityAnnotation(final Class<?> type) {
        return Optional.fromNullable(type.getAnnotation(Plugin.Priority.class));
    }

    /**
     * Load all implementations of T.
     *
     * @param <T>
     * @param type
     * @return all implementations of T
     */
    public <T extends Plugin.Extension> List<T> loadAll(final Class<T> type) {
        Preconditions.checkNotNull(type, "null type");

        final List<T> plugins = Lists.newArrayList(this.loader.load(type));

        //Sort in place
        Collections.sort(plugins, new Comparator<T>() {
            @Override
            public int compare(final T plugin1, final T plugin2) {
                Preconditions.checkNotNull(plugin1, "null plugin1");
                Preconditions.checkNotNull(plugin2, "null plugin2");

                final Optional<Plugin.Priority> optionalAnnotation1 = loadPriorityAnnotation(plugin1.getClass());
                final Optional<Plugin.Priority> optionalAnnotation2 = loadPriorityAnnotation(plugin2.getClass());
                if (optionalAnnotation1.isPresent() && optionalAnnotation2.isPresent()) {
                    return Ints.compare(optionalAnnotation2.get().value(), optionalAnnotation1.get().value()); //Reverse order so that higher priority goes first
                } else if (optionalAnnotation1.isPresent()) {
                    return -1;
                } else if (optionalAnnotation2.isPresent()) {
                    return 1;
                }
                return 0;
            }
        });
        return Collections.unmodifiableList(plugins);
    }

}