/*
 * 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 Community 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) 2001 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.venice.htmlcheck.dict;

import java.io.*;
import java.util.*;
import java.util.zip.*;
import org.apache.log4j.*;
import com.silverwrist.venice.htmlcheck.ModSpellingDictionary;

public class Lexicon implements ModSpellingDictionary, Serializable
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static Category logger = Category.getInstance(Lexicon.class.getName());

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

  private HashSet[] sets;        // 26 word sets, one for each letter of the alphabet

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

  public Lexicon()
  {
    sets = new HashSet[26];
    for (int i=0; i<26; i++)
      sets[i] = new HashSet(500,0.9F);

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Implementations from interface SpellingDictionary
   *--------------------------------------------------------------------------------
   */

  public synchronized int size()
  {
    int rc = 0;
    for (int i=0; i<26; i++)
      rc += sets[i].size();
    return rc;

  } // end size

  public synchronized boolean checkWord(String word)
  {
    String real_word = word.toLowerCase();
    int ndx = (int)(real_word.charAt(0) - 'a');
    return sets[ndx].contains(real_word);

  } // end checkWord

  /*--------------------------------------------------------------------------------
   * Implementations from interface ModSpellingDictionary
   *--------------------------------------------------------------------------------
   */

  public synchronized void addWord(String word)
  {
    String real_word = word.toLowerCase();
    int ndx = (int)(real_word.charAt(0) - 'a');
    sets[ndx].add(real_word);

  } // end addWord

  public synchronized void delWord(String word)
  {
    String real_word = word.toLowerCase();
    int ndx = (int)(real_word.charAt(0) - 'a');
    sets[ndx].remove(real_word);

  } // end delWord

  public synchronized void clear()
  {
    for (int i=0; i<26; i++)
      sets[i].clear();

  } // end clear

  /*--------------------------------------------------------------------------------
   * External operations
   *--------------------------------------------------------------------------------
   */

  public synchronized void saveToStream(OutputStream stm, boolean compress) throws IOException
  {
    ObjectOutputStream real_stm;
    if (compress)
    { // run through a GZIP object before writing
      GZIPOutputStream gz_stm = new GZIPOutputStream(stm);
      real_stm = new ObjectOutputStream(gz_stm);

    } // end if
    else  // just create the ObjectOutputStream directly
      real_stm = new ObjectOutputStream(stm);

    real_stm.writeObject(this);
    real_stm.flush();

  } // end saveToStream

  public synchronized void saveToFile(File file, boolean compress) throws IOException
  {
    FileOutputStream fstm = new FileOutputStream(file);
    try
    { // save the object data
      saveToStream(fstm,compress);
      fstm.flush();

    } // end try
    finally
    { // make sure we close the file before leaving
      fstm.close();

    } // end finally

  } // end saveToFile

  public synchronized void saveToFile(String filename, boolean compress) throws IOException
  {
    FileOutputStream fstm = new FileOutputStream(filename);
    try
    { // save the object data
      saveToStream(fstm,compress);
      fstm.flush();

    } // end try
    finally
    { // make sure we close the file before leaving
      fstm.close();

    } // end finally

  } // end saveToFile

  public synchronized void readFile(String filename) throws IOException
  {
    if (logger.isDebugEnabled())
      logger.debug("Lexicon.readFile loading " + filename);
    BufferedReader rdr = new BufferedReader(new FileReader(filename));

    int counter = 0;
    String word = rdr.readLine();
    while (word!=null)
    { // add each word to the lexicon as we go
      addWord(word);
      word = rdr.readLine();
      if (((++counter % 1000)==0) && logger.isDebugEnabled())
	logger.debug("loaded " + String.valueOf(counter) + " words");

    } // end while

    rdr.close();
    if (logger.isDebugEnabled())
      logger.debug("finished loading " + filename + ", " + String.valueOf(counter) + " words loaded");

  } // end readFile

  /*--------------------------------------------------------------------------------
   * External static operations
   *--------------------------------------------------------------------------------
   */

  public static Lexicon loadFromStream(InputStream stm, boolean compress) throws IOException
  {
    ObjectInputStream real_stm;
    if (compress)
    { // chain in a decompressor before we read the object
      GZIPInputStream gz_stm = new GZIPInputStream(stm);
      real_stm = new ObjectInputStream(gz_stm);

    } // end if
    else  // just load uncompressed
      real_stm = new ObjectInputStream(stm);

    try
    { // attempt to read the Lexicon object
      Object tmp = real_stm.readObject();
      if (tmp instanceof Lexicon)
	return (Lexicon)tmp;
      throw new IOException("invalid stream data format");

    } // end try
    catch (OptionalDataException od)
    { // translate exception here so we only throw one type
      throw new IOException("invalid stream data format");

    } // end catch
    catch (ClassNotFoundException cnf)
    { // translate exception here so we only throw one type
      throw new IOException("invalid stream data format");

    } // end catch

  } // end loadFromStream

  public static Lexicon loadFromFile(File file, boolean compress) throws IOException
  {
    FileInputStream fstm = new FileInputStream(file);
    try
    { // load the object data and return it
      return loadFromStream(fstm,compress);

    } // end try
    finally
    { // close the file before leaving
      fstm.close();

    } // end finally

  } // end loadFromFile

  public static Lexicon loadFromFile(String filename, boolean compress) throws IOException
  {
    FileInputStream fstm = new FileInputStream(filename);
    try
    { // load the object data and return it
      return loadFromStream(fstm,compress);

    } // end try
    finally
    { // close the file before leaving
      fstm.close();

    } // end finally

  } // end loadFromFile

} // end class Lexicon
