se.jbee.inject.bind.SuppliedBy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of silk-di Show documentation
Show all versions of silk-di Show documentation
Silk Java dependency injection framework
/*
* 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 extends Supplier extends T>> 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 extends E>[] 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 super T> 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 super T> 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 extends E>[] elements;
ElementsSupplier( Class arrayType, Supplier extends E>[] elements ) {
super();
this.arrayType = arrayType;
this.elements = elements;
}
@SuppressWarnings ( "unchecked" )
@Override
public E[] supply( Dependency super E[]> 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 extends E> e : elements ) {
res[i++] = e.supply( elementDependency, injector );
}
return res;
}
}
private static final class ReferenceSupplier
implements Supplier {
private final Class extends Supplier extends T>> type;
ReferenceSupplier( Class extends Supplier extends T>> type ) {
super();
this.type = type;
}
@Override
public T supply( Dependency super T> dependency, Injector injector ) {
final Supplier extends T> supplier = injector.resolve( dependency.anyTyped( type ) );
return supplier.supply( dependency, injector );
}
}
private static final class ParametrizedInstanceSupplier
implements Supplier {
private final Instance extends T> instance;
ParametrizedInstanceSupplier( Instance extends T> instance ) {
super();
this.instance = instance;
}
@Override
public T supply( Dependency super T> dependency, Injector injector ) {
Type super T> type = dependency.getType();
Instance extends T> 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 extends T> instance;
InstanceSupplier( Instance extends T> instance ) {
super();
this.instance = instance;
}
@Override
public T supply( Dependency super T> 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 super T> dependency, Injector injector ) {
return provider.provide();
}
}
private static final class ProviderSupplier
implements Supplier> {
ProviderSupplier() {
//make visible
}
@Override
public Provider> supply( Dependency super Provider>> 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 super T> 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 super T> 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 super T> 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 super T> 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 super Logger> produced, Instance
injected ) {
return Logger.getLogger( injected.getType().getRawType().getCanonicalName() );
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy