/*
 * MiningMart Version 1.0
 * 
 * Copyright (C) 2006 Martin Scholz, Timm Euler, 
 *                    Daniel Hakenjos, Katharina Morik
 *
 * Contact: miningmart@ls8.cs.uni-dortmund.de
 *
 * A list of contributing developers (other than the copyright 
 * holders) can be found at
 * http://mmart.cs.uni-dortmund.de/downloads/download.html
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program, see the file MM_HOME/LICENSE; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 */
package edu.udo.cs.miningmart.operator;

import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;

import edu.udo.cs.miningmart.exception.M4CompilerError;
import edu.udo.cs.miningmart.exception.M4CompilerWarning;
import edu.udo.cs.miningmart.exception.M4Exception;
import edu.udo.cs.miningmart.exception.ParameterDeselectedError;
import edu.udo.cs.miningmart.exception.UserError;
import edu.udo.cs.miningmart.m4.BaseAttribute;
import edu.udo.cs.miningmart.m4.Column;
import edu.udo.cs.miningmart.m4.Concept;
import edu.udo.cs.miningmart.m4.EstimatedStatistics;
import edu.udo.cs.miningmart.m4.OpParam;
import edu.udo.cs.miningmart.m4.Step;
import edu.udo.cs.miningmart.m4.utils.Print;

/**
 * This class is the superclass for all operators whose output
 * is a BaseAttribute. It implements the loop mechanism for these
 * operators. Use the method <code>getCurrentLoopNumber()</code>
 * to learn the number of the loop in which your operator is applied.
 * 
 * @author Martin Scholz
 * @version $Id: FeatureConstruction.java,v 1.7 2006/04/11 14:10:10 euler Exp $
 */
public abstract class FeatureConstruction extends ExecutableOperator {

    private Column[] theOutputColumns;
    private String[] generatedSQLDefinitions;
    private int currentLoopNr = 0;

	
	/**
	 * Load method to load all parameters for this operator.
	 * 
	 * @param st the step this operator is applied in
	 */
    public void load(Step st) throws M4CompilerError {
		try {
			super.load(st);
		}
		catch (ParameterDeselectedError e) {
			Collection violated = e.getViolatedOpParams();
			Iterator it = violated.iterator();
			while (it.hasNext()) {
				OpParam op = (OpParam) it.next();
				if ( ! this.handleParameterDeselected(op))
					throw new M4CompilerError("FeatureConstruction: Parameter " + 
					                          op.getName() + " is missing / deselected, but is obligatory!");
			}
		}			
		this.currentLoopNr = 0;
	    this.theOutputColumns        = new Column[this.getNumberOfLoops()];
	    this.generatedSQLDefinitions = new String[this.getNumberOfLoops()];
    }
    
	
    private boolean targetAttribDeselected = false;
    
    private boolean handleParameterDeselected(OpParam op) {
		this.targetAttribDeselected = op.getName().equals("TheTargetAttribute");
	    return this.targetAttribDeselected;
    }

	/**
	 * @throws UserError
	 * @throws M4CompilerError
	 * @throws SQLException
	 * @throws M4Exception
	 * @see edu.udo.cs.miningmart.m4.core.operator.Operator#execute
	 */
	public void execute(boolean lazy) throws UserError, M4CompilerWarning, M4CompilerError, M4Exception, SQLException {
		if (this.targetAttribDeselected) {
			this.print();	
		}
		else super.execute(lazy);
	}


    /** 
     * @see edu.udo.cs.miningmart.m4.core.operator.Operator#createStatement
     */
    public void createStatement(boolean lazy) throws SQLException, M4CompilerWarning, M4CompilerError
    {
        // ignore lazy!

        // for every loop:
        for (this.currentLoopNr = 0; this.currentLoopNr < this.getNumberOfLoops(); this.currentLoopNr++)
        {
            // First update Meta-Data
	        theOutputColumns[this.currentLoopNr] = generateColumnForOp();
	        // Get the Column-Definition out of the new col
	        generatedSQLDefinitions[this.currentLoopNr] = theOutputColumns[this.currentLoopNr].getSQLDefinition();
        }
    }

