// DataBaseBureau.java
// $Id: DataBaseBureau.java,v 1.3 1997/07/28 12:49:11 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.jigsaw.pics ;

import java.io.* ;
import java.util.*;


/**
 * This class implements a label bureau.
 * The database for labels is provided by a real database. The 
 * organisation is as follows:
 * <p>The LabelBureau is attached to some directory <strong>D.db</strong>, the 
 * <strong>db</strong> extension is used to make difference between 
 * <a href="w3c.jigsaw.pics.SampleLabelBureau.html">SampleLabelBureau</a>
 * and DataBaseBureau. 
 * For each DataBaseBureau, you have to create a properties file, 
 * <strong>D.db/dbs.props</strong> and <strong>D.db/dbd.props</strong>
 * containing information about the services database and the bureaux
 * database .<p>
 * the properties in dbs.props must be:
 * <ul><li>w3c.jigsaw.pics.DataBase.dbname : the real name of the database.
 * <li>w3c.jigsaw.pics.DataBase.dburl : the jdbc database url.
 * <li>w3c.jigsaw.pics.DataBase.jdbcdriver : the jdbc driver used.
 * <li>w3c.jigsaw.pics.DataBase.username : the user name.
 * <li>w3c.jigsaw.pics.DataBase.passwd : the password relative to the 
 * user name.
 * <li>w3c.jigsaw.pics.DataBase.servicefield : the field name relative to 
 * the service.
 * <li>w3c.jigsaw.pics.DataBase.urlfield : the field name relative to the url.
 * <li>w3c.jigsaw.pics.DataBase.labelfield : the field name containing 
 * the label relative to the url.
 * <li>w3c.jigsaw.pics.DataBase.genericfield : the field name containing 
 * the generic flag
 * of the label.</ul><p>
 * the properties in dbs.props must be:
 * <ul><li>w3c.jigsaw.pics.DataBase.dbname : the real name of the database.
 * <li>w3c.jigsaw.pics.DataBase.dburl : the jdbc database url.
 * <li>w3c.jigsaw.pics.DataBase.jdbcdriver : the jdbc driver used.
 * <li>w3c.jigsaw.pics.DataBase.username : the user name.
 * <li>w3c.jigsaw.pics.DataBase.passwd : the password relative to the 
 * user name.
 * <li>w3c.jigsaw.pics.DataBase.servicefield : the field name relative to 
 * the service.
 * <li>w3c.jigsaw.pics.DataBase.bureaufield : the field name relative to the
 * bureau.</ul>
 * If a database doesn't exists, you have to create it and you must 
 * have some jdbc drivers to access it. 
 * @author Benoit Mahe <bmahe@sophia.inria.fr>
 */

public class DataBaseBureau implements LabelBureauInterface {

  private File directory = null;
  private Properties serviceProperties = null;
  private Properties bureauProperties = null;
  private Hashtable existence = null;

  /**
   * The property key relative to the name of the database.
   */

  public final static String 
  DB_NAME_P = "w3c.jigsaw.pics.DataBase.dbname";

  /**
   * The property key relative to the name of the service field.
   */

  public final static String 
  DB_SERVICE_FIELD_P = "w3c.jigsaw.pics.DataBase.servicefield";

  /**
   * The property key relative to the name of the bureau field.
   */

  public final static String 
  DB_BUREAU_FIELD_P = "w3c.jigsaw.pics.DataBase.bureaufield";


  private final static String BUREAU_PROPS_FILE = "dbb.props";
  private final static String SERVICE_PROPS_FILE = "dbs.props";


  private Properties getDataBaseBureauProperties()
    throws IOException
  {
    if (bureauProperties == null) {
      FileInputStream in = 
	new FileInputStream(getIdentifier()+"/"+BUREAU_PROPS_FILE);
      bureauProperties = new Properties();
      bureauProperties.load(in);
    }
    return bureauProperties;  
  }

  private Properties getDataBaseServiceProperties()
    throws IOException
  {
    if (serviceProperties == null) {
      FileInputStream in = 
	new FileInputStream(getIdentifier()+"/"+SERVICE_PROPS_FILE);
      serviceProperties = new Properties();
      serviceProperties.load(in);
    }
    return serviceProperties;
  }

  /**
   * Get this label bureau directory.
   */

  public String getIdentifier () {
    return directory.getAbsolutePath();
  }

  /**
   * Create a new service in this bureau.
   * @param service The service url
   * @return True if the service is create
   */

