1 /*
\rJazzy - a Java library for Spell Checking
\rCopyright (C) 2001 Mindaugas Idzelis
\rFull text of license can be found in LICENSE.txt
\r\rThis library is free software; you can redistribute it and/or
\rmodify it under the terms of the GNU Lesser General Public
\rLicense as published by the Free Software Foundation; either
\rversion 2.1 of the License, or (at your option) any later version.
\r\rThis library is distributed in the hope that it will be useful,
\rbut WITHOUT ANY WARRANTY; without even the implied warranty of
\rMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\rLesser General Public License for more details.
\r\rYou should have received a copy of the GNU Lesser General Public
\rLicense along with this library; if not, write to the Free Software
\rFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
\r*/
\rpackage com.swabunga.spell.engine;
\r\rimport java.io.File;
\rimport java.io.FileInputStream;
\rimport java.io.FileNotFoundException;
\rimport java.io.FileOutputStream;
\rimport java.io.IOException;
\rimport java.io.ObjectInputStream;
\rimport java.io.ObjectOutputStream;
\rimport java.io.Serializable;
\rimport java.util.HashMap;
\rimport java.util.Iterator;
\rimport java.util.List;
\r\r/**
\r * Yet another <code>SpellDictionary</code> this one is based on Damien Guillaume's
\r * Diskbased dictionary but adds a cache to try to improve abit on performance.
\r *
\r * @author Robert Gustavsson
\r * @version 0.01
\r */
\r\rpublic class SpellDictionaryCachedDichoDisk extends SpellDictionaryDichoDisk {
\r \r // Only used for testing to measure the effectiveness of the cache.
\r static public int hits=0;
\r static public int codes=0;
\r\r public static final String JAZZY_DIR=".jazzy";
\r public static final String PRE_CACHE_FILE_EXT=".pre";
\r\r private static int MAX_CACHED=10000;
\r\r @SuppressWarnings("unchecked")
\r private final HashMap suggestionCache=new HashMap(MAX_CACHED);
\r private String preCacheFileName;
\r private String preCacheDir;
\r\r /**
\r * Dictionary Convienence Constructor.
\r */
\r public SpellDictionaryCachedDichoDisk(File wordList)
\r throws FileNotFoundException, IOException {
\r super(wordList);
\r loadPreCache(wordList);
\r }
\r \r /**
\r * Dictionary Convienence Constructor.
\r */
\r public SpellDictionaryCachedDichoDisk(File wordList, String encoding)
\r throws FileNotFoundException, IOException {
\r super(wordList, encoding);
\r loadPreCache(wordList);
\r }
\r\r /**
\r * Dictionary constructor that uses an aspell phonetic file to
\r * build the transformation table.
\r */
\r\r public SpellDictionaryCachedDichoDisk(File wordList, File phonetic)
\r throws FileNotFoundException, IOException {
\r super(wordList, phonetic);
\r loadPreCache(wordList);
\r }
\r\r /**
\r * Dictionary constructor that uses an aspell phonetic file to
\r * build the transformation table.
\r */
\r public SpellDictionaryCachedDichoDisk(File wordList, File phonetic, String encoding)
\r throws FileNotFoundException, IOException {
\r super(wordList, phonetic, encoding);
\r loadPreCache(wordList);
\r }
\r\r /**
\r * Add a word permanantly to the dictionary (and the dictionary file).
\r * <i>not implemented !</i>
\r */
\r @Override
\r public void addWord(String word) {
\r System.err.println("error: addWord is not implemented for SpellDictionaryCachedDichoDisk");
\r }
\r\r /**
\r * Clears the cache.
\r */
\r public void clearCache(){
\r suggestionCache.clear();
\r }
\r\r /**
\r * Returns a list of strings (words) for the code.
\r */
\r @Override
\r @SuppressWarnings("unchecked")
\r public List getWords(String code) {
\r List list;
\r codes++;
\r if(suggestionCache.containsKey(code)){
\r hits++;
\r list=getCachedList(code);
\r return list;
\r }
\r list=super.getWords(code);
\r addToCache(code,list);
\r \r return list;
\r }
\r /**
\r * This method returns the cached suggestionlist and also moves the code to
\r * the top of the codeRefQueue to indicate this code has resentlly been
\r * referenced.
\r */
\r @SuppressWarnings("unchecked")
\r private List getCachedList(String code){
\r CacheObject obj=(CacheObject)suggestionCache.get(code);
\r obj.setRefTime();
\r return obj.getSuggestionList();
\r }
\r\r /**
\r * Adds a code and it's suggestion list to the cache.
\r */
\r @SuppressWarnings("unchecked")
\r private void addToCache(String code, List l){
\r String c=null;
\r String lowestCode=null;
\r long lowestTime=Long.MAX_VALUE;
\r Iterator it;
\r CacheObject obj;
\r\r if(suggestionCache.size()>=MAX_CACHED){
\r it=suggestionCache.keySet().iterator();
\r while(it.hasNext()){
\r c=(String)it.next();
\r obj=(CacheObject)suggestionCache.get(c);
\r if(obj.getRefTime()==0){
\r lowestCode=c;
\r break;
\r }
\r if(lowestTime>obj.getRefTime()){
\r lowestCode=c;
\r lowestTime=obj.getRefTime();
\r }
\r }
\r suggestionCache.remove(lowestCode);
\r }
\r suggestionCache.put(code,new CacheObject(l));
\r }
\r\r /**
\r * Load the cache from file. The cach file has the same name as the
\r * dico file with the .pre extension added.
\r */
\r @SuppressWarnings("unchecked")
\r private void loadPreCache(File dicoFile)throws IOException{
\r String code;
\r List suggestions;
\r long size,
\r time;
\r File preFile;
\r ObjectInputStream in;
\r\r preCacheDir=System.getProperty("user.home")+"/"+JAZZY_DIR;
\r preCacheFileName=preCacheDir+"/"+dicoFile.getName()+PRE_CACHE_FILE_EXT;
\r //System.out.println(preCacheFileName);
\r preFile=new File(preCacheFileName);
\r if(!preFile.exists()){
\r System.err.println("No precache file");
\r return;
\r }
\r //System.out.println("Precaching...");
\r in=new ObjectInputStream(new FileInputStream(preFile));
\r try{
\r size=in.readLong();
\r for(int i=0;i<size;i++){
\r code=(String)in.readObject();
\r time=in.readLong();
\r suggestions=(List)in.readObject();
\r suggestionCache.put(code,new CacheObject(suggestions,time));
\r }
\r }catch(ClassNotFoundException ex){
\r System.out.println(ex.getMessage());
\r }
\r in.close();
\r }
\r\r /**
\r * Saves the current cache to file.
\r */
\r @SuppressWarnings("unchecked")
\r public void saveCache() throws IOException{
\r String code;
\r CacheObject obj;
\r File preFile,
\r preDir;
\r ObjectOutputStream out;
\r Iterator it;
\r\r if(preCacheFileName==null || preCacheDir==null){
\r System.err.println("Precache filename has not been set.");
\r return;
\r }
\r //System.out.println("Saving cache to precache file...");
\r preDir=new File(preCacheDir);
\r if(!preDir.exists())
\r preDir.mkdir();
\r preFile=new File(preCacheFileName);
\r out=new ObjectOutputStream(new FileOutputStream(preFile));
\r it=suggestionCache.keySet().iterator();
\r out.writeLong(suggestionCache.size());
\r while(it.hasNext()){
\r code=(String)it.next();
\r obj=(CacheObject)suggestionCache.get(code);
\r out.writeObject(code);
\r out.writeLong(obj.getRefTime());
\r out.writeObject(obj.getSuggestionList());
\r }
\r out.close();
\r }
\r\r // INNER CLASSES
\r // ------------------------------------------------------------------------
\r @SuppressWarnings("serial")
\r private class CacheObject implements Serializable{
\r \r @SuppressWarnings("unchecked")
\r private List suggestions=null;
\r private long refTime=0;
\r\r @SuppressWarnings("unchecked")
\r public CacheObject(List list){
\r this.suggestions=list;
\r }
\r\r @SuppressWarnings("unchecked")
\r public CacheObject(List list, long time){
\r this.suggestions=list;
\r this.refTime=time;
\r }
\r \r @SuppressWarnings("unchecked")
\r public List getSuggestionList(){
\r return suggestions;
\r }
\r\r public void setRefTime(){
\r refTime=System.currentTimeMillis();
\r }
\r\r public long getRefTime(){
\r return refTime;
\r }
\r }
\r}
\r