    /** 
     * @see edu.udo.cs.miningmart.m4.core.operator.Operator#compileStatement
     */
    public void compileStatement()  throws SQLException, M4CompilerWarning, M4CompilerError
    {
    	long oldDataType, newDataType;
    	
	    //Test column definition statement szot the output data type of the column
        for (int i = 0; i < this.getNumberOfLoops(); i++)
	    {  
    	    oldDataType = theOutputColumns[i].getColumnDataType();    
    	    newDataType = this.getM4Db().testSQLColumn(theOutputColumns[i]);
	    	
	    	if (newDataType != oldDataType)
	    	{
	    		this.doPrint(Print.OPERATOR, "Warning: FeatureConstruction: OutputAttribute Column " +
	    					 "is of different datatype than TargetAttribute Column.");
	    		try {
		    		theOutputColumns[i].setColumnDataTypeName(this.getNameForColumnDataType(newDataType));
	    		}
	    		catch (M4Exception e) {
	    			throw new M4CompilerError(
	    				"Error setting column datatype by name (type was: " + newDataType + ")!\n"
	    				+ e.getMessage());
	    		}
	    	}
	    	theOutputColumns[i].setColumnDataType(newDataType);  
	    }
    }

	/** Deprecated */
	public void writeResults() {}

    /**
     * @see edu.udo.cs.miningmart.operator.ExecutableOperator#estimateStatistics(Step)
     */
    public EstimatedStatistics estimateStatistics(Step theStep) throws M4Exception {
    	// a feature construction operator must create at least the 
    	// information that nothing is known about the new feature:
    	Collection inCons = theStep.getAllInputConcepts();
    	if (inCons == null || inCons.size() != 1) {
    		throw new M4Exception("Step '" + theStep.getName() + "' uses a FeatureConstruction operator but has 0 or more than 2 input concepts!");
    	}
    	Concept inputCon = (Concept) inCons.iterator().next();
    	EstimatedStatistics inStat = inputCon.getEstimatedStatistics();
    	
    	EstimatedStatistics outStat = inStat.copy();
    	try {
    		// for every loop add unknown estimates for the attribute
    		// created in that loop; subclasses may add known estimates:
    		for (int loop = 0; loop < this.getNumberOfLoops(); loop++) {
    			outStat.addAttribute(this.getTheOutputAttribute(loop).getName());
    		}
    		return outStat; // allows subclasses to extend the information. 
    						// It will be connected to the output concept 
    						// in the method Concept.getEstimatedStatistics(), from where
    						// this method is called.
    	}
    	catch (M4CompilerError mce) {
    		throw new M4Exception(mce.getMessage());
    	}
    }
	
    /**
     * Method to generate the new Column that all FeatureConstruction operators
     * create. The SQL definition of the new column is operator specific, 
     * thus every subclass of FeatureConstruction has to implement
     * the method <code>generateSQL()</code>.
     *
     * @return A Column object with the new structure
     * @throws M4CompilerError A simple exception object with an error message.
     */
    public Column generateColumnForOp() throws M4CompilerError
    {
    	try {
	        final Column theTargetAttributeColumn = this.getTheTargetAttribute().getCurrentColumn();
	        	
	        if (theTargetAttributeColumn == null) {
	        	throw new M4CompilerError("Operator '" + this.getName() + "': no column was found for the target attribute! Compilation stopped.");				
	        }
	        BaseAttribute theBA = getTheOutputAttribute();
	        if (theBA == null) {
	        	throw new M4CompilerError("Operator '" + this.getName() + "': no output attribute was found! Compilation stopped.");				
	        }
	        
	        final String newColSqlDef = generateSQL(theTargetAttributeColumn);
	
	        // A new column is added to the columns of the output BA
	        Column newCol = (Column) this.getM4Db().createNewInstance(edu.udo.cs.miningmart.m4.core.Column.class);
	        newCol.setId(0); // set Id to 0, indicating to DB.createUpdateColumn() that a new Id is needed        
	
	        // the only BaseAttribute for the new Column is the output attribute:
	        newCol.setBaseAttribute(theBA);
	
	        // add new column to the output attribute's columns:
	        // getTheOutputAttribute().addColumn(newCol);
	
	        // fill new Column object; most values can be taken over from the target attribute's column:
	        newCol.setName(getTheOutputAttribute().getName()); // no better name can be known for a new column
	        newCol.setColumnset(theTargetAttributeColumn.getColumnset());
	        // theTargetAttributeColumn.getColumnset().addColumn(newCol);
	        
	        // Attention: the new column's data type is checked and may be changed
	        // in method FeatureConstruction.compileStatement!
	        newCol.setColumnDataType(theTargetAttributeColumn.getColumnDataType());
	        newCol.setColumnDataTypeName(theTargetAttributeColumn.getColumnDataTypeName());
	        // statistics for newCol do not exist yet; the newCol object knows it
	
	        newCol.setSQLDefinition(newColSqlDef);
	
			this.getStep().addToTrash(newCol);
	        return newCol;
    	}
   		catch (M4Exception m4e)
   		{   throw new M4CompilerError("M4 interface error in " + this.getName() + ": " + m4e.getMessage());  }
    } // end Column generateColumnForOp

