/** 
 * Project    : KANT (LMT)
 * Source Name: Term.java
 * Copyright  : Copyright (c) LTI, Carnegie Mellon University
 * Description: Class for Term
 * @version     1.0 (created by jko - 10/01/01)
 **/

package lmt;

import java.util.*;
import java.sql.*;

public class Term {
	
  String	term_id;
  String	term_string;
  String	pos;
  String	type_phrase;
  String	phrase_head;
  String	active;

  Vector  conceptVector;
  SQLConnection con;
	
  /** constructor 
   **/
  public Term (SQLConnection con) {
    this.con = con;
    reset();
  }	

  /** reset all variables
   **/	
  public void reset() {
    term_id = "";
    term_string = "";
    pos = "";
    type_phrase = "";
    phrase_head = "";
    active = "T";
    conceptVector = new Vector();
  }	
	
  // setters and getters
  public String getTermID() {
    return term_id;
  }	
  public void setTermID(String term_id) {
    if ( term_id == null )
      term_id = "";
    this.term_id = term_id;
  }	
  public String getTermString() {
    return term_string;
  }	
  public void setTermString(String term_string) {
    if ( term_string == null )
      term_string = "";
    this.term_string = term_string;
  }
  public void setPos (String pos) {
    if ( pos == null )
      pos = "";
    this.pos = pos;
  }
  public String getPos () {
    return pos;
  }
  public void setTypePhrase (String type_phrase) {
    if ( type_phrase == null )
      type_phrase = "";		
    this.type_phrase = type_phrase;
  }
  public String getTypePhrase () {
    return type_phrase;
  }
  public void setPhraseHead (String phrase_head) {
    if ( phrase_head == null )
      phrase_head = "";		
    this.phrase_head = phrase_head;
  }
  public String getPhraseHead () {
    return phrase_head;
  }
  public void setActive (String active) {
    if ( active == null )
      active = "";		
    this.active = active;
  }
  public String getActive() {
    return active;
  }
  public Vector getConcepts() {
    if ( conceptVector.size() == 0 )
      conceptVector = Concept.fetchConcepts(this);	
    return conceptVector;
  }
  public void setConcepts(Vector conceptVector) {
    this.conceptVector = conceptVector;
  }
  public SQLConnection getConn() {
    return con;
  }

  /**
   * Check the term is empty or not
   * @return boolean if empty, return true. Otherwise return false.
   */
  public boolean isEmpty() {
    return ( term_id.length() == 0 );	
  }

  public static Vector fetchAllTerms(SQLConnection con) {
    Vector termVectors = new Vector();
		
    if (!con.connect()) 
      return null;
		
    String sql = "SELECT * FROM LMT_TERMS ORDER BY TERM_STRING";
    Debug.debug("-- fetchAllTerms: " + sql);		

    try {			
      // get the result set 
      SQLConnection.Result r = con.query(sql);
      while (r != null && r.hasData()){
	Term term = new Term(con);
	term.setTermID(r.rs.getString(1));	
	term.setTermString(r.rs.getString(2));	
	term.setPos(changePosForUI(r.rs.getString(3)));	
	term.setTypePhrase(r.rs.getString(4));	
	term.setPhraseHead(r.rs.getString(5));	
	term.setActive(r.rs.getString(6));	
	// fetch its concepts
	//conceptVector = Concept.fetchConcepts(this);
				
	termVectors.add(term);
      }
      if (r!=null) r.close();
    } catch (SQLException E) {
      System.out.println("SQLException: " + E.getMessage());			
      return null;
    }	

    return termVectors;
  }

  /** Get the term from the database and lock the record 
   *  To lock it, we should use one connection.
   **/
  public synchronized int fetchTerm() {//SQLConnection con) {
    Vector termVector = new Vector();
		
    if (!con.connect()) 
      return Constants.DATABASE_ERROR;
		
    String sql = "SELECT * FROM LMT_TERMS WHERE ";
    sql += "term_id = '" + term_id + "'";
    Debug.debug("-- fetchTerm: " + sql);		

    try {			
      // get the result set 
      SQLConnection.Result r = con.query(sql);
      if (r != null && r.hasData()){
	setTermString(r.rs.getString(2));	
	setPos(changePosForUI(r.rs.getString(3)));	
	setTypePhrase(r.rs.getString(4));	
	setPhraseHead(r.rs.getString(5));	
	setActive(r.rs.getString(6));	
      }
      if (r!=null) r.close();
    } catch (SQLException E) {
      System.out.println("SQLException: " + E.getMessage());			
      return Constants.FAIL;
    }	

    // fetch its concepts
    conceptVector = Concept.fetchConcepts(this);
		
    return Constants.SUCCESS;
  }
		
