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

se.jbee.inject.bind.Binder Maven / Gradle / Ivy

There is a newer version: 0.6
Show newest version
/*
 *  Copyright (c) 2012, 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.bind.SuppliedBy.constant;
import static se.jbee.inject.bind.SuppliedBy.parametrizedInstance;
import static se.jbee.inject.bind.SuppliedBy.provider;
import static se.jbee.inject.util.Metaclass.metaclass;

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

import se.jbee.inject.DeclarationType;
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.Source;
import se.jbee.inject.Supplier;
import se.jbee.inject.Target;
import se.jbee.inject.Type;
import se.jbee.inject.bootstrap.Bindings;
import se.jbee.inject.bootstrap.Inspector;
import se.jbee.inject.util.Factory;
import se.jbee.inject.util.Provider;

/**
 * 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 ([email protected])
 */
public class Binder {

	public static Bindings autobinding( Bindings delegate ) {
		return new AutobindBindings( delegate );
	}

	public static RootBinder create( Bindings bindings, Inspector inspector, Source source,
			Scope scope ) {
		return new RootBinder( bindings, inspector, source, scope );
	}

	final Bindings bindings;
	final Inspector inspector;
	final Source source;
	final Scope scope;
	final Target target;

	Binder( Bindings bindings, Inspector inspector, Source source, Scope scope, Target target ) {
		super();
		this.bindings = bindings;
		this.inspector = inspector;
		this.source = source;
		this.scope = scope;
		this.target = target;
	}

	public  TypedElementBinder arraybind( Class type ) {
		return new TypedElementBinder( this, defaultInstanceOf( raw( type ) ) );
	}

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

	public  TypedBinder autobind( Type type ) {
		return into( autobinding( bindings ) ).auto().bind( type );
	}

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

	public  TypedBinder bind( Instance instance ) {
		return new TypedBinder( this, instance );
	}

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

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

