package net.global_village.eofextensions;

import java.io.*;
import java.sql.*;

import org.dbunit.*;
import org.dbunit.database.*;
import org.dbunit.dataset.*;
import org.dbunit.dataset.xml.*;
import org.dbunit.operation.*;

import com.webobjects.eoaccess.*;
import com.webobjects.eocontrol.*;
import com.webobjects.foundation.*;
import com.webobjects.jdbcadaptor.*;

import net.global_village.foundation.*;



/**
 * Collection of utility methods to make working with EOF and DBUnit easier.
 *
 * @author Copyright (c) 2008  Global Village Consulting, Inc.  All rights reserved.
 * This software is published under the terms of the Educational Community License (ECL) version 1.0,
 * a copy of which has been included with this distribution in the LICENSE.TXT file.
 * @version $Revsion:$
 */
public class DBUnitUtilities
{


    /**
     * Uses DBUnit to perform the selected operation on the indicated file of data inside of a transaction.
     *
     * @param operation the operation (e.g. insert, delete) to perform on the data
     * @param resourcePath the path below Resources/ to to take the data from
     * @throws SQLException
     * @throws DatabaseUnitException
     */
    public static void performOperationOnDataSet(IDatabaseConnection connection,
                                          DatabaseOperation operation,
                                          IDataSet dataset) throws DatabaseUnitException, SQLException
    {
            new TransactionOperation(operation).execute(connection, dataset);
    }



    /**
     * Creates and configures a new JDBC database connection.
     *
     * @param model provides JDBC URL and credentials and used with ec to locate the EOAdaptor
     * @param ec used with model to locate the EOAdaptor
     * @param shouldQualifyTableNames true if table names should be qualified with the schema/owner
     * @param escapePattern optional pattern to escape names in SQL, "\"?\"" is the standard double quote
     *
     * @return the created and configured database connection
     *
     * @throws ClassNotFoundException if the JDBC driver class can't be loaded
     * @throws SQLException if a connection can't be made
     * @throws DatabaseUnitException if DBUnit can't connect
     */
    public static IDatabaseConnection createConnection(EOModel model,
                                                    EOEditingContext ec,
                                                    boolean shouldQualifyTableNames,
                                                    String escapePattern) throws ClassNotFoundException, SQLException, DatabaseUnitException
    {
        /** require [valid_model] model != null;
                    [valid_ec] ec != null;     **/
        NSDictionary connectionDictionary = model.connectionDictionary();

        // force loading of JDBC driver
        String driverName = (String) connectionDictionary.objectForKey(JDBCAdaptor.DriverKey);
        if (StringAdditions.isEmpty(driverName))
        {
            // Find the default driver name
            EODatabaseContext dbContext = EODatabaseContext.registeredDatabaseContextForModel(model, ec);
            dbContext.lock();
            try
            {
                JDBCAdaptor jdbcAdaptor = (JDBCAdaptor)dbContext.adaptorContext().adaptor();
                driverName = jdbcAdaptor.plugIn().defaultDriverName();
            }
            finally
            {
                dbContext.unlock();
            }
        }
        Class.forName(driverName);


        Connection jdbcConnection = DriverManager.getConnection( (String) connectionDictionary.objectForKey(JDBCAdaptor.URLKey),
                                                                 (String) connectionDictionary.objectForKey(JDBCAdaptor.UsernameKey),
                                                                 (String) connectionDictionary.objectForKey(JDBCAdaptor.PasswordKey));
        IDatabaseConnection dbConnection = new DatabaseConnection(jdbcConnection);
        DatabaseConfig dbConfig = dbConnection.getConfig();

        if (escapePattern != null)
        {
            dbConfig.setProperty(DatabaseConfig.PROPERTY_ESCAPE_PATTERN, escapePattern);
        }
        dbConfig.setFeature(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, shouldQualifyTableNames);

        return dbConnection;
        /** ensure [valid_result] Result != null;  **/
    }



