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

net.codingwell.scalaguice.ScalaMultibinder.scala Maven / Gradle / Ivy

/*
 *  Copyright 2010-2014 Benjamin Lings
 *  Author: Thomas Suckow
 *
 * 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 net.codingwell.scalaguice

import com.google.inject.multibindings.Multibinder
import com.google.inject.{Binder, Key, Module, TypeLiteral}
import java.lang.annotation.Annotation
import java.util.{Set => JSet}
import net.codingwell.scalaguice.ScalaModule.ScalaLinkedBindingBuilder
import scala.collection.{immutable => im}
import scala.reflect.ClassTag
import scala.reflect.runtime.universe.TypeTag

/**
 * Analog to Guice's Multibinder
 *
 * Use ScalaMultibinder.newSetBinder to create a multibinder that is scala friendly.
 */
sealed trait ScalaMultibinder[T] {
  /**
   * Configures the bound set to silently discard duplicate elements. When multiple equal values are
   * bound, the one that gets included is arbitrary. When multiple modules contribute elements to
   * the set, this configuration option impacts all of them.
   *
   * @return this multibinder
   * @since 3.0
   */
  def permitDuplicates(): ScalaMultibinder[T]

  /**
   * Returns a binding builder used to add a new element in the set. Each
   * bound element must have a distinct value. Bound providers will be
   * evaluated each time the set is injected.
   *
   * 

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

Scoping elements independently is supported. Use the `in` method * to specify a binding scope. */ def addBinding: ScalaLinkedBindingBuilder[T] } object ScalaMultibinder { /** Preferred Scala Methods */ /** * Returns a new multibinder that collects instances of type `T` in a [[scala.collection.immutable.Set]] that is * itself bound with no binding annotation. */ def newSetBinder[T: TypeTag](binder: Binder): ScalaMultibinder[T] = { newMultibinder(binder, typeLiteral[T]) } /** * Returns a new multibinder that collects instances of type `T` in a [[scala.collection.immutable.Set]] that is * itself bound with a binding annotation `Ann`. */ def newSetBinder[T: TypeTag, Ann <: Annotation : ClassTag](binder: Binder): ScalaMultibinder[T] = { newMultibinder[T, Ann](binder, typeLiteral[T], cls[Ann]) } /** * Returns a new multibinder that collects instances of type `T` in a [[scala.collection.immutable.Set]] that is * itself bound with a binding annotation. */ def newSetBinder[T: TypeTag](binder: Binder, annotation: Annotation): ScalaMultibinder[T] = { newMultibinder(binder, typeLiteral[T], annotation) } /** Methods Compatible w/Guice's API */ /** * Returns a new multibinder that collects instances of `typ` in a [[scala.collection.immutable.Set]] that is * itself bound with no binding annotation. */ def newSetBinder[T](binder: Binder, typ: TypeLiteral[T]): ScalaMultibinder[T] = { newMultibinder(binder, typ) } /** * Returns a new multibinder that collects instances of `typ` in a [[scala.collection.immutable.Set]] that is * itself bound with no binding annotation. Note that `typ` is ignored in favor of using the `T` TypeTag to capture * type arguments. */ def newSetBinder[T: TypeTag](binder: Binder, typ: Class[T]): ScalaMultibinder[T] = { newMultibinder(binder, typeLiteral[T]) } /** * Returns a new multibinder that collects instances of `typ` in a [[scala.collection.immutable.Set]] that is * itself bound with a binding annotation. */ def newSetBinder[T: TypeTag](binder: Binder, typ: TypeLiteral[T], annotation: Annotation): ScalaMultibinder[T] = { newMultibinder(binder, typ, annotation) } /** * Returns a new multibinder that collects instances of `typ` in a [[scala.collection.immutable.Set]] that is * itself bound with a binding annotation. Note that `typ` is ignored in favor of using the TypeTag to capture * type arguments. */ def newSetBinder[T: TypeTag](binder: Binder, typ: Class[T], annotation: Annotation): ScalaMultibinder[T] = { newMultibinder(binder, typeLiteral[T], annotation) } /** * Returns a new multibinder that collects instances of `typ` in a [[scala.collection.immutable.Set]] that is * itself bound with a binding annotation. */ def newSetBinder[T](binder: Binder, typ: TypeLiteral[T], annotation: Class[_ <: Annotation]): ScalaMultibinder[T] = { newMultibinder(binder, typ, annotation) } /** * Returns a new multibinder that collects instances of `typ` in a [[scala.collection.immutable.Set]] that is * itself bound with a binding annotation. Note that `typ` is ignored in favor of using the TypeTag to capture * type arguments. */ def newSetBinder[T: TypeTag](binder: Binder, typ: Class[T], annotation: Class[_ <: Annotation]): ScalaMultibinder[T] = { newMultibinder(binder, typeLiteral[T], annotation) } /** Implementation Details */ private def newMultibinder[T](parentBinder: Binder, typ: TypeLiteral[T]): ScalaMultibinder[T] = { val binder = skipSources(parentBinder) val jMultibinder = Multibinder.newSetBinder(binder, typ) newMultibinder(binder, jMultibinder, Key.get(typ)) } private def newMultibinder[T](parentBinder: Binder, typ: TypeLiteral[T], annotation: Annotation): ScalaMultibinder[T] = { val binder = skipSources(parentBinder) val jMultibinder = Multibinder.newSetBinder(binder, typ, annotation) newMultibinder(binder, jMultibinder, Key.get(typ, annotation)) } private def newMultibinder[T, Ann <: Annotation](parentBinder: Binder, typ: TypeLiteral[T], annotationType: Class[Ann]): ScalaMultibinder[T] = { val binder = skipSources(parentBinder) val jMultibinder = Multibinder.newSetBinder(binder, typ, annotationType) newMultibinder(binder, jMultibinder, Key.get(typ, annotationType)) } private def newMultibinder[T](binder: Binder, parent: Multibinder[T], key: Key[T]): ScalaMultibinder[T] = { val result = new RealScalaMultibinder[T](parent, key) binder.install(result) result } private def skipSources(binder: Binder): Binder = { binder.skipSources( ScalaMultibinder.getClass, classOf[ScalaMultibinder[_]], classOf[RealScalaMultibinder[_]] ) } /** * Analog to the Guice's [[com.google.inject.multibindings.Multibinder.RealMultibinder]] * * As a Module, the [[RealScalaMultibinder]] installs the binding to the set itself. As a module, this implements * `equals()` and `hashCode()` in order to trick Guice into executing its `configure` method only once. That makes * it so that multiple binders can be created for the same target collection, but only one is bound. The binding maps * the [[java.util.Set]] to a [[im.Set]] for useful Scala injection. */ private class RealScalaMultibinder[T](parent: Multibinder[T], key: Key[T]) extends ScalaMultibinder[T] with Module { private val setKey = key.ofType(wrap[im.Set].around(key.getTypeLiteral)) private[this] val setName = nameOf(setKey) def addBinding: ScalaLinkedBindingBuilder[T] = new ScalaLinkedBindingBuilder[T] { val self = parent.addBinding() } def permitDuplicates(): ScalaMultibinder[T] = { parent.permitDuplicates this } def getJavaMultibinder: Multibinder[T] = { parent } def configure(binder: Binder): Unit = { binder.bind(setKey).toProvider(new SetProvider(key.ofType(wrap[JSet].around(key.getTypeLiteral)))) } /** Trick Guice into installing this Module once; be careful to not use the jSetKey. */ override def equals(o: Any): Boolean = o match { case o: RealScalaMultibinder[_] => o.setKey == setKey case _ => false } override def hashCode: Int = { setKey.hashCode } override def toString: String = { (if (setName.isEmpty) "" else setName + " ") + "ScalaMultibinder<" + key.getTypeLiteral + ">" } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy