diff --git a/magnolia-core/src/main/java/info/magnolia/objectfactory/annotation/Multi.java b/magnolia-core/src/main/java/info/magnolia/objectfactory/annotation/Multi.java new file mode 100644 index 0000000..7e3df35 --- /dev/null +++ b/magnolia-core/src/main/java/info/magnolia/objectfactory/annotation/Multi.java @@ -0,0 +1,54 @@ +/** + * This file Copyright (c) 2011-2014 Magnolia International + * Ltd. (http://www.magnolia-cms.com). All rights reserved. + * + * + * This file is dual-licensed under both the Magnolia + * Network Agreement and the GNU General Public License. + * You may elect to use one or the other of these licenses. + * + * This file is distributed in the hope that it will be + * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT. + * Redistribution, except as permitted by whichever of the GPL + * or MNA you select, is prohibited. + * + * 1. For the GPL license (GPL), you can redistribute and/or + * modify this file under the terms of the GNU General + * Public License, Version 3, as published by the Free Software + * Foundation. You should have received a copy of the GNU + * General Public License, Version 3 along with this program; + * if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * 2. For the Magnolia Network Agreement (MNA), this file + * and the accompanying materials are made available under the + * terms of the MNA which accompanies this distribution, and + * is available at http://www.magnolia-cms.com/mna.html + * + * Any modifications to this file must keep this entire header + * intact. + * + */ +package info.magnolia.objectfactory.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Components with this annotation (directly or in one of their super types) will be registered in multi-bind mode + * (as well as regular mode) for the type where the annotation is. + * E.g. if type Foo is annoted with @Multi, and we register 3 components who happen to implement Foo, regardless of the + * key with which they specific components are registered, one will be able to get Set injected, with a set containing + * these 3 implementations. + */ +@Target({TYPE}) +@Retention(RUNTIME) +@Documented +public @interface Multi { + +} diff --git a/magnolia-core/src/main/java/info/magnolia/objectfactory/guice/GuiceComponentConfigurationModule.java b/magnolia-core/src/main/java/info/magnolia/objectfactory/guice/GuiceComponentConfigurationModule.java index 4d09235..57f1a7f 100644 --- a/magnolia-core/src/main/java/info/magnolia/objectfactory/guice/GuiceComponentConfigurationModule.java +++ b/magnolia-core/src/main/java/info/magnolia/objectfactory/guice/GuiceComponentConfigurationModule.java @@ -33,10 +33,13 @@ */ package info.magnolia.objectfactory.guice; +import com.google.inject.binder.LinkedBindingBuilder; +import com.google.inject.multibindings.Multibinder; import info.magnolia.module.model.ComponentDefinition; import info.magnolia.objectfactory.ComponentFactory; import info.magnolia.objectfactory.annotation.LazySingleton; import info.magnolia.objectfactory.annotation.LocalScoped; +import info.magnolia.objectfactory.annotation.Multi; import info.magnolia.objectfactory.annotation.SessionScoped; import info.magnolia.objectfactory.configuration.ComponentConfiguration; import info.magnolia.objectfactory.configuration.ComponentProviderConfiguration; @@ -47,6 +50,7 @@ import info.magnolia.objectfactory.configuration.ProviderConfiguration; import java.lang.annotation.Annotation; import java.util.Map; +import java.util.Set; import javax.inject.Provider; import javax.inject.Singleton; @@ -57,6 +61,7 @@ import com.google.inject.AbstractModule; import com.google.inject.Module; import com.google.inject.binder.ScopedBindingBuilder; import com.google.inject.util.Providers; +import org.reflections.ReflectionUtils; /** @@ -155,8 +160,27 @@ public class GuiceComponentConfigurationModule extends AbstractModule { builder = bind(key).to(implementation); } bindInScope(builder, configuration); + + // if anything in type hierarchy of impl has @Multi + final Set> multis = getAllSuperTypesWith(implementation, Multi.class); + for (Class multi : multis) { + addMultiBinding(implementation, multi, getScope(configuration)); + } + } + + private void addMultiBinding(Class implementation, Class multi, Class scope) { + final Multibinder multiBinder = Multibinder.newSetBinder(binder(), multi, scope); + final LinkedBindingBuilder builder = multiBinder.addBinding(); + builder.in(scope); + builder.to((Class) implementation); } + @SuppressWarnings("unchecked") + private static Set> getAllSuperTypesWith(Class type, Class annotationClass) { + return ReflectionUtils.getAllSuperTypes(type, ReflectionUtils.withAnnotation(annotationClass)); + } + + private ScopedBindingBuilder bindProvider(Class type, Provider provider) { return bind(type).toProvider(Providers.guicify(provider)); }