package edu.uprm.admg.nettraveler.schema;

// JDK imports
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;

import edu.uprm.admg.nettraveler.type.MDObject;
import edu.uprm.admg.util.DataVerify;

/**
 * <code>GeneralizedProjectionAttribute</code> implements a complex projection 
 * operation over a group of attributes contained in a tuple. 
 * Each complex projection is defined in the body 
 * of a static method defined in a given Java Class. Each instance of this
 * class contains the name to be used for the projected attribute, the base 
 * type for the attribute, the class that implements the base type, 
 * the index needed to find the arguments for the projection in a
 * <code>Tuple</code> object, and the <code>MethodDescriptor</code> used
 * to compute the complex function in the projection.
 * 
 * <P>
 * Example: Consider the following query on table
 * </P>
 * <code>Person(name:char(30), age:integer, salary:float)</code>:
 * <PRE>
 * SELECT name, age, DoubleSalary(salary) 
 * FROM Person;
 * </PRE>
 * Function <code>DoubleSalary</code> returns twice the value of its
 * argument.  In this case, a DAP will read each tuple from the table 
 * <code>Person</code>, and extract the base attributes
 * <code>name</code>, <code>age</code>, and <code>salary</code>.
 * Then, it will project the first two attributes, using the same names,
 * and perform the generalized projection <code>DoubleSalary(salary)</code>.
 * One possible way to create the objects for the projected attributes is:
 * <P>
 * <PRE>
 * ProjectionAttribute[] proj_attrs;
 * MethodDescriptor salary_descriptor;
 * int[] args_idx;
 * ...
 * // create the necessary objects
 * proj_attrs = new ProjectionAttribute[3];
 * proj_attrs[0] = new ProjectionAttribute("name", Types.STRING, 
 * Class.forName("edu.umd.umiacs.mocha.type.MIString"), 0);
 * proj_attrs[1] = new ProjectionAttribute("age", Types.INTEGER, 
 * Class.forName("edu.umd.umiacs.mocha.type.MIInteger"), 1);
 * proj_attrs[2] = new GeneralizedProjectionAttribute("DoubleSalary(salary)", 
 * Types.FLOAT, Class.forName("edu.umd.umiacs.mocha.type.MIFLOAT"), args_idx, 
 * salary_descriptor);
 * ...
 * // project a tuple and save the results
 * result1 = projs_attrs[0].project(tuple_instance);
 * result2 = projs_attrs[1].project(tuple_instance);
 * result3 = projs_attrs[2].project(tuple_instance);
 * </PRE>
 * This code will project each of the required attributes from the
 * group of attributes contained in the tuple passed as argument.
 * In each case, the result of the projection will be stored 
 * in the <code>resultX</code> argument.
 * 
 * @author M. Rodriguez-Martinez
 */

public final class GeneralizedProjectionAttribute extends ProjectionAttribute {
    
    /**
	 * Serial version
	 */
	private static final long serialVersionUID = 9050680201919248390L;
	/**
	 * The actual generalized projection
	 */
	private FunctionValueAttribute function = null;
    
    /**
     * Constructs a <code>GeneralizedProjectionAttribute</code> based on:
     * a projection attribute name, a base type, a class implementing this
     * base type, an array used as index to find the arguments of the 
     * projection in a <code>Tuple</code> object, and a descriptor for the 
     * user-defined function to be associated with the projection. A run 
     * time exception will be thrown if any of the arguments is null.
     * 
     * @param name the name of the projected attribute
     * @param resultClassName the class implementing the base type of the projection
     * @param argsIdx an array used as index to find the arguments for the projection in a <code>Tuple</code>
     * @param methodDesc a descriptor used to execute the function associated with this generalized projection
     * @param projIdx Index in the working <code>Tuple</code> of 
     * the result object of the operation. 
     * @see MethodDescriptor
     */
    public GeneralizedProjectionAttribute(String name, int[] argsIdx,
					  MethodDescriptor methodDesc, String resultClassName,
					  int projIdx) {
	    	super(name, projIdx, resultClassName);
	    	this.function = new FunctionValueAttribute(name, 
	    			argsIdx, methodDesc, resultClassName, projIdx);
    }
    /**
     * Executes a generalized projection operation by running the body
     * of an user-defined function, using one or more attributes contained
     * in a <code>Tuple</code> as arguments to this function. The result of 
     * the projection is set in the <code>Tuple</code> using the
     * <code>index</code> of the attribute as passed to this object
     * constructor 
     * ({@link #GeneralizedProjectionAttribute(String, int[], MethodDescriptor, String, int)}). 
     * 
     * @param tuple the tuple containing the attributes to the used in the projection
     * @exception ProjectionException thrown if an error occurs while executing the generalized projection operation.
     */
    public void project(Tuple tuple) throws ProjectionException {
    		DataVerify.VerifyObjectParam("tuple", tuple);
    		try{
    			MDObject obj = function.compute(tuple);
    			tuple.setAttributeValue(this.getArgIdx(), obj);
    		}catch(Exception e){
    			throw new ProjectionException(e.toString());
    		}
    }

    /**
     * Initializes the method descriptor contained in this generalized
     * projection. 
     * @exception MethodInitializeException if an error occurs during initialization.
     * @see MethodDescriptor#initializeMethod
     */
    public void initializeMethod() throws MethodInitializeException{
    		try {
				function.initializeMethod();
			} catch (FunctionValueException e) {
				throw new MethodInitializeException(e.toString());
			}
    }
	/**
	 * @return <code>true</code> if the 
	 * <code>GeneralizedProjectionAttribute</code>
	 * is initialized and ready
	 * to project.
	 */
	public boolean isInitialized() {
		return function.isInitialized();
	} 
    /**
     * @see edu.uprm.admg.nettraveler.schema.Attribute#localEquals(edu.uprm.admg.nettraveler.schema.Attribute)
     */
	protected boolean localEquals(Attribute attr) {
		if(!(attr instanceof GeneralizedProjectionAttribute))
			return false;
		GeneralizedProjectionAttribute o = (GeneralizedProjectionAttribute) attr;
		return ((this.getArgIdx() == o.getArgIdx())
				&& (function.equals(o.function)));
		
	}
	/**
	 * @see edu.uprm.admg.nettraveler.schema.Attribute#localHashCode()
	 */
	protected int localHashCode() {
		int result = 17;
		result += result * 37 + this.getArgIdx();
		result += result * 37 + function.hashCode();
		return result;
	}
    /**
     * Serialization stuff
     */
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
    		in.defaultReadObject();
    		try{
    			DataVerify.VerifyObjectParam("Function", function);
    		}catch(Exception e){
    			throw new InvalidObjectException(e.toString());
    		}
    }
}
					  

