/*
 * 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;

/**
 * <p>M4 Compiler</p>
 * <p>Copyright: Copyright (c) 2002</p>
 * <p>Company: University Dortmund</p>
 */

import java.sql.SQLException;

import edu.udo.cs.miningmart.storedProcedures.TimeOperatorException;

import edu.udo.cs.miningmart.db.DB;
import edu.udo.cs.miningmart.exception.M4CompilerError;
import edu.udo.cs.miningmart.exception.M4Exception;
import edu.udo.cs.miningmart.m4.BaseAttribute;
import edu.udo.cs.miningmart.m4.Column;
import edu.udo.cs.miningmart.m4.Columnset;
import edu.udo.cs.miningmart.m4.Concept;
import edu.udo.cs.miningmart.m4.Value;

/**
 * Abstract super class for all time operators creating a
 * new <b>table</b> by calling a stored procedure of the
 * database.
 * 
 * @author Martin Scholz
 * @version $Id: TimeOperator.java,v 1.5 2006/04/11 14:10:11 euler Exp $
 */
public abstract class TimeOperator extends SingleCSOperator {

    /** @return the suffix added to the concept name to get the name
     * of the new column set */
    protected abstract String getColumnSetSuffix();

    /**
     * This method calls the stored procedure in the database. It
     * passes all the involved table and column names to the stored
     * procedure, which creates a new table for the results.
     * @param newColumnSetName the name of the table to be created
     * for the results
    */
    protected abstract void runStoredProcedure(String newColumnSetName)
    	throws SQLException, M4CompilerError, TimeOperatorException;

    /** This method needs to create the M4 JAVA objects for the new
     * columns and has to link these objects to the base attributes
     * (and vice versa).  Additionally the new columns need to be
     * linked to the column set (and vice versa).
     * If an operator creates base attributes by a kind of template
     * mechanism, then the links of these objects to the concept
     * should be set here, as well.
     *
     * @param newColumnSet the new column set generated by this operator
    */
    protected abstract void createJavaColumns(Columnset newColumnSet) throws SQLException, M4CompilerError;

    /* This method is only implemented for the java compiler, it is never called... */
    public String generateSQLDefinition(String selectPart) throws M4CompilerError
    {  return null;  }

    /** @return the type of <code>Columnset</code>, a table in this case. */
    public String getTypeOfNewColumnSet()
    {   return Columnset.CS_TYPE_TABLE;  }

    /* This method is only implemented for the java compiler, it is never called... */
    protected boolean mustCopyFeature(String nameOfFeature)
    {  return false;  }

    /**
     * This method overrides the method in ConceptOperator!
     * This method holds the control structure of the class
     * <i>TimeOperator</i>.  It generates the name of the new column
     * set to be created, calls the method for running the stored
     * procedure and registers the new column set linked to the output
     * concept in the JAVA cache for M4 objects.
     *
     * @param index Can be ignored as this is a SingleCSOperator.
     * @return The new Columnset for the output concept.
    */
    protected Columnset createSingleColumnSet(int index) throws M4CompilerError
    {
        try
        {
	        final String columnSetName = this.getNewCSName();

	        /* --- run stored procedure: --- */
	        try {
		        this.runStoredProcedure(getInputConcept().getCurrentColumnSet().getSchema() + "." + columnSetName);
	        }
	        catch (TimeOperatorException e) {
	        	throw new M4CompilerError(
	        		"Exception in stored procedure caught:\n" + e.getMessage()
	        	);	
	        }

	        /* --- create java columnset with link to concept: --- */
	        Columnset newCS = this.createJavaColumnset(columnSetName);

	        /* --- create java columns --- */
	        this.createJavaColumns(newCS);

	        /* --- store the new table in the garbage collection table --- */
	        long   step   = this.getStep().getId();
	        String schema = getInputConcept().getCurrentColumnSet().getSchema();
	        this.getM4Db().addTableToTrash(columnSetName, schema, step);
			
	        /* --- return the columnset --- */
	        return newCS;
         }
         catch (SQLException sqle)
         {  throw new M4CompilerError("TimeOperator: SQL error: " + sqle.getMessage());  }
   		 catch (M4Exception m4e)
   		 {   throw new M4CompilerError("M4 interface error in " + this.getName() + ": " + m4e.getMessage());  } 
    }

    private Columnset createJavaColumnset(String columnSetName) throws M4CompilerError 
    {
    	try {
	        final Columnset newCS = (Columnset) this.getM4Db().createNewInstance(edu.udo.cs.miningmart.m4.core.Columnset.class);
	        newCS.setId(0);
			newCS.setName(columnSetName);
	        final Columnset inputColumnSet = getInputConcept().getCurrentColumnSet();
	        newCS.setSchema(inputColumnSet.getSchema());
			newCS.setType(this.getTypeOfNewColumnSet());
			newCS.setSQLDefinition(columnSetName);
	        newCS.setTheConcept(this.getOutputConcept());
			newCS.setMultiStepBranch(inputColumnSet.getMultiStepBranch());
			
			this.getStep().addToTrash(newCS);
			return (newCS);
    	}
   		catch (M4Exception m4e)
   		{   throw new M4CompilerError("M4 interface error in " + this.getName() + ": " + m4e.getMessage());  } 
    }


