package edu.uprm.admg.nettraveler.schema;

// JDK imports
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import edu.uprm.admg.nettraveler.type.MDObject;
import edu.uprm.admg.util.DataVerify;

/**
 * Generic attribute.
 * <p/> 
 * Represents a generic attribute that can be 
 * manipulated by the execution engine during query 
 * execution. For each attribute there is: a name and a
 * class that implements this 
 * base type. Attributes can also have an alias name.
 * For example for when a query such as<br>
 * SELECT COUNT(*) AS count FROM table <br>
 * is entered.
 * <p/> 
 * This class should be used in the implementation of the
 * physical operators of the query plan.
 * 
 * <br>
 * Universidad de Puerto Rico@Mayaguez.
 * <br>
 * <code>NetTraveler</code>
 */
/*
 * @author Elliot A. Vargas-Figueroa - M.S. Thesis Project.
 * P.I. Manuel Rodriguez-Martinez
 */
public abstract class Attribute implements Serializable{
	/**
	 * The name of this attribute.
	 */
	private String name = null;
	/**
	 * Alias of this attribute.
	 */
	private String alias = null;
	
	/**
	 * The class object for the Java class used implement the 
	 * base type for this attribute.
	 */
	private transient Class baseClass = null;
	
	/**
	 * Creates an attribute based on an attribute name, base type and class 
	 * implementing the base type.
	 * <p/>
	 * An exception is thrown if 
	 * <code>null</code> parameters are passed. 
	 * 
	 * @param className Class name of the type.
	 */ 
	protected Attribute(String name, String className) {
		// sanity checks
		DataVerify.VerifyObjectParam("name,", name);
		DataVerify.VerifyObjectParam("className", className);
		this.name = name;
		try {
			this.baseClass = Class.forName(className);
			test();
		} catch (Exception e) {
			throw new RuntimeException(e.toString());
		}
	}
	/**
	 * Test the class.
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 *
	 */
	private void test() throws InstantiationException, IllegalAccessException{
		Object test = baseClass.newInstance();
		if(!(test instanceof MDObject))
			throw new IllegalArgumentException("The passed class " + 
					baseClass.getName()+ " is not " +
					"an instance of " + MDObject.class);
		test = null;
	}
	/**
	 *  Returns <code>true</code> if this object is equal to the
	 * passed object. Two attributes are considered equal if 
	 * their base types are equal. The base type name is
	 * obtained by calling the 
	 * {@link #getBaseClassName()} method.
	 * 
	 * @return <code>true</code> if this object is equal to the
	 * passed object.
	 */
	public final boolean equals(Object obj){
		if(obj == this)
			return true;
		if(!(obj instanceof Attribute))
			return false;
		Attribute o = (Attribute) obj;
		return (name.equals(o.name)
				&& (baseClass.equals(o.baseClass))
				&& localEquals(o));
	}
	/**
	 * Returns <code>true</code> if the subclass is equal to the
	 * passed object.
	 * 
	 * @return <code>true</code> if the subclass is equal to the
	 * passed object.
	 */
	protected abstract boolean localEquals(Attribute attr);
	/**
	 * @return hash code of this object.
	 */
	public final int hashCode(){
		int result = 17;
		result += result * 37 + name.hashCode();
		result += result * 37 + baseClass.hashCode();
		result += result * 37 + localHashCode();
		return result;
	}
	/**
	 * 
	 * @return hash code of the subclass.
	 */
	protected abstract int localHashCode();
	/**
	 * Returns the name of this attribute.
	 * 
	 * @return the name of this attribute.
	 */
	public final String getName(){
		return name;
	}
	/**
	 * Returns the class implementing the base type for this attribute.
	 * 
	 * @return the class that implements the base type for this attribute.
	 */
	public final Class getBaseClass(){
		return baseClass;
	}
	/**
	 * Returns the name of the class implementing the base type for this attribute.
	 * @return Name of the class implementing the base type for this attribute.
	 */
	public final String getBaseClassName(){
		return baseClass.getName();
	}
	/**
	 * @return The alias name of this attribute.
	 */
	public final String getAlias() {
		return alias;
	}
	/**
	 * @param alias The alias name of this attribute.
	 */
	public final void setAlias(String alias) {
		this.alias = alias;
	}
	/*
	 * Serialization stuff
	 */
	/**
	 * @param out
	 * @exception IOException
	 */
	private void writeObject(ObjectOutputStream out) throws IOException{
		out.defaultWriteObject();
		out.writeUTF(baseClass.getName());
	}
	
	/**
	 * @param in
	 * @exception IOException
	 * @exception ClassNotFoundException
	 */
	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
		in.defaultReadObject();
		try{
			DataVerify.VerifyObjectParam("Attribute name,", name);
			String x = in.readUTF();
			DataVerify.VerifyObjectParam("Class name", x);
			baseClass = Class.forName(x);
			test();
			x = null;
		} catch (Exception e) {
			throw new IOException(e.toString());
		}
	}
}