  /** Get all terms which have the pos and term_string
   * term_string can have wildcards.
   * @return Vector all terms fetched from the database.
   **/	
  public Vector searchTerms() {
    String sql = "SELECT POS, TERM_STRING, TERM_ID FROM LMT_TERMS WHERE ";
    Vector termVector = new Vector();
	
    if (term_string.indexOf("%%") >= 0 ) {		//  LIKE '%\%%' escape '\'
      sql += "term_string LIKE \'%\\%%\' escape \'\\\'";
    } else if (term_string.indexOf("%") >= 0 || term_string.indexOf("_") >= 0 ) {
      sql += "term_string LIKE '" + Util.replaceSingleQuote(term_string) + "'";
    } else {
      sql += "term_string = '" + Util.replaceSingleQuote(term_string) + "'";
    }
		
    if (!pos.equals("ANY"))
      sql += " AND POS = '" + changePosForDB(pos) + "'";
		
    sql += " order by term_string";
		
    try {
      SQLConnection.Result r = con.query(sql);
      while (r!= null && r.hasData()){
	Vector row = new Vector();
	row.addElement(changePosForUI(r.rs.getString(1)) );				 
	row.addElement(r.rs.getString(2));				 	
	row.addElement(r.rs.getString(3));				 					
	termVector.addElement(row);
      }
      if (r!=null) r.close();
    } catch (SQLException E) {
      System.out.println("SQLException: " + E.getMessage());
    }		

    Debug.debug("-- searchTerms: " + sql);		
    //Debug.debug("   size of result: " + termVector.size());
	
    return termVector;
  }

  /** delete all terms which have the pos and term_string
   * @param termVector vector of vectors which have pos, term_string, term_id 
   * @return boolean result of deletion
   **/	
  public synchronized boolean deleteTerms(Vector termVector) {
		
    Debug.debug("-- deleteTerms for batch mode: delete " + termVector.size() + " terms");		
		
    // make delete query
    for ( int i=0; i<termVector.size(); i++){
      Vector row = (Vector)termVector.elementAt(i);
      String id = (String)row.elementAt(2);
      Debug.debug("-- delete term: term_id = " + id);
			
      boolean success = con.callProcedure("DeleteTerm", id); 
      if ( !success ) {
	Debug.debug("-- Fail to delete a term (term_id=" + id +")");
	return false;
      }
    }
    return true;
  }
	
  /** update term and its concepts for batch change
   * @param termVector It consists of vectors of term objects
   * @return int result of change
   **/	
  public synchronized int batchChange(Vector termVector) {
		
    Debug.debug("-- batchChange for batch mode: update " + termVector.size() + " terms");		

    // check duplication to see the replaced term string already exists in the DB
    for ( int i=0; i<termVector.size(); i++){
      Term term = (Term)termVector.elementAt(i);
      if (term.isDuplicate())
	return Constants.DUPLICATION;
    }
				
    // start transaction
    con.setAutoCommit(false);	
    con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
		
    boolean success = true;
    for ( int i=0; i<termVector.size(); i++){
      Term term = (Term)termVector.elementAt(i);
      // step 1. update term			
      if ( term.update() != Constants.SUCCESS ) {
	success = false; 
	break;
      }
			
      // step 2. update concept symbol. It's needed because term string is changed.
      Vector conVector = term.getConcepts();
      for ( int j=0; j<conVector.size(); j++){
	Concept concept = (Concept)conVector.elementAt(j);
	if ( concept.update() != Constants.SUCCESS ) {
	  success = false; 
	  break;
	}
      }
			
      if ( !success ) break;
    }

    // close transaction
    if ( success ) con.commit();
    else con.rollback();
		
    con.setAutoCommit(true);
		
    return ( success ? Constants.SUCCESS : Constants.FAIL );
  }
	
  /** change POS to match the screen with the database
   * @param string representing part-of-speech
   **/
  public static String changePosForDB(String pos) {
    String newPos = "N";
    if ( pos != null ) {
      if (pos.equals("NOUN")) 
	newPos = "N";
      else if (pos.equals("VERB")) 
	newPos = "V";
      else if (pos.equals("ADVERB")) 
	newPos = "ADV";	
      else
	newPos = pos;			
    }
    return newPos;
  }

  /** change POS to match the screen with the database
   * @param string representing part-of-speech
   **/
  public static String changePosForUI(String pos) {
    String newPos = "NOUN";
    if ( pos != null ) {
      if (pos.equals("N")) 
	newPos = "NOUN";
      else if (pos.equals("V")) 
	newPos = "VERB";
      else if (pos.equals("ADV")) 
	newPos = "ADVERB";	
      else
	newPos = pos;
    }			
    return newPos;
  }
	
