package net.sf.jabref.imports;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import net.sf.jabref.AbstractWorker;
import net.sf.jabref.BasePanel;
import net.sf.jabref.BibtexDatabase;
import net.sf.jabref.BibtexEntry;
import net.sf.jabref.BibtexEntryType;
import net.sf.jabref.BibtexFields;
import net.sf.jabref.BibtexString;
import net.sf.jabref.DuplicateCheck;
import net.sf.jabref.DuplicateResolverDialog;
import net.sf.jabref.Globals;
import net.sf.jabref.JabRefFrame;
import net.sf.jabref.KeyCollisionException;
import net.sf.jabref.Util;
import net.sf.jabref.gui.ImportInspectionDialog;
import net.sf.jabref.gui.FileDialogs;
import net.sf.jabref.labelPattern.LabelPatternUtil;
import net.sf.jabref.undo.NamedCompound;
import net.sf.jabref.undo.UndoableInsertEntry;
import net.sf.jabref.undo.UndoableRemoveEntry;
import net.sf.jabref.util.Pair;
/*
* TODO: could separate the "menu item" functionality from the importing functionality
*
*/
public class ImportMenuItem extends JMenuItem implements ActionListener {
JabRefFrame frame;
boolean openInNew;
MyWorker worker = null;
ImportFormat importer;
public ImportMenuItem(JabRefFrame frame, boolean openInNew) {
this(frame, openInNew, null);
}
public ImportMenuItem(JabRefFrame frame, boolean openInNew, ImportFormat importer) {
super(importer != null ? importer.getFormatName()
: Globals.lang("Autodetect format"));
this.importer = importer;
this.frame = frame;
this.openInNew = openInNew;
addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
worker = new MyWorker();
worker.init();
worker.getWorker().run();
worker.getCallBack().update();
}
/**
* Automatically imports the files given as arguments
* @param filenames List of files to import
*/
public void automatedImport(String filenames[]) {
// replace the work of the init step:
MyWorker worker = new MyWorker();
worker.fileOk = true;
worker.filenames = filenames;
worker.getWorker().run();
worker.getCallBack().update();
}
class MyWorker extends AbstractWorker {
String[] filenames = null, formatName = null;
ParserResult bibtexResult = null; // Contains the merged import results
boolean fileOk = false;
public void init() {
filenames = FileDialogs.getMultipleFiles(frame,
new File(Globals.prefs.get("workingDirectory")),
(importer != null ? importer.getExtensions() : null), true);
if ((filenames != null) && (filenames.length > 0)) {
frame.block();
frame.output(Globals.lang("Starting import"));
fileOk = true;
Globals.prefs.put("workingDirectory", filenames[0]);
}
}
public void run() {
if (!fileOk)
return;
// We import all files and collect their results:
List<Pair<String, ParserResult>> imports = new ArrayList<Pair<String, ParserResult>>();
for (String filename : filenames) {
try {
if (importer != null) {
// Specific importer:
ParserResult pr = new ParserResult(
Globals.importFormatReader.importFromFile(importer,
filename));
imports.add(new Pair<String, ParserResult>(importer
.getFormatName(), pr));
} else {
// Unknown format:
frame.output(Globals.lang("Importing in unknown format")+"...");
imports.add(Globals.importFormatReader
.importUnknownFormat(filename));
}
} catch (IOException e) {
// No entries found...
e.printStackTrace();
}
}
// Ok, done. Then try to gather in all we have found. Since we might
// have found
// one or more bibtex results, it's best to gather them in a
// BibtexDatabase.
bibtexResult = mergeImportResults(imports);
/* show parserwarnings, if any. */
for (Pair<String, ParserResult> p : imports) {
ParserResult pr = p.v;
if (pr.hasWarnings()) {
if (Globals.prefs
.getBoolean("displayKeyWarningDialogAtStartup")
&& pr.hasWarnings()) {
String[] wrns = pr.warnings();
StringBuffer wrn = new StringBuffer();
for (int j = 0; j < wrns.length; j++)
wrn.append(j + 1).append(". ").append(wrns[j])
.append("\n");
if (wrn.length() > 0)
wrn.deleteCharAt(wrn.length() - 1);
JOptionPane.showMessageDialog(frame, wrn.toString(),
Globals.lang("Warnings"),
JOptionPane.WARNING_MESSAGE);
}
}
}
}
public void update() {
if (!fileOk)
return;
// TODO: undo is not handled properly here, except for the entries
// added by
// the import inspection dialog.
if (bibtexResult != null) {
if (!openInNew) {
final BasePanel panel = (BasePanel) frame.getTabbedPane().getSelectedComponent();
BibtexDatabase toAddTo = panel.database();
// Use the import inspection dialog if it is enabled in preferences, and
// (there are more than one entry or the inspection dialog is also enabled
// for single entries):
if (Globals.prefs.getBoolean("useImportInspectionDialog") &&
(Globals.prefs.getBoolean("useImportInspectionDialogForSingle")
|| (bibtexResult.getDatabase().getEntryCount() > 1))) {
ImportInspectionDialog diag = new ImportInspectionDialog(frame, panel,
BibtexFields.DEFAULT_INSPECTION_FIELDS,
Globals.lang("Import"), openInNew);
diag.addEntries(bibtexResult.getDatabase().getEntries());
diag.entryListComplete();
Util.placeDialog(diag, frame);
diag.setVisible(true);
diag.toFront();
} else {
boolean generateKeys = Globals.prefs.getBoolean("generateKeysAfterInspection");
NamedCompound ce = new NamedCompound(Globals.lang("Import entries"));
// Check if we should unmark entries before adding the new ones:
if (Globals.prefs.getBoolean("unmarkAllEntriesBeforeImporting"))
for (BibtexEntry entry : toAddTo.getEntries()) {
Util.unmarkEntry(entry, toAddTo, ce);
}
for (BibtexEntry entry : bibtexResult.getDatabase().getEntries()){
try {
// Check if the entry is a duplicate of an existing one:
boolean keepEntry = true;
BibtexEntry duplicate = DuplicateCheck.containsDuplicate(toAddTo, entry);
if (duplicate != null) {
int answer = DuplicateResolverDialog.resolveDuplicateInImport
(frame, duplicate, entry);
// The upper entry is the
if (answer == DuplicateResolverDialog.DO_NOT_IMPORT)
keepEntry = false;
if (answer == DuplicateResolverDialog.IMPORT_AND_DELETE_OLD) {
// Remove the old one and import the new one.
toAddTo.removeEntry(duplicate.getId());
ce.addEdit(new UndoableRemoveEntry(toAddTo, duplicate, panel));
}
}
// Add the entry, if we are supposed to:
if (keepEntry) {
toAddTo.insertEntry(entry);
// Generate key, if we are supposed to:
if (generateKeys) {
LabelPatternUtil.makeLabel(Globals.prefs.getKeyPattern(), toAddTo, entry);
//System.out.println("gen:"+entry.getCiteKey());
}
// Let the autocompleters, if any, harvest words from the entry:
Util.updateCompletersForEntry(panel.getAutoCompleters(), entry);
ce.addEdit(new UndoableInsertEntry(toAddTo, entry, panel));
}
} catch (KeyCollisionException e) {
e.printStackTrace();
}
}
ce.end();
if (ce.hasEdits()) {
panel.undoManager.addEdit(ce);
panel.markBaseChanged();
}
}
}
else {
frame.addTab(bibtexResult.getDatabase(), bibtexResult.getFile(),
bibtexResult.getMetaData(), Globals.prefs.get("defaultEncoding"), true);
frame.output(Globals.lang("Imported entries") + ": " + bibtexResult.getDatabase().getEntryCount());
}
} else {
if (importer == null)
frame.output(Globals.lang("Could not find a suitable import format."));
else
JOptionPane.showMessageDialog(frame, Globals.lang("No entries found. Please make sure you are "
+"using the correct import filter."), Globals.lang("Import failed"),
JOptionPane.ERROR_MESSAGE);
}
frame.unblock();
}
}
public ParserResult mergeImportResults(List<Pair<String, ParserResult>> imports) {
BibtexDatabase database = new BibtexDatabase();
ParserResult directParserResult = null;
boolean anythingUseful = false;
for (Pair<String, ParserResult> importResult : imports){
if (importResult == null)
continue;
if (importResult.p.equals(ImportFormatReader.BIBTEX_FORMAT)){
// Bibtex result. We must merge it into our main base.
ParserResult pr = importResult.v;
anythingUseful = anythingUseful
|| ((pr.getDatabase().getEntryCount() > 0) || (pr.getDatabase().getStringCount() > 0));
// Record the parserResult, as long as this is the first bibtex result:
if (directParserResult == null) {
directParserResult = pr;
}
// Merge entries:
for (BibtexEntry entry : pr.getDatabase().getEntries()) {
database.insertEntry(entry);
}
// Merge strings:
for (BibtexString bs : pr.getDatabase().getStringValues()){
try {
database.addString((BibtexString)bs.clone());
} catch (KeyCollisionException e) {
// TODO: This means a duplicate string name exists, so it's not
// a very exceptional situation. We should maybe give a warning...?
}
}
} else {
ParserResult pr = importResult.v;
Collection<BibtexEntry> entries = pr.getDatabase().getEntries();
anythingUseful = anythingUseful | (entries.size() > 0);
// set timestamp and owner
Util.setAutomaticFields(entries, Globals.prefs.getBoolean("overwriteOwner"),
Globals.prefs.getBoolean("overwriteTimeStamp"),
!openInNew && Globals.prefs.getBoolean("markImportedEntries")); // set timestamp and owner
for (BibtexEntry entry : entries){
database.insertEntry(entry);
}
}
}
if (!anythingUseful)
return null;
if ((imports.size() == 1) && (directParserResult != null)) {
return directParserResult;
} else {
ParserResult pr = new ParserResult(database, new HashMap<String, String>(), new HashMap<String, BibtexEntryType>());
return pr;
}
}
}