package ModSQL;
import java.util.*;

/* $Id: MetaManager.java,v 1.10 2004/01/03 18:09:18 cvs Exp $
 *
 * Copyright (c) 2004 Chris Studholme <chris.studholme@utoronto.ca>
 *
 * May be copied or modified under the terms of the GNU General Public
 * License.  See COPYING for more information.
 */

/**
 * <p>DatabaseManager that can be used to combine multiple DatabaseManagers
 * into one metadatabase.
 *
 * <p>This implementation of DatabaseManager allows other classes 
 * implementing DatabaseManager to register themselves.  When this object
 * is asked for a particular DatabaseTable, the MetaManager searches its
 * list of DatabaseManager's for one that can provide the required table.
 *
 * @author chris.studholme@utoronto.ca
 */
public final class MetaManager implements DatabaseManager {

  /** Array of database managers. */
  private static Vector managers=null;

  /**
   * Constructor.
   */
  public MetaManager() {
    if (managers==null)
      managers = new Vector();
    
    // load default modules
    Properties props = DriverConfig.getDatabaseProperties("MetaManager");
    String[] classes = DriverConfig.makeArray(props.getProperty("databases"));
    if (classes!=null)
      for (int i=0; i<classes.length; ++i) {
	try {
	  Class manager = Class.forName(classes[i]);
	  registerManager((DatabaseManager)manager.newInstance());
	}
	catch (Exception e) {
	  java.lang.System.err.println("failed to load database module "+
				       classes[i]);
	}
      }
  }

  /**
   * Register a database manager with the meta-manager.
   *
   * @param manager DatabaseManager to register
   */
  public static void registerManager(DatabaseManager manager) {
    if (managers==null)
      managers = new Vector();
    if (manager!=null && managers.indexOf(manager)<0)
      managers.add(manager);
  }

  /**
   * Get current list of database managers.
   *
   * @return array of database managers
   */
  public static DatabaseManager[] getManagers() {
    if (managers==null || managers.size()==0)
      return new DatabaseManager[0];
    DatabaseManager[] result = new DatabaseManager[managers.size()];
    return (DatabaseManager[])managers.toArray(result);
  }

  /**
   * Major version number.
   *
   * @return major version number
   */
  public int getMajorVersion() {
    return DriverConfig.majorVersion;
  }
  
  /**
   * Minor version number.
   *
   * @return minor version number
   */
  public int getMinorVersion() {
    return DriverConfig.minorVersion;
  }

  /**
   * Check all managers for the specified table.
   *
   * @param name table to search for
   * @return true if some manager has the table
   * @throws DatabaseException if a database-access error occurs
   */
  public boolean hasTable(String name) throws DatabaseException {
    for (int i=0; i<managers.size(); ++i)
      if (((DatabaseManager)managers.elementAt(i)).hasTable(name))
	return true;
    return false;
  }

  /**
   * Open table (read-only) using the first manager that provides the table.
   *
   * @param name table to search for
   * @return open table or null if not found
   * @throws DatabaseException if a database-access error occurs
   */
  public DatabaseTable openTable(String name) throws DatabaseException {
    for (int i=0; i<managers.size(); ++i) {
      DatabaseTable table = 
	((DatabaseManager)managers.elementAt(i)).openTable(name);
      if (table!=null)
	return table;
    }      
    return null;
  }

  /**
   * Open table using the first manager that provides the table.
   *
   * @param name table to search for
   * @param readonly true for read-only, false for read-write
   * @return open table or null if not found
   * @throws DatabaseException if a database-access error occurs
   */
  public DatabaseTable openTable(String name, boolean readonly) 
    throws DatabaseException {
    for (int i=0; i<managers.size(); ++i) {
      DatabaseTable table = 
	((DatabaseManager)managers.elementAt(i)).openTable(name,readonly);
      if (table!=null)
	return table;
    }      
    return null;
  }

  /**
   * Create a new table using the first manager that will host it.  If an
   * existing table is found with the same name before a manager willing
   * to host the new table is found, an exception is thrown.
   *
   * @param name table to create
   * @param temporary true if the table is temporary
   * @return open table or null if no manager will host it
   * @throws DatabaseException if a database-access error occurs
   */
  public DatabaseTable createTable(String name, boolean temporary) 
    throws DatabaseException {
    for (int i=0; i<managers.size(); ++i) {
      DatabaseTable table = 
	((DatabaseManager)managers.elementAt(i)).createTable(name,temporary);
      if (table!=null)
	return table;
    }
    return null;
  }

  /**
   * Create an index of the specified column in the specified table
   * (if capable).
   *
   * @param tablename name of the table to index
   * @param columnname name of the column to index
   * @return true if index was created, false if database is not
   * capable of creating indicies
   * @throws DatabaseException if a database-access error occurs
   */
  public boolean createIndex(String name, String column) 
    throws DatabaseException {
    for (int i=0; i<managers.size(); ++i)
      if (((DatabaseManager)managers.elementAt(i)).hasTable(name)) 
	return ((DatabaseManager)managers.elementAt(i)).
	  createIndex(name,column);
    return false;
  }

  /**
   * Drop a table.
   *
   * @param tablename name of the table to drop
   * @throws DatabaseException if a database-access error occurs
   */
  public void dropTable(String name) 
    throws DatabaseException {
    for (int i=0; i<managers.size(); ++i) {
      DatabaseManager m = (DatabaseManager)managers.elementAt(i);
      if (m.hasTable(name)) {
	m.dropTable(name);
	return;
      }
    }
    throw new DatabaseException("table '"+name+"' not found");
  }

  /**
   * Drop an index.  This method should throw an exception if the table does
   * not exist, but should return false if the column has not been indexed.
   *
   * @param tablename name of the table with index
   * @param columnname name of the column with index
   * @return true if the index existed and has been dropped,
   * false if it didn't exist
   * @throws DatabaseException if a database-access error occurs
   */
  public boolean dropIndex(String name, String column) 
    throws DatabaseException {
    for (int i=0; i<managers.size(); ++i) {
      DatabaseManager m = (DatabaseManager)managers.elementAt(i);
      if (m.hasTable(name)) 
	return m.dropIndex(name,column);
    }
    throw new DatabaseException("table '"+name+"' not found");
  }

  /**
   * Get a database specific function for use in SQL.  This method returns
   * null if a function by the specified name is not found.
   *
   * @param name name of function to lookup (always lowercase)
   * @return new function object (or null if function not found)
   * @throws DatabaseException if a database-access error occurs
   */
  public Function getFunction(String name)
    throws DatabaseException {
    for (int i=0; i<managers.size(); ++i) {
      DatabaseManager m = (DatabaseManager)managers.elementAt(i);
      Function f = m.getFunction(name);
      if (f!=null) 
	return f;
    }
    return null;
  }

};

