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

se.jbee.inject.bind.SuppliedBy 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.Dependency.dependency;
import static se.jbee.inject.Type.parameterTypes;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import se.jbee.inject.Array;
import se.jbee.inject.Dependency;
import se.jbee.inject.Injector;
import se.jbee.inject.Instance;
import se.jbee.inject.Parameter;
import se.jbee.inject.Supplier;
import se.jbee.inject.Type;
import se.jbee.inject.bootstrap.Invoke;
import se.jbee.inject.util.Argument;
import se.jbee.inject.util.Factory;
import se.jbee.inject.util.Metaclass;
import se.jbee.inject.util.Provider;

/**
 * Utility as a factory to create different kinds of {@link Supplier}s.
 * 
 * @author Jan Bernitt ([email protected])
 */
public final class SuppliedBy {

	private static final Object[] NO_ARGS = new Object[0];

	public static final Supplier> PROVIDER_BRIDGE = new ProviderSupplier();
	public static final Supplier> LIST_BRIDGE = new ArrayToListBridgeSupplier();
	public static final Supplier> SET_BRIDGE = new ArrayToSetBridgeSupplier();
	public static final Factory LOGGER = new LoggerFactory();

	public static  Supplier provider( Provider provider ) {
		return new ProviderAsSupplier( provider );
	}

	public static  Supplier> constant( Provider provider ) {
		return new ConstantSupplier>( provider );
	}

	public static  Supplier constant( T constant ) {
		return new ConstantSupplier( constant );
	}

	public static  Supplier reference( Class> type ) {
		return new ReferenceSupplier( type );
	}

	public static  Supplier instance( Instance instance ) {
		return new InstanceSupplier( instance );
	}

	public static  Supplier parametrizedInstance( Instance instance ) {
		return new ParametrizedInstanceSupplier( instance );
	}

	public static  Supplier elements( Class arrayType, Supplier[] elements ) {
		return new ElementsSupplier( arrayType, elements );
	}

	public static  Supplier method( Type returnType, Method factory, Object instance,
			Parameter... parameters ) {
		if ( !Type.returnType( factory ).isAssignableTo( returnType ) ) {
			throw new IllegalArgumentException( "The factory methods methods return type `"
					+ Type.returnType( factory ) + "` is not assignable to: " + returnType );
		}
		if ( instance != null && factory.getDeclaringClass() != instance.getClass() ) {
			throw new IllegalArgumentException(
					"The factory method and the instance it is invoked on have to be the same class." );
		}
		Argument[] arguments = Argument.arguments( Type.parameterTypes( factory ), parameters );
		return new FactoryMethodSupplier( returnType, factory, instance, arguments );
	}

	public static  Supplier costructor( Constructor constructor,
			Parameter... parameters ) {
		final Class[] params = constructor.getParameterTypes();
		if ( params.length == 0 ) {
			return new StaticConstructorSupplier( constructor, NO_ARGS );
		}
		Argument[] arguments = Argument.arguments( parameterTypes( constructor ), parameters );
		return Argument.allConstants( arguments )
			? new StaticConstructorSupplier( constructor, Argument.constantsFrom( arguments ) )
			: new ConstructorSupplier( constructor, arguments );
	}

	public static  Supplier factory( Factory factory ) {
		return new FactorySupplier( factory );
	}

	private SuppliedBy() {
		throw new UnsupportedOperationException( "util" );
	}

	private static abstract class ArrayBridgeSupplier
			implements Supplier {

		ArrayBridgeSupplier() {
			// make visible
		}

		@Override
		public final T supply( Dependency dependency, Injector injector ) {
			Type elementType = dependency.getType().parameter( 0 );
			return bridge( supplyArray( dependency.anyTyped( elementType.getArrayType() ), injector ) );
		}

		private static  E[] supplyArray( Dependency elementType, Injector resolver ) {
			return resolver.resolve( elementType );
		}

		abstract  T bridge( E[] elements );
	}

	/**
	 * Shows how support for {@link List}s and such works.
	 * 
	 * Basically we just resolve the array of the element type (generic of the list). Arrays itself
	 * have build in support that will (if not redefined by a more precise binding) return all known
	 * 
	 * @author Jan Bernitt ([email protected])
	 * 
	 */
	private static final class ArrayToListBridgeSupplier
			extends ArrayBridgeSupplier> {

		ArrayToListBridgeSupplier() {
			//make visible
		}

		@Override
		 List bridge( E[] elements ) {
			return new ArrayList( Arrays.asList( elements ) );
		}

	}

	private static final class ArrayToSetBridgeSupplier
			extends ArrayBridgeSupplier> {

		ArrayToSetBridgeSupplier() {
			// make visible
		}

		@Override
		 Set bridge( E[] elements ) {
			return new HashSet( Arrays.asList( elements ) );
		}

	}

	private static final class ConstantSupplier
			implements Supplier {

		private final T instance;

		ConstantSupplier( T instance ) {
			super();
			this.instance = instance;
		}

		@Override
		public T supply( Dependency dependency, Injector injector ) {
			return instance;
		}

		@Override
		public String toString() {
			return instance.toString();
		}

	}

	/**
	 * A {@link Supplier} uses multiple different separate suppliers to provide the elements of a
	 * array of the supplied type.
	 * 
	 * @author Jan Bernitt ([email protected])
	 */
	private static final class ElementsSupplier
			implements Supplier {

		private final Class arrayType;
		private final Supplier[] elements;

		ElementsSupplier( Class arrayType, Supplier[] elements ) {
			super();
			this.arrayType = arrayType;
			this.elements = elements;
		}

		@SuppressWarnings ( "unchecked" )
		@Override
		public E[] supply( Dependency dependency, Injector injector ) {
			E[] res = (E[]) Array.newInstance( arrayType.getComponentType(), elements.length );
			int i = 0;
			final Dependency elementDependency = (Dependency) dependency.typed( Type.raw(
					arrayType ).elementType() );
			for ( Supplier e : elements ) {
				res[i++] = e.supply( elementDependency, injector );
			}
			return res;
		}

	}

