// DataBaseService.java
// $Id: DataBaseService.java,v 1.2 1997/07/28 12:49:23 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.util.*;
import java.io.* ;
import java.net.URL ;
import java.net.MalformedURLException ;
import java.util.Vector ;

/**
 * The internal representation of a LabelService. 
 * A LabelService is an object which should be able to deliver labels for any
 * URL. This implementation use a fancy database, via JDBC.
 * <p>For each requested labels, the DataBaseService is looked up for the 
 * appropriate URL in the database. So if you want to consult a 
 * DataBaseService, you have to call <strong>DataBaseService 
 * (Properties P, String name)</strong> where P is a Properties Object 
 * containing information about the database and name the url relative to the
 * service. The properties file <strong>
 * (dbs.props)</strong> must be in the LabelBureau directory. 
 * @author Benoit Mahe <bmahe@sophia.inria.fr>
 */

public class DataBaseService extends DataBase 
                             implements LabelServiceInterface 
{

  private String name = null;
  private Properties P = null;

  // Field Names

  private String servicef = null;
  private String urlf = null;
  private String labelf = null;
  private String genf = null;

  /**
   * 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 url field.
   */
  public final static String 
  DB_URL_FIELD_P = "w3c.jigsaw.pics.DataBase.urlfield";

  /**
   * The property key relative to the name of the label field.
   */
  public final static String 
  DB_LABEL_FIELD_P = "w3c.jigsaw.pics.DataBase.labelfield";

  /**
   * The property key relative to the name of the field containing the generic
   * flag.
   */
  public final static String 
  DB_GENERIC_FIELD_P = "w3c.jigsaw.pics.DataBase.genericfield";


  private boolean exists(){
    return request("SELECT "+servicef+" FROM "+getRealName()+
		   " WHERE "+servicef+" = '"+getName()+"'");
  }

  private void initFieldNames(Properties P){
    servicef = P.getProperty(DB_SERVICE_FIELD_P);
    urlf  = P.getProperty(DB_URL_FIELD_P);
    labelf = P.getProperty(DB_LABEL_FIELD_P);
    genf = P.getProperty(DB_GENERIC_FIELD_P);
  }

  /**
   * Get this service name
   * @return A String instance, being the service name.
   */

  public String getName() {
    return name;
  }

  /**
   * Stringify an URL.
   * This method takes an URL as input, and returns a String Object relative
   * to the DataBaseService name in the real database. 
   * @param u The URL to Stringify
   * @param generic Stringify for generic labels if <strong>true</strong>.
   */
  
  private String stringify (URL u, boolean generic) {
    String string = null;
    string = new String(u.getProtocol()+"://");
    string = string.concat(u.getHost());
    if (generic){
      string = string.concat("%");
    } else {
      if ( (u.getPort() != 80) && (u.getPort() != -1) ) 
	string = string.concat( new Integer(u.getPort()).toString());
      if ( (u.getFile() != null) && ( ! u.getFile().equals("/")) )
	string = string.concat(u.getFile());
    }
    return string;
  }

  /**
   * Get the list of the labels in HTML format
   * @return A String instance, being the HTML list.
   */

  public String list(){
    if (request("SELECT "+urlf+","+labelf+" FROM "+getRealName()+
		" WHERE "+servicef+"='"+getName()+"'")){
      StringBuffer buffer = new StringBuffer("<HTML><HEAD>");
      buffer.append("<META http-equiv=\"PICS-Label\" content='(PICS-1.0 ");
      buffer.append("\"http://www43.inria.fr:8005/Rating\" ");
      buffer.append("l gen true by \"bmahe@sophia.inria.fr\" ");
      buffer.append("for \"http://www43.inria.fr:8005\" ");
      buffer.append("on \"1997.04.18T12:04-0500\" ");
      buffer.append("r (movies 1))'>");
      buffer.append("</HEAD><BODY>");
      buffer.append("<CENTER><H1>Labels :</H1></CENTER><H3><BR><HR>");
      do {
	buffer.append("URL : <A HREF=\""+getUrl()+"\">"+getUrl()+
		      "</A><BR> LABEL : "+getLabel());
	buffer.append("<FORM ACTION");
	buffer.append("=\"http://www43.inria.fr:8005/LabelBureauDB\">");
	buffer.append("<INPUT TYPE=HIDDEN NAME=opt VALUE=delete>");
	buffer.append("<INPUT TYPE=HIDDEN NAME=s");
	buffer.append(" VALUE=http://www43.inria.fr:8005/Rating>");
	buffer.append("<INPUT TYPE=HIDDEN NAME=u VALUE="+getUrl()+">");
	buffer.append("<INPUT TYPE=SUBMIT VALUE=delete></FORM>");
	buffer.append(" <HR>");
      } while (next());
      buffer.append("</H3></BODY></HTML>");
      return new String(buffer);
    }
    return "";
  }

  private boolean insert(String url, String label, int gen){
    return super.insert("INSERT INTO "+getRealName()
		   +" ("+servicef+","+urlf+","+labelf+","+genf+") VALUES ('"+
		   getName()+"','"+url+"','"+label+"',"+gen+")");
  } 

  private boolean update(String url, String label, int gen){
    return super.insert("UPDATE "+getRealName()
			+" SET "+labelf+"='"+label
			+"' WHERE "+urlf+"='"+url+"'"
			+" AND "+servicef+"='"+getName()+"'");
  }


  /**
   * Delete a label in the DataBaseService.
   * @param url The url labelled.
   * @return true if the label was correctly deleted.
   */

  public boolean delLabel(String url){
    return super.insert("DELETE FROM "+getRealName()+" WHERE "+
			servicef+"='"+getName()+"' AND "+urlf+"='"+url+"'");
  }

  /**
   * Add a new label in the DataBaseService.
   * @param url The url to label.
   * @param label The label relative to the given url.
   * @param gen Add generic label if <strong>true</strong>.
   * @return true if the label was correctly added.
   */

  public boolean addLabel(String url, String label, boolean gen, boolean ov)
    throws java.net.MalformedURLException
  {
      url = stringify( new URL(url),false);
      return ((ov)
	      ? ((gen)
		 ? update(url,label,1)
		 : update(url,label,0))
	      : ((gen)
		 ? insert(url,label,1)
		 : insert(url,label,0)));
  }

  /**
   * Get the label telative to the given url in the DataBaseService.
   * @param url The url.
   * @return String The label relative to the url or ""
   */

  public String getLabelFromUrl(String url){
    try {
      url = stringify( new URL(url),false);
      if (specificRequest(url)) return getLabel();
      return "";
    } catch (java.net.MalformedURLException e) {
      System.out.println(e.getMessage());
      e.printStackTrace();
      return "";
    }
  }

  private boolean specificRequest(String u){
    return request("SELECT "+labelf+" FROM "+getRealName()+" WHERE "+
		   servicef+" = '"+getName()+"' AND "+urlf+" = '"+u+"'");
  }

  private boolean genericRequest(String u){
    return request("SELECT "+urlf+","+labelf+","+genf+" FROM "+getRealName()+
		   " WHERE "+servicef+" = '"+getName()+"' AND "+urlf+
		   " LIKE '"+u+"' ORDER by "+urlf+" DESC");
  }

  private boolean treeRequest(String u){
    return request("SELECT "+urlf+","+labelf+","+genf+" FROM "+getRealName()+
		   " WHERE "+servicef+" = '"+getName()+"' AND "+urlf+
		   " LIKE '"+u+"%' ORDER by "+urlf+"");
  }

  private String getLabel(){
    return getString(labelf);
  }

  private String getUrl(){
    return getString(urlf);
  }

  private boolean isGeneric(){
    return (getInt(genf) == 1) ;
  }

  /**
   * Get the specific label for the given URL.
   * @param url The URL whose label is searched.
   */
    
  public LabelInterface getSpecificLabel (URL u) {
    if (specificRequest(stringify(u,false))) {
      try {
	String label = getLabel();
	return ((label == null)
		? null
		: new SampleLabel (label));
      } catch (InvalidLabelException e) {
	System.out.println(e.getMessage());
	e.printStackTrace();
	return null;
      }
    }
    return null;
  } 


  /**
   * Get the most specific generic label for an URL.
   * @param url The URL whose generic label is to be retreived.
   * @return An object conforming to the LabelInterface, or
   *    <strong>null</strong> if none was found.
   */
  
  public LabelInterface getGenericLabel (URL u) {
    String url = stringify(u,false);
    if (genericRequest(stringify(u,true))){
      String label = null;
      do {
	if ((url.startsWith(getUrl())) && isGeneric()) {
	    label = getLabel();
	    break;
	  }
	} while (next());

      SampleLabel l = null;
      try{
	if (label != null) l = new SampleLabel (label);
	else return null;
      } catch (InvalidLabelException e) {
	System.out.println(e.getMessage());
	e.printStackTrace();
	return null;
      }
      return l;
    }
    return null;
  }


  /**
   * Get the tree labels for the given URL.
   * @param url The URL whose tree labels are to be retreived.
   * @return An array of SampleLabel, each comforming to the LabelInterface.
   */
    
  public LabelInterface[] getTreeLabels (URL u) {
    // return the full tree labels.
    // first, the label for the queried url
    // must test if url is a directory
    
    LabelInterface myself = getGenericLabel(u) ;
    Vector v = new Vector(50); // Don't know the number of elements, so 50 !
    if (myself != null) v.addElement(myself);
    String baseurl = stringify(u,false);
    if (treeRequest(baseurl)){
      do {
	SampleLabel l = null ;
	try {
	  l = new SampleLabel (getLabel());
	} catch (InvalidLabelException e) {
	  System.out.println(e.getMessage());
	  e.printStackTrace();
	  l = null ;
	} 
	if ( l != null )
	  v.addElement (l) ;
      } while (next());
    }
    if ( v.size() > 0 ) {
      SampleLabel ls[] = new SampleLabel[v.size()] ;
      v.copyInto (ls) ;
      return ls ;
    }
    else return null ;
  }

  
  /**
   * Get the generic tree labels for given URL.
   * @param url The URL whose tree labels are to be retreived.
   * @return An array of SampleLabel, each of which conforms to the 
   *    LabelInterface.
   */

  public LabelInterface[] getGenericTreeLabels (URL u) {
    // return the full tree labels.
    // first, the label for the queried url
    // must test if url is a directory
    
    LabelInterface myself = getGenericLabel(u) ;
    Vector v = new Vector(50); // Don't the number of elements, so 50 !
    if (myself != null) v.addElement(myself);
    String baseurl = stringify(u,false);
    if (treeRequest(baseurl)){
      do {
	if (isGeneric()){
	  SampleLabel l = null ;
	  try {
	    l = new SampleLabel (getLabel());
	  } catch (InvalidLabelException e) {
	    l = null ;
	  } 
	  if ( l != null )
	    v.addElement (l) ;
	}
      } while (next());
    }
    if ( v.size() > 0 ) {
      SampleLabel ls[] = new SampleLabel[v.size()] ;
      v.copyInto (ls) ;
      return ls ;
    }
    else {
      return null ;
    }
  }

  /**
   * Dump this service description into the given buffer.
   * @param into The StringBuffer to dump the service to.
   */
  
  public void dump (StringBuffer into, int format) {
    into.append (" \""+getName()+"\"") ;
  }
  
  public DataBaseService (Properties P, String name)
    throws UnknownServiceException
  { 
    super(P);
    this.name = name;
    initFieldNames(P);
  }

}
