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

org.refcodes.decoupling.Dependency Maven / Gradle / Ivy

Go to download

Artifact for breaking up dependencies between components or modules of a software system (dependency injection and inversion of control).

There is a newer version: 3.3.8
Show newest version
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// /////////////////////////////////////////////////////////////////////////////
// This code is copyright (c) by Siegfried Steiner, Munich, Germany and licensed
// under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// -----------------------------------------------------------------------------
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// -----------------------------------------------------------------------------
// Apache License, v2.0 ("http://www.apache.org/licenses/LICENSE-2.0")
// -----------------------------------------------------------------------------
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////

package org.refcodes.decoupling;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.refcodes.data.Delimiter;
import org.refcodes.mixin.AliasAccessor;
import org.refcodes.mixin.Schemable;
import org.refcodes.mixin.TypeAccessor;
import org.refcodes.textual.CaseStyleBuilder;
import org.refcodes.textual.VerboseTextBuilder;

/**
 * A {@link Dependency} describes a component wired together by the
 * {@link Reactor} with other components also defined by {@link Dependency}
 * instances.
 *
 * @param  the generic type of the {@link Dependency}.
 */
public class Dependency implements Schemable, AliasAccessor, TypeAccessor, ProfilesAccessor, TagsAccessor, InstanceMetricsAccessor, ClaimsAccessor, Comparable> {

	// /////////////////////////////////////////////////////////////////////////
	// STATICS:
	// /////////////////////////////////////////////////////////////////////////

	private static final InstanceMetrics DEFAULT_INSTANCE_MODE = InstanceMode.SINGLETON_IS_MANDATORY;

	// /////////////////////////////////////////////////////////////////////////
	// CONSTANTS:
	// /////////////////////////////////////////////////////////////////////////

	private static final Logger LOGGER = Logger.getLogger( Dependency.class.getName() );

	// /////////////////////////////////////////////////////////////////////////
	// VARIABLES:
	// /////////////////////////////////////////////////////////////////////////

	private Constructor _constructor;
	private T _dangling; // Instance has been created but not (yet) injected!
	private boolean _isSetupSingletonFromInstance = false;
	protected T _singleton = null;
	protected String _alias;
	protected Set _claims = new HashSet<>();
	protected Dependency[] _dependencies;
	protected InstanceMetrics _instanceMetrics;
	protected Set _instances = new HashSet<>();
	protected Set _profiles;
	protected Set _tags;
	protected Class _type;
	protected InitializerClaim _initializer;
	protected FactoryClaim _factory;
	protected Reactor _reactor;

	// /////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Instantiates a new {@link Dependency}.
	 */
	protected Dependency() {}

	/**
	 * Instantiates a new {@link Dependency} from the given
	 * {@link DependencyBuilder} instance.
	 *
	 * @param aDependency the {@link DependencyBuilder} from which to
	 *        initialize.
	 * 
	 * @param aReactor The {@link Reactor} creating this {@link Dependency}.
	 */
	Dependency( DependencyBuilder aDependency, Reactor aReactor ) {
		this( aDependency.getType(), aDependency.getInstance(), aDependency.getInstanceMetrics(), aDependency.getAlias(), aDependency.getTags(), aDependency.getProfiles(), aDependency.getClaims(), aDependency.getSetup(), aDependency.getFactory(), aReactor );
	}

	/**
	 * Constructs a {@link Dependency} for the given type (the alias is derived
	 * from type).
	 * 
	 * @param aType The type of the dependency.
	 */
	Dependency( Class aType ) {
		this( aType, (T) null, null, (String) null, (Collection) null, (Collection) null, null, (InitializerClaim) null, (FactoryClaim) null, null );
	}

	/**
	 * Constructs a {@link Dependency} for the given instance (the type is
	 * derived from the instance, the alias is derived from type).
	 * 
	 * @param aInstance The instance of the dependency.
	 */
	Dependency( T aInstance ) {
		this( (Class) null, aInstance, (InstanceMetrics) null, (String) null, (Collection) null, (Collection) null, null, (InitializerClaim) null, (FactoryClaim) null, null );
	}

	private Dependency( Class aType, T aInstance, InstanceMetrics aInstanceMetrics, String aAlias, Object[] aTags, Object[] aProfiles, Claim[] aClaims, InitializerClaim aInitializerClaim, FactoryClaim aFactory, Reactor aReactor ) {
		this( aType, aInstance, aInstanceMetrics, aAlias, aTags != null && aTags.length != 0 ? Arrays.asList( aTags ) : null, aProfiles != null && aProfiles.length != 0 ? Arrays.asList( aProfiles ) : null, aClaims != null && aClaims.length != 0 ? Arrays.asList( aClaims ) : null, aInitializerClaim, aFactory, aReactor );
	}

