package info.ebenoit.ebul.cmp;


import java.util.Objects;



/**
 * This class carries information about a dependency's target. Dependencies may be specified by name or by type.
 *
 * @author <a href="mailto:ebenoit@ebenoit.info">E. BenoƮt</a>
 */
public class DependencyInfo
{

	/**
	 * Checks that a class can be used as a dependency
	 *
	 * @param klass
	 *            the class to check
	 * @throws ComponentDefinitionException
	 *             if the specified "class" is in fact a primitive or array type
	 */
	public static void checkClassValidity( final Class< ? > klass )
			throws ComponentDefinitionException
	{
		if ( klass.isPrimitive( ) ) {
			throw new ComponentDefinitionException( "specified dependency is a primitive type" );
		}
		if ( klass.isArray( ) ) {
			throw new ComponentDefinitionException( "specified dependency is an array type" );
		}
	}

	/**
	 * The name of the component being depended upon, or <code>null</code> if the dependency information is based on
	 * type.
	 */
	private final String name;
	/**
	 * The type of the component being depended upon, or <code>null</code> if the dependency information is based on the
	 * name.
	 */
	private final Class< ? > klass;


	/**
	 * Creates a name-based dependency information record
	 *
	 * @param name
	 *            the name of the component being depended upon
	 */
	public DependencyInfo( final String name )
	{
		Objects.requireNonNull( name );
		this.name = name;
		this.klass = null;
	}


	/**
	 * Creates a type-based dependency information record
	 *
	 * @param klass
	 *            the type of the component being depended upon
	 * @throws ComponentDefinitionException
	 *             if the specified type is not supported (e.g. primitives or arrays)
	 */
	public DependencyInfo( final Class< ? > klass )
			throws ComponentDefinitionException
	{
		Objects.requireNonNull( klass );
		this.name = null;
		this.klass = klass;
		DependencyInfo.checkClassValidity( klass );
	}


	@Override
	public int hashCode( )
	{
		final int prime = 31;
		int result = 1;
		result = prime * result + ( this.klass == null ? 0 : this.klass.hashCode( ) );
		result = prime * result + ( this.name == null ? 0 : this.name.hashCode( ) );
		return result;
	}


	@Override
	public boolean equals( final Object obj )
	{
		if ( this == obj ) {
			return true;
		}
		if ( obj == null ) {
			return false;
		}
		if ( this.getClass( ) != obj.getClass( ) ) {
			return false;
		}
		final DependencyInfo other = (DependencyInfo) obj;
		if ( this.klass == null ) {
			if ( other.klass != null ) {
				return false;
			}
			return this.name.equals( other.name );
		} else if ( this.klass != other.klass ) {
			return false;
		}
		return true;
	}


	/**
	 * @return the name of the component being depended upon, or <code>null</code> if the dependency information is
	 *         based on type.
	 */
	public String getTargetName( )
	{
		return this.name;
	}


	/**
	 * @return the type of the component being depended upon, or <code>null</code> if the dependency information is
	 *         based on the name.
	 */
	public Class< ? > getTargetClass( )
	{
		return this.klass;
	}
}