Java tutorial
/** * 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); } }