Package se.jbee.inject.bind

Source Code of se.jbee.inject.bind.Binder$ScopedBinder

/*
*  Copyright (c) 2012-2013, Jan Bernitt
*     
*  Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0
*/
package se.jbee.inject.bind;

import static se.jbee.inject.Instance.anyOf;
import static se.jbee.inject.Instance.defaultInstanceOf;
import static se.jbee.inject.Instance.instance;
import static se.jbee.inject.Type.raw;
import static se.jbee.inject.bootstrap.Configuring.configuring;
import static se.jbee.inject.util.Constructible.constructible;
import static se.jbee.inject.util.Metaclass.metaclass;
import static se.jbee.inject.util.Producible.producible;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import se.jbee.inject.Array;
import se.jbee.inject.Instance;
import se.jbee.inject.Name;
import se.jbee.inject.Packages;
import se.jbee.inject.Parameter;
import se.jbee.inject.Resource;
import se.jbee.inject.Scope;
import se.jbee.inject.Supplier;
import se.jbee.inject.Target;
import se.jbee.inject.Type;
import se.jbee.inject.bootstrap.Binding;
import se.jbee.inject.bootstrap.BindingType;
import se.jbee.inject.bootstrap.Configuring;
import se.jbee.inject.bootstrap.Inspector;
import se.jbee.inject.bootstrap.Module;
import se.jbee.inject.bootstrap.Naming;
import se.jbee.inject.bootstrap.SuppliedBy;
import se.jbee.inject.util.Factory;
import se.jbee.inject.util.Scoped;

/**
* The default implementation of a fluent binder interface that provides a lot of utility methods to
* improve readability and keep binding compact.
*
* @author Jan Bernitt (jan@jbee.se)
*/
public class Binder {

  public static RootBinder create( Bind bind ) {
    return new RootBinder( bind );
  }

  final RootBinder root;
  private final Bind bind;

  Binder( RootBinder root, Bind bind ) {
    super();
    this.root = root == null
      ? (RootBinder) this
      : root;
    this.bind = bind;
  }

  Bind bind() {
    return bind;
  }

  public <E> TypedElementBinder<E> arraybind( Class<E[]> type ) {
    return new TypedElementBinder<E>( bind(), defaultInstanceOf( raw( type ) ) );
  }

  public <T> TypedBinder<T> autobind( Class<T> type ) {
    return autobind( Type.raw( type ) );
  }

  public <T> TypedBinder<T> autobind( Type<T> type ) {
    return on( bind().autobinding().asAuto() ).bind( type );
  }

  public <T> TypedBinder<T> bind( Class<T> type ) {
    return bind( Type.raw( type ) );
  }

  public <T> TypedBinder<T> bind( Instance<T> instance ) {
    return new TypedBinder<T>( bind(), instance );
  }

  public <T> TypedBinder<T> bind( Name name, Class<T> type ) {
    return bind( name, Type.raw( type ) );
  }

  public <T> TypedBinder<T> bind( Name name, Type<T> type ) {
    return bind( instance( name, type ) );
  }

  public <T> TypedBinder<T> bind( Type<T> type ) {
    return bind( defaultInstanceOf( type ) );
  }

  public void construct( Class<?> type ) {
    construct( ( defaultInstanceOf( raw( type ) ) ) );
  }

  public void construct( Instance<?> instance ) {
    bind( instance ).toConstructor();
  }

  public void construct( Name name, Class<?> type ) {
    construct( instance( name, raw( type ) ) );
  }

  public <T> TypedBinder<T> multibind( Class<T> type ) {
    return multibind( Type.raw( type ) );
  }

  public <T> TypedBinder<T> multibind( Instance<T> instance ) {
    return on( bind().asMulti() ).bind( instance );
  }

  public <T> TypedBinder<T> multibind( Name name, Class<T> type ) {
    return multibind( instance( name, Type.raw( type ) ) );
  }

  public <T> TypedBinder<T> multibind( Name name, Type<T> type ) {
    return multibind( instance( name, type ) );
  }

  public <T> TypedBinder<T> multibind( Type<T> type ) {
    return multibind( defaultInstanceOf( type ) );
  }

  public <T> TypedBinder<T> starbind( Class<T> type ) {
    return bind( Instance.anyOf( Type.raw( type ) ) );
  }

  public <T, C> ConfigBinder<T> configbind( Class<T> type ) {
    return configbind( raw( type ) );
  }

  public <T, C> ConfigBinder<T> configbind( Type<T> type ) {
    return new ConfigBinder<T>( root, type );
  }

  protected Binder on( Bind bind ) {
    return new Binder( root, bind );
  }

