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