package org.jabref.gui.importer.actions; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import javax.swing.JOptionPane; import org.jabref.Globals; import org.jabref.JabRefExecutorService; import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; import org.jabref.gui.FXDialogService; import org.jabref.gui.JabRefFrame; import org.jabref.gui.MergeDialog; import org.jabref.gui.actions.BaseAction; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableInsertEntry; import org.jabref.gui.undo.UndoableInsertString; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.logic.importer.OpenDatabase; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.FileExtensions; 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.model.groups.AllEntriesGroup; import org.jabref.model.groups.ExplicitGroup; import org.jabref.model.groups.GroupHierarchyType; import org.jabref.model.metadata.ContentSelector; import org.jabref.model.metadata.MetaData; import org.jabref.preferences.JabRefPreferences; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class AppendDatabaseAction implements BaseAction { private static final Log LOGGER = LogFactory.getLog(AppendDatabaseAction.class); private final JabRefFrame frame; private final BasePanel panel; private final List<Path> filesToOpen = new ArrayList<>(); public AppendDatabaseAction(JabRefFrame frame, BasePanel panel) { this.frame = frame; this.panel = panel; } @Override public void action() { filesToOpen.clear(); final MergeDialog md = new MergeDialog(frame, Localization.lang("Append library"), true); md.setLocationRelativeTo(panel); md.setVisible(true); if (md.isOkPressed()) { FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder() .withDefaultExtension(FileExtensions.BIBTEX_DB) .withInitialDirectory(Globals.prefs.get(JabRefPreferences.WORKING_DIRECTORY)) .build(); DialogService ds = new FXDialogService(); List<Path> chosen = DefaultTaskExecutor .runInJavaFXThread(() -> ds.showFileOpenDialogAndGetMultipleFiles(fileDialogConfiguration)); if (chosen.isEmpty()) { return; } filesToOpen.addAll(chosen); // Run the actual open in a thread to prevent the program // locking until the file is loaded. JabRefExecutorService.INSTANCE.execute( () -> openIt(md.importEntries(), md.importStrings(), md.importGroups(), md.importSelectorWords())); } } private void openIt(boolean importEntries, boolean importStrings, boolean importGroups, boolean importSelectorWords) { if (filesToOpen.isEmpty()) { return; } for (Path file : filesToOpen) { try { Globals.prefs.put(JabRefPreferences.WORKING_DIRECTORY, file.getParent().toString()); // Should this be done _after_ we know it was successfully opened? ParserResult pr = OpenDatabase.loadDatabase(file.toFile(), Globals.prefs.getImportFormatPreferences()); AppendDatabaseAction.mergeFromBibtex(frame, panel, pr, importEntries, importStrings, importGroups, importSelectorWords); panel.output(Localization.lang("Imported from library") + " '" + file + "'"); } catch (IOException | KeyCollisionException ex) { LOGGER.warn("Could not open database", ex); JOptionPane.showMessageDialog(panel, ex.getMessage(), Localization.lang("Open library"), JOptionPane.ERROR_MESSAGE); } } } private static void mergeFromBibtex(JabRefFrame frame, BasePanel panel, ParserResult pr, boolean importEntries, boolean importStrings, boolean importGroups, boolean importSelectorWords) throws KeyCollisionException { BibDatabase fromDatabase = pr.getDatabase(); List<BibEntry> appendedEntries = new ArrayList<>(); List<BibEntry> originalEntries = new ArrayList<>(); BibDatabase database = panel.getDatabase(); NamedCompound ce = new NamedCompound(Localization.lang("Append library")); MetaData meta = pr.getMetaData(); if (importEntries) { // Add entries boolean overwriteOwner = Globals.prefs.getBoolean(JabRefPreferences.OVERWRITE_OWNER); boolean overwriteTimeStamp = Globals.prefs.getBoolean(JabRefPreferences.OVERWRITE_TIME_STAMP); for (BibEntry originalEntry : fromDatabase.getEntries()) { BibEntry be = (BibEntry) originalEntry.clone(); UpdateField.setAutomaticFields(be, overwriteOwner, overwriteTimeStamp, Globals.prefs.getUpdateFieldPreferences()); database.insertEntry(be); appendedEntries.add(be); originalEntries.add(originalEntry); ce.addEdit(new UndoableInsertEntry(database, be, panel)); } } if (importStrings) { for (BibtexString bs : fromDatabase.getStringValues()) { if (!database.hasStringLabel(bs.getName())) { database.addString(bs); ce.addEdit(new UndoableInsertString(panel, database, bs)); } } } if (importGroups) { meta.getGroups().ifPresent(newGroups -> { // ensure that there is always only one AllEntriesGroup if (newGroups.getGroup() instanceof AllEntriesGroup) { // create a dummy group try { ExplicitGroup group = new ExplicitGroup("Imported", GroupHierarchyType.INDEPENDENT, Globals.prefs.getKeywordDelimiter()); newGroups.setGroup(group); group.add(appendedEntries); } catch (IllegalArgumentException e) { LOGGER.error(e); } } // groupsSelector is always created, even when no groups // have been defined. therefore, no check for null is // required here frame.getGroupSelector().addGroups(newGroups, ce); }); } if (importSelectorWords) { for (ContentSelector selector : meta.getContentSelectorList()) { panel.getBibDatabaseContext().getMetaData().addContentSelector(selector); } } ce.end(); panel.getUndoManager().addEdit(ce); panel.markBaseChanged(); } }