  protected final <I> void implicitBindToConstructor( Class<I> impl ) {
    implicitBindToConstructor( defaultInstanceOf( raw( impl ) ) );
  }

  protected final <I> void implicitBindToConstructor( Instance<I> instance ) {
    Class<I> impl = instance.getType().getRawType();
    if ( metaclass( impl ).undeterminable() ) {
      return;
    }
    Constructor<I> constructor = bind().getInspector().constructorFor( impl );
    if ( constructor != null ) {
      implicit().with( Target.ANY ).bind( instance ).to( constructor );
    }
  }

  protected final Binder implicit() {
    return on( bind().asImplicit() );
  }

  protected Binder with( Target target ) {
    return new Binder( root, bind().with( target ) );
  }

  public static class ConfigBinder<T> {

    private final RootBinder binder;
    private final Type<T> type;

    ConfigBinder( RootBinder binder, Type<T> type ) {
      super();
      this.binder = new RootBinder( binder.bind().next() );
      this.type = type;
    }

    public <C> TypedBinder<T> on( Configuring<C> configuring, C value ) {
      binder.per( Scoped.INJECTION ).implicit().bind( type ).to( configuring );
      return binder.bind( configuring.name( value ), type );
    }

    public <C extends Enum<C>> TypedBinder<T> onOther( Class<C> valueType ) {
      return on( Name.DEFAULT, null, valueType, Configuring.ENUM );
    }

    public <C extends Enum<C>> TypedBinder<T> on( C value ) {
      return on( Name.DEFAULT, value, value.getDeclaringClass(), Configuring.ENUM );
    }

    public <C> TypedBinder<T> onOther( Name name, Class<C> valueType ) {
      return on( name, null, valueType, Configuring.TO_STRING );
    }

    public <C> TypedBinder<T> on( Name name, C value ) {
      return on( name, value, Configuring.TO_STRING );
    }

    public <C> TypedBinder<T> on( Name name, C value, Naming<? super C> naming ) {
      @SuppressWarnings ( "unchecked" )
      final Class<C> valueType = (Class<C>) value.getClass();
      return on( name, value, valueType, naming );
    }

    private <C> TypedBinder<T> on( Name name, C value, final Class<C> valueType,
        Naming<? super C> naming ) {
      return on( configuring( naming, instance( name, raw( valueType ) ) ), value );
    }

  }

  public static class InspectBinder {

    private final Inspector inspector;
    private final ScopedBinder binder;

    InspectBinder( Inspector inspector, RootBinder binder, Scope scope ) {
      super();
      this.inspector = inspector;
      this.binder = binder.on( binder.bind().asAuto() ).on( binder.bind().next() ).per( scope );
    }

    public void in( Class<?> implementor ) {
      in( implementor, new Parameter<?>[0] );
    }

    public void in( Object implementingInstance, Parameter<?>... parameters ) {
      bindMethodsIn( implementingInstance.getClass(), implementingInstance, parameters );
    }

    public void in( Class<?> implementer, Parameter<?>... parameters ) {
      boolean instanceMethods = bindMethodsIn( implementer, null, parameters );
      Constructor<?> c = inspector.constructorFor( implementer );
      if ( c == null ) {
        if ( instanceMethods ) {
          binder.root.per( Scoped.APPLICATION ).implicit().construct( implementer );
        }
      } else {
        if ( parameters.length == 0 ) {
          parameters = inspector.parametersFor( c );
        }
        bind( (Constructor<?>) c, parameters );
      }
    }

    private boolean bindMethodsIn( Class<?> implementer, Object instance,
        Parameter<?>[] parameters ) {
      boolean instanceMethods = false;
      for ( Method method : inspector.methodsIn( implementer ) ) {
        Type<?> returnType = Type.returnType( method );
        if ( !Type.VOID.equalTo( returnType ) ) {
          if ( parameters.length == 0 ) {
            parameters = inspector.parametersFor( method );
          }
          binder.bind( inspector.nameFor( method ), returnType ).to( instance, method,
              parameters );
          instanceMethods = instanceMethods || !Modifier.isStatic( method.getModifiers() );
        }
      }
      return instanceMethods;
    }

    private <T> void bind( Constructor<T> constructor, Parameter<?>... parameters ) {
      Name name = inspector.nameFor( constructor );
      Class<T> implementation = constructor.getDeclaringClass();
      if ( name.isDefault() ) {
        binder.autobind( implementation ).to( constructor, parameters );
      } else {
        binder.bind( name, implementation ).to( constructor, parameters );
        for ( Type<? super T> st : Type.raw( implementation ).supertypes() ) {
          if ( st.isInterface() ) {
            binder.implicit().bind( name, st ).to( name, implementation );
          }
        }
      }
    }