    /**
     * Abstract method to be implemented by all subclasses. In this method
     * the SQL definition for the Column which this operator creates is
     * created.
     * 
     * @param targetColumn The Column to which this FeatureConstruction Operator
     *        is applied. Some subclasses may ignore this parameter.
     * @return An SQL definition for the newly constructed Column.
     * @throws M4CompilerError A simple exception object with an error message.
     */
    public abstract String generateSQL(Column targetColumn) throws M4CompilerError;

	/**
	 * Get the current loop number. 
	 * 
	 * @return the current loop in which this operator is applied
	 */
    public int getCurrentLoopNumber()
    {  return currentLoopNr;  }

	/**
	 * All FeatureConstruction operators have an input concept which 
	 * is the same in all loops. Get it with this method.
	 * 
	 * @return the input concept for this operator
	 */
    public Concept getTheInputConcept() throws M4CompilerError {
       return (Concept) this.getSingleParameter("TheInputConcept");
    }

	/**
	 * All FeatureConstruction operators have an output BaseAttribute which 
	 * is different from loop to loop. Get it with this method.
	 * 
	 * @return the output attribute for this operator in the current loop
	 */
    public BaseAttribute getTheOutputAttribute() throws M4CompilerError {
       return this.getTheOutputAttribute(this.getCurrentLoopNumber());
    }

	/**
	 * All FeatureConstruction operators have a target attribute which 
	 * is different from loop to loop. Get it with this method.
	 * 
	 * @return the target attribute for this operator in the current loop
	 */
    public BaseAttribute getTheTargetAttribute() throws M4CompilerError {
    	BaseAttribute target = this.getTheTargetAttribute(this.getCurrentLoopNumber());
    	if (target == null) {
    		throw new M4CompilerError("Operator '" + this.getName() + "': cannot find target attribute, compilation stopped!");
    	}
    	return target;
    }

	/**
	 * All FeatureConstruction operators have an output attribute which 
	 * is different from loop to loop. Get it with this method.
	 * 
	 * @param loopNr the number of the loop for which the output attribute is wanted
	 * @return the output attribute for this operator in the given loop
	 */
    public BaseAttribute getTheOutputAttribute(int loopNr) throws M4CompilerError {
        return (BaseAttribute) this.getSingleParameter("TheOutputAttribute", loopNr);
    }

	/**
	 * All FeatureConstruction operators have a target attribute which 
	 * is different from loop to loop. Get it with this method.
	 * 
	 * @param loopNr the number of the loop for which the target attribute is wanted
	 * @return the target attribute for this operator in the given loop
	 */
    public BaseAttribute getTheTargetAttribute(int loopNr) throws M4CompilerError {
		return (BaseAttribute) this.getSingleParameter("TheTargetAttribute", loopNr);
    }
    
    private String getNameForColumnDataType(long type) throws M4CompilerError
    {    	
    	if (type == 12) {  return "NUMBER";  }
    	if (type == 13) {  return "STRING";  }
    	if (type == 14) {  return "DATE";  }
    	if (type == 15) {  return "KEY";  }
    	throw new M4CompilerError("FeatureConstruction: Found unknown column data type number: " + type);	
    }
}
/*
 * Historie
 * --------
 *
 * $Log: FeatureConstruction.java,v $
 * Revision 1.7  2006/04/11 14:10:10  euler
 * Updated license text.
 *
 * Revision 1.6  2006/04/06 16:31:10  euler
 * Prepended license remark.
 *
 * Revision 1.5  2006/03/23 11:13:45  euler
 * Improved exception handling.
 *
 * Revision 1.4  2006/03/22 16:49:27  euler
 * *** empty log message ***
 *
 * Revision 1.3  2006/03/19 21:17:13  scholz
 * refactoring
 *
 * Revision 1.2  2006/01/18 16:58:58  euler
 * Added some basic estimations of statistics.
 * Will need improvements.
 *
 * Revision 1.1  2006/01/03 09:54:20  hakenjos
 * Initial version!
 *
 */

/*
 * Old Historie
 * ------------
 *
 * Revision 1.28  2003/07/04 13:47:35  scholz
 * Bugfix: Do not introduce the target column to the columnset before having its definition.
 * This is useful for DrawSample, because columns attached to a BaseAttribute will be copied
 * to the sample table, which does not succeed, if the column does not yet exist.
 *
 * Revision 1.27  2003/01/09 13:53:38  euler
 * Added warning if created column differs in
 * datatype from target attribute's column.
 *
 * Revision 1.26  2002/10/24 14:20:38  euler
 * New Javadoc comments.
 *
 * Revision 1.25  2002/10/24 10:20:05  euler
 * New Javadoc comments.
 *
 * Revision 1.24  2002/10/21 16:17:20  scholz
 * javadoc comments: fixed some invalid tags
 *
 * Revision 1.23  2002/10/09 17:11:25  scholz
 * Deleted control information in Case.
 * Instead an instead of DB is passed to rthe constructor of several M4Objects.
 * Operators copy the DB instance from Step during load(Step).
 * Print information is available via doPrint(..) for M4Objects.
 *
 * Status: DM Case compiles
 *
 * Revision 1.22  2002/10/08 18:07:56  scholz
 * Prepared code for parallel execution of multiple compile-Threads.
 * Calls to static fields like DB.m4Db were removed. Now each
 * Thread has its own DB object, reachable via
 * CompilerAccessLogic or Case.
 * The methods getCase() and getM4Db() were added to M4Object.
 * The static methods of Parameter now need an additional
 * parameter of type DB.
 * All direct calls from Operators to these Parameter methods were
 * removed.
 * All old load() and print() routines were removed.
 * The static calls to Print were removed. Now CompilerAccessLogic
 * references a valid Print object for the current Thread. This is
 * reachable via Case. The methods doPrint for messages and
 * Exceptions were added to M4Object.
 * The Print mechanism is not fully functional, yet.
 * A getStatus method was added to the Interface. It is not
 * functional yet for multiple Threads.
 *
 * Status: Compiles.
 *
 * Revision 1.21  2002/10/02 17:57:47  scholz
 * beautified autoLoad().
 * getSingleParameter(..) added.
 *
 * Revision 1.20  2002/10/01 14:26:10  scholz
 * Operator.getParameter() now always outputs an M4Object[]-object.
 *
 * Revision 1.19  2002/09/05 16:30:10  scholz
 * autoload integrated into class FeatureConstuction,
 * status: successfully tested on DM_SALES_PREDICTION with
 * and without operator Unsegment.
 *
 * Revision 1.18  2002/08/05 10:35:47  euler
 * Restructured the operator hierarchy: introduction of SingleCSOperator
 * and MultipleCSOperator. Changed this class accordingly.
 *
 * Revision 1.17  2002/07/26 12:41:35  euler
 * Deleted superfluous call of statistics function.
 *
 * Revision 1.16  2002/07/22 17:14:13  euler
 * Bugfix.
 *
 * Revision 1.15  2002/07/19 11:53:12  euler
 * Bugfix: new Column was not added to output columnset.
 *
 * Revision 1.14  2002/07/18 17:00:48  euler
 * Implemented the Feature.getCurrent()-methods.
 *
 * Revision 1.13  2002/06/26 14:27:17  scholz
 *
 * support for output template baseattributes in "get methods" added
 *
 * Revision 1.12  2002/06/11 09:21:37  euler
 * Column Statistics handling added, not tested yet.
 *
 * Revision 1.11  2002/06/10 08:45:11  euler
 * Added trash index, first tests ok.
 *
 * Revision 1.10  2002/06/06 14:08:53  euler
 * Made operator loopable. Not tested yet.
 *
 * Revision 1.9  2002/06/06 09:59:58  euler
 * Made operator loopable. Not tested yet.
 *
 * Revision 1.8  2002/06/06 07:55:40  euler
 * Fixed some bugs.
 *
 * Revision 1.7  2002/06/03 12:02:36  euler
 * Restructured FeatureConstruction.
 *
 * Revision 1.6  2002/05/31 12:35:00  euler
 * *** empty log message ***
 *
 * Revision 1.5  2002/05/15 13:05:01  euler
 * First version that compiles.
 *
 * Revision 1.4  2002/05/14 12:57:14  euler
 * First M4 extensions, does not yet compile
 *
 * Revision 1.3  2002/05/10 13:23:21  euler
 * First M4 extensions, does not yet compile
 *
 * Revision 1.2  2002/05/07 13:06:56  wiese
 * get+set methods
 *
 * Revision 1.1  2002/04/30 13:00:27  wiese
 * compileable version
 *
 * Revision 1.6  2002/04/19 15:23:12  wiese
 * Initialversion nach der uebergabe
 *
 */