	private static final class ReferenceSupplier
			implements Supplier {

		private final Class> type;

		ReferenceSupplier( Class> type ) {
			super();
			this.type = type;
		}

		@Override
		public T supply( Dependency dependency, Injector injector ) {
			final Supplier supplier = injector.resolve( dependency.anyTyped( type ) );
			return supplier.supply( dependency, injector );
		}
	}

	private static final class ParametrizedInstanceSupplier
			implements Supplier {

		private final Instance instance;

		ParametrizedInstanceSupplier( Instance instance ) {
			super();
			this.instance = instance;
		}

		@Override
		public T supply( Dependency dependency, Injector injector ) {
			Type type = dependency.getType();
			Instance parametrized = instance.typed( instance.getType().parametized(
					type.getParameters() ).lowerBound( dependency.getType().isLowerBound() ) );
			return injector.resolve( dependency.instanced( parametrized ) );
		}

		@Override
		public String toString() {
			return instance.toString();
		}

	}

	private static final class InstanceSupplier
			implements Supplier {

		private final Instance instance;

		InstanceSupplier( Instance instance ) {
			super();
			this.instance = instance;
		}

		@Override
		public T supply( Dependency dependency, Injector injector ) {
			return injector.resolve( dependency.instanced( instance ) );
		}

		@Override
		public String toString() {
			return instance.toString();
		}
	}

	private static final class ProviderAsSupplier
			implements Supplier {

		private final Provider provider;

		ProviderAsSupplier( Provider provider ) {
			super();
			this.provider = provider;
		}

		@Override
		public T supply( Dependency dependency, Injector injector ) {
			return provider.provide();
		}

	}

	private static final class ProviderSupplier
			implements Supplier> {

		ProviderSupplier() {
			//make visible
		}

		@Override
		public Provider supply( Dependency> dependency, Injector injector ) {
			Dependency providedType = dependency.onTypeParameter();
			if ( !dependency.getName().isDefault() ) {
				providedType = providedType.named( dependency.getName() );
			}
			return newProvider( providedType, injector );
		}

		private static  Provider newProvider( Dependency dependency, Injector context ) {
			return new LazyResolvedDependencyProvider( dependency, context );
		}
	}

	private static final class LazyResolvedDependencyProvider
			implements Provider {

		private final Dependency dependency;
		private final Injector resolver;

		LazyResolvedDependencyProvider( Dependency dependency, Injector resolver ) {
			super();
			this.dependency = dependency;
			this.resolver = resolver;
		}

		@Override
		public T provide() {
			return resolver.resolve( dependency );
		}

		@Override
		public String toString() {
			return "provides<" + dependency + ">";
		}
	}

	/**
	 * Adapter to a simpler API that will not need any {@link Injector} to supply it's value in any
	 * case.
	 * 
	 * @author Jan Bernitt ([email protected])
	 * 
	 */
	private static final class FactorySupplier
			implements Supplier {

		private final Factory factory;

		FactorySupplier( Factory factory ) {
			super();
			this.factory = factory;
		}

		@Override
		public T supply( Dependency dependency, Injector injector ) {
			return factory.produce( dependency.getInstance(), dependency.target( 1 ) );
		}

	}

	/**
	 * A simple version for all the constructors where we know all arguments as constants.
	 */
	private static final class StaticConstructorSupplier
			implements Supplier {

		private final Constructor constructor;
		private final Object[] arguments;

		StaticConstructorSupplier( Constructor constructor, Object[] arguments ) {
			super();
			this.constructor = Metaclass.accessible( constructor );
			this.arguments = arguments;
		}

		@Override
		public T supply( Dependency dependency, Injector injector ) {
			return Invoke.constructor( constructor, arguments );
		}

	}

	private static final class ConstructorSupplier
			implements Supplier {

		private final Constructor constructor;
		private final Argument[] arguments;

		ConstructorSupplier( Constructor constructor, Argument[] arguments ) {
			super();
			this.constructor = Metaclass.accessible( constructor );
			this.arguments = arguments;
		}

		@Override
		public T supply( Dependency dependency, Injector injector ) {
			return Invoke.constructor( constructor,
					Argument.resolve( dependency, injector, arguments ) );
		}

	}

	private static final class FactoryMethodSupplier
			implements Supplier {

		private final Type returnType;
		private final Method factory;
		private final Object instance;
		private final Argument[] arguments;
		private final boolean instanceMethod;

		FactoryMethodSupplier( Type returnType, Method factory, Object instance,
				Argument[] arguments ) {
			super();
			this.returnType = returnType;
			this.factory = Metaclass.accessible( factory );
			this.instance = instance;
			this.arguments = arguments;
			this.instanceMethod = !Modifier.isStatic( factory.getModifiers() );
		}

		@Override
		public T supply( Dependency dependency, Injector injector ) {
			Object owner = instance;
			if ( instanceMethod && owner == null ) {
				owner = injector.resolve( dependency( factory.getDeclaringClass() ) );
			}
			final Object[] args = Argument.resolve( dependency, injector, arguments );
			return returnType.getRawType().cast( Invoke.method( factory, owner, args ) );
		}

	}

	private static class LoggerFactory
			implements Factory {

		LoggerFactory() {
			// make visible
		}

		@Override
		public 

Logger produce( Instance produced, Instance

injected ) { return Logger.getLogger( injected.getType().getRawType().getCanonicalName() ); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy