package org.alastairmailer;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JDialog;
import net.sf.jabref.BasePanel;
import net.sf.jabref.BibtexDatabase;
import net.sf.jabref.BibtexEntry;
import net.sf.jabref.GUIGlobals;
import net.sf.jabref.Globals;
import net.sf.jabref.MetaData;
import net.sf.jabref.Util;
import net.sf.jabref.gui.FileListEntry;
import net.sf.jabref.gui.FileListTableModel;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.store.FSDirectory;
public class LuceneBibtexDatabase implements ActionListener {
public static Logger log = Logger.getLogger(LuceneBibtexDatabase.class
.getCanonicalName());
public final synchronized int getUpdateTime() {
Vector<String> auto = getMd().getData("lucene-AutoIndexInterval");
if (auto == null) {
return 60;
} else {
return Integer.parseInt(auto.firstElement());
}
}
public synchronized void setUpdateTime(int update_time) {
Vector<String> auto = new Vector<String>();
auto.add("" + update_time);
getMd().putData("lucene-AutoIndexInterval", auto);
bp.markBaseChanged();
log.info("Automatic update interval for " + getDb().toString() + " changed to " + update_time + "s");
if (getAutoIndexEnabled()) {
stopUpdateMonitor();
startUpdateMonitor();
}
}
private Map<String, Set<BibtexEntry>> fileBibMap;
private IndexReader index;
private BasePanel bp;
private Timer updateMonitorTimer;
private Thread updateMonitorThread;
private IndexBuilder builder;
private long lastIndexed = -1l;
private Thread indexThread;
public final synchronized boolean getAutoIndexEnabled() {
Vector<String> auto = getMd().getData("lucene-AutoIndexEnabled");
if (auto == null) {
return true;
} else {
return Boolean.parseBoolean(auto.firstElement());
}
}
public void setAutoIndexEnabled(boolean auto_index) {
if (auto_index) {
startUpdateMonitor();
} else {
stopUpdateMonitor();
}
Vector<String> auto = new Vector<String>();
auto.add("" + auto_index);
getMd().putData("lucene-AutoIndexEnabled", auto);
bp.markBaseChanged();
}
public LuceneBibtexDatabase(BasePanel bp) {
this.bp = bp;
initFileBibMap();
File indexDir = getIndexDir();
File fileDir = getFileDir();
boolean autoIndex = getAutoIndexEnabled();
this.updateMonitorTimer = new Timer("Update monitor thread");
if (autoIndex) {
startUpdateMonitor();
}
}
public void stopUpdateMonitor() {
updateMonitorTimer.cancel();
log.info("Automatic updates stopped for " + getDb().toString());
}
public final void startUpdateMonitor() {
updateMonitorTimer = new Timer("Update monitor thread");
updateMonitorTimer.schedule(new TimerTask() {
@Override
public void run() {
if (getIndexDir() == null || getFileDir() == null
|| isIndexed() != true) {
log
.info("Automatic background update skipped as index or file directory does not exist");
return;
} else if (isUpdating()) {
log
.info("Automatic background update skipped as update already in progress");
return;
} else if (System.currentTimeMillis() - lastIndexed < getUpdateTime()) {
log
.info("Automatic background update skipped as update performed recently");
return;
} else {
try {
log.info("Automatic background update started");
updateIndex(null, false);
log.info("Automatic background update finished");
} catch (FileNotFoundException e) {
log.info("Automatic background update failed");
}
}
}
}, Math.round(getUpdateTime()*1000), getUpdateTime()*1000);
log.info("Automatic updates activated for " + getDb().toString() + " with update interval " + getUpdateTime() + "s");
}
public synchronized boolean isUpdating() {
if (indexThread == null) {
return false;
} else {
return indexThread.isAlive();
}
}
public boolean indexingError() {
if (builder == null) {
return true;
} else {
return builder.getStatus() == IndexBuilder.ERROR;
}
}
public boolean isIndexed() {
if (getFileDir() == null || getIndexDir() == null) {
return false;
}
boolean indexed = false;
try {
indexed = IndexReader.indexExists(FSDirectory.open(getIndexDir()));
} catch (IOException e) {
}
return indexed;
}
public final File getFileDir() {
String[] fileDirs = getMd().getFileDirectory(GUIGlobals.FILE_FIELD);
// This function now returns an array, as it seems multiple file directories can be defined
// TODO: Handle this functionality - for now, just use top of list
File fileDir = Util.expandFilename(fileDirs[0], "");
/* This no longer needed, as getFileDirectory looks in all the places for us
* if (fileDir == null) {
String fDir = Globals.prefs.get("fileDirectory");
if (fDir != null) {
fileDir = new File(fDir);
}
}*/
return fileDir;
}
public void setIndexDir(File indexDir) {
if (indexDir == null) {
getMd().remove("lucene-IndexDirectory");
} else {
Vector<String> temp = new Vector<String>(1);
temp.add(indexDir.getAbsolutePath());
getMd().putData("lucene-IndexDirectory", temp);
}
bp.markBaseChanged();
}
public final File getIndexDir() {
Vector<String> indexDirStr = getMd().getData("lucene-IndexDirectory");
if (indexDirStr != null) {
return new File(indexDirStr.firstElement());
} else {
return null;
}
}
public IndexReader getIndexReader() throws IOException,
CorruptIndexException {
if (index == null) {
index = IndexReader.open(FSDirectory.open(getIndexDir()));
} else if (!index.isCurrent()) {
index.close();
index = IndexReader.open(FSDirectory.open(getIndexDir()));
}
return index;
}
public void updateIndex(Window logDialogParent, boolean rebuild)
throws FileNotFoundException {
File indexDir = getIndexDir();
File fileDir = getFileDir();
if (indexDir == null) {
throw new FileNotFoundException("The index for " + toString()
+ " cannot be found - please ensure it is set correctly.");
}
if (fileDir == null) {
throw new FileNotFoundException("The file directory for "
+ toString()
+ " cannot be found - please ensure it is set correctly.");
}
IndexBuilder myBuilder = new IndexBuilder(this, rebuild);
indexThread = new Thread(myBuilder);
indexThread.start();
lastIndexed = System.currentTimeMillis();
if (logDialogParent != null) {
IndexLogMsgHandler handler = new IndexLogMsgHandler(myBuilder);
JDialog dialog = handler.getDialog(logDialogParent, true);
handler.startTimer();
dialog.setVisible(true);
}
}
@Override
public String toString() {
File bibFile = getMd().getFile();
if (bibFile == null) { return "<unsaved database>"; }
else { return bibFile.getName(); }
}
public Set<BibtexEntry> getBibtexEntries(String f) {
return fileBibMap.get(f);
}
public final void initFileBibMap() {
long start = 0, stop = 0;
if (log.isLoggable(Level.INFO)) {
start = System.currentTimeMillis();
}
fileBibMap = new HashMap<String, Set<BibtexEntry>>();
for (BibtexEntry entry : getDb().getEntries()) {
String fField = entry.getField(GUIGlobals.FILE_FIELD);
FileListTableModel fLTM = new FileListTableModel();
fLTM.setContent(fField);
for (int i = 0; i < fLTM.getRowCount(); i++) {
FileListEntry flEntry = fLTM.getEntry(i);
String link = flEntry.getLink();
// String fileDir = getMd().getFileDirectory(GUIGlobals.FILE_FIELD);
// File f = net.sf.jabref.Util.expandFilename(link, fileDir);
if (fileBibMap.containsKey(link)) {
Set<BibtexEntry> s = fileBibMap.get(link);
s.add(entry);
} else {
Set<BibtexEntry> s = new HashSet<BibtexEntry>();
s.add(entry);
fileBibMap.put(link, s);
}
}
}
if (log.isLoggable(Level.INFO)) {
stop = System.currentTimeMillis();
log.info("Total file mapping time: " + (stop - start) / 1000.0
+ " ms");
log.info("Mapped " + fileBibMap.keySet().size()
+ " file entries to " + getDb().getEntries().size()
+ " bibtex entries.");
}
}
public Map<String, Set<BibtexEntry>> getFileBibMap() {
return fileBibMap;
}
public void actionPerformed(ActionEvent e) {
log.info("Background update of " + getMd().getFile().toString());
// if (isUpdating()) {
// log.info("Background update skipped as update already in progress");
// } else if (indexingError()) {
// log.info("Backgorund update skipped due to indexing error");
// } else if (getIndexDir() == null || getFileDir() == null) {
// log
// .info("Background update skipped as index or file directories are not set.");
try {
if (indexCurrent()) {
log.info("Background update unnecessary.");
} else {
builder = new IndexBuilder(this, false);
updateMonitorThread = new Thread(builder);
updateMonitorThread.start();
}
} catch (IOException ex) {
log.info("Background update skipped due to IOException: " + ex.toString());
}
}
public synchronized BasePanel getBp() {
return bp;
}
public BibtexDatabase getDb() {
return getBp().database();
}
public synchronized MetaData getMd() {
return getBp().metaData();
}
public synchronized boolean indexCurrent() throws IOException {
try {
long indexMod = getIndexDir().lastModified();
long fileMod = getFileDir().lastModified();
//log.info(getFileDir().toString() + " indexCurrent = " + (indexMod >= fileMod) + " indexMod: " + (new Date(indexMod)) + "fileMod: " + (new Date(fileMod)));
return indexMod >= fileMod;
} catch (NullPointerException e) {
throw new IOException("Index or file directory not set: " + e);
}
}
}