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

import java.sql.*;

/**
 * @author Martin Scholz
 * @version $Id: OutputColumnset.java,v 1.4 2006/04/11 14:10:16 euler Exp $
 */
class OutputColumnset {

    private final String      tableName;
    private final Attribute[] attributes;
    private final Connection  connection;


    private Connection getConnection() {
	return (this.connection); 
    }

    private String getTableName() {
	return (this.tableName);
    }

    private int getAttributesSize() {
	return (this.attributes.length);
    }

    private Attribute getAttribute(int i) {
	if (i >= 0 && i < getAttributesSize())
	    return (this.attributes[i]);
	return null;
    }

    // -------------------------------------------------------------

    public OutputColumnset(String tableName, Attribute[] attributes, Connection connection)
	throws TimeOperatorException
    {
	this.tableName  = tableName;
	this.attributes = attributes;
	this.connection = connection;

	// Validity checks
	if (this.tableName == null)
	    throw new TimeOperatorException
		("No table name specified in constructor of OutputColumnset.");

	if (this.attributes == null)
	    throw new TimeOperatorException
		("No columns specified in constructor of OutputColumnset.");
	for (int i=0; i<this.attributes.length; i++) {
	    if (this.attributes[i] == null)
		throw new TimeOperatorException
		    ("Null value found in array argument to constructor of OutputColumnset.");
	}

	if (this.connection == null)
	    throw new TimeOperatorException
		("No database connection specified in constructor of OutputColumnset.");


	// Delete the target table if it already exists.
	boolean deletionWorked = deleteDatabaseTable();
	// Hack for Postgres: if the table did not exist, the attempt to delete it
	// failed; then the current transaction must be ended by a commit:
	if ( ! deletionWorked) {
		this.executeUpdateClose("COMMIT");
	}
	
	// Then create the new target table.
	boolean created = createDatabaseTable();
	if (!created)
	    throw new TimeOperatorException
		("Could not create target table '" + this.getTableName() +
		 "' in constructor of OutputColumnset.");
    }


    public void insert(Object[] values) throws TimeOperatorException {
	if (values == null)
	    throw new TimeOperatorException
		("Trying to insert an array of <null> into table '" + this.getTableName() + "'.");
	
	if (values.length != this.getAttributesSize())
	    throw new TimeOperatorException
		("Trying to insert an array of wrong length into table '" + this.getTableName() + "'.");
	
	String sql = "INSERT INTO " + this.getTableName() + " VALUES ( ";

	for (int i=0; i<values.length; i++) {
	    final String val = this.getAttribute(i).getConvertedOutput(values[i]);
	    if (val == null)
		throw new TimeOperatorException
		    ("Trying to insert an object of unconvertable type into table '"
		     + this.getTableName() + "'.");
	    sql += val + (i < values.length - 1 ? ", " : " )");
	}

	DbConnector.infoOutput("DB Query: " + sql);

	if (!executeUpdateClose(sql))
	    throw new TimeOperatorException
		("An error occurred trying to insert values into table '"
		 + this.getTableName() + "'\nInsert statement was: " + sql);
    }


    /**
     * Creates an index on attribute number i of the output columnset.
     * @param i number of the attribute (see array for constructor) to
     *          create an index on
     * @return <i>true</i> iff an index could be created.
    */
    public boolean createIndexOn(int i) {
	final String attrName = this.getAttribute(i).getName();
	if (attrName == null)
	    return false;

	String sql = "CREATE INDEX " + this.getTableName() + "_idx ON "
	    + this.getTableName() + " ( " + attrName + " )";

	return (this.executeUpdateClose(sql));
    }

    /**
     * Creates an index on attributes number i and j of the output
     * columnset.
     * @param i number of the first attribute (see array for
     *        constructor) to create an index on
     * @param i number of the second attribute
     * @return <i>true</i> iff an index could be created.
    */
    public boolean createIndexOn(int i, int j) {
	if (i == j)
	    return this.createIndexOn(i);
	
	final String attrName1 = this.getAttribute(i).getName();
	final String attrName2 = this.getAttribute(j).getName();
	if (attrName1 == null || attrName2 == null)
	    return false;

	String sql = "CREATE INDEX " + this.getTableName() + "_idx ON "
	    + this.getTableName() + " ( " + attrName1 + ", " + attrName2 + " )";

	return (this.executeUpdateClose(sql));
    }





    // ------------------------------------------------------------------
    //                Helper methods of the constructor
    // ------------------------------------------------------------------

    /* deletes table with name <i>this.tableName</i>
     * @return <i>true</i> iff the table could be created */
    private boolean deleteDatabaseTable() {
	String delSql = "DROP TABLE " + this.getTableName();
	DbConnector.infoOutput("INFO: Deleting " + this.getTableName());

	return this.executeUpdateClose(delSql);
    }

    /* creates the table with name <i>this.tableName</i> and the
     * attributes specified in <i>this.attributes</i>
     * @return <i>true</i> iff the table could be created */
    private boolean createDatabaseTable() {
	String sql = "CREATE TABLE " + this.getTableName() + " (";

	final int size = this.getAttributesSize();
	for (int i=0; i<size; i++) {
	    final String attrName = this.getAttribute(i).getName();
	    final String attrType = this.getAttribute(i).getDataTypeName();
	    sql += attrName + " " + attrType + (i < size - 1 ? ", " : ")");
	}
	DbConnector.infoOutput("INFO: Creating " + sql);

	return this.executeUpdateClose(sql);
    }



    // ------------------------------------------------------------------
    //                Helper methods for database access
    // ------------------------------------------------------------------

    /* Reads an SQL string, does an <i>executeUpdate</i> and returns
     * if it succeded.  All exceptions are caught and result in return
     * value <i>false</i>. */
    private boolean executeUpdateClose(String sql) {
	if (sql == null)
	    return false;

	boolean   ret  = true;
	Statement stmt = null;
	
	try {
	    stmt = this.getConnection().createStatement();
	    stmt.executeUpdate(sql);
	} catch (SQLException e) {
	    DbConnector.infoOutput(e.getMessage());
	    ret = false;
	} finally {
		closeStatement(stmt);
    }

	return ret;
    }

    private static void closeStatement(Statement statement) {
	if (statement == null)
	    return;
	
	try {
	    statement.close();
	    statement = null;
	}
	catch (SQLException e) { DbConnector.infoOutput(e.getMessage()); }
    } 

}
/*
 *$Log: OutputColumnset.java,v $
 *Revision 1.4  2006/04/11 14:10:16  euler
 *Updated license text.
 *
 *Revision 1.3  2006/04/06 16:31:16  euler
 *Prepended license remark.
 *
 *Revision 1.2  2006/03/30 16:07:15  scholz
 *fixed author tags for release
 *
 *Revision 1.1  2006/01/03 09:54:23  hakenjos
 *Initial version!
 * 
 */