    /**
     * Locates one of the JDBC connections that EOF is using and configures the database connection.
     *
     * @param model used with ec to locate the EOAdaptor
     * @param ec used with model to locate the EOAdaptor
     * @param shouldQualifyTableNames true if table names should be qualified with the schema/owner
     * @param escapePattern optional pattern to escape names in SQL, "\"?\"" is the standard double quote
     *
     * @return the configured database connection
     *
     * @throws DatabaseUnitException if DBUnit can't connect
     */
    public static IDatabaseConnection getConnection(EOModel model,
                                                    EOEditingContext ec,
                                                    boolean shouldQualifyTableNames,
                                                    String escapePattern) throws DatabaseUnitException
    {
        /** require [valid_model] model != null;
                    [valid_ec] ec != null;     **/
        IDatabaseConnection dbConnection = null;
        EODatabaseContext dbContext = EODatabaseContext.registeredDatabaseContextForModel(model, ec);
        dbContext.lock();
        try
        {
            JDBCAdaptor jdbcAdaptor = (JDBCAdaptor)dbContext.adaptorContext().adaptor();
            // Ensure there is an active context
            if (jdbcAdaptor.contexts().count() == 0)
            {
                jdbcAdaptor.createAdaptorContext();
            }
            JDBCContext jdbcContext = (JDBCContext)jdbcAdaptor.contexts().lastObject();
            dbConnection = new DatabaseConnection(jdbcContext.connection());

            DatabaseConfig dbConfig = dbConnection.getConfig();
            if (escapePattern != null)
            {
                dbConfig.setProperty(DatabaseConfig.PROPERTY_ESCAPE_PATTERN, escapePattern);
            }
            dbConfig.setFeature(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, shouldQualifyTableNames);
        }
        finally
        {
            dbContext.unlock();
        }



        return dbConnection;
        /** ensure [valid_result] Result != null;  **/
    }



    /**
     * Grabs the current JDBC connection from the passed JDBCContext and uses that for DBUnit.
     *
     * @param context JDBCContext providing a JDBC Connection object to use
     * @param shouldQualifyTableNames true if table names should be qualified with the schema/owner
     * @param escapePattern optional pattern to escape names in SQL, "\"?\"" is the standard double quote
     *
     * @return the configured database connection
     *
     * @throws DatabaseUnitException if DBUnit can't connect
     */
    public static IDatabaseConnection getConnection(JDBCContext context,
                                                    boolean shouldQualifyTableNames,
                                                    String escapePattern) throws DatabaseUnitException
    {
        /** require [valid_context] context != null;     **/
        IDatabaseConnection dbConnection = new DatabaseConnection(context.connection());
        DatabaseConfig dbConfig = dbConnection.getConfig();

        if (escapePattern != null)
        {
            dbConfig.setProperty(DatabaseConfig.PROPERTY_ESCAPE_PATTERN, escapePattern);
        }
        dbConfig.setFeature(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, shouldQualifyTableNames);

        return dbConnection;
        /** ensure [valid_result] Result != null;  **/
    }



    /**
     * Returns a data set loaded from the named resource in the bundle.
     *
     * @param resourcePath name of the data file to construct a DataSet for
     * @return data set loaded from the named resource in the bundle
     * @throws DataSetException
     * @throws Exception if the data can't be loaded or the DataSet created
     */
    public static IDataSet getDataSet(NSBundle bundle, String resourcePath) throws DataSetException
    {
        /** require [valid_bundle] bundle != null;
                    [valid_resourcePath] resourcePath != null;     **/
        InputStream dataSet = bundle.inputStreamForResourcePath(resourcePath);

        if (dataSet == null)
        {
            throw new RuntimeException("Can't locate resource " + resourcePath + " in bundle " + bundle.name());
        }
        return new XmlDataSet(dataSet);
        /** ensure [valid_result] Result != null;  **/
    }



}
