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

com.google.gwt.inject.client.multibindings.GinMapBinder Maven / Gradle / Ivy

Go to download

GIN (GWT INjection) brings automatic dependency injection to Google Web Toolkit client-side code. GIN is built on top of Guice and uses (a subset of) Guice's binding language.

There is a newer version: 2.1.2
Show newest version
/*
 * Copyright 2013 Google Inc.
 *
 * 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.google.gwt.inject.client.multibindings;

import static com.google.gwt.inject.client.multibindings.TypeLiterals.mapOf;
import static com.google.gwt.inject.client.multibindings.TypeLiterals.providerOf;
import static com.google.gwt.inject.client.multibindings.TypeLiterals.setOf;

import com.google.gwt.inject.client.binder.GinBinder;
import com.google.gwt.inject.client.binder.GinLinkedBindingBuilder;
import com.google.gwt.inject.client.binder.GinScopedBindingBuilder;
import com.google.gwt.inject.client.multibindings.InternalModule.SingletonInternalModule;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;

import java.lang.annotation.Annotation;
import java.util.Map;

/**
 * A utility that mimics the behavior and API of Guice MapBinder for GIN.
 *
 * 

Example usage: *

 *   interface X {};
 *
 *   class X1Impl implements X {};
 *
 *   class X2Impl implements X {};
 *
 *   class X3Provider implements Provider<X> {};
 *
 *   GinMapBinder<String, X> mapBinder =
 *       GinMapBinder.newMapBinder(binder(), String.class, X.class);
 *   mapBinder.addBinding("id1").to(X1Impl.class);
 *   mapBinder.addBinding("id2").to(X2Impl.class);
 *   mapBinder.addBinding("id3").toProvider(X3Provider.class);
 * 
* *

* GIN supports instance binding for only limited set of types. To overcome this limitation, * GinMapBinder provides {@link #addBinding(Class)} method so bindings can be added via a key * provider class that will instantiate the actual key during runtime. This alternative approach * is needed to used for all key types that cannot be bound via * {@link com.google.gwt.inject.client.binder.GinConstantBindingBuilder}: *

 *   class Place {
 *     public Place(String key) { ... }
 *   }
 *
 *   class HomePlaceProvider implements Provider<Place> {
 *     public Place get() {
 *       return new Place("home");
 *     }
 *   }
 *
 *   class AboutPlaceProvider implements Provider<Place> {
 *     public Place get() {
 *       return new Place("about");
 *     }
 *   }
 *
 *   GinMapBinder<Place, X> mapBinder =
 *       GinMapBinder.newMapBinder(binder(), Place.class, X.class);
 *   mapBinder.addBinding(HomePlaceProvider.class).to(XImpl1.class);
 *   mapBinder.addBinding(AboutPlaceProvider.class).to(XImpl2.class);
 * 
*

* * @param type of key for map * @param type of value for map */ public final class GinMapBinder { /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with no binding annotation. */ public static GinMapBinder newMapBinder( GinBinder binder, TypeLiteral keyType, TypeLiteral valueType) { return newMapBinder(binder, keyType, valueType, Key.get(entryOf(keyType, valueType))); } /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with no binding annotation. */ public static GinMapBinder newMapBinder( GinBinder binder, Class keyType, Class valueType) { return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType)); } /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with {@code annotation}. */ public static GinMapBinder newMapBinder( GinBinder binder, TypeLiteral keyType, TypeLiteral valueType, Annotation annotation) { return newMapBinder( binder, keyType, valueType, Key.get(entryOf(keyType, valueType), annotation)); } /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with {@code annotation}. */ public static GinMapBinder newMapBinder( GinBinder binder, Class keyType, Class valueType, Annotation annotation) { return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotation); } /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with {@code annotationType}. */ public static GinMapBinder newMapBinder(GinBinder binder, TypeLiteral keyType, TypeLiteral valueType, Class annotationType) { return newMapBinder( binder, keyType, valueType, Key.get(entryOf(keyType, valueType), annotationType)); } /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with {@code annotationType}. */ public static GinMapBinder newMapBinder(GinBinder binder, Class keyType, Class valueType, Class annotationType) { return newMapBinder( binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotationType); } private static GinMapBinder newMapBinder(GinBinder binder, TypeLiteral keyType, TypeLiteral valueType, Key> registryKey) { GinMapBinder mapBinder = new GinMapBinder(binder, keyType, valueType, registryKey); mapBinder.install(); return mapBinder; } private final GinBinder ginBinder; private final TypeLiteral keyType; private final TypeLiteral valueType; private final Key> multibindingKey; private GinMapBinder(GinBinder ginBinder, TypeLiteral keyType, TypeLiteral valueType, Key> keyForMultibinding) { this.ginBinder = ginBinder; this.keyType = keyType; this.valueType = valueType; this.multibindingKey = keyForMultibinding; } private void install() { ginBinder.install(new RuntimeBindingsRegistryModule>(multibindingKey)); ginBinder.install(new MapModule()); } /** * Configures the {@code MapBinder} to handle duplicate entries. *

* When multiple equal keys are bound, the value that gets included in the map is arbitrary. *

* In addition to the {@code Map} and {@code Map>} maps that are normally * bound, a {@code Map>} and {@code Map>>} are also bound, * which contain all values bound to each key. *

* When multiple modules contribute elements to the map, this configuration option impacts all of * them. * * @return this map binder */ public GinMapBinder permitDuplicates() { ginBinder.install(new PermitDuplicatesModule>(multibindingKey)); ginBinder.install(new MultimapModule()); return this; } /** * Returns a binding builder used to add a new entry in the map. Each key must be distinct (and * non-null). Bound providers will be evaluated each time the map is injected. *

* It is an error to call this method without also calling one of the {@code to} methods on the * returned binding builder. *

* Scoping elements independently is supported. Use the {@code in} method to specify a binding * scope. */ public GinLinkedBindingBuilder addBinding(K key) { BindingRecorder recorder = createRecorder(); if (key instanceof String) { recorder.bindConstant().to((String) key); } else if (key instanceof Enum) { recorder.bindConstant().to((Enum) key); } else if (key instanceof Integer) { recorder.bindConstant().to((Integer) key); } else if (key instanceof Long) { recorder.bindConstant().to((Long) key); } else if (key instanceof Float) { recorder.bindConstant().to((Float) key); } else if (key instanceof Double) { recorder.bindConstant().to((Double) key); } else if (key instanceof Short) { recorder.bindConstant().to((Short) key); } else if (key instanceof Boolean) { recorder.bindConstant().to((Boolean) key); } else if (key instanceof Character) { recorder.bindConstant().to((Character) key); } else if (key instanceof Class) { recorder.bindConstant().to((Class) key); } else { throw new IllegalArgumentException( "Key type " + keyType + " is non-constant and can only be added using providers"); } return recorder.bind(valueType); } /** * Returns a binding builder used to add a new entry in the map using a key provider. *

* This API is not compatible with Guice however it is provided as GIN has limitation to bind * 'instances'. For that reason for all key types that are not defined in * {@link com.google.gwt.inject.client.binder.GinConstantBindingBuilder} needs to use a provider * class for each key together with this method. * * @see #addBinding(Object) */ public GinLinkedBindingBuilder addBinding( Class> keyProvider) { return addBinding(TypeLiteral.get(keyProvider)); } /** * Returns a binding builder used to add a new entry in the map using a key provider. *

* This API is not compatible with Guice however it is provided as GIN has limitation to bind * 'instances'. For that reason for all key types that are not defined in * {@link com.google.gwt.inject.client.binder.GinConstantBindingBuilder} needs to use a provider * class for each key together with this method. * * @see #addBinding(Object) */ public GinLinkedBindingBuilder addBinding( TypeLiteral> keyProvider) { BindingRecorder recorder = createRecorder(); recorder.bind(keyType).toProvider(Key.get(keyProvider)); return recorder.bind(valueType); } private BindingRecorder createRecorder() { BindingRecorder recorder = new BindingRecorder(ginBinder, multibindingKey); // binds @Internal MapEntry to MapEntry recorder.bind(multibindingKey.getTypeLiteral()).to(multibindingKey.getTypeLiteral()); return recorder; } // TODO(user): not private due to http://code.google.com/p/google-gin/issues/detail?id=184 final class MapModule extends AbstractMapModule { @Override protected void configure() { bindInternalBindingsRegistry(); bindMap(valueType, providerForMapOf(keyType, valueType)); bindMap(providerOf(valueType), providerForProviderMapOf(keyType, valueType)) .in(Singleton.class); } } // TODO(user): not private due to http://code.google.com/p/google-gin/issues/detail?id=184 final class MultimapModule extends AbstractMapModule { @Override protected void configure() { bindInternalBindingsRegistry(); bindMap(setOf(valueType), providerForMultiMapOf(keyType, valueType)); bindMap(setOf(providerOf(valueType)), providerForProviderMultiMapOf(keyType, valueType)) .in(Singleton.class); } } private abstract class AbstractMapModule extends SingletonInternalModule> { public AbstractMapModule() { super(multibindingKey); } protected GinScopedBindingBuilder bindMap( TypeLiteral valueType, TypeLiteral>> providerType) { return bindAndExpose(mapOf(keyType, valueType)).toProvider(Key.get(providerType)); } } @SuppressWarnings("unchecked") private static TypeLiteral> entryOf( TypeLiteral keyType, TypeLiteral valueType) { return TypeLiterals.newParameterizedType(MapEntry.class, keyType, valueType); } @SuppressWarnings("unchecked") private static TypeLiteral> providerForMapOf(TypeLiteral keyType, TypeLiteral valueType) { return TypeLiterals.newParameterizedType(ProviderForMap.class, keyType, valueType); } @SuppressWarnings("unchecked") private static TypeLiteral> providerForMultiMapOf( TypeLiteral keyType, TypeLiteral valueType) { return TypeLiterals.newParameterizedType(ProviderForMultiMap.class, keyType, valueType); } @SuppressWarnings("unchecked") private static TypeLiteral> providerForProviderMapOf( TypeLiteral keyType, TypeLiteral valueType) { return TypeLiterals.newParameterizedType(ProviderForProviderMap.class, keyType, valueType); } @SuppressWarnings("unchecked") private static TypeLiteral> providerForProviderMultiMapOf(TypeLiteral keyType, TypeLiteral valueType) { return TypeLiterals.newParameterizedType(ProviderForProviderMultiMap.class, keyType, valueType); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy