Locked History Actions

subcut/Injectable

Injectable.scala

package org.scala_tools.subcut.inject

/**
* クラスあるいはオブジェクトに依存性注入機能を提供するトレイト。このトレイトを使うには、クラスあるいはオブジェクトの
* 定義にミックスした上で、使用するバインディングを保持するbindingModuleを定義すること。
* これを提供するには様々な方法がある。クラス中で単純なvalとしてオーバライドする、コンストラクタパラメータとする、
* あるいは最もフレキシブルな方法であるが、カリー化されたパラメータリストとしてimplicitなコンストラクタパラメータ
* とする方法がある。
* 最後の方法は、フレキシブルであり、たいていの場合インスタンス生成チェインを下るときに見えないようにすることができる。
*/
trait Injectable {
  val bindingModule: BindingModule

  /**
* Inject an instance for the given trait based on the class type required. If there is no binding, this
* method will throw a BindingException. This form is for straight trait injection without an identifying name.
* @return an instance configured by the binding module to use for the given trait.
*/
  def inject[T <: Any](implicit m: scala.reflect.Manifest[T]): T =
    bindingModule.inject(m.erasure.asInstanceOf[Class[T]], None)

  /**
* Inject an instance for the given trait based on the class type required and an ID symbol. If there is no
* matching binding, this method will throw a BindingException. The Symbol provided will be converted to a string
* prior to the lookup, so the symbol is interchangeable with the string version of the same ID, in other words
* 'maxPoolSize and "maxPoolSize" are considered equivalent by the lookup mechanism.
* @param symbol the identifying name to look up for the binding, e.g. 'maxPoolSize
* @return an instance configured by the binding module to use for the given trait and ID
*/
  def inject[T <: Any](symbol: Symbol)(implicit m: scala.reflect.Manifest[T]): T =
    bindingModule.inject(m.erasure.asInstanceOf[Class[T]], Some(symbol.name))

  /**
* Inject an instance for the given trait based on the class type required and an ID string. If there is no
* matching binding, this method will throw a BindingException. The string ID is interchangeable with the
* symbol version of the same ID, in other words 'maxPoolSize and "maxPoolSize" are considered equivalent by the
* lookup mechanism.
* @param symbol the identifying name to look up for the binding, e.g. "maxPoolSize"
* @return an instance configured by the binding module to use for the given trait and ID
*/
  def inject[T <: Any](name: String)(implicit m: scala.reflect.Manifest[T]): T =
    bindingModule.inject(m.erasure.asInstanceOf[Class[T]], Some(name))

  /**
* Inject an instance for the given trait based on the class type only if there is no instance already provided.
* If no instance is provided (i.e. the existing impl passed in is null) and no binding is available to match, a
* BindingException will be thrown. If an existing impl is provided (not null), then the binding will not be
* used and does not need to be present. This form of the inject does not need a provided ID symbol or string.
* @param the implToUse from the call site. If it is null, the binding provider will fill it in instead
* @return an instance configured by the binding module to use for the given trait
*/
  def injectIfMissing[T <: Any](implToUse: Option[T])(implicit m: scala.reflect.Manifest[T]): T =
    if (implToUse != None) implToUse.get
    else inject[T]

  /**
* Inject an instance for the given trait based on the class type only if there is no instance already provided.
* If no instance is provided (i.e. the existing impl passed in is null) and no binding is available to match, a
* BindingException will be thrown. If an existing impl is provided (not null), then the binding will not be
* used and does not need to be present. This form of the inject takes a symbol ID to use to match the binding.
* @param implToUse from the call site. If it is null, the binding provider will fill it in instead
* @param name binding ID symbol to use - e.g. 'maxPoolSize
* @return an instance configured by the binding module to use for the given trait
*/
  def injectIfMissing[T <: Any](implToUse: Option[T], name: String)(implicit m: scala.reflect.Manifest[T]): T =
    if (implToUse != None) implToUse.get
    else inject[T](name)

  /**
* Inject an instance for the given trait based on the class type only if there is no instance already provided.
* If no instance is provided (i.e. the existing impl passed in is null) and no binding is available to match, a
* BindingException will be thrown. If an existing impl is provided (not null), then the binding will not be
* used and does not need to be present. This form of the inject takes a string ID to use to match the binding.
* @param implToUse from the call site. If it is null, the binding provider will fill it in instead
* @param name binding ID string to use - e.g. 'maxPoolSize
* @return an instance configured by the binding module to use for the given trait
*/
  def injectIfMissing[T <: Any](implToUse: Option[T], symbol: Symbol)(implicit m: scala.reflect.Manifest[T]): T =
    if (implToUse != None) implToUse.get
    else inject[T](symbol)

  /**
* Inject an instance if a binding for that type is defined. If it is not defined, the function provided will
* be used instead to create an instance to be used. This is arguably the most useful and efficient form of
* injection usage, as the typical configuration can be provided at the call site and developers can easily
* see what the "usual" instance is. An alternative binding will only be used if it is defined, e.g. for testing.
* This form of the injector takes only a trait to match and no ID name.
* @param fn a function to be used to return an instance, if there is no binding defined for the desired trait.
* @return an instance that subclasses the trait, either from the binding definitions, or using the provided
* function if no matching binding is defined.
*/
  def injectIfBound[T <: Any](fn: => T)(implicit m: scala.reflect.Manifest[T]): T = {
    bindingModule.injectOptional(m.erasure.asInstanceOf[Class[Any]], None) match {
      case None => // must then have a valid impltouse
        val implToUse = fn
        if (implToUse == null)
          throw new IllegalStateException("No binding for %s, so provided impl function may not result in null".format(m.erasure.toString))
        implToUse
      case Some(instance) => instance.asInstanceOf[T]
    }
  }

  /**
* Inject an instance if a binding for that type is defined. If it is not defined, the function provided will
* be used instead to create an instance to be used. This is arguably the most useful and efficient form of
* injection usage, as the typical configuration can be provided at the call site and developers can easily
* see what the "usual" instance is. An alternative binding will only be used if it is defined, e.g. for testing.
* This form of the injector takes a symbol ID to use in the binding definition lookup, e.g. 'maxPoolSize.
* @param name symbol ID to be used to identify the matching binding definition.
* @param fn a function to be used to return an instance, if there is no binding defined for the desired trait.
* @return an instance that subclasses the trait, either from the binding definitions, or using the provided
* function if no matching binding is defined.
*/
  def injectIfBound[T <: Any](name: String)(fn: => T)(implicit m: scala.reflect.Manifest[T]): T = {
    bindingModule.injectOptional(m.erasure.asInstanceOf[Class[Any]], Some(name)) match {
      case None => // must then have a valid impltouse
        val implToUse = fn
        if (implToUse == null)
          throw new IllegalStateException("No binding for %s named %s, so provided impl function may not result in null".format(m.erasure.toString, name))
        implToUse
      case Some(instance) => instance.asInstanceOf[T]
    }
  }

  /**
* Inject an instance if a binding for that type is defined. If it is not defined, the function provided will
* be used instead to create an instance to be used. This is arguably the most useful and efficient form of
* injection usage, as the typical configuration can be provided at the call site and developers can easily
* see what the "usual" instance is. An alternative binding will only be used if it is defined, e.g. for testing.
* This form of the injector takes a string ID to use in the binding definition lookup, e.g. "maxPoolSize".
* @param name string ID to be used to identify the matching binding definition.
* @param fn a function to be used to return an instance, if there is no binding defined for the desired trait.
* @return an instance that subclasses the trait, either from the binding definitions, or using the provided
* function if no matching binding is defined.
*/
  def injectIfBound[T <: Any](symbol: Symbol)(fn: => T)(implicit m: scala.reflect.Manifest[T]): T =
    injectIfBound(symbol.name)(fn)

}

/**
* A trait that can be used to provide the cake-like ability to mix in a trait upon instance creation
* rather than using the implicit parameter. This is less flexible but may still be desired by some
* developers. To use it, simply create a new trait that extends this one, and provide the definition of
* bindingModule to point to a suitable BindingModule. You can then mix this trait in to any other class or
* new composition to provide the injector bindings.
*/
trait BoundToModule {
  val bindingModule: BindingModule
}