/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2003 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.dict;

import java.util.*;
import org.apache.log4j.Logger;
import org.w3c.dom.*;
import com.silverwrist.util.xml.*;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.util.*;

public class DatabaseDictionary
    implements NamedObject, ComponentInitialize, ComponentShutdown, DynamoModifiableDictionary
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static Logger logger = Logger.getLogger(DatabaseDictionary.class);

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private String m_name;                       // name of this object
  private DatabaseDictOps m_ops;               // database operations object
  private int m_cache_size = -1;               // cached size
  private WeakHashMap m_cache_yes;             // words in dictionary
  private WeakHashMap m_cache_no;              // words not in dictionary

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  public DatabaseDictionary()
  {
    m_cache_yes = new WeakHashMap();
    m_cache_no = new WeakHashMap();

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Implementations from interface NamedObject
   *--------------------------------------------------------------------------------
   */

  public String getName()
  {
    return m_name;

  } // end getName

  /*--------------------------------------------------------------------------------
   * Implementations from interface ComponentInitialize
   *--------------------------------------------------------------------------------
   */

  /**
   * Initialize the component.
   *
   * @param config_root Pointer to the section of the Dynamo XML configuration file that configures this
   *                    particular component.  This is to be considered "read-only" by the component.
   * @param services An implementation of {@link com.silverwrist.dynamo.iface.ServiceProvider ServiceProvider}
   *                 which provides initialization services to the component.  This will include an implementation
   *                 of {@link com.silverwrist.dynamo.iface.ObjectProvider ObjectProvider} which may be used to
   *                 get information about other objects previously initialized by the application.
   * @exception com.silverwrist.dynamo.except.ConfigException If an error is encountered in the component
   *            configuration.
   */
  public void initialize(Element config_root, ServiceProvider services) throws ConfigException
  {
    XMLLoader loader = XMLLoader.get();
    String conn_name = null;
    try
    { // verify the right node name
      loader.verifyNodeName(config_root,"dictionary");

      // get the object's name
      m_name = loader.getAttribute(config_root,"name");

      // get the database configuration connection
      DOMElementHelper config_root_h = new DOMElementHelper(config_root);
      Element elt = loader.getSubElement(config_root_h,"database");
      conn_name = loader.getAttribute(elt,"connection");

    } // end try
    catch (XMLLoadException e)
    { // error loading XML config data
      throw new ConfigException(e);

    } // end catch

    // Get the database connection pool.
    DBConnectionPool pool = GetObjectUtils.getDatabaseConnection(services,conn_name);

    // Get the operations object.
    m_ops = DatabaseDictOps.get(pool);

  } // end initialize

  /*--------------------------------------------------------------------------------
   * Implementations from interface ComponentShutdown
   *--------------------------------------------------------------------------------
   */

  public void shutdown()
  {
    m_ops.dispose();
    m_ops = null;

  } // end shutdown

  /*--------------------------------------------------------------------------------
   * Implementations from interface DynamoDictionary
   *--------------------------------------------------------------------------------
   */

  public synchronized int size()
  {
    if (m_cache_size<0)
    { // need to get the dictionary size
      try
      { // get the size from the database
	m_cache_size = m_ops.getSize();

      } // end try
      catch (DatabaseException e)
      { // whoops!  error
	logger.warn("DatabaseDictionary.size(): database error",e);
	m_cache_size = -1;

      } // end catch

    } // end if

    return m_cache_size;

  } // end size

  public synchronized boolean check(String word)
  {
    String real_word = word.toLowerCase();
    if (m_cache_yes.containsKey(real_word))
      return true;
    else if (m_cache_no.containsKey(real_word))
      return false;

    boolean rc = false;
    try
    { // check the database, stash result in cache
      rc = m_ops.checkWord(real_word);
      if (rc)
	m_cache_yes.put(real_word,Boolean.TRUE);
      else
	m_cache_no.put(real_word,Boolean.FALSE);

    } // end try
    catch (DatabaseException e)
    { // database error
      logger.warn("DatabaseDictionary.check(): database error",e);
      rc = false;

    } // end catch

    return rc;

  } // end check

  /*--------------------------------------------------------------------------------
   * Implementations from interface DynamoModifiableDictionary
   *--------------------------------------------------------------------------------
   */

  public synchronized void add(String word) throws DictionaryException
  {
    String real_word = word.toLowerCase();
    try
    { // add to the dictionary
      boolean added = m_ops.addWord(real_word);
      if (added && (m_cache_size>=0))
	m_cache_size++;
      m_cache_yes.put(real_word,Boolean.TRUE);
      m_cache_no.remove(real_word);

    } // end try
    catch (DatabaseException de)
    { // error adding to the database
      throw new DictionaryException(DatabaseDictionary.class,"DatabaseDictionaryMessages","add.failed",de);

    } // end catch

  } // end add

  public void remove(String word) throws DictionaryException
  {
    String real_word = word.toLowerCase();
    try
    { // add to the dictionary
      boolean removed = m_ops.removeWord(real_word);
      if (removed && (m_cache_size>=0))
	m_cache_size--;
      m_cache_no.put(real_word,Boolean.FALSE);
      m_cache_yes.remove(real_word);

    } // end try
    catch (DatabaseException de)
    { // error adding to the database
      throw new DictionaryException(DatabaseDictionary.class,"DatabaseDictionaryMessages","remove.failed",de);

    } // end catch

  } // end remove

  public void clear() throws DictionaryException
  {
    try
    { // zap it
      m_ops.clearDictionary();
      m_cache_size = 0;
      m_cache_yes.clear();

    } // end try
    catch (DatabaseException de)
    { // error adding to the database
      throw new DictionaryException(DatabaseDictionary.class,"DatabaseDictionaryMessages","clear.failed",de);

    } // end catch

  } // end clear

} // end class DatabaseDictionary
