From 16de272317ee0f50b4abd5efcd84c56e9621bb76 Mon Sep 17 00:00:00 2001 From: zkl-ai <72965042+zkl-ai@users.noreply.github.com> Date: Sun, 7 Aug 2022 03:44:07 +0800 Subject: [PATCH] Refactor of DOI import failure dialog, import format reader and clipboard manager (#8839) Co-authored-by: Siedlerchr --- .../java/org/jabref/gui/ClipBoardManager.java | 93 ++----------------- .../java/org/jabref/gui/DefaultInjector.java | 3 + .../java/org/jabref/gui/EntryTypeView.java | 4 +- .../org/jabref/gui/EntryTypeViewModel.java | 25 +++-- src/main/java/org/jabref/gui/JabRefFrame.java | 5 +- src/main/java/org/jabref/gui/JabRefMain.java | 2 +- src/main/java/org/jabref/gui/LibraryTab.java | 16 +++- .../BibtexExtractorViewModel.java | 7 +- .../bibtexextractor/ExtractBibtexDialog.java | 4 +- .../errorconsole/ErrorConsoleViewModel.java | 5 - .../gui/externalfiles/ImportHandler.java | 66 ++++++++++++- .../UnlinkedFilesDialogView.java | 4 +- .../UnlinkedFilesDialogViewModel.java | 7 +- .../IdentifierEditorViewModel.java | 22 ++++- .../importer/GenerateEntryFromIdAction.java | 30 +++--- .../org/jabref/gui/importer/ImportAction.java | 6 +- .../gui/importer/ImportEntriesViewModel.java | 4 +- .../importer/actions/OpenDatabaseAction.java | 2 +- .../org/jabref/gui/maintable/MainTable.java | 39 +++++++- .../gui/mergeentries/FetchAndMergeEntry.java | 12 ++- .../CustomImporterTabViewModel.java | 1 - .../importer/FetcherClientException.java | 19 ++++ .../importer/FetcherServerException.java | 18 ++++ .../logic/importer/IdBasedParserFetcher.java | 5 +- .../logic/importer/IdParserFetcher.java | 6 +- .../logic/importer/ImportFormatReader.java | 5 +- .../importer/PagedSearchBasedFetcher.java | 1 + .../logic/importer/fetcher/DoiFetcher.java | 8 +- .../logic/importer/fetcher/IsbnFetcher.java | 18 ++-- .../org/jabref/logic/net/URLDownload.java | 28 +++--- src/main/resources/l10n/JabRef_en.properties | 17 +++- .../ImportFormatReaderIntegrationTest.java | 2 +- .../ImportFormatReaderTestParameterless.java | 2 +- .../CompositeSearchBasedFetcherTest.java | 2 +- .../logic/importer/fetcher/CrossRefTest.java | 4 +- .../importer/fetcher/DoiFetcherTest.java | 20 +++- .../fetcher/IsbnViaEbookDeFetcherTest.java | 7 +- .../fetcher/LibraryOfCongressTest.java | 4 +- .../importer/fetcher/MedlineFetcherTest.java | 4 +- .../logic/importer/fetcher/MedraTest.java | 10 +- .../fetcher/OpenLibraryFetcherTest.java | 5 +- .../importer/fetcher/RfcFetcherTest.java | 8 +- .../org/jabref/logic/net/URLDownloadTest.java | 19 ++++ 43 files changed, 373 insertions(+), 196 deletions(-) create mode 100644 src/main/java/org/jabref/logic/importer/FetcherClientException.java create mode 100644 src/main/java/org/jabref/logic/importer/FetcherServerException.java diff --git a/src/main/java/org/jabref/gui/ClipBoardManager.java b/src/main/java/org/jabref/gui/ClipBoardManager.java index 290cb774c72..a75dd1798d9 100644 --- a/src/main/java/org/jabref/gui/ClipBoardManager.java +++ b/src/main/java/org/jabref/gui/ClipBoardManager.java @@ -5,10 +5,7 @@ import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; import java.util.List; import java.util.Optional; @@ -22,19 +19,8 @@ import org.jabref.architecture.AllowedToUseAwt; import org.jabref.logic.bibtex.BibEntryWriter; import org.jabref.logic.bibtex.FieldWriter; -import org.jabref.logic.importer.FetcherException; -import org.jabref.logic.importer.ImportException; -import org.jabref.logic.importer.ImportFormatReader; -import org.jabref.logic.importer.ImportFormatReader.UnknownFormatImport; -import org.jabref.logic.importer.ParseException; -import org.jabref.logic.importer.fetcher.ArXiv; -import org.jabref.logic.importer.fetcher.DoiFetcher; -import org.jabref.logic.importer.fileformat.BibtexParser; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.identifier.ArXivIdentifier; -import org.jabref.model.entry.identifier.DOI; -import org.jabref.model.util.OptionalUtil; import org.jabref.preferences.PreferencesService; import org.slf4j.Logger; @@ -49,18 +35,16 @@ public class ClipBoardManager { private static Clipboard clipboard; private static java.awt.datatransfer.Clipboard primary; - private static ImportFormatReader importFormatReader; + private final PreferencesService preferencesService; public ClipBoardManager(PreferencesService preferencesService) { - this(Clipboard.getSystemClipboard(), Toolkit.getDefaultToolkit().getSystemSelection(), Globals.IMPORT_FORMAT_READER, preferencesService); + this(Clipboard.getSystemClipboard(), Toolkit.getDefaultToolkit().getSystemSelection(), preferencesService); } - public ClipBoardManager(Clipboard clipboard, java.awt.datatransfer.Clipboard primary, ImportFormatReader importFormatReader, PreferencesService preferencesService) { + public ClipBoardManager(Clipboard clipboard, java.awt.datatransfer.Clipboard primary, PreferencesService preferencesService) { ClipBoardManager.clipboard = clipboard; ClipBoardManager.primary = primary; - ClipBoardManager.importFormatReader = importFormatReader; - this.preferencesService = preferencesService; } @@ -103,6 +87,10 @@ public static String getContents() { return result; } + public Optional getBibTeXEntriesFromClipbaord() { + return Optional.ofNullable(clipboard.getContent(DragAndDropDataFormats.ENTRIES)).map(String.class::cast); + } + /** * Get the String residing on the primary clipboard (if it exists). * @@ -167,71 +155,4 @@ public void setContent(List entries) throws IOException { clipboard.setContent(content); setPrimaryClipboardContent(content); } - - public List extractData() { - Object entries = clipboard.getContent(DragAndDropDataFormats.ENTRIES); - - if (entries == null) { - return handleStringData(clipboard.getString()); - } - return handleBibTeXData((String) entries); - } - - private List handleBibTeXData(String entries) { - BibtexParser parser = new BibtexParser(preferencesService.getImportFormatPreferences(), Globals.getFileUpdateMonitor()); - try { - return parser.parseEntries(new ByteArrayInputStream(entries.getBytes(StandardCharsets.UTF_8))); - } catch (ParseException ex) { - LOGGER.error("Could not paste", ex); - return Collections.emptyList(); - } - } - - private List handleStringData(String data) { - if ((data == null) || data.isEmpty()) { - return Collections.emptyList(); - } - - Optional doi = DOI.parse(data); - if (doi.isPresent()) { - return fetchByDOI(doi.get()); - } - Optional arXiv = ArXivIdentifier.parse(data); - if (arXiv.isPresent()) { - return fetchByArXiv(arXiv.get()); - } - - return tryImportFormats(data); - } - - private List tryImportFormats(String data) { - try { - UnknownFormatImport unknownFormatImport = importFormatReader.importUnknownFormat(data); - return unknownFormatImport.parserResult.getDatabase().getEntries(); - } catch (ImportException ignored) { - return Collections.emptyList(); - } - } - - private List fetchByDOI(DOI doi) { - LOGGER.info("Found DOI in clipboard"); - try { - Optional entry = new DoiFetcher(preferencesService.getImportFormatPreferences()).performSearchById(doi.getDOI()); - return OptionalUtil.toList(entry); - } catch (FetcherException ex) { - LOGGER.error("Error while fetching", ex); - return Collections.emptyList(); - } - } - - private List fetchByArXiv(ArXivIdentifier arXivIdentifier) { - LOGGER.info("Found arxiv identifier in clipboard"); - try { - Optional entry = new ArXiv(preferencesService.getImportFormatPreferences()).performSearchById(arXivIdentifier.getNormalizedWithoutVersion()); - return OptionalUtil.toList(entry); - } catch (FetcherException ex) { - LOGGER.error("Error while fetching", ex); - return Collections.emptyList(); - } - } } diff --git a/src/main/java/org/jabref/gui/DefaultInjector.java b/src/main/java/org/jabref/gui/DefaultInjector.java index d198dbe1136..5318c3b95dc 100644 --- a/src/main/java/org/jabref/gui/DefaultInjector.java +++ b/src/main/java/org/jabref/gui/DefaultInjector.java @@ -7,6 +7,7 @@ import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.theme.ThemeManager; import org.jabref.gui.util.TaskExecutor; +import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.protectedterms.ProtectedTermsLoader; import org.jabref.model.entry.BibEntryTypesManager; @@ -52,6 +53,8 @@ private static Object createDependency(Class clazz) { return Globals.undoManager; } else if (clazz == BibEntryTypesManager.class) { return Globals.entryTypesManager; + } else if (clazz == ImportFormatReader.class) { + return Globals.IMPORT_FORMAT_READER; } else { try { return clazz.newInstance(); diff --git a/src/main/java/org/jabref/gui/EntryTypeView.java b/src/main/java/org/jabref/gui/EntryTypeView.java index 165e1d7a380..7db73467529 100644 --- a/src/main/java/org/jabref/gui/EntryTypeView.java +++ b/src/main/java/org/jabref/gui/EntryTypeView.java @@ -24,6 +24,7 @@ import org.jabref.gui.util.IconValidationDecorator; import org.jabref.gui.util.ViewModelListCellFactory; import org.jabref.logic.importer.IdBasedFetcher; +import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.importer.WebFetcher; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseMode; @@ -48,6 +49,7 @@ public class EntryTypeView extends BaseDialog { @Inject StateManager stateManager; + @Inject ImportFormatReader importFormatReader; @FXML private ButtonType generateButton; @FXML private TextField idTextField; @@ -119,7 +121,7 @@ private void addEntriesToPane(FlowPane pane, Collection @FXML public void initialize() { visualizer.setDecoration(new IconValidationDecorator()); - viewModel = new EntryTypeViewModel(preferencesService, libraryTab, dialogService, stateManager); + viewModel = new EntryTypeViewModel(preferencesService, libraryTab, dialogService, stateManager, importFormatReader); idBasedFetchers.itemsProperty().bind(viewModel.fetcherItemsProperty()); idTextField.textProperty().bindBidirectional(viewModel.idTextProperty()); diff --git a/src/main/java/org/jabref/gui/EntryTypeViewModel.java b/src/main/java/org/jabref/gui/EntryTypeViewModel.java index 16782b19c00..4e18e4d87c0 100644 --- a/src/main/java/org/jabref/gui/EntryTypeViewModel.java +++ b/src/main/java/org/jabref/gui/EntryTypeViewModel.java @@ -17,8 +17,11 @@ import org.jabref.gui.externalfiles.ImportHandler; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.importer.NewEntryAction; +import org.jabref.logic.importer.FetcherClientException; import org.jabref.logic.importer.FetcherException; +import org.jabref.logic.importer.FetcherServerException; import org.jabref.logic.importer.IdBasedFetcher; +import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.importer.WebFetchers; import org.jabref.logic.importer.fetcher.DoiFetcher; import org.jabref.logic.l10n.Localization; @@ -50,15 +53,19 @@ public class EntryTypeViewModel { private final DialogService dialogService; private final Validator idFieldValidator; private final StateManager stateManager; + private final ImportFormatReader importFormatReader; public EntryTypeViewModel(PreferencesService preferences, LibraryTab libraryTab, DialogService dialogService, - StateManager stateManager) { + StateManager stateManager, + ImportFormatReader importFormatReader) { this.libraryTab = libraryTab; this.preferencesService = preferences; this.dialogService = dialogService; this.stateManager = stateManager; + this.importFormatReader = importFormatReader; + fetchers.addAll(WebFetchers.getIdBasedFetchers( preferences.getImportFormatPreferences(), preferences.getImporterPreferences())); @@ -140,15 +147,18 @@ public void runFetcherWorker() { String fetcherExceptionMessage = exception.getMessage(); String fetcher = selectedItemProperty().getValue().getName(); String searchId = idText.getValue(); - if (exception instanceof FetcherException) { - dialogService.showErrorDialogAndWait(Localization.lang("Error"), Localization.lang("Error while fetching from %0", fetcher + "." + "\n" + fetcherExceptionMessage)); + + if (exception instanceof FetcherClientException) { + dialogService.showInformationDialogAndWait(Localization.lang("Failed to import by ID"), Localization.lang("Bibliographic data not found. Cause is likely the client side. Please check connection and identifier for correctness.") + "\n" + fetcherExceptionMessage); + } else if (exception instanceof FetcherServerException) { + dialogService.showInformationDialogAndWait(Localization.lang("Failed to import by ID"), Localization.lang("Bibliographic data not found. Cause is likely the server side. Please try agan later.") + "\n" + fetcherExceptionMessage); } else { - dialogService.showErrorDialogAndWait(Localization.lang("No files found.", Localization.lang("Fetcher '%0' did not find an entry for id '%1'.", fetcher, searchId) + "\n" + fetcherExceptionMessage)); + dialogService.showInformationDialogAndWait(Localization.lang("Failed to import by ID"), Localization.lang("Error message %0", fetcherExceptionMessage)); } + LOGGER.error(String.format("Exception during fetching when using fetcher '%s' with entry id '%s'.", searchId, fetcher), exception); searchingProperty.set(false); - fetcherWorker = new FetcherWorker(); }); @@ -164,7 +174,8 @@ public void runFetcherWorker() { Globals.getFileUpdateMonitor(), libraryTab.getUndoManager(), stateManager, - dialogService); + dialogService, + importFormatReader); handler.importEntryWithDuplicateCheck(libraryTab.getBibDatabaseContext(), entry); searchSuccesfulProperty.set(true); @@ -177,7 +188,7 @@ public void runFetcherWorker() { String searchId = idText.getValue(); // When DOI ID is not found, allow the user to either return to the dialog or add entry manually - boolean addEntryFlag = dialogService.showConfirmationDialogAndWait(Localization.lang("DOI not found"), + boolean addEntryFlag = dialogService.showConfirmationDialogAndWait(Localization.lang("Identifier not found"), Localization.lang("Fetcher '%0' did not find an entry for id '%1'.", fetcher, searchId), Localization.lang("Add entry manually"), Localization.lang("Return to dialog")); diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 834f7beb325..440dc84dec6 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -127,6 +127,7 @@ import org.jabref.logic.help.HelpFile; import org.jabref.logic.importer.IdFetcher; import org.jabref.logic.importer.ImportCleanup; +import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.importer.WebFetchers; import org.jabref.logic.l10n.Localization; @@ -183,6 +184,7 @@ public class JabRefFrame extends BorderPane { private Subscription dividerSubscription; private final TaskExecutor taskExecutor; + private final ImportFormatReader importFormatReader; public JabRefFrame(Stage mainStage) { this.mainStage = mainStage; @@ -194,6 +196,7 @@ public JabRefFrame(Stage mainStage) { this.globalSearchBar = new GlobalSearchBar(this, stateManager, prefs, undoManager); this.fileHistory = new FileHistoryMenu(prefs, dialogService, getOpenDatabaseAction()); this.taskExecutor = Globals.TASK_EXECUTOR; + this.importFormatReader = Globals.IMPORT_FORMAT_READER; this.setOnKeyTyped(key -> { if (this.fileHistory.isShowing()) { if (this.fileHistory.openFileByKey(key)) { @@ -1140,7 +1143,7 @@ private void trackOpenNewDatabase(LibraryTab libraryTab) { public LibraryTab addTab(BibDatabaseContext databaseContext, boolean raisePanel) { Objects.requireNonNull(databaseContext); - LibraryTab libraryTab = new LibraryTab(this, prefs, stateManager, themeManager, databaseContext, ExternalFileTypes.getInstance()); + LibraryTab libraryTab = new LibraryTab(this, prefs, stateManager, themeManager, databaseContext, ExternalFileTypes.getInstance(), importFormatReader); addTab(libraryTab, raisePanel); return libraryTab; } diff --git a/src/main/java/org/jabref/gui/JabRefMain.java b/src/main/java/org/jabref/gui/JabRefMain.java index 32de8b6f0d9..e26b1286c9b 100644 --- a/src/main/java/org/jabref/gui/JabRefMain.java +++ b/src/main/java/org/jabref/gui/JabRefMain.java @@ -163,7 +163,7 @@ private static void applyPreferences(PreferencesService preferences) { // Build list of Import and Export formats Globals.IMPORT_FORMAT_READER.resetImportFormats(preferences.getImporterPreferences(), - preferences.getGeneralPreferences(), preferences.getImportFormatPreferences(), + preferences.getImportFormatPreferences(), preferences.getXmpPreferences(), Globals.getFileUpdateMonitor()); Globals.entryTypesManager.addCustomOrModifiedTypes(preferences.getBibEntryTypes(BibDatabaseMode.BIBTEX), preferences.getBibEntryTypes(BibDatabaseMode.BIBLATEX)); diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 139cdf31fd0..f037135f6d9 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -44,6 +44,7 @@ import org.jabref.logic.autosaveandbackup.AutosaveManager; import org.jabref.logic.autosaveandbackup.BackupManager; import org.jabref.logic.citationstyle.CitationStyleCache; +import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.importer.util.FileFieldParser; import org.jabref.logic.l10n.Localization; @@ -114,13 +115,15 @@ public class LibraryTab extends Tab { private BackgroundTask dataLoadingTask = BackgroundTask.wrap(() -> null); private final IndexingTaskManager indexingTaskManager = new IndexingTaskManager(Globals.TASK_EXECUTOR); + private final ImportFormatReader importFormatReader; public LibraryTab(JabRefFrame frame, PreferencesService preferencesService, StateManager stateManager, ThemeManager themeManager, BibDatabaseContext bibDatabaseContext, - ExternalFileTypes externalFileTypes) { + ExternalFileTypes externalFileTypes, + ImportFormatReader importFormatReader) { this.frame = Objects.requireNonNull(frame); this.bibDatabaseContext = Objects.requireNonNull(bibDatabaseContext); this.externalFileTypes = Objects.requireNonNull(externalFileTypes); @@ -129,6 +132,7 @@ public LibraryTab(JabRefFrame frame, this.preferencesService = Objects.requireNonNull(preferencesService); this.stateManager = Objects.requireNonNull(stateManager); this.themeManager = Objects.requireNonNull(themeManager); + this.importFormatReader = importFormatReader; bibDatabaseContext.getDatabase().registerListener(this); bibDatabaseContext.getMetaData().registerListener(this); @@ -485,7 +489,9 @@ private void createMainTable() { dialogService, stateManager, externalFileTypes, - Globals.getKeyPrefs()); + Globals.getKeyPrefs(), + Globals.getClipboardManager(), + Globals.IMPORT_FORMAT_READER); // Add the listener that binds selection to state manager (TODO: should be replaced by proper JavaFX binding as soon as table is implemented in JavaFX) mainTable.addSelectionListener(listEvent -> stateManager.setSelectedEntries(mainTable.getSelectedEntries())); @@ -798,11 +804,11 @@ public void resetChangedProperties() { } public static class Factory { - public LibraryTab createLibraryTab(JabRefFrame frame, PreferencesService preferencesService, StateManager stateManager, ThemeManager themeManager, Path file, BackgroundTask dataLoadingTask) { + public LibraryTab createLibraryTab(JabRefFrame frame, PreferencesService preferencesService, StateManager stateManager, ThemeManager themeManager, Path file, BackgroundTask dataLoadingTask, ImportFormatReader importFormatReader) { BibDatabaseContext context = new BibDatabaseContext(); context.setDatabasePath(file); - LibraryTab newTab = new LibraryTab(frame, preferencesService, stateManager, themeManager, context, ExternalFileTypes.getInstance()); + LibraryTab newTab = new LibraryTab(frame, preferencesService, stateManager, themeManager, context, ExternalFileTypes.getInstance(), importFormatReader); newTab.setDataLoadingTask(dataLoadingTask); dataLoadingTask.onRunning(newTab::onDatabaseLoadingStarted) @@ -938,7 +944,7 @@ public void notify(Node graphic, String text, List actions, Duration dur this.setText(text); this.getActions().setAll(actions); this.show(); - if (duration != null && !duration.equals(Duration.ZERO)) { + if ((duration != null) && !duration.equals(Duration.ZERO)) { PauseTransition delay = new PauseTransition(duration); delay.setOnFinished(e -> this.hide()); delay.play(); diff --git a/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java b/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java index 3472e69d466..8224873ad70 100644 --- a/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java +++ b/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java @@ -17,6 +17,7 @@ import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.importer.FetcherException; +import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.importer.fetcher.GrobidCitationFetcher; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; @@ -43,7 +44,8 @@ public BibtexExtractorViewModel(BibDatabaseContext bibdatabaseContext, FileUpdateMonitor fileUpdateMonitor, TaskExecutor taskExecutor, UndoManager undoManager, - StateManager stateManager) { + StateManager stateManager, + ImportFormatReader importFormatReader) { this.dialogService = dialogService; this.preferencesService = preferencesService; @@ -55,7 +57,8 @@ public BibtexExtractorViewModel(BibDatabaseContext bibdatabaseContext, fileUpdateMonitor, undoManager, stateManager, - dialogService); + dialogService, + importFormatReader); } public StringProperty inputTextProperty() { diff --git a/src/main/java/org/jabref/gui/bibtexextractor/ExtractBibtexDialog.java b/src/main/java/org/jabref/gui/bibtexextractor/ExtractBibtexDialog.java index 04affccbf1a..3a5d0ec7a3f 100644 --- a/src/main/java/org/jabref/gui/bibtexextractor/ExtractBibtexDialog.java +++ b/src/main/java/org/jabref/gui/bibtexextractor/ExtractBibtexDialog.java @@ -13,6 +13,7 @@ import org.jabref.gui.StateManager; import org.jabref.gui.util.BaseDialog; import org.jabref.gui.util.TaskExecutor; +import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.util.FileUpdateMonitor; @@ -35,6 +36,7 @@ public class ExtractBibtexDialog extends BaseDialog { @Inject private TaskExecutor taskExecutor; @Inject private UndoManager undoManager; @Inject private PreferencesService preferencesService; + @Inject private ImportFormatReader importFormatReader; public ExtractBibtexDialog() { ViewLoader.view(this) @@ -53,7 +55,7 @@ public ExtractBibtexDialog() { @FXML private void initialize() { BibDatabaseContext database = stateManager.getActiveDatabase().orElseThrow(() -> new NullPointerException("Database null")); - this.viewModel = new BibtexExtractorViewModel(database, dialogService, preferencesService, fileUpdateMonitor, taskExecutor, undoManager, stateManager); + this.viewModel = new BibtexExtractorViewModel(database, dialogService, preferencesService, fileUpdateMonitor, taskExecutor, undoManager, stateManager, importFormatReader); input.textProperty().bindBidirectional(viewModel.inputTextProperty()); } } diff --git a/src/main/java/org/jabref/gui/errorconsole/ErrorConsoleViewModel.java b/src/main/java/org/jabref/gui/errorconsole/ErrorConsoleViewModel.java index 3d2752a42ef..bd3bb87da8c 100644 --- a/src/main/java/org/jabref/gui/errorconsole/ErrorConsoleViewModel.java +++ b/src/main/java/org/jabref/gui/errorconsole/ErrorConsoleViewModel.java @@ -2,9 +2,6 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -30,8 +27,6 @@ public class ErrorConsoleViewModel extends AbstractViewModel { private static final Logger LOGGER = LoggerFactory.getLogger(ErrorConsoleViewModel.class); - private final DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); - private final Date date = new Date(); private final DialogService dialogService; private final ClipBoardManager clipBoardManager; private final BuildInfo buildInfo; diff --git a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java index 7de10cf7b35..61cc7afd2e8 100644 --- a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java +++ b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java @@ -1,6 +1,8 @@ package org.jabref.gui.externalfiles; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; @@ -22,7 +24,15 @@ import org.jabref.logic.citationkeypattern.CitationKeyGenerator; import org.jabref.logic.database.DuplicateCheck; import org.jabref.logic.externalfiles.ExternalFilesContentImporter; +import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.ImportCleanup; +import org.jabref.logic.importer.ImportException; +import org.jabref.logic.importer.ImportFormatReader; +import org.jabref.logic.importer.ImportFormatReader.UnknownFormatImport; +import org.jabref.logic.importer.ParseException; +import org.jabref.logic.importer.fetcher.ArXiv; +import org.jabref.logic.importer.fetcher.DoiFetcher; +import org.jabref.logic.importer.fileformat.BibtexParser; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.UpdateField; import org.jabref.logic.util.io.FileUtil; @@ -30,9 +40,12 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.identifier.ArXivIdentifier; +import org.jabref.model.entry.identifier.DOI; import org.jabref.model.groups.GroupEntryChanger; import org.jabref.model.groups.GroupTreeNode; import org.jabref.model.util.FileUpdateMonitor; +import org.jabref.model.util.OptionalUtil; import org.jabref.preferences.PreferencesService; import org.slf4j.Logger; @@ -49,6 +62,7 @@ public class ImportHandler { private final UndoManager undoManager; private final StateManager stateManager; private final DialogService dialogService; + private final ImportFormatReader importFormatReader; public ImportHandler(BibDatabaseContext database, ExternalFileTypes externalFileTypes, @@ -56,13 +70,15 @@ public ImportHandler(BibDatabaseContext database, FileUpdateMonitor fileupdateMonitor, UndoManager undoManager, StateManager stateManager, - DialogService dialogService) { + DialogService dialogService, + ImportFormatReader importFormatReader) { this.bibDatabaseContext = database; this.preferencesService = preferencesService; this.fileUpdateMonitor = fileupdateMonitor; this.stateManager = stateManager; this.dialogService = dialogService; + this.importFormatReader = importFormatReader; this.linker = new ExternalFilesEntryLinker(externalFileTypes, preferencesService.getFilePreferences(), database); this.contentImporter = new ExternalFilesContentImporter( @@ -245,4 +261,52 @@ private void generateKeys(List entries) { keyGenerator.generateAndSetKey(entry); } } + + public List handleBibTeXData(String entries) { + BibtexParser parser = new BibtexParser(preferencesService.getImportFormatPreferences(), Globals.getFileUpdateMonitor()); + try { + return parser.parseEntries(new ByteArrayInputStream(entries.getBytes(StandardCharsets.UTF_8))); + } catch (ParseException ex) { + LOGGER.error("Could not paste", ex); + return Collections.emptyList(); + } + } + + public List handleStringData(String data) throws FetcherException { + if ((data == null) || data.isEmpty()) { + return Collections.emptyList(); + } + + Optional doi = DOI.parse(data); + if (doi.isPresent()) { + return fetchByDOI(doi.get()); + } + Optional arXiv = ArXivIdentifier.parse(data); + if (arXiv.isPresent()) { + return fetchByArXiv(arXiv.get()); + } + + return tryImportFormats(data); + } + + private List tryImportFormats(String data) { + try { + UnknownFormatImport unknownFormatImport = importFormatReader.importUnknownFormat(data); + return unknownFormatImport.parserResult.getDatabase().getEntries(); + } catch (ImportException ignored) { + return Collections.emptyList(); + } + } + + private List fetchByDOI(DOI doi) throws FetcherException { + LOGGER.info("Found DOI in clipboard"); + Optional entry = new DoiFetcher(preferencesService.getImportFormatPreferences()).performSearchById(doi.getDOI()); + return OptionalUtil.toList(entry); + } + + private List fetchByArXiv(ArXivIdentifier arXivIdentifier) throws FetcherException { + LOGGER.info("Found arxiv identifier in clipboard"); + Optional entry = new ArXiv(preferencesService.getImportFormatPreferences()).performSearchById(arXivIdentifier.getNormalizedWithoutVersion()); + return OptionalUtil.toList(entry); + } } diff --git a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java index 2d1af899d12..0805c8a1f22 100644 --- a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java +++ b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java @@ -42,6 +42,7 @@ import org.jabref.gui.util.ValueTableCellFactory; import org.jabref.gui.util.ViewModelListCellFactory; import org.jabref.gui.util.ViewModelTreeCellFactory; +import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.l10n.Localization; import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; @@ -81,6 +82,7 @@ public class UnlinkedFilesDialogView extends BaseDialog { @Inject private TaskExecutor taskExecutor; @Inject private FileUpdateMonitor fileUpdateMonitor; @Inject private ThemeManager themeManager; + @Inject private ImportFormatReader importFormatReader; private final ControlsFxVisualizer validationVisualizer; private UnlinkedFilesDialogViewModel viewModel; @@ -106,7 +108,7 @@ public UnlinkedFilesDialogView() { @FXML private void initialize() { - viewModel = new UnlinkedFilesDialogViewModel(dialogService, ExternalFileTypes.getInstance(), undoManager, fileUpdateMonitor, preferencesService, stateManager, taskExecutor); + viewModel = new UnlinkedFilesDialogViewModel(dialogService, ExternalFileTypes.getInstance(), undoManager, fileUpdateMonitor, preferencesService, stateManager, taskExecutor, importFormatReader); progressDisplay.progressProperty().bind(viewModel.progressValueProperty()); progressText.textProperty().bind(viewModel.progressTextProperty()); diff --git a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java index 7d19b2bdc98..fd942a61508 100644 --- a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java +++ b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java @@ -36,6 +36,7 @@ import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.gui.util.FileNodeViewModel; import org.jabref.gui.util.TaskExecutor; +import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.StandardFileType; import org.jabref.model.database.BibDatabaseContext; @@ -86,7 +87,8 @@ public UnlinkedFilesDialogViewModel(DialogService dialogService, FileUpdateMonitor fileUpdateMonitor, PreferencesService preferences, StateManager stateManager, - TaskExecutor taskExecutor) { + TaskExecutor taskExecutor, + ImportFormatReader importFormatReader) { this.preferences = preferences; this.dialogService = dialogService; this.taskExecutor = taskExecutor; @@ -98,7 +100,8 @@ public UnlinkedFilesDialogViewModel(DialogService dialogService, fileUpdateMonitor, undoManager, stateManager, - dialogService); + dialogService, + importFormatReader); this.fileFilterList = FXCollections.observableArrayList( new FileExtensionViewModel(StandardFileType.ANY_FILE, externalFileTypes), diff --git a/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java index 04025e60be7..5d69547367d 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java @@ -15,6 +15,8 @@ import org.jabref.gui.mergeentries.FetchAndMergeEntry; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.TaskExecutor; +import org.jabref.logic.importer.FetcherClientException; +import org.jabref.logic.importer.FetcherServerException; import org.jabref.logic.importer.WebFetchers; import org.jabref.logic.importer.util.IdentifierParser; import org.jabref.logic.integrity.FieldCheckers; @@ -27,8 +29,13 @@ import org.jabref.preferences.PreferencesService; import com.tobiasdiez.easybind.EasyBind; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class IdentifierEditorViewModel extends AbstractEditorViewModel { + + private static final Logger LOGGER = LoggerFactory.getLogger(IdentifierEditorViewModel.class); + private final BooleanProperty validIdentifierIsNotPresent = new SimpleBooleanProperty(true); private final BooleanProperty identifierLookupInProgress = new SimpleBooleanProperty(false); private final BooleanProperty idFetcherAvailable = new SimpleBooleanProperty(true); @@ -119,8 +126,19 @@ public void lookupIdentifier(BibEntry entry) { dialogService.notify(Localization.lang("No %0 found", field.getDisplayName())); } }) - .onFailure(dialogService::showErrorDialogAndWait) - .executeWith(taskExecutor); + .onFailure(exception -> { + LOGGER.error("Error while fetching bibliographic information", exception); + if (exception instanceof FetcherClientException) { + dialogService.showInformationDialogAndWait(Localization.lang("Look up %0", idFetcher.getName()), Localization.lang("No data was found for the identifier")); + } else if (exception instanceof FetcherServerException) { + dialogService.showInformationDialogAndWait(Localization.lang("Look up %0", idFetcher.getName()), Localization.lang("Server not available")); + } else if (exception.getCause() != null) { + dialogService.showWarningDialogAndWait(Localization.lang("Look up %0", idFetcher.getName()), Localization.lang("Error occured %0", exception.getCause().getMessage())); + } else { + dialogService.showWarningDialogAndWait(Localization.lang("Look up %0", idFetcher.getName()), Localization.lang("Error occured %0", exception.getCause().getMessage())); + } + }) + .executeWith(taskExecutor); }); } } diff --git a/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java b/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java index 4c5c8a5747f..619ec5a8e35 100644 --- a/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java +++ b/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java @@ -11,9 +11,10 @@ import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.TaskExecutor; -import org.jabref.logic.JabRefException; import org.jabref.logic.importer.CompositeIdFetcher; +import org.jabref.logic.importer.FetcherClientException; import org.jabref.logic.importer.FetcherException; +import org.jabref.logic.importer.FetcherServerException; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.types.StandardEntryType; @@ -47,11 +48,17 @@ public void execute() { backgroundTask.titleProperty().set(Localization.lang("Import by ID")); backgroundTask.showToUser(true); backgroundTask.onRunning(() -> dialogService.notify("%s".formatted(backgroundTask.messageProperty().get()))); - backgroundTask.onFailure((e) -> { - // When unable to import by ID, present the user options to cancel or add entry manually - boolean addEntryFlag = dialogService.showConfirmationDialogAndWait(Localization.lang("Failed to import by ID"), - e.getMessage(), - Localization.lang("Add entry manually")); + backgroundTask.onFailure((exception) -> { + String fetcherExceptionMessage = exception.getMessage(); + + boolean addEntryFlag; + if (exception instanceof FetcherClientException) { + addEntryFlag = dialogService.showConfirmationDialogAndWait(Localization.lang("Failed to import by ID"), Localization.lang("Bibliographic data not found. Cause is likely the client side. Please check connection and identifier for correctness.") + "\n" + fetcherExceptionMessage, Localization.lang("Add entry manually")); + } else if (exception instanceof FetcherServerException) { + addEntryFlag = dialogService.showConfirmationDialogAndWait(Localization.lang("Failed to import by ID"), Localization.lang("Bibliographic data not found. Cause is likely the server side. Please try agan later.") + "\n" + fetcherExceptionMessage, Localization.lang("Add entry manually")); + } else { + addEntryFlag = dialogService.showConfirmationDialogAndWait(Localization.lang("Failed to import by ID"), Localization.lang("Error message %0", fetcherExceptionMessage), Localization.lang("Add entry manually")); + } if (addEntryFlag) { // add entry manually new NewEntryAction(libraryTab.frame(), StandardEntryType.Article, dialogService, @@ -62,7 +69,7 @@ public void execute() { Optional result = bibEntry; if (result.isPresent()) { final BibEntry entry = result.get(); - ImportHandler handler = new ImportHandler(libraryTab.getBibDatabaseContext(), ExternalFileTypes.getInstance(), preferencesService, Globals.getFileUpdateMonitor(), libraryTab.getUndoManager(), stateManager, dialogService); + ImportHandler handler = new ImportHandler(libraryTab.getBibDatabaseContext(), ExternalFileTypes.getInstance(), preferencesService, Globals.getFileUpdateMonitor(), libraryTab.getUndoManager(), stateManager, dialogService, null); handler.importEntryWithDuplicateCheck(libraryTab.getBibDatabaseContext(), entry); } else { dialogService.notify("No entry found or import canceled"); @@ -76,17 +83,12 @@ public void execute() { private BackgroundTask> searchAndImportEntryInBackground() { return new BackgroundTask<>() { @Override - protected Optional call() throws JabRefException { + protected Optional call() throws FetcherException { if (isCanceled()) { return Optional.empty(); } - updateMessage(Localization.lang("Searching...")); - try { - return new CompositeIdFetcher(preferencesService.getImportFormatPreferences()).performSearchById(identifier); - } catch (FetcherException fetcherException) { - throw new JabRefException("Fetcher error: %s".formatted(fetcherException.getMessage())); - } + return new CompositeIdFetcher(preferencesService.getImportFormatPreferences()).performSearchById(identifier); } }; } diff --git a/src/main/java/org/jabref/gui/importer/ImportAction.java b/src/main/java/org/jabref/gui/importer/ImportAction.java index 37566e8dc83..3b08767886d 100644 --- a/src/main/java/org/jabref/gui/importer/ImportAction.java +++ b/src/main/java/org/jabref/gui/importer/ImportAction.java @@ -122,7 +122,7 @@ private List doImport(List files) // Unknown format: DefaultTaskExecutor.runAndWaitInJavaFXThread(() -> { if (fileIsPdf(filename) && GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(frame.getDialogService(), prefs.getImporterPreferences())) { - Globals.IMPORT_FORMAT_READER.resetImportFormats(prefs.getImporterPreferences(), prefs.getGeneralPreferences(), prefs.getImportFormatPreferences(), prefs.getXmpPreferences(), Globals.getFileUpdateMonitor()); + Globals.IMPORT_FORMAT_READER.resetImportFormats(prefs.getImporterPreferences(), prefs.getImportFormatPreferences(), prefs.getXmpPreferences(), Globals.getFileUpdateMonitor()); } frame.getDialogService().notify(Localization.lang("Importing in unknown format") + "..."); }); @@ -130,8 +130,8 @@ private List doImport(List files) imports.add(Globals.IMPORT_FORMAT_READER.importUnknownFormat(filename, Globals.getFileUpdateMonitor())); } else { DefaultTaskExecutor.runAndWaitInJavaFXThread(() -> { - if (importer.get() instanceof PdfGrobidImporter || importer.get() instanceof PdfMergeMetadataImporter && GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(frame.getDialogService(), prefs.getImporterPreferences())) { - Globals.IMPORT_FORMAT_READER.resetImportFormats(prefs.getImporterPreferences(), prefs.getGeneralPreferences(), prefs.getImportFormatPreferences(), prefs.getXmpPreferences(), Globals.getFileUpdateMonitor()); + if ((importer.get() instanceof PdfGrobidImporter) || ((importer.get() instanceof PdfMergeMetadataImporter) && GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(frame.getDialogService(), prefs.getImporterPreferences()))) { + Globals.IMPORT_FORMAT_READER.resetImportFormats(prefs.getImporterPreferences(), prefs.getImportFormatPreferences(), prefs.getXmpPreferences(), Globals.getFileUpdateMonitor()); } frame.getDialogService().notify(Localization.lang("Importing in %0 format", importer.get().getName()) + "..."); }); diff --git a/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java b/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java index b139a4573c4..877a677234c 100644 --- a/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java +++ b/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java @@ -12,6 +12,7 @@ import org.jabref.gui.AbstractViewModel; import org.jabref.gui.DialogService; +import org.jabref.gui.Globals; import org.jabref.gui.JabRefGUI; import org.jabref.gui.StateManager; import org.jabref.gui.duplicationFinder.DuplicateResolverDialog; @@ -170,7 +171,8 @@ private void buildImportHandlerThenImportEntries(List entriesToImport) fileUpdateMonitor, undoManager, stateManager, - dialogService); + dialogService, + Globals.IMPORT_FORMAT_READER); importHandler.importEntries(entriesToImport); dialogService.notify(Localization.lang("Number of entries successfully imported") + ": " + entriesToImport.size()); } diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java index 02c38ad9a34..052dba2ccac 100644 --- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java +++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java @@ -180,7 +180,7 @@ private void openTheFile(Path file, boolean raisePanel) { BackgroundTask backgroundTask = BackgroundTask.wrap(() -> loadDatabase(file)); LibraryTab.Factory libraryTabFactory = new LibraryTab.Factory(); - LibraryTab newTab = libraryTabFactory.createLibraryTab(frame, preferencesService, stateManager, themeManager, file, backgroundTask); + LibraryTab newTab = libraryTabFactory.createLibraryTab(frame, preferencesService, stateManager, themeManager, file, backgroundTask, Globals.IMPORT_FORMAT_READER); backgroundTask.onFinished(() -> trackOpenNewDatabase(newTab)); } diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index 32a80afb754..d37f3bb09c4 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -24,6 +25,7 @@ import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; +import org.jabref.gui.ClipBoardManager; import org.jabref.gui.DialogService; import org.jabref.gui.DragAndDropDataFormats; import org.jabref.gui.Globals; @@ -41,6 +43,10 @@ import org.jabref.gui.util.CustomLocalDragboard; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.ViewModelTableRowFactory; +import org.jabref.logic.importer.FetcherClientException; +import org.jabref.logic.importer.FetcherException; +import org.jabref.logic.importer.FetcherServerException; +import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.event.EntriesAddedEvent; @@ -63,7 +69,7 @@ public class MainTable extends TableView { private final ImportHandler importHandler; private final CustomLocalDragboard localDragboard; - + private final ClipBoardManager clipBoardManager; private long lastKeyPressTime; private String columnSearchTerm; @@ -74,7 +80,9 @@ public MainTable(MainTableDataModel model, DialogService dialogService, StateManager stateManager, ExternalFileTypes externalFileTypes, - KeyBindingRepository keyBindingRepository) { + KeyBindingRepository keyBindingRepository, + ClipBoardManager clipBoardManager, + ImportFormatReader importFormatReader) { super(); this.libraryTab = libraryTab; @@ -82,6 +90,7 @@ public MainTable(MainTableDataModel model, this.stateManager = stateManager; this.database = Objects.requireNonNull(database); this.model = model; + this.clipBoardManager = clipBoardManager; UndoManager undoManager = libraryTab.getUndoManager(); MainTablePreferences mainTablePreferences = preferencesService.getMainTablePreferences(); @@ -91,7 +100,8 @@ public MainTable(MainTableDataModel model, Globals.getFileUpdateMonitor(), undoManager, stateManager, - dialogService); + dialogService, + importFormatReader); localDragboard = stateManager.getLocalDragboard(); @@ -304,8 +314,10 @@ private void clearAndSelectLast() { } public void paste() { - // Find entries in clipboard - List entriesToAdd = Globals.getClipboardManager().extractData(); + List entriesToAdd = new ArrayList<>(); + entriesToAdd = this.clipBoardManager.getBibTeXEntriesFromClipbaord() + .map(importHandler::handleBibTeXData) + .orElseGet(this::handleNonBibteXStringData); for (BibEntry entry : entriesToAdd) { importHandler.importEntryWithDuplicateCheck(database, entry); @@ -315,6 +327,23 @@ public void paste() { } } + private List handleNonBibteXStringData() { + String data = this.clipBoardManager.getContents(); + List entries = new ArrayList<>(); + try { + entries = this.importHandler.handleStringData(data); + } catch (FetcherException exception) { + if (exception instanceof FetcherClientException) { + dialogService.showInformationDialogAndWait(Localization.lang("Look up identifier"), Localization.lang("No data was found for the identifier")); + } else if (exception instanceof FetcherServerException) { + dialogService.showInformationDialogAndWait(Localization.lang("Look up identifier"), Localization.lang("Server not available")); + } else { + dialogService.showErrorDialogAndWait(exception); + } + } + return entries; + } + public void dropEntry(List entriesToAdd) { for (BibEntry entry : entriesToAdd) { importHandler.importEntryWithDuplicateCheck(database, (BibEntry) entry.clone()); diff --git a/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java b/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java index 7c1a9e127ba..ed7220b532e 100644 --- a/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java +++ b/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java @@ -17,6 +17,8 @@ import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.importer.EntryBasedFetcher; +import org.jabref.logic.importer.FetcherClientException; +import org.jabref.logic.importer.FetcherServerException; import org.jabref.logic.importer.IdBasedFetcher; import org.jabref.logic.importer.ImportCleanup; import org.jabref.logic.importer.WebFetcher; @@ -76,7 +78,13 @@ public void fetchAndMerge(BibEntry entry, List fields) { }) .onFailure(exception -> { LOGGER.error("Error while fetching bibliographic information", exception); - dialogService.showErrorDialogAndWait(exception); + if (exception instanceof FetcherClientException) { + dialogService.showInformationDialogAndWait(Localization.lang("Fetching information using %0", fetcher.get().getName()), Localization.lang("No data was found for the identifier")); + } else if (exception instanceof FetcherServerException) { + dialogService.showInformationDialogAndWait(Localization.lang("Fetching information using %0", fetcher.get().getName()), Localization.lang("Server not available")); + } else { + dialogService.showInformationDialogAndWait(Localization.lang("Fetching information using %0", fetcher.get().getName()), Localization.lang("Error occured %0", exception.getMessage())); + } }) .executeWith(Globals.TASK_EXECUTOR); } @@ -158,7 +166,7 @@ public void fetchAndMerge(BibEntry entry, EntryBasedFetcher fetcher) { } }) .onFailure(exception -> { - LOGGER.error("Error while fetching entry with " + fetcher.getName(), exception); + LOGGER.error("Error while fetching entry with {} ", fetcher.getName(), exception); dialogService.showErrorDialogAndWait(Localization.lang("Error while fetching from %0", fetcher.getName()), exception); }) .executeWith(taskExecutor); diff --git a/src/main/java/org/jabref/gui/preferences/customimporter/CustomImporterTabViewModel.java b/src/main/java/org/jabref/gui/preferences/customimporter/CustomImporterTabViewModel.java index 218c76d940a..08012693858 100644 --- a/src/main/java/org/jabref/gui/preferences/customimporter/CustomImporterTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/customimporter/CustomImporterTabViewModel.java @@ -55,7 +55,6 @@ public void storeSettings() { .collect(Collectors.toSet())); Globals.IMPORT_FORMAT_READER.resetImportFormats( preferences.getImporterPreferences(), - preferences.getGeneralPreferences(), preferences.getImportFormatPreferences(), preferences.getXmpPreferences(), Globals.getFileUpdateMonitor()); diff --git a/src/main/java/org/jabref/logic/importer/FetcherClientException.java b/src/main/java/org/jabref/logic/importer/FetcherClientException.java new file mode 100644 index 00000000000..8de9d0d6114 --- /dev/null +++ b/src/main/java/org/jabref/logic/importer/FetcherClientException.java @@ -0,0 +1,19 @@ +package org.jabref.logic.importer; + +/** + * Should be thrown when you encounter a http status code error >= 400 and < 500 + */ +public class FetcherClientException extends FetcherException { + + public FetcherClientException(String errorMessage, Throwable cause) { + super(errorMessage, cause); + } + + public FetcherClientException(String errorMessage) { + super(errorMessage); + } + + public FetcherClientException(String errorMessage, String localizedMessage, Throwable cause) { + super(errorMessage, localizedMessage, cause); + } +} diff --git a/src/main/java/org/jabref/logic/importer/FetcherServerException.java b/src/main/java/org/jabref/logic/importer/FetcherServerException.java new file mode 100644 index 00000000000..537d71ff530 --- /dev/null +++ b/src/main/java/org/jabref/logic/importer/FetcherServerException.java @@ -0,0 +1,18 @@ +package org.jabref.logic.importer; +/** + * Should be thrown when you encounter a http status code error >= 500 + */ +public class FetcherServerException extends FetcherException { + + public FetcherServerException(String errorMessage, Throwable cause) { + super(errorMessage, cause); + } + + public FetcherServerException(String errorMessage) { + super(errorMessage); + } + + public FetcherServerException(String errorMessage, String localizedMessage, Throwable cause) { + super(errorMessage, localizedMessage, cause); + } +} diff --git a/src/main/java/org/jabref/logic/importer/IdBasedParserFetcher.java b/src/main/java/org/jabref/logic/importer/IdBasedParserFetcher.java index 6be4a8dddfd..e348b69b8d8 100644 --- a/src/main/java/org/jabref/logic/importer/IdBasedParserFetcher.java +++ b/src/main/java/org/jabref/logic/importer/IdBasedParserFetcher.java @@ -81,7 +81,10 @@ default Optional performSearchById(String identifier) throws FetcherEx } catch (URISyntaxException e) { throw new FetcherException("Search URI is malformed", e); } catch (IOException e) { - // TODO: Catch HTTP Response 401 errors and report that user has no rights to access resource. It might be that there is an UnknownHostException (eutils.ncbi.nlm.nih.gov cannot be resolved). + // check for the case where we already have a FetcherException from UrlDownload + if (e.getCause() instanceof FetcherException fe) { + throw fe; + } throw new FetcherException("A network error occurred", e); } catch (ParseException e) { throw new FetcherException("An internal parser error occurred", e); diff --git a/src/main/java/org/jabref/logic/importer/IdParserFetcher.java b/src/main/java/org/jabref/logic/importer/IdParserFetcher.java index bf4b2bb32b9..b9b5ed92e44 100644 --- a/src/main/java/org/jabref/logic/importer/IdParserFetcher.java +++ b/src/main/java/org/jabref/logic/importer/IdParserFetcher.java @@ -88,8 +88,10 @@ default Optional findIdentifier(BibEntry entry) throws FetcherException { LOGGER.debug("Id not found"); return Optional.empty(); } catch (IOException e) { - // TODO: Catch HTTP Response 401 errors and report that user has no rights to access resource - // TODO catch 503 service unavailable and alert user + // check for the case where we already have a FetcherException from UrlDownload + if (e.getCause() instanceof FetcherException fe) { + throw fe; + } throw new FetcherException("An I/O exception occurred", e); } catch (ParseException e) { throw new FetcherException("An internal parser error occurred", e); diff --git a/src/main/java/org/jabref/logic/importer/ImportFormatReader.java b/src/main/java/org/jabref/logic/importer/ImportFormatReader.java index f3b8826171d..b9b6f4b850c 100644 --- a/src/main/java/org/jabref/logic/importer/ImportFormatReader.java +++ b/src/main/java/org/jabref/logic/importer/ImportFormatReader.java @@ -39,7 +39,6 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.strings.StringUtil; import org.jabref.model.util.FileUpdateMonitor; -import org.jabref.preferences.GeneralPreferences; public class ImportFormatReader { @@ -51,11 +50,9 @@ public class ImportFormatReader { */ private final List formats = new ArrayList<>(); - private GeneralPreferences generalPreferences; private ImportFormatPreferences importFormatPreferences; - public void resetImportFormats(ImporterPreferences importerPreferences, GeneralPreferences generalPreferences, ImportFormatPreferences newImportFormatPreferences, XmpPreferences xmpPreferences, FileUpdateMonitor fileMonitor) { - this.generalPreferences = generalPreferences; + public void resetImportFormats(ImporterPreferences importerPreferences, ImportFormatPreferences newImportFormatPreferences, XmpPreferences xmpPreferences, FileUpdateMonitor fileMonitor) { this.importFormatPreferences = newImportFormatPreferences; formats.clear(); diff --git a/src/main/java/org/jabref/logic/importer/PagedSearchBasedFetcher.java b/src/main/java/org/jabref/logic/importer/PagedSearchBasedFetcher.java index 1b8d31a064b..bafae176485 100644 --- a/src/main/java/org/jabref/logic/importer/PagedSearchBasedFetcher.java +++ b/src/main/java/org/jabref/logic/importer/PagedSearchBasedFetcher.java @@ -52,6 +52,7 @@ default int getPageSize() { * @param luceneQuery the root node of the lucene query * @return a list of {@link BibEntry}, which are matched by the query (may be empty) */ + @Override default List performSearch(QueryNode luceneQuery) throws FetcherException { return new ArrayList<>(performSearchPaged(luceneQuery, 0).getContent()); } diff --git a/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java index 41afea25707..4f0927ef468 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java @@ -72,7 +72,6 @@ public Optional performSearchById(String identifier) throws FetcherExc return new Medra().performSearchById(identifier); } URL doiURL = new URL(doi.get().getURIAsASCIIString()); - // BibTeX data URLDownload download = getUrlDownload(doiURL); download.addHeader("Accept", MediaTypes.APPLICATION_BIBTEX); @@ -80,8 +79,11 @@ public Optional performSearchById(String identifier) throws FetcherExc try { bibtexString = download.asString(); } catch (IOException e) { - // an IOException will be thrown if download is unable to download from the doiURL - throw new FetcherException(Localization.lang("No DOI data exists"), e); + // an IOException with a nested FetcherException will be thrown when you encounter a 400x or 500x http status code + if (e.getCause() instanceof FetcherException fe) { + throw fe; + } + throw e; } // BibTeX entry diff --git a/src/main/java/org/jabref/logic/importer/fetcher/IsbnFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/IsbnFetcher.java index c97184bd36b..f4dedae3e7c 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/IsbnFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/IsbnFetcher.java @@ -50,13 +50,19 @@ public Optional performSearchById(String identifier) throws FetcherExc identifier = NEWLINE_SPACE_PATTERN.matcher(identifier).replaceAll(""); OpenLibraryFetcher openLibraryFetcher = new OpenLibraryFetcher(importFormatPreferences); - Optional bibEntry = openLibraryFetcher.performSearchById(identifier); - // nothing found at OpenLibrary: try ebook.de - if (!bibEntry.isPresent()) { - LOGGER.debug("No entry found at OpenLibrary; trying ebook.de"); - IsbnViaEbookDeFetcher isbnViaEbookDeFetcher = new IsbnViaEbookDeFetcher(importFormatPreferences); - bibEntry = isbnViaEbookDeFetcher.performSearchById(identifier); + Optional bibEntry = Optional.empty(); + try { + bibEntry = openLibraryFetcher.performSearchById(identifier); + } catch (FetcherException ex) { + LOGGER.debug("Got a fetcher exception for IBSN search", ex); + } finally { + // nothing found at OpenLibrary: try ebook.de + if (!bibEntry.isPresent()) { + LOGGER.debug("No entry found at OpenLibrary; trying ebook.de"); + IsbnViaEbookDeFetcher isbnViaEbookDeFetcher = new IsbnViaEbookDeFetcher(importFormatPreferences); + bibEntry = isbnViaEbookDeFetcher.performSearchById(identifier); + } } return bibEntry; diff --git a/src/main/java/org/jabref/logic/net/URLDownload.java b/src/main/java/org/jabref/logic/net/URLDownload.java index 77ac1ff3093..c499dcf3635 100644 --- a/src/main/java/org/jabref/logic/net/URLDownload.java +++ b/src/main/java/org/jabref/logic/net/URLDownload.java @@ -40,6 +40,8 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import org.jabref.logic.importer.FetcherClientException; +import org.jabref.logic.importer.FetcherServerException; import org.jabref.logic.util.io.FileUtil; import org.jabref.model.util.FileHelper; @@ -350,21 +352,23 @@ private URLConnection openConnection() throws IOException { if (connection instanceof HttpURLConnection) { // normally, 3xx is redirect int status = ((HttpURLConnection) connection).getResponseCode(); - if (status != HttpURLConnection.HTTP_OK) { - if ((status == HttpURLConnection.HTTP_MOVED_TEMP) - || (status == HttpURLConnection.HTTP_MOVED_PERM) - || (status == HttpURLConnection.HTTP_SEE_OTHER)) { - // get redirect url from "location" header field - String newUrl = connection.getHeaderField("location"); - // open the new connnection again - connection = new URLDownload(newUrl).openConnection(); - } + + if ((status == HttpURLConnection.HTTP_MOVED_TEMP) + || (status == HttpURLConnection.HTTP_MOVED_PERM) + || (status == HttpURLConnection.HTTP_SEE_OTHER)) { + // get redirect url from "location" header field + String newUrl = connection.getHeaderField("location"); + // open the new connection again + connection = new URLDownload(newUrl).openConnection(); + } + if ((status >= 400) && (status < 500)) { + throw new IOException(new FetcherClientException("Encountered HTTP Status code " + status)); + } + if (status >= 500) { + throw new IOException(new FetcherServerException("Encountered HTTP Status Code " + status)); } } - // this does network i/o: GET + read returned headers - connection.connect(); - return connection; } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 4100fc090b5..4f355612618 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -224,8 +224,6 @@ cut\ entries=cut entries cut\ entry\ %0=cut entry %0 -DOI\ not\ found=DOI not found - Library\ encoding=Library encoding Library\ properties=Library properties @@ -578,8 +576,6 @@ No\ journal\ names\ could\ be\ abbreviated.=No journal names could be abbreviate No\ journal\ names\ could\ be\ unabbreviated.=No journal names could be unabbreviated. -No\ DOI\ data\ exists=No DOI data exists - not=not not\ found=not found @@ -2489,6 +2485,17 @@ Version=Version Error\ downloading=Error downloading +No\ data\ was\ found\ for\ the\ identifier=No data was found for the identifier +Server\ not\ available=Server not available +Fetching\ information\ using\ %0=Fetching information using %0 +Look\ up\ identifier=Look up identifier + +Bibliographic\ data\ not\ found.\ Cause\ is\ likely\ the\ client\ side.\ Please\ check\ connection\ and\ identifier\ for\ correctness.=Bibliographic data not found. Cause is likely the client side. Please check connection and identifier for correctness. +Bibliographic\ data\ not\ found.\ Cause\ is\ likely\ the\ server\ side.\ Please\ try\ agan\ later.=Bibliographic data not found. Cause is likely the server side. Please try agan later. +Error\ message\ %0=Error message %0 +Identifier\ not\ found=Identifier not found + + Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=Error while writing metadata. See the error log for details. Failed\ to\ write\ metadata,\ file\ %1\ not\ found.=Failed to write metadata, file %1 not found. Success\!\ Finished\ writing\ metadata.=Success! Finished writing metadata. @@ -2518,3 +2525,5 @@ To=To (Note\:\ If\ original\ entries\ lack\ keywords\ to\ qualify\ for\ the\ new\ group\ configuration,\ confirming\ here\ will\ add\ them)=(Note: If original entries lack keywords to qualify for the new group configuration, confirming here will add them) Assign=Assign Do\ not\ assign=Do not assign + +Error\ occured\ %0=Error occured %0 diff --git a/src/test/java/org/jabref/logic/importer/ImportFormatReaderIntegrationTest.java b/src/test/java/org/jabref/logic/importer/ImportFormatReaderIntegrationTest.java index e7f45023830..ac6262322fb 100644 --- a/src/test/java/org/jabref/logic/importer/ImportFormatReaderIntegrationTest.java +++ b/src/test/java/org/jabref/logic/importer/ImportFormatReaderIntegrationTest.java @@ -30,7 +30,7 @@ void setUp() { ImportFormatPreferences importFormatPreferences = mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS); when(importFormatPreferences.getCustomImportList()).thenReturn(Set.of()); GeneralPreferences generalPreferences = mock(GeneralPreferences.class, Answers.RETURNS_DEEP_STUBS); - reader.resetImportFormats(mock(ImporterPreferences.class), generalPreferences, importFormatPreferences, mock(XmpPreferences.class), new DummyFileUpdateMonitor()); + reader.resetImportFormats(mock(ImporterPreferences.class), importFormatPreferences, mock(XmpPreferences.class), new DummyFileUpdateMonitor()); } @ParameterizedTest diff --git a/src/test/java/org/jabref/logic/importer/ImportFormatReaderTestParameterless.java b/src/test/java/org/jabref/logic/importer/ImportFormatReaderTestParameterless.java index 4f867e05141..b0eadfda8dd 100644 --- a/src/test/java/org/jabref/logic/importer/ImportFormatReaderTestParameterless.java +++ b/src/test/java/org/jabref/logic/importer/ImportFormatReaderTestParameterless.java @@ -27,7 +27,7 @@ void setUp() { GeneralPreferences generalPreferences = mock(GeneralPreferences.class, Answers.RETURNS_DEEP_STUBS); ImportFormatPreferences importFormatPreferences = mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS); when(importFormatPreferences.getCustomImportList()).thenReturn(Set.of()); - reader.resetImportFormats(mock(ImporterPreferences.class), generalPreferences, importFormatPreferences, mock(XmpPreferences.class), fileMonitor); + reader.resetImportFormats(mock(ImporterPreferences.class), importFormatPreferences, mock(XmpPreferences.class), fileMonitor); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fetcher/CompositeSearchBasedFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/CompositeSearchBasedFetcherTest.java index 8d50ea7192b..fe0db637765 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/CompositeSearchBasedFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/CompositeSearchBasedFetcherTest.java @@ -126,7 +126,7 @@ static Stream performSearchParameters() { // Only shift i at maximum to its MSB to the right for (int j = 0; Math.pow(2, j) <= i; j++) { // Add fetcher j to the list if the j-th bit of i is 1 - if ((i >> j) % 2 == 1) { + if (((i >> j) % 2) == 1) { fetchers.add(list.get(j)); } } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/CrossRefTest.java b/src/test/java/org/jabref/logic/importer/fetcher/CrossRefTest.java index 130206433ec..770290269ac 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/CrossRefTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/CrossRefTest.java @@ -4,6 +4,7 @@ import java.util.Locale; import java.util.Optional; +import org.jabref.logic.importer.FetcherClientException; import org.jabref.logic.importer.FetcherException; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; @@ -14,6 +15,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; @FetcherTest public class CrossRefTest { @@ -145,6 +147,6 @@ public void performSearchByEmptyQuery() throws Exception { */ @Test public void testPerformSearchValidReturnNothingDOI() throws FetcherException { - assertEquals(Optional.empty(), fetcher.performSearchById("10.1392/BC1.0")); + assertThrows(FetcherClientException.class, () -> fetcher.performSearchById("10.1392/BC1.0")); } } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/DoiFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/DoiFetcherTest.java index 3b081aa9c30..da157b42347 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/DoiFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/DoiFetcherTest.java @@ -19,9 +19,9 @@ @FetcherTest public class DoiFetcherTest { - private DoiFetcher fetcher = new DoiFetcher(mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS)); + private final DoiFetcher fetcher = new DoiFetcher(mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS)); - private BibEntry bibEntryBurd2011 = new BibEntry(StandardEntryType.Book) + private final BibEntry bibEntryBurd2011 = new BibEntry(StandardEntryType.Book) .withCitationKey("Burd_2011") .withField(StandardField.TITLE, "Java{\\textregistered} For Dummies{\\textregistered}") .withField(StandardField.PUBLISHER, "Wiley") @@ -29,7 +29,7 @@ public class DoiFetcherTest { .withField(StandardField.AUTHOR, "Barry Burd") .withField(StandardField.MONTH, "jul") .withField(StandardField.DOI, "10.1002/9781118257517"); - private BibEntry bibEntryDecker2007 = new BibEntry(StandardEntryType.InProceedings) + private final BibEntry bibEntryDecker2007 = new BibEntry(StandardEntryType.InProceedings) .withCitationKey("Decker_2007") .withField(StandardField.AUTHOR, "Gero Decker and Oliver Kopp and Frank Leymann and Mathias Weske") .withField(StandardField.BOOKTITLE, "{IEEE} International Conference on Web Services ({ICWS} 2007)") @@ -38,7 +38,7 @@ public class DoiFetcherTest { .withField(StandardField.TITLE, "{BPEL}4Chor: Extending {BPEL} for Modeling Choreographies") .withField(StandardField.YEAR, "2007") .withField(StandardField.DOI, "10.1109/icws.2007.59"); - private BibEntry bibEntryIannarelli2019 = new BibEntry(StandardEntryType.Article) + private final BibEntry bibEntryIannarelli2019 = new BibEntry(StandardEntryType.Article) .withField(StandardField.AUTHOR, "" + "Iannarelli Riccardo and " @@ -55,7 +55,7 @@ public class DoiFetcherTest { .withField(StandardField.JOURNAL, "Chemical Engineering Transactions") .withField(StandardField.PAGES, "871-876") .withField(StandardField.VOLUME, "77"); - private BibEntry bibEntryStenzel2020 = new BibEntry(StandardEntryType.Article) + private final BibEntry bibEntryStenzel2020 = new BibEntry(StandardEntryType.Article) .withCitationKey("Stenzel_2020") .withField(StandardField.AUTHOR, "L. Stenzel and A. L. C. Hayward and U. Schollwöck and F. Heidrich-Meisner") .withField(StandardField.JOURNAL, "Physical Review A") @@ -101,6 +101,16 @@ public void testPerformSearchInvalidDOI() { assertThrows(FetcherException.class, () -> fetcher.performSearchById("10.1002/9781118257517F")); } + @Test + public void testPerformSearchInvalidDOIClientResultsinFetcherClientException() { + assertThrows(FetcherException.class, () -> fetcher.performSearchById("10.1002/9781118257517F")); + } + + @Test + public void testPerformSearchInvalidDOIClientResultsinFetcherClientException2() { + assertThrows(FetcherException.class, () -> fetcher.performSearchById("10.1002/9781517F")); + } + @Test public void testPerformSearchNonTrimmedDOI() throws FetcherException { Optional fetchedEntry = fetcher.performSearchById("http s://doi.org/ 10.1109 /ICWS .2007.59 "); diff --git a/src/test/java/org/jabref/logic/importer/fetcher/IsbnViaEbookDeFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/IsbnViaEbookDeFetcherTest.java index bbb2742d208..b00d3523d6d 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/IsbnViaEbookDeFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/IsbnViaEbookDeFetcherTest.java @@ -2,6 +2,7 @@ import java.util.Optional; +import org.jabref.logic.importer.FetcherClientException; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.model.entry.BibEntry; @@ -15,6 +16,7 @@ import org.mockito.Answers; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; @FetcherTest @@ -77,11 +79,10 @@ public void authorsAreCorrectlyFormatted() throws Exception { /** * This test searches for a valid ISBN. See https://www.amazon.de/dp/3728128155/?tag=jabref-21 However, this ISBN is - * not available on ebook.de. The fetcher should return nothing rather than throwing an exception. + * not available on ebook.de. */ @Test public void searchForValidButNotFoundISBN() throws Exception { - Optional fetchedEntry = fetcher.performSearchById("3728128155"); - assertEquals(Optional.empty(), fetchedEntry); + assertThrows(FetcherClientException.class, ()-> fetcher.performSearchById("3728128155")); } } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/LibraryOfCongressTest.java b/src/test/java/org/jabref/logic/importer/fetcher/LibraryOfCongressTest.java index b18e2b88ba6..562ded1f3bd 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/LibraryOfCongressTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/LibraryOfCongressTest.java @@ -2,6 +2,7 @@ import java.util.Optional; +import org.jabref.logic.importer.FetcherClientException; import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; @@ -12,6 +13,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -54,6 +56,6 @@ public void performSearchByEmptyId() throws Exception { @Test public void performSearchByInvalidId() throws Exception { - assertEquals(Optional.empty(), fetcher.performSearchById("xxx")); + assertThrows(FetcherClientException.class, () -> fetcher.performSearchById("xxx")); } } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/MedlineFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/MedlineFetcherTest.java index c0e35312e49..ed156ef4c04 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/MedlineFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/MedlineFetcherTest.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Optional; +import org.jabref.logic.importer.FetcherClientException; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.field.UnknownField; @@ -14,6 +15,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @FetcherTest @@ -188,7 +190,7 @@ public void testWithLuceneQueryAuthorDateRange() throws Exception { @Test public void testInvalidSearchTerm() throws Exception { - assertEquals(Optional.empty(), fetcher.performSearchById("this.is.a.invalid.search.term.for.the.medline.fetcher")); + assertThrows(FetcherClientException.class, () -> fetcher.performSearchById("this.is.a.invalid.search.term.for.the.medline.fetcher")); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fetcher/MedraTest.java b/src/test/java/org/jabref/logic/importer/fetcher/MedraTest.java index 4f0e2f9814b..3d940312fa1 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/MedraTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/MedraTest.java @@ -3,6 +3,7 @@ import java.util.Optional; import java.util.stream.Stream; +import org.jabref.logic.importer.FetcherClientException; import org.jabref.logic.importer.FetcherException; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; @@ -15,6 +16,7 @@ import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; @FetcherTest public class MedraTest { @@ -23,9 +25,6 @@ public class MedraTest { private static Stream getDoiBibEntryPairs() { return Stream.of( - Arguments.of("10.1016/j.bjoms.2007.08.004", - Optional.empty()), - Arguments.of("10.2143/TVF.80.3.3285690", Optional.of( new BibEntry(StandardEntryType.Article) @@ -82,6 +81,11 @@ public void testPerformSearchEmptyDOI() throws FetcherException { assertEquals(Optional.empty(), fetcher.performSearchById("")); } + @Test + public void testPerformNonExistent() throws FetcherException { + assertThrows(FetcherClientException.class, () -> fetcher.performSearchById("10.1016/j.bjoms.2007.08.004")); + } + @ParameterizedTest @MethodSource("getDoiBibEntryPairs") public void testDoiBibEntryPairs(String identifier, Optional expected) throws FetcherException { diff --git a/src/test/java/org/jabref/logic/importer/fetcher/OpenLibraryFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/OpenLibraryFetcherTest.java index 93b67e37c3c..6161b3b9879 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/OpenLibraryFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/OpenLibraryFetcherTest.java @@ -2,6 +2,7 @@ import java.util.Optional; +import org.jabref.logic.importer.FetcherClientException; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.model.entry.BibEntry; @@ -13,6 +14,7 @@ import org.mockito.Answers; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; public class OpenLibraryFetcherTest extends AbstractIsbnFetcherTest { @@ -71,7 +73,6 @@ public void authorsAreCorrectlyFormatted() throws Exception { public void testIsbnNeitherAvailableOnEbookDeNorOrViaOpenLibrary() throws Exception { // In this test, the ISBN needs to be a valid (syntax+checksum) ISBN number // However, the ISBN number must not be assigned to a real book - Optional fetchedEntry = fetcher.performSearchById("9785646216541"); - assertEquals(Optional.empty(), fetchedEntry); + assertThrows(FetcherClientException.class, () -> fetcher.performSearchById("9785646216541")); } } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/RfcFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/RfcFetcherTest.java index 66f36912554..ea04ddca1f4 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/RfcFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/RfcFetcherTest.java @@ -2,6 +2,7 @@ import java.util.Optional; +import org.jabref.logic.importer.FetcherClientException; import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.InternalField; @@ -14,6 +15,7 @@ import org.mockito.Answers; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; @FetcherTest @@ -88,16 +90,16 @@ public void performSearchByIdFindsNothingWithoutIdentifier() throws Exception { @Test public void performSearchByIdFindsNothingWithValidDraftIdentifier() throws Exception { - assertEquals(Optional.empty(), fetcher.performSearchById("draft-test-draft-spec")); + assertThrows(FetcherClientException.class, () -> fetcher.performSearchById("draft-test-draft-spec")); } @Test public void performSearchByIdFindsNothingWithValidIdentifier() throws Exception { - assertEquals(Optional.empty(), fetcher.performSearchById("RFC9999")); + assertThrows(FetcherClientException.class, () -> fetcher.performSearchById("RFC9999")); } @Test public void performSearchByIdFindsNothingWithInvalidIdentifier() throws Exception { - assertEquals(Optional.empty(), fetcher.performSearchById("banana")); + assertThrows(FetcherClientException.class, () -> fetcher.performSearchById("banana")); } } diff --git a/src/test/java/org/jabref/logic/net/URLDownloadTest.java b/src/test/java/org/jabref/logic/net/URLDownloadTest.java index 84c63c6b74d..b7788d961fb 100644 --- a/src/test/java/org/jabref/logic/net/URLDownloadTest.java +++ b/src/test/java/org/jabref/logic/net/URLDownloadTest.java @@ -7,6 +7,8 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import org.jabref.logic.importer.FetcherClientException; +import org.jabref.logic.importer.FetcherServerException; import org.jabref.support.DisabledOnCIServer; import kong.unirest.UnirestException; @@ -19,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class URLDownloadTest { + private static final Logger LOGGER = LoggerFactory.getLogger(URLDownloadTest.class); @Test @@ -120,4 +123,20 @@ public void connectTimeoutIsNeverNull() throws MalformedURLException { urlDownload.setConnectTimeout(null); assertNotNull(urlDownload.getConnectTimeout(), "no null value can be set"); } + + @Test + public void test503ErrorThrowsNestedIOExceptionWithFetcherServerException() throws Exception { + URLDownload urlDownload = new URLDownload(new URL("http://httpstat.us/503")); + + Exception exception = assertThrows(IOException.class, () -> urlDownload.asString()); + assertTrue(exception.getCause() instanceof FetcherServerException); + } + + @Test + public void test429ErrorThrowsNestedIOExceptionWithFetcherServerException() throws Exception { + URLDownload urlDownload = new URLDownload(new URL("http://httpstat.us/429")); + + Exception exception = assertThrows(IOException.class, () -> urlDownload.asString()); + assertTrue(exception.getCause() instanceof FetcherClientException); + } }