package org.needle4j.injection;
import static org.needle4j.common.Annotations.assertIsQualifier;
import static org.needle4j.common.Preconditions.checkArgument;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.inject.Named;
/**
* Utility class for creating {@link InjectionProvider}s with default behavior.
* <p/>
* Usage:<br/>
*
* <pre>
* import static org.needle4j.injection.InjectionProviders.*;
* </pre>
* <p/>
* then call static factory methods to create providers.
*
* @author Jan Galinski, Holisticon AG
*
*/
public final class InjectionProviders {
/**
* InjectionProvider that provides a singleton instance of type T whenever
* injection is required.
*
* @param instance
* the instance to return whenever assignable
* @return InjectionProvider for instance
*/
public static <T> InjectionProvider<T> providerForInstance(final T instance) {
return new DefaultInstanceInjectionProvider<T>(instance);
}
/**
* InjectionProvider that provides a singleton instance of type T for every
* injection point that is annotated with a {@link Named} qualifier with
* value "name".
*
* @param name
* value of Named annotation
* @param instance
* the instance to return whenever needed
* @return InjectionProvider for instance
*/
public static <T> InjectionProvider<T> providerForNamedInstance(final String name, final T instance) {
return new NamedInstanceInjectionProvider<T>(name, instance);
}
/**
* InjectionProvider that provides a singleton instance of type T for every
* injection point that is annotated with the given qualifier.
*
* @param qualifier
* qualifying annotation of injection point
* @param instance
* the instance to return whenever needed
* @return InjectionProvider for instance
*/
public static <T> InjectionProvider<T> providerForQualifiedInstance(final Class<? extends Annotation> qualifier,
final T instance) {
return new QualifiedInstanceInjectionProvider<T>(qualifier, instance);
}
/**
* Creates a new Set.
*
* @param providers
* vararg array of providers
* @return set containing providers
*/
private static Set<InjectionProvider<?>> newProviderSet(final InjectionProvider<?>... providers) {
final Set<InjectionProvider<?>> result = new LinkedHashSet<InjectionProvider<?>>();
if (providers != null && providers.length > 0) {
for (final InjectionProvider<?> provider : providers) {
result.add(provider);
}
}
return result;
}
/**
* Creates a new Supplier.
*
* @param providers
* vararg array of providers
* @return new supplier
*/
public static InjectionProviderInstancesSupplier supplierForInjectionProviders(
final InjectionProvider<?>... providers) {
return supplierForInjectionProviders(newProviderSet(providers));
}
public static InjectionProviderInstancesSupplier supplierForInjectionProviders(
final Set<InjectionProvider<?>> providers) {
return new InjectionProviderInstancesSupplier() {
@Override
public Set<InjectionProvider<?>> get() {
return providers;
}
};
}
/**
* Creates new supplier containing all providers in a new set.
*
* @param suppliers
* vararg array of existing suppliers
* @return new instance containing all providers
*/
private static InjectionProviderInstancesSupplier mergeSuppliers(
final InjectionProviderInstancesSupplier... suppliers) {
final Set<InjectionProvider<?>> result = new LinkedHashSet<InjectionProvider<?>>();
if (suppliers != null && suppliers.length > 0) {
for (final InjectionProviderInstancesSupplier supplier : suppliers) {
result.addAll(supplier.get());
}
}
return new InjectionProviderInstancesSupplier() {
@Override
public Set<InjectionProvider<?>> get() {
return result;
}
};
}
/**
* Create array of providers from given suppliers.
*
* @param suppliers
* vararg array of suppliers
* @return array of providers for use with vararg method
*/
public static InjectionProvider<?>[] providersForInstancesSuppliers(
final InjectionProviderInstancesSupplier... suppliers) {
final InjectionProviderInstancesSupplier supplier = mergeSuppliers(suppliers);
return supplier.get().toArray(new InjectionProvider<?>[supplier.get().size()]);
}
@SuppressWarnings("serial")
public static Set<InjectionProvider<?>> providersToSet(final InjectionProvider<?>... providers) {
return new HashSet<InjectionProvider<?>>() {
{
for (InjectionProvider<?> provider : providers) {
add(provider);
}
}
};
}
/**
* Create array of InjectionProviders for given collection.
*
* @param providers
* providers to be contained
* @return array of given providers
*/
public static InjectionProvider<?>[] providersToArray(final Collection<InjectionProvider<?>> providers) {
return providers == null ? new InjectionProvider<?>[0] : providers.toArray(new InjectionProvider<?>[providers
.size()]);
}
private InjectionProviders() {
// hide default constructor
}
/**
* Base class for all instance injection providers.
*
* @param <T>
* type of instance
*/
private abstract static class InstanceInjectionProvider<T> implements InjectionProvider<T> {
protected final T instance;
public InstanceInjectionProvider(final T instance) {
checkArgument(instance != null, "Parameter instance must not be null!");
this.instance = instance;
}
@Override
public T getInjectedObject(final Class<?> unusedInjectionPointType) {
return instance;
}
/**
* <code>true</code> when injection target is or extends/implements
* instance type
*
* @param injectionTargetInformation
* @return true when type is assignable from instance
*/
protected boolean isTargetAssignable(final InjectionTargetInformation injectionTargetInformation) {
return injectionTargetInformation.getType().isAssignableFrom(instance.getClass());
}
protected boolean isTargetQualifierPresent(final InjectionTargetInformation injectionTargetInformation,
final Class<? extends Annotation> qualifier) {
assertIsQualifier(qualifier);
return injectionTargetInformation.isAnnotationPresent(qualifier);
}
}
private static class DefaultInstanceInjectionProvider<T> extends InstanceInjectionProvider<T> {
public DefaultInstanceInjectionProvider(final T instance) {
super(instance);
}
@Override
public Object getKey(final InjectionTargetInformation injectionTargetInformation) {
return injectionTargetInformation.getType();
}
@Override
public boolean verify(final InjectionTargetInformation injectionTargetInformation) {
return isTargetAssignable(injectionTargetInformation);
}
}
private static class QualifiedInstanceInjectionProvider<T> extends InstanceInjectionProvider<T> {
protected final Class<? extends Annotation> qualifier;
protected QualifiedInstanceInjectionProvider(final Class<? extends Annotation> qualifier, final T instance) {
super(instance);
assertIsQualifier(qualifier);
this.qualifier = qualifier;
}
@Override
public Object getKey(final InjectionTargetInformation injectionTargetInformation) {
return String.format("%s#%s", injectionTargetInformation.getType().getCanonicalName(), qualifier.getName());
}
@Override
public boolean verify(final InjectionTargetInformation injectionTargetInformation) {
return isTargetQualifierPresent(injectionTargetInformation, qualifier) //
&& isTargetAssignable(injectionTargetInformation);
}
}
private static class NamedInstanceInjectionProvider<T> extends QualifiedInstanceInjectionProvider<T> {
protected final String name;
/**
* Constructs a provider for given @Named element encapsulating the
* value.
*
* @param name
* name of the interest.
* @param instance
* value to return.
*/
protected NamedInstanceInjectionProvider(final String name, final T instance) {
super(Named.class, instance);
checkArgument(name != null && !"".equals(name.trim()), "Parameter name must not be null nor empty!");
this.name = name;
}
@Override
public Object getKey(final InjectionTargetInformation injectionTargetInformation) {
return String.format("%s#%s", injectionTargetInformation.getType().getCanonicalName(), name);
}
@Override
public boolean verify(final InjectionTargetInformation injectionTargetInformation) {
return isTargetQualifierPresent(injectionTargetInformation, qualifier) //
&& ((Named) injectionTargetInformation.getAnnotation(qualifier)).value().equals(name) //
&& isTargetAssignable(injectionTargetInformation);
}
}
}