    /*
     * Method to create new M4 column objects.
     * @param outputBA the base attribute of the output concept the
     * new column should belong to
     * @param newCS the new ColumnSet created by the operator
     * @param columnForType a column to copy the datatype and datatype
     * name information from
    */
    protected Column createM4Column(BaseAttribute outputBA, Columnset newCS, Column columnForType)
		throws M4CompilerError
    {
    	try {
			final String newColumnName = outputBA.getName();
	
			Column newColumn = (Column) this.getM4Db().createNewInstance(edu.udo.cs.miningmart.m4.core.Column.class);
			newColumn.setId(0);
			newColumn.setName(newColumnName);
			newColumn.setColumnset(newCS);
			newColumn.setBaseAttribute(outputBA);
			newColumn.setColumnDataType(columnForType.getColumnDataType());
			newColumn.setColumnDataTypeName(columnForType.getColumnDataTypeName());
			newColumn.setSQLDefinition(newColumnName);
			// outputBA.addColumn(newColumn);
			// newCS.addColumn(newColumn);

			this.getStep().addToTrash(newColumn);
			return newColumn;
    	}
   		catch (M4Exception m4e)
   		{   throw new M4CompilerError("M4 interface error in " + this.getName() + ": " + m4e.getMessage());  } 
    }

	protected void executeDbProcedure(String procedureName, String[] parameters)
		throws M4CompilerError
	{
		if (parameters != null) {
			String[] p = new String[parameters.length+1];
			for (int i=0; i<parameters.length; i++) {
				p[i+1] = parameters[i];
			}
			p[0] = "NULL"; // indicates that the code is run as a stored procedure
			parameters = p;
		}	
		this.getM4Db().executeDBProcedure(procedureName, parameters, true);
	}

    // --- Helper methods to construct the parameter strings for the stored procedure ---

    protected static String getQuotedColNameforInpBA(BaseAttribute ba) throws M4CompilerError {
		try {  return (DB.quote(ba.getColumn(0).getName().toUpperCase()));  }
   		catch (M4Exception m4e)
   		{   throw new M4CompilerError("M4 interface error in TimeOperator: " + m4e.getMessage());  } 
    }

    protected static String getQuotedColNameforOutBA(BaseAttribute ba) {
		return (DB.quote(ba.getName()));
    }

    protected static String getQuotedColSetNameforCon(Concept c) throws M4CompilerError {
    	try {
			final edu.udo.cs.miningmart.m4.Columnset cs = c.getCurrentColumnSet();
			return (DB.quote(cs.getSchema().toUpperCase() + "." + cs.getName().toUpperCase()));
    	}
   		catch (M4Exception m4e)
   		{   throw new M4CompilerError("M4 interface error in TimeOperator: " + m4e.getMessage());  } 
    }

    protected static String getUnquotedIntFromValue(Value v) {
		return (v.getValue().trim());
    }

	protected static String unquote(String s) {
		return s.substring(1, s.length() - 1).trim();
	}

    // --- Helper method throwing exceptions in case of invalid parameter objects: ---

    protected void valueIsLong(Value v, String name)
		throws M4CompilerError
    {
		if ((v == null) || (!v.isLong()))
	    	createParameterException(name);
    }

    protected void valueIsDouble(Value v, String name)
		throws M4CompilerError
    {
		if ((v == null) || (!v.isDouble()))
	    	createParameterException(name);
    }

    protected void baseattribHasColumns(BaseAttribute b, String name)
		throws M4CompilerError
    {
    	try {
			if ((b == null) || ( ! b.hasColumns()))
		    	createParameterException(name);
    	}
   		catch (M4Exception m4e)
   		{   throw new M4CompilerError("M4 interface error in " + this.getName() + ": " + m4e.getMessage());  } 
    }

    private void createParameterException(String name)
		throws M4CompilerError
    {
		throw new M4CompilerError(
					"Parameter '" + name + "' of step "	+ this.getStep().getId()
					+ " of wrong data type (or BA without column)!");
    }
}
/*
 * Historie
 * --------
 *
 * $Log: TimeOperator.java,v $
 * Revision 1.5  2006/04/11 14:10:11  euler
 * Updated license text.
 *
 * Revision 1.4  2006/04/06 16:31:11  euler
 * Prepended license remark.
 *
 * Revision 1.3  2006/03/30 16:07:13  scholz
 * fixed author tags for release
 *
 * Revision 1.2  2006/03/23 11:13:45  euler
 * Improved exception handling.
 *
 * Revision 1.1  2006/01/03 09:54:21  hakenjos
 * Initial version!
 *
 */