  public boolean createService (String service) {
    try {
      Properties dbprops = getDataBaseBureauProperties();
      DataBase db = new DataBase(dbprops);
      String table = 
	dbprops.getProperty(DB_NAME_P);
      String servicef = 
	dbprops.getProperty(DB_SERVICE_FIELD_P);
      String bureauf = 
	dbprops.getProperty(DB_BUREAU_FIELD_P);
      boolean created = 
	db.insert("INSERT INTO "+
		  table +"("+bureauf+","+servicef+") "+
		  "VALUES ('"+getIdentifier()+"','"+service+"')");
      if (created) {
	existence.put(service,Boolean.TRUE);
	return true;
      }
      return false;
    } catch (IOException e) {
      return false ;
    }
  }

  /**
   * Remove a service from this bureau
   * @param service The service url
   * @return True if removed
   */

  public boolean removeService (String service) {
    try {
      Properties dbprops = getDataBaseBureauProperties();
      DataBase db = new DataBase(dbprops);
      String table = 
	dbprops.getProperty(DB_NAME_P);
      String servicef = 
	dbprops.getProperty(DB_SERVICE_FIELD_P);
      String bureauf = 
	dbprops.getProperty(DB_BUREAU_FIELD_P);

      boolean del1 = 
	db.insert("DELETE FROM "+table+" WHERE "+servicef+"='"+
		  service+"' AND "+bureauf+"='"+getIdentifier()+"'");
      boolean del2 = false;

      if (del1) {
	dbprops = getDataBaseServiceProperties();
	db = new DataBase(dbprops);
	table = 
	  dbprops.getProperty(DB_NAME_P);
	servicef = 
	  dbprops.getProperty(DB_SERVICE_FIELD_P);

	del2 = db.insert("DELETE FROM "+table+" WHERE "+servicef+"='"+
			 service+"'");
	if (del2)
	  existence.put(service,Boolean.FALSE);
      }
      return (del1 && del2);
    } catch (IOException e) {
      return false;
    }
  }


  /**
   * Test the service existence
   * @param service The service url
   * @return True if the given service exists in this bureau
   */

  public boolean isAService (String service) {
    try {
      Properties dbprops = getDataBaseBureauProperties();
      DataBase db = new DataBase(dbprops);
      String table = 
	dbprops.getProperty(DB_NAME_P);
      String servicef = 
	dbprops.getProperty(DB_SERVICE_FIELD_P);
      String bureauf = 
	dbprops.getProperty(DB_BUREAU_FIELD_P);
      return (db.request ("SELECT "+servicef+" FROM "+table+
			  " WHERE "+bureauf+"='"+getIdentifier()+"' AND "+
			  servicef+"='"+service+"'"));
    } catch (IOException e) {
      return false;
    }
  }


  /**
   * Get the list of all services available in this bureau.
   * @return String[] The services list.
   */

  public String[] getLabelServicesNames () {
    try {
      Properties dbprops = getDataBaseBureauProperties();
      DataBase db = new DataBase(dbprops);
      String table = 
	dbprops.getProperty(DB_NAME_P);
      String servicef = 
	dbprops.getProperty(DB_SERVICE_FIELD_P);
      db.request("SELECT DISTINCT "+
		 servicef+
		 " FROM "+
		 table);
      Vector v = new Vector(50);
      do {
	v.addElement (db.getString(servicef));
      } while ( db.next() );
      db.closeConnection();
      if ( v.size() > 0 ) {
	String services[] = new String[v.size()] ;
	v.copyInto (services);
	return services;
      }
      else return null;
    } catch (IOException e) {
      return null ;
    }
  }

  
  /**
   * Lookup for the given service in this bureau.
   * @param name The service name.
   * @return A dataBaseService instance, or <strong>null</strong> if none
   *    was found.
   */

  public LabelServiceInterface getLabelService (String url) {
    LabelServiceInterface s = null;
    if (url == null) return null;
    try {
      Boolean exists = (Boolean) existence.get(url);

      if (exists == null) {
	if (!isAService(url)){
	  existence.put(url,Boolean.FALSE);
	  return null;
	}
	else existence.put(url,Boolean.TRUE);
      }
      else  if (!exists.booleanValue()) return null;

      s = new DataBaseService (getDataBaseServiceProperties(), url) ;

    } catch (UnknownServiceException e) {
      return null ;
    } catch (IOException e) {
      return null ;
    }
    return s ;
  }
  
  /**
   * Create a new DataBaseBureau.
   * The configuration file from the label bureau (the place were it takes
   * information about the services database), is given by the 
   * provided directory.
   * @param directory This bureau root directory.
   * @see w3c.jigsaw.pics.DataBaseService
   */

  public DataBaseBureau (File directory) {
    this.directory = directory;
    existence = new Hashtable();
  }

}