	@SuppressWarnings("unchecked")
	private Dependency( Class aType, T aInstance, InstanceMetrics aInstanceMetrics, String aAlias, Collection aTags, Collection aProfiles, Collection aClaims, InitializerClaim aInitializerClaim, FactoryClaim aFactory, Reactor aReactor ) {
		_type = aInstance != null && aType == null ? (Class) aInstance.getClass() : aType;
		_alias = (aAlias == null || aAlias.isEmpty()) ? CaseStyleBuilder.asCamelCase( toAlias( _type ) ) : aAlias;
		_tags = aTags != null && aTags.size() != 0 ? new HashSet<>( aTags ) : new HashSet<>();
		_profiles = aProfiles != null && aProfiles.size() != 0 ? new HashSet<>( aProfiles ) : new HashSet<>();
		_instanceMetrics = aInstanceMetrics != null ? aInstanceMetrics : DEFAULT_INSTANCE_MODE;
		_claims = aClaims != null && aClaims.size() != 0 ? new HashSet<>( aClaims ) : new HashSet<>();
		_initializer = aInitializerClaim;
		_factory = aFactory;
		_reactor = aReactor;
		if ( aInstance == null && aType == null ) {
			throw new IllegalArgumentException( "At least an  or a  must be provided, but neither a  nor  an  has been provided!" );
		}
		if ( aInstance != null && aFactory != null ) {
			throw new IllegalArgumentException( "Either an  or a  must be provided, but not an instance <" + aInstance + "> and a factory <" + aFactory + "> at the same time!" );
		}
		if ( aInstance != null ) {
			_instances.add( aInstance );
			// An instance must be a singleton (we don't clone!) and is already there (cannot be omitted) |-->
			if ( _instanceMetrics.isSingleton() && _instanceMetrics.isMandatory() ) {
				_singleton = aInstance;
				_isSetupSingletonFromInstance = true;
			}
			// An instance must be a singleton (we don't clone!) and is already there (cannot be omitted) <--|
			else {
				throw new IllegalArgumentException( "The instance <" + aInstance + "> may only be provided in case the dependency <" + this + "> instance metrics denotes singleton (we only have that single instance) and mandatory (as it has already been created)!" );
			}
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// INJECTION:
	// /////////////////////////////////////////////////////////////////////////

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean equals( Object obj ) {
		if ( this == obj ) {
			return true;
		}
		if ( obj == null ) {
			return false;
		}
		if ( getClass() != obj.getClass() ) {
			return false;
		}
		final Dependency other = (Dependency) obj;
		return Objects.equals( _alias, other._alias ) && Objects.equals( _profiles, other._profiles ) && Objects.equals( _tags, other._tags ) && Objects.equals( _type, other._type );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getAlias() {
		return _alias;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Claim[] getClaims() {
		return _claims.toArray( new Claim[_claims.size()] );
	}

	/**
	 * Retrieves the {@link InitializerClaim}, being a {@link Claim} dedicated
	 * to initialize the dependency after instantiation.
	 * 
	 * @return The {@link InitializerClaim} being set for this
	 *         {@link Dependency}.
	 */
	public InitializerClaim getSetup() {
		return _initializer;
	}

	/**
	 * Retrieves the {@link FactoryClaim}, being a {@link Claim} dedicated to
	 * fabricate (create) the dependency.
	 * 
	 * @return The {@link FactoryClaim} being set for this {@link Dependency}.
	 */
	public FactoryClaim getFactory() {
		return _factory;
	}

	/**
	 * Retrieves the {@link InstanceMetrics} which describes how an instance for
	 * a {@link Dependency} is managed.
	 * 
	 * @return The according {@link InstanceMetrics}.
	 */
	@Override
	public InstanceMetrics getInstanceMetrics() {
		return _instanceMetrics;
	}

	/**
	 * Retrieves the instances produced by this {@link Dependency} (as of the
	 * {@link Dependency}'s {@link InstanceMetrics} configuration).
	 * 
	 * @return The instances fabricated by this {@link Dependency} declaration.
	 */
	@SuppressWarnings("unchecked")
	public T[] getInstances() {
		return _instances.toArray( (T[]) Array.newInstance( _type, _instances.size() ) );
	}

	/**
	 * Retrieves the profiles assigned to the {@link Dependency} declaration.
	 * 
	 * @return The assigned profiles.
	 */
	@Override
	public Object[] getProfiles() {
		return _profiles.toArray();
	}

	/**
	 * Retrieves the tags assigned to the {@link Dependency} declaration.
	 * 
	 * @return The assigned tags.
	 */
	@Override
	public Object[] getTags() {
		if ( _tags == null ) {
			_tags = new HashSet<>();
		}
		return _tags.toArray();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Class getType() {
		return _type;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		return Objects.hash( _alias, _profiles, _tags, _type );
	}

	/**
	 * Determines weather this {@link Dependency} element has at least one
	 * instance of its type {@link #getType()} being created.
	 * 
	 * @return True in case instances for this {@link Dependency} have already
	 *         been created.
	 */
	public boolean hasInstances() {
		return !_instances.isEmpty();
	}

	/**
	 * Determines whether this {@link Dependency} element contains the provided
	 * instance.
	 * 
	 * @param aInstance The instance which is to be tested.
	 * 
	 * @return True in case this {@link Dependency} element contains the given
	 *         instance, else false.
	 */
	public boolean hasInstance( Object aInstance ) {
		for ( T eInnstance : getInstances() ) {
			if ( eInnstance == aInstance ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Tests whether this {@link Dependency} matches any of the provided
	 * profiles.
	 * 
	 * @param aProfiles The profiles (as provided by the {@link Reactor}) to
	 *        match against.
	 * 
	 * @return True in case this {@link Dependency} matches at least one of the
	 *         provided profiles.
	 */
	public boolean hasProfile( Object... aProfiles ) {
		if ( _profiles == null || _profiles.size() == 0 ) {
			return true;
		}
		else {
			if ( aProfiles == null || aProfiles.length == 0 ) {
				return false;
			}
			for ( Object eProfile : aProfiles ) {
				for ( Object eDependencyProfile : _profiles ) {
					if ( eProfile.toString().equalsIgnoreCase( eDependencyProfile.toString() ) ) {
						return true;
					}
				}
			}
		}
		return false;
	}

	/**
	 * Creates the {@link Dependency}'s instance using the prepared
	 * {@link Dependency} declarations (the {@link Dependency} declarations are
	 * prepared by the {@link Reactor}'s {@link Reactor#createContext()} and the
	 * like methods and in turn by the underlying {@link #toInstance()}
	 * methods).
	 *
	 * @return The instance retrieved from the {@link Dependency}.
	 * 
	 * @throws DependencyInstanciationException the dependency instantiation
	 *         exception
	 */
	@SuppressWarnings("unchecked")
	public T toInstance() throws DependencyInstanciationException {
		if ( _singleton != null ) {
			return _singleton;
		}
		final Object[] theArgs = new Object[_dependencies.length];
		for ( int i = 0; i < _dependencies.length; i++ ) {
			theArgs[i] = _dependencies[i].toInstance();
		}
		try {
			final T theInstance = (T) _constructor.newInstance( theArgs );
			if ( _instanceMetrics.isSingleton() ) {
				_singleton = theInstance;
				_isSetupSingletonFromInstance = true;
			}
			_instances.add( theInstance );
			return theInstance;
		}
		catch ( Exception e ) {
			throw new DependencyInstanciationException( "Cannot instanciate dependency <{0}> using provided dependencies <{1}>!", this, _dependencies, e );
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public DependencySchema toSchema() {
		DependencySchema[] theSchemas = null;
		if ( _dependencies != null && _dependencies.length != 0 ) {
			theSchemas = new DependencySchema[_dependencies.length];
			for ( int i = 0; i < theSchemas.length; i++ ) {
				theSchemas[i] = _dependencies[i].toSchema();
			}
		}
		if ( getFactory() != null ) {
			if ( theSchemas == null ) {
				theSchemas = new DependencySchema[1];
			}
			else {
				theSchemas = Arrays.copyOf( theSchemas, theSchemas.length + 1 );
			}
			theSchemas[theSchemas.length - 1] = getFactory().toSchema();
		}
		if ( getSetup() != null ) {
			if ( theSchemas == null ) {
				theSchemas = new DependencySchema[1];
			}
			else {
				theSchemas = Arrays.copyOf( theSchemas, theSchemas.length + 1 );
			}
			theSchemas[theSchemas.length - 1] = getSetup().toSchema();
		}
		return new DependencySchema( this, theSchemas );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int compareTo( Dependency aDependency ) {
		final String thisAlias = getAlias() != null ? getAlias() : "";
		final String thatAlias = aDependency != null && aDependency.getAlias() != null ? aDependency.getAlias() : "";
		return thisAlias.compareTo( thatAlias );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return getClass().getSimpleName() + " [alias=" + _alias + ", tags=" + _tags + ", profiles=" + _profiles + ", type=" + _type + ", instances=" + _instances + ", instanceMetrics=" + _instanceMetrics + "]";
	}

	// /////////////////////////////////////////////////////////////////////////
	// HOOKS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Sets the instance for the {@link Dependency} (the type is derived from
	 * the instance if not already set, the alias is derived from type if not
	 * already set).
	 * 
	 * @param aInstance The instance of the dependency;
	 */
	@SuppressWarnings("unchecked")
	protected void setInstance( T aInstance ) {
		if ( aInstance == null ) {
			throw new IllegalArgumentException( "The provided instance must not(!) be null!" );
		}
		_singleton = null;
		_instances.clear();
		if ( aInstance != null ) {
			_instances.add( aInstance );
		}
		if ( _instanceMetrics.isSingleton() ) {
			_singleton = aInstance;
			_isSetupSingletonFromInstance = true;
		}
		if ( _type == null ) {
			_type = (Class) aInstance.getClass();
		}
		if ( _alias == null && _alias.isEmpty() ) {
			_alias = CaseStyleBuilder.asCamelCase( toAlias( _type ) );
		}
	}

	/**
	 * Creates the {@link Dependency}'s instance as of its type using the
	 * provided {@link Dependency} declarations.
	 *
	 * @param aDependencies the {@link Dependency} declarations to use.
	 * @param aProfiles The profiles as provided by the {@link Reactor} to take
	 *        into account.
	 * 
	 * @return The instance retrieved from the {@link Dependency}.
	 * 
	 * @throws CircularDependencyException thrown in case there is some
	 *         (transitive) circular dependency between two {@link Dependency}
	 *         instances.
	 * @throws AmbigousDependencyException thrown in case for a required
	 *         {@link Dependency} there are more than one matching
	 *         {@link Dependency} candidates in the {@link Reactor}.
	 * @throws UnsatisfiedDependencyException thrown in case for a required
	 *         {@link Dependency} none matching {@link Dependency} candidates in
	 *         the {@link Reactor} have been found (taking the profiles applied
	 *         into account) for the constructors of the given
	 *         {@link Dependency}.
	 * @throws AmbigousClaimException is thrown in case one {@link Claim} can be
	 *         matched by multiple dependencies.
	 * @throws AmbigousInitializerException is thrown in case one
	 *         {@link InitializerClaim} instance can be matched by multiple
	 *         dependencies.
	 * @throws AmbigousFactoryException is thrown in case one
	 *         {@link FactoryClaim} instance can be matched by multiple
	 *         dependencies.
	 * @throws InstallDependencyException is thrown when trying to install a
	 *         {@link Dependency} into the {@link Context}, though installing
	 *         failed die to the dependency throwing an exception during the
	 *         process of installing (e.g. instantiation).
	 */
	protected T toInstance( Dependency[] aDependencies, Object... aProfiles ) throws CircularDependencyException, AmbigousDependencyException, UnsatisfiedDependencyException, AmbigousClaimException, AmbigousInitializerException, AmbigousFactoryException, InstallDependencyException {
		return toInstance( aDependencies, new HashSet<>(), aProfiles );
	}

	/**
	 * Creates the {@link Dependency}'s instance as of its type using the
	 * provided {@link Dependency} declarations.
	 *
	 * @param aDependencies the {@link Dependency} declarations to use.
	 * @param aVistedDependencies The {@link Dependency} declarations already
	 *        visited in order to detect a circular dependency situation.
	 * @param aProfiles The profiles as provided by the {@link Reactor} to take
	 *        into account.
	 * 
	 * @return The instance retrieved from the {@link Dependency}.
	 * 
	 * @throws CircularDependencyException thrown in case there is some
	 *         (transitive) circular dependency between two {@link Dependency}
	 *         instances.
	 * @throws AmbigousDependencyException thrown in case for a required
	 *         {@link Dependency} there are more than one matching
	 *         {@link Dependency} candidates in the {@link Reactor}.
	 * @throws UnsatisfiedDependencyException thrown in case for a required
	 *         {@link Dependency} none matching {@link Dependency} candidates in
	 *         the {@link Reactor} have been found (taking the profiles applied
	 *         into account) for the constructors of the given
	 *         {@link Dependency}.
	 * @throws AmbigousClaimException is thrown in case one {@link Claim} can be
	 *         matched by multiple dependencies.
	 * @throws AmbigousInitializerException is thrown in case one
	 *         {@link InitializerClaim} instance can be matched by multiple
	 *         dependencies.
	 * @throws AmbigousFactoryException is thrown in case one
	 *         {@link FactoryClaim} instance can be matched by multiple
	 *         dependencies.
	 * @throws InstallDependencyException is thrown when trying to install a
	 *         {@link Dependency} into the {@link Context}, though installing
	 *         failed die to the dependency throwing an exception during the
	 *         process of installing (e.g. instantiation).
	 */
	@SuppressWarnings("unchecked")
	protected T toInstance( Dependency[] aDependencies, Set> aVistedDependencies, Object... aProfiles ) throws CircularDependencyException, AmbigousDependencyException, UnsatisfiedDependencyException, AmbigousClaimException, AmbigousInitializerException, AmbigousFactoryException, InstallDependencyException {
		if ( !aVistedDependencies.add( this ) ) {
			if ( _instances.isEmpty() ) {
				throw new CircularDependencyException( "A circular dependency between this dependency <{0}> and one or more dependencies <{1}> has been detected, unable to resolve conflict (you may contribute and add some proxy mechanism here)!", this, aVistedDependencies.toArray( new Dependency[aVistedDependencies.size()] ) );
			}
		}
		if ( _dangling != null ) {
			return _dangling; // An instance has been requested and we still have a dangling one which we return
		}
		T theInstance = null;
		if ( _singleton != null ) {
			if ( !_isSetupSingletonFromInstance ) {
				return _singleton;
			}
			theInstance = _singleton;
		}
		else if ( _factory != null ) {
			final Dependency theDependency = toMatchingDependencyByClaim( aDependencies, _factory );
			final Object theFactory = theDependency.toInstance( aDependencies, aVistedDependencies, aProfiles );
			theInstance = _factory.toInstance( theFactory );
		}
		else {
			final Dependency[] theDependencies = toMatchingDependenciesByClaims( aDependencies );
			final Constructor[] theCtors = toSuitableConstructors( theDependencies, aProfiles );
			final List> eCtorDependencies = new ArrayList<>();
			final List eCtorArgs = new ArrayList<>();
			CircularDependencyException theCircularDependencyException = null;
			InstallDependencyException theInstallDependencyException = null;
			out: {
				for ( Constructor eCtor : theCtors ) {
					try {
						Dependency eParamDependency;
						for ( Parameter eParam : eCtor.getParameters() ) {
							eParamDependency = toDependencyMatch( theDependencies, eParam.getType(), toParamAlias( eParam ), aProfiles );
							eCtorArgs.add( eParamDependency.toInstance( aDependencies, aVistedDependencies, aProfiles ) );
							eCtorDependencies.add( eParamDependency );
						}
						theInstance = (T) eCtor.newInstance( eCtorArgs.toArray() );
						theInstance = intercept( theInstance );
						for ( Dependency eDependency : eCtorDependencies ) {
							eDependency._dangling = null; // Dangling instances have been assigned to instance as above, they are not dangling any more
						}
						// if ( !_instanceMetrics.isSingleton() ) {
						_dependencies = eCtorDependencies.toArray( new Dependency[eCtorDependencies.size()] );
						_constructor = eCtor;
						// }
						break out;
					}
					catch ( InstantiationException | IllegalAccessException | InvocationTargetException | CircularDependencyException e ) {
						if ( e instanceof CircularDependencyException ) {
							theCircularDependencyException = (CircularDependencyException) e;
						}
						else {
							theInstallDependencyException = new InstallDependencyException( "Cannot install dependency <" + this + ">!", this, e );
						}
						LOGGER.log( Level.INFO, "Skipping dependnecy's <" + this + "> constructor with parameters <" + VerboseTextBuilder.asString( eCtor.getParameters() ) + "> as of: " + e.getMessage() );
					}
					eCtorDependencies.clear();
					eCtorArgs.clear();
				}
				if ( theCircularDependencyException != null ) {
					throw theCircularDependencyException;
				}

				if ( theInstallDependencyException != null ) {
					throw theInstallDependencyException;
				}
				throw new UnsatisfiedDependencyException( "Cannot satisfy all required dependencies for dependency <{0}> for its constructors!", this, aDependencies );
			}
		}
		if ( !_instanceMetrics.isSingleton() ) {
			_dangling = theInstance; // We created an instance which is still dangling as some other dependency has to clear it's dangling status
		}
		if ( _initializer != null ) {
			if ( _singleton == null || _isSetupSingletonFromInstance ) {
				final Dependency theDependency = toMatchingDependencyByClaim( aDependencies, _initializer );
				final Object theSetup = theDependency.toInstance( aDependencies, aVistedDependencies, aProfiles );
				theInstance = _initializer.toInstance( theSetup, theInstance );
				_isSetupSingletonFromInstance = false;
			}
		}
		if ( _instanceMetrics.isSingleton() && _singleton == null ) {
			_singleton = theInstance;
		}
		_instances.add( theInstance );
		return theInstance;
	}

	/**
	 * Hook method to be overwritten by sub-classes in order to decorate new
	 * aspects to the produced instance.
	 * 
	 * @param aInstance The instance to be post processed.
	 * 
	 * @return The (new) post processed instance.
	 */
	protected T intercept( T aInstance ) {
		if ( _reactor != null ) {
			aInstance = _reactor.intercept( aInstance, this );
		}
		return aInstance;
	}

	// /////////////////////////////////////////////////////////////////////////
	// TEST HOOKS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Determines the constructors which can be satisfied by the
	 * {@link Dependency} declarations known by the {@link Reactor}.
	 *
	 * @param aDependencies The dependencies which to use for lookup.
	 * @param aProfiles The profiles as provided by the {@link Reactor} to take
	 *        into account.
	 * 
	 * @return An array with the suitable constructors or null if none was
	 *         found.
	 * 
	 * @throws AmbigousDependencyException thrown in case for a required
	 *         {@link Dependency} there are more than one matching
	 *         {@link Dependency} candidates in the {@link Reactor}.
	 * @throws UnsatisfiedDependencyException thrown in case for a required
	 *         {@link Dependency} none matching {@link Dependency} candidates in
	 *         the {@link Reactor} have been found (taking the profiles applied
	 *         into account) for the constructors of the given
	 *         {@link Dependency}.
	 */
	Constructor[] toSuitableConstructors( Dependency[] aDependencies, Object... aProfiles ) throws AmbigousDependencyException, UnsatisfiedDependencyException {
		final Constructor[] aConstructors = getType().getConstructors();
		final List> theCtors = new ArrayList<>();
		for ( Constructor eCtr : aConstructors ) {
			out: {
				for ( Parameter eParam : eCtr.getParameters() ) {
					if ( !hasDependencyMatch( aDependencies, eParam.getType(), toParamAlias( eParam ), aProfiles ) ) {
						break out;
					}
				}
				theCtors.add( eCtr );
			}
		}
		if ( theCtors.size() == 0 ) {
			throw new UnsatisfiedDependencyException( "Cannot find suitable constructor for dependency <" + this + "> as none constructor matches the provided dependencies <" + VerboseTextBuilder.asString( aDependencies ) + ">!", this, aDependencies );
		}
		return toOrderedConstructors( theCtors );
	}

	// /////////////////////////////////////////////////////////////////////////
	// HELPER:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Determines whether the given type can be satisfied by the provided
	 * dependencies taking the tags into account.
	 * 
	 * @param aDependencies The dependencies which to use for testing.
	 * @param aType The type to be tested whether it can be satisfied by one of
	 *        the provided dependencies.
	 * @param aAlias The alias assigned to the given type.
	 * @param aProfiles The profiles as provided by the {@link Reactor} to take
	 *        into account.
	 *
	 * @return True in case the type can be satisfied by the provided
	 *         dependencies.
	 * 
	 * @throws AmbigousDependencyException thrown in case for a required
	 *         {@link Dependency} there are more than one matching
	 *         {@link Dependency} candidates in the {@link Reactor}.
	 */
	private boolean hasDependencyMatch( Dependency[] aDependencies, Class aType, String aAlias, Object... aProfiles ) throws AmbigousDependencyException {
		final Set> theDependencies = toMatchingDependencies( aDependencies, aType, aAlias, aProfiles );
		if ( theDependencies.size() > 1 ) {
			throw new AmbigousDependencyException( "There are <" + theDependencies.size() + "> dependencies matching the type <" + aType.getName() + "> required by the dependency<" + this + ">: " + VerboseTextBuilder.asString( theDependencies ), this, theDependencies.toArray( new Dependency[theDependencies.size()] ) );
		}
		return theDependencies.size() == 1;
	}

	/**
	 * Test whether the given {@link Dependency}'s tags matches at least one tag
	 * from this {@link Dependency}.
	 * 
	 * @param aDependency The {@link Dependency} which's tags are to be tested.
	 * 
	 * @return True in case we have a tag match.
	 */
	private boolean hasTagMatchWithDependency( Dependency aDependency ) {
		final Object[] theTagsA = getTags();
		final Object[] theTagsB = aDependency.getTags();
		if ( (theTagsA == null || theTagsA.length == 0) && (theTagsB == null || theTagsB.length == 0) ) {
			return true;
		}
		if ( (theTagsA != null && theTagsA.length != 0) && (theTagsB == null || theTagsB.length == 0) ) {
			return false;
		}
		if ( (theTagsA == null || theTagsA.length == 0) && (theTagsB != null && theTagsB.length != 0) ) {
			return false;
		}
		for ( Object eObjA : theTagsA ) {
			for ( Object eObjB : theTagsB ) {
				if ( eObjA.toString().equalsIgnoreCase( eObjB.toString() ) ) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Determines the {@link Dependency} matching the given type as of the
	 * provided {@link Dependency} declarations and their tags.
	 * 
	 * @param aDependencies The {@link Dependency} declarations which to use for
	 *        testing.
	 * @param aType The type for which to determine the matching
	 *        {@link Dependency}.
	 * @param aAlias The alias assigned to the given type.
	 * @param aProfiles The profiles as provided by the {@link Reactor} to take
	 *        into account.
	 * 
	 * @return The matching {@link Dependency}.
	 * 
	 * @throws AmbigousDependencyException thrown in case for a required
	 *         {@link Dependency} there are more than one matching
	 *         {@link Dependency} candidates in the {@link Reactor}.
	 * 
	 * @throws UnsatisfiedDependencyException thrown in case for a required
	 *         {@link Dependency} none matching {@link Dependency} candidates in
	 *         the {@link Reactor} have been found (taking the profiles applied
	 *         into account) for the constructors of the given
	 *         {@link Dependency}.
	 */
	private Dependency toDependencyMatch( Dependency[] aDependencies, Class aType, String aAlias, Object... aProfiles ) throws AmbigousDependencyException, UnsatisfiedDependencyException {
		final Set> theDependencies = toMatchingDependencies( aDependencies, aType, aAlias, aProfiles );
		if ( theDependencies.size() > 1 ) {
			throw new AmbigousDependencyException( "There are <" + theDependencies.size() + "> dependencies matching the type <" + aType.getName() + "> required by the dependency<" + this + ">: " + VerboseTextBuilder.asString( theDependencies ), this, theDependencies.toArray( new Dependency[theDependencies.size()] ) );
		}
		if ( theDependencies.isEmpty() ) {
			throw new UnsatisfiedDependencyException( "There are no dependencies matching the type <" + aType.getName() + "> required by the dependency<" + this + ">: " + VerboseTextBuilder.asString( aDependencies ), this, aDependencies );
		}
		return theDependencies.iterator().next();
	}

	/**
	 * Determines those {@link Dependency} declarations which are ambiguous by
	 * considering their alias whether them are matching or not the target
	 * (parameter) alias.
	 * 
	 * @param aDependencies The ambiguous {@link Dependency} declarations which
	 *        tags are to be matched against the (parameter) alias.
	 * 
	 * @param aAlias The alias to be matched.
	 * 
	 * @return The (still) matching {@link Dependency} declarations.
	 */
	private Set> toMatchingAmbigousDependenciesByAlias( Set> aDependencies, String aAlias ) {
		if ( aAlias == null || aAlias.isEmpty() ) {
			return aDependencies;
		}
		final Set> theAmbigousDependencies = new HashSet<>( aDependencies );
		final Iterator> e = aDependencies.iterator();
		while ( e.hasNext() ) {
			if ( !e.next().getAlias().equalsIgnoreCase( aAlias ) ) {
				e.remove();
			}
		}
		if ( aDependencies.size() == 0 ) {
			return theAmbigousDependencies;
		}
		return aDependencies;
	}

	/**
	 * Determines those {@link Dependency} declarations which are ambiguous by
	 * considering their profiles whether them are matching or not the given
	 * profiles.
	 * 
	 * @param aDependencies The ambiguous {@link Dependency} declarations which
	 *        tags are to be matched against the (parameter) alias.
	 * @param aProfiles The profiles as provided by the {@link Reactor} to take
	 *        into account.
	 * 
	 * @return The (still) matching {@link Dependency} declarations.
	 */
	private Set> toMatchingAmbigousDependenciesByProfiles( Set> aDependencies, Object[] aProfiles ) {
		if ( aProfiles == null || aProfiles.length == 0 ) {
			return aDependencies;
		}
		final Set> theAmbigousDependencies = new HashSet<>( aDependencies );
		final Iterator> e = aDependencies.iterator();
		Dependency eNext;
		while ( e.hasNext() ) {
			eNext = e.next();
			if ( !eNext.hasProfile( aProfiles ) || eNext.getProfiles() == null || eNext.getProfiles().length == 0 ) {
				e.remove();
			}
		}
		if ( aDependencies.size() == 0 ) {
			return theAmbigousDependencies;
		}
		return aDependencies;
	}

	/**
	 * Determines the {@link Dependency} declarations resulting form keeping
	 * only {@link Dependency} declarations not being ambiguous any more after
	 * probing against the {@link InitializerClaim} instance.
	 * 
	 * @param aDependencies The dependencies which's ambiguousness is to be
	 *        resolved by the {@link InitializerClaim} instance being declared.
	 * 
	 * @return The {@link Dependency} declarations after removing ambiguousness.
	 * 
	 * @throws AmbigousInitializerException is thrown in case one
	 *         {@link InitializerClaim} instance can be matched by multiple
	 *         dependencies.
	 * @throws AmbigousFactoryException is thrown in case one
	 *         {@link FactoryClaim} instance can be matched by multiple
	 *         dependencies.
	 * @throws AmbigousClaimException is thrown in case one {@link Claim}
	 *         instance can be matched by multiple dependencies.
	 * 
	 */
	private Dependency toMatchingDependencyByClaim( Dependency[] aDependencies, Claim aClaim ) throws AmbigousInitializerException, AmbigousFactoryException, AmbigousClaimException {
		Dependency theClaim = null;
		if ( aClaim != null ) {
			for ( Dependency eDependency : aDependencies ) {
				if ( aClaim.isClaim( eDependency ) ) {
					if ( theClaim != null ) {
						if ( aClaim instanceof InitializerClaim ) {
							throw new AmbigousInitializerException( "The setup (claim) <" + aClaim + "> can be satisfied by more than one <" + theClaim + "> dependency <" + eDependency + ">: " + VerboseTextBuilder.asString( aDependencies ), (InitializerClaim) aClaim, this, aDependencies );
						}
						if ( aClaim instanceof FactoryClaim ) {
							throw new AmbigousFactoryException( "The factory (claim) <" + aClaim + "> can be satisfied by more than one <" + theClaim + "> dependency <" + eDependency + ">: " + VerboseTextBuilder.asString( aDependencies ), (FactoryClaim) aClaim, this, aDependencies );
						}
						throw new AmbigousClaimException( "The claim <" + aClaim + "> can be satisfied by more than one <" + theClaim + "> dependency <" + eDependency + ">: " + VerboseTextBuilder.asString( aDependencies ), aClaim, this, aDependencies );
					}
					theClaim = eDependency;
					continue;
				}
			}
		}
		return theClaim;
	}

	/**
	 * Determines the {@link Dependency} declarations resulting form keeping
	 * only {@link Dependency} declarations not being ambiguous any more after
	 * probing against the {@link Claim} instances or not being of interest for
	 * the {@link Claim} instances .
	 * 
	 * @param aDependencies The dependencies which's ambiguousness is to be
	 *        resolved by the {@link Claim} instances being declared.
	 * 
	 * @return The {@link Dependency} declarations after removing ambiguousness.
	 * 
	 * @throws AmbigousClaimException is thrown in case one {@link Claim} can be
	 *         matched by multiple dependencies.
	 */
	private Dependency[] toMatchingDependenciesByClaims( Dependency[] aDependencies ) throws AmbigousClaimException {
		final Set> theClaims = new HashSet<>();
		boolean hasClaim;
		for ( Claim eClaim : _claims ) {
			hasClaim = false;
			for ( Dependency eDependency : aDependencies ) {
				if ( eClaim.isClaim( eDependency ) ) {
					if ( hasClaim ) {
						throw new AmbigousClaimException( "The claim <" + eClaim + "> can be satisfied by more than one <" + eClaim + "> dependency <" + eDependency + ">: " + VerboseTextBuilder.asString( aDependencies ), eClaim, this, aDependencies );
					}
					theClaims.add( eDependency );
					hasClaim = true;
				}
			}
		}
		final Set> theDependencies = new HashSet<>( Arrays.asList( aDependencies ) );
		final Iterator> e = theDependencies.iterator();
		Dependency eDependency;
		while ( e.hasNext() ) {
			eDependency = e.next();
			for ( Dependency eClaim : theClaims ) {
				if ( eClaim != eDependency && eClaim.getType().isAssignableFrom( eDependency.getType() ) ) {
					e.remove();
					continue;
				}
			}
		}
		return theDependencies.toArray( new Dependency[theDependencies.size()] );
	}

	/**
	 * Determines those {@link Dependency} declarations which are ambiguous by
	 * considering their tags whether them are matching or not.
	 * 
	 * @param aDependencies The ambiguous {@link Dependency} declarations which
	 *        tags are to be matched against this {@link Dependency}.
	 * 
	 * @return The (still) matching {@link Dependency} declarations.
	 */
	private Set> toMatchingAmbigousDependenciesByTag( Set> aDependencies ) {
		final Set> theAmbigousDependencies = new HashSet<>( aDependencies );
		final Iterator> e = aDependencies.iterator();
		while ( e.hasNext() ) {
			if ( !hasTagMatchWithDependency( e.next() ) ) {
				e.remove();
			}
		}
		if ( aDependencies.size() == 0 ) {
			return theAmbigousDependencies;
		}
		return aDependencies;
	}

	/**
	 * Determines the dependencies matching the type and in case of ambiguous
	 * hits also by tag (the tags to test against are implicit as of this
	 * {@link Dependency}).
	 * 
	 * @param aDependencies The {@link Dependency} declarations which to use for
	 *        testing.
	 * @param aType The type for which to test the {@link Dependency}
	 *        declarations against.
	 * @param aAlias The alias assigned to the given type.
	 * @param aProfiles The profiles as provided by the {@link Reactor} to take
	 *        into account.
	 * 
	 * @return A set with the determined {@link Dependency} declarations..
	 */
	private Set> toMatchingDependencies( Dependency[] aDependencies, Class aType, String aAlias, Object... aProfiles ) {
		Set> theDependencies = toMatchingDependenciesByType( aDependencies, aType );
		if ( theDependencies.size() > 1 ) {
			theDependencies = toMatchingAmbigousDependenciesByTag( theDependencies );
		}
		if ( theDependencies.size() > 1 ) {
			theDependencies = toMatchingAmbigousDependenciesByAlias( theDependencies, aAlias );
		}
		if ( theDependencies.size() > 1 ) {
			theDependencies = toMatchingAmbigousDependenciesByProfiles( theDependencies, aProfiles );
		}
		return theDependencies;
	}

	/**
	 * Determines the matching {@link Dependency} declarations for the given
	 * type taking the tags into account.
	 * 
	 * @param aDependencies The {@link Dependency} declarations which to use for
	 *        testing.
	 * @param aType The type for which to retrieve the matching
	 *        {@link Dependency} declarations.
	 * 
	 * @return A set with the determined {@link Dependency} declarations..
	 */
	private Set> toMatchingDependenciesByType( Dependency[] aDependencies, Class aType ) {
		final Set> theDependencies = new HashSet<>();
		for ( Dependency eDependency : aDependencies ) {
			if ( eDependency != this ) {
				if ( aType.isAssignableFrom( eDependency.getType() ) ) {
					theDependencies.add( eDependency );
				}
			}
		}
		return theDependencies;
	}

	/**
	 * Sorts the provided constructors so that the one with the most arguments
	 * is first and the one with the least arguments is last. This is for making
	 * sure that a dependency gets as many dependency injected as possible as
	 * the fist constructor is tried out first, the second next, and so on.
	 * 
	 * @param aCtors The constructors to be ordered accordingly.
	 * 
	 * @return The accordingly ordered constructors.
	 */
	private Constructor[] toOrderedConstructors( List> aCtors ) {
		int i = 0;
		final Constructor[] theResult = new Constructor[aCtors.size()];
		while ( i < theResult.length ) {
			Constructor maxCtor = null;
			for ( Constructor eCtor : aCtors ) {
				if ( maxCtor == null || maxCtor.getParameters().length < eCtor.getParameters().length ) {
					maxCtor = eCtor;
				}
			}
			aCtors.remove( maxCtor );
			theResult[i] = maxCtor;
			i++;
		}
		return theResult;
	}

	/**
	 * Retrieves the alias from the {@link Alias} annotation of a parameter.
	 * 
	 * @param aParam The param from which to retrieve the alias.
	 * 
	 * @return The according alias or null if none was detected.
	 */
	private String toParamAlias( Parameter aParam ) {
		final Alias theAlias = aParam.getAnnotation( Alias.class );
		if ( theAlias != null ) {
			return theAlias.value();
		}
		return aParam.getName();
	}

	/**
	 * Determines an alias for the given type, taking enclosing classes into
	 * account.
	 * 
	 * @param aType The type for which to get the alias.
	 * 
	 * @return The according alias.
	 */
	private static String toAlias( Class aType ) {
		String theAlias = null;
		if ( aType != null ) {
			theAlias = aType.getSimpleName();
			while ( (aType = aType.getEnclosingClass()) != null ) {
				theAlias = aType.getSimpleName() + Delimiter.INNER_CLASS.getChar() + theAlias;
			}
		}
		return theAlias;
	}

	// /////////////////////////////////////////////////////////////////////////
	// INNER CLASSES:
	// /////////////////////////////////////////////////////////////////////////
}