	public  TypedBinder bind( Type 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  TypedBinder multibind( Class type ) {
		return multibind( Type.raw( type ) );
	}

	public  TypedBinder multibind( Instance instance ) {
		return multi().bind( instance );
	}

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

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

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

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

	protected final  void bind( Resource resource, Supplier supplier ) {
		bindings.add( resource, supplier, scope, source );
	}

	protected final Binder implicit() {
		return with( source.typed( DeclarationType.IMPLICIT ) );
	}

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

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

	protected Binder into( Bindings bindings ) {
		return new Binder( bindings, inspector, source, scope, target );
	}

	protected Binder multi() {
		return with( source.typed( DeclarationType.MULTI ) );
	}

	protected Binder auto() {
		return with( source.typed( DeclarationType.AUTO ) );
	}

	protected Binder with( Source source ) {
		return new Binder( bindings, inspector, source, scope, target );
	}

	protected Binder with( Target target ) {
		return new Binder( bindings, inspector, source, scope, target );
	}

	public static class InspectBinder {

		private final Inspector inspector;
		private final RootBinder binder;

		InspectBinder( Inspector inspector, RootBinder binder ) {
			super();
			this.inspector = inspector;
			this.binder = binder.with( binder.source.typed( DeclarationType.AUTO ) );
		}

		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 implementor, Parameter... parameters ) {
			boolean instanceMethods = bindMethodsIn( implementor, null, parameters );
			Constructor c = inspector.constructorFor( implementor );
			if ( c == null ) {
				if ( instanceMethods ) {
					binder.implicit().bind( implementor ).toConstructor();
				}
			} else {
				if ( parameters.length == 0 ) {
					parameters = inspector.parametersFor( c );
				}
				bind( (Constructor) c, parameters );
			}
		}

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

		private  void bind( Type returnType, Method method, Object instance,
				Parameter[] parameters ) {
			binder.bind( inspector.nameFor( method ), returnType ).to(
					SuppliedBy.method( returnType, method, instance, parameters ) );
		}

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

		public void in( Class implementor, Class... implementors ) {
			in( implementor );
			for ( Class i : implementors ) {
				in( i );
			}
		}

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

	public static class RootBinder
			extends ScopedBinder {

		RootBinder( Bindings bindings, Inspector inspector, Source source, Scope scope ) {
			super( bindings, inspector, source, scope );
		}

		public ScopedBinder per( Scope scope ) {
			return new ScopedBinder( bindings, inspector, source, scope );
		}

		public InspectBinder bind( Inspector inspector ) {
			return new InspectBinder( inspector, this );
		}

		public RootBinder asDefault() {
			return with( source.typed( DeclarationType.DEFAULT ) );
		}

		@Override
		protected RootBinder into( Bindings bindings ) {
			return new RootBinder( bindings, inspector, source, scope );
		}

		protected RootBinder using( Inspector inspector ) {
			return new RootBinder( bindings, inspector, source, scope );
		}

		@Override
		protected RootBinder with( Source source ) {
			return new RootBinder( bindings, inspector, source, scope );
		}
	}

	public static class ScopedBinder
			extends TargetedBinder {

		ScopedBinder( Bindings bindings, Inspector inspector, Source source, Scope scope ) {
			super( bindings, inspector, source, scope, Target.ANY );
		}

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

		public TargetedBinder injectingInto( Instance target ) {
			return new TargetedBinder( bindings, inspector, source, scope,
					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 static class TargetedBinder
			extends Binder {

		TargetedBinder( Bindings bindings, Inspector inspector, Source source, Scope scope,
				Target target ) {
			super( bindings, inspector, source, scope, target );
		}

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

		public Binder inPackageAndSubPackagesOf( Class type ) {
			return with( target.inPackageAndSubPackagesOf( type ) );
		}

		public Binder inPackageOf( Class type ) {
			return with( target.inPackageOf( type ) );
		}

		public Binder inSubPackagesOf( Class type ) {
			return with( target.inSubPackagesOf( type ) );
		}

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

		public TargetedBinder within( Instance parent ) {
			return new TargetedBinder( bindings, inspector, source, scope, target.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 {

		/**
		 * The binder instance who's {@link RichBasicBinder#assemble(Instance)} method had been
		 * called to get to this {@link TypedBinder}.
		 */
		private final Binder binder;
		private final Resource resource;

		TypedBinder( Binder binder, Instance instance ) {
			this( binder, new Resource( instance, binder.target ) );
		}

		TypedBinder( Binder binder, Resource resource ) {
			super();
			this.binder = binder;
			this.resource = resource;
		}

		public  void to( Class impl ) {
			to( Instance.anyOf( raw( impl ) ) );
		}

		public void to( Constructor constructor, Parameter... parameters ) {
			to( SuppliedBy.costructor( constructor, parameters ) );
		}

		public void to( Factory factory ) {
			to( SuppliedBy.factory( factory ) );
		}

		public  void to( Instance instance ) {
			to( supply( instance ) );
		}

		public  void to( Name name, Class type ) {
			to( instance( name, raw( type ) ) );
		}

		public  void to( Name name, Type type ) {
			to( instance( name, type ) );
		}

		public void to( Provider provider ) {
			to( provider( provider ) );
			implicitBindToConstant( provider );
		}

		public void to( Supplier supplier ) {
			binder.bind( resource, supplier );
		}

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

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

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

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

		public void toConstructor() {
			to( binder.inspector.constructorFor( resource.getType().getRawType() ) );
		}

		public void toConstructor( Class impl, Parameter... parameters ) {
			if ( metaclass( impl ).undeterminable() ) {
				throw new IllegalArgumentException( "Not a constructable type: " + impl );
			}
			to( SuppliedBy.costructor( binder.inspector.constructorFor( impl ), parameters ) );
		}

		public void toConstructor( Parameter... parameters ) {
			toConstructor( resource.getType().getRawType(), parameters );
		}

		public  void toParametrized( Class impl ) {
			to( parametrizedInstance( Instance.anyOf( raw( impl ) ) ) );
		}

		public > void toSupplier( Class impl ) {
			to( SuppliedBy.reference( impl ) );
			implicitBindToConstructor( defaultInstanceOf( raw( impl ) ) );
		}

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

		protected final TypedBinder multi() {
			return new TypedBinder( binder.multi(), resource );
		}

		 Supplier supply( Class impl ) {
			return supply( Instance.anyOf( raw( impl ) ) );
		}

		 Supplier supply( Instance instance ) {
			if ( !resource.getInstance().equalTo( instance ) ) {
				implicitBindToConstructor( instance );
				return SuppliedBy.instance( instance );
			}
			if ( instance.getType().getRawType().isInterface() ) {
				throw new IllegalArgumentException( "Interface type linked in a loop: "
						+ resource.getInstance() + " > " + instance );
			}
			return SuppliedBy.costructor( binder.inspector.constructorFor( instance.getType().getRawType() ) );
		}

		@SuppressWarnings ( "unchecked" )
		private 

void implicitBindToConstant( Provider

provider ) { Type> providerType = (Type>) Type.supertype( Provider.class, raw( provider.getClass() ) ); binder.implicit().bind( defaultInstanceOf( providerType ) ).to( SuppliedBy.constant( provider ) ); } private void implicitBindToConstructor( Instance instance ) { binder.implicitBindToConstructor( instance ); } private TypedBinder toConstant( T constant ) { to( SuppliedBy.constant( constant ) ); return this; } } /** * 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 ([email protected]) * */ public static class TypedElementBinder extends TypedBinder { TypedElementBinder( Binder binder, Instance instance ) { super( binder.multi(), instance ); } @SuppressWarnings ( "unchecked" ) public void to( Supplier supplier1, Supplier supplier2 ) { to( (Supplier[]) new Supplier[] { supplier1, supplier2 } ); } @SuppressWarnings ( "unchecked" ) public void to( Supplier supplier1, Supplier supplier2, Supplier supplier3 ) { to( (Supplier[]) new Supplier[] { supplier1, supplier2, supplier3 } ); } public void to( Supplier[] elements ) { to( SuppliedBy.elements( getType().getRawType(), elements ) ); } @SuppressWarnings ( "unchecked" ) public void toElements( Class... impls ) { Supplier[] suppliers = (Supplier[]) new Supplier[impls.length]; for ( int i = 0; i < impls.length; i++ ) { suppliers[i] = supply( impls[i] ); } to( suppliers ); } public void toElements( Class impl1, Class impl2 ) { to( supply( impl1 ), supply( impl2 ) ); } public void toElements( Class impl1, Class impl2, Class impl3 ) { to( supply( impl1 ), supply( impl2 ), supply( impl3 ) ); } @SuppressWarnings ( "unchecked" ) public void toElements( Class impl1, Class impl2, Class impl3, Class impl4 ) { to( (Supplier[]) new Supplier[] { supply( impl1 ), supply( impl2 ), supply( impl3 ), supply( impl4 ) } ); } public void toElements( E constant1, E constant2 ) { to( constant( constant1 ), constant( constant2 ) ); } } private static class AutobindBindings implements Bindings { private final Bindings delegate; AutobindBindings( Bindings delegate ) { super(); this.delegate = delegate; } @Override public void add( Resource resource, Supplier supplier, Scope scope, Source source ) { delegate.add( resource, supplier, scope, source ); Type type = resource.getType(); for ( Type supertype : type.supertypes() ) { // Object is of cause a superclass of everything but not indented when doing auto-binds if ( supertype.getRawType() != Object.class ) { delegate.add( resource.typed( supertype ), supplier, scope, source ); } } } } }