    public void in( Class<?> implementer, Class<?>... implementers ) {
      in( implementer );
      for ( Class<?> i : implementers ) {
        in( i );
      }
    }

    public void inModule() {
      in( binder.bind().source.getIdent() );
    }
  }

  public static class RootBinder
      extends ScopedBinder {

    RootBinder( Bind bind ) {
      super( null, bind );
    }

    public ScopedBinder per( Scope scope ) {
      return new ScopedBinder( root, bind().per( scope ) );
    }

    public RootBinder asDefault() {
      return on( bind().asDefault() );
    }

    //OPEN also allow naming for provided instances - this is used for value objects that become parameter

    public <T> void provide( Class<T> implementation, Parameter<?>... parameters ) {
      on( bind().autobinding().asProvided() ).bind( implementation ).toConstructor(
          parameters );
    }

    public <T> void require( Class<T> dependency ) {
      require( raw( dependency ) );
    }

    public <T> void require( Type<T> dependency ) {
      on( bind().asRequired() ).bind( dependency ).to( SuppliedBy.<T> required(),
          BindingType.REQUIRED );
    }

    @Override
    protected RootBinder on( Bind bind ) {
      return new RootBinder( bind );
    }

  }

  public static class ScopedBinder
      extends TargetedBinder {

    ScopedBinder( RootBinder root, Bind bind ) {
      super( root, bind );
    }

    public TargetedBinder injectingInto( Class<?> target ) {
      return injectingInto( raw( target ) );
    }

    public TargetedBinder injectingInto( Instance<?> target ) {
      return new TargetedBinder( root, bind().with( Target.targeting( target ) ) );
    }

    public TargetedBinder injectingInto( Name name, Class<?> type ) {
      return injectingInto( name, raw( type ) );
    }

    public TargetedBinder injectingInto( Name name, Type<?> type ) {
      return injectingInto( Instance.instance( name, type ) );
    }

    public TargetedBinder injectingInto( Type<?> target ) {
      return injectingInto( defaultInstanceOf( target ) );
    }

    public InspectBinder bind( Inspector inspector ) {
      return new InspectBinder( inspector, root, bind().scope );
    }

  }

  public static class TargetedBinder
      extends Binder {

    TargetedBinder( RootBinder root, Bind bind ) {
      super( root, bind );
    }

    public Binder in( Packages packages ) {
      return with( bind().target.in( packages ) );
    }

    public Binder inPackageAndSubPackagesOf( Class<?> type ) {
      return with( bind().target.inPackageAndSubPackagesOf( type ) );
    }

    public Binder inPackageOf( Class<?> type ) {
      return with( bind().target.inPackageOf( type ) );
    }

    public Binder inSubPackagesOf( Class<?> type ) {
      return with( bind().target.inSubPackagesOf( type ) );
    }

    public TargetedBinder within( Class<?> parent ) {
      return within( raw( parent ) );
    }

    public TargetedBinder within( Instance<?> parent ) {
      return new TargetedBinder( root, bind().within( parent ) );
    }

    public TargetedBinder within( Name name, Class<?> parent ) {
      return within( instance( name, raw( parent ) ) );
    }

    public TargetedBinder within( Name name, Type<?> parent ) {
      return within( instance( name, parent ) );
    }

    public TargetedBinder within( Type<?> parent ) {
      return within( anyOf( parent ) );
    }
  }

  public static class TypedBinder<T> {

    private final Bind bind;
    protected final Resource<T> resource;

    TypedBinder( Bind bind, Instance<T> instance ) {
      this( bind.next(), new Resource<T>( instance, bind.target ) );
    }

    TypedBinder( Bind bind, Resource<T> resource ) {
      super();
      this.bind = bind;
      this.resource = resource;
    }

    public <I extends T> void to( Class<I> impl ) {
      to( Instance.anyOf( raw( impl ) ) );
    }

    public void to( Constructor<? extends T> constructor, Parameter<?>... parameters ) {
      expand( constructible( constructor, parameters ) );
    }

    void to( Object instance, Method method, Parameter<?>[] parameters ) {
      expand( producible( method, parameters, instance ) );
    }

    public void toMacro( Module macro ) {
      if ( macro instanceof Binding<?> ) {
        Binding<?> b = (Binding<?>) macro;
        macro = bind().bindings.getMacros().expand( b, b );
      }
      macro.declare( bind().bindings );
    }

    protected final void expand( Object value ) {
      toMacro( bind().bindings.getMacros().expand( bind().asMacro( resource ), value ) );
    }

