package org.jabref.gui.importer; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import org.jabref.Globals; import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; import org.jabref.gui.EntryMarker; import org.jabref.gui.FXDialogService; import org.jabref.gui.JabRefFrame; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.gui.worker.AbstractWorker; import org.jabref.logic.importer.ImportException; import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.importer.Importer; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.UpdateField; import org.jabref.model.database.BibDatabase; import org.jabref.model.database.KeyCollisionException; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibtexString; import org.jabref.preferences.JabRefPreferences; /* * TODO: could separate the "menu item" functionality from the importing functionality */ public class ImportMenuItem extends JMenuItem implements ActionListener { private final JabRefFrame frame; private final boolean openInNew; private final Importer importer; private Exception importError; public ImportMenuItem(JabRefFrame frame, boolean openInNew) { this(frame, openInNew, null); } public ImportMenuItem(JabRefFrame frame, boolean openInNew, Importer importer) { super(importer == null ? Localization.lang("Autodetect format") : importer.getName()); this.importer = importer; this.frame = frame; this.openInNew = openInNew; addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { MyWorker 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(List<String> filenames) { // replace the work of the init step: MyWorker worker = new MyWorker(); worker.fileOk = true; worker.filenames = filenames.stream().map(Paths::get).collect(Collectors.toList()); worker.getWorker().run(); worker.getCallBack().update(); } class MyWorker extends AbstractWorker { private List<Path> filenames; private ParserResult bibtexResult; // Contains the merged import results private boolean fileOk; @Override public void init() { importError = null; FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder() .withInitialDirectory(Globals.prefs.get(JabRefPreferences.WORKING_DIRECTORY)).build(); DialogService ds = new FXDialogService(); filenames = DefaultTaskExecutor .runInJavaFXThread(() -> ds.showFileOpenDialogAndGetMultipleFiles(fileDialogConfiguration)); if (!filenames.isEmpty()) { frame.block(); frame.output(Localization.lang("Starting import")); fileOk = true; Globals.prefs.put(JabRefPreferences.WORKING_DIRECTORY, filenames.get(0).getParent().toString()); } } @Override public void run() { if (!fileOk) { return; } // We import all files and collect their results: List<ImportFormatReader.UnknownFormatImport> imports = new ArrayList<>(); for (Path filename : filenames) { try { if (importer == null) { // Unknown format: frame.output(Localization.lang("Importing in unknown format") + "..."); // This import method never throws an IOException: imports.add(Globals.IMPORT_FORMAT_READER.importUnknownFormat(filename)); } else { frame.output(Localization.lang("Importing in %0 format", importer.getName()) + "..."); // Specific importer: ParserResult pr = importer.importDatabase(filename, Globals.prefs.getDefaultEncoding()); imports.add(new ImportFormatReader.UnknownFormatImport(importer.getName(), pr)); } } catch (ImportException | IOException e) { // This indicates that a specific importer was specified, and that // this importer has thrown an IOException. We store the exception, // so a relevant error message can be displayed. importError = e; } } // 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 // BibDatabase. bibtexResult = mergeImportResults(imports); /* show parserwarnings, if any. */ for (ImportFormatReader.UnknownFormatImport p : imports) { if (p != null) { ParserResult pr = p.parserResult; ParserResultWarningDialog.showParserResultWarningDialog(pr, frame); } } } @Override public void update() { if (!fileOk) { return; } if (bibtexResult == null) { if (importer == null) { frame.output(Localization.lang("Could not find a suitable import format.")); } else { // Import in a specific format was specified. Check if we have stored error information: if (importError == null) { JOptionPane.showMessageDialog(frame, Localization .lang("No entries found. Please make sure you are using the correct import filter."), Localization.lang("Import failed"), JOptionPane.ERROR_MESSAGE); } else { JOptionPane.showMessageDialog(frame, importError.getMessage(), Localization.lang("Import failed"), JOptionPane.ERROR_MESSAGE); } } } else { if (openInNew) { frame.addTab(bibtexResult.getDatabaseContext(), true); frame.output( Localization.lang("Imported entries") + ": " + bibtexResult.getDatabase().getEntryCount()); } else { final BasePanel panel = (BasePanel) frame.getTabbedPane().getSelectedComponent(); ImportInspectionDialog diag = new ImportInspectionDialog(frame, panel, Localization.lang("Import"), openInNew); diag.addEntries(bibtexResult.getDatabase().getEntries()); diag.entryListComplete(); diag.setLocationRelativeTo(frame); diag.setVisible(true); diag.toFront(); } } frame.unblock(); } } private ParserResult mergeImportResults(List<ImportFormatReader.UnknownFormatImport> imports) { BibDatabase database = new BibDatabase(); ParserResult directParserResult = null; boolean anythingUseful = false; for (ImportFormatReader.UnknownFormatImport importResult : imports) { if (importResult == null) { continue; } if (ImportFormatReader.BIBTEX_FORMAT.equals(importResult.format)) { // Bibtex result. We must merge it into our main base. ParserResult pr = importResult.parserResult; anythingUseful = anythingUseful || pr.getDatabase().hasEntries() || (!pr.getDatabase().hasNoStrings()); // Record the parserResult, as long as this is the first bibtex result: if (directParserResult == null) { directParserResult = pr; } // Merge entries: for (BibEntry 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.parserResult; Collection<BibEntry> entries = pr.getDatabase().getEntries(); anythingUseful = anythingUseful | !entries.isEmpty(); // set timestamp and owner UpdateField.setAutomaticFields(entries, Globals.prefs.getUpdateFieldPreferences()); // set timestamp and owner boolean markEntries = !openInNew && EntryMarker.shouldMarkEntries(); for (BibEntry entry : entries) { if (markEntries) { EntryMarker.markEntry(entry, EntryMarker.IMPORT_MARK_LEVEL, false, new NamedCompound("")); } database.insertEntry(entry); } } } if (!anythingUseful) { return null; } if ((imports.size() == 1) && (directParserResult != null)) { return directParserResult; } else { return new ParserResult(database); } } }