  /** insert the term and concept into database
   * @return integer representing the result of operation
   **/		
  public int insert(Concept concept) {

    // check duplication
    if (isDuplicate())
      return Constants.DUPLICATION;
				
    String[] sql = new String[2];
		
    // query for term
    term_id = con.getNewID("TERM_ID");		// get New Term ID					
    sql[0] = makeInsertQuery();
		
    // query for concept
    concept.setTermID(term_id);
    concept.setConceptID(con.getNewID("CONCEPT_ID"));	// get New Concept ID							
    sql[1] = concept.makeInsertQuery();
				
    Debug.debug("-- Term inserted : " + sql[0]);		
    Debug.debug("-- Concept inserted : " + sql[1]);		
		
    boolean success = con.transaction(sql);
    return (success ? Constants.SUCCESS : Constants.FAIL); 
  }
	
  /** delete this data from the table.
   * @return boolean representing the result of operation
   **/
  public int delete(Session session) {
    Debug.debug("-- Term deleted: term_id - " + term_id);		

    if (!isExistent()) 
      return Constants.DONOTEXIST;
		
    if (!con.connect()) 
      return Constants.DATABASE_ERROR;
		
    // start transaction
    con.setAutoCommit(false);	
    con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
		
    // step 1. release lock for this term 
    boolean success = false;
    success = session.releaseLock();
    if ( success ) {
      // step 2. delete term
      success = con.callProcedure("DeleteTerm", term_id); 
    } 
    // close transaction
    if ( success ) con.commit();
    else con.rollback();
		
    con.setAutoCommit(true);
    return (success? Constants.SUCCESS: Constants.FAIL);
  }
		
  /** update this data in the table.
   * @return integer representing the result of operation
   **/
  public int update() {
    // check where this exists
    if (!isExistent())
      return Constants.DONOTEXIST;
		
    String sql = makeUpdateQuery();
    //Debug.debug("-- Term updated : " + sql);		
		
    return con.update(sql);
  }
	
  /** check whether the information exists or not in the database using term_string and pos
   * @return boolean representing the result of operation
   **/	
  public boolean isDuplicate() {
		
    String sql = "SELECT * FROM LMT_TERMS WHERE ";
    sql += " term_string = '" + Util.replaceSingleQuote(term_string) + "'";
    sql += " AND POS = '" + changePosForDB(pos) + "'";

    Debug.debug("-- isDuplicated : " + sql);	
    return con.isExistent(sql);
  }
		
  /** check whether the information exists or not in the database using term_string and pos
   * @return boolean representing the result of operation
   **/	
  public boolean isDuplicate2() {
		
    String sql = "SELECT * FROM LMT_TERMS WHERE ";
    sql += " term_string = '" + Util.replaceSingleQuote(term_string) + "'";
    sql += " AND POS = '" + changePosForDB(pos) + "'";
    sql += " AND NOT(TERM_ID = '" + term_id + "')";

    Debug.debug("-- isDuplicated : " + sql);	
    return con.isExistent(sql);
  }
	
  /** check whether the information exists or not in the database using term_id
   * @return boolean representing the result of operation
   **/	
  public boolean isExistent() {
		
    String sql = "SELECT * FROM LMT_TERMS WHERE ";
    sql += " term_id = '" + term_id + "'";
		
    //Debug.debug("-- isExistent : " + sql);	
    return con.isExistent(sql);
  }
	
  /** make the query using input vector
   * @param comVector	vector which has objects of this class
   * @return String[] array including SQL
   **/	
  public void makeUpdateQuery(Vector queryVector) {
    queryVector.addElement(makeUpdateQuery());
  }	
	
  String makeUpdateQuery() {
    String query;
    query = "UPDATE LMT_TERMS SET";
    query += " term_string = '" + Util.replaceSingleQuote(term_string) + "',";
    query += " pos = '" + changePosForDB(pos) + "',";
    query += " type_phrase = '" + type_phrase + "',";
    query += " phrase_head = '" + phrase_head + "'";
    query += " WHERE term_id ='" + term_id + "'";		
    return query;
  }
	
  String makeInsertQuery () {
    String sql = "INSERT INTO LMT_TERMS VALUES (";
    sql += "'" + term_id + "',";
    sql += "'" + Util.replaceSingleQuote(term_string) + "',";
    sql += "'" + changePosForDB(pos) + "',";
    sql += "'" + type_phrase + "',";
    sql += "'" + phrase_head + "',";
    sql += "'" + active + "')";
    return sql;		
  }
}