    public void to( Factory<? extends T> factory ) {
      to( SuppliedBy.factory( factory ) );
    }

    public void to( Supplier<? extends T> supplier ) {
      to( supplier, BindingType.PREDEFINED );
    }

    public final void to( T constant ) {
      toConstant( constant );
    }

    public final void to( T constant1, T constant2 ) {
      onMulti().toConstant( constant1 ).toConstant( constant2 );
    }

    public final void to( T constant1, T constant2, T constant3 ) {
      onMulti().toConstant( constant1 ).toConstant( constant2 ).toConstant( constant3 );
    }

    public final void to( T constant1, T... constants ) {
      TypedBinder<T> multibinder = onMulti().toConstant( constant1 );
      for ( int i = 0; i < constants.length; i++ ) {
        multibinder.toConstant( constants[i] );
      }
    }

    public void toConstructor() {
      to( bind().getInspector().constructorFor( resource.getType().getRawType() ) );
    }

    public void toConstructor( Class<? extends T> impl, Parameter<?>... parameters ) {
      if ( metaclass( impl ).undeterminable() ) {
        throw new IllegalArgumentException( "Not a constructable type: " + impl );
      }
      to( bind().getInspector().constructorFor( impl ), parameters );
    }

    public void toConstructor( Parameter<?>... parameters ) {
      toConstructor( getType().getRawType(), parameters );
    }

    public <I extends T> void to( Name name, Class<I> type ) {
      to( instance( name, raw( type ) ) );
    }

    public <I extends T> void to( Name name, Type<I> type ) {
      to( instance( name, type ) );
    }

    public <I extends T> void to( Instance<I> instance ) {
      expand( instance );
    }

    public void to( Configuring<?> configuration ) {
      expand( configuration );
    }

    public <I extends T> void toParametrized( Class<I> impl ) {
      expand( impl );
    }

    public <I extends Supplier<? extends T>> void toSupplier( Class<I> impl ) {
      expand( defaultInstanceOf( raw( impl ) ) );
    }

    protected final void to( Supplier<? extends T> supplier, BindingType type ) {
      toMacro( bind().asType( resource, type, supplier ) );
    }

    private TypedBinder<T> toConstant( T constant ) {
      to( SuppliedBy.constant( constant ) );
      return this;
    }

    final Bind bind() {
      return bind;
    }

    protected final Type<T> getType() {
      return resource.getType();
    }

    protected final TypedBinder<T> on( Bind bind ) {
      return new TypedBinder<T>( bind, resource );
    }

    protected final TypedBinder<T> onMulti() {
      return on( bind().asMulti() );
    }

  }

  /**
   * This kind of bindings actually re-map the []-type so that the automatic behavior of returning
   * all known instances of the element type will no longer be used whenever the bind made
   * applies.
   *
   * @author Jan Bernitt (jan@jbee.se)
   *
   */
  public static class TypedElementBinder<E>
      extends TypedBinder<E[]> {

    TypedElementBinder( Bind bind, Instance<E[]> instance ) {
      super( bind.asMulti().next(), instance );
    }

    @SuppressWarnings ( "unchecked" )
    public void toElements( Parameter<? extends E> p1 ) {
      toElements( new Parameter[] { p1 } );
    }

    @SuppressWarnings ( "unchecked" )
    public void toElements( Parameter<? extends E> p1, Parameter<? extends E> p2 ) {
      toElements( new Parameter[] { p1, p2 } );
    }

    @SuppressWarnings ( "unchecked" )
    public void toElements( Parameter<? extends E> p1, Parameter<? extends E> p2,
        Parameter<? extends E> p3 ) {
      toElements( new Parameter[] { p1, p2, p3 } );
    }

    public void toElements( Parameter<? extends E>... parameters ) {
      expand( parameters );
    }

    public void toElements( E c1 ) {
      to( array( c1 ) );
    }

    public void toElements( E c1, E c2 ) {
      to( array( c1, c2 ) );
    }

    public void toElements( E c1, E c2, E c3 ) {
      to( array( c1, c2, c3 ) );
    }

    public void toElements( E... constants ) {
      to( array( constants ) );
    }

    @SuppressWarnings ( "unchecked" )
    private E[] array( Object... elements ) {
      Class<E[]> rawType = getType().getRawType();
      if ( elements.getClass() == rawType ) {
        return (E[]) elements;
      }
      E[] a = Array.newArrayInstance( rawType, elements.length );
      System.arraycopy( elements, 0, a, 0, a.length );
      return a;
    }
  }

}
TOP

Related Classes of se.jbee.inject.bind.Binder$ScopedBinder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.