package org.jabref.pdfimport; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Optional; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.jabref.Globals; import org.jabref.gui.BasePanel; import org.jabref.gui.BasePanelMode; import org.jabref.gui.EntryTypeDialog; import org.jabref.gui.JabRefFrame; import org.jabref.gui.entryeditor.EntryEditor; import org.jabref.gui.externalfiles.DroppedFileHandler; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.filelist.FileListEntry; import org.jabref.gui.filelist.FileListTableModel; import org.jabref.gui.maintable.MainTable; import org.jabref.gui.undo.UndoableInsertEntry; import org.jabref.logic.bibtexkeypattern.BibtexKeyPatternUtil; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.importer.fileformat.PdfContentImporter; import org.jabref.logic.importer.fileformat.PdfXmpImporter; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.UpdateField; import org.jabref.logic.util.io.FileUtil; import org.jabref.logic.xmp.XMPUtil; import org.jabref.model.database.KeyCollisionException; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.EntryType; import org.jabref.model.entry.FieldName; import org.jabref.preferences.JabRefPreferences; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class PdfImporter { private static final Log LOGGER = LogFactory.getLog(PdfImporter.class); private final JabRefFrame frame; private final BasePanel panel; private final MainTable entryTable; private final int dropRow; /** * Creates the PdfImporter * * @param frame the JabRef frame * @param panel the panel to use * @param entryTable the entry table to work on * @param dropRow the row the entry is dropped to. May be -1 to indicate that no row is selected. */ public PdfImporter(JabRefFrame frame, BasePanel panel, MainTable entryTable, int dropRow) { this.frame = frame; this.panel = panel; this.entryTable = entryTable; this.dropRow = dropRow; } /** * * Imports the PDF files given by fileNames * * @param fileNames states the names of the files to import * @return list of successful created BibTeX entries and list of non-PDF files */ public ImportPdfFilesResult importPdfFiles(List<String> fileNames) { // sort fileNames in PDFfiles to import and other files // PDFfiles: variable files // other files: variable noPdfFiles List<String> files = new ArrayList<>(fileNames); List<String> noPdfFiles = new ArrayList<>(); for (String file : files) { if (!PdfFileFilter.accept(file)) { noPdfFiles.add(file); } } files.removeAll(noPdfFiles); // files and noPdfFiles correctly sorted // import the files List<BibEntry> entries = importPdfFilesInternal(files); return new ImportPdfFilesResult(noPdfFiles, entries); } /** * @param fileNames - PDF files to import * @return true if the import succeeded, false otherwise */ private List<BibEntry> importPdfFilesInternal(List<String> fileNames) { if (panel == null) { return Collections.emptyList(); } ImportDialog importDialog = null; boolean doNotShowAgain = false; boolean neverShow = Globals.prefs.getBoolean(JabRefPreferences.IMPORT_ALWAYSUSE); int globalChoice = Globals.prefs.getInt(JabRefPreferences.IMPORT_DEFAULT_PDF_IMPORT_STYLE); List<BibEntry> res = new ArrayList<>(); for (String fileName : fileNames) { if (!neverShow && !doNotShowAgain) { importDialog = new ImportDialog(dropRow >= 0, fileName); if (!XMPUtil.hasMetadata(Paths.get(fileName), Globals.prefs.getXMPPreferences())) { importDialog.disableXMPChoice(); } importDialog.setLocationRelativeTo(frame); importDialog.showDialog(); doNotShowAgain = importDialog.isDoNotShowAgain(); } if (neverShow || (importDialog.getResult() == JOptionPane.OK_OPTION)) { int choice = neverShow ? globalChoice : importDialog.getChoice(); switch (choice) { case ImportDialog.XMP: doXMPImport(fileName, res); break; case ImportDialog.CONTENT: doContentImport(fileName, res); break; case ImportDialog.NOMETA: createNewBlankEntry(fileName).ifPresent(res::add); break; case ImportDialog.ONLYATTACH: DroppedFileHandler dfh = new DroppedFileHandler(frame, panel); if (dropRow >= 0) { dfh.linkPdfToEntry(fileName, entryTable, dropRow); } else { dfh.linkPdfToEntry(fileName, entryTable, entryTable.getSelectedRow()); } break; default: break; } } } return res; } private void doXMPImport(String fileName, List<BibEntry> res) { List<BibEntry> localRes = new ArrayList<>(); PdfXmpImporter importer = new PdfXmpImporter(Globals.prefs.getXMPPreferences()); Path filePath = Paths.get(fileName); ParserResult result = importer.importDatabase(filePath, Globals.prefs.getDefaultEncoding()); if (result.hasWarnings()) { frame.showMessage(result.getErrorMessage()); } localRes.addAll(result.getDatabase().getEntries()); BibEntry entry; if (localRes.isEmpty()) { // import failed -> generate default entry LOGGER.info("Import failed"); createNewBlankEntry(fileName).ifPresent(res::add); return; } // only one entry is imported entry = localRes.get(0); // insert entry to database and link file panel.getDatabase().insertEntry(entry); panel.markBaseChanged(); FileListTableModel tm = new FileListTableModel(); Path toLink = Paths.get(fileName); // Get a list of file directories: List<Path> dirsS = panel.getBibDatabaseContext() .getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences()); tm.addEntry(0, new FileListEntry(toLink.getFileName().toString(), FileUtil.shortenFileName(toLink, dirsS).toString(), ExternalFileTypes.getInstance().getExternalFileTypeByName("PDF"))); entry.setField(FieldName.FILE, tm.getStringRepresentation()); res.add(entry); } private Optional<BibEntry> createNewBlankEntry(String fileName) { Optional<BibEntry> newEntry = createNewEntry(); newEntry.ifPresent(bibEntry -> { DroppedFileHandler dfh = new DroppedFileHandler(frame, panel); dfh.linkPdfToEntry(fileName, bibEntry); }); return newEntry; } private void doContentImport(String fileName, List<BibEntry> res) { PdfContentImporter contentImporter = new PdfContentImporter( Globals.prefs.getImportFormatPreferences()); Path filePath = Paths.get(fileName); ParserResult result = contentImporter.importDatabase(filePath, Globals.prefs.getDefaultEncoding()); if (result.hasWarnings()) { frame.showMessage(result.getErrorMessage()); } if (!result.getDatabase().hasEntries()) { // import failed -> generate default entry createNewBlankEntry(fileName).ifPresent(res::add); return; } // only one entry is imported BibEntry entry = result.getDatabase().getEntries().get(0); // insert entry to database and link file panel.getDatabase().insertEntry(entry); panel.markBaseChanged(); BibtexKeyPatternUtil.makeAndSetLabel(panel.getBibDatabaseContext().getMetaData() .getCiteKeyPattern(Globals.prefs.getBibtexKeyPatternPreferences().getKeyPattern()), panel.getDatabase(), entry, Globals.prefs.getBibtexKeyPatternPreferences()); DroppedFileHandler dfh = new DroppedFileHandler(frame, panel); dfh.linkPdfToEntry(fileName, entry); SwingUtilities.invokeLater(() -> panel.highlightEntry(entry)); if (Globals.prefs.getBoolean(JabRefPreferences.AUTO_OPEN_FORM)) { EntryEditor editor = panel.getEntryEditor(entry); panel.showEntryEditor(editor); } res.add(entry); } private Optional<BibEntry> createNewEntry() { // Find out what type is desired EntryTypeDialog etd = new EntryTypeDialog(frame); // We want to center the dialog, to make it look nicer. etd.setLocationRelativeTo(frame); etd.setVisible(true); EntryType type = etd.getChoice(); if (type != null) { // Only if the dialog was not canceled. final BibEntry bibEntry = new BibEntry(type.getName()); try { panel.getDatabase().insertEntry(bibEntry); // Set owner/timestamp if options are enabled: List<BibEntry> list = new ArrayList<>(); list.add(bibEntry); UpdateField.setAutomaticFields(list, true, true, Globals.prefs.getUpdateFieldPreferences()); // Create an UndoableInsertEntry object. panel.getUndoManager().addEdit(new UndoableInsertEntry(panel.getDatabase(), bibEntry, panel)); panel.output(Localization.lang("Added new") + " '" + type.getName().toLowerCase(Locale.ROOT) + "' " + Localization.lang("entry") + "."); // We are going to select the new entry. Before that, make sure that we are in // show-entry mode. If we aren't already in that mode, enter the WILL_SHOW_EDITOR // mode which makes sure the selection will trigger display of the entry editor // and adjustment of the splitter. if (panel.getMode() != BasePanelMode.SHOWING_EDITOR) { panel.setMode(BasePanelMode.WILL_SHOW_EDITOR); } SwingUtilities.invokeLater(() -> panel.showEntry(bibEntry)); // The database just changed. panel.markBaseChanged(); return Optional.of(bibEntry); } catch (KeyCollisionException ex) { LOGGER.info("Key collision occurred", ex); } } return Optional.empty(); } public class ImportPdfFilesResult { private final List<String> noPdfFiles; private final List<BibEntry> entries; public ImportPdfFilesResult(List<String> noPdfFiles, List<BibEntry> entries) { this.noPdfFiles = noPdfFiles; this.entries = entries; } public List<String> getNoPdfFiles() { return noPdfFiles; } public List<BibEntry> getEntries() { return entries; } } }