From 7e69ef57ce1e1b66a2dfd44b9a5c0d43dfd41c3b Mon Sep 17 00:00:00 2001 From: Austin Schaefer Date: Wed, 18 Sep 2024 13:57:15 +0200 Subject: [PATCH 1/7] Add more OS-dependent context to panel freeze dev documentation. (#11781) After discussing, it was decided that the text section didn't precisely name how a "working password manager" could be checked. The problem is, is that this is OS dependent, and the knowledge isn't quite fully assembled yet. Therefore, a partial description was made. --- .../trouble-shooting.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/trouble-shooting.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/trouble-shooting.md index 5591392cea5..a264643f888 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/trouble-shooting.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/trouble-shooting.md @@ -88,4 +88,10 @@ java.lang.UnsupportedClassVersionError: org/javamodularity/moduleplugin/ModuleSy This is likely caused by improper integration of your OS or Desktop Environment with your password prompting program or password manager. Ensure that these are working properly, then restart your machine and attempt to run the program. -For reference, see issue [#11766](https://github.com/JabRef/jabref/issues/11766). +In an ideal scenario, a password prompt should appear when the program starts, provided the keyring your OS uses has not already been unlocked. However, the implementation details vary depending on the operating system, which makes troubleshooting more complex. + +For Windows and macOS users, specific configurations may differ based on the password management tools and settings used, so ensure your OS's password management system is properly set up and functioning. + +For Linux users, ensure that your [xdg-desktop-portal](https://wiki.archlinux.org/title/XDG_Desktop_Portal) settings refer to active and valid portal implementations installed on your system. However, there might be other factors involved, so additional research or guidance specific to your distribution may be necessary. + +For reference, see the discussion at issue [#11766](https://github.com/JabRef/jabref/issues/11766). From 88fc8465df757ad75ca5dcc26f6dfa9665ecfcde Mon Sep 17 00:00:00 2001 From: leaf-soba Date: Wed, 18 Sep 2024 20:46:42 +0800 Subject: [PATCH 2/7] minor refactor to JabRefDialogService (#11767) * minor refactor to JabRefDialogService 1. replace multiple if-else to switch case 2. remove unused generics `` 3. replace `.collect(Collectors.toList())` to `.toList()` 4. remove unnecessary braces * Update JabRefDialogService 1. remove `type` parameter in createDialogWithOptOut, since it always be `AlertType.CONFIRMATION` 2. remove `Math.min` in `shortenDialogMessage`, since it after a if compare, it means the dialogMessage.length() is always not less than JabRefDialogService.DIALOG_SIZE_LIMIT 3. refactor the switch case more. 4. fix the indent issue. * fix missing Localization.lang fix missing Localization.lang * fix missing string add missing newline character and localizedMessage * fix Localization.lang issue need to use plain string not a method result or a var to pass the unit test. --- .../org/jabref/gui/JabRefDialogService.java | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefDialogService.java b/src/main/java/org/jabref/gui/JabRefDialogService.java index c3e3ce962d9..dccdc17898d 100644 --- a/src/main/java/org/jabref/gui/JabRefDialogService.java +++ b/src/main/java/org/jabref/gui/JabRefDialogService.java @@ -6,11 +6,9 @@ import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.Consumer; -import java.util.stream.Collectors; import javafx.concurrent.Task; import javafx.geometry.Pos; @@ -99,9 +97,9 @@ private FXDialog createDialog(AlertType type, String title, String content) { return alert; } - private FXDialog createDialogWithOptOut(AlertType type, String title, String content, + private FXDialog createDialogWithOptOut(String title, String content, String optOutMessage, Consumer optOutAction) { - FXDialog alert = new FXDialog(type, title, true); + FXDialog alert = new FXDialog(AlertType.CONFIRMATION, title, true); // Need to force the alert to layout in order to grab the graphic as we are replacing the dialog pane with a custom pane alert.getDialogPane().applyCss(); Node graphic = alert.getDialogPane().getGraphic(); @@ -135,7 +133,7 @@ public static String shortenDialogMessage(String dialogMessage) { if (dialogMessage.length() < JabRefDialogService.DIALOG_SIZE_LIMIT) { return dialogMessage.trim(); } - return (dialogMessage.substring(0, Math.min(dialogMessage.length(), JabRefDialogService.DIALOG_SIZE_LIMIT)) + "...").trim(); + return (dialogMessage.substring(0, JabRefDialogService.DIALOG_SIZE_LIMIT) + "...").trim(); } private ChoiceDialog createChoiceDialog(String title, String content, String okButtonLabel, T defaultChoice, Collection choices) { @@ -226,16 +224,7 @@ public void showErrorDialogAndWait(FetcherException fetcherException) { String localizedMessage = fetcherException.getLocalizedMessage(); Optional httpResponse = fetcherException.getHttpResponse(); if (httpResponse.isPresent()) { - int statusCode = httpResponse.get().statusCode(); - if (statusCode == 401) { - this.showInformationDialogAndWait(failedTitle, Localization.lang("Access denied. You are not authorized to access this resource. Please check your credentials and try again. If you believe you should have access, please contact the administrator for assistance.") + "\n\n" + localizedMessage); - } else if (statusCode == 403) { - this.showInformationDialogAndWait(failedTitle, Localization.lang("Access denied. You do not have permission to access this resource. Please contact the administrator for assistance or try a different action.") + "\n\n" + localizedMessage); - } else if (statusCode == 404) { - this.showInformationDialogAndWait(failedTitle, Localization.lang("The requested resource could not be found. It seems that the file you are trying to download is not available or has been moved. Please verify the URL and try again. If you believe this is an error, please contact the administrator for further assistance.") + "\n\n" + localizedMessage); - } else { - this.showErrorDialogAndWait(failedTitle, Localization.lang("Something is wrong on JabRef side. Please check the URL and try again.") + "\n\n" + localizedMessage); - } + this.showInformationDialogAndWait(failedTitle, getContentByCode(httpResponse.get().statusCode()) + "\n\n" + localizedMessage); } else if (fetcherException instanceof FetcherClientException) { this.showErrorDialogAndWait(failedTitle, Localization.lang("Something is wrong on JabRef side. Please check the URL and try again.") + "\n\n" + localizedMessage); } else if (fetcherException instanceof FetcherServerException) { @@ -288,7 +277,7 @@ public boolean showConfirmationDialogAndWait(String title, String content, @Override public boolean showConfirmationDialogWithOptOutAndWait(String title, String content, String optOutMessage, Consumer optOutAction) { - FXDialog alert = createDialogWithOptOut(AlertType.CONFIRMATION, title, content, optOutMessage, optOutAction); + FXDialog alert = createDialogWithOptOut(title, content, optOutMessage, optOutAction); alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO); return alert.showAndWait().filter(buttonType -> buttonType == ButtonType.YES).isPresent(); } @@ -297,7 +286,7 @@ public boolean showConfirmationDialogWithOptOutAndWait(String title, String cont public boolean showConfirmationDialogWithOptOutAndWait(String title, String content, String okButtonLabel, String cancelButtonLabel, String optOutMessage, Consumer optOutAction) { - FXDialog alert = createDialogWithOptOut(AlertType.CONFIRMATION, title, content, optOutMessage, optOutAction); + FXDialog alert = createDialogWithOptOut(title, content, optOutMessage, optOutAction); ButtonType okButtonType = new ButtonType(okButtonLabel, ButtonBar.ButtonData.YES); ButtonType cancelButtonType = new ButtonType(cancelButtonLabel, ButtonBar.ButtonData.NO); alert.getButtonTypes().setAll(okButtonType, cancelButtonType); @@ -388,7 +377,7 @@ public void showProgressDialogAndWait(String title, String content, Task } @Override - public Optional showBackgroundProgressDialogAndWait(String title, String content, StateManager stateManager) { + public Optional showBackgroundProgressDialogAndWait(String title, String content, StateManager stateManager) { TaskProgressView> taskProgressView = new TaskProgressView<>(); EasyBind.bindContent(taskProgressView.getTasks(), stateManager.getRunningBackgroundTasks()); taskProgressView.setRetainTasks(false); @@ -424,25 +413,24 @@ public void notify(String message) { // The event log is not that user friendly (different purpose). LOGGER.info(message); - UiTaskExecutor.runInJavaFXThread(() -> { + UiTaskExecutor.runInJavaFXThread(() -> Notifications.create() .text(message) .position(Pos.BOTTOM_CENTER) .hideAfter(TOAST_MESSAGE_DISPLAY_TIME) .owner(mainWindow) .threshold(5, - Notifications.create() - .title(Localization.lang("Last notification")) - .text( - "(" + Localization.lang("Check the event log to see all notifications") + ")" - + "\n\n" + message) - .onAction(e -> { - ErrorConsoleAction ec = new ErrorConsoleAction(); - ec.execute(); - })) + Notifications.create() + .title(Localization.lang("Last notification")) + .text( + "(" + Localization.lang("Check the event log to see all notifications") + ")" + + "\n\n" + message) + .onAction(e -> { + ErrorConsoleAction ec = new ErrorConsoleAction(); + ec.execute(); + })) .hideCloseButton() - .show(); - }); + .show()); } @Override @@ -472,7 +460,7 @@ public Optional showDirectorySelectionDialog(DirectoryDialogConfiguration public List showFileOpenDialogAndGetMultipleFiles(FileDialogConfiguration fileDialogConfiguration) { FileChooser chooser = getConfiguredFileChooser(fileDialogConfiguration); List files = chooser.showOpenMultipleDialog(mainWindow); - return files != null ? files.stream().map(File::toPath).collect(Collectors.toList()) : Collections.emptyList(); + return files != null ? files.stream().map(File::toPath).toList() : List.of(); } private DirectoryChooser getConfiguredDirectoryChooser(DirectoryDialogConfiguration directoryDialogConfiguration) { @@ -520,4 +508,17 @@ public void showCustomWindow(BaseWindow window) { window.applyStylesheets(mainWindow.getScene().getStylesheets()); window.show(); } + + private String getContentByCode(int statusCode) { + return switch (statusCode) { + case 401 -> + Localization.lang("Access denied. You are not authorized to access this resource. Please check your credentials and try again. If you believe you should have access, please contact the administrator for assistance."); + case 403 -> + Localization.lang("Access denied. You do not have permission to access this resource. Please contact the administrator for assistance or try a different action."); + case 404 -> + Localization.lang("The requested resource could not be found. It seems that the file you are trying to download is not available or has been moved. Please verify the URL and try again. If you believe this is an error, please contact the administrator for further assistance."); + default -> + Localization.lang("Something is wrong on JabRef side. Please check the URL and try again."); + }; + } } From 604896c67e2bc0b7af799be1211c12f9f92c87be Mon Sep 17 00:00:00 2001 From: Christoph Date: Wed, 18 Sep 2024 15:07:26 +0200 Subject: [PATCH 3/7] Improve pdf content parser for DOIs (#11782) * Improve pdf content parser for DOIs * changelog * try to fix checkstyle * reorder * fix * fuu checstyle * Modernize test java code Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> * Add missing dot Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> --------- Co-authored-by: Oliver Kopp Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> --- CHANGELOG.md | 1 + .../fileformat/PdfContentImporter.java | 44 +++--- .../fileformat/PdfContentImporterTest.java | 132 +++++++++++------- 3 files changed, 102 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57f964c3101..b191b87606c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - The browse button for a Custom exporter now opens in the directory of the current used exporter file. [#11717](https://github.com/JabRef/jabref/pull/11717) - We improved the display of long messages in the integrity check dialog. [#11619](https://github.com/JabRef/jabref/pull/11619) - We improved the undo/redo buttons in the main toolbar and main menu to be disabled when there is nothing to undo/redo. [#8807](https://github.com/JabRef/jabref/issues/8807) +- We improved the DOI detection in PDF imports. [#11782](https://github.com/JabRef/jabref/pull/11782) ### Fixed diff --git a/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java index 6264e0dcd97..5006eed92c9 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java @@ -22,6 +22,7 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.identifier.DOI; import org.jabref.model.entry.types.EntryType; import org.jabref.model.entry.types.StandardEntryType; import org.jabref.model.strings.StringUtil; @@ -241,7 +242,7 @@ Optional getEntryFromPDFContent(String firstpageContents, String lineS String keywords = null; String title; String conference = null; - String DOI = null; + String doi = null; String series = null; String volume = null; String number = null; @@ -253,6 +254,7 @@ Optional getEntryFromPDFContent(String firstpageContents, String lineS if (curString.length() > 4) { // special case: possibly conference as first line on the page extractYear(); + doi = getDoi(null); if (curString.contains("Conference")) { fillCurStringWithNonEmptyLines(); conference = curString; @@ -384,27 +386,7 @@ Optional getEntryFromPDFContent(String firstpageContents, String lineS } } } else { - if (DOI == null) { - pos = curString.indexOf("DOI"); - if (pos < 0) { - pos = curString.indexOf(StandardField.DOI.getName()); - } - if (pos >= 0) { - pos += 3; - if (curString.length() > pos) { - char delimiter = curString.charAt(pos); - if ((delimiter == ':') || (delimiter == ' ')) { - pos++; - } - int nextSpace = curString.indexOf(' ', pos); - if (nextSpace > 0) { - DOI = curString.substring(pos, nextSpace); - } else { - DOI = curString.substring(pos); - } - } - } - } + doi = getDoi(doi); if ((publisher == null) && curString.contains("IEEE")) { // IEEE has the conference things at the end @@ -459,8 +441,8 @@ Optional getEntryFromPDFContent(String firstpageContents, String lineS if (conference != null) { entry.setField(StandardField.BOOKTITLE, conference); } - if (DOI != null) { - entry.setField(StandardField.DOI, DOI); + if (doi != null) { + entry.setField(StandardField.DOI, doi); } if (series != null) { entry.setField(StandardField.SERIES, series); @@ -483,6 +465,20 @@ Optional getEntryFromPDFContent(String firstpageContents, String lineS return Optional.of(entry); } + private String getDoi(String doi) { + int pos; + if (doi == null) { + pos = curString.indexOf("DOI"); + if (pos < 0) { + pos = curString.indexOf(StandardField.DOI.getName()); + } + if (pos >= 0) { + return DOI.findInText(curString).map(DOI::getDOI).orElse(null); + } + } + return doi; + } + private String getFirstPageContents(PDDocument document) throws IOException { PDFTextStripper stripper = new PDFTextStripper(); diff --git a/src/test/java/org/jabref/logic/importer/fileformat/PdfContentImporterTest.java b/src/test/java/org/jabref/logic/importer/fileformat/PdfContentImporterTest.java index 83e6466f411..62443d3accf 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/PdfContentImporterTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/PdfContentImporterTest.java @@ -1,7 +1,6 @@ package org.jabref.logic.importer.fileformat; import java.nio.file.Path; -import java.util.Collections; import java.util.List; import java.util.Optional; @@ -10,25 +9,19 @@ import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.StandardEntryType; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class PdfContentImporterTest { - private PdfContentImporter importer; - - @BeforeEach - void setUp() { - importer = new PdfContentImporter(); - } + private PdfContentImporter importer = new PdfContentImporter(); @Test void doesNotHandleEncryptedPdfs() throws Exception { Path file = Path.of(PdfContentImporter.class.getResource("/pdfs/encrypted.pdf").toURI()); List result = importer.importDatabase(file).getDatabase().getEntries(); - assertEquals(Collections.emptyList(), result); + assertEquals(List.of(), result); } @Test @@ -36,61 +29,98 @@ void importTwiceWorksAsExpected() throws Exception { Path file = Path.of(PdfContentImporter.class.getResource("/pdfs/minimal.pdf").toURI()); List result = importer.importDatabase(file).getDatabase().getEntries(); - BibEntry expected = new BibEntry(StandardEntryType.InProceedings); - expected.setField(StandardField.AUTHOR, "1 "); - expected.setField(StandardField.TITLE, "Hello World"); - expected.setFiles(Collections.singletonList(new LinkedFile("", file.toAbsolutePath(), "PDF"))); + BibEntry expected = new BibEntry(StandardEntryType.InProceedings) + .withField(StandardField.AUTHOR, "1 ") + .withField(StandardField.TITLE, "Hello World") + .withFiles(List.of(new LinkedFile("", file.toAbsolutePath(), "PDF"))); + assertEquals(List.of(expected), result); List resultSecondImport = importer.importDatabase(file).getDatabase().getEntries(); - assertEquals(Collections.singletonList(expected), result); - assertEquals(Collections.singletonList(expected), resultSecondImport); + assertEquals(List.of(expected), resultSecondImport); } @Test void parsingEditorWithoutPagesorSeriesInformation() { - BibEntry entry = new BibEntry(StandardEntryType.InProceedings); - entry.setField(StandardField.AUTHOR, "Anke Lüdeling and Merja Kytö (Eds.)"); - entry.setField(StandardField.EDITOR, "Anke Lüdeling and Merja Kytö"); - entry.setField(StandardField.PUBLISHER, "Springer"); - entry.setField(StandardField.TITLE, "Corpus Linguistics – An International Handbook – Lüdeling, Anke, Kytö, Merja (Eds.)"); - - String firstPageContents = "Corpus Linguistics – An International Handbook – Lüdeling, Anke,\n" + - "Kytö, Merja (Eds.)\n" + - "\n" + - "Anke Lüdeling, Merja Kytö (Eds.)\n" + - "\n" + - "VOLUME 2\n" + - "\n" + - "This handbook provides an up-to-date survey of the field of corpus linguistics, a Handbücher zur Sprach- und\n" + - "field whose methodology has revolutionized much of the empirical work done in Kommunikationswissenschaft / Handbooks\n" + - "\n" + - "of Linguistics and Communication Science\n" + - "most fields of linguistic study over the past decade. (HSK) 29/2\n" + - "\n" + - "vii, 578 pages\n" + - "Corpus linguistics investigates human language by starting out from large\n"; + BibEntry entry = new BibEntry(StandardEntryType.InProceedings) + .withField(StandardField.AUTHOR, "Anke Lüdeling and Merja Kytö (Eds.)") + .withField(StandardField.EDITOR, "Anke Lüdeling and Merja Kytö") + .withField(StandardField.PUBLISHER, "Springer") + .withField(StandardField.TITLE, "Corpus Linguistics – An International Handbook – Lüdeling, Anke, Kytö, Merja (Eds.)"); + + String firstPageContents = """ + Corpus Linguistics – An International Handbook – Lüdeling, Anke, + Kytö, Merja (Eds.) + + Anke Lüdeling, Merja Kytö (Eds.) + + VOLUME 2 + + This handbook provides an up-to-date survey of the field of corpus linguistics, a Handbücher zur Sprach- und + field whose methodology has revolutionized much of the empirical work done in Kommunikationswissenschaft / Handbooks + + of Linguistics and Communication Science + most fields of linguistic study over the past decade. (HSK) 29/2 + + vii, 578 pages + Corpus linguistics investigates human language by starting out from large + """; assertEquals(Optional.of(entry), importer.getEntryFromPDFContent(firstPageContents, "\n")); } @Test void parsingWithoutActualDOINumber() { - BibEntry entry = new BibEntry(StandardEntryType.InProceedings); - entry.withField(StandardField.AUTHOR, "Link to record in KAR and http://kar.kent.ac.uk/51043/ and Document Version and UNSPECIFIED and Master of Research (MRes) thesis and University of Kent") - .withField(StandardField.TITLE, "Kent Academic Repository Full text document (pdf) Citation for published version Smith, Lucy Anna (2014) Mortality in the Ornamental Fish Retail Sector: an Analysis of Stock Losses and Stakeholder Opinions. DOI") - .withField(StandardField.YEAR, "5104"); - - String firstPageContents = "Kent Academic Repository Full text document (pdf)\n" - + "Citation for published version\n" - + "Smith, Lucy Anna (2014) Mortality in the Ornamental Fish Retail Sector: an Analysis of Stock\n" - + "Losses and Stakeholder Opinions.\n" - + "DOI\n\n\n" - + "Link to record in KAR\n" - + "http://kar.kent.ac.uk/51043/\n" - + "Document Version\n" - + "UNSPECIFIED\n" - + "Master of Research (MRes) thesis, University of Kent,."; + BibEntry entry = new BibEntry(StandardEntryType.InProceedings) + .withField(StandardField.AUTHOR, "Link to record in KAR and http://kar.kent.ac.uk/51043/ and Document Version and UNSPECIFIED and Master of Research (MRes) thesis and University of Kent") + .withField(StandardField.TITLE, "Kent Academic Repository Full text document (pdf) Citation for published version Smith, Lucy Anna (2014) Mortality in the Ornamental Fish Retail Sector: an Analysis of Stock Losses and Stakeholder Opinions. DOI") + .withField(StandardField.YEAR, "5104"); + + String firstPageContents = """ + Kent Academic Repository Full text document (pdf) + Citation for published version + Smith, Lucy Anna (2014) Mortality in the Ornamental Fish Retail Sector: an Analysis of Stock + Losses and Stakeholder Opinions. + DOI + + Link to record in KAR + http://kar.kent.ac.uk/51043/ + Document Version + UNSPECIFIED + Master of Research (MRes) thesis, University of Kent,."""; assertEquals(Optional.of(entry), importer.getEntryFromPDFContent(firstPageContents, "\n")); } + + @Test + void extractDOIFromPage1() { + BibEntry entry = new BibEntry(StandardEntryType.InProceedings) + .withField(StandardField.DOI, "10.1017/S0007114507795296") + .withField(StandardField.AUTHOR, "Review Article") + .withField(StandardField.TITLE, "British Journal of Nutrition (2008), 99, 1–11 doi: 10.1017/S0007114507795296 q The Authors") + .withField(StandardField.YEAR, "2008"); + + String firstPageContent = """ + British Journal of Nutrition (2008), 99, 1–11 doi: 10.1017/S0007114507795296 + q The Authors 2008 + + Review Article + + Cocoa and health: a decade of research + + Karen A. Cooper1, Jennifer L. Donovan2, Andrew L. Waterhouse3 and Gary Williamson1* + 1Nestlé Research Center, Vers-Chez-les-Blanc, PO Box 44, CH-1000 Lausanne 26, Switzerland + 2Department of Psychiatry and Behavioural Sciences, Medical University of South Carolina, Charleston, SC 29425, USA + 3Department of Viticulture & Enology, University of California, Davis, CA 95616, USA + + (Received 5 December 2006 – Revised 29 May 2007 – Accepted 31 May 2007) + + Abbreviations: FMD, flow-mediated dilation; NO, nitirc oxide. + + *Corresponding author: Dr Gary Williamson, fax þ41 21 785 8544, email gary.williamson@rdls.nestle.com + + British Journal of Nutrition + https://doi.org/10.1017/S0007114507795296 Published online by Cambridge University Press"""; + + assertEquals(Optional.of(entry), importer.getEntryFromPDFContent(firstPageContent, "\n")); + } } From a6912d4ecc92a2ede23bb21250cf3ced7c4669f1 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Wed, 18 Sep 2024 21:58:05 +0200 Subject: [PATCH 4/7] Update djl api dependency (#11787) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9d7221b5025..cd344966b18 100644 --- a/build.gradle +++ b/build.gradle @@ -336,7 +336,7 @@ dependencies { implementation('dev.langchain4j:langchain4j-mistral-ai:0.34.0') implementation('dev.langchain4j:langchain4j-google-ai-gemini:0.34.0') implementation('dev.langchain4j:langchain4j-hugging-face:0.34.0') - implementation 'ai.djl:api:0.29.0' + implementation 'ai.djl:api:0.30.0' implementation 'ai.djl.pytorch:pytorch-model-zoo:0.30.0' implementation 'ai.djl.huggingface:tokenizers:0.29.0' implementation 'io.github.stefanbratanov:jvm-openai:0.11.0' From c5e81a948bb8971fc83555325808cb35889faed5 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Thu, 19 Sep 2024 19:48:41 +0200 Subject: [PATCH 5/7] [AI] Add more uses statements (#11788) * Add more uses statements * Make use of BOM * Add requires 'ai.djl.pytorch_model_zoo' * Trying downgrade of djl * Sort "requires" alphabetically * Add org.freedesktop.dbus as requriement * Sort "uses" alphabetically * Add "provides" * Update djl to 0.30.0 * Fix jackson requirements --- build.gradle | 70 ++++++++++++++++++++-------------- src/main/java/module-info.java | 6 ++- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/build.gradle b/build.gradle index cd344966b18..85a0c904de9 100644 --- a/build.gradle +++ b/build.gradle @@ -327,7 +327,7 @@ dependencies { // YAML formatting implementation 'org.yaml:snakeyaml:2.3' - // AI + // region AI implementation 'dev.langchain4j:langchain4j:0.34.0' // Even though we use jvm-openai for LLM connection, we still need this package for tokenization. implementation('dev.langchain4j:langchain4j-open-ai:0.34.0') { @@ -336,9 +336,11 @@ dependencies { implementation('dev.langchain4j:langchain4j-mistral-ai:0.34.0') implementation('dev.langchain4j:langchain4j-google-ai-gemini:0.34.0') implementation('dev.langchain4j:langchain4j-hugging-face:0.34.0') - implementation 'ai.djl:api:0.30.0' - implementation 'ai.djl.pytorch:pytorch-model-zoo:0.30.0' - implementation 'ai.djl.huggingface:tokenizers:0.29.0' + + implementation platform('ai.djl:bom:0.30.0') + implementation 'ai.djl:api' + implementation 'ai.djl.huggingface:tokenizers' + implementation 'ai.djl.pytorch:pytorch-model-zoo' implementation 'io.github.stefanbratanov:jvm-openai:0.11.0' // openai depends on okhttp, which needs kotlin - see https://github.com/square/okhttp/issues/5299 for details implementation ('com.squareup.okhttp3:okhttp:4.12.0') { @@ -346,6 +348,7 @@ dependencies { } // GemxFX also (transitively) depends on kotlin implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.20' + // endregion implementation 'commons-io:commons-io:2.16.1' @@ -735,16 +738,14 @@ jlink { // TODO: Remove the following correction to the merged module // The module descriptor automatically generated by the plugin for the merged module contained some invalid entries. - // This is based on ./gradlew suggestMergedModuleInfo, sort, strip ";"", comment non-used modules, and include the suggested directives here. - // However, we need to fine-tune this manually (non-alphabetic order) + // This is based on ./gradlew suggestMergedModuleInfo, sort, strip ";"", remove non-used modules, and include the suggested directives here. mergedModule { - requires 'javafx.base' - requires 'javafx.controls' - requires 'javafx.fxml' - requires 'javafx.graphics' - requires 'javafx.media' - requires 'javafx.swing' - + requires 'com.google.gson' + requires 'com.fasterxml.jackson.annotation' + requires 'com.fasterxml.jackson.databind' + requires 'com.fasterxml.jackson.core' + requires 'com.fasterxml.jackson.datatype.jdk8' + requires 'jakarta.xml.bind' requires 'java.compiler' requires 'java.datatransfer' requires 'java.desktop' @@ -752,34 +753,43 @@ jlink { requires 'java.management' requires 'java.naming' requires 'java.net.http' + requires 'java.rmi' requires 'java.scripting' requires 'java.security.jgss' requires 'java.security.sasl' requires 'java.sql' requires 'java.sql.rowset' requires 'java.transaction.xa' - requires 'java.rmi' requires 'java.xml' + requires 'javafx.base' + requires 'javafx.controls' + requires 'javafx.fxml' + requires 'javafx.graphics' + requires 'javafx.media' + requires 'javafx.swing' requires 'jdk.jsobject' + requires 'jdk.security.jgss' requires 'jdk.unsupported' requires 'jdk.unsupported.desktop' - requires 'jdk.security.jgss' requires 'jdk.xml.dom' - requires 'com.google.gson' - requires 'org.jsoup' - requires 'org.slf4j' - requires 'jakarta.xml.bind' requires 'org.apache.commons.lang3' + requires 'org.apache.commons.logging' requires 'org.apache.commons.text' - requires 'org.apache.commons.logging'; - uses 'org.mariadb.jdbc.credential.CredentialPlugin' + requires 'org.freedesktop.dbus' + requires 'org.jsoup' + requires 'org.slf4j' + uses 'ai.djl.engine.EngineProvider' + uses 'ai.djl.repository.RepositoryFactory' + uses 'ai.djl.repository.zoo.ZooProvider' + uses 'dev.langchain4j.spi.prompt.PromptTemplateFactory' + uses 'kong.unirest.core.json.JsonEngine' + uses 'org.eclipse.jgit.lib.GpgSigner' + uses 'org.eclipse.jgit.transport.SshSessionFactory' + uses 'org.mariadb.jdbc.LocalInfileInterceptor' uses 'org.mariadb.jdbc.authentication.AuthenticationPlugin' + uses 'org.mariadb.jdbc.credential.CredentialPlugin' uses 'org.mariadb.jdbc.tls.TlsSocketPlugin' - uses 'org.mariadb.jdbc.LocalInfileInterceptor' - uses 'org.eclipse.jgit.transport.SshSessionFactory' - uses 'org.eclipse.jgit.lib.GpgSigner' - uses 'kong.unirest.core.json.JsonEngine'; - uses 'ai.djl.repository.zoo.ZooProvider'; + provides 'org.mariadb.jdbc.tls.TlsSocketPlugin' with 'org.mariadb.jdbc.internal.protocol.tls.DefaultTlsSocketPlugin' provides 'java.sql.Driver' with 'org.postgresql.Driver' provides 'org.mariadb.jdbc.authentication.AuthenticationPlugin' with 'org.mariadb.jdbc.internal.com.send.authentication.CachingSha2PasswordPlugin', @@ -797,9 +807,11 @@ jlink { 'org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider' provides 'kong.unirest.core.json.JsonEngine' with 'kong.unirest.modules.gson.GsonEngine'; provides 'ai.djl.repository.zoo.ZooProvider' with 'ai.djl.engine.rust.zoo.RsZooProvider', - 'ai.djl.huggingface.zoo.HfZooProvider', - 'ai.djl.pytorch.zoo.PtZooProvider', - 'ai.djl.repository.zoo.DefaultZooProvider'; + 'ai.djl.huggingface.zoo.HfZooProvider', + 'ai.djl.pytorch.zoo.PtZooProvider', + 'ai.djl.repository.zoo.DefaultZooProvider'; + provides 'ai.djl.engine.EngineProvider' with 'ai.djl.engine.rust.RsEngineProvider', + 'ai.djl.pytorch.engine.PtEngineProvider'; } jpackage { diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index dc1fce79618..54e5cc04851 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -144,7 +144,7 @@ // region AI requires ai.djl.api; - uses ai.djl.repository.zoo.ZooProvider; + requires ai.djl.pytorch_model_zoo; requires ai.djl.tokenizers; requires jvm.openai; requires langchain4j; @@ -152,6 +152,10 @@ requires langchain4j.hugging.face; requires langchain4j.mistral.ai; requires langchain4j.open.ai; + uses ai.djl.engine.EngineProvider; + uses ai.djl.repository.RepositoryFactory; + uses ai.djl.repository.zoo.ZooProvider; + uses dev.langchain4j.spi.prompt.PromptTemplateFactory; // endregion // region: Lucene From 4e864f62c8f222fd291cad3ee2466a24ed5cbc6c Mon Sep 17 00:00:00 2001 From: Christoph Date: Thu, 19 Sep 2024 23:44:40 +0200 Subject: [PATCH 6/7] Fix ai chat not on fx thread (#11796) * Fix ai chat not on fx thread Fixes https://github.com/JabRef/jabref-issue-melting-pot/issues/549 * fix import --- .../org/jabref/gui/ai/components/aichat/AiChatComponent.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/ai/components/aichat/AiChatComponent.java b/src/main/java/org/jabref/gui/ai/components/aichat/AiChatComponent.java index c1a43b45dbd..25e52d294e4 100644 --- a/src/main/java/org/jabref/gui/ai/components/aichat/AiChatComponent.java +++ b/src/main/java/org/jabref/gui/ai/components/aichat/AiChatComponent.java @@ -21,6 +21,7 @@ import org.jabref.gui.ai.components.util.notifications.Notification; import org.jabref.gui.ai.components.util.notifications.NotificationsComponent; import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.util.UiTaskExecutor; import org.jabref.logic.ai.AiPreferences; import org.jabref.logic.ai.AiService; import org.jabref.logic.ai.chatting.AiChatLogic; @@ -140,7 +141,7 @@ private void updateNotifications() { notificationsButton.setManaged(!notifications.isEmpty()); if (!notifications.isEmpty()) { - notificationsButton.setGraphic(IconTheme.JabRefIcons.WARNING.withColor(Color.YELLOW).getGraphicNode()); + UiTaskExecutor.runInJavaFXThread(() -> notificationsButton.setGraphic(IconTheme.JabRefIcons.WARNING.withColor(Color.YELLOW).getGraphicNode())); } } From ec62ec0fae346d25e174ab7ce0a94bda5a898aa1 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Fri, 20 Sep 2024 18:39:48 +0300 Subject: [PATCH 7/7] Fix focus for keywords and crossref fields (#11792) --- CHANGELOG.md | 1 + src/main/java/org/jabref/gui/Base.css | 13 +++++++------ .../org/jabref/gui/fieldeditors/KeywordsEditor.java | 12 +++++------- .../gui/fieldeditors/LinkedEntriesEditor.java | 11 +++++++---- .../autocompletion/AutoCompletionTab.java | 4 ++++ 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b191b87606c..320ed820875 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an exception when searching for unlinked files. [#11731](https://github.com/JabRef/jabref/issues/11731) - We fixed an issue where two contradicting notifications were shown when cutting an entry in the main table. [#11724](https://github.com/JabRef/jabref/pull/11724) - We fixed an issue where unescaped braces in the arXiv fetcher were not treated. [#11704](https://github.com/JabRef/jabref/issues/11704) +- We fixed an issue where the keywords and crossref fields were not properly focused. [#11177](https://github.com/JabRef/jabref/issues/11177) ### Removed diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 3832348936a..14ae1873d4a 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -1427,17 +1427,15 @@ We want to have a look that matches our icons in the tool-bar */ -fx-label-padding: 5 0 10 10; } -.chips-pane > .editor { +.tags-field { -fx-pref-height: 30px; - -fx-padding: 0px 0px 0px -8px; -fx-margin: 0em; -fx-border-style: none; + -fx-background-color: -fx-outer-border, -fx-control-inner-background; } -.tags-field { - -fx-pref-height: 30px; - -fx-margin: 0em; - -fx-border-style: none; +.tags-field:focused { + -fx-border-color: -jr-accent; } .tags-field > .flow-pane > .tag-view { @@ -1452,6 +1450,9 @@ We want to have a look that matches our icons in the tool-bar */ .tags-field-editor { -fx-border-width: 0; + -fx-text-fill: -fx-focused-text-base-color; + -fx-highlight-text-fill: -fx-text-inner-color; + -fx-highlight-fill: derive(-jr-accent, 20%); } .searchBar:invalid { diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java index f06cbc42989..4da18163ab2 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java @@ -5,6 +5,7 @@ import javax.swing.undo.UndoManager; import javafx.beans.binding.Bindings; +import javafx.css.PseudoClass; import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.Parent; @@ -37,7 +38,9 @@ public class KeywordsEditor extends HBox implements FieldEditorFX { private static final Logger LOGGER = LoggerFactory.getLogger(KeywordsEditor.class); + private static final PseudoClass FOCUSED = PseudoClass.getPseudoClass("focused"); + @FXML private KeywordsEditorViewModel viewModel; @FXML private TagsField keywordTagsField; @Inject private CliPreferences preferences; @@ -45,8 +48,6 @@ public class KeywordsEditor extends HBox implements FieldEditorFX { @Inject private UndoManager undoManager; @Inject private ClipBoardManager clipBoardManager; - private final KeywordsEditorViewModel viewModel; - public KeywordsEditor(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { @@ -73,8 +74,10 @@ public KeywordsEditor(Field field, keywordTagsField.setNewItemProducer(searchText -> viewModel.getStringConverter().fromString(searchText)); keywordTagsField.setShowSearchIcon(false); + keywordTagsField.setOnMouseClicked(event -> keywordTagsField.getEditor().requestFocus()); keywordTagsField.getEditor().getStyleClass().clear(); keywordTagsField.getEditor().getStyleClass().add("tags-field-editor"); + keywordTagsField.getEditor().focusedProperty().addListener((observable, oldValue, newValue) -> keywordTagsField.pseudoClassStateChanged(FOCUSED, newValue)); String keywordSeparator = String.valueOf(viewModel.getKeywordSeparator()); keywordTagsField.getEditor().setOnKeyReleased(event -> { @@ -118,11 +121,6 @@ public Parent getNode() { return this; } - @Override - public void requestFocus() { - keywordTagsField.requestFocus(); - } - @Override public double getWeight() { return 2; diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java index 0dd4ace6b99..4b65ad78349 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java @@ -5,6 +5,7 @@ import javax.swing.undo.UndoManager; import javafx.beans.binding.Bindings; +import javafx.css.PseudoClass; import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.Parent; @@ -39,18 +40,17 @@ import org.slf4j.LoggerFactory; public class LinkedEntriesEditor extends HBox implements FieldEditorFX { - private static final Logger LOGGER = LoggerFactory.getLogger(LinkedEntriesEditor.class); + private static final PseudoClass FOCUSED = PseudoClass.getPseudoClass("focused"); - @FXML public TagsField entryLinkField; + @FXML private LinkedEntriesEditorViewModel viewModel; + @FXML private TagsField entryLinkField; @Inject private DialogService dialogService; @Inject private ClipBoardManager clipBoardManager; @Inject private UndoManager undoManager; @Inject private StateManager stateManager; - private final LinkedEntriesEditorViewModel viewModel; - public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { ViewLoader.view(this) .root(this) @@ -66,9 +66,12 @@ public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, Sugg entryLinkField.setNewItemProducer(searchText -> viewModel.getStringConverter().fromString(searchText)); entryLinkField.setMatcher((entryLink, searchText) -> entryLink.getKey().toLowerCase().startsWith(searchText.toLowerCase())); entryLinkField.setComparator(Comparator.comparing(ParsedEntryLink::getKey)); + entryLinkField.setShowSearchIcon(false); + entryLinkField.setOnMouseClicked(event -> entryLinkField.getEditor().requestFocus()); entryLinkField.getEditor().getStyleClass().clear(); entryLinkField.getEditor().getStyleClass().add("tags-field-editor"); + entryLinkField.getEditor().focusedProperty().addListener((observable, oldValue, newValue) -> entryLinkField.pseudoClassStateChanged(FOCUSED, newValue)); String separator = EntryLinkList.SEPARATOR; entryLinkField.getEditor().setOnKeyReleased(event -> { diff --git a/src/main/java/org/jabref/gui/preferences/autocompletion/AutoCompletionTab.java b/src/main/java/org/jabref/gui/preferences/autocompletion/AutoCompletionTab.java index 4b02b535df3..d8837eb0443 100644 --- a/src/main/java/org/jabref/gui/preferences/autocompletion/AutoCompletionTab.java +++ b/src/main/java/org/jabref/gui/preferences/autocompletion/AutoCompletionTab.java @@ -1,5 +1,6 @@ package org.jabref.gui.preferences.autocompletion; +import javafx.css.PseudoClass; import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.control.CheckBox; @@ -18,6 +19,7 @@ import com.dlsc.gemsfx.TagsField; public class AutoCompletionTab extends AbstractPreferenceTabView implements PreferencesTab { + private static final PseudoClass FOCUSED = PseudoClass.getPseudoClass("focused"); @FXML private CheckBox enableAutoComplete; @FXML private TagsField autoCompleteFields; @@ -58,8 +60,10 @@ private void setupTagsFiled() { autoCompleteFields.setConverter(viewModel.getFieldStringConverter()); autoCompleteFields.setTagViewFactory(this::createTag); autoCompleteFields.setShowSearchIcon(false); + autoCompleteFields.setOnMouseClicked(event -> autoCompleteFields.getEditor().requestFocus()); autoCompleteFields.getEditor().getStyleClass().clear(); autoCompleteFields.getEditor().getStyleClass().add("tags-field-editor"); + autoCompleteFields.getEditor().focusedProperty().addListener((observable, oldValue, newValue) -> autoCompleteFields.pseudoClassStateChanged(FOCUSED, newValue)); } private Node createTag(Field field) {