From 2a4d378f4a3d87a1b45b774c00550d47020c0597 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 25 Sep 2023 00:46:20 +0100 Subject: [PATCH 001/144] change name to extension comparison in fromString and toStringList --- .../jabref/gui/externalfiletype/ExternalFileTypes.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java index c3a7ebf3d46..25e2bfaa088 100644 --- a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java +++ b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java @@ -148,10 +148,10 @@ public static String toStringList(Collection fileTypes) { for (ExternalFileType type : fileTypes) { results.add(type); - // See if we can find a type with matching name in the default type list: + // See if we can find a type with matching extension in the default type list: ExternalFileType found = null; for (ExternalFileType defType : defTypes) { - if (defType.getName().equals(type.getName())) { + if (defType.getExtension().equals(type.getExtension())) { found = defType; break; } @@ -221,11 +221,11 @@ public static Set fromString(String storedFileTypes) { } else { // A new or modified entry type. Construct it from the string array: ExternalFileType type = CustomExternalFileType.buildFromArgs(val); - // Check if there is a default type with the same name. If so, this is a + // Check if there is a default type with the same extension. If so, this is a // modification of that type, so remove the default one: ExternalFileType toRemove = null; for (ExternalFileType defType : types) { - if (type.getName().equals(defType.getName())) { + if (type.getExtension().equals(defType.getExtension())) { toRemove = defType; break; } From 7187b699659dcf464d5b88e626e8c45f8eb20b30 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Sun, 8 Oct 2023 19:30:07 +0100 Subject: [PATCH 002/144] fix problem of abort button still add external file type entry in EditExternalFileTypeEntryDialog class --- .../externalfiletypes/EditExternalFileTypeEntryDialog.java | 6 ++++++ .../externalfiletypes/ExternalFileTypesTabViewModel.java | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java index e5aa3e55c03..5799a73cd1b 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java @@ -36,9 +36,12 @@ public class EditExternalFileTypeEntryDialog extends BaseDialog { private final ExternalFileTypeItemViewModel item; + private final boolean isNewItem; private EditExternalFileTypeViewModel viewModel; public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, String dialogTitle) { + this.isNewItem = (item.extensionProperty().get().equals("")) ? true : false; + this.item = item; this.setTitle(dialogTitle); @@ -50,6 +53,8 @@ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, Strin this.setResultConverter(button -> { if (button == ButtonType.OK) { viewModel.storeSettings(); + } else { + name.setText(""); } return null; }); @@ -67,6 +72,7 @@ public void initialize() { btnBrowse.disableProperty().bind(viewModel.defaultApplicationSelectedProperty()); extension.textProperty().bindBidirectional(viewModel.extensionProperty()); + extension.setEditable(isNewItem); name.textProperty().bindBidirectional(viewModel.nameProperty()); mimeType.textProperty().bindBidirectional(viewModel.mimeTypeProperty()); selectedApplication.textProperty().bindBidirectional(viewModel.selectedApplicationProperty()); diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index b61818105f0..817e930b382 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -56,8 +56,10 @@ public void resetToDefaults() { public void addNewType() { ExternalFileTypeItemViewModel item = new ExternalFileTypeItemViewModel(); - fileTypes.add(item); showEditDialog(item, Localization.lang("Add new file type")); + if (item.getName() != "") { + fileTypes.add(item); + } } public ObservableList getFileTypes() { From 341c52c66ee3125da260e0b9135d166183d0ae6f Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Sun, 15 Oct 2023 11:37:06 +0100 Subject: [PATCH 003/144] Block the Close action of EditExternalFileTypeEntryDialog when value empty --- .../EditExternalFileTypeEntryDialog.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java index 5799a73cd1b..5841a143965 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java @@ -50,6 +50,13 @@ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, Strin .load() .setAsDialogPane(this); + final Button btOk = (Button) this.getDialogPane().lookupButton(ButtonType.OK); + btOk.addEventFilter(ActionEvent.ACTION, event -> { + if (!isValidExternalFileTypeEntry()) { + event.consume(); + } + }); + this.setResultConverter(button -> { if (button == ButtonType.OK) { viewModel.storeSettings(); @@ -60,6 +67,16 @@ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, Strin }); } + public boolean isValidExternalFileTypeEntry() { + if (name.getText().trim().equalsIgnoreCase("") + || extension.getText().trim().equalsIgnoreCase("") + || mimeType.getText().trim().equalsIgnoreCase("") + ) { + return false; + } + return true; + } + @FXML public void initialize() { viewModel = new EditExternalFileTypeViewModel(item); From f7cc511e22006fc995acc46c945e161931a4f320 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Sun, 15 Oct 2023 13:12:39 +0100 Subject: [PATCH 004/144] ExternalFileTypesTab only scroll to bottom when new fileType added successfully --- .../externalfiletypes/ExternalFileTypesTab.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java index b131484ddc3..cd358279445 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java @@ -88,9 +88,12 @@ public void initialize() { @FXML private void addNewType() { + int fileTypeCount = fileTypesTable.getItems().size(); viewModel.addNewType(); - fileTypesTable.getSelectionModel().selectLast(); - fileTypesTable.scrollTo(viewModel.getFileTypes().size() - 1); + if (fileTypeCount < fileTypesTable.getItems().size()) { + fileTypesTable.getSelectionModel().selectLast(); + fileTypesTable.scrollTo(viewModel.getFileTypes().size() - 1); + } } @FXML From f6166c02043e122c13fadc2c4881175cc4767e7f Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Sun, 15 Oct 2023 15:47:32 +0100 Subject: [PATCH 005/144] check if external file type exists then cannot add a new one --- .../ExternalFileTypesTabViewModel.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index 817e930b382..d21fd494300 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -57,9 +57,19 @@ public void resetToDefaults() { public void addNewType() { ExternalFileTypeItemViewModel item = new ExternalFileTypeItemViewModel(); showEditDialog(item, Localization.lang("Add new file type")); - if (item.getName() != "") { - fileTypes.add(item); + + if (item.getName() == "") { + return; + } + + String newExt = item.extensionProperty().get(); + for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { + if (newExt.equalsIgnoreCase(fileTypeItem.extensionProperty().get())) { + return; + } } + + fileTypes.add(item); } public ObservableList getFileTypes() { From 5fe6df9844b43c3c8ed52f1171dad48859fbdf3c Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Thu, 19 Oct 2023 01:41:34 +0100 Subject: [PATCH 006/144] update changelog.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d77a4fdd04..0e15d9e269c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where exporting "Embedded BibTeX pdf" without selecting an existing document would produce an exception. [#10101](https://github.com/JabRef/jabref/issues/10101) - We fixed an issue where it was not possible to connect to a shared database once a group with entries was added or other metadata modified [#10336](https://github.com/JabRef/jabref/issues/10336) - We fixed an issue where middle-button paste in X not always worked [#7905](https://github.com/JabRef/jabref/issues/7905) +- We fixed issues where in 1) preferences: external file type duplicates when change languages. [#10271] (https://github.com/JabRef/jabref/issues/10271) 2) Cancel button only close EditExternalFileTypeEntryDialog without further action 3) Missed value in EditExternalFileTypeEntryDialog cannot close dialog even OK button clicked 4) Cannot save new External File Type if it extension exists already ### Removed From f0b21d02bc32352d924f96c37ac5d56997ea8a33 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Sat, 21 Oct 2023 00:41:54 +0100 Subject: [PATCH 007/144] reformat the new entry in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e0d07244e0..49d454e0017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where there was a failure to access the url link for "eprint" for the ArXiv entry.[#10474](https://github.com/JabRef/jabref/issues/10474) - We fixed an issue where it was not possible to connect to a shared database once a group with entries was added or other metadata modified [#10336](https://github.com/JabRef/jabref/issues/10336) - We fixed an issue where middle-button paste in X not always worked [#7905](https://github.com/JabRef/jabref/issues/7905) -- We fixed issues where in 1) preferences: external file type duplicates when change languages. [#10271] (https://github.com/JabRef/jabref/issues/10271) 2) Cancel button only close EditExternalFileTypeEntryDialog without further action 3) Missed value in EditExternalFileTypeEntryDialog cannot close dialog even OK button clicked 4) Cannot save new External File Type if it extension exists already +- We fixed issues where in 1) preferences: external file type duplicates when change languages. [#10271](https://github.com/JabRef/jabref/issues/10271) 2) Cancel button only close EditExternalFileTypeEntryDialog without further action 3) Missed value in EditExternalFileTypeEntryDialog cannot close dialog even OK button clicked 4) Cannot save new External File Type if it extension exists already ### Removed From a84d4da72b3064553ee568a852fc3446fd603baa Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Sun, 22 Oct 2023 17:21:56 +0100 Subject: [PATCH 008/144] added ExternalFileTypesTabViewModelTest and run rewriteRun --- .../EditExternalFileTypeEntryDialog.java | 10 +-- .../ExternalFileTypesTabViewModel.java | 2 +- .../ExternalFileTypesTabViewModelTest.java | 67 +++++++++++++++++++ 3 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java index 5841a143965..f205bf09825 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java @@ -40,7 +40,7 @@ public class EditExternalFileTypeEntryDialog extends BaseDialog { private EditExternalFileTypeViewModel viewModel; public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, String dialogTitle) { - this.isNewItem = (item.extensionProperty().get().equals("")) ? true : false; + this.isNewItem = item.extensionProperty().get().equals(""); this.item = item; @@ -68,13 +68,9 @@ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, Strin } public boolean isValidExternalFileTypeEntry() { - if (name.getText().trim().equalsIgnoreCase("") + return !(name.getText().trim().equalsIgnoreCase("") || extension.getText().trim().equalsIgnoreCase("") - || mimeType.getText().trim().equalsIgnoreCase("") - ) { - return false; - } - return true; + || mimeType.getText().trim().equalsIgnoreCase("")); } @FXML diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index d21fd494300..2dbd2c24edb 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -58,7 +58,7 @@ public void addNewType() { ExternalFileTypeItemViewModel item = new ExternalFileTypeItemViewModel(); showEditDialog(item, Localization.lang("Add new file type")); - if (item.getName() == "") { + if ("".equals(item.getName())) { return; } diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java new file mode 100644 index 00000000000..77a66fb26bb --- /dev/null +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java @@ -0,0 +1,67 @@ +package org.jabref.gui.preferences.externalfiletypes; + +import java.util.Set; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +import org.jabref.gui.externalfiletype.ExternalFileType; +import org.jabref.gui.externalfiletype.StandardExternalFileType; +import org.jabref.preferences.FilePreferences; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ExternalFileTypesTabViewModelTest { + private static final Set TEST_LIST = Set.of( + StandardExternalFileType.MARKDOWN, + StandardExternalFileType.PDF, + StandardExternalFileType.URL, + StandardExternalFileType.JPG, + StandardExternalFileType.TXT); + + private ExternalFileTypesTabViewModel externalFileTypesTabViewModel = mock(ExternalFileTypesTabViewModel.class); + private FilePreferences filePreferences = mock(FilePreferences.class); + private ObservableList fileTypes = FXCollections.observableArrayList(); + + @BeforeEach + void setUp() { + // Arrange + when(filePreferences.getExternalFileTypes()).thenReturn(FXCollections.observableSet(TEST_LIST)); + fileTypes.addAll(filePreferences.getExternalFileTypes().stream().map(ExternalFileTypeItemViewModel::new).toList()); + } + + @Test + public void addNewTypeSuccess() { + // Arrange + doAnswer(invocation -> { + ExternalFileTypeItemViewModel item = new ExternalFileTypeItemViewModel(); + fileTypes.add(item); + return null; + }).when(externalFileTypesTabViewModel).addNewType(); + + //Action + externalFileTypesTabViewModel.addNewType(); + + //Assert + assertEquals(fileTypes.size(), 6); + } + + @Test + public void addNewTypeFailed() { + // Arrange + doNothing().when(externalFileTypesTabViewModel).addNewType(); + + //Action + externalFileTypesTabViewModel.addNewType(); + + //Assert + assertEquals(fileTypes.size(), 5); + } +} From b5c1d1698d768274826b26279018b6563a30d1cb Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Sun, 22 Oct 2023 17:29:01 +0100 Subject: [PATCH 009/144] updated the comment format --- .../ExternalFileTypesTabViewModelTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java index 77a66fb26bb..e10a03703fc 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java @@ -46,10 +46,10 @@ public void addNewTypeSuccess() { return null; }).when(externalFileTypesTabViewModel).addNewType(); - //Action + // Action externalFileTypesTabViewModel.addNewType(); - //Assert + // Assert assertEquals(fileTypes.size(), 6); } @@ -58,10 +58,10 @@ public void addNewTypeFailed() { // Arrange doNothing().when(externalFileTypesTabViewModel).addNewType(); - //Action + // Action externalFileTypesTabViewModel.addNewType(); - //Assert + // Assert assertEquals(fileTypes.size(), 5); } } From ac06f61e51f2049ba1a04da7d718fee3db0b696a Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 23 Oct 2023 01:31:09 +0100 Subject: [PATCH 010/144] rephase entry in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e14e2ee85b..12fb3808048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,7 +68,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where there was a failure to access the url link for "eprint" for the ArXiv entry.[#10474](https://github.com/JabRef/jabref/issues/10474) - We fixed an issue where it was not possible to connect to a shared database once a group with entries was added or other metadata modified [#10336](https://github.com/JabRef/jabref/issues/10336) - We fixed an issue where middle-button paste in X not always worked [#7905](https://github.com/JabRef/jabref/issues/7905) -- We fixed issues where in 1) preferences: external file type duplicates when change languages. [#10271](https://github.com/JabRef/jabref/issues/10271) 2) Cancel button only close EditExternalFileTypeEntryDialog without further action 3) Missed value in EditExternalFileTypeEntryDialog cannot close dialog even OK button clicked 4) Cannot save new External File Type if it extension exists already +- We fixed issues in the external file type dialog, e.g., duplicate entries in the case of a language switch. [#10271](https://github.com/JabRef/jabref/issues/10271) ## [5.10] – 2023-09-02 From c604ecbb8287b9f1c07ca6a573baf20c55920c21 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Fri, 27 Oct 2023 22:51:47 +0100 Subject: [PATCH 011/144] change viewModel.addNewType to return boolean --- .../externalfiletypes/ExternalFileTypesTab.java | 3 +-- .../externalfiletypes/ExternalFileTypesTabViewModel.java | 7 ++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java index cd358279445..36409314e90 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java @@ -89,8 +89,7 @@ public void initialize() { @FXML private void addNewType() { int fileTypeCount = fileTypesTable.getItems().size(); - viewModel.addNewType(); - if (fileTypeCount < fileTypesTable.getItems().size()) { + if (viewModel.addNewType()) { fileTypesTable.getSelectionModel().selectLast(); fileTypesTable.scrollTo(viewModel.getFileTypes().size() - 1); } diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index 2dbd2c24edb..42cd631909f 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -54,22 +54,23 @@ public void resetToDefaults() { fileTypes.sort(Comparator.comparing(ExternalFileTypeItemViewModel::getName)); } - public void addNewType() { + public boolean addNewType() { ExternalFileTypeItemViewModel item = new ExternalFileTypeItemViewModel(); showEditDialog(item, Localization.lang("Add new file type")); if ("".equals(item.getName())) { - return; + return false; } String newExt = item.extensionProperty().get(); for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { if (newExt.equalsIgnoreCase(fileTypeItem.extensionProperty().get())) { - return; + return false; } } fileTypes.add(item); + return true; } public ObservableList getFileTypes() { From 5bf568847f1ff961346e035517a92f0b26a3ca04 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 30 Oct 2023 00:56:44 +0000 Subject: [PATCH 012/144] replace .trim.getEqual() with isblank() --- .../externalfiletypes/EditExternalFileTypeEntryDialog.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java index f205bf09825..8e087ec8023 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java @@ -68,9 +68,9 @@ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, Strin } public boolean isValidExternalFileTypeEntry() { - return !(name.getText().trim().equalsIgnoreCase("") - || extension.getText().trim().equalsIgnoreCase("") - || mimeType.getText().trim().equalsIgnoreCase("")); + return !(name.getText().isBlank() + || extension.getText().isBlank() + || mimeType.getText().isBlank()); } @FXML From ca3ed25a20f5ffc308704589521bbead026ba33c Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Sun, 5 Nov 2023 16:35:26 +0000 Subject: [PATCH 013/144] refactor logic of externalFileType add and edit --- .../EditExternalFileTypeEntryDialog.java | 21 +-------- .../ExternalFileTypesTab.java | 11 ++++- .../ExternalFileTypesTabViewModel.java | 44 ++++++++++++++----- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java index 8e087ec8023..0b9df2b1fe7 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java @@ -33,15 +33,10 @@ public class EditExternalFileTypeEntryDialog extends BaseDialog { private final NativeDesktop nativeDesktop = OS.getNativeDesktop(); private final FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder().withInitialDirectory(nativeDesktop.getApplicationDirectory()).build(); - private final ExternalFileTypeItemViewModel item; - - private final boolean isNewItem; private EditExternalFileTypeViewModel viewModel; public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, String dialogTitle) { - this.isNewItem = item.extensionProperty().get().equals(""); - this.item = item; this.setTitle(dialogTitle); @@ -51,28 +46,15 @@ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, Strin .setAsDialogPane(this); final Button btOk = (Button) this.getDialogPane().lookupButton(ButtonType.OK); - btOk.addEventFilter(ActionEvent.ACTION, event -> { - if (!isValidExternalFileTypeEntry()) { - event.consume(); - } - }); this.setResultConverter(button -> { if (button == ButtonType.OK) { viewModel.storeSettings(); - } else { - name.setText(""); } return null; }); } - public boolean isValidExternalFileTypeEntry() { - return !(name.getText().isBlank() - || extension.getText().isBlank() - || mimeType.getText().isBlank()); - } - @FXML public void initialize() { viewModel = new EditExternalFileTypeViewModel(item); @@ -83,14 +65,13 @@ public void initialize() { customApplication.selectedProperty().bindBidirectional(viewModel.customApplicationSelectedProperty()); selectedApplication.disableProperty().bind(viewModel.defaultApplicationSelectedProperty()); btnBrowse.disableProperty().bind(viewModel.defaultApplicationSelectedProperty()); - extension.textProperty().bindBidirectional(viewModel.extensionProperty()); - extension.setEditable(isNewItem); name.textProperty().bindBidirectional(viewModel.nameProperty()); mimeType.textProperty().bindBidirectional(viewModel.mimeTypeProperty()); selectedApplication.textProperty().bindBidirectional(viewModel.selectedApplicationProperty()); } + @FXML private void openFileChooser(ActionEvent event) { dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(path -> viewModel.selectedApplicationProperty().setValue(path.toAbsolutePath().toString())); diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java index 36409314e90..5a9ba1a6018 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java @@ -78,7 +78,7 @@ public void initialize() { .install(fileTypesTableIconColumn); new ValueTableCellFactory() .withGraphic(none -> IconTheme.JabRefIcons.EDIT.getGraphicNode()) - .withOnMouseClickedEvent((type, none) -> event -> viewModel.edit(type)) + .withOnMouseClickedEvent((type, none) -> event -> editType(type)) .install(fileTypesTableEditColumn); new ValueTableCellFactory() .withGraphic(none -> IconTheme.JabRefIcons.DELETE_ENTRY.getGraphicNode()) @@ -86,15 +86,22 @@ public void initialize() { .install(fileTypesTableDeleteColumn); } + private void editType(ExternalFileTypeItemViewModel type){ + if(viewModel.edit(type)){ + fileTypesTable.getSelectionModel().selectLast(); + fileTypesTable.scrollTo(viewModel.getFileTypes().size() - 1); + } + } + @FXML private void addNewType() { - int fileTypeCount = fileTypesTable.getItems().size(); if (viewModel.addNewType()) { fileTypesTable.getSelectionModel().selectLast(); fileTypesTable.scrollTo(viewModel.getFileTypes().size() - 1); } } + @FXML private void resetToDefault() { viewModel.resetToDefaults(); diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index 42cd631909f..ea95bc04c10 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -8,14 +8,19 @@ import javafx.collections.ObservableList; import org.jabref.gui.DialogService; +import org.jabref.gui.exporter.CreateModifyExporterDialogViewModel; import org.jabref.gui.externalfiletype.ExternalFileType; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.preferences.PreferenceTabViewModel; import org.jabref.logic.l10n.Localization; import org.jabref.preferences.FilePreferences; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class ExternalFileTypesTabViewModel implements PreferenceTabViewModel { + private static final Logger LOGGER = LoggerFactory.getLogger(CreateModifyExporterDialogViewModel.class); private final ObservableList fileTypes = FXCollections.observableArrayList(); private final FilePreferences filePreferences; @@ -58,16 +63,8 @@ public boolean addNewType() { ExternalFileTypeItemViewModel item = new ExternalFileTypeItemViewModel(); showEditDialog(item, Localization.lang("Add new file type")); - if ("".equals(item.getName())) { + if (!isValidExternalFileType(item)) return false; - } - - String newExt = item.extensionProperty().get(); - for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { - if (newExt.equalsIgnoreCase(fileTypeItem.extensionProperty().get())) { - return false; - } - } fileTypes.add(item); return true; @@ -81,11 +78,36 @@ private void showEditDialog(ExternalFileTypeItemViewModel item, String dialogTit dialogService.showCustomDialogAndWait(new EditExternalFileTypeEntryDialog(item, dialogTitle)); } - public void edit(ExternalFileTypeItemViewModel type) { - showEditDialog(type, Localization.lang("Edit file type")); + public boolean edit(ExternalFileTypeItemViewModel type) { + ExternalFileTypeItemViewModel typeToModify = new ExternalFileTypeItemViewModel(type.toExternalFileType()); + showEditDialog(typeToModify, Localization.lang("Edit file type")); + + if (!isValidExternalFileType(typeToModify)) + return false; + + fileTypes.remove(type); + fileTypes.add(typeToModify); + return true; } public void remove(ExternalFileTypeItemViewModel type) { fileTypes.remove(type); } + public boolean isValidExternalFileType(ExternalFileTypeItemViewModel item){ + // Check that there are no empty strings. + if (item.getName().isEmpty() || item.extensionProperty().get().isEmpty() || item.mimetypeProperty().get().isEmpty()) { + LOGGER.info("One of the fields is empty or invalid!"); + return false; + } + + //check extension need to be unique + String newExt = item.extensionProperty().get(); + for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) + if (newExt.equalsIgnoreCase(fileTypeItem.extensionProperty().get())) { + LOGGER.info("File Extension exists already!"); + return false; + } + + return true; + } } From d797e032cdde1946c457c0040b370ae7c8776eb7 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Tue, 7 Nov 2023 02:04:45 +0000 Subject: [PATCH 014/144] add test for preference externalFileType addNewType() and isValidExternalFileType() --- .../ExternalFileTypesTabViewModel.java | 2 +- .../ExternalFileTypeItemViewModelDTO.java | 36 +++++++ .../ExternalFileTypesTabViewModelTest.java | 101 +++++++++++------- 3 files changed, 98 insertions(+), 41 deletions(-) create mode 100644 src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelDTO.java diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index ea95bc04c10..3d20a86ddb3 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -74,7 +74,7 @@ public ObservableList getFileTypes() { return fileTypes; } - private void showEditDialog(ExternalFileTypeItemViewModel item, String dialogTitle) { + protected void showEditDialog(ExternalFileTypeItemViewModel item, String dialogTitle) { dialogService.showCustomDialogAndWait(new EditExternalFileTypeEntryDialog(item, dialogTitle)); } diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelDTO.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelDTO.java new file mode 100644 index 00000000000..5b055cf8189 --- /dev/null +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelDTO.java @@ -0,0 +1,36 @@ +package org.jabref.gui.preferences.externalfiletypes; + +public class ExternalFileTypeItemViewModelDTO { + private ExternalFileTypeItemViewModel externalFileTypeItemViewModel = new ExternalFileTypeItemViewModel(); + + public void setup(){ + externalFileTypeItemViewModel.nameProperty().set("Excel 2007"); + externalFileTypeItemViewModel.extensionProperty().set("xlsx"); + externalFileTypeItemViewModel.mimetypeProperty().set("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + externalFileTypeItemViewModel.applicationProperty().set("oocalc"); + } + public void setupWithoutName(){ + externalFileTypeItemViewModel.nameProperty().set(""); + } + public ExternalFileTypeItemViewModel get(){ + return externalFileTypeItemViewModel; + }; + + public void clone(ExternalFileTypeItemViewModel updatedModel){ + updatedModel.nameProperty().set(externalFileTypeItemViewModel.getName()); + updatedModel.extensionProperty().set(externalFileTypeItemViewModel.extensionProperty().get()); + updatedModel.mimetypeProperty().set(externalFileTypeItemViewModel.mimetypeProperty().get()); + updatedModel.applicationProperty().set(externalFileTypeItemViewModel.applicationProperty().get()); + } + + public boolean isSameValue(ExternalFileTypeItemViewModel item) { + if (!item.getName().equals(externalFileTypeItemViewModel.getName()) + || !item.extensionProperty().get().equals(externalFileTypeItemViewModel.extensionProperty().get()) + || !item.mimetypeProperty().get().equals(externalFileTypeItemViewModel.mimetypeProperty().get()) + || !item.applicationProperty().get().equals(externalFileTypeItemViewModel.applicationProperty().get()) + ) + return false; + else + return true; + } +} diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java index e10a03703fc..c4001df295d 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java @@ -1,67 +1,88 @@ package org.jabref.gui.preferences.externalfiletypes; -import java.util.Set; - -import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import org.jabref.gui.externalfiletype.ExternalFileType; -import org.jabref.gui.externalfiletype.StandardExternalFileType; +import org.jabref.gui.DialogService; import org.jabref.preferences.FilePreferences; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Spy; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.spy; public class ExternalFileTypesTabViewModelTest { - private static final Set TEST_LIST = Set.of( - StandardExternalFileType.MARKDOWN, - StandardExternalFileType.PDF, - StandardExternalFileType.URL, - StandardExternalFileType.JPG, - StandardExternalFileType.TXT); - - private ExternalFileTypesTabViewModel externalFileTypesTabViewModel = mock(ExternalFileTypesTabViewModel.class); + private FilePreferences filePreferences = mock(FilePreferences.class); - private ObservableList fileTypes = FXCollections.observableArrayList(); + private DialogService dialogService = mock(DialogService.class); + @Spy + private ExternalFileTypesTabViewModel externalFileTypesTabViewModel = spy(new ExternalFileTypesTabViewModel(filePreferences, dialogService)); + private ExternalFileTypeItemViewModelDTO externalFileTypeItemViewModel = new ExternalFileTypeItemViewModelDTO(); + @BeforeEach + void setUp() { + //arrange + externalFileTypeItemViewModel.setup(); + } - @BeforeEach - void setUp() { - // Arrange - when(filePreferences.getExternalFileTypes()).thenReturn(FXCollections.observableSet(TEST_LIST)); - fileTypes.addAll(filePreferences.getExternalFileTypes().stream().map(ExternalFileTypeItemViewModel::new).toList()); + @Test + public void whenExternalFileTypeItemViewModelWithNonEmptyStringValueThenisValidExternalFileTypeReturnTrue(){ + //action, assert + assertTrue(externalFileTypesTabViewModel.isValidExternalFileType(externalFileTypeItemViewModel.get())); } - @Test - public void addNewTypeSuccess() { - // Arrange - doAnswer(invocation -> { - ExternalFileTypeItemViewModel item = new ExternalFileTypeItemViewModel(); - fileTypes.add(item); - return null; - }).when(externalFileTypesTabViewModel).addNewType(); + @Test + public void whenExternalFileTypeItemViewModelWithEmptyNameThenisValidExternalFileTypeReturnFalse(){ - // Action - externalFileTypesTabViewModel.addNewType(); + //arrange + externalFileTypeItemViewModel.setupWithoutName(); + //action, assert + assertFalse(externalFileTypesTabViewModel.isValidExternalFileType(externalFileTypeItemViewModel.get())); + } - // Assert - assertEquals(fileTypes.size(), 6); - } + @Test + public void WhenExternalFileTypeItemViewModelIsValidThenAddNewTypeIsSuccessful(){ + + //arrange + ArgumentCaptor worldCaptor = ArgumentCaptor.forClass(ExternalFileTypeItemViewModel.class); + doAnswer(mocked -> { + ExternalFileTypeItemViewModel capturedWorld = worldCaptor.getValue(); + externalFileTypeItemViewModel.clone(capturedWorld); + return null; + }).when(externalFileTypesTabViewModel).showEditDialog(worldCaptor.capture(),any()); + + //action + externalFileTypesTabViewModel.addNewType(); + + //assert + ObservableList actualFileTypes = externalFileTypesTabViewModel.getFileTypes(); + assertEquals(actualFileTypes.size(),1); + assertTrue(externalFileTypeItemViewModel.isSameValue(actualFileTypes.getFirst())); + } @Test - public void addNewTypeFailed() { - // Arrange - doNothing().when(externalFileTypesTabViewModel).addNewType(); + public void WhenExternalFileTypeItemViewModelMissNameThenAddNewTypeIsFailed(){ + + //arrange + externalFileTypeItemViewModel.setupWithoutName(); + ArgumentCaptor worldCaptor = ArgumentCaptor.forClass(ExternalFileTypeItemViewModel.class); + doAnswer(mocked -> { + ExternalFileTypeItemViewModel capturedWorld = worldCaptor.getValue(); + externalFileTypeItemViewModel.clone(capturedWorld); + return null; + }).when(externalFileTypesTabViewModel).showEditDialog(worldCaptor.capture(),any()); - // Action + //action externalFileTypesTabViewModel.addNewType(); - // Assert - assertEquals(fileTypes.size(), 5); + //assert + ObservableList emptyFileTypes = externalFileTypesTabViewModel.getFileTypes(); + assertEquals(emptyFileTypes.size(),0); } } From 2c5de27b196a4cef4c44d8e7a3196efec4671d78 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Tue, 7 Nov 2023 02:11:51 +0000 Subject: [PATCH 015/144] updated changelog.md --- CHANGELOG.md | 2 +- .../ExternalFileTypesTabViewModel.java | 14 +++-- .../ExternalFileTypeItemViewModelDTO.java | 17 ++++--- .../ExternalFileTypesTabViewModelTest.java | 51 +++++++++---------- 4 files changed, 44 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8056057ad2..5d58d354a32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where the added protected term has unwanted leading and trailing whitespaces, where the formatted text has unwanted empty brackets and where the word at the cursor in the textbox can be added to the list. [#10415](https://github.com/JabRef/jabref/issues/10415) - We fixed an issue where in the merge dialog the file field of entries was not correctly merged when the first and second entry both contained values inside the file field. [#10572](https://github.com/JabRef/jabref/issues/10572) - We fixed some small inconsistencies in the user interface. [#10507](https://github.com/JabRef/jabref/issues/10507) [#10458](https://github.com/JabRef/jabref/issues/10458) +- We fixed issues in the external file type dialog, e.g., duplicate entries in the case of a language switch. [#10271](https://github.com/JabRef/jabref/issues/10271) ### Removed @@ -81,7 +82,6 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where there was a failure to access the url link for "eprint" for the ArXiv entry.[#10474](https://github.com/JabRef/jabref/issues/10474) - We fixed an issue where it was not possible to connect to a shared database once a group with entries was added or other metadata modified [#10336](https://github.com/JabRef/jabref/issues/10336) - We fixed an issue where middle-button paste in X not always worked [#7905](https://github.com/JabRef/jabref/issues/7905) -- We fixed issues in the external file type dialog, e.g., duplicate entries in the case of a language switch. [#10271](https://github.com/JabRef/jabref/issues/10271) ## [5.10] – 2023-09-02 diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index 3d20a86ddb3..faa4cdc25dd 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -63,8 +63,9 @@ public boolean addNewType() { ExternalFileTypeItemViewModel item = new ExternalFileTypeItemViewModel(); showEditDialog(item, Localization.lang("Add new file type")); - if (!isValidExternalFileType(item)) + if (!isValidExternalFileType(item)) { return false; + } fileTypes.add(item); return true; @@ -82,8 +83,9 @@ public boolean edit(ExternalFileTypeItemViewModel type) { ExternalFileTypeItemViewModel typeToModify = new ExternalFileTypeItemViewModel(type.toExternalFileType()); showEditDialog(typeToModify, Localization.lang("Edit file type")); - if (!isValidExternalFileType(typeToModify)) + if (!isValidExternalFileType(typeToModify)) { return false; + } fileTypes.remove(type); fileTypes.add(typeToModify); @@ -93,20 +95,22 @@ public boolean edit(ExternalFileTypeItemViewModel type) { public void remove(ExternalFileTypeItemViewModel type) { fileTypes.remove(type); } - public boolean isValidExternalFileType(ExternalFileTypeItemViewModel item){ + + public boolean isValidExternalFileType(ExternalFileTypeItemViewModel item) { // Check that there are no empty strings. if (item.getName().isEmpty() || item.extensionProperty().get().isEmpty() || item.mimetypeProperty().get().isEmpty()) { LOGGER.info("One of the fields is empty or invalid!"); return false; } - //check extension need to be unique + // check extension need to be unique in the list String newExt = item.extensionProperty().get(); - for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) + for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { if (newExt.equalsIgnoreCase(fileTypeItem.extensionProperty().get())) { LOGGER.info("File Extension exists already!"); return false; } + } return true; } diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelDTO.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelDTO.java index 5b055cf8189..4d10c4600de 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelDTO.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelDTO.java @@ -3,20 +3,22 @@ public class ExternalFileTypeItemViewModelDTO { private ExternalFileTypeItemViewModel externalFileTypeItemViewModel = new ExternalFileTypeItemViewModel(); - public void setup(){ + public void setup() { externalFileTypeItemViewModel.nameProperty().set("Excel 2007"); externalFileTypeItemViewModel.extensionProperty().set("xlsx"); externalFileTypeItemViewModel.mimetypeProperty().set("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); externalFileTypeItemViewModel.applicationProperty().set("oocalc"); } - public void setupWithoutName(){ + + public void setupWithoutName() { externalFileTypeItemViewModel.nameProperty().set(""); } - public ExternalFileTypeItemViewModel get(){ + + public ExternalFileTypeItemViewModel get() { return externalFileTypeItemViewModel; - }; + } - public void clone(ExternalFileTypeItemViewModel updatedModel){ + public void clone(ExternalFileTypeItemViewModel updatedModel) { updatedModel.nameProperty().set(externalFileTypeItemViewModel.getName()); updatedModel.extensionProperty().set(externalFileTypeItemViewModel.extensionProperty().get()); updatedModel.mimetypeProperty().set(externalFileTypeItemViewModel.mimetypeProperty().get()); @@ -28,9 +30,10 @@ public boolean isSameValue(ExternalFileTypeItemViewModel item) { || !item.extensionProperty().get().equals(externalFileTypeItemViewModel.extensionProperty().get()) || !item.mimetypeProperty().get().equals(externalFileTypeItemViewModel.mimetypeProperty().get()) || !item.applicationProperty().get().equals(externalFileTypeItemViewModel.applicationProperty().get()) - ) + ) { return false; - else + } else { return true; + } } } diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java index c4001df295d..2dec8f1fcca 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java @@ -27,62 +27,59 @@ public class ExternalFileTypesTabViewModelTest { private ExternalFileTypeItemViewModelDTO externalFileTypeItemViewModel = new ExternalFileTypeItemViewModelDTO(); @BeforeEach void setUp() { - //arrange + // arrange externalFileTypeItemViewModel.setup(); } @Test - public void whenExternalFileTypeItemViewModelWithNonEmptyStringValueThenisValidExternalFileTypeReturnTrue(){ - //action, assert + public void whenExternalFileTypeItemViewModelWithNonEmptyStringValueThenisValidExternalFileTypeReturnTrue() { + // action, assert assertTrue(externalFileTypesTabViewModel.isValidExternalFileType(externalFileTypeItemViewModel.get())); } @Test - public void whenExternalFileTypeItemViewModelWithEmptyNameThenisValidExternalFileTypeReturnFalse(){ - - //arrange + public void whenExternalFileTypeItemViewModelWithEmptyNameThenisValidExternalFileTypeReturnFalse() { + // arrange externalFileTypeItemViewModel.setupWithoutName(); - //action, assert + // action, assert assertFalse(externalFileTypesTabViewModel.isValidExternalFileType(externalFileTypeItemViewModel.get())); } @Test - public void WhenExternalFileTypeItemViewModelIsValidThenAddNewTypeIsSuccessful(){ - - //arrange - ArgumentCaptor worldCaptor = ArgumentCaptor.forClass(ExternalFileTypeItemViewModel.class); + public void WhenExternalFileTypeItemViewModelIsValidThenAddNewTypeIsSuccessful() { + // arrange + ArgumentCaptor itemCaptor = ArgumentCaptor.forClass(ExternalFileTypeItemViewModel.class); doAnswer(mocked -> { - ExternalFileTypeItemViewModel capturedWorld = worldCaptor.getValue(); - externalFileTypeItemViewModel.clone(capturedWorld); + ExternalFileTypeItemViewModel capturedItem = itemCaptor.getValue(); + externalFileTypeItemViewModel.clone(capturedItem); return null; - }).when(externalFileTypesTabViewModel).showEditDialog(worldCaptor.capture(),any()); + }).when(externalFileTypesTabViewModel).showEditDialog(itemCaptor.capture(), any()); - //action + // action externalFileTypesTabViewModel.addNewType(); - //assert + // assert ObservableList actualFileTypes = externalFileTypesTabViewModel.getFileTypes(); - assertEquals(actualFileTypes.size(),1); + assertEquals(actualFileTypes.size(), 1); assertTrue(externalFileTypeItemViewModel.isSameValue(actualFileTypes.getFirst())); } @Test - public void WhenExternalFileTypeItemViewModelMissNameThenAddNewTypeIsFailed(){ - - //arrange + public void WhenExternalFileTypeItemViewModelMissNameThenAddNewTypeIsFailed() { + // arrange externalFileTypeItemViewModel.setupWithoutName(); - ArgumentCaptor worldCaptor = ArgumentCaptor.forClass(ExternalFileTypeItemViewModel.class); + ArgumentCaptor itemCaptor = ArgumentCaptor.forClass(ExternalFileTypeItemViewModel.class); doAnswer(mocked -> { - ExternalFileTypeItemViewModel capturedWorld = worldCaptor.getValue(); - externalFileTypeItemViewModel.clone(capturedWorld); + ExternalFileTypeItemViewModel capturedItem = itemCaptor.getValue(); + externalFileTypeItemViewModel.clone(capturedItem); return null; - }).when(externalFileTypesTabViewModel).showEditDialog(worldCaptor.capture(),any()); + }).when(externalFileTypesTabViewModel).showEditDialog(itemCaptor.capture(), any()); - //action + // action externalFileTypesTabViewModel.addNewType(); - //assert + // assert ObservableList emptyFileTypes = externalFileTypesTabViewModel.getFileTypes(); - assertEquals(emptyFileTypes.size(),0); + assertEquals(emptyFileTypes.size(), 0); } } From ca54d1753563eab56d1661e456a8f549b20b2417 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Tue, 7 Nov 2023 02:27:36 +0000 Subject: [PATCH 016/144] updated codestyle of ExternalFileTypesTab.java --- .../preferences/externalfiletypes/ExternalFileTypesTab.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java index 5a9ba1a6018..4e5913c6d2a 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java @@ -86,8 +86,8 @@ public void initialize() { .install(fileTypesTableDeleteColumn); } - private void editType(ExternalFileTypeItemViewModel type){ - if(viewModel.edit(type)){ + private void editType(ExternalFileTypeItemViewModel type) { + if (viewModel.edit(type)) { fileTypesTable.getSelectionModel().selectLast(); fileTypesTable.scrollTo(viewModel.getFileTypes().size() - 1); } @@ -101,7 +101,6 @@ private void addNewType() { } } - @FXML private void resetToDefault() { viewModel.resetToDefaults(); From 7da377972b333d1cc07a06943035550fc651cad1 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Tue, 7 Nov 2023 02:33:43 +0000 Subject: [PATCH 017/144] updated codestyle of ExternalFileTypesEntryDialog.java --- .../externalfiletypes/EditExternalFileTypeEntryDialog.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java index 0b9df2b1fe7..f9f901a1bf1 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java @@ -71,7 +71,6 @@ public void initialize() { selectedApplication.textProperty().bindBidirectional(viewModel.selectedApplicationProperty()); } - @FXML private void openFileChooser(ActionEvent event) { dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(path -> viewModel.selectedApplicationProperty().setValue(path.toAbsolutePath().toString())); From 4f7ce6dcaa48f9ab48f466c0957c90cec78f9011 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Tue, 7 Nov 2023 09:23:21 +0000 Subject: [PATCH 018/144] rename externalFileTypes test data file --- ...ModelDTO.java => ExternalFileTypeItemViewModelTestData.java} | 2 +- .../externalfiletypes/ExternalFileTypesTabViewModelTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/test/java/org/jabref/gui/preferences/externalfiletypes/{ExternalFileTypeItemViewModelDTO.java => ExternalFileTypeItemViewModelTestData.java} (97%) diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelDTO.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java similarity index 97% rename from src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelDTO.java rename to src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java index 4d10c4600de..171f319fb09 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelDTO.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java @@ -1,6 +1,6 @@ package org.jabref.gui.preferences.externalfiletypes; -public class ExternalFileTypeItemViewModelDTO { +public class ExternalFileTypeItemViewModelTestData { private ExternalFileTypeItemViewModel externalFileTypeItemViewModel = new ExternalFileTypeItemViewModel(); public void setup() { diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java index 2dec8f1fcca..779990d7a21 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java @@ -24,7 +24,7 @@ public class ExternalFileTypesTabViewModelTest { private DialogService dialogService = mock(DialogService.class); @Spy private ExternalFileTypesTabViewModel externalFileTypesTabViewModel = spy(new ExternalFileTypesTabViewModel(filePreferences, dialogService)); - private ExternalFileTypeItemViewModelDTO externalFileTypeItemViewModel = new ExternalFileTypeItemViewModelDTO(); + private ExternalFileTypeItemViewModelTestData externalFileTypeItemViewModel = new ExternalFileTypeItemViewModelTestData(); @BeforeEach void setUp() { // arrange From 74aa42d8396eaf252e9f62ab2ddfc2563c3300e7 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Tue, 7 Nov 2023 09:34:24 +0000 Subject: [PATCH 019/144] gradle rewrite for some code formatting --- .../ExternalFileTypeItemViewModelTestData.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java index 171f319fb09..02a43e3fe09 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java @@ -26,14 +26,9 @@ public void clone(ExternalFileTypeItemViewModel updatedModel) { } public boolean isSameValue(ExternalFileTypeItemViewModel item) { - if (!item.getName().equals(externalFileTypeItemViewModel.getName()) - || !item.extensionProperty().get().equals(externalFileTypeItemViewModel.extensionProperty().get()) - || !item.mimetypeProperty().get().equals(externalFileTypeItemViewModel.mimetypeProperty().get()) - || !item.applicationProperty().get().equals(externalFileTypeItemViewModel.applicationProperty().get()) - ) { - return false; - } else { - return true; - } + return !(!item.getName().equals(externalFileTypeItemViewModel.getName()) + || !item.extensionProperty().get().equals(externalFileTypeItemViewModel.extensionProperty().get()) + || !item.mimetypeProperty().get().equals(externalFileTypeItemViewModel.mimetypeProperty().get()) + || !item.applicationProperty().get().equals(externalFileTypeItemViewModel.applicationProperty().get())); } } From c9f130c00d481ef15aecc2ac14ce304990e3d880 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Tue, 7 Nov 2023 09:54:21 +0000 Subject: [PATCH 020/144] refactor folder structure of ExternalFileTypeItemViewModelTestData --- .../externalfiletypes/ExternalFileTypesTabViewModelTest.java | 1 + .../ExternalFileTypeItemViewModelTestData.java | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) rename src/test/java/org/jabref/{gui/preferences/externalfiletypes => model}/ExternalFileTypeItemViewModelTestData.java (94%) diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java index 779990d7a21..29b43793d64 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java @@ -3,6 +3,7 @@ import javafx.collections.ObservableList; import org.jabref.gui.DialogService; +import org.jabref.model.ExternalFileTypeItemViewModelTestData; import org.jabref.preferences.FilePreferences; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java b/src/test/java/org/jabref/model/ExternalFileTypeItemViewModelTestData.java similarity index 94% rename from src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java rename to src/test/java/org/jabref/model/ExternalFileTypeItemViewModelTestData.java index 02a43e3fe09..22047736b31 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java +++ b/src/test/java/org/jabref/model/ExternalFileTypeItemViewModelTestData.java @@ -1,4 +1,6 @@ -package org.jabref.gui.preferences.externalfiletypes; +package org.jabref.model; + +import org.jabref.gui.preferences.externalfiletypes.ExternalFileTypeItemViewModel; public class ExternalFileTypeItemViewModelTestData { private ExternalFileTypeItemViewModel externalFileTypeItemViewModel = new ExternalFileTypeItemViewModel(); From 52b1ad736b146876338cbf904f484040d7c68121 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Tue, 7 Nov 2023 10:16:09 +0000 Subject: [PATCH 021/144] reverse change of folder structure of ExternalFileTypeItemViewModelTestData --- .../ExternalFileTypeItemViewModelTestData.java | 2 +- .../externalfiletypes/ExternalFileTypesTabViewModelTest.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) rename src/test/java/org/jabref/{model => gui/preferences/externalfiletypes}/ExternalFileTypeItemViewModelTestData.java (97%) diff --git a/src/test/java/org/jabref/model/ExternalFileTypeItemViewModelTestData.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java similarity index 97% rename from src/test/java/org/jabref/model/ExternalFileTypeItemViewModelTestData.java rename to src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java index 22047736b31..c8fd873729f 100644 --- a/src/test/java/org/jabref/model/ExternalFileTypeItemViewModelTestData.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java @@ -1,4 +1,4 @@ -package org.jabref.model; +package org.jabref.gui.preferences.externalfiletypes; import org.jabref.gui.preferences.externalfiletypes.ExternalFileTypeItemViewModel; diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java index 29b43793d64..779990d7a21 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java @@ -3,7 +3,6 @@ import javafx.collections.ObservableList; import org.jabref.gui.DialogService; -import org.jabref.model.ExternalFileTypeItemViewModelTestData; import org.jabref.preferences.FilePreferences; import org.junit.jupiter.api.BeforeEach; From 1cbb1e2be283794d0642d4df8e9a73e5fc82de68 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Tue, 7 Nov 2023 10:30:07 +0000 Subject: [PATCH 022/144] remove extra import statement from ExternalFileTypeItemViewModelTestData.java --- .../ExternalFileTypeItemViewModelTestData.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java index c8fd873729f..3bf8b15349d 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java @@ -1,7 +1,4 @@ package org.jabref.gui.preferences.externalfiletypes; - -import org.jabref.gui.preferences.externalfiletypes.ExternalFileTypeItemViewModel; - public class ExternalFileTypeItemViewModelTestData { private ExternalFileTypeItemViewModel externalFileTypeItemViewModel = new ExternalFileTypeItemViewModel(); From afa9a5c666cc5d4ae33bd18ccffa051287f32ff8 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Fri, 10 Nov 2023 06:52:52 +0000 Subject: [PATCH 023/144] add ExternalFileTypesTabViewModelTestData to TestArchitectureTest --- src/test/java/org/jabref/architecture/TestArchitectureTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/org/jabref/architecture/TestArchitectureTest.java b/src/test/java/org/jabref/architecture/TestArchitectureTest.java index 81f73e5a117..b9d87deb659 100644 --- a/src/test/java/org/jabref/architecture/TestArchitectureTest.java +++ b/src/test/java/org/jabref/architecture/TestArchitectureTest.java @@ -48,6 +48,7 @@ public void testNaming(JavaClasses classes) { .and().doNotHaveFullyQualifiedName("org.jabref.logic.shared.TestManager") .and().doNotHaveFullyQualifiedName("org.jabref.model.search.rules.MockSearchMatcher") .and().doNotHaveFullyQualifiedName("org.jabref.model.TreeNodeTestData") + .and().doNotHaveFullyQualifiedName("org.jabref.model.ExternalFileTypesTabViewModelTestData") .and().doNotHaveFullyQualifiedName("org.jabref.performance.BibtexEntryGenerator") .and().doNotHaveFullyQualifiedName("org.jabref.support.DisabledOnCIServer") .and().doNotHaveFullyQualifiedName("org.jabref.support.CIServerCondition") From 7eb9eeb329b7205110eb223b0acb205ead105b9d Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Fri, 10 Nov 2023 07:01:35 +0000 Subject: [PATCH 024/144] Update CHANGELOG.md Co-authored-by: Oliver Kopp --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f173157a9b..48605f246fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where the added protected term has unwanted leading and trailing whitespaces, where the formatted text has unwanted empty brackets and where the word at the cursor in the textbox can be added to the list. [#10415](https://github.com/JabRef/jabref/issues/10415) - We fixed an issue where in the merge dialog the file field of entries was not correctly merged when the first and second entry both contained values inside the file field. [#10572](https://github.com/JabRef/jabref/issues/10572) - We fixed some small inconsistencies in the user interface. [#10507](https://github.com/JabRef/jabref/issues/10507) [#10458](https://github.com/JabRef/jabref/issues/10458) -- We fixed issues in the external file type dialog, e.g., duplicate entries in the case of a language switch. [#10271](https://github.com/JabRef/jabref/issues/10271) +- We fixed issues in the external file type dialog w.r.t. duplicate entries in the case of a language switch. [#10271](https://github.com/JabRef/jabref/issues/10271) ### Removed From c290c86f96caaa4fa9d2806557151447a1bb6420 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Fri, 10 Nov 2023 07:05:01 +0000 Subject: [PATCH 025/144] remove unnecessary variable --- .../externalfiletypes/EditExternalFileTypeEntryDialog.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java index f9f901a1bf1..d1008ebfeaa 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java @@ -45,8 +45,6 @@ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, Strin .load() .setAsDialogPane(this); - final Button btOk = (Button) this.getDialogPane().lookupButton(ButtonType.OK); - this.setResultConverter(button -> { if (button == ButtonType.OK) { viewModel.storeSettings(); From 490a8a5c48364f6f4b137861f7f16edc9f204774 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Fri, 10 Nov 2023 07:10:19 +0000 Subject: [PATCH 026/144] reformat code --- .../ExternalFileTypesTabViewModelTest.java | 62 ++++++++----------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java index 779990d7a21..76627f52833 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java @@ -22,51 +22,45 @@ public class ExternalFileTypesTabViewModelTest { private FilePreferences filePreferences = mock(FilePreferences.class); private DialogService dialogService = mock(DialogService.class); + @Spy private ExternalFileTypesTabViewModel externalFileTypesTabViewModel = spy(new ExternalFileTypesTabViewModel(filePreferences, dialogService)); private ExternalFileTypeItemViewModelTestData externalFileTypeItemViewModel = new ExternalFileTypeItemViewModelTestData(); - @BeforeEach - void setUp() { - // arrange - externalFileTypeItemViewModel.setup(); - } + + @BeforeEach + void setUp() { + externalFileTypeItemViewModel.setup(); + } @Test public void whenExternalFileTypeItemViewModelWithNonEmptyStringValueThenisValidExternalFileTypeReturnTrue() { - // action, assert assertTrue(externalFileTypesTabViewModel.isValidExternalFileType(externalFileTypeItemViewModel.get())); } - @Test - public void whenExternalFileTypeItemViewModelWithEmptyNameThenisValidExternalFileTypeReturnFalse() { - // arrange - externalFileTypeItemViewModel.setupWithoutName(); - // action, assert - assertFalse(externalFileTypesTabViewModel.isValidExternalFileType(externalFileTypeItemViewModel.get())); - } - - @Test - public void WhenExternalFileTypeItemViewModelIsValidThenAddNewTypeIsSuccessful() { - // arrange - ArgumentCaptor itemCaptor = ArgumentCaptor.forClass(ExternalFileTypeItemViewModel.class); - doAnswer(mocked -> { - ExternalFileTypeItemViewModel capturedItem = itemCaptor.getValue(); - externalFileTypeItemViewModel.clone(capturedItem); - return null; - }).when(externalFileTypesTabViewModel).showEditDialog(itemCaptor.capture(), any()); - - // action - externalFileTypesTabViewModel.addNewType(); - - // assert - ObservableList actualFileTypes = externalFileTypesTabViewModel.getFileTypes(); - assertEquals(actualFileTypes.size(), 1); - assertTrue(externalFileTypeItemViewModel.isSameValue(actualFileTypes.getFirst())); - } + @Test + public void whenExternalFileTypeItemViewModelWithEmptyNameThenisValidExternalFileTypeReturnFalse() { + externalFileTypeItemViewModel.setupWithoutName(); + assertFalse(externalFileTypesTabViewModel.isValidExternalFileType(externalFileTypeItemViewModel.get())); + } + + @Test + public void WhenExternalFileTypeItemViewModelIsValidThenAddNewTypeIsSuccessful() { + ArgumentCaptor itemCaptor = ArgumentCaptor.forClass(ExternalFileTypeItemViewModel.class); + doAnswer(mocked -> { + ExternalFileTypeItemViewModel capturedItem = itemCaptor.getValue(); + externalFileTypeItemViewModel.clone(capturedItem); + return null; + }).when(externalFileTypesTabViewModel).showEditDialog(itemCaptor.capture(), any()); + + externalFileTypesTabViewModel.addNewType(); + + ObservableList actualFileTypes = externalFileTypesTabViewModel.getFileTypes(); + assertEquals(actualFileTypes.size(), 1); + assertTrue(externalFileTypeItemViewModel.isSameValue(actualFileTypes.getFirst())); + } @Test public void WhenExternalFileTypeItemViewModelMissNameThenAddNewTypeIsFailed() { - // arrange externalFileTypeItemViewModel.setupWithoutName(); ArgumentCaptor itemCaptor = ArgumentCaptor.forClass(ExternalFileTypeItemViewModel.class); doAnswer(mocked -> { @@ -75,10 +69,8 @@ public void WhenExternalFileTypeItemViewModelMissNameThenAddNewTypeIsFailed() { return null; }).when(externalFileTypesTabViewModel).showEditDialog(itemCaptor.capture(), any()); - // action externalFileTypesTabViewModel.addNewType(); - // assert ObservableList emptyFileTypes = externalFileTypesTabViewModel.getFileTypes(); assertEquals(emptyFileTypes.size(), 0); } From 8b89bf6d9090e3ee2fc57f90c4e36837a7aee26c Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Fri, 10 Nov 2023 07:20:20 +0000 Subject: [PATCH 027/144] refactor TestArchitectureTest --- src/test/java/org/jabref/architecture/TestArchitectureTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/jabref/architecture/TestArchitectureTest.java b/src/test/java/org/jabref/architecture/TestArchitectureTest.java index b9d87deb659..3a69b760303 100644 --- a/src/test/java/org/jabref/architecture/TestArchitectureTest.java +++ b/src/test/java/org/jabref/architecture/TestArchitectureTest.java @@ -38,6 +38,7 @@ public void testNaming(JavaClasses classes) { .and().doNotHaveFullyQualifiedName("org.jabref.benchmarks.Benchmarks") .and().doNotHaveFullyQualifiedName("org.jabref.http.server.TestBibFile") .and().doNotHaveFullyQualifiedName("org.jabref.gui.autocompleter.AutoCompleterUtil") + .and().doNotHaveFullyQualifiedName("org.jabref.gui.preferences.externalfiletypes.ExternalFileTypeItemViewModelTestData") .and().doNotHaveFullyQualifiedName("org.jabref.gui.search.TextFlowEqualityHelper") .and().doNotHaveFullyQualifiedName("org.jabref.logic.bibtex.BibEntryAssert") .and().doNotHaveFullyQualifiedName("org.jabref.logic.importer.fileformat.ImporterTestEngine") @@ -48,7 +49,6 @@ public void testNaming(JavaClasses classes) { .and().doNotHaveFullyQualifiedName("org.jabref.logic.shared.TestManager") .and().doNotHaveFullyQualifiedName("org.jabref.model.search.rules.MockSearchMatcher") .and().doNotHaveFullyQualifiedName("org.jabref.model.TreeNodeTestData") - .and().doNotHaveFullyQualifiedName("org.jabref.model.ExternalFileTypesTabViewModelTestData") .and().doNotHaveFullyQualifiedName("org.jabref.performance.BibtexEntryGenerator") .and().doNotHaveFullyQualifiedName("org.jabref.support.DisabledOnCIServer") .and().doNotHaveFullyQualifiedName("org.jabref.support.CIServerCondition") From a3ab7d5c36e5b6d212a5de8b8706b81d7ec9362e Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 17 Nov 2023 16:53:39 +0100 Subject: [PATCH 028/144] Remove comments - don't work on forks --- .github/workflows/tests.yml | 58 ------------------------------------- 1 file changed, 58 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4e81d87e865..86ae1543a1a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -49,20 +49,6 @@ jobs: checkstyle_version: '10.3' - name: Run checkstyle using gradle run: ./gradlew checkstyleMain checkstyleTest checkstyleJmh - - name: Add comment on pull request - if: ${{ failure() }} - uses: thollander/actions-comment-pull-request@v2 - with: - message: > - Your code currently does not meet JabRef's code guidelines. - We use [Checkstyle](https://checkstyle.sourceforge.io/) to identify issues. - The tool reviewdog already placed comments on GitHub to indicate the places. See the tab "Files" in you PR. - Please carefully follow [the setup guide for the codestyle](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-13-code-style.html). - Afterwards, please run checkstyle locally and fix the issues. - - - More information on code quality in JabRef is available at . - comment_tag: checkstyle openrewrite: name: OpenRewrite runs-on: ubuntu-latest @@ -81,19 +67,6 @@ jobs: - name: Run OpenRewrite run: | ./gradlew rewriteDryRun - - name: Add comment on pull request - if: ${{ failure() }} - uses: thollander/actions-comment-pull-request@v2 - with: - message: > - Your code currently does not meet JabRef's code guidelines. - We use [OpenRewrite](https://docs.openrewrite.org/) to ensure "modern" Java coding practices. - The issues found can be **automatically fixed**. - Please execute the gradle task *`rewriteRun`*, check the results, commit, and push. - - - You can check the detailed error output at the tab "Checks", section "Tests" (on the left), subsection "OpenRewrite". - comment_tag: openrewrite modernizer: name: Modernizer runs-on: ubuntu-latest @@ -114,18 +87,6 @@ jobs: # enable failing of this task if modernizer complains sed -i "s/failOnViolations = false/failOnViolations = true/" build.gradle ./gradlew modernizer - - name: Add comment on pull request - if: ${{ failure() }} - uses: thollander/actions-comment-pull-request@v2 - with: - message: > - Your code currently does not meet JabRef's code guidelines. - We use [Gradle Modernizer Plugin](https://github.com/andygoossens/gradle-modernizer-plugin#gradle-modernizer-plugin) to ensure "modern" Java coding practices. - Please fix the detected errors, commit, and push. - - - You can check the detailed error output at the tab "Checks", section "Tests" (on the left), subsection "Modernizer". - comment_tag: modernizer markdown: name: Markdown runs-on: ubuntu-latest @@ -141,17 +102,6 @@ jobs: globs: | *.md docs/**/*.md - - name: Add comment on pull request - if: ${{ failure() }} - uses: thollander/actions-comment-pull-request@v2 - with: - message: > - You modified Markdown (*.md) files. - To ensure consistent styling, we have [markdown-lint](https://github.com/DavidAnson/markdownlint) in place. - [Markdown lint's rules](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#rules) help to keep our Markdown files consistent within this repository and consistent with the Markdown files outside here. - - You can check the detailed error output at the tab "Checks", section "Tests" (on the left), subsection "Markdown". - comment_tag: markdown changelog: name: CHANGELOG.md runs-on: ubuntu-latest @@ -198,14 +148,6 @@ jobs: diff \ <(git show origin/main:CHANGELOG.md | clparse --format=json --separator=– - | jq '.releases[] | select(.version != null)') \ <(git show HEAD:CHANGELOG.md | clparse --format=json --separator=– - | jq '.releases[] | select(.version != null)') - - name: Add comment on pull request - if: ${{ failure() }} - uses: thollander/actions-comment-pull-request@v2 - with: - message: > - While the PR was in progress, JabRef released a new version. - You have to merge `upstream/main` and move your entry in `CHANGELOG.md` to section `## [Unreleased]`. - comment_tag: changelog-unreleased-only tests: name: Unit tests runs-on: ubuntu-latest From f300b6f6552f2bc37588020b1609f488cfc4400b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:11:01 +0000 Subject: [PATCH 029/144] Bump com.github.Dansoftowner:jSystemThemeDetector from 3.6 to 3.8 Bumps [com.github.Dansoftowner:jSystemThemeDetector](https://github.com/Dansoftowner/jSystemThemeDetector) from 3.6 to 3.8. - [Release notes](https://github.com/Dansoftowner/jSystemThemeDetector/releases) - [Commits](https://github.com/Dansoftowner/jSystemThemeDetector/compare/3.6...3.8) --- updated-dependencies: - dependency-name: com.github.Dansoftowner:jSystemThemeDetector dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c72f3c74bd0..1b3b37242f5 100644 --- a/build.gradle +++ b/build.gradle @@ -180,7 +180,7 @@ dependencies { } implementation 'org.controlsfx:controlsfx:11.2.0' - implementation 'com.github.Dansoftowner:jSystemThemeDetector:3.6' + implementation 'com.github.Dansoftowner:jSystemThemeDetector:3.8' implementation 'org.jsoup:jsoup:1.16.2' implementation 'com.konghq:unirest-java:3.14.5' From b639d8f0999a857af51494d62c61917640c038de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:20:00 +0000 Subject: [PATCH 030/144] Bump com.fasterxml.jackson.dataformat:jackson-dataformat-yaml (#10653) Bumps [com.fasterxml.jackson.dataformat:jackson-dataformat-yaml](https://github.com/FasterXML/jackson-dataformats-text) from 2.15.3 to 2.16.0. - [Commits](https://github.com/FasterXML/jackson-dataformats-text/compare/jackson-dataformats-text-2.15.3...jackson-dataformats-text-2.16.0) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c72f3c74bd0..56bfbeff40f 100644 --- a/build.gradle +++ b/build.gradle @@ -144,7 +144,7 @@ dependencies { implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '6.7.0.202309050840-r' - implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.15.3' + implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.16.0' implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.15.3' implementation 'com.fasterxml:aalto-xml:1.3.2' From d6a8ffad2f7d1edd84b548a27dd64835f700c3fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:23:09 +0000 Subject: [PATCH 031/144] Bump com.puppycrawl.tools:checkstyle from 10.12.4 to 10.12.5 (#10654) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 10.12.4 to 10.12.5. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.12.4...checkstyle-10.12.5) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 56bfbeff40f..c34b01ac576 100644 --- a/build.gradle +++ b/build.gradle @@ -248,7 +248,7 @@ dependencies { testImplementation "org.testfx:testfx-junit5:4.0.16-alpha" testImplementation "org.hamcrest:hamcrest-library:2.2" - checkstyle 'com.puppycrawl.tools:checkstyle:10.12.4' + checkstyle 'com.puppycrawl.tools:checkstyle:10.12.5' // xjc needs the runtime as well for the ant task, otherwise it fails xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2' xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2' From 77a360a9f1fcb2cdf7387c649452c5b67788f679 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 00:58:28 +0100 Subject: [PATCH 032/144] Bump org.openrewrite.rewrite from 6.4.0 to 6.5.4 (#10650) * Bump org.openrewrite.rewrite from 6.4.0 to 6.5.4 Bumps org.openrewrite.rewrite from 6.4.0 to 6.5.4. --- updated-dependencies: - dependency-name: org.openrewrite.rewrite dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Bump org.openrewrite.recipe:rewrite-recipe-bom from 2.4.1 to 2.5.0 Bumps [org.openrewrite.recipe:rewrite-recipe-bom](https://github.com/openrewrite/rewrite-recipe-bom) from 2.4.1 to 2.5.0. - [Release notes](https://github.com/openrewrite/rewrite-recipe-bom/releases) - [Commits](https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.4.1...v2.5.0) --- updated-dependencies: - dependency-name: org.openrewrite.recipe:rewrite-recipe-bom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Apply one fix only * Disable cast rule --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Oliver Kopp --- build.gradle | 4 ++-- buildSrc/src/main/groovy/org/jabref/build/xjc/XjcTask.groovy | 5 +++-- rewrite.yml | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 7f09e8154f6..800fa9fe067 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ plugins { id 'idea' - id 'org.openrewrite.rewrite' version '6.4.0' + id 'org.openrewrite.rewrite' version '6.5.4' } // Enable following for debugging @@ -253,7 +253,7 @@ dependencies { xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2' xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2' - rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.4.1")) + rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.5.0")) rewrite("org.openrewrite.recipe:rewrite-static-analysis") rewrite("org.openrewrite.recipe:rewrite-logging-frameworks") rewrite("org.openrewrite.recipe:rewrite-testing-frameworks") diff --git a/buildSrc/src/main/groovy/org/jabref/build/xjc/XjcTask.groovy b/buildSrc/src/main/groovy/org/jabref/build/xjc/XjcTask.groovy index 8b68e35ab98..fb7bbf7cdb6 100644 --- a/buildSrc/src/main/groovy/org/jabref/build/xjc/XjcTask.groovy +++ b/buildSrc/src/main/groovy/org/jabref/build/xjc/XjcTask.groovy @@ -103,8 +103,9 @@ class XjcTask extends DefaultTask { } private void updateOutput() { - if (outputDirectory != null && javaPackage != null) - outputs.dir(new File(getOutputDirectory(), packageAsPath(javaPackage))) + if (outputDirectory != null && javaPackage != null) { + outputs.dir(new File(getOutputDirectory(), packageAsPath(javaPackage))) + } } private static String packageAsPath(String pkg) { diff --git a/rewrite.yml b/rewrite.yml index 7d5eaad4866..1b146b619a3 100644 --- a/rewrite.yml +++ b/rewrite.yml @@ -163,7 +163,7 @@ recipeList: - org.openrewrite.staticanalysis.RemoveExtraSemicolons - org.openrewrite.staticanalysis.RemoveJavaDocAuthorTag - org.openrewrite.staticanalysis.RemoveHashCodeCallsFromArrayInstances - - org.openrewrite.staticanalysis.RemoveRedundantTypeCast +# - org.openrewrite.staticanalysis.RemoveRedundantTypeCast - org.openrewrite.staticanalysis.RemoveToStringCallsFromArrayInstances - org.openrewrite.staticanalysis.RemoveUnneededAssertion - org.openrewrite.staticanalysis.RemoveUnneededBlock From 75107ab69b1d02c2386019432d03333fcae81a0d Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 21 Nov 2023 09:27:50 +0100 Subject: [PATCH 033/144] Clean up test code (#10658) * Enable org.openrewrite.java.testing.junit5.CleanupAssertions * Fix indent * Update rewrite.yml --- rewrite.yml | 3 +++ .../gui/externalfiles/FileFilterUtilsTest.java | 18 ++++++++++-------- .../maintable/MainTableColumnModelTest.java | 2 +- .../gui/sidepane/SidePaneViewModelTest.java | 12 ++++++------ .../org/jabref/gui/theme/ThemeManagerTest.java | 4 ++-- .../bibtex/comparator/BibStringDiffTest.java | 8 +++----- .../logic/database/DatabaseMergerTest.java | 4 ++-- .../fetcher/IacrEprintFetcherTest.java | 2 +- .../logic/journals/AbbreviationTest.java | 8 ++++---- .../logic/layout/format/ReplaceTest.java | 3 ++- .../jabref/logic/msbib/MsBibAuthorTest.java | 5 +++-- .../java/org/jabref/logic/net/ProxyTest.java | 5 +++-- .../java/org/jabref/model/FieldChangeTest.java | 2 +- .../org/jabref/model/entry/BibEntryTest.java | 6 +++--- .../model/openoffice/CitationEntryTest.java | 2 +- .../jabref/model/strings/StringUtilTest.java | 3 ++- 16 files changed, 47 insertions(+), 40 deletions(-) diff --git a/rewrite.yml b/rewrite.yml index 1b146b619a3..bcb32628d3c 100644 --- a/rewrite.yml +++ b/rewrite.yml @@ -195,3 +195,6 @@ recipeList: - org.openrewrite.staticanalysis.UseSystemLineSeparator - org.openrewrite.staticanalysis.WhileInsteadOfFor # - org.openrewrite.staticanalysis.WriteOctalValuesAsDecimal + + - org.openrewrite.java.testing.junit5.CleanupAssertions +# - org.openrewrite.java.testing.junit5.JUnit5BestPractices diff --git a/src/test/java/org/jabref/gui/externalfiles/FileFilterUtilsTest.java b/src/test/java/org/jabref/gui/externalfiles/FileFilterUtilsTest.java index 9af4b1364bb..9deab592e8f 100755 --- a/src/test/java/org/jabref/gui/externalfiles/FileFilterUtilsTest.java +++ b/src/test/java/org/jabref/gui/externalfiles/FileFilterUtilsTest.java @@ -16,7 +16,9 @@ import org.junit.jupiter.api.io.TempDir; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class FileFilterUtilsTest { @@ -25,42 +27,42 @@ public class FileFilterUtilsTest { @Test public void isDuringLastDayNegativeTest() { - assertEquals(fileFilterUtils.isDuringLastDay(time.minusHours(24)), false); + assertFalse(fileFilterUtils.isDuringLastDay(time.minusHours(24))); } @Test public void isDuringLastDayPositiveTest() { - assertEquals(fileFilterUtils.isDuringLastDay(time.minusHours(23)), true); + assertTrue(fileFilterUtils.isDuringLastDay(time.minusHours(23))); } @Test public void isDuringLastWeekNegativeTest() { - assertEquals(fileFilterUtils.isDuringLastWeek(time.minusDays(7)), false); + assertFalse(fileFilterUtils.isDuringLastWeek(time.minusDays(7))); } @Test public void isDuringLastWeekPositiveTest() { - assertEquals(fileFilterUtils.isDuringLastWeek(time.minusDays(6).minusHours(23)), true); + assertTrue(fileFilterUtils.isDuringLastWeek(time.minusDays(6).minusHours(23))); } @Test public void isDuringLastMonthNegativeTest() { - assertEquals(fileFilterUtils.isDuringLastMonth(time.minusDays(30)), false); + assertFalse(fileFilterUtils.isDuringLastMonth(time.minusDays(30))); } @Test public void isDuringLastMonthPositiveTest() { - assertEquals(fileFilterUtils.isDuringLastMonth(time.minusDays(29).minusHours(23)), true); + assertTrue(fileFilterUtils.isDuringLastMonth(time.minusDays(29).minusHours(23))); } @Test public void isDuringLastYearNegativeTest() { - assertEquals(fileFilterUtils.isDuringLastYear(time.minusDays(365)), false); + assertFalse(fileFilterUtils.isDuringLastYear(time.minusDays(365))); } @Test public void isDuringLastYearPositiveTest() { - assertEquals(fileFilterUtils.isDuringLastYear(time.minusDays(364).minusHours(23)), true); + assertTrue(fileFilterUtils.isDuringLastYear(time.minusDays(364).minusHours(23))); } @Nested diff --git a/src/test/java/org/jabref/gui/maintable/MainTableColumnModelTest.java b/src/test/java/org/jabref/gui/maintable/MainTableColumnModelTest.java index a344886401a..750d92708fb 100644 --- a/src/test/java/org/jabref/gui/maintable/MainTableColumnModelTest.java +++ b/src/test/java/org/jabref/gui/maintable/MainTableColumnModelTest.java @@ -52,6 +52,6 @@ public void typeOnlyMainTableColumnModelParserRetrievesCorrectType() { public void typeOnlyMainTableColumnModelParserRetrievesCorrectQualifier() { MainTableColumnModel testColumnModel = MainTableColumnModel.parse(testTypeOnlyName); - assertEquals(testColumnModel.getQualifier(), ""); + assertEquals("", testColumnModel.getQualifier()); } } diff --git a/src/test/java/org/jabref/gui/sidepane/SidePaneViewModelTest.java b/src/test/java/org/jabref/gui/sidepane/SidePaneViewModelTest.java index 27db78fe64d..0419752b78e 100644 --- a/src/test/java/org/jabref/gui/sidepane/SidePaneViewModelTest.java +++ b/src/test/java/org/jabref/gui/sidepane/SidePaneViewModelTest.java @@ -76,30 +76,30 @@ void setUp() { void moveUp() { sidePaneViewModel.moveUp(SidePaneType.WEB_SEARCH); - assertEquals(sidePaneComponents.get(0), SidePaneType.WEB_SEARCH); - assertEquals(sidePaneComponents.get(1), SidePaneType.GROUPS); + assertEquals(SidePaneType.WEB_SEARCH, sidePaneComponents.get(0)); + assertEquals(SidePaneType.GROUPS, sidePaneComponents.get(1)); } @Test void moveUpFromFirstPosition() { sidePaneViewModel.moveUp(SidePaneType.GROUPS); - assertEquals(sidePaneComponents.get(0), SidePaneType.GROUPS); + assertEquals(SidePaneType.GROUPS, sidePaneComponents.get(0)); } @Test void moveDown() { sidePaneViewModel.moveDown(SidePaneType.WEB_SEARCH); - assertEquals(sidePaneComponents.get(1), SidePaneType.OPEN_OFFICE); - assertEquals(sidePaneComponents.get(2), SidePaneType.WEB_SEARCH); + assertEquals(SidePaneType.OPEN_OFFICE, sidePaneComponents.get(1)); + assertEquals(SidePaneType.WEB_SEARCH, sidePaneComponents.get(2)); } @Test void moveDownFromLastPosition() { sidePaneViewModel.moveDown(SidePaneType.OPEN_OFFICE); - assertEquals(sidePaneComponents.get(2), SidePaneType.OPEN_OFFICE); + assertEquals(SidePaneType.OPEN_OFFICE, sidePaneComponents.get(2)); } @Test diff --git a/src/test/java/org/jabref/gui/theme/ThemeManagerTest.java b/src/test/java/org/jabref/gui/theme/ThemeManagerTest.java index f96308c18b8..87e1d1a654c 100644 --- a/src/test/java/org/jabref/gui/theme/ThemeManagerTest.java +++ b/src/test/java/org/jabref/gui/theme/ThemeManagerTest.java @@ -145,7 +145,7 @@ public void largeCustomThemeNotHeldInMemory() throws IOException { Files.move(largeCssTestFile, largeCssTestFile.resolveSibling("renamed.css")); // getAdditionalStylesheet() should no longer offer the deleted stylesheet as it is not been held in memory - assertEquals(themeManager.getActiveTheme().getAdditionalStylesheet().get().getWebEngineStylesheet(), "", + assertEquals("", themeManager.getActiveTheme().getAdditionalStylesheet().get().getWebEngineStylesheet(), "didn't expect additional stylesheet after css was deleted"); Files.move(largeCssTestFile.resolveSibling("renamed.css"), largeCssTestFile); @@ -194,7 +194,7 @@ public void installThemeOnWebEngine() throws IOException { }); Assertions.assertDoesNotThrow(() -> { - assertEquals(webEngineStyleSheetLocation.get(), TEST_CSS_DATA); + assertEquals(TEST_CSS_DATA, webEngineStyleSheetLocation.get()); }); } diff --git a/src/test/java/org/jabref/logic/bibtex/comparator/BibStringDiffTest.java b/src/test/java/org/jabref/logic/bibtex/comparator/BibStringDiffTest.java index 28f8ed4d801..03527cb4387 100644 --- a/src/test/java/org/jabref/logic/bibtex/comparator/BibStringDiffTest.java +++ b/src/test/java/org/jabref/logic/bibtex/comparator/BibStringDiffTest.java @@ -10,9 +10,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -54,17 +52,17 @@ void notEqualTest() { @Test void identicalObjectsAreEqual() { BibStringDiff other = diff; - assertTrue(other.equals(diff)); + assertEquals(other, diff); } @Test void compareToNullObjectIsFalse() { - assertFalse(diff.equals(null)); + assertNotEquals(null, diff); } @Test void compareToDifferentClassIsFalse() { - assertFalse(diff.equals(new Object())); + assertNotEquals(diff, new Object()); } @Test diff --git a/src/test/java/org/jabref/logic/database/DatabaseMergerTest.java b/src/test/java/org/jabref/logic/database/DatabaseMergerTest.java index a7464dbaed5..b6a874c93b6 100644 --- a/src/test/java/org/jabref/logic/database/DatabaseMergerTest.java +++ b/src/test/java/org/jabref/logic/database/DatabaseMergerTest.java @@ -162,7 +162,7 @@ void mergeMetaDataWithoutAllEntriesGroup() { // Assert that groups of other are children of root node of target assertEquals(targetRootGroup, target.getGroups().get()); - assertEquals(target.getGroups().get().getChildren().size(), 1); + assertEquals(1, target.getGroups().get().getChildren().size()); assertEquals(otherRootGroup, target.getGroups().get().getChildren().get(0)); } @@ -185,7 +185,7 @@ void mergeMetaDataWithAllEntriesGroup() { // Assert that groups of other are children of root node of target assertEquals(targetRootGroup, target.getGroups().get()); - assertEquals(target.getGroups().get().getChildren().size(), 1); + assertEquals(1, target.getGroups().get().getChildren().size()); assertEquals(expectedImportedGroupNode, target.getGroups().get().getChildren().get(0)); assertEquals(expectedContentSelectors, target.getContentSelectorList()); } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/IacrEprintFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/IacrEprintFetcherTest.java index 2eb837d2364..c168c4bceb7 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/IacrEprintFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/IacrEprintFetcherTest.java @@ -168,7 +168,7 @@ public void searchByIdWithOldHtmlFormatWithoutDateCheck(String id) throws Fetche Optional fetchedEntry = fetcher.performSearchById(id); assertTrue(fetchedEntry.isPresent(), "Expected to get an entry for id " + id); assertNotEquals(Optional.empty(), fetchedEntry.get().getField(StandardField.DATE), "Expected non empty date field, entry is\n" + fetchedEntry.toString()); - assertTrue(fetchedEntry.get().getField(StandardField.DATE).get().length() == 10, "Expected yyyy-MM-dd date format, entry is\n" + fetchedEntry.toString()); + assertEquals(10, fetchedEntry.get().getField(StandardField.DATE).get().length(), "Expected yyyy-MM-dd date format, entry is\n" + fetchedEntry.toString()); assertNotEquals(Optional.empty(), fetchedEntry.get().getField(StandardField.ABSTRACT), "Expected non empty abstract field, entry is\n" + fetchedEntry.toString()); } diff --git a/src/test/java/org/jabref/logic/journals/AbbreviationTest.java b/src/test/java/org/jabref/logic/journals/AbbreviationTest.java index 1f3ba7a8eab..423e235c0c2 100644 --- a/src/test/java/org/jabref/logic/journals/AbbreviationTest.java +++ b/src/test/java/org/jabref/logic/journals/AbbreviationTest.java @@ -105,10 +105,10 @@ void testDefaultAndShortestUniqueAbbreviationsAreSame() { @Test void testEquals() { - Abbreviation abbreviation = new Abbreviation("Long Name", "L N", "LN"); - Abbreviation otherAbbreviation = new Abbreviation("Long Name", "L N", "LN"); - assertEquals(abbreviation, otherAbbreviation); - assertNotEquals(abbreviation, "String"); + Abbreviation abbreviation = new Abbreviation("Long Name", "L N", "LN"); + Abbreviation otherAbbreviation = new Abbreviation("Long Name", "L N", "LN"); + assertEquals(abbreviation, otherAbbreviation); + assertNotEquals("String", abbreviation); } @Test diff --git a/src/test/java/org/jabref/logic/layout/format/ReplaceTest.java b/src/test/java/org/jabref/logic/layout/format/ReplaceTest.java index 3d1eb4ebb9b..31bc81144f5 100644 --- a/src/test/java/org/jabref/logic/layout/format/ReplaceTest.java +++ b/src/test/java/org/jabref/logic/layout/format/ReplaceTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class ReplaceTest { @@ -26,7 +27,7 @@ public void testSimpleTextNoHit() { public void testFormatNull() { ParamLayoutFormatter a = new Replace(); a.setArgument("Eds.,Ed."); - assertEquals(null, a.format(null)); + assertNull(a.format(null)); } @Test diff --git a/src/test/java/org/jabref/logic/msbib/MsBibAuthorTest.java b/src/test/java/org/jabref/logic/msbib/MsBibAuthorTest.java index 3b09b26c7d9..2ccef4f8722 100644 --- a/src/test/java/org/jabref/logic/msbib/MsBibAuthorTest.java +++ b/src/test/java/org/jabref/logic/msbib/MsBibAuthorTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class MsBibAuthorTest { @@ -26,14 +27,14 @@ public void testGetMiddleName() { public void testGetNoMiddleName() { Author author = new Author("Gustav", null, null, "Bach", null); MsBibAuthor msBibAuthor = new MsBibAuthor(author); - assertEquals(null, msBibAuthor.getMiddleName()); + assertNull(msBibAuthor.getMiddleName()); } @Test public void testGetNoFirstName() { Author author = new Author(null, null, null, "Bach", null); MsBibAuthor msBibAuthor = new MsBibAuthor(author); - assertEquals(null, msBibAuthor.getMiddleName()); + assertNull(msBibAuthor.getMiddleName()); } @Test diff --git a/src/test/java/org/jabref/logic/net/ProxyTest.java b/src/test/java/org/jabref/logic/net/ProxyTest.java index 6b6e844d1b6..20f904441cb 100644 --- a/src/test/java/org/jabref/logic/net/ProxyTest.java +++ b/src/test/java/org/jabref/logic/net/ProxyTest.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ProxyTest { /** @@ -31,10 +32,10 @@ public void testProxyPreferencesStorePassword() { persist); // Check if mock data is stored in object memory and can be extracted - assertEquals(proxyPref.shouldUseProxy(), true); + assertTrue(proxyPref.shouldUseProxy()); assertEquals(proxyPref.getHostname(), hostname); assertEquals(proxyPref.getPort(), port); - assertEquals(proxyPref.shouldUseAuthentication(), true); + assertTrue(proxyPref.shouldUseAuthentication()); assertEquals(proxyPref.getUsername(), username); assertEquals(proxyPref.getPassword(), password); assertEquals(proxyPref.shouldPersistPassword(), persist); diff --git a/src/test/java/org/jabref/model/FieldChangeTest.java b/src/test/java/org/jabref/model/FieldChangeTest.java index 1625a1ae652..b2a6b77f64b 100644 --- a/src/test/java/org/jabref/model/FieldChangeTest.java +++ b/src/test/java/org/jabref/model/FieldChangeTest.java @@ -57,7 +57,7 @@ void selfEqualsFieldchangeDifferentEntry() { @Test void fieldChangeDoesNotEqualString() { - assertNotEquals(fc, "foo"); + assertNotEquals("foo", fc); } @Test diff --git a/src/test/java/org/jabref/model/entry/BibEntryTest.java b/src/test/java/org/jabref/model/entry/BibEntryTest.java index b913030cc73..63cd6657bc9 100644 --- a/src/test/java/org/jabref/model/entry/BibEntryTest.java +++ b/src/test/java/org/jabref/model/entry/BibEntryTest.java @@ -364,17 +364,17 @@ void isEmptyCiteKey() { @Test void identicObjectsareEqual() throws Exception { BibEntry otherEntry = entry; - assertTrue(entry.equals(otherEntry)); + assertEquals(entry, otherEntry); } @Test void compareToNullObjectIsFalse() throws Exception { - assertFalse(entry.equals(null)); + assertNotEquals(null, entry); } @Test void compareToDifferentClassIsFalse() throws Exception { - assertFalse(entry.equals(new Object())); + assertNotEquals(entry, new Object()); } @Test diff --git a/src/test/java/org/jabref/model/openoffice/CitationEntryTest.java b/src/test/java/org/jabref/model/openoffice/CitationEntryTest.java index 8648d9b5df9..481afa00eac 100644 --- a/src/test/java/org/jabref/model/openoffice/CitationEntryTest.java +++ b/src/test/java/org/jabref/model/openoffice/CitationEntryTest.java @@ -48,7 +48,7 @@ void testCitationEntryEquals() { assertEquals(citationEntry1, citationEntry1); assertEquals(citationEntry1, citationEntry3); assertNotEquals(citationEntry1, citationEntry2); - assertNotEquals(citationEntry1, "Random String"); + assertNotEquals("Random String", citationEntry1); } @Test diff --git a/src/test/java/org/jabref/model/strings/StringUtilTest.java b/src/test/java/org/jabref/model/strings/StringUtilTest.java index a1ba47b247a..f41584e1939 100644 --- a/src/test/java/org/jabref/model/strings/StringUtilTest.java +++ b/src/test/java/org/jabref/model/strings/StringUtilTest.java @@ -14,6 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -142,7 +143,7 @@ void testStripBrackets() { assertEquals("]", StringUtil.stripBrackets("]")); assertEquals("", StringUtil.stripBrackets("[]")); assertEquals("f[]f", StringUtil.stripBrackets("f[]f")); - assertEquals(null, StringUtil.stripBrackets(null)); + assertNull(StringUtil.stripBrackets(null)); } @Test From 0b0fcc94c61fedbbad5e6185836614dd6df59f28 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Wed, 22 Nov 2023 11:10:45 +0100 Subject: [PATCH 034/144] Replace cache: gradle with "odern" gradle-build-action (#10657) --- .github/workflows/deployment-arm64.yml | 3 ++- .github/workflows/deployment.yml | 3 ++- .github/workflows/refresh-journal-lists.yml | 3 ++- .github/workflows/tests-fetchers.yml | 3 ++- .github/workflows/tests.yml | 21 ++++++++++++++------- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index b5cb0cf4bc0..cfb9ba0c52f 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -63,7 +63,6 @@ jobs: with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' - name: Clean up keychain run: | security delete-keychain signing_temp.keychain ${{runner.temp}}/keychain/notarization.keychain || true @@ -85,6 +84,8 @@ jobs: mkdir ${{runner.temp}}/keychain security create-keychain -p jabref ${{runner.temp}}/keychain/notarization.keychain security set-keychain-settings ${{runner.temp}}/keychain/notarization.keychain + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Prepare merged jars and modules dir (macOS) run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" prepareModulesDir - name: Build dmg (macOS) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index c23f0ccf516..15597b6833a 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -94,7 +94,8 @@ jobs: with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Prepare merged jars and modules dir (macOS) if: (matrix.os == 'macos-latest') || (steps.checksecrets.outputs.secretspresent == 'NO') run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" prepareModulesDir diff --git a/.github/workflows/refresh-journal-lists.yml b/.github/workflows/refresh-journal-lists.yml index 88f43f673c1..8762eace3da 100644 --- a/.github/workflows/refresh-journal-lists.yml +++ b/.github/workflows/refresh-journal-lists.yml @@ -32,7 +32,8 @@ jobs: with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Check whether journal-list.mv can be generated (the "real" generation is done inside JabRef's build process) run: | ./gradlew generateJournalListMV diff --git a/.github/workflows/tests-fetchers.yml b/.github/workflows/tests-fetchers.yml index ee155b0c243..267ecc18c72 100644 --- a/.github/workflows/tests-fetchers.yml +++ b/.github/workflows/tests-fetchers.yml @@ -50,7 +50,8 @@ jobs: with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run fetcher tests run: ./gradlew fetcherTest env: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 86ae1543a1a..e9684d5cb6a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,7 +39,6 @@ jobs: with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' - name: Run checkstyle reporter uses: nikitasavinov/checkstyle-action@master with: @@ -47,6 +46,8 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} checkstyle_config: 'config/checkstyle/checkstyle_reviewdog.xml' checkstyle_version: '10.3' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run checkstyle using gradle run: ./gradlew checkstyleMain checkstyleTest checkstyleJmh openrewrite: @@ -63,7 +64,8 @@ jobs: with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run OpenRewrite run: | ./gradlew rewriteDryRun @@ -81,7 +83,8 @@ jobs: with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run modernizer run: | # enable failing of this task if modernizer complains @@ -162,7 +165,8 @@ jobs: with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run tests run: xvfb-run --auto-servernum ./gradlew check -x checkstyleJmh -x checkstyleMain -x checkstyleTest -x modernizer env: @@ -197,7 +201,8 @@ jobs: with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run tests on PostgreSQL run: ./gradlew databaseTest --rerun-tasks env: @@ -234,7 +239,8 @@ jobs: with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run GUI tests run: xvfb-run --auto-servernum ./gradlew guiTest env: @@ -278,7 +284,8 @@ jobs: with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Update test coverage metrics if: (github.ref == 'refs/heads/main') && (steps.checksecrets.outputs.secretspresent == 'YES') run: xvfb-run --auto-servernum ./gradlew jacocoTestReport From 43364b8b873e7a867007c13c16bbd879b6fa4f24 Mon Sep 17 00:00:00 2001 From: Christoph Date: Wed, 22 Nov 2023 13:48:42 +0100 Subject: [PATCH 035/144] App semantic scholar api key to properties (#10656) * Update fetchers.md * Add API key handling for semantic scholar * checkstyle * add env var * fix template expansion * Fix some fetcher tests * checkstyle * revert deletion --------- Co-authored-by: Oliver Kopp --- build.gradle | 3 +- docs/code-howtos/fetchers.md | 21 +++++----- .../CitationRelationsTab.java | 2 +- .../SemanticScholarFetcher.java | 42 ++++++++++++++----- .../java/org/jabref/logic/util/BuildInfo.java | 4 +- src/main/resources/build.properties | 2 +- .../importer/fetcher/ArXivFetcherTest.java | 15 +++---- .../fetcher/AstrophysicsDataSystemTest.java | 3 +- .../fetcher/CompositeIdFetcherTest.java | 10 +++-- .../CompositeSearchBasedFetcherTest.java | 4 +- 10 files changed, 67 insertions(+), 39 deletions(-) diff --git a/build.gradle b/build.gradle index 800fa9fe067..dfc30a63a31 100644 --- a/build.gradle +++ b/build.gradle @@ -277,7 +277,8 @@ processResources { "astrophysicsDataSystemAPIKey": System.getenv('AstrophysicsDataSystemAPIKey') ? System.getenv('AstrophysicsDataSystemAPIKey') : '', "ieeeAPIKey": System.getenv('IEEEAPIKey') ? System.getenv('IEEEAPIKey') : '', "scienceDirectApiKey": System.getenv('SCIENCEDIRECTAPIKEY') ? System.getenv('SCIENCEDIRECTAPIKEY') : '', - "biodiversityHeritageApiKey": System.getenv('BiodiversityHeritageApiKey') ? System.getenv('BiodiversityHeritageApiKey') : '' + "biodiversityHeritageApiKey": System.getenv('BiodiversityHeritageApiKey') ? System.getenv('BiodiversityHeritageApiKey') : '', + "semanticScholarApiKey": System.getenv('SemanticScholarApiKey') ? System.getenv("SemanticScholarApiKey") : '' ) filteringCharset = 'UTF-8' } diff --git a/docs/code-howtos/fetchers.md b/docs/code-howtos/fetchers.md index 3c540014bf7..a0be55e912d 100644 --- a/docs/code-howtos/fetchers.md +++ b/docs/code-howtos/fetchers.md @@ -5,15 +5,16 @@ parent: Code Howtos Fetchers are the implementation of the [search using online services](https://docs.jabref.org/collect/import-using-online-bibliographic-database). Some fetchers require API keys to get them working. To get the fetchers running in a JabRef development setup, the keys need to be placed in the respective environment variable. The following table lists the respective fetchers, where to get the key from and the environment variable where the key has to be placed. -| Service | Key Source | Environment Variable | Rate Limit | -|:-----------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|--------------------------------|----------------------------------| -| [IEEEXplore](https://docs.jabref.org/collect/import-using-online-bibliographic-database#ieeexplore) | [IEEE Xplore API portal](https://developer.ieee.org) | `IEEEAPIKey` | 200 calls/day | -| [MathSciNet](http://www.ams.org/mathscinet) | (none) | (none) | Depending on the current network | -| [SAO/NASA Astrophysics Data System](https://docs.jabref.org/collect/import-using-online-bibliographic-database#sao-nasa-astrophysics-data-system) | [ADS UI](https://ui.adsabs.harvard.edu/user/settings/token) | `AstrophysicsDataSystemAPIKey` | 5000 calls/day | -| [ScienceDirect](https://www.sciencedirect.com) | | `ScienceDirectApiKey` | | -| [Springer Nature](https://docs.jabref.org/collect/import-using-online-bibliographic-database#springer) | [Springer Nature API Portal](https://dev.springernature.com) | `SpringerNatureAPIKey` | 5000 calls/day | -| [Zentralblatt Math](https://www.zbmath.org) | (none) | (none) | Depending on the current network | -| [Biodiversity Heritage Library](https://www.biodiversitylibrary.org/) | [Biodiversitylibrary](https://about.biodiversitylibrary.org/tools-and-services/developer-and-data-tools/#APIs) | `BiodiversityHeritageApiKey` | - | +| Service | Key Source | Environment Variable | Rate Limit | +|:--------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|--------------------------------|----------------------------------| +| [IEEEXplore](https://docs.jabref.org/collect/import-using-online-bibliographic-database#ieeexplore) | [IEEE Xplore API portal](https://developer.ieee.org) | `IEEEAPIKey` | 200 calls/day | +| [MathSciNet](http://www.ams.org/mathscinet) | (none) | (none) | Depending on the current network | +| [SAO/NASA Astrophysics Data System](https://docs.jabref.org/collect/import-using-online-bibliographic-database#sao-nasa-astrophysics-data-system) | [ADS UI](https://ui.adsabs.harvard.edu/user/settings/token) | `AstrophysicsDataSystemAPIKey` | 5000 calls/day | +| [ScienceDirect](https://www.sciencedirect.com) | | `ScienceDirectApiKey` | | +| [SemanticScholar](https://www.semanticscholar.org/) | | `SemanticScholarApiKey` | | +| [Springer Nature](https://docs.jabref.org/collect/import-using-online-bibliographic-database#springer) | [Springer Nature API Portal](https://dev.springernature.com) | `SpringerNatureAPIKey` | 5000 calls/day | +| [Zentralblatt Math](https://www.zbmath.org) | (none) | (none) | Depending on the current network | +| [Biodiversity Heritage Library](https://www.biodiversitylibrary.org/) | [Biodiversitylibrary](https://about.biodiversitylibrary.org/tools-and-services/developer-and-data-tools/#APIs) | `BiodiversityHeritageApiKey` | - | "Depending on the current network" means that it depends on whether your request is routed through a network having paid access. For instance, some universities have subscriptions to MathSciNet. @@ -72,7 +73,7 @@ springerNatureAPIKey=${springerNatureAPIKey} In `build.gradle`, these variables are filled: ```groovy -"springerNatureAPIKey": System.getenv('SpringerNatureAPIKey') +"springerNatureAPIKey" : System.getenv('SpringerNatureAPIKey') ``` The `BuildInfo` class reads from that file. diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java index d3c6862aaf6..01e2249d1b0 100644 --- a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java @@ -83,7 +83,7 @@ public CitationRelationsTab(EntryEditorPreferences preferences, DialogService di setText(Localization.lang("Citation relations")); setTooltip(new Tooltip(Localization.lang("Show articles related by citation"))); - this.bibEntryRelationsRepository = new BibEntryRelationsRepository(new SemanticScholarFetcher(), + this.bibEntryRelationsRepository = new BibEntryRelationsRepository(new SemanticScholarFetcher(preferencesService.getImporterPreferences()), new BibEntryRelationsCache()); } diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java index 6699596a7be..0a5ca3b16a9 100644 --- a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java @@ -7,14 +7,25 @@ import java.util.List; import org.jabref.logic.importer.FetcherException; +import org.jabref.logic.importer.ImporterPreferences; +import org.jabref.logic.importer.fetcher.CustomizableKeyFetcher; import org.jabref.logic.net.URLDownload; +import org.jabref.logic.util.BuildInfo; import org.jabref.model.entry.BibEntry; import com.google.gson.Gson; -public class SemanticScholarFetcher implements CitationFetcher { +public class SemanticScholarFetcher implements CitationFetcher, CustomizableKeyFetcher { private static final String SEMANTIC_SCHOLAR_API = "https://api.semanticscholar.org/graph/v1/"; + private static final String API_KEY = new BuildInfo().semanticScholarApiKey; + + private final ImporterPreferences importerPreferences; + + public SemanticScholarFetcher(ImporterPreferences importerPreferences) { + this.importerPreferences = importerPreferences; + } + @Override public List searchCitedBy(BibEntry entry) throws FetcherException { if (entry.getDOI().isPresent()) { @@ -24,17 +35,20 @@ public List searchCitedBy(BibEntry entry) throws FetcherException { .append("/citations") .append("?fields=").append("title,authors,year,citationCount,referenceCount") .append("&limit=1000"); - try { URL citationsUrl = URI.create(urlBuilder.toString()).toURL(); - URLDownload urlDownload = new URLDownload(citationsUrl); + + String apiKey = getApiKey(); + if (!apiKey.isEmpty()) { + urlDownload.addHeader("x-api-key", apiKey); + } CitationsResponse citationsResponse = new Gson() .fromJson(urlDownload.asString(), CitationsResponse.class); return citationsResponse.getData() - .stream().filter(citationDataItem -> citationDataItem.getCitingPaper() != null) - .map(citationDataItem -> citationDataItem.getCitingPaper().toBibEntry()).toList(); + .stream().filter(citationDataItem -> citationDataItem.getCitingPaper() != null) + .map(citationDataItem -> citationDataItem.getCitingPaper().toBibEntry()).toList(); } catch (IOException e) { throw new RuntimeException(e); } @@ -50,19 +64,23 @@ public List searchCiting(BibEntry entry) { .append("paper/") .append("DOI:").append(entry.getDOI().get().getDOI()) .append("/references") - .append("?fields=").append("title,authors,year,citationCount,referenceCount") + .append("?fields=") + .append("title,authors,year,citationCount,referenceCount") .append("&limit=1000"); try { URL referencesUrl = URI.create(urlBuilder.toString()).toURL(); - URLDownload urlDownload = new URLDownload(referencesUrl); + String apiKey = getApiKey(); + if (!apiKey.isEmpty()) { + urlDownload.addHeader("x-api-key", apiKey); + } ReferencesResponse referencesResponse = new Gson() .fromJson(urlDownload.asString(), ReferencesResponse.class); return referencesResponse.getData() - .stream() - .filter(citationDataItem -> citationDataItem.getCitedPaper() != null) - .map(referenceDataItem -> referenceDataItem.getCitedPaper().toBibEntry()).toList(); + .stream() + .filter(citationDataItem -> citationDataItem.getCitedPaper() != null) + .map(referenceDataItem -> referenceDataItem.getCitedPaper().toBibEntry()).toList(); } catch (IOException e) { throw new RuntimeException(e); } @@ -75,4 +93,8 @@ public List searchCiting(BibEntry entry) { public String getName() { return "Semantic Scholar Citations Fetcher"; } + + private String getApiKey() { + return importerPreferences.getApiKey(getName()).orElse(API_KEY); + } } diff --git a/src/main/java/org/jabref/logic/util/BuildInfo.java b/src/main/java/org/jabref/logic/util/BuildInfo.java index 245e90b1d2e..352d4531a4a 100644 --- a/src/main/java/org/jabref/logic/util/BuildInfo.java +++ b/src/main/java/org/jabref/logic/util/BuildInfo.java @@ -28,6 +28,7 @@ public final class BuildInfo { public final String minRequiredJavaVersion; public final boolean allowJava9; public final String biodiversityHeritageApiKey; + public final String semanticScholarApiKey; public BuildInfo() { this("/build.properties"); @@ -57,6 +58,7 @@ public BuildInfo(String path) { minRequiredJavaVersion = properties.getProperty("minRequiredJavaVersion", "1.8"); allowJava9 = "true".equals(properties.getProperty("allowJava9", "true")); biodiversityHeritageApiKey = BuildInfo.getValue(properties, "biodiversityHeritageApiKey", "36b910b6-2eb3-46f2-b64c-9abc149925ba"); + semanticScholarApiKey = BuildInfo.getValue(properties, "semanticScholarApiKey", ""); } private static String getValue(Properties properties, String key, String defaultValue) { @@ -64,7 +66,7 @@ private static String getValue(Properties properties, String key, String default // workaround unprocessed build.properties file --> just remove the reference to some variable used in build.gradle .map(value -> value.replaceAll("\\$\\{.*\\}", "")) .orElse(""); - if (!"".equals(result)) { + if (!result.isEmpty()) { return result; } return defaultValue; diff --git a/src/main/resources/build.properties b/src/main/resources/build.properties index 6e646dd0160..e875158b817 100644 --- a/src/main/resources/build.properties +++ b/src/main/resources/build.properties @@ -6,4 +6,4 @@ springerNatureAPIKey=${springerNatureAPIKey} astrophysicsDataSystemAPIKey=${astrophysicsDataSystemAPIKey} ieeeAPIKey=${ieeeAPIKey} biodiversityHeritageApiKey=${biodiversityHeritageApiKey} - +semanticScholarApiKey=${semanticScholarApiKey} diff --git a/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java index 65d3c2bcfc9..25556bb9577 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java @@ -51,8 +51,6 @@ class ArXivFetcherTest implements SearchBasedFetcherCapabilityTest, PagedSearchF private BibEntry mainOriginalPaper; private BibEntry mainResultPaper; - private BibEntry completePaper; - @BeforeAll static void setUp() { importFormatPreferences = mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS); @@ -111,7 +109,7 @@ void eachSetUp() { // Example of a robust result, with information from both ArXiv-assigned and user-assigned DOIs // FixMe: Test this BibEntry - completePaper = new BibEntry(StandardEntryType.Article) + BibEntry completePaper = new BibEntry(StandardEntryType.Article) .withField(StandardField.AUTHOR, "Büscher, Tobias and Diez, Angel L. and Gompper, Gerhard and Elgeti, Jens") .withField(StandardField.TITLE, "Instability and fingering of interfaces in growing tissue") .withField(StandardField.DATE, "2020-03-10") @@ -340,7 +338,8 @@ void searchEntryByOldId() throws Exception { .withField(StandardField.PAGES, "17--29") .withField(StandardField.DATE, "2003-07-07") .withField(StandardField.YEAR, "2003") - .withField(StandardField.MONTH, "oct") + .withField(StandardField.MONTH, "#oct#") + .withField(StandardField.ISSN, "1434-6052") .withField(StandardField.ABSTRACT, "Multi-electron production is studied at high electron transverse momentum in positron- and electron-proton collisions using the H1 detector at HERA. The data correspond to an integrated luminosity of 115 pb-1. Di-electron and tri-electron event yields are measured. Cross sections are derived in a restricted phase space region dominated by photon-photon collisions. In general good agreement is found with the Standard Model predictions. However, for electron pair invariant masses above 100 GeV, three di-electron events and three tri-electron events are observed, compared to Standard Model expectations of 0.30 \\pm 0.04 and 0.23 \\pm 0.04, respectively.") .withField(StandardField.PUBLISHER, "Springer Science and Business Media {LLC}") .withField(StandardField.EPRINT, "hep-ex/0307015") @@ -474,11 +473,12 @@ public void supportsBooleanANDSearch() throws Exception { .withField(StandardField.TITLE, "Instability and fingering of interfaces in growing tissue") .withField(StandardField.DATE, "2020-03-10") .withField(StandardField.YEAR, "2020") - .withField(StandardField.MONTH, "aug") + .withField(StandardField.MONTH, "#aug#") .withField(StandardField.NUMBER, "8") .withField(StandardField.VOLUME, "22") + .withField(StandardField.ISSN, "1367-2630") .withField(StandardField.PAGES, "083005") - .withField(StandardField.PUBLISHER, "{IOP} Publishing") + .withField(StandardField.PUBLISHER, "IOP Publishing") .withField(StandardField.JOURNAL, "New Journal of Physics") .withField(StandardField.ABSTRACT, "Interfaces in tissues are ubiquitous, both between tissue and environment as well as between populations of different cell types. The propagation of an interface can be driven mechanically. % e.g. by a difference in the respective homeostatic stress of the different cell types. Computer simulations of growing tissues are employed to study the stability of the interface between two tissues on a substrate. From a mechanical perspective, the dynamics and stability of this system is controlled mainly by four parameters of the respective tissues: (i) the homeostatic stress (ii) cell motility (iii) tissue viscosity and (iv) substrate friction. For propagation driven by a difference in homeostatic stress, the interface is stable for tissue-specific substrate friction even for very large differences of homeostatic stress; however, it becomes unstable above a critical stress difference when the tissue with the larger homeostatic stress has a higher viscosity. A small difference in directed bulk motility between the two tissues suffices to result in propagation with a stable interface, even for otherwise identical tissues. Larger differences in motility force, however, result in a finite-wavelength instability of the interface. Interestingly, the instability is apparently bound by nonlinear effects and the amplitude of the interface undulations only grows to a finite value in time.") .withField(StandardField.DOI, "10.1088/1367-2630/ab9e88") @@ -573,12 +573,13 @@ public void retrievePartialResultWhenCannotGetInformationFromArXivAssignedDOI() .withField(StandardField.JOURNAL, "PNAS December 27, 2016 vol. 113 no. 52 15000-15005") .withField(StandardField.ABSTRACT, "Bacteria tightly regulate and coordinate the various events in their cell cycles to duplicate themselves accurately and to control their cell sizes. Growth of Escherichia coli, in particular, follows a relation known as Schaechter 's growth law. This law says that the average cell volume scales exponentially with growth rate, with a scaling exponent equal to the time from initiation of a round of DNA replication to the cell division at which the corresponding sister chromosomes segregate. Here, we sought to test the robustness of the growth law to systematic perturbations in cell dimensions achieved by varying the expression levels of mreB and ftsZ. We found that decreasing the mreB level resulted in increased cell width, with little change in cell length, whereas decreasing the ftsZ level resulted in increased cell length. Furthermore, the time from replication termination to cell division increased with the perturbed dimension in both cases. Moreover, the growth law remained valid over a range of growth conditions and dimension perturbations. The growth law can be quantitatively interpreted as a consequence of a tight coupling of cell division to replication initiation. Thus, its robustness to perturbations in cell dimensions strongly supports models in which the timing of replication initiation governs that of cell division, and cell volume is the key phenomenological variable governing the timing of replication initiation. These conclusions are discussed in the context of our recently proposed adder-per-origin model, in which cells add a constant volume per origin between initiations and divide a constant time after initiation.") .withField(StandardField.DOI, "10.1073/pnas.1617932114") + .withField(StandardField.ISSN, "1091-6490") .withField(StandardField.EPRINT, "1701.00587") .withField(StandardField.FILE, ":http\\://arxiv.org/pdf/1701.00587v1:PDF") .withField(StandardField.EPRINTTYPE, "arXiv") .withField(StandardField.EPRINTCLASS, "q-bio.CB") .withField(StandardField.KEYWORDS, "q-bio.CB") - .withField(StandardField.MONTH, "dec") + .withField(StandardField.MONTH, "#dec#") .withField(StandardField.YEAR, "2016") .withField(StandardField.VOLUME, "113") .withField(InternalField.KEY_FIELD, "Zheng_2016") diff --git a/src/test/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystemTest.java b/src/test/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystemTest.java index ad0fb840030..8ba31caec84 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystemTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystemTest.java @@ -103,7 +103,6 @@ public void setUp() throws Exception { .withField(StandardField.TITLE, "Multiyear On-Orbit Calibration and Performance of Terra MODIS Reflective Solar Bands") .withField(StandardField.VOLUME, "45") .withField(StandardField.YEAR, "2007") - .withField(StandardField.KEYWORDS, "Earth Science") .withField(StandardField.URL, "https://ui.adsabs.harvard.edu/abs/2007ITGRS..45..879X"); ingersollPollardEntry = new BibEntry(StandardEntryType.Article) @@ -112,7 +111,7 @@ public void setUp() throws Exception { .withField(StandardField.AUTHOR, "Ingersoll, A.~P. and Pollard, D.") .withField(StandardField.DOI, "10.1016/0019-1035(82)90169-5") .withField(StandardField.JOURNAL, "\\icarus") - .withField(StandardField.KEYWORDS, "Atmospheric Circulation, Barotropic Flow, Convective Flow, Flow Stability, Jupiter Atmosphere, Rotating Fluids, Saturn Atmosphere, Adiabatic Flow, Anelasticity, Compressible Fluids, Planetary Rotation, Rotating Cylinders, Scaling Laws, Wind Profiles, PLANETS, JUPITER, SATURN, MOTION, INTERIORS, ATMOSPHERE, ANALYSIS, SCALE, BAROTROPY, CHARACTERISTICS, STRUCTURE, WINDS, VISCOSITY, DATA, CONVECTION, ROTATION, EDDY EFFECTS, ENERGY, ADIABATICITY, DIAGRAMS, REVIEW, LATITUDE, ZONES, VELOCITY, MATHEMATICAL MODELS, HEAT FLOW, EQUATIONS OF MOTION, FLUIDS, DYNAMICS, TEMPERATURE, GRADIENTS, Lunar and Planetary Exploration; Planets, Earth Science, Earth Science") + .withField(StandardField.KEYWORDS, "Atmospheric Circulation, Barotropic Flow, Convective Flow, Flow Stability, Jupiter Atmosphere, Rotating Fluids, Saturn Atmosphere, Adiabatic Flow, Anelasticity, Compressible Fluids, Planetary Rotation, Rotating Cylinders, Scaling Laws, Wind Profiles, PLANETS, JUPITER, SATURN, MOTION, INTERIORS, ATMOSPHERE, ANALYSIS, SCALE, BAROTROPY, CHARACTERISTICS, STRUCTURE, WINDS, VISCOSITY, DATA, CONVECTION, ROTATION, EDDY EFFECTS, ENERGY, ADIABATICITY, DIAGRAMS, REVIEW, LATITUDE, ZONES, VELOCITY, MATHEMATICAL MODELS, HEAT FLOW, EQUATIONS OF MOTION, FLUIDS, DYNAMICS, TEMPERATURE, GRADIENTS, Lunar and Planetary Exploration; Planets, Earth Science") .withField(StandardField.MONTH, "#oct#") .withField(StandardField.NUMBER, "1") .withField(StandardField.PAGES, "62-80") diff --git a/src/test/java/org/jabref/logic/importer/fetcher/CompositeIdFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/CompositeIdFetcherTest.java index d171ed5d8e1..02eb4c2f14c 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/CompositeIdFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/CompositeIdFetcherTest.java @@ -42,7 +42,8 @@ public static Stream performSearchByIdReturnsCorrectEntryForIdentifie .withField(StandardField.TITLE, "Reading the CARDs: the Imprint of Accretion History in the Chemical Abundances of the Milky Way's Stellar Halo") .withField(StandardField.DATE, "2021-10-06") .withField(StandardField.YEAR, "2021") - .withField(StandardField.MONTH, "aug") + .withField(StandardField.MONTH, "#aug#") + .withField(StandardField.ISSN, "1538-4357") .withField(StandardField.NUMBER, "2") .withField(StandardField.VOLUME, "934") .withField(StandardField.PUBLISHER, "American Astronomical Society") @@ -94,11 +95,12 @@ public static Stream performSearchByIdReturnsCorrectEntryForIdentifie Arguments.arguments( "performSearchByIdReturnsCorrectEntryForDoiId", new BibEntry(StandardEntryType.Book) - .withField(StandardField.TITLE, "Java{\\textregistered} For Dummies{\\textregistered}") + .withField(StandardField.TITLE, "Java® For Dummies®") .withField(StandardField.PUBLISHER, "Wiley") .withField(StandardField.YEAR, "2011") - .withField(StandardField.AUTHOR, "Barry Burd") - .withField(StandardField.MONTH, "jul") + .withField(StandardField.AUTHOR, "Burd, Barry") + .withField(StandardField.MONTH, "#jul#") + .withField(StandardField.ISBN, "9781118257517") .withField(StandardField.DOI, "10.1002/9781118257517") .withCitationKey("Burd_2011"), "10.1002/9781118257517" 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 5d00d34b356..8bf41644612 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/CompositeSearchBasedFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/CompositeSearchBasedFetcherTest.java @@ -37,7 +37,7 @@ public class CompositeSearchBasedFetcherTest { private static final Logger LOGGER = LoggerFactory.getLogger(CompositeSearchBasedFetcherTest.class); - private ImporterPreferences importerPreferences = mock(ImporterPreferences.class, Answers.RETURNS_DEEP_STUBS); + private final ImporterPreferences importerPreferences = mock(ImporterPreferences.class, Answers.RETURNS_DEEP_STUBS); @Test public void createCompositeFetcherWithNullSet() { @@ -77,7 +77,7 @@ public void performSearchOnNonEmptyQuery(Set fetchers) throw try { List fetcherResult = fetcher.performSearch("quantum"); fetcherResult.forEach(cleanup::doPostCleanup); - Assertions.assertTrue(compositeResult.containsAll(fetcherResult)); + Assertions.assertTrue(compositeResult.containsAll(fetcherResult), "Did not contain " + fetcherResult); } catch (FetcherException e) { /* We catch the Fetcher exception here, since the failing fetcher also fails in the CompositeFetcher * and just leads to no additional results in the returned list. Therefore, the test should not fail From ffb70a616275db620c44edec39d85f9e5e1d386f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:47:50 +0000 Subject: [PATCH 036/144] Bump DavidAnson/markdownlint-cli2-action from 13 to 14 (#10665) Bumps [DavidAnson/markdownlint-cli2-action](https://github.com/davidanson/markdownlint-cli2-action) from 13 to 14. - [Release notes](https://github.com/davidanson/markdownlint-cli2-action/releases) - [Commits](https://github.com/davidanson/markdownlint-cli2-action/compare/v13...v14) --- updated-dependencies: - dependency-name: DavidAnson/markdownlint-cli2-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e9684d5cb6a..c7c89a73930 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -100,7 +100,7 @@ jobs: submodules: 'false' show-progress: 'false' - name: markdownlint-cli2-action - uses: DavidAnson/markdownlint-cli2-action@v13 + uses: DavidAnson/markdownlint-cli2-action@v14 with: globs: | *.md From a681ee15c22fbd67e30d1c4157208e18c4ab6b62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:55:58 +0000 Subject: [PATCH 037/144] Bump org.bouncycastle:bcprov-jdk18on from 1.76 to 1.77 Bumps [org.bouncycastle:bcprov-jdk18on](https://github.com/bcgit/bc-java) from 1.76 to 1.77. - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) --- updated-dependencies: - dependency-name: org.bouncycastle:bcprov-jdk18on dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index dfc30a63a31..9fd5b4318f0 100644 --- a/build.gradle +++ b/build.gradle @@ -128,7 +128,7 @@ dependencies { implementation 'com.h2database:h2-mvstore:2.2.224' // required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635 - implementation 'org.bouncycastle:bcprov-jdk18on:1.76' + implementation 'org.bouncycastle:bcprov-jdk18on:1.77' implementation 'commons-cli:commons-cli:1.6.0' From 2cbb0f86bbc918e71b7ec02132609811b8eafb1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:04:55 +0000 Subject: [PATCH 038/144] Bump org.apache.commons:commons-lang3 from 3.13.0 to 3.14.0 (#10670) Bumps org.apache.commons:commons-lang3 from 3.13.0 to 3.14.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index dfc30a63a31..eb3064c8e2d 100644 --- a/build.gradle +++ b/build.gradle @@ -124,7 +124,7 @@ dependencies { implementation 'org.apache.lucene:lucene-highlighter:9.8.0' implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.10.0' - implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.13.0' + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.14.0' implementation 'com.h2database:h2-mvstore:2.2.224' // required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635 From 16d111aca66b3c7486d6450b3023ba563fde8d5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:05:58 +0100 Subject: [PATCH 039/144] Bump org.openrewrite.recipe:rewrite-recipe-bom from 2.5.0 to 2.5.1 (#10667) Bumps [org.openrewrite.recipe:rewrite-recipe-bom](https://github.com/openrewrite/rewrite-recipe-bom) from 2.5.0 to 2.5.1. - [Release notes](https://github.com/openrewrite/rewrite-recipe-bom/releases) - [Commits](https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.5.0...v2.5.1) --- updated-dependencies: - dependency-name: org.openrewrite.recipe:rewrite-recipe-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 81c4a92d76c..cf136281877 100644 --- a/build.gradle +++ b/build.gradle @@ -253,7 +253,7 @@ dependencies { xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2' xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2' - rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.5.0")) + rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.5.1")) rewrite("org.openrewrite.recipe:rewrite-static-analysis") rewrite("org.openrewrite.recipe:rewrite-logging-frameworks") rewrite("org.openrewrite.recipe:rewrite-testing-frameworks") From 2a4bd6cac3593fafa88e4909953c03b96339cb72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o?= <127297775+theoo33@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:30:18 +0100 Subject: [PATCH 040/144] Add 'More' option to right click menu in the main table #9432 (#10659) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add show more option in the right click main table header menu, openingthe preferences * comments * Achieving contribution 2 * adjustments for right click menu on main table * Update CHANGELOG.md for 9432 * PR test requirements * Other PR requirements * Other PR requirements * remove optional from parameter * openrewrite test * openrewrite test * change string to class for selection * change string to class for selection * remove comment + pass keybindings as parameter * Update JabRef_en.properties * Introduced StandardActions.withText * Refactored for code maintenance * Moved calls to frame out of PreferencesDialogView --------- Co-authored-by: Théo Granier Co-authored-by: newma2n Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> --- CHANGELOG.md | 1 + src/main/java/org/jabref/gui/MainMenu.java | 2 +- .../jabref/gui/actions/StandardActions.java | 8 +++- .../org/jabref/gui/maintable/MainTable.java | 2 +- .../maintable/MainTableHeaderContextMenu.java | 40 ++++++++++++++----- .../preferences/PreferencesDialogView.java | 21 +++++++--- .../PreferencesDialogViewModel.java | 22 ++-------- .../preferences/ShowPreferencesAction.java | 18 ++++++--- src/main/resources/l10n/JabRef_en.properties | 1 + 9 files changed, 74 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffaa0b80b05..4efd64530f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added a button to let users reset the cite command to the default value. [#10569](https://github.com/JabRef/jabref/issues/10569) - We added the option to use System Preference for Light/Dark Theme [#8729](https://github.com/JabRef/jabref/issues/8729). - We added [scholar.archive.org](https://scholar.archive.org/) as a new fetcher. [#10498](https://github.com/JabRef/jabref/issues/10498) +- We added a 'More options' section in the main table right click menu opening the preferences dialog. [#9432](https://github.com/JabRef/jabref/issues/9432) ### Changed diff --git a/src/main/java/org/jabref/gui/MainMenu.java b/src/main/java/org/jabref/gui/MainMenu.java index 28a08a31a0d..8ac907cf6ed 100644 --- a/src/main/java/org/jabref/gui/MainMenu.java +++ b/src/main/java/org/jabref/gui/MainMenu.java @@ -153,7 +153,7 @@ private void createMenu() { new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.SHOW_PREFS, new ShowPreferencesAction(frame, taskExecutor)), + factory.createMenuItem(StandardActions.SHOW_PREFS, new ShowPreferencesAction(frame)), new SeparatorMenuItem(), diff --git a/src/main/java/org/jabref/gui/actions/StandardActions.java b/src/main/java/org/jabref/gui/actions/StandardActions.java index 4053cdc7ea1..eb0c94f866d 100644 --- a/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -1,5 +1,6 @@ package org.jabref.gui.actions; +import java.util.Objects; import java.util.Optional; import org.jabref.gui.icon.IconTheme; @@ -194,7 +195,7 @@ public enum StandardActions implements Action { GROUP_ENTRIES_ADD(Localization.lang("Add selected entries to this group")), GROUP_ENTRIES_REMOVE(Localization.lang("Remove selected entries from this group")); - private final String text; + private String text; private final String description; private final Optional icon; private final Optional keyBinding; @@ -271,4 +272,9 @@ public String getText() { public String getDescription() { return description; } + + public Action withText(String text) { + this.text = Objects.requireNonNull(text); + return this; + } } diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index 7c4e2b2e329..6ff70140fc7 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -198,7 +198,7 @@ public MainTable(MainTableDataModel model, taskExecutor); // Enable the header right-click menu. - new MainTableHeaderContextMenu(this, rightClickMenuFactory).show(true); + new MainTableHeaderContextMenu(this, rightClickMenuFactory, this.libraryTab.frame(), keyBindingRepository).show(true); } /** diff --git a/src/main/java/org/jabref/gui/maintable/MainTableHeaderContextMenu.java b/src/main/java/org/jabref/gui/maintable/MainTableHeaderContextMenu.java index 37b628ed5cd..7bf82372c10 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTableHeaderContextMenu.java +++ b/src/main/java/org/jabref/gui/maintable/MainTableHeaderContextMenu.java @@ -5,27 +5,42 @@ import javafx.collections.ObservableList; import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; import javafx.scene.control.RadioMenuItem; import javafx.scene.control.SeparatorMenuItem; import javafx.scene.control.TableColumn; import javafx.scene.layout.StackPane; +import org.jabref.gui.JabRefFrame; +import org.jabref.gui.actions.ActionFactory; +import org.jabref.gui.actions.StandardActions; +import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.maintable.columns.MainTableColumn; +import org.jabref.gui.preferences.ShowPreferencesAction; +import org.jabref.gui.preferences.table.TableTab; +import org.jabref.logic.l10n.Localization; public class MainTableHeaderContextMenu extends ContextMenu { private static final int OUT_OF_BOUNDS = -1; MainTable mainTable; MainTableColumnFactory factory; + private final JabRefFrame frame; + private final KeyBindingRepository keyBindingRepository; /** * Constructor for the right click menu * */ - public MainTableHeaderContextMenu(MainTable mainTable, MainTableColumnFactory factory) { + public MainTableHeaderContextMenu(MainTable mainTable, + MainTableColumnFactory factory, + JabRefFrame frame, + KeyBindingRepository keyBindingRepository) { super(); + this.frame = frame; this.mainTable = mainTable; this.factory = factory; + this.keyBindingRepository = keyBindingRepository; constructItems(mainTable); } @@ -66,15 +81,22 @@ private void constructItems(MainTable mainTable) { } } - SeparatorMenuItem separator = new SeparatorMenuItem(); - this.getItems().add(separator); + if (!commonColumns.isEmpty()) { + this.getItems().add(new SeparatorMenuItem()); - // Append to the menu the current remaining columns in the common columns. - - for (TableColumn tableColumn : commonColumns) { - RightClickMenuItem itemToAdd = createMenuItem(tableColumn, false); - this.getItems().add(itemToAdd); + // Append to the menu the current remaining columns in the common columns. + for (TableColumn tableColumn : commonColumns) { + RightClickMenuItem itemToAdd = createMenuItem(tableColumn, false); + this.getItems().add(itemToAdd); + } } + + this.getItems().add(new SeparatorMenuItem()); + ActionFactory actionfactory = new ActionFactory(this.keyBindingRepository); + MenuItem showMoreItem = actionfactory.createMenuItem( + StandardActions.SHOW_PREFS.withText(Localization.lang("More options...")), + new ShowPreferencesAction(frame, TableTab.class)); + this.getItems().add(showMoreItem); } /** @@ -110,7 +132,7 @@ private int obtainIndexOfColumn(MainTableColumn searchColumn) { */ @SuppressWarnings("rawtypes") private void addColumn(MainTableColumn tableColumn, int index) { - if (index <= OUT_OF_BOUNDS || index >= mainTable.getColumns().size()) { + if ((index <= OUT_OF_BOUNDS) || (index >= mainTable.getColumns().size())) { mainTable.getColumns().add(tableColumn); } else { mainTable.getColumns().add(index, tableColumn); diff --git a/src/main/java/org/jabref/gui/preferences/PreferencesDialogView.java b/src/main/java/org/jabref/gui/preferences/PreferencesDialogView.java index 1de0bdd99f0..864e1a76ad7 100644 --- a/src/main/java/org/jabref/gui/preferences/PreferencesDialogView.java +++ b/src/main/java/org/jabref/gui/preferences/PreferencesDialogView.java @@ -1,6 +1,7 @@ package org.jabref.gui.preferences; import java.util.Locale; +import java.util.Optional; import javafx.fxml.FXML; import javafx.scene.control.ButtonType; @@ -10,7 +11,6 @@ import javafx.scene.input.KeyCode; import org.jabref.gui.DialogService; -import org.jabref.gui.JabRefFrame; import org.jabref.gui.icon.IconTheme; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.theme.ThemeManager; @@ -42,12 +42,12 @@ public class PreferencesDialogView extends BaseDialog preferencesTabToSelectClass; - public PreferencesDialogView(JabRefFrame frame) { - this.frame = frame; + public PreferencesDialogView(Class preferencesTabToSelectClass) { this.setTitle(Localization.lang("JabRef preferences")); + this.preferencesTabToSelectClass = preferencesTabToSelectClass; ViewLoader.view(this) .load() @@ -71,7 +71,7 @@ public PreferencesDialogViewModel getViewModel() { @FXML private void initialize() { - viewModel = new PreferencesDialogViewModel(dialogService, preferencesService, frame); + viewModel = new PreferencesDialogViewModel(dialogService, preferencesService); preferenceTabList.itemsProperty().setValue(viewModel.getPreferenceTabs()); @@ -102,7 +102,16 @@ private void initialize() { } }); - preferenceTabList.getSelectionModel().selectFirst(); + if (this.preferencesTabToSelectClass != null) { + Optional tabToSelectIfExist = preferenceTabList.getItems() + .stream() + .filter(prefTab -> prefTab.getClass().equals(preferencesTabToSelectClass)) + .findFirst(); + tabToSelectIfExist.ifPresent(preferencesTab -> preferenceTabList.getSelectionModel().select(preferencesTab)); + } else { + preferenceTabList.getSelectionModel().selectFirst(); + } + new ViewModelListCellFactory() .withText(PreferencesTab::getTabName) .install(preferenceTabList); diff --git a/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java b/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java index 35f6a9db00b..3647a845a61 100644 --- a/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java @@ -13,7 +13,6 @@ import org.jabref.gui.AbstractViewModel; import org.jabref.gui.DialogService; import org.jabref.gui.Globals; -import org.jabref.gui.JabRefFrame; import org.jabref.gui.preferences.autocompletion.AutoCompletionTab; import org.jabref.gui.preferences.citationkeypattern.CitationKeyPatternTab; import org.jabref.gui.preferences.customentrytypes.CustomEntryTypesTab; @@ -55,12 +54,10 @@ public class PreferencesDialogViewModel extends AbstractViewModel { private final DialogService dialogService; private final PreferencesService preferences; private final ObservableList preferenceTabs; - private final JabRefFrame frame; - public PreferencesDialogViewModel(DialogService dialogService, PreferencesService preferences, JabRefFrame frame) { + public PreferencesDialogViewModel(DialogService dialogService, PreferencesService preferences) { this.dialogService = dialogService; this.preferences = preferences; - this.frame = frame; preferenceTabs = FXCollections.observableArrayList( new GeneralTab(), @@ -102,7 +99,7 @@ public void importPreferences() { .ifPresent(file -> { try { preferences.importPreferences(file); - updateAfterPreferenceChanges(); + setValues(); dialogService.showWarningDialogAndWait(Localization.lang("Import preferences"), Localization.lang("You must restart JabRef for this to come into effect.")); @@ -154,19 +151,10 @@ public void resetPreferences() { dialogService.showErrorDialogAndWait(Localization.lang("Reset preferences"), ex); } - updateAfterPreferenceChanges(); + setValues(); } } - /** - * Reloads the preferences into the UI - */ - private void updateAfterPreferenceChanges() { - setValues(); - - frame.getLibraryTabs().forEach(panel -> panel.getMainTable().getTableModel().refresh()); - } - /** * Checks if all tabs are valid */ @@ -203,12 +191,10 @@ public void storeAllSettings() { + Localization.lang("You must restart JabRef for this to come into effect.")); } - frame.setupAllTables(); - frame.getGlobalSearchBar().updateHintVisibility(); Globals.entryTypesManager = preferences.getCustomEntryTypesRepository(); dialogService.notify(Localization.lang("Preferences recorded.")); - updateAfterPreferenceChanges(); + setValues(); } /** diff --git a/src/main/java/org/jabref/gui/preferences/ShowPreferencesAction.java b/src/main/java/org/jabref/gui/preferences/ShowPreferencesAction.java index b070e914aea..08eec1e610c 100644 --- a/src/main/java/org/jabref/gui/preferences/ShowPreferencesAction.java +++ b/src/main/java/org/jabref/gui/preferences/ShowPreferencesAction.java @@ -3,23 +3,31 @@ import org.jabref.gui.DialogService; import org.jabref.gui.JabRefFrame; import org.jabref.gui.actions.SimpleCommand; -import org.jabref.gui.util.TaskExecutor; import com.airhacks.afterburner.injection.Injector; public class ShowPreferencesAction extends SimpleCommand { private final JabRefFrame jabRefFrame; - private final TaskExecutor taskExecutor; + private final Class preferencesTabToSelectClass; - public ShowPreferencesAction(JabRefFrame jabRefFrame, TaskExecutor taskExecutor) { + public ShowPreferencesAction(JabRefFrame jabRefFrame) { + this(jabRefFrame, null); + } + + public ShowPreferencesAction(JabRefFrame jabRefFrame, Class preferencesTabToSelectClass) { this.jabRefFrame = jabRefFrame; - this.taskExecutor = taskExecutor; + this.preferencesTabToSelectClass = preferencesTabToSelectClass; } @Override public void execute() { DialogService dialogService = Injector.instantiateModelOrService(DialogService.class); - dialogService.showCustomDialog(new PreferencesDialogView(jabRefFrame)); + dialogService.showCustomDialog(new PreferencesDialogView(preferencesTabToSelectClass)); + + // Refresh frame and tables + jabRefFrame.getGlobalSearchBar().updateHintVisibility(); + jabRefFrame.setupAllTables(); + jabRefFrame.getLibraryTabs().forEach(panel -> panel.getMainTable().getTableModel().refresh()); } } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index e111db9d9b1..bb5a2f40c7d 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2621,3 +2621,4 @@ Would\ you\ like\ to\ enable\ fetching\ of\ journal\ information?\ This\ can\ be Enable=Enable Keep\ disabled=Keep disabled +More\ options...=More options... From fe07c12cb1e0a2f7a4dcc128e44a2c04f3588584 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 28 Nov 2023 10:29:37 +0100 Subject: [PATCH 041/144] Add CITATION.CFF (#10672) * Add CITATION.CFF * Aktualisieren von CITATION.cff --- CITATION.cff | 58 +++++++++++++++++++ src/test/resources/testbib/jabref-authors.bib | 15 ++++- 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 CITATION.cff diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 00000000000..570a4e57b0c --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,58 @@ +# This CITATION.cff file was generated with cffinit. +# Visit https://bit.ly/cffinit to generate yours today! + +cff-version: 1.2.0 +title: JabRef +message: >- + If you use this software, please cite it using the + metadata from this file. +type: software +authors: + - given-names: Oliver + family-names: Kopp + orcid: 'https://orcid.org/0000-0001-6962-4290' + - given-names: Tobias + family-names: Diez + orcid: 'https://orcid.org/0000-0002-1407-7696' + - given-names: Christoph + family-names: Schwentker + - given-names: Carl Christian + family-names: Snethlage + - given-names: Jonatan + family-names: Asketorp + - given-names: Benedikt + family-names: Tutzer + - given-names: Thilo + family-names: Ertel + - given-names: Houssem + family-names: Nasri +repository-code: 'https://github.com/jabref/jabref/' +url: 'https://www.jabref.org' +abstract: >- + JabRef is an open-source, cross-platform citation and + reference management tool. +keywords: + - reference manager + - bibtex + - biblatex +license: MIT +preferred-citation: + type: article + authors: + - family-names: "Kopp" + given-names: "Oliver" + orcid: "https://orcid.org/0000-0001-6962-4290" + - family-names: "Snethlage" + given-names: "Carl Christian" + - family-names: "Schwentker" + given-names: "Christoph" + doi: "10.47397/tb/44-3/tb138kopp-jabref" + journal: "TUGboat" + month: 11 + start: 441 + end: 447 + title: "JabRef: BibTeX-based literature management software" + issue: 138 + volume: 44 + number: 3 + year: 2023 diff --git a/src/test/resources/testbib/jabref-authors.bib b/src/test/resources/testbib/jabref-authors.bib index 118df45c5b5..50119bc6454 100644 --- a/src/test/resources/testbib/jabref-authors.bib +++ b/src/test/resources/testbib/jabref-authors.bib @@ -1821,7 +1821,7 @@ @Article{BinzBreitenbuecherKoppEtAl2014a year = {2014}, } -@Article{, +@Article{Wettinger2014, author = {Johannes Wettinger and Tobias Binz and Uwe Breitenb{\"{u}}cher}, title = {Streamlining Cloud Management Incomplete}, doi = {10.4018/978-1-4666-6539-2.ch106}, @@ -3016,6 +3016,19 @@ @InProceedings{Kopp2018 keywords = {ADR, MADR, architecture decision records, architectural decision records, Nygard}, } +@Article{jabref, + author = {Oliver Kopp and Carl Christian Snethlage and Christoph Schwentker}, + title = {JabRef: BibTeX-based literature management software}, + doi = {10.47397/tb/44-3/tb138kopp-jabref}, + issn = {0896-3207}, + issue = {138}, + number = {3}, + pages = {441--447}, + volume = {44}, + journal = {TUGboat}, + year = {2023}, +} + @Comment{jabref-meta: databaseType:biblatex;} @Comment{jabref-meta: grouping: From d7b67949a8a3575e81445cbe62092dc48b3320cb Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 1 Dec 2023 03:34:58 +0100 Subject: [PATCH 042/144] Update CSL styles (#10678) Co-authored-by: Siedlerchr --- src/main/resources/csl-styles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles index 5bea241e5a2..42f54b22d80 160000 --- a/src/main/resources/csl-styles +++ b/src/main/resources/csl-styles @@ -1 +1 @@ -Subproject commit 5bea241e5a2acacc29b947e21539be9cbe79c8bd +Subproject commit 42f54b22d8058c957404945b7dca5cc59c1fc68d From 846929d185be2a22f1f4ad5b8728003c33f2e1f7 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 3 Dec 2023 14:25:24 +0100 Subject: [PATCH 043/144] Fixes concurrency group to speed up merge queues (#10680) --- .github/workflows/check-links.yml | 2 +- .github/workflows/deployment-arm64.yml | 2 +- .github/workflows/deployment.yml | 2 +- .github/workflows/tests-fetchers.yml | 2 +- .github/workflows/tests.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index a121d295643..0f56e3c8499 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -11,7 +11,7 @@ on: workflow_dispatch: concurrency: - group: "${{ github.workflow }}-${{ github.head_ref }}" + group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}" cancel-in-progress: true jobs: diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index cfb9ba0c52f..c9092fe2262 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -22,7 +22,7 @@ env: JAVA_OPTS: -Xmx4g concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}" cancel-in-progress: true jobs: diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 15597b6833a..eb6381e9c9c 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -31,7 +31,7 @@ env: JDK21: "" concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}" cancel-in-progress: true jobs: diff --git a/.github/workflows/tests-fetchers.yml b/.github/workflows/tests-fetchers.yml index 267ecc18c72..4adfd919c8c 100644 --- a/.github/workflows/tests-fetchers.yml +++ b/.github/workflows/tests-fetchers.yml @@ -29,7 +29,7 @@ env: BiodiversityHeritageApiKey: ${{ secrets.BiodiversityHeritageApiKey_FOR_TESTS}} concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}" cancel-in-progress: true permissions: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c7c89a73930..6dad61e07c0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ env: JAVA_OPTS: -Xmx4g concurrency: - group: tests-${{ github.head_ref }} + group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}" cancel-in-progress: true permissions: From 5eb3a77a1871832b8c463ec8b5e7cf5168489a34 Mon Sep 17 00:00:00 2001 From: Christoph Date: Mon, 4 Dec 2023 10:56:54 +0100 Subject: [PATCH 044/144] update to jdk 21 --- .github/workflows/update-gradle-wrapper.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index afa016cb262..eb74482af0b 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -8,9 +8,14 @@ on: jobs: update-gradle-wrapper: runs-on: ubuntu-latest - + steps: - uses: actions/checkout@v4 + - name: Setup JDK + uses: actions/setup-java@v3 + with: + java-version: 21.0.1 + distribution: 'liberica' - name: Update Gradle Wrapper uses: gradle-update/update-gradle-wrapper-action@v1 From 1185fdaed482c8c90393e539a02c229a7e922a15 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 4 Dec 2023 11:11:51 +0100 Subject: [PATCH 045/144] Update Gradle Wrapper from 8.4 to 8.5. (#10681) Signed-off-by: gradle-update-robot Co-authored-by: gradle-update-robot --- gradle/wrapper/gradle-wrapper.jar | Bin 63721 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c49b765f8051ef9d0a6055ff8e46073d8..d64cd4917707c1f8861d8cb53dd15194d4248596 100644 GIT binary patch literal 43462 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 literal 63721 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鯊%1Et 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⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 46671acb6e1..db8c3baafe3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From dde11ae26ed95fa7cf7de3060788f3bd49621563 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:19:25 +0000 Subject: [PATCH 046/144] Bump io.github.classgraph:classgraph from 4.8.164 to 4.8.165 (#10684) Bumps [io.github.classgraph:classgraph](https://github.com/classgraph/classgraph) from 4.8.164 to 4.8.165. - [Release notes](https://github.com/classgraph/classgraph/releases) - [Commits](https://github.com/classgraph/classgraph/compare/classgraph-4.8.164...classgraph-4.8.165) --- updated-dependencies: - dependency-name: io.github.classgraph:classgraph dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index cf136281877..61b5d5ddd2d 100644 --- a/build.gradle +++ b/build.gradle @@ -235,7 +235,7 @@ dependencies { // Allow objects "magically" to be mapped to JSON using GSON // implementation 'org.glassfish.jersey.media:jersey-media-json-gson:3.1.1' - testImplementation 'io.github.classgraph:classgraph:4.8.164' + testImplementation 'io.github.classgraph:classgraph:4.8.165' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.10.1' From 4dffce152a7ba6d0478905f996f27fbb54897650 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:20:47 +0000 Subject: [PATCH 047/144] Bump org.apache.pdfbox:xmpbox from 3.0.0 to 3.0.1 (#10683) Bumps org.apache.pdfbox:xmpbox from 3.0.0 to 3.0.1. --- updated-dependencies: - dependency-name: org.apache.pdfbox:xmpbox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 61b5d5ddd2d..b3bc53972c7 100644 --- a/build.gradle +++ b/build.gradle @@ -115,7 +115,7 @@ dependencies { implementation 'org.apache.pdfbox:pdfbox:3.0.0' implementation 'org.apache.pdfbox:fontbox:3.0.0' - implementation 'org.apache.pdfbox:xmpbox:3.0.0' + implementation 'org.apache.pdfbox:xmpbox:3.0.1' implementation 'org.apache.lucene:lucene-core:9.8.0' implementation 'org.apache.lucene:lucene-queryparser:9.8.0' From 5a8f8270b1686e815720504f3a16e3ec1ecb4d93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:21:00 +0000 Subject: [PATCH 048/144] Bump org.libreoffice:libreoffice from 7.6.1 to 7.6.4 (#10685) Bumps org.libreoffice:libreoffice from 7.6.1 to 7.6.4. --- updated-dependencies: - dependency-name: org.libreoffice:libreoffice dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b3bc53972c7..9142105d9b1 100644 --- a/build.gradle +++ b/build.gradle @@ -132,8 +132,8 @@ dependencies { implementation 'commons-cli:commons-cli:1.6.0' - implementation 'org.libreoffice:unoloader:7.6.1' - implementation 'org.libreoffice:libreoffice:7.6.1' + implementation 'org.libreoffice:unoloader:7.6.4' + implementation 'org.libreoffice:libreoffice:7.6.4' implementation 'io.github.java-diff-utils:java-diff-utils:4.12' implementation 'info.debatty:java-string-similarity:2.0.0' From 3844ff0a99cb6fb5fc3e46f01563eff4b6379a9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:50:31 +0000 Subject: [PATCH 049/144] Bump actions/setup-java from 3 to 4 (#10688) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deployment-arm64.yml | 2 +- .github/workflows/deployment.yml | 2 +- .github/workflows/refresh-journal-lists.yml | 2 +- .github/workflows/tests-fetchers.yml | 2 +- .github/workflows/tests.yml | 14 +++++++------- .github/workflows/update-gradle-wrapper.yml | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index c9092fe2262..4f436d1fd30 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -59,7 +59,7 @@ jobs: id: gitversion uses: gittools/actions/gitversion/execute@v0.10.2 - name: Setup JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index eb6381e9c9c..6fc6a5a6fef 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -90,7 +90,7 @@ jobs: id: gitversion uses: gittools/actions/gitversion/execute@v0.10.2 - name: Setup JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' diff --git a/.github/workflows/refresh-journal-lists.yml b/.github/workflows/refresh-journal-lists.yml index 8762eace3da..3ee5042adf2 100644 --- a/.github/workflows/refresh-journal-lists.yml +++ b/.github/workflows/refresh-journal-lists.yml @@ -28,7 +28,7 @@ jobs: git checkout main git pull - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' diff --git a/.github/workflows/tests-fetchers.yml b/.github/workflows/tests-fetchers.yml index 4adfd919c8c..f107721e328 100644 --- a/.github/workflows/tests-fetchers.yml +++ b/.github/workflows/tests-fetchers.yml @@ -46,7 +46,7 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6dad61e07c0..af7f28c8fc0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -35,7 +35,7 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' @@ -60,7 +60,7 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' @@ -79,7 +79,7 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' @@ -161,7 +161,7 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' @@ -197,7 +197,7 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' @@ -235,7 +235,7 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' @@ -280,7 +280,7 @@ jobs: show-progress: 'false' - name: Set up JDK if: github.ref == 'refs/heads/main' - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index eb74482af0b..1cce0bab119 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Setup JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' From 32fbf088d6e1cb143b7202356d078715d83e62a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 16:22:16 +0000 Subject: [PATCH 050/144] Bump org.apache.lucene:lucene-queries from 9.8.0 to 9.9.0 (#10686) * Bump org.apache.lucene:lucene-queries from 9.8.0 to 9.9.0 Bumps org.apache.lucene:lucene-queries from 9.8.0 to 9.9.0. --- updated-dependencies: - dependency-name: org.apache.lucene:lucene-queries dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Update Lucene index version * Update all of lucene --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Oliver Kopp --- CHANGELOG.md | 1 + build.gradle | 10 +++++----- src/main/java/module-info.java | 2 +- .../jabref/model/pdf/search/SearchFieldConstants.java | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4efd64530f1..cc66aecf4e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We replaced "SearchAll" in Web Search by "Search Selected". [#10556](https://github.com/JabRef/jabref/issues/10556) - Short DOI formatter now checks, if the value is already formatted. If so, it returns the value instead of calling the ShortDOIService again. [#10589](https://github.com/JabRef/jabref/issues/10589) - We upgraded to JavaFX 21.0.1. As a consequence JabRef requires now macOS 11 or later and GTK 3.8 or later on Linux [10627](https://github.com/JabRef/jabref/pull/10627). +- We upgraded to Lucene 9.9 for the fulltext search. The search index will be rebuild. [#10686](https://github.com/JabRef/jabref/pull/10686) ### Fixed diff --git a/build.gradle b/build.gradle index 9142105d9b1..3a9c699e1eb 100644 --- a/build.gradle +++ b/build.gradle @@ -117,11 +117,11 @@ dependencies { implementation 'org.apache.pdfbox:fontbox:3.0.0' implementation 'org.apache.pdfbox:xmpbox:3.0.1' - implementation 'org.apache.lucene:lucene-core:9.8.0' - implementation 'org.apache.lucene:lucene-queryparser:9.8.0' - implementation 'org.apache.lucene:lucene-queries:9.8.0' - implementation 'org.apache.lucene:lucene-analysis-common:9.8.0' - implementation 'org.apache.lucene:lucene-highlighter:9.8.0' + implementation 'org.apache.lucene:lucene-core:9.9.0' + implementation 'org.apache.lucene:lucene-queryparser:9.9.0' + implementation 'org.apache.lucene:lucene-queries:9.9.0' + implementation 'org.apache.lucene:lucene-analysis-common:9.9.0' + implementation 'org.apache.lucene:lucene-highlighter:9.9.0' implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.10.0' implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.14.0' diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 70eb10b2914..8e26519c8e5 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -121,7 +121,7 @@ // fulltext search requires org.apache.lucene.core; // In case the version is updated, please also adapt SearchFieldConstants#VERSION to the newly used version - uses org.apache.lucene.codecs.lucene95.Lucene95Codec; + uses org.apache.lucene.codecs.lucene99.Lucene99Codec; requires org.apache.lucene.queryparser; uses org.apache.lucene.queryparser.classic.MultiFieldQueryParser; diff --git a/src/main/java/org/jabref/model/pdf/search/SearchFieldConstants.java b/src/main/java/org/jabref/model/pdf/search/SearchFieldConstants.java index b4fa07c5d34..6d381a2b249 100644 --- a/src/main/java/org/jabref/model/pdf/search/SearchFieldConstants.java +++ b/src/main/java/org/jabref/model/pdf/search/SearchFieldConstants.java @@ -10,5 +10,5 @@ public class SearchFieldConstants { public static final String[] PDF_FIELDS = new String[]{PATH, CONTENT, PAGE_NUMBER, MODIFIED, ANNOTATIONS}; - public static final String VERSION = "95"; + public static final String VERSION = "99"; } From b08e78c99e40852c5b5ed8a95b441157fe396f77 Mon Sep 17 00:00:00 2001 From: Christoph Date: Mon, 4 Dec 2023 20:58:33 +0100 Subject: [PATCH 051/144] Fix missing module error for CSL (#10689) * Add more debug output * add commons lang 3 --------- Co-authored-by: Oliver Kopp --- build.gradle | 1 + .../org/jabref/gui/preview/PreviewViewer.java | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 3a9c699e1eb..36c746ae76b 100644 --- a/build.gradle +++ b/build.gradle @@ -592,6 +592,7 @@ jlink { requires 'com.google.gson' requires 'org.slf4j' requires 'jakarta.xml.bind' + requires 'org.apache.commons.lang3' uses 'org.mariadb.jdbc.credential.CredentialPlugin' uses 'org.mariadb.jdbc.authentication.AuthenticationPlugin' uses 'org.mariadb.jdbc.tls.TlsSocketPlugin' diff --git a/src/main/java/org/jabref/gui/preview/PreviewViewer.java b/src/main/java/org/jabref/gui/preview/PreviewViewer.java index 544275f741a..ebe6f5c08c4 100644 --- a/src/main/java/org/jabref/gui/preview/PreviewViewer.java +++ b/src/main/java/org/jabref/gui/preview/PreviewViewer.java @@ -1,6 +1,8 @@ package org.jabref.gui.preview; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.net.MalformedURLException; import java.util.Objects; import java.util.Optional; @@ -257,13 +259,22 @@ private void update() { Number.serialExportNumber = 1; // Set entry number in case that is included in the preview layout. + final BibEntry theEntry = entry.get(); BackgroundTask - .wrap(() -> layout.generatePreview(entry.get(), database)) + .wrap(() -> layout.generatePreview(theEntry, database)) .onRunning(() -> setPreviewText("" + Localization.lang("Processing %0", Localization.lang("Citation Style")) + ": " + layout.getDisplayName() + " ..." + "")) .onSuccess(this::setPreviewText) .onFailure(exception -> { LOGGER.error("Error while generating citation style", exception); - setPreviewText(Localization.lang("Error while generating citation style")); + + // Convert stack trace to a string + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + exception.printStackTrace(printWriter); + String stackTraceString = stringWriter.toString(); + + // Set the preview text with the localized error message and the stack trace + setPreviewText(Localization.lang("Error while generating citation style") + "\n\n" + exception.getLocalizedMessage() + "\n\nBibTeX (internal):\n" + theEntry + "\n\nStack Trace:\n" + stackTraceString); }) .executeWith(taskExecutor); } From 67e25a0c21ef3cdabd2d03541f4678694c5af6cf Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 4 Dec 2023 21:11:11 +0100 Subject: [PATCH 052/144] Add hide button for user comments (#10610) * Place BibTeX comment field first (and use JDK21 data structure) - Place all user comments togehter (and BibTeX standard comment first) - Switch from Set to StructuredSet * Enable "Add" button * Fix codestyle * Hide user comment button remove from grid todo move to prefs * Add new property for hiding/showing comment tabs move entry editor preferences * Add new property for hiding/showing comment tabs move entry editor preferences * wire checkbox/button to preferences * fix checkstyle * Move back * fix * fix test * Fix logic - and adapt tests --------- Co-authored-by: Christoph --- CHANGELOG.md | 1 + .../jabref/gui/entryeditor/CommentsTab.java | 96 +++++++++++++++---- .../gui/entryeditor/DeprecatedFieldsTab.java | 12 +-- .../entryeditor/EntryEditorPreferences.java | 17 +++- .../gui/entryeditor/FieldsEditorTab.java | 76 ++++++++------- .../entryeditor/OptionalFieldsTabBase.java | 10 +- .../gui/entryeditor/OtherFieldsTab.java | 12 +-- .../gui/entryeditor/RequiredFieldsTab.java | 6 +- .../gui/entryeditor/UserDefinedFieldsTab.java | 3 +- .../entryeditor/EntryEditorTab.fxml | 5 +- .../entryeditor/EntryEditorTab.java | 2 + .../entryeditor/EntryEditorTabViewModel.java | 8 ++ .../citationstyle/JabRefItemDataProvider.java | 11 ++- .../java/org/jabref/model/entry/BibEntry.java | 10 +- .../org/jabref/model/entry/BibEntryType.java | 19 ++-- .../entry/field/UserSpecificCommentField.java | 3 +- .../jabref/preferences/GuiPreferences.java | 8 -- .../jabref/preferences/JabRefPreferences.java | 11 ++- src/main/resources/l10n/JabRef_en.properties | 4 + .../gui/entryeditor/CommentsTabTest.java | 41 +++++++- 20 files changed, 248 insertions(+), 107 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc66aecf4e3..6d4d298bfb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We replaced "SearchAll" in Web Search by "Search Selected". [#10556](https://github.com/JabRef/jabref/issues/10556) - Short DOI formatter now checks, if the value is already formatted. If so, it returns the value instead of calling the ShortDOIService again. [#10589](https://github.com/JabRef/jabref/issues/10589) - We upgraded to JavaFX 21.0.1. As a consequence JabRef requires now macOS 11 or later and GTK 3.8 or later on Linux [10627](https://github.com/JabRef/jabref/pull/10627). +- A user-specific comment fields is not enabled by default, but can be enabled using the "Add" button. [#10424](https://github.com/JabRef/jabref/issues/10424) - We upgraded to Lucene 9.9 for the fulltext search. The search index will be rebuild. [#10686](https://github.com/JabRef/jabref/pull/10686) ### Fixed diff --git a/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java b/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java index 269ab381a83..dc4434ce5d4 100644 --- a/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java @@ -1,16 +1,25 @@ package org.jabref.gui.entryeditor; +import java.util.Comparator; import java.util.LinkedHashSet; import java.util.Map; -import java.util.Set; +import java.util.Optional; +import java.util.SequencedSet; import java.util.stream.Collectors; import javax.swing.undo.UndoManager; +import javafx.collections.ObservableList; +import javafx.geometry.VPos; +import javafx.scene.control.Button; +import javafx.scene.layout.Priority; +import javafx.scene.layout.RowConstraints; + import org.jabref.gui.DialogService; import org.jabref.gui.StateManager; import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.gui.fieldeditors.FieldEditorFX; +import org.jabref.gui.fieldeditors.FieldNameLabel; import org.jabref.gui.icon.IconTheme; import org.jabref.gui.theme.ThemeManager; import org.jabref.gui.util.TaskExecutor; @@ -28,6 +37,10 @@ public class CommentsTab extends FieldsEditorTab { public static final String NAME = "Comments"; private final String defaultOwner; + private final UserSpecificCommentField userSpecificCommentField; + + private final EntryEditorPreferences entryEditorPreferences; + public CommentsTab(PreferencesService preferences, BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, @@ -54,38 +67,89 @@ public CommentsTab(PreferencesService preferences, this.defaultOwner = preferences.getOwnerPreferences().getDefaultOwner(); setText(Localization.lang("Comments")); setGraphic(IconTheme.JabRefIcons.COMMENT.getGraphicNode()); + + userSpecificCommentField = new UserSpecificCommentField(defaultOwner); + entryEditorPreferences = preferences.getEntryEditorPreferences(); } @Override - protected Set determineFieldsToShow(BibEntry entry) { - UserSpecificCommentField defaultCommentField = new UserSpecificCommentField(defaultOwner); + protected SequencedSet determineFieldsToShow(BibEntry entry) { + SequencedSet comments = new LinkedHashSet<>(); - // As default: Show BibTeX comment field and the user-specific comment field of the default owner - Set comments = new LinkedHashSet<>(Set.of(defaultCommentField, StandardField.COMMENT)); + // First comes the standard comment field + comments.add(StandardField.COMMENT); - comments.addAll(entry.getFields().stream() - .filter(field -> field instanceof UserSpecificCommentField || - field.getName().toLowerCase().contains("comment")) - .collect(Collectors.toSet())); + // Also show comment field of the current user (if enabled in the preferences) + if (entry.hasField(userSpecificCommentField) || entryEditorPreferences.shouldShowUserCommentsFields()) { + comments.add(userSpecificCommentField); + } + // Show all non-empty comment fields (otherwise, they are completely hidden) + comments.addAll(entry.getFields().stream() + .filter(field -> (field instanceof UserSpecificCommentField && !field.equals(userSpecificCommentField)) + || field.getName().toLowerCase().contains("comment")) + .sorted(Comparator.comparing(Field::getName)) + .collect(Collectors.toCollection(LinkedHashSet::new))); return comments; } + /** + * Comment editors: three times size of button + */ + private void setCompressedRowLayout() { + int numberOfComments = gridPane.getRowCount() - 1; + double totalWeight = numberOfComments * 3 + 1; + + RowConstraints commentConstraint = new RowConstraints(); + commentConstraint.setVgrow(Priority.ALWAYS); + commentConstraint.setValignment(VPos.TOP); + double commentHeightPercent = 3.0 / totalWeight * 100.0; + commentConstraint.setPercentHeight(commentHeightPercent); + + RowConstraints buttonConstraint = new RowConstraints(); + buttonConstraint.setVgrow(Priority.ALWAYS); + buttonConstraint.setValignment(VPos.TOP); + double addButtonHeightPercent = 1.0 / totalWeight * 100.0; + buttonConstraint.setPercentHeight(addButtonHeightPercent); + + ObservableList rowConstraints = gridPane.getRowConstraints(); + rowConstraints.clear(); + for (int i = 1; i <= numberOfComments; i++) { + rowConstraints.add(commentConstraint); + } + rowConstraints.add(buttonConstraint); + } + @Override protected void setupPanel(BibEntry entry, boolean compressed) { super.setupPanel(entry, compressed); + Optional fieldEditorForUserDefinedComment = editors.entrySet().stream().filter(f -> f.getKey().getName().contains(defaultOwner)).map(Map.Entry::getValue).findFirst(); for (Map.Entry fieldEditorEntry : editors.entrySet()) { Field field = fieldEditorEntry.getKey(); FieldEditorFX editor = fieldEditorEntry.getValue(); - if (field instanceof UserSpecificCommentField) { - if (field.getName().contains(defaultOwner)) { - editor.getNode().setDisable(false); - } - } else { - editor.getNode().setDisable(!field.getName().equals(StandardField.COMMENT.getName())); - } + boolean isStandardBibtexComment = field == StandardField.COMMENT; + boolean isDefaultOwnerComment = field.equals(userSpecificCommentField); + boolean shouldBeEnabled = isStandardBibtexComment || isDefaultOwnerComment; + editor.getNode().setDisable(!shouldBeEnabled); + } + + // Show "Hide" button only if user-specific comment field is empty. Otherwise, it is a strange UI, because the + // button would just disappear and no change **in the current** editor would be made + if (entryEditorPreferences.shouldShowUserCommentsFields() && !entry.hasField(userSpecificCommentField)) { + Button hideDefaultOwnerCommentButton = new Button(Localization.lang("Hide user comments")); + hideDefaultOwnerCommentButton.setOnAction(e -> { + var labelForField = gridPane.getChildren().stream().filter(s -> s instanceof FieldNameLabel).filter(x -> ((FieldNameLabel) x).getText().equals(userSpecificCommentField.getDisplayName())).findFirst(); + labelForField.ifPresent(label -> gridPane.getChildren().remove(label)); + fieldEditorForUserDefinedComment.ifPresent(f -> gridPane.getChildren().remove(f.getNode())); + editors.remove(userSpecificCommentField); + + entryEditorPreferences.setShowUserCommentsFields(false); + setupPanel(entry, false); + }); + gridPane.add(hideDefaultOwnerCommentButton, 1, gridPane.getRowCount(), 2, 1); + setCompressedRowLayout(); } } } diff --git a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java index a40302d54c6..ca15fe49b7c 100644 --- a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java @@ -1,8 +1,8 @@ package org.jabref.gui.entryeditor; -import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Optional; -import java.util.Set; +import java.util.SequencedSet; import java.util.stream.Collectors; import javax.swing.undo.UndoManager; @@ -59,14 +59,14 @@ public DeprecatedFieldsTab(BibDatabaseContext databaseContext, } @Override - protected Set determineFieldsToShow(BibEntry entry) { + protected SequencedSet determineFieldsToShow(BibEntry entry) { BibDatabaseMode mode = databaseContext.getMode(); Optional entryType = entryTypesManager.enrich(entry.getType(), mode); if (entryType.isPresent()) { - return entryType.get().getDeprecatedFields(mode).stream().filter(field -> !entry.getField(field).isEmpty()).collect(Collectors.toSet()); + return entryType.get().getDeprecatedFields(mode).stream().filter(field -> !entry.getField(field).isEmpty()).collect(Collectors.toCollection(LinkedHashSet::new)); } else { - // Entry type unknown -> treat all fields as required - return Collections.emptySet(); + // Entry type unknown -> treat all fields as required (thus no optional fields) + return new LinkedHashSet<>(); } } } diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java index c2afea3b0af..ae0fda0194c 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java @@ -48,6 +48,7 @@ public static JournalPopupEnabled fromString(String status) { private final BooleanProperty autoLinkFiles; private final ObjectProperty enablementStatus; private final BooleanProperty shouldShowSciteTab; + private final BooleanProperty showUserCommentsFields; public EntryEditorPreferences(Map> entryEditorTabList, Map> defaultEntryEditorTabList, @@ -60,7 +61,8 @@ public EntryEditorPreferences(Map> entryEditorTabList, double dividerPosition, boolean autolinkFilesEnabled, JournalPopupEnabled journalPopupEnabled, - boolean showSciteTab) { + boolean showSciteTab, + boolean showUserCommentsFields) { this.entryEditorTabList = new SimpleMapProperty<>(FXCollections.observableMap(entryEditorTabList)); this.defaultEntryEditorTabList = new SimpleMapProperty<>(FXCollections.observableMap(defaultEntryEditorTabList)); @@ -74,6 +76,7 @@ public EntryEditorPreferences(Map> entryEditorTabList, this.autoLinkFiles = new SimpleBooleanProperty(autolinkFilesEnabled); this.enablementStatus = new SimpleObjectProperty<>(journalPopupEnabled); this.shouldShowSciteTab = new SimpleBooleanProperty(showSciteTab); + this.showUserCommentsFields = new SimpleBooleanProperty(showUserCommentsFields); } public ObservableMap> getEntryEditorTabs() { @@ -211,4 +214,16 @@ public BooleanProperty shouldShowLSciteTabProperty() { public void setShouldShowSciteTab(boolean shouldShowSciteTab) { this.shouldShowSciteTab.set(shouldShowSciteTab); } + + public boolean shouldShowUserCommentsFields() { + return showUserCommentsFields.get(); + } + + public BooleanProperty showUserCommentsFieldsProperty() { + return showUserCommentsFields; + } + + public void setShowUserCommentsFields(boolean showUserCommentsFields) { + this.showUserCommentsFields.set(showUserCommentsFields); + } } diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java index 780af05c3c4..bdaf404b4c1 100644 --- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java @@ -6,11 +6,12 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; +import java.util.SequencedSet; import java.util.stream.Stream; import javax.swing.undo.UndoManager; +import javafx.collections.ObservableList; import javafx.geometry.VPos; import javafx.scene.Node; import javafx.scene.Parent; @@ -47,6 +48,7 @@ abstract class FieldsEditorTab extends EntryEditorTab { protected final BibDatabaseContext databaseContext; protected final Map editors = new LinkedHashMap<>(); + protected GridPane gridPane; private final boolean isCompressed; private final SuggestionProviders suggestionProviders; private final DialogService dialogService; @@ -59,7 +61,6 @@ abstract class FieldsEditorTab extends EntryEditorTab { private PreviewPanel previewPanel; private final UndoManager undoManager; private Collection fields = new ArrayList<>(); - private GridPane gridPane; public FieldsEditorTab(boolean compressed, BibDatabaseContext databaseContext, @@ -107,36 +108,22 @@ protected void setupPanel(BibEntry entry, boolean compressed) { fields = determineFieldsToShow(entry); - List

diff --git a/src/main/java/org/jabref/cli/JournalListMvGenerator.java b/src/main/java/org/jabref/cli/JournalListMvGenerator.java index 9a4fd3d67c6..dad187dc81a 100644 --- a/src/main/java/org/jabref/cli/JournalListMvGenerator.java +++ b/src/main/java/org/jabref/cli/JournalListMvGenerator.java @@ -37,15 +37,15 @@ public static void main(String[] args) throws IOException { // we currently do not have good support for BibTeX strings "journal_abbreviations_ieee_strings.csv" - ); + ); Files.createDirectories(journalListMvFile.getParent()); try (DirectoryStream stream = Files.newDirectoryStream(abbreviationsDirectory, "*.csv"); MVStore store = new MVStore.Builder(). - fileName(journalListMvFile.toString()). - compressHigh(). - open()) { + fileName(journalListMvFile.toString()). + compressHigh(). + open()) { MVMap fullToAbbreviation = store.openMap("FullToAbbreviation"); stream.forEach(Unchecked.consumer(path -> { String fileName = path.getFileName().toString(); diff --git a/src/main/java/org/jabref/cli/Launcher.java b/src/main/java/org/jabref/cli/Launcher.java index a49ea92e865..2e8033267d6 100644 --- a/src/main/java/org/jabref/cli/Launcher.java +++ b/src/main/java/org/jabref/cli/Launcher.java @@ -12,6 +12,7 @@ import org.jabref.gui.Globals; import org.jabref.gui.MainApplication; import org.jabref.logic.journals.JournalAbbreviationLoader; +import org.jabref.logic.journals.predatory.PredatoryJournalListLoader; import org.jabref.logic.l10n.Localization; import org.jabref.logic.net.ProxyAuthenticator; import org.jabref.logic.net.ProxyPreferences; @@ -169,6 +170,8 @@ private static void initGlobals(PreferencesService preferences) { // Read list(s) of journal names and abbreviations Globals.journalAbbreviationRepository = JournalAbbreviationLoader .loadRepository(preferences.getJournalAbbreviationPreferences()); + Globals.predatoryJournalRepository = PredatoryJournalListLoader + .loadRepository(); Globals.entryTypesManager = preferences.getCustomEntryTypesRepository(); Globals.protectedTermsLoader = new ProtectedTermsLoader(preferences.getProtectedTermsPreferences()); diff --git a/src/main/java/org/jabref/cli/PredatoryJournalsMvGenerator.java b/src/main/java/org/jabref/cli/PredatoryJournalsMvGenerator.java new file mode 100644 index 00000000000..574e287abf8 --- /dev/null +++ b/src/main/java/org/jabref/cli/PredatoryJournalsMvGenerator.java @@ -0,0 +1,47 @@ +package org.jabref.cli; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.jabref.logic.journals.predatory.PredatoryJournalInformation; +import org.jabref.logic.journals.predatory.PredatoryJournalListCrawler; + +import org.h2.mvstore.MVMap; +import org.h2.mvstore.MVStore; + +public class PredatoryJournalsMvGenerator { + public static void main(String[] args) throws IOException { + boolean verbose = (args.length == 1) && ("--verbose".equals(args[0])); + + Path predatoryJournalsMvFile = Path.of("build", "resources", "main", "journals", "predatory-journals.mv"); + Files.createDirectories(predatoryJournalsMvFile.getParent()); + + try (MVStore store = new MVStore.Builder() + .fileName(predatoryJournalsMvFile.toString()) + .compressHigh() + .backgroundExceptionHandler((t, e) -> { + System.err.println("Exception occurred in Thread " + t + "with exception " + e); + e.printStackTrace(); + }) + .open()) { + MVMap predatoryJournalsMap = store.openMap("PredatoryJournals"); + + PredatoryJournalListCrawler loader = new PredatoryJournalListCrawler(); + Set predatoryJournals = loader.loadFromOnlineSources(); + + var resultMap = predatoryJournals.stream().collect(Collectors.toMap(PredatoryJournalInformation::name, Function.identity(), + (predatoryJournalInformation, predatoryJournalInformation2) -> { + if (verbose) { + System.out.println("Double entry " + predatoryJournalInformation.name()); + } + return predatoryJournalInformation2; + })); + + predatoryJournalsMap.putAll(resultMap); + } + } +} diff --git a/src/main/java/org/jabref/gui/Globals.java b/src/main/java/org/jabref/gui/Globals.java index 04ead50bf63..06437d7f6c9 100644 --- a/src/main/java/org/jabref/gui/Globals.java +++ b/src/main/java/org/jabref/gui/Globals.java @@ -9,6 +9,7 @@ import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.logic.journals.predatory.PredatoryJournalRepository; import org.jabref.logic.protectedterms.ProtectedTermsLoader; import org.jabref.logic.remote.RemotePreferences; import org.jabref.logic.remote.server.RemoteListenerServerManager; @@ -51,6 +52,7 @@ public class Globals { * Only GUI code is allowed to access it, logic code should use dependency injection. */ public static JournalAbbreviationRepository journalAbbreviationRepository; + public static PredatoryJournalRepository predatoryJournalRepository; /** * This field is initialized upon startup. diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 8a829134b96..3f0f95a6955 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -488,6 +488,7 @@ private void initLayout() { taskExecutor, dialogService, Globals.journalAbbreviationRepository, + Globals.predatoryJournalRepository, entryTypesManager, undoManager, Globals.getClipboardManager()); diff --git a/src/main/java/org/jabref/gui/MainMenu.java b/src/main/java/org/jabref/gui/MainMenu.java index 8ac907cf6ed..589c740f448 100644 --- a/src/main/java/org/jabref/gui/MainMenu.java +++ b/src/main/java/org/jabref/gui/MainMenu.java @@ -65,6 +65,7 @@ import org.jabref.logic.importer.IdFetcher; import org.jabref.logic.importer.WebFetchers; import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.logic.journals.predatory.PredatoryJournalRepository; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.OS; import org.jabref.model.entry.BibEntryTypesManager; @@ -82,6 +83,7 @@ public class MainMenu extends MenuBar { private final TaskExecutor taskExecutor; private final DialogService dialogService; private final JournalAbbreviationRepository abbreviationRepository; + private final PredatoryJournalRepository predatoryJournalRepository; private final BibEntryTypesManager entryTypesManager; private final UndoManager undoManager; private final ClipBoardManager clipBoardManager; @@ -95,6 +97,7 @@ public MainMenu(JabRefFrame frame, TaskExecutor taskExecutor, DialogService dialogService, JournalAbbreviationRepository abbreviationRepository, + PredatoryJournalRepository predatoryJournalRepository, BibEntryTypesManager entryTypesManager, UndoManager undoManager, ClipBoardManager clipBoardManager) { @@ -107,6 +110,7 @@ public MainMenu(JabRefFrame frame, this.taskExecutor = taskExecutor; this.dialogService = dialogService; this.abbreviationRepository = abbreviationRepository; + this.predatoryJournalRepository = predatoryJournalRepository; this.entryTypesManager = entryTypesManager; this.undoManager = undoManager; this.clipBoardManager = clipBoardManager; @@ -224,7 +228,7 @@ private void createMenu() { quality.getItems().addAll( factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(frame, dialogService, stateManager, preferencesService, entryTypesManager, taskExecutor)), factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(dialogService, stateManager, preferencesService)), - factory.createMenuItem(StandardActions.CHECK_INTEGRITY, new IntegrityCheckAction(frame, preferencesService, dialogService, stateManager, taskExecutor, abbreviationRepository)), + factory.createMenuItem(StandardActions.CHECK_INTEGRITY, new IntegrityCheckAction(frame, preferencesService, dialogService, stateManager, taskExecutor, abbreviationRepository, predatoryJournalRepository)), factory.createMenuItem(StandardActions.CLEANUP_ENTRIES, new CleanupAction(frame, preferencesService, dialogService, stateManager, taskExecutor)), new SeparatorMenuItem(), diff --git a/src/main/java/org/jabref/gui/integrity/IntegrityCheckAction.java b/src/main/java/org/jabref/gui/integrity/IntegrityCheckAction.java index 5e6e2df5a37..0acf35de17a 100644 --- a/src/main/java/org/jabref/gui/integrity/IntegrityCheckAction.java +++ b/src/main/java/org/jabref/gui/integrity/IntegrityCheckAction.java @@ -14,6 +14,7 @@ import org.jabref.logic.integrity.IntegrityCheck; import org.jabref.logic.integrity.IntegrityMessage; import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.logic.journals.predatory.PredatoryJournalRepository; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; @@ -29,19 +30,22 @@ public class IntegrityCheckAction extends SimpleCommand { private final PreferencesService preferencesService; private final StateManager stateManager; private final JournalAbbreviationRepository abbreviationRepository; + private final PredatoryJournalRepository predatoryJournalRepository; public IntegrityCheckAction(JabRefFrame frame, PreferencesService preferencesService, DialogService dialogService, StateManager stateManager, TaskExecutor taskExecutor, - JournalAbbreviationRepository abbreviationRepository) { + JournalAbbreviationRepository abbreviationRepository, + PredatoryJournalRepository predatoryJournalRepository) { this.frame = frame; this.stateManager = stateManager; this.taskExecutor = taskExecutor; this.preferencesService = preferencesService; this.dialogService = dialogService; this.abbreviationRepository = abbreviationRepository; + this.predatoryJournalRepository = predatoryJournalRepository; this.executable.bind(needsDatabase(this.stateManager)); } @@ -53,6 +57,7 @@ public void execute() { preferencesService.getFilePreferences(), preferencesService.getCitationKeyPatternPreferences(), abbreviationRepository, + predatoryJournalRepository, preferencesService.getEntryEditorPreferences().shouldAllowIntegerEditionBibtex()); Task> task = new Task<>() { diff --git a/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java b/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java index 1a4207c98c1..bcf87382a55 100644 --- a/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java +++ b/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java @@ -6,6 +6,7 @@ import org.jabref.logic.citationkeypattern.CitationKeyPatternPreferences; import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.logic.journals.predatory.PredatoryJournalRepository; import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; @@ -22,6 +23,7 @@ public IntegrityCheck(BibDatabaseContext bibDatabaseContext, FilePreferences filePreferences, CitationKeyPatternPreferences citationKeyPatternPreferences, JournalAbbreviationRepository journalAbbreviationRepository, + PredatoryJournalRepository predatoryJournalRepository, boolean allowIntegerEdition) { this.bibDatabaseContext = bibDatabaseContext; @@ -40,7 +42,9 @@ public IntegrityCheck(BibDatabaseContext bibDatabaseContext, new CitationKeyDuplicationChecker(bibDatabaseContext.getDatabase()), new AmpersandChecker(), new LatexIntegrityChecker(), - new JournalInAbbreviationListChecker(StandardField.JOURNAL, journalAbbreviationRepository) + new JournalInAbbreviationListChecker(StandardField.JOURNAL, journalAbbreviationRepository), + new PredatoryJournalChecker(predatoryJournalRepository, + List.of(StandardField.JOURNAL, StandardField.PUBLISHER, StandardField.BOOKTITLE)) )); if (bibDatabaseContext.isBiblatexMode()) { entryCheckers.add(new UTF8Checker(bibDatabaseContext.getMetaData().getEncoding().orElse(StandardCharsets.UTF_8))); diff --git a/src/main/java/org/jabref/logic/integrity/PredatoryJournalChecker.java b/src/main/java/org/jabref/logic/integrity/PredatoryJournalChecker.java new file mode 100644 index 00000000000..eb4f7030ced --- /dev/null +++ b/src/main/java/org/jabref/logic/integrity/PredatoryJournalChecker.java @@ -0,0 +1,29 @@ +package org.jabref.logic.integrity; + +import java.util.List; +import java.util.Objects; + +import org.jabref.logic.journals.predatory.PredatoryJournalRepository; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.Field; + +public class PredatoryJournalChecker implements EntryChecker { + + private final PredatoryJournalRepository predatoryJournalRepository; + private final List fieldNames; + + public PredatoryJournalChecker(PredatoryJournalRepository predatoryJournalRepository, List fieldsToCheck) { + this.predatoryJournalRepository = Objects.requireNonNull(predatoryJournalRepository); + this.fieldNames = fieldsToCheck; + } + + @Override + public List check(BibEntry entry) { + return entry.getFieldMap().entrySet().stream() + .filter(field -> fieldNames.contains(field.getKey())) + .filter(field -> predatoryJournalRepository.isKnownName(field.getValue())) + .map(field -> new IntegrityMessage(Localization.lang("Predatory journal %0 found", field.getValue()), entry, field.getKey())) + .toList(); + } +} diff --git a/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalInformation.java b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalInformation.java new file mode 100644 index 00000000000..83c936f8748 --- /dev/null +++ b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalInformation.java @@ -0,0 +1,16 @@ +package org.jabref.logic.journals.predatory; + +import java.io.Serializable; + +/** + * Represents predatory journal information + * + * @param name The full journal name + * @param abbr Abbreviation, if any + * @param url Url of the journal + */ +public record PredatoryJournalInformation( + String name, + String abbr, + String url) implements Serializable { // must implement @Serializable otherwise MVStore fails +} diff --git a/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalListCrawler.java b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalListCrawler.java new file mode 100644 index 00000000000..dfedda4eec5 --- /dev/null +++ b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalListCrawler.java @@ -0,0 +1,160 @@ +package org.jabref.logic.journals.predatory; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.jabref.logic.net.URLDownload; +import org.jabref.model.strings.StringUtil; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Converts (hard-coded) online resources to a set. {@link #loadFromOnlineSources} is the method containing the result. + */ +public class PredatoryJournalListCrawler { + + private record PJSource(URL url, Optional elementPattern) { + PJSource(String url, String regex) { + this(createURL(url), Optional.of(Pattern.compile(regex))); + } + + PJSource(String url) { + this(createURL(url), Optional.empty()); + } + + private static URL createURL(String urlString) { + try { + return new URI(urlString).toURL(); + } catch (MalformedURLException | URISyntaxException ex) { + throw new IllegalArgumentException("Malformed URL has occurred in PJSource", ex); + } + } + } + + private static final Logger LOGGER = LoggerFactory.getLogger(PredatoryJournalListCrawler.class); + private static final Pattern PATTERN_NAME = Pattern.compile("(?<=\">).*?(?=<)"); + private static final Pattern PATTERN_URL = Pattern.compile("http.*?(?=\")"); + private static final Pattern PATTERN_ABBR = Pattern.compile("(?<=\\()[^ ]*(?=\\))"); + private final List predatorySources = List.of( + new PJSource("https://raw.githubusercontent.com/stop-predatory-journals/stop-predatory-journals.github.io/master/_data/journals.csv"), + new PJSource("https://raw.githubusercontent.com/stop-predatory-journals/stop-predatory-journals.github.io/master/_data/publishers.csv"), + new PJSource("https://beallslist.net/", + "
  • .*?
  • "), + new PJSource("https://beallslist.net/standalone-journals/", + "
  • .*?
  • "), + new PJSource("https://beallslist.net/hijacked-journals/", + ".*?") + ); + + private final List linkElements = new ArrayList<>(); + + private final List predatoryJournalInformation = new ArrayList<>(); + + /** + * Loads predatory journal information from online resources + * This method should be only called once when building JabRef + * + * @return the set of journal information + */ + public HashSet loadFromOnlineSources() { + predatorySources.forEach(this::crawl); + linkElements.forEach(this::clean); + return new HashSet<>(predatoryJournalInformation); + } + + private void crawl(PJSource source) { + try { + URLDownload download = new URLDownload(source.url); + + if (!download.canBeReached()) { + LOGGER.warn("Url {} is unreachable", source.url); + } else if (source.url.getPath().contains(".csv")) { + handleCSV(new InputStreamReader(download.asInputStream())); + } else { + if (source.elementPattern.isPresent()) { + handleHTML(source.elementPattern.get(), download.asString()); + } + } + } catch (IOException ex) { + LOGGER.error("Could not crawl source for predatory journals {}", source.url, ex); + } + } + + private void handleCSV(Reader reader) throws IOException { + CSVFormat format = CSVFormat.EXCEL.builder().setSkipHeaderRecord(true).build(); + CSVParser csvParser = new CSVParser(reader, format); + + for (CSVRecord csvRecord : csvParser) { + String name = csvRecord.get(1); + String abbr = csvRecord.get(2); + String url = csvRecord.get(0); + + if (StringUtil.isNullOrEmpty(name)) { + if (!abbr.isEmpty()) { + name = abbr; + } else { + continue; + } + } + // changes column order from CSV (source: url, name, abbr) + predatoryJournalInformation.add(new PredatoryJournalInformation(decode(name), decode(abbr), url)); + } + } + + private void handleHTML(Pattern pattern, String body) { + Matcher matcher = pattern.matcher(body); + while (matcher.find()) { + linkElements.add(matcher.group()); + } + } + + private void clean(String item) { + Matcher m_name = PATTERN_NAME.matcher(item); + Matcher m_url = PATTERN_URL.matcher(item); + Matcher m_abbr = PATTERN_ABBR.matcher(item); + + // using `if` gets only first link in element, `while` gets all, but this may not be desirable + // e.g. this way only the hijacked journals are recorded and not the authentic originals + if (m_name.find() && m_url.find()) { + String name = m_name.group(); + if (name != null) { + name = name.replace("\u200B", ""); // zero width space + } + String abbr = m_abbr.find() ? m_abbr.group() : ""; + String url = m_url.group(); + + if (StringUtil.isNullOrEmpty(name)) { + if (!abbr.isEmpty()) { + name = abbr; + } else { + return; + } + } + predatoryJournalInformation.add(new PredatoryJournalInformation(decode(name), decode(abbr), url)); + } + } + + private String decode(String s) { + return Optional.ofNullable(s) + .orElse("") + .replace(",", "") + .replace("&", "&") + .replace("’", "'") + .replace("–", "-"); + } +} diff --git a/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalListLoader.java b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalListLoader.java new file mode 100644 index 00000000000..2b03f9dcb36 --- /dev/null +++ b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalListLoader.java @@ -0,0 +1,32 @@ +package org.jabref.logic.journals.predatory; + +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PredatoryJournalListLoader { + + private static final Logger LOGGER = LoggerFactory.getLogger(PredatoryJournalListLoader.class); + + public static PredatoryJournalRepository loadRepository() { + PredatoryJournalRepository repository = new PredatoryJournalRepository(); + + Path path; + try { + URL resource = PredatoryJournalRepository.class.getResource("/journals/predatory-journals.mv"); + if (resource == null) { + LOGGER.error("predatoryJournal-list.mv not found. Using demo list."); + return new PredatoryJournalRepository(); + } + path = Path.of(resource.toURI()); + } catch (URISyntaxException e) { + LOGGER.error("Could not determine path to predatoryJournal-list.mv. Using demo list."); + return new PredatoryJournalRepository(); + } + + return new PredatoryJournalRepository(path); + } +} diff --git a/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalRepository.java b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalRepository.java new file mode 100644 index 00000000000..64b930026d2 --- /dev/null +++ b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalRepository.java @@ -0,0 +1,61 @@ +package org.jabref.logic.journals.predatory; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.stream.Collectors; + +import org.jabref.logic.util.strings.StringSimilarity; + +import org.h2.mvstore.MVMap; +import org.h2.mvstore.MVStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A repository for all predatory journals and publishers, including add and find methods. + */ +public class PredatoryJournalRepository { + private final Logger LOGGER = LoggerFactory.getLogger(PredatoryJournalRepository.class); + private final Map predatoryJournals = new HashMap<>(); + private final StringSimilarity match = new StringSimilarity(); + + /** + * Initializes the internal data based on the predatory journals found in the given MV file + */ + public PredatoryJournalRepository(Path mvStore) { + MVMap predatoryJournalsMap; + try (MVStore store = new MVStore.Builder().readOnly().fileName(mvStore.toAbsolutePath().toString()).open()) { + predatoryJournalsMap = store.openMap("PredatoryJournals"); + predatoryJournals.putAll(predatoryJournalsMap); + } + } + + /** + * Initializes the repository with demonstration data. Used if no abbreviation file is found. + */ + public PredatoryJournalRepository() { + predatoryJournals.put("Demo", new PredatoryJournalInformation("Demo", "Demo", "")); + } + + /** + * Returns true if the given journal name is contained in the list in its full form + */ + public boolean isKnownName(String journalName) { + String journal = journalName.trim().replaceAll(Matcher.quoteReplacement("\\&"), "&"); + + if (predatoryJournals.containsKey(journal)) { + LOGGER.debug("Found predatory journal {}", journal); + return true; + } + + var matches = predatoryJournals.keySet().stream() + .filter(key -> match.isSimilar(journal.toLowerCase(Locale.ROOT), key.toLowerCase(Locale.ROOT))) + .collect(Collectors.toList()); + + LOGGER.info("Found multiple possible predatory journals {}", String.join(", ", matches)); + return !matches.isEmpty(); + } +} diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 7ea2dceb5e7..ea1e11f80ae 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2621,6 +2621,7 @@ Would\ you\ like\ to\ enable\ fetching\ of\ journal\ information?\ This\ can\ be Enable=Enable Keep\ disabled=Keep disabled +Predatory\ journal\ %0\ found=Predatory journal %0 found Hide\ user\ comments=Hide user comments Show\ user\ comments\ field=Show user comments field diff --git a/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java b/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java index 5a589970aba..21b2d0b2274 100644 --- a/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java +++ b/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java @@ -13,6 +13,7 @@ import org.jabref.logic.citationkeypattern.CitationKeyPatternPreferences; import org.jabref.logic.citationkeypattern.GlobalCitationKeyPattern; import org.jabref.logic.journals.JournalAbbreviationLoader; +import org.jabref.logic.journals.predatory.PredatoryJournalListLoader; import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.BibDatabaseMode; @@ -139,7 +140,8 @@ void testEntryIsUnchangedAfterChecks() { new IntegrityCheck(context, mock(FilePreferences.class), createCitationKeyPatternPreferences(), - JournalAbbreviationLoader.loadBuiltInRepository(), false) + JournalAbbreviationLoader.loadBuiltInRepository(), + PredatoryJournalListLoader.loadRepository(), false) .check(); assertEquals(clonedEntry, entry); @@ -171,7 +173,8 @@ private void assertWrong(BibDatabaseContext context) { List messages = new IntegrityCheck(context, mock(FilePreferences.class), createCitationKeyPatternPreferences(), - JournalAbbreviationLoader.loadBuiltInRepository(), false) + JournalAbbreviationLoader.loadBuiltInRepository(), + PredatoryJournalListLoader.loadRepository(), false) .check(); assertNotEquals(Collections.emptyList(), messages); } @@ -182,8 +185,9 @@ private void assertCorrect(BibDatabaseContext context) { List messages = new IntegrityCheck(context, filePreferencesMock, createCitationKeyPatternPreferences(), - JournalAbbreviationLoader.loadBuiltInRepository(), false - ).check(); + JournalAbbreviationLoader.loadBuiltInRepository(), + PredatoryJournalListLoader.loadRepository(), false) + .check(); assertEquals(Collections.emptyList(), messages); } diff --git a/src/test/java/org/jabref/logic/integrity/PredatoryJournalCheckerTest.java b/src/test/java/org/jabref/logic/integrity/PredatoryJournalCheckerTest.java new file mode 100644 index 00000000000..b56d997c252 --- /dev/null +++ b/src/test/java/org/jabref/logic/integrity/PredatoryJournalCheckerTest.java @@ -0,0 +1,70 @@ +package org.jabref.logic.integrity; + +import java.util.Collections; +import java.util.List; + +import org.jabref.logic.journals.predatory.PredatoryJournalListLoader; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PredatoryJournalCheckerTest { + + static PredatoryJournalChecker checker; + + @BeforeAll + static void initChecker() { + checker = new PredatoryJournalChecker(PredatoryJournalListLoader.loadRepository(), + List.of(StandardField.JOURNAL, StandardField.PUBLISHER, StandardField.BOOKTITLE)); + } + + @Test + void journalIsNotPredatory() { + BibEntry entry = new BibEntry().withField(StandardField.JOURNAL, "IEEE Software"); + assertEquals(Collections.emptyList(), checker.check(entry)); + } + + @Test + void journalIsPredatory() { + String journalName = "European International Journal of Science and Technology"; + BibEntry entry = new BibEntry().withField(StandardField.JOURNAL, journalName); + assertEquals(List.of(new IntegrityMessage("Predatory journal %s found".formatted(journalName), + entry, StandardField.JOURNAL)), checker.check(entry)); + } + + @Test + void journalIsPredatoryCaseInsensitive() { + String journalName = "european international journal of science and technology"; + BibEntry entry = new BibEntry().withField(StandardField.JOURNAL, journalName); + assertEquals(List.of(new IntegrityMessage("Predatory journal %s found".formatted(journalName), + entry, StandardField.JOURNAL)), checker.check(entry)); + } + + @Test + void journalIsPredatoryExtraCharacters() { + String journalName = "European International Journal, of Science and Technology"; + BibEntry entry = new BibEntry().withField(StandardField.JOURNAL, journalName); + assertEquals(List.of(new IntegrityMessage("Predatory journal %s found".formatted(journalName), + entry, StandardField.JOURNAL)), checker.check(entry)); + } + + @Test + void publisherIsPredatory() { + String publisherName = "Academia Scholarly Journals"; + BibEntry entry = new BibEntry().withField(StandardField.PUBLISHER, publisherName); + assertEquals(List.of(new IntegrityMessage("Predatory journal %s found".formatted(publisherName), + entry, StandardField.PUBLISHER)), checker.check(entry)); + } + + @Test + void bookTitleIsPredatory() { + String bookTitle = "Biosciences International"; + BibEntry entry = new BibEntry().withField(StandardField.BOOKTITLE, bookTitle); + assertEquals(List.of(new IntegrityMessage("Predatory journal %s found".formatted(bookTitle), + entry, StandardField.BOOKTITLE)), checker.check(entry)); + } +} From 2a7fc1aa0e2bc3edad3291e96a40087cded4da03 Mon Sep 17 00:00:00 2001 From: Christoph Date: Wed, 6 Dec 2023 22:13:25 +0100 Subject: [PATCH 054/144] Fix mathscinet fetcher by id (#10691) Fixes #10499 --- .../logic/importer/fetcher/MathSciNet.java | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java b/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java index e1b795c1062..c5c39d8136e 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java @@ -31,9 +31,9 @@ import org.jabref.model.entry.field.UnknownField; import org.jabref.model.util.DummyFileUpdateMonitor; +import kong.unirest.JsonNode; import kong.unirest.json.JSONArray; import kong.unirest.json.JSONException; -import kong.unirest.json.JSONObject; import org.apache.http.client.utils.URIBuilder; import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; import org.jbibtex.TokenMgrException; @@ -82,22 +82,18 @@ public URL getURLForEntry(BibEntry entry) throws URISyntaxException, MalformedUR @Override public URL getURLForQuery(QueryNode luceneQuery) throws URISyntaxException, MalformedURLException, FetcherException { - URIBuilder uriBuilder = new URIBuilder("https://mathscinet.ams.org/mathscinet/publications-search"); - uriBuilder.addParameter("pg7", "ALLF"); // search all fields - uriBuilder.addParameter("s7", new DefaultQueryTransformer().transformLuceneQuery(luceneQuery).orElse("")); // query - uriBuilder.addParameter("r", "1"); // start index - uriBuilder.addParameter("extend", "1"); // should return up to 100 items (instead of default 10) - uriBuilder.addParameter("fmt", "bibtex"); // BibTeX format - + URIBuilder uriBuilder = new URIBuilder("https://mathscinet.ams.org/mathscinet/api/publications/search"); + uriBuilder.addParameter("query", new DefaultQueryTransformer().transformLuceneQuery(luceneQuery).orElse("")); // query + uriBuilder.addParameter("currentPage", "1"); // start index + uriBuilder.addParameter("pageSize", "100"); // page size return uriBuilder.build().toURL(); } @Override public URL getUrlForIdentifier(String identifier) throws URISyntaxException, MalformedURLException, FetcherException { - URIBuilder uriBuilder = new URIBuilder("https://mathscinet.ams.org/mathscinet/publications-search"); - uriBuilder.addParameter("pg1", "MR"); // search MR number - uriBuilder.addParameter("s1", identifier); // identifier - uriBuilder.addParameter("fmt", "bibtex"); // BibTeX format + URIBuilder uriBuilder = new URIBuilder("https://mathscinet.ams.org/mathscinet/api/publications/format"); + uriBuilder.addParameter("formats", "bib"); + uriBuilder.addParameter("ids", identifier); // identifier return uriBuilder.build().toURL(); } @@ -106,17 +102,25 @@ public URL getUrlForIdentifier(String identifier) throws URISyntaxException, Mal public Parser getParser() { return inputStream -> { String response = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining(OS.NEWLINE)); + BibtexParser bibtexParser = new BibtexParser(preferences, new DummyFileUpdateMonitor()); List entries = new ArrayList<>(); try { - JSONObject jsonResponse = new JSONObject(response); - JSONArray entriesArray = jsonResponse.getJSONObject("all").getJSONArray("results"); - - BibtexParser bibtexParser = new BibtexParser(preferences, new DummyFileUpdateMonitor()); - - for (int i = 0; i < entriesArray.length(); i++) { - String bibTexFormat = entriesArray.getJSONObject(i).getString("bibTexFormat"); - entries.addAll(bibtexParser.parseEntries(bibTexFormat)); + // Depending on the type of query we might get either a json object or directly a json array + JsonNode node = new JsonNode(response); + if (node.isArray()) { + JSONArray entriesArray = node.getArray(); + for (int i = 0; i < entriesArray.length(); i++) { + String bibTexFormat = entriesArray.getJSONObject(i).getString("bib"); + entries.addAll(bibtexParser.parseEntries(bibTexFormat)); + } + } else { + var element = node.getObject(); + JSONArray entriesArray = element.getJSONObject("all").getJSONArray("results"); + for (int i = 0; i < entriesArray.length(); i++) { + String bibTexFormat = entriesArray.getJSONObject(i).getString("bibTexFormat"); + entries.addAll(bibtexParser.parseEntries(bibTexFormat)); + } } } catch (JSONException | TokenMgrException e) { LOGGER.error("An error occurred while parsing fetched data", e); From 43a4393de955fb7debf0d64dd0f29720b6baeeb5 Mon Sep 17 00:00:00 2001 From: Christoph Date: Wed, 6 Dec 2023 22:13:43 +0100 Subject: [PATCH 055/144] Fix predatory journal loading resoures (#10693) * Fix resource does not exist * Fix journal loading and resource opening * Use MVStore directly (an experiment) * Add comments --------- Co-authored-by: Oliver Kopp --- .../java/org/jabref/gui/MainApplication.java | 10 +++ .../predatory/PredatoryJournalListLoader.java | 35 ++++++---- .../predatory/PredatoryJournalRepository.java | 22 +++--- .../logic/integrity/IntegrityCheckTest.java | 68 +++++++++++-------- .../PredatoryJournalCheckerTest.java | 10 ++- 5 files changed, 91 insertions(+), 54 deletions(-) diff --git a/src/main/java/org/jabref/gui/MainApplication.java b/src/main/java/org/jabref/gui/MainApplication.java index a187843977d..76a33eb6f24 100644 --- a/src/main/java/org/jabref/gui/MainApplication.java +++ b/src/main/java/org/jabref/gui/MainApplication.java @@ -10,10 +10,15 @@ import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.JabRefPreferences; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * JabRef's main class to process command line options and to start the UI */ public class MainApplication extends Application { + private static final Logger LOGGER = LoggerFactory.getLogger(MainApplication.class); + private static List parserResults; private static boolean isBlank; private static JabRefPreferences preferences; @@ -43,5 +48,10 @@ public void stop() { OOBibBaseConnect.closeOfficeConnection(); Globals.stopBackgroundTasks(); Globals.shutdownThreadPools(); + try { + Globals.predatoryJournalRepository.close(); + } catch (Exception e) { + LOGGER.warn("Cloud not shut down predatoryJournalRepository", e); + } } } diff --git a/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalListLoader.java b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalListLoader.java index 2b03f9dcb36..011f6e38fd8 100644 --- a/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalListLoader.java +++ b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalListLoader.java @@ -1,7 +1,8 @@ package org.jabref.logic.journals.predatory; -import java.net.URISyntaxException; -import java.net.URL; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; import java.nio.file.Path; import org.slf4j.Logger; @@ -13,20 +14,24 @@ public class PredatoryJournalListLoader { public static PredatoryJournalRepository loadRepository() { PredatoryJournalRepository repository = new PredatoryJournalRepository(); - - Path path; - try { - URL resource = PredatoryJournalRepository.class.getResource("/journals/predatory-journals.mv"); - if (resource == null) { - LOGGER.error("predatoryJournal-list.mv not found. Using demo list."); - return new PredatoryJournalRepository(); + // Initialize with built-in list + // We cannot use PredatoryJournalRepository.class.getResource, because this is null in JPackage, thus we need "getResourceAsStream" + try (InputStream resourceAsStream = PredatoryJournalListLoader.class.getResourceAsStream("/journals/predatory-journals.mv")) { + if (resourceAsStream == null) { + LOGGER.warn("There is no predatory-journal.mv. We use a default predatory dummy list"); + repository = new PredatoryJournalRepository(); + } else { + // MVStore does not support loading from stream. Thus, we need to have a file copy of the stream. + Path tempDir = Files.createTempDirectory("jabref-journal"); + Path tempJournalList = tempDir.resolve("predatory-journals.mv"); + Files.copy(resourceAsStream, tempJournalList); + repository = new PredatoryJournalRepository(tempJournalList); + tempDir.toFile().deleteOnExit(); + tempJournalList.toFile().deleteOnExit(); } - path = Path.of(resource.toURI()); - } catch (URISyntaxException e) { - LOGGER.error("Could not determine path to predatoryJournal-list.mv. Using demo list."); - return new PredatoryJournalRepository(); + } catch (IOException e) { + LOGGER.error("Error while copying journal list", e); } - - return new PredatoryJournalRepository(path); + return repository; } } diff --git a/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalRepository.java b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalRepository.java index 64b930026d2..2c73530daff 100644 --- a/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalRepository.java +++ b/src/main/java/org/jabref/logic/journals/predatory/PredatoryJournalRepository.java @@ -1,7 +1,6 @@ package org.jabref.logic.journals.predatory; import java.nio.file.Path; -import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; @@ -17,27 +16,27 @@ /** * A repository for all predatory journals and publishers, including add and find methods. */ -public class PredatoryJournalRepository { +public class PredatoryJournalRepository implements AutoCloseable { private final Logger LOGGER = LoggerFactory.getLogger(PredatoryJournalRepository.class); - private final Map predatoryJournals = new HashMap<>(); + private final Map predatoryJournals; private final StringSimilarity match = new StringSimilarity(); + private final MVStore store; /** * Initializes the internal data based on the predatory journals found in the given MV file */ public PredatoryJournalRepository(Path mvStore) { MVMap predatoryJournalsMap; - try (MVStore store = new MVStore.Builder().readOnly().fileName(mvStore.toAbsolutePath().toString()).open()) { - predatoryJournalsMap = store.openMap("PredatoryJournals"); - predatoryJournals.putAll(predatoryJournalsMap); - } + store = new MVStore.Builder().readOnly().fileName(mvStore.toAbsolutePath().toString()).open(); + predatoryJournals = store.openMap("PredatoryJournals"); } /** * Initializes the repository with demonstration data. Used if no abbreviation file is found. */ public PredatoryJournalRepository() { - predatoryJournals.put("Demo", new PredatoryJournalInformation("Demo", "Demo", "")); + store = null; + predatoryJournals = Map.of("Demo", new PredatoryJournalInformation("Demo", "Demo", "")); } /** @@ -58,4 +57,11 @@ public boolean isKnownName(String journalName) { LOGGER.info("Found multiple possible predatory journals {}", String.join(", ", matches)); return !matches.isEmpty(); } + + @Override + public void close() throws Exception { + if (store != null) { + store.close(); + } + } } diff --git a/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java b/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java index 21b2d0b2274..daaa371c3f7 100644 --- a/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java +++ b/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java @@ -1,6 +1,5 @@ package org.jabref.logic.integrity; -import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; @@ -14,6 +13,7 @@ import org.jabref.logic.citationkeypattern.GlobalCitationKeyPattern; import org.jabref.logic.journals.JournalAbbreviationLoader; import org.jabref.logic.journals.predatory.PredatoryJournalListLoader; +import org.jabref.logic.journals.predatory.PredatoryJournalRepository; import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.BibDatabaseMode; @@ -47,28 +47,28 @@ class IntegrityCheckTest { @Test - void bibTexAcceptsStandardEntryType() { + void bibTexAcceptsStandardEntryType() throws Exception { assertCorrect(withMode(createContext(StandardField.TITLE, "sometitle", StandardEntryType.Article), BibDatabaseMode.BIBTEX)); } @Test - void bibTexDoesNotAcceptIEEETranEntryType() { + void bibTexDoesNotAcceptIEEETranEntryType() throws Exception { assertWrong(withMode(createContext(StandardField.TITLE, "sometitle", IEEETranEntryType.Patent), BibDatabaseMode.BIBTEX)); } @Test - void bibLaTexAcceptsIEEETranEntryType() { + void bibLaTexAcceptsIEEETranEntryType() throws Exception { assertCorrect((withMode(createContext(StandardField.TITLE, "sometitle", IEEETranEntryType.Patent), BibDatabaseMode.BIBLATEX))); } @Test - void bibLaTexAcceptsStandardEntryType() { + void bibLaTexAcceptsStandardEntryType() throws Exception { assertCorrect(withMode(createContext(StandardField.TITLE, "sometitle", StandardEntryType.Article), BibDatabaseMode.BIBLATEX)); } @ParameterizedTest @MethodSource("provideCorrectFormat") - void authorNameChecksCorrectFormat(String input) { + void authorNameChecksCorrectFormat(String input) throws Exception { for (Field field : FieldFactory.getPersonNameFields()) { assertCorrect(withMode(createContext(field, input), BibDatabaseMode.BIBLATEX)); } @@ -76,7 +76,7 @@ void authorNameChecksCorrectFormat(String input) { @ParameterizedTest @MethodSource("provideIncorrectFormat") - void authorNameChecksIncorrectFormat(String input) { + void authorNameChecksIncorrectFormat(String input) throws Exception { for (Field field : FieldFactory.getPersonNameFields()) { assertWrong(withMode(createContext(field, input), BibDatabaseMode.BIBLATEX)); } @@ -94,7 +94,7 @@ private static Stream provideIncorrectFormat() { } @Test - void testFileChecks() { + void testFileChecks() throws Exception { MetaData metaData = mock(MetaData.class); Mockito.when(metaData.getDefaultFileDirectory()).thenReturn(Optional.of(".")); Mockito.when(metaData.getUserFileDirectory(any(String.class))).thenReturn(Optional.empty()); @@ -107,7 +107,7 @@ void testFileChecks() { } @Test - void fileCheckFindsFilesRelativeToBibFile(@TempDir Path testFolder) throws IOException { + void fileCheckFindsFilesRelativeToBibFile(@TempDir Path testFolder) throws Exception { Path bibFile = testFolder.resolve("lit.bib"); Files.createFile(bibFile); Path pdfFile = testFolder.resolve("file.pdf"); @@ -120,7 +120,7 @@ void fileCheckFindsFilesRelativeToBibFile(@TempDir Path testFolder) throws IOExc } @Test - void testEntryIsUnchangedAfterChecks() { + void testEntryIsUnchangedAfterChecks() throws Exception { BibEntry entry = new BibEntry(); // populate with all known fields @@ -137,12 +137,14 @@ void testEntryIsUnchangedAfterChecks() { bibDatabase.insertEntry(entry); BibDatabaseContext context = new BibDatabaseContext(bibDatabase); - new IntegrityCheck(context, - mock(FilePreferences.class), - createCitationKeyPatternPreferences(), - JournalAbbreviationLoader.loadBuiltInRepository(), - PredatoryJournalListLoader.loadRepository(), false) - .check(); + try (PredatoryJournalRepository predatoryJournalRepository = PredatoryJournalListLoader.loadRepository()) { + new IntegrityCheck(context, + mock(FilePreferences.class), + createCitationKeyPatternPreferences(), + JournalAbbreviationLoader.loadBuiltInRepository(), + predatoryJournalRepository, false) + .check(); + } assertEquals(clonedEntry, entry); } @@ -169,25 +171,31 @@ private BibDatabaseContext createContext(Field field, String value) { return createContext(field, value, metaData); } - private void assertWrong(BibDatabaseContext context) { - List messages = new IntegrityCheck(context, - mock(FilePreferences.class), - createCitationKeyPatternPreferences(), - JournalAbbreviationLoader.loadBuiltInRepository(), - PredatoryJournalListLoader.loadRepository(), false) - .check(); + private void assertWrong(BibDatabaseContext context) throws Exception { + List messages; + try (PredatoryJournalRepository predatoryJournalRepository = PredatoryJournalListLoader.loadRepository()) { + messages = new IntegrityCheck(context, + mock(FilePreferences.class), + createCitationKeyPatternPreferences(), + JournalAbbreviationLoader.loadBuiltInRepository(), + predatoryJournalRepository, false) + .check(); + } assertNotEquals(Collections.emptyList(), messages); } - private void assertCorrect(BibDatabaseContext context) { + private void assertCorrect(BibDatabaseContext context) throws Exception { FilePreferences filePreferencesMock = mock(FilePreferences.class); when(filePreferencesMock.shouldStoreFilesRelativeToBibFile()).thenReturn(true); - List messages = new IntegrityCheck(context, - filePreferencesMock, - createCitationKeyPatternPreferences(), - JournalAbbreviationLoader.loadBuiltInRepository(), - PredatoryJournalListLoader.loadRepository(), false) - .check(); + List messages; + try (PredatoryJournalRepository predatoryJournalRepository = PredatoryJournalListLoader.loadRepository()) { + messages = new IntegrityCheck(context, + filePreferencesMock, + createCitationKeyPatternPreferences(), + JournalAbbreviationLoader.loadBuiltInRepository(), + predatoryJournalRepository, false) + .check(); + } assertEquals(Collections.emptyList(), messages); } diff --git a/src/test/java/org/jabref/logic/integrity/PredatoryJournalCheckerTest.java b/src/test/java/org/jabref/logic/integrity/PredatoryJournalCheckerTest.java index b56d997c252..74f9caa093f 100644 --- a/src/test/java/org/jabref/logic/integrity/PredatoryJournalCheckerTest.java +++ b/src/test/java/org/jabref/logic/integrity/PredatoryJournalCheckerTest.java @@ -4,9 +4,11 @@ import java.util.List; import org.jabref.logic.journals.predatory.PredatoryJournalListLoader; +import org.jabref.logic.journals.predatory.PredatoryJournalRepository; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -15,13 +17,19 @@ class PredatoryJournalCheckerTest { static PredatoryJournalChecker checker; + static PredatoryJournalRepository predatoryJournalRepository = PredatoryJournalListLoader.loadRepository(); @BeforeAll static void initChecker() { - checker = new PredatoryJournalChecker(PredatoryJournalListLoader.loadRepository(), + checker = new PredatoryJournalChecker(predatoryJournalRepository, List.of(StandardField.JOURNAL, StandardField.PUBLISHER, StandardField.BOOKTITLE)); } + @AfterAll + static void close() throws Exception { + predatoryJournalRepository.close(); + } + @Test void journalIsNotPredatory() { BibEntry entry = new BibEntry().withField(StandardField.JOURNAL, "IEEE Software"); From 05024d3e9e360c9f12deed5a102e9ab24008edb2 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Thu, 7 Dec 2023 11:27:34 +0000 Subject: [PATCH 056/144] EditExternalFileTypeEntryDialog form OK button disabled if empty extension field --- .../EditExternalFileTypeEntryDialog.java | 5 +++ .../EditExternalFileTypeViewModel.java | 35 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java index d1008ebfeaa..b95745fad38 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java @@ -45,6 +45,11 @@ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, Strin .load() .setAsDialogPane(this); + getDialogPane().getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL); + + final Button confirmDialogButton = (Button) getDialogPane().lookupButton(ButtonType.OK); + confirmDialogButton.disableProperty().bind(viewModel.validationStatus().validProperty().not()); + this.setResultConverter(button -> { if (button == ButtonType.OK) { viewModel.storeSettings(); diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java index 7a095e76b94..3f07c33e531 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java @@ -6,9 +6,17 @@ import javafx.beans.property.StringProperty; import javafx.scene.Node; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.strings.StringUtil; + +import de.saxsys.mvvmfx.utils.validation.CompositeValidator; +import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator; +import de.saxsys.mvvmfx.utils.validation.ValidationMessage; +import de.saxsys.mvvmfx.utils.validation.ValidationStatus; +import de.saxsys.mvvmfx.utils.validation.Validator; + public class EditExternalFileTypeViewModel { private final ExternalFileTypeItemViewModel fileTypeViewModel; - private final StringProperty nameProperty = new SimpleStringProperty(""); private final StringProperty mimeTypeProperty = new SimpleStringProperty(""); private final StringProperty extensionProperty = new SimpleStringProperty(""); @@ -16,6 +24,11 @@ public class EditExternalFileTypeViewModel { private final BooleanProperty defaultApplicationSelectedProperty = new SimpleBooleanProperty(false); private final BooleanProperty customApplicationSelectedProperty = new SimpleBooleanProperty(false); + + private Validator extensionValidator; + private Validator sameExtensionValidator; + private CompositeValidator validator; + public EditExternalFileTypeViewModel(ExternalFileTypeItemViewModel fileTypeViewModel) { this.fileTypeViewModel = fileTypeViewModel; @@ -29,6 +42,22 @@ public EditExternalFileTypeViewModel(ExternalFileTypeItemViewModel fileTypeViewM customApplicationSelectedProperty.setValue(true); selectedApplicationProperty.setValue(fileTypeViewModel.applicationProperty().getValue()); } + + setupValidation(); + } + + private void setupValidation() { + validator = new CompositeValidator(); + extensionValidator = new FunctionBasedValidator<>( + extensionProperty, + StringUtil::isNotBlank, + ValidationMessage.error(Localization.lang("Please enter a name for the extension."))); + + validator.addValidators(extensionValidator); + } + + public ValidationStatus validationStatus() { + return validator.getValidationStatus(); } public Node getIcon() { @@ -59,6 +88,10 @@ public BooleanProperty customApplicationSelectedProperty() { return customApplicationSelectedProperty; } + public BooleanProperty validExtensionTypeProperty() { + return defaultApplicationSelectedProperty; + } + public void storeSettings() { fileTypeViewModel.nameProperty().setValue(nameProperty.getValue().trim()); fileTypeViewModel.mimetypeProperty().setValue(mimeTypeProperty.getValue().trim()); From 43817fd2c51a2e3fb03beca43be8d65e6f1e66b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o?= <127297775+theoo33@users.noreply.github.com> Date: Sun, 10 Dec 2023 14:06:38 +0100 Subject: [PATCH 057/144] Sort languages alphabetically in the preferences #10660 (#10675) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * classic sort, only for latin alphabet * sort all languages alphabetically * sort all languages alphabetically without normalized * Check style modification * Update CHANGELOG.md * classic sort, only for latin alphabet * sort all languages alphabetically * sort all languages alphabetically without normalized * Check style modification * Update CHANGELOG.md * Update CHANGELOG.md * use list * delete comment * use pattern * fix * pattern var * remove duplicated sorted --------- Co-authored-by: Théo Granier Co-authored-by: j23sanch Co-authored-by: Siedlerchr --- CHANGELOG.md | 2 +- .../preferences/general/GeneralTabViewModel.java | 2 +- .../java/org/jabref/logic/l10n/Language.java | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8b3ec8c5b3..0cc588d5f64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where the added protected term has unwanted leading and trailing whitespaces, where the formatted text has unwanted empty brackets and where the word at the cursor in the textbox can be added to the list. [#10415](https://github.com/JabRef/jabref/issues/10415) - We fixed an issue where in the merge dialog the file field of entries was not correctly merged when the first and second entry both contained values inside the file field. [#10572](https://github.com/JabRef/jabref/issues/10572) -- We fixed some small inconsistencies in the user interface. [#10507](https://github.com/JabRef/jabref/issues/10507) [#10458](https://github.com/JabRef/jabref/issues/10458) +- We fixed some small inconsistencies in the user interface. [#10507](https://github.com/JabRef/jabref/issues/10507) [#10458](https://github.com/JabRef/jabref/issues/10458) [#10660](https://github.com/JabRef/jabref/issues/10660) - We fixed the issue where the Hayagriva YAML exporter would not include a parent field for the publisher/series. [#10596](https://github.com/JabRef/jabref/issues/10596) ### Removed diff --git a/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java b/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java index d47bb33183f..810bfdc50e3 100644 --- a/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java @@ -57,7 +57,7 @@ public class GeneralTabViewModel implements PreferenceTabViewModel { new SpinnerValueFactory.IntegerSpinnerValueFactory(9, Integer.MAX_VALUE); private final ReadOnlyListProperty languagesListProperty = - new ReadOnlyListWrapper<>(FXCollections.observableArrayList(Language.values())); + new ReadOnlyListWrapper<>(FXCollections.observableArrayList(Language.getSorted())); private final ObjectProperty selectedLanguageProperty = new SimpleObjectProperty<>(); private final ReadOnlyListProperty themesListProperty = diff --git a/src/main/java/org/jabref/logic/l10n/Language.java b/src/main/java/org/jabref/logic/l10n/Language.java index 2805b6b3ee5..9d3e9958fb8 100644 --- a/src/main/java/org/jabref/logic/l10n/Language.java +++ b/src/main/java/org/jabref/logic/l10n/Language.java @@ -1,8 +1,12 @@ package org.jabref.logic.l10n; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.Optional; +import java.util.regex.Pattern; /** * Contains all supported languages. @@ -35,9 +39,9 @@ public enum Language { UKRAINIAN("украї́нська (Ukrainian)", "uk"), VIETNAMESE("Vietnamese", "vi"); + private static final Pattern IS_NOT_LATIN = Pattern.compile("[^\\p{IsLatin}]"); private final String displayName; private final String id; - /** * @param id Typically as 639-1 code */ @@ -70,4 +74,14 @@ public String getDisplayName() { public String getId() { return id; } + + public static List getSorted() { + return Arrays.stream(values()) + .sorted(Comparator.comparing(language -> removeNonLatinCharacters(language.getDisplayName()))) + .toList(); + } + + private static String removeNonLatinCharacters(String input) { + return IS_NOT_LATIN.matcher(input).replaceAll(""); + } } From 424adb71bc7615cbc5edd17268f469580686dbc3 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 11 Dec 2023 00:29:06 +0000 Subject: [PATCH 058/144] EditExternalFileTypeEntryDialog OK button disabled if duplicated file type found --- .../CreateModifyExporterDialogViewModel.java | 2 +- .../EditExternalFileTypeEntryDialog.java | 12 +++++--- .../EditExternalFileTypeViewModel.java | 28 ++++++++++++++----- .../ExternalFileTypesTabViewModel.java | 3 +- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/jabref/gui/exporter/CreateModifyExporterDialogViewModel.java b/src/main/java/org/jabref/gui/exporter/CreateModifyExporterDialogViewModel.java index 78c808dad3c..d9e0c1bc69c 100644 --- a/src/main/java/org/jabref/gui/exporter/CreateModifyExporterDialogViewModel.java +++ b/src/main/java/org/jabref/gui/exporter/CreateModifyExporterDialogViewModel.java @@ -57,7 +57,7 @@ public ExporterViewModel saveExporter() { // Check that there are no empty strings. if (layoutFile.get().isEmpty() || name.get().isEmpty() || extension.get().isEmpty() || !layoutFile.get().endsWith(".layout")) { - LOGGER.info("One of the fields is empty or invalid!"); + LOGGER.info("One of the fields is empty or invalid."); return null; } diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java index b95745fad38..91d66003c35 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java @@ -1,5 +1,6 @@ package org.jabref.gui.preferences.externalfiletypes; +import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.Button; @@ -34,11 +35,15 @@ public class EditExternalFileTypeEntryDialog extends BaseDialog { private final NativeDesktop nativeDesktop = OS.getNativeDesktop(); private final FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder().withInitialDirectory(nativeDesktop.getApplicationDirectory()).build(); private final ExternalFileTypeItemViewModel item; + + private final ObservableList fileTypes; private EditExternalFileTypeViewModel viewModel; - public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, String dialogTitle) { - this.item = item; + + public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, String dialogTitle, ObservableList fileTypes) { + this.item = item; + this.fileTypes = fileTypes; this.setTitle(dialogTitle); ViewLoader.view(this) @@ -49,7 +54,6 @@ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, Strin final Button confirmDialogButton = (Button) getDialogPane().lookupButton(ButtonType.OK); confirmDialogButton.disableProperty().bind(viewModel.validationStatus().validProperty().not()); - this.setResultConverter(button -> { if (button == ButtonType.OK) { viewModel.storeSettings(); @@ -60,7 +64,7 @@ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, Strin @FXML public void initialize() { - viewModel = new EditExternalFileTypeViewModel(item); + viewModel = new EditExternalFileTypeViewModel(item, fileTypes); icon.setGraphic(viewModel.getIcon()); diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java index 3f07c33e531..938cf4b83ba 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java @@ -4,6 +4,7 @@ import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; +import javafx.collections.ObservableList; import javafx.scene.Node; import org.jabref.logic.l10n.Localization; @@ -23,15 +24,16 @@ public class EditExternalFileTypeViewModel { private final StringProperty selectedApplicationProperty = new SimpleStringProperty(""); private final BooleanProperty defaultApplicationSelectedProperty = new SimpleBooleanProperty(false); private final BooleanProperty customApplicationSelectedProperty = new SimpleBooleanProperty(false); - - + private final ObservableList fileTypes; + private final String originalExtension; private Validator extensionValidator; private Validator sameExtensionValidator; private CompositeValidator validator; - public EditExternalFileTypeViewModel(ExternalFileTypeItemViewModel fileTypeViewModel) { + public EditExternalFileTypeViewModel(ExternalFileTypeItemViewModel fileTypeViewModel, ObservableList fileTypes) { this.fileTypeViewModel = fileTypeViewModel; - + this.fileTypes = fileTypes; + this.originalExtension = fileTypeViewModel.extensionProperty().getValue(); extensionProperty.setValue(fileTypeViewModel.extensionProperty().getValue()); nameProperty.setValue(fileTypeViewModel.nameProperty().getValue()); mimeTypeProperty.setValue(fileTypeViewModel.mimetypeProperty().getValue()); @@ -51,9 +53,21 @@ private void setupValidation() { extensionValidator = new FunctionBasedValidator<>( extensionProperty, StringUtil::isNotBlank, - ValidationMessage.error(Localization.lang("Please enter a name for the extension."))); - - validator.addValidators(extensionValidator); + ValidationMessage.error(Localization.lang("Please enter a name for the extension.")) + ); + sameExtensionValidator = new FunctionBasedValidator<>( + extensionProperty, + extension -> { + for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { + if (extension.equalsIgnoreCase(fileTypeItem.extensionProperty().get()) && !extension.equalsIgnoreCase(originalExtension)) { + return false; + } + } + return true; + }, + ValidationMessage.error(Localization.lang("There is already an exists extension with the same name.")) + ); + validator.addValidators(extensionValidator, sameExtensionValidator); } public ValidationStatus validationStatus() { diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index faa4cdc25dd..d84ae74f6dd 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -76,7 +76,7 @@ public ObservableList getFileTypes() { } protected void showEditDialog(ExternalFileTypeItemViewModel item, String dialogTitle) { - dialogService.showCustomDialogAndWait(new EditExternalFileTypeEntryDialog(item, dialogTitle)); + dialogService.showCustomDialogAndWait(new EditExternalFileTypeEntryDialog(item, dialogTitle, fileTypes)); } public boolean edit(ExternalFileTypeItemViewModel type) { @@ -97,7 +97,6 @@ public void remove(ExternalFileTypeItemViewModel type) { } public boolean isValidExternalFileType(ExternalFileTypeItemViewModel item) { - // Check that there are no empty strings. if (item.getName().isEmpty() || item.extensionProperty().get().isEmpty() || item.mimetypeProperty().get().isEmpty()) { LOGGER.info("One of the fields is empty or invalid!"); return false; From 7bc67c402e1ff1b895998a6390c9a793b214460c Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 11 Dec 2023 00:36:16 +0000 Subject: [PATCH 059/144] remove exclamation mark in error message --- .../externalfiletypes/ExternalFileTypesTabViewModel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index d84ae74f6dd..cc835723572 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -98,7 +98,7 @@ public void remove(ExternalFileTypeItemViewModel type) { public boolean isValidExternalFileType(ExternalFileTypeItemViewModel item) { if (item.getName().isEmpty() || item.extensionProperty().get().isEmpty() || item.mimetypeProperty().get().isEmpty()) { - LOGGER.info("One of the fields is empty or invalid!"); + LOGGER.info("One of the fields is empty or invalid."); return false; } @@ -106,7 +106,7 @@ public boolean isValidExternalFileType(ExternalFileTypeItemViewModel item) { String newExt = item.extensionProperty().get(); for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { if (newExt.equalsIgnoreCase(fileTypeItem.extensionProperty().get())) { - LOGGER.info("File Extension exists already!"); + LOGGER.info("File Extension exists already."); return false; } } From ca9eaeb705f2e836a5670d2d0b44fb59cfc1cd7e Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 11 Dec 2023 00:39:09 +0000 Subject: [PATCH 060/144] change message File Extension exists already from info to debug --- .../externalfiletypes/ExternalFileTypesTabViewModel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index cc835723572..3f5faa0c9e9 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -98,7 +98,7 @@ public void remove(ExternalFileTypeItemViewModel type) { public boolean isValidExternalFileType(ExternalFileTypeItemViewModel item) { if (item.getName().isEmpty() || item.extensionProperty().get().isEmpty() || item.mimetypeProperty().get().isEmpty()) { - LOGGER.info("One of the fields is empty or invalid."); + LOGGER.debug("One of the fields is empty or invalid."); return false; } @@ -106,7 +106,7 @@ public boolean isValidExternalFileType(ExternalFileTypeItemViewModel item) { String newExt = item.extensionProperty().get(); for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { if (newExt.equalsIgnoreCase(fileTypeItem.extensionProperty().get())) { - LOGGER.info("File Extension exists already."); + LOGGER.debug("File Extension exists already."); return false; } } From eec134fdaf2c09aff5fe285b253cff4dec974a95 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 11 Dec 2023 00:51:06 +0000 Subject: [PATCH 061/144] refactor ExternalFileTypesTabViewModelTest.java --- ...ExternalFileTypeItemViewModelTestData.java | 33 ---------------- .../ExternalFileTypesTabViewModelTest.java | 39 ++++++++++++++----- 2 files changed, 30 insertions(+), 42 deletions(-) delete mode 100644 src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java deleted file mode 100644 index 3bf8b15349d..00000000000 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModelTestData.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.jabref.gui.preferences.externalfiletypes; -public class ExternalFileTypeItemViewModelTestData { - private ExternalFileTypeItemViewModel externalFileTypeItemViewModel = new ExternalFileTypeItemViewModel(); - - public void setup() { - externalFileTypeItemViewModel.nameProperty().set("Excel 2007"); - externalFileTypeItemViewModel.extensionProperty().set("xlsx"); - externalFileTypeItemViewModel.mimetypeProperty().set("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - externalFileTypeItemViewModel.applicationProperty().set("oocalc"); - } - - public void setupWithoutName() { - externalFileTypeItemViewModel.nameProperty().set(""); - } - - public ExternalFileTypeItemViewModel get() { - return externalFileTypeItemViewModel; - } - - public void clone(ExternalFileTypeItemViewModel updatedModel) { - updatedModel.nameProperty().set(externalFileTypeItemViewModel.getName()); - updatedModel.extensionProperty().set(externalFileTypeItemViewModel.extensionProperty().get()); - updatedModel.mimetypeProperty().set(externalFileTypeItemViewModel.mimetypeProperty().get()); - updatedModel.applicationProperty().set(externalFileTypeItemViewModel.applicationProperty().get()); - } - - public boolean isSameValue(ExternalFileTypeItemViewModel item) { - return !(!item.getName().equals(externalFileTypeItemViewModel.getName()) - || !item.extensionProperty().get().equals(externalFileTypeItemViewModel.extensionProperty().get()) - || !item.mimetypeProperty().get().equals(externalFileTypeItemViewModel.mimetypeProperty().get()) - || !item.applicationProperty().get().equals(externalFileTypeItemViewModel.applicationProperty().get())); - } -} diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java index 76627f52833..f587571b8b0 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java @@ -22,25 +22,46 @@ public class ExternalFileTypesTabViewModelTest { private FilePreferences filePreferences = mock(FilePreferences.class); private DialogService dialogService = mock(DialogService.class); + private ExternalFileTypeItemViewModel externalFileTypeItemViewModel = new ExternalFileTypeItemViewModel(); @Spy private ExternalFileTypesTabViewModel externalFileTypesTabViewModel = spy(new ExternalFileTypesTabViewModel(filePreferences, dialogService)); - private ExternalFileTypeItemViewModelTestData externalFileTypeItemViewModel = new ExternalFileTypeItemViewModelTestData(); @BeforeEach void setUp() { - externalFileTypeItemViewModel.setup(); + externalFileTypeItemViewModel.nameProperty().set("Excel 2007"); + externalFileTypeItemViewModel.extensionProperty().set("xlsx"); + externalFileTypeItemViewModel.mimetypeProperty().set("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + externalFileTypeItemViewModel.applicationProperty().set("oocalc"); + } + + public void setupViewModelWithoutName() { + externalFileTypeItemViewModel.nameProperty().set(""); + } + + public void viewModelClone(ExternalFileTypeItemViewModel updatedModel) { + updatedModel.nameProperty().set(externalFileTypeItemViewModel.getName()); + updatedModel.extensionProperty().set(externalFileTypeItemViewModel.extensionProperty().get()); + updatedModel.mimetypeProperty().set(externalFileTypeItemViewModel.mimetypeProperty().get()); + updatedModel.applicationProperty().set(externalFileTypeItemViewModel.applicationProperty().get()); + } + + public boolean viewModelIsSameValue(ExternalFileTypeItemViewModel item) { + return !(!item.getName().equals(externalFileTypeItemViewModel.getName()) + || !item.extensionProperty().get().equals(externalFileTypeItemViewModel.extensionProperty().get()) + || !item.mimetypeProperty().get().equals(externalFileTypeItemViewModel.mimetypeProperty().get()) + || !item.applicationProperty().get().equals(externalFileTypeItemViewModel.applicationProperty().get())); } @Test public void whenExternalFileTypeItemViewModelWithNonEmptyStringValueThenisValidExternalFileTypeReturnTrue() { - assertTrue(externalFileTypesTabViewModel.isValidExternalFileType(externalFileTypeItemViewModel.get())); + assertTrue(externalFileTypesTabViewModel.isValidExternalFileType(externalFileTypeItemViewModel)); } @Test public void whenExternalFileTypeItemViewModelWithEmptyNameThenisValidExternalFileTypeReturnFalse() { - externalFileTypeItemViewModel.setupWithoutName(); - assertFalse(externalFileTypesTabViewModel.isValidExternalFileType(externalFileTypeItemViewModel.get())); + this.setupViewModelWithoutName(); + assertFalse(externalFileTypesTabViewModel.isValidExternalFileType(externalFileTypeItemViewModel)); } @Test @@ -48,7 +69,7 @@ public void WhenExternalFileTypeItemViewModelIsValidThenAddNewTypeIsSuccessful() ArgumentCaptor itemCaptor = ArgumentCaptor.forClass(ExternalFileTypeItemViewModel.class); doAnswer(mocked -> { ExternalFileTypeItemViewModel capturedItem = itemCaptor.getValue(); - externalFileTypeItemViewModel.clone(capturedItem); + this.viewModelClone(capturedItem); return null; }).when(externalFileTypesTabViewModel).showEditDialog(itemCaptor.capture(), any()); @@ -56,16 +77,16 @@ public void WhenExternalFileTypeItemViewModelIsValidThenAddNewTypeIsSuccessful() ObservableList actualFileTypes = externalFileTypesTabViewModel.getFileTypes(); assertEquals(actualFileTypes.size(), 1); - assertTrue(externalFileTypeItemViewModel.isSameValue(actualFileTypes.getFirst())); + assertTrue(viewModelIsSameValue(actualFileTypes.getFirst())); } @Test public void WhenExternalFileTypeItemViewModelMissNameThenAddNewTypeIsFailed() { - externalFileTypeItemViewModel.setupWithoutName(); + setupViewModelWithoutName(); ArgumentCaptor itemCaptor = ArgumentCaptor.forClass(ExternalFileTypeItemViewModel.class); doAnswer(mocked -> { ExternalFileTypeItemViewModel capturedItem = itemCaptor.getValue(); - externalFileTypeItemViewModel.clone(capturedItem); + viewModelClone(capturedItem); return null; }).when(externalFileTypesTabViewModel).showEditDialog(itemCaptor.capture(), any()); From 73cf3d51e9a5a7f7d2ece96274eee2ddb6718364 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 11 Dec 2023 00:52:37 +0000 Subject: [PATCH 062/144] updated TestArchitectureTest for removing ExternalFileTypeItemViewModelTestData class --- src/test/java/org/jabref/architecture/TestArchitectureTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/org/jabref/architecture/TestArchitectureTest.java b/src/test/java/org/jabref/architecture/TestArchitectureTest.java index 3a69b760303..81f73e5a117 100644 --- a/src/test/java/org/jabref/architecture/TestArchitectureTest.java +++ b/src/test/java/org/jabref/architecture/TestArchitectureTest.java @@ -38,7 +38,6 @@ public void testNaming(JavaClasses classes) { .and().doNotHaveFullyQualifiedName("org.jabref.benchmarks.Benchmarks") .and().doNotHaveFullyQualifiedName("org.jabref.http.server.TestBibFile") .and().doNotHaveFullyQualifiedName("org.jabref.gui.autocompleter.AutoCompleterUtil") - .and().doNotHaveFullyQualifiedName("org.jabref.gui.preferences.externalfiletypes.ExternalFileTypeItemViewModelTestData") .and().doNotHaveFullyQualifiedName("org.jabref.gui.search.TextFlowEqualityHelper") .and().doNotHaveFullyQualifiedName("org.jabref.logic.bibtex.BibEntryAssert") .and().doNotHaveFullyQualifiedName("org.jabref.logic.importer.fileformat.ImporterTestEngine") From d983edd54c21be71d28cfbf67135d53f55579974 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 11 Dec 2023 01:01:30 +0000 Subject: [PATCH 063/144] extract methods for logics in isValidExternalFileType() --- .../ExternalFileTypesTabViewModel.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index 3f5faa0c9e9..89c639bb924 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -97,20 +97,31 @@ public void remove(ExternalFileTypeItemViewModel type) { } public boolean isValidExternalFileType(ExternalFileTypeItemViewModel item) { - if (item.getName().isEmpty() || item.extensionProperty().get().isEmpty() || item.mimetypeProperty().get().isEmpty()) { + if (withEmptyValue(item)){ LOGGER.debug("One of the fields is empty or invalid."); return false; } + if(!isUniqueExtension(item)){ + LOGGER.debug("File Extension exists already."); + return false; + } + + return true; + } + + private boolean withEmptyValue(ExternalFileTypeItemViewModel item){ + return item.getName().isEmpty() || item.extensionProperty().get().isEmpty() || item.mimetypeProperty().get().isEmpty(); + } + + private boolean isUniqueExtension(ExternalFileTypeItemViewModel item){ // check extension need to be unique in the list String newExt = item.extensionProperty().get(); for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { if (newExt.equalsIgnoreCase(fileTypeItem.extensionProperty().get())) { - LOGGER.debug("File Extension exists already."); return false; } } - return true; } } From 3fa21a3472a7d45ebb27151123017e5c260f9c78 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 11 Dec 2023 01:05:28 +0000 Subject: [PATCH 064/144] add validator for name and MIME type --- .../EditExternalFileTypeViewModel.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java index 938cf4b83ba..c0c7644b95a 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java @@ -27,6 +27,8 @@ public class EditExternalFileTypeViewModel { private final ObservableList fileTypes; private final String originalExtension; private Validator extensionValidator; + private Validator nameValidator; + private Validator mimeTypeValidator; private Validator sameExtensionValidator; private CompositeValidator validator; @@ -55,6 +57,19 @@ private void setupValidation() { StringUtil::isNotBlank, ValidationMessage.error(Localization.lang("Please enter a name for the extension.")) ); + + nameValidator = new FunctionBasedValidator<>( + nameProperty, + StringUtil::isNotBlank, + ValidationMessage.error(Localization.lang("Please enter a name for the name.")) + ); + + mimeTypeValidator = new FunctionBasedValidator<>( + mimeTypeProperty, + StringUtil::isNotBlank, + ValidationMessage.error(Localization.lang("Please enter a name for the MIME type.")) + ); + sameExtensionValidator = new FunctionBasedValidator<>( extensionProperty, extension -> { @@ -67,7 +82,8 @@ private void setupValidation() { }, ValidationMessage.error(Localization.lang("There is already an exists extension with the same name.")) ); - validator.addValidators(extensionValidator, sameExtensionValidator); + + validator.addValidators(extensionValidator, sameExtensionValidator, nameValidator, mimeTypeValidator); } public ValidationStatus validationStatus() { From 59e8ff4f80e5099f703db101b5b292e0ed724b11 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 11 Dec 2023 01:16:11 +0000 Subject: [PATCH 065/144] code reformatting --- .../externalfiletypes/ExternalFileTypesTabViewModelTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java index f587571b8b0..8c730d6b583 100644 --- a/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java +++ b/src/test/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModelTest.java @@ -76,7 +76,7 @@ public void WhenExternalFileTypeItemViewModelIsValidThenAddNewTypeIsSuccessful() externalFileTypesTabViewModel.addNewType(); ObservableList actualFileTypes = externalFileTypesTabViewModel.getFileTypes(); - assertEquals(actualFileTypes.size(), 1); + assertEquals(1, actualFileTypes.size()); assertTrue(viewModelIsSameValue(actualFileTypes.getFirst())); } @@ -93,6 +93,6 @@ public void WhenExternalFileTypeItemViewModelMissNameThenAddNewTypeIsFailed() { externalFileTypesTabViewModel.addNewType(); ObservableList emptyFileTypes = externalFileTypesTabViewModel.getFileTypes(); - assertEquals(emptyFileTypes.size(), 0); + assertEquals(0, emptyFileTypes.size()); } } From 0a2bcf039f22e32f20734dccf23e2e7fb121df6f Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 11 Dec 2023 01:29:03 +0000 Subject: [PATCH 066/144] reformatting code --- .../EditExternalFileTypeEntryDialog.java | 2 -- .../externalfiletypes/ExternalFileTypesTabViewModel.java | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java index 91d66003c35..73778e6d36e 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java @@ -39,8 +39,6 @@ public class EditExternalFileTypeEntryDialog extends BaseDialog { private final ObservableList fileTypes; private EditExternalFileTypeViewModel viewModel; - - public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, String dialogTitle, ObservableList fileTypes) { this.item = item; this.fileTypes = fileTypes; diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java index 89c639bb924..faacc66b978 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java @@ -97,12 +97,12 @@ public void remove(ExternalFileTypeItemViewModel type) { } public boolean isValidExternalFileType(ExternalFileTypeItemViewModel item) { - if (withEmptyValue(item)){ + if (withEmptyValue(item)) { LOGGER.debug("One of the fields is empty or invalid."); return false; } - if(!isUniqueExtension(item)){ + if (!isUniqueExtension(item)) { LOGGER.debug("File Extension exists already."); return false; } @@ -110,11 +110,11 @@ public boolean isValidExternalFileType(ExternalFileTypeItemViewModel item) { return true; } - private boolean withEmptyValue(ExternalFileTypeItemViewModel item){ + private boolean withEmptyValue(ExternalFileTypeItemViewModel item) { return item.getName().isEmpty() || item.extensionProperty().get().isEmpty() || item.mimetypeProperty().get().isEmpty(); } - private boolean isUniqueExtension(ExternalFileTypeItemViewModel item){ + private boolean isUniqueExtension(ExternalFileTypeItemViewModel item) { // check extension need to be unique in the list String newExt = item.extensionProperty().get(); for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { From 275cf4693cf8a784e2aef81abb09092ea86d1d13 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Mon, 11 Dec 2023 01:55:07 +0000 Subject: [PATCH 067/144] update language key --- .../externalfiletypes/EditExternalFileTypeViewModel.java | 2 +- src/main/resources/l10n/JabRef_en.properties | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java index c0c7644b95a..ee1083fcb47 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java @@ -61,7 +61,7 @@ private void setupValidation() { nameValidator = new FunctionBasedValidator<>( nameProperty, StringUtil::isNotBlank, - ValidationMessage.error(Localization.lang("Please enter a name for the name.")) + ValidationMessage.error(Localization.lang("Please enter a name.")) ); mimeTypeValidator = new FunctionBasedValidator<>( diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index ea1e11f80ae..8a36af400ca 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2040,6 +2040,11 @@ Matching=Matching Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=Same as --import, but will be imported to the opened tab Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=Allow integers in 'edition' field in BibTeX mode +Please\ enter\ a\ name\ for\ the\ MIME\ type.=Please enter a name for the MIME type. +Please\ enter\ a\ name\ for\ the\ extension.=Please enter a name for the extension. +Please\ enter\ a\ name.=Please enter a name. +There\ is\ already\ an\ exists\ extension\ with\ the\ same\ name.=There is already an exists extension with the same name. + Search\ for\ citations\ in\ LaTeX\ files...=Search for citations in LaTeX files... LaTeX\ Citations\ Search\ Results=LaTeX Citations Search Results LaTeX\ files\ directory\:=LaTeX files directory: @@ -2622,7 +2627,7 @@ Enable=Enable Keep\ disabled=Keep disabled Predatory\ journal\ %0\ found=Predatory journal %0 found - + Hide\ user\ comments=Hide user comments Show\ user\ comments\ field=Show user comments field From d32566cd0ca0d0f7d3a7a271f65db99ed18d2b56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:26:33 +0000 Subject: [PATCH 068/144] Bump actions/configure-pages from 3 to 4 (#10694) Bumps [actions/configure-pages](https://github.com/actions/configure-pages) from 3 to 4. - [Release notes](https://github.com/actions/configure-pages/releases) - [Commits](https://github.com/actions/configure-pages/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/configure-pages dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index cf662647b3e..000caa6310d 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -39,7 +39,7 @@ jobs: working-directory: docs/ - name: Setup Pages id: pages - uses: actions/configure-pages@v3 + uses: actions/configure-pages@v4 - name: Build with Jekyll run: | cd docs From d681fbe396f1ddbb2911a179717a18e6e36c1083 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:27:06 +0000 Subject: [PATCH 069/144] Bump actions/deploy-pages from 2 to 3 (#10695) Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 2 to 3. - [Release notes](https://github.com/actions/deploy-pages/releases) - [Commits](https://github.com/actions/deploy-pages/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/deploy-pages dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 000caa6310d..c6799ea1a90 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -59,4 +59,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v2 + uses: actions/deploy-pages@v3 From be8ffd7559b445643d0ac251c80706702b2dda86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:50:51 +0000 Subject: [PATCH 070/144] Bump com.tngtech.archunit:archunit-junit5-api from 1.2.0 to 1.2.1 (#10696) Bumps [com.tngtech.archunit:archunit-junit5-api](https://github.com/TNG/ArchUnit) from 1.2.0 to 1.2.1. - [Release notes](https://github.com/TNG/ArchUnit/releases) - [Commits](https://github.com/TNG/ArchUnit/compare/v1.2.0...v1.2.1) --- updated-dependencies: - dependency-name: com.tngtech.archunit:archunit-junit5-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e34673740da..a5f11826b24 100644 --- a/build.gradle +++ b/build.gradle @@ -243,7 +243,7 @@ dependencies { testImplementation 'org.xmlunit:xmlunit-core:2.9.1' testImplementation 'org.xmlunit:xmlunit-matchers:2.9.1' testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.2.0' - testImplementation 'com.tngtech.archunit:archunit-junit5-api:1.2.0' + testImplementation 'com.tngtech.archunit:archunit-junit5-api:1.2.1' testImplementation "org.testfx:testfx-core:4.0.16-alpha" testImplementation "org.testfx:testfx-junit5:4.0.16-alpha" testImplementation "org.hamcrest:hamcrest-library:2.2" From e912a519cd47862b4a009e56bf0e146e73964b85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:51:16 +0000 Subject: [PATCH 071/144] Bump org.glassfish.jersey.core:jersey-server from 3.1.3 to 3.1.4 (#10698) Bumps org.glassfish.jersey.core:jersey-server from 3.1.3 to 3.1.4. --- updated-dependencies: - dependency-name: org.glassfish.jersey.core:jersey-server dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a5f11826b24..826dd803f0d 100644 --- a/build.gradle +++ b/build.gradle @@ -221,7 +221,7 @@ dependencies { // API implementation 'jakarta.ws.rs:jakarta.ws.rs-api:3.1.0' // Implementation of the API - implementation 'org.glassfish.jersey.core:jersey-server:3.1.3' + implementation 'org.glassfish.jersey.core:jersey-server:3.1.4' // injection framework implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.3' implementation 'org.glassfish.hk2:hk2-api:3.0.5' From 6e34239710f8e8958aad0231b2318907437c49f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 18:47:32 +0100 Subject: [PATCH 072/144] Bump org.glassfish.jersey.containers:jersey-container-grizzly2-http from 3.1.3 to 3.1.4 (#10697) * Bump org.glassfish.jersey.containers:jersey-container-grizzly2-http Bumps org.glassfish.jersey.containers:jersey-container-grizzly2-http from 3.1.3 to 3.1.4. --- updated-dependencies: - dependency-name: org.glassfish.jersey.containers:jersey-container-grizzly2-http dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * fix glassfish jersey grizzly updates and modules --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Siedlerchr --- build.gradle | 6 +++--- src/main/java/module-info.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 826dd803f0d..1c14c9f49cf 100644 --- a/build.gradle +++ b/build.gradle @@ -223,15 +223,15 @@ dependencies { // Implementation of the API implementation 'org.glassfish.jersey.core:jersey-server:3.1.4' // injection framework - implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.3' + implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.4' implementation 'org.glassfish.hk2:hk2-api:3.0.5' // testImplementation 'org.glassfish.hk2:hk2-testing:3.0.4' // implementation 'org.glassfish.hk2:hk2-testing-jersey:3.0.4' // testImplementation 'org.glassfish.hk2:hk2-junitrunner:3.0.4' // HTTP server // implementation 'org.glassfish.jersey.containers:jersey-container-netty-http:3.1.1' - implementation 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.3' - testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:3.1.3' + implementation 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.4' + testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:3.1.4' // Allow objects "magically" to be mapped to JSON using GSON // implementation 'org.glassfish.jersey.media:jersey-media-json-gson:3.1.1' diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 8e26519c8e5..0ac40fe2aa4 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -61,7 +61,7 @@ // http server and client exchange requires java.net.http; requires jakarta.ws.rs; - requires grizzly.framework; + requires org.glassfish.grizzly; // data mapping requires jakarta.xml.bind; From fd0835fcf36ccd207a684579600b30dc4bc34952 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Tue, 12 Dec 2023 13:19:51 +0000 Subject: [PATCH 073/144] add unique name and mimetype validation for OK Button of EditExternalFileTypeView --- .../EditExternalFileTypeViewModel.java | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java index ee1083fcb47..fc6489fa2e0 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java @@ -26,16 +26,22 @@ public class EditExternalFileTypeViewModel { private final BooleanProperty customApplicationSelectedProperty = new SimpleBooleanProperty(false); private final ObservableList fileTypes; private final String originalExtension; + private final String originalName; + private final String originalMimeType; private Validator extensionValidator; private Validator nameValidator; private Validator mimeTypeValidator; private Validator sameExtensionValidator; + private Validator sameNameValidator; + private Validator sameMimeTypeValidator; private CompositeValidator validator; public EditExternalFileTypeViewModel(ExternalFileTypeItemViewModel fileTypeViewModel, ObservableList fileTypes) { this.fileTypeViewModel = fileTypeViewModel; this.fileTypes = fileTypes; this.originalExtension = fileTypeViewModel.extensionProperty().getValue(); + this.originalName = fileTypeViewModel.nameProperty().getValue(); + this.originalMimeType = fileTypeViewModel.mimetypeProperty().getValue(); extensionProperty.setValue(fileTypeViewModel.extensionProperty().getValue()); nameProperty.setValue(fileTypeViewModel.nameProperty().getValue()); mimeTypeProperty.setValue(fileTypeViewModel.mimetypeProperty().getValue()); @@ -80,10 +86,36 @@ private void setupValidation() { } return true; }, - ValidationMessage.error(Localization.lang("There is already an exists extension with the same name.")) + ValidationMessage.error(Localization.lang("There is already an same external file type with same extension exists")) ); - validator.addValidators(extensionValidator, sameExtensionValidator, nameValidator, mimeTypeValidator); + sameNameValidator = new FunctionBasedValidator<>( + nameProperty, + name -> { + for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { + if (name.equalsIgnoreCase(fileTypeItem.nameProperty().get()) && !name.equalsIgnoreCase(originalName)) { + return false; + } + } + return true; + }, + ValidationMessage.error(Localization.lang("There is already an same external file type with same name exists")) + ); + + sameMimeTypeValidator = new FunctionBasedValidator<>( + mimeTypeProperty, + mimeType -> { + for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { + if (mimeType.equalsIgnoreCase(fileTypeItem.mimetypeProperty().get()) && !mimeType.equalsIgnoreCase(originalMimeType)) { + return false; + } + } + return true; + }, + ValidationMessage.error(Localization.lang("There is already an same external file type with same MIME type exists")) + ); + + validator.addValidators(extensionValidator, sameExtensionValidator, nameValidator, sameNameValidator, mimeTypeValidator, sameMimeTypeValidator); } public ValidationStatus validationStatus() { From 134251d691209bab68a4c580dfbe0f2d564cddd2 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Tue, 12 Dec 2023 13:31:38 +0000 Subject: [PATCH 074/144] add message in JabRef_en.properties --- src/main/resources/l10n/JabRef_en.properties | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 8a36af400ca..314e7721ae5 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2043,7 +2043,9 @@ Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=Allow integers in 'editi Please\ enter\ a\ name\ for\ the\ MIME\ type.=Please enter a name for the MIME type. Please\ enter\ a\ name\ for\ the\ extension.=Please enter a name for the extension. Please\ enter\ a\ name.=Please enter a name. -There\ is\ already\ an\ exists\ extension\ with\ the\ same\ name.=There is already an exists extension with the same name. +There\ is\ already\ an\ same\ external\ file\ type\ with\ same\ MIME\ type\ exists=There is already an same external file type with same MIME type exists +There\ is\ already\ an\ same\ external\ file\ type\ with\ same\ extension\ exists=There is already an same external file type with same extension exists +There\ is\ already\ an\ same\ external\ file\ type\ with\ same\ name\ exists=There is already an same external file type with same name exists Search\ for\ citations\ in\ LaTeX\ files...=Search for citations in LaTeX files... LaTeX\ Citations\ Search\ Results=LaTeX Citations Search Results From f0999948d4b9251f62909e8dd0d8c8e4f0702f34 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 15 Dec 2023 03:30:11 +0100 Subject: [PATCH 075/144] Update CSL styles (#10701) Co-authored-by: Siedlerchr --- src/main/resources/csl-styles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles index 42f54b22d80..a3d9a63426d 160000 --- a/src/main/resources/csl-styles +++ b/src/main/resources/csl-styles @@ -1 +1 @@ -Subproject commit 42f54b22d8058c957404945b7dca5cc59c1fc68d +Subproject commit a3d9a63426d2390068b4c98da6f48bd4ce73b257 From b4e8c219d26088b2961ce5b0f75d8cb4d7a9bdba Mon Sep 17 00:00:00 2001 From: Siedlerchr Date: Sat, 16 Dec 2023 17:49:54 +0100 Subject: [PATCH 076/144] add validation visualizaion and improve error message grammar --- .../EditExternalFileTypeEntryDialog.java | 13 ++++ .../EditExternalFileTypeViewModel.java | 67 ++++++++++++------- src/main/resources/csl-styles | 2 +- src/main/resources/l10n/JabRef_en.properties | 8 ++- 4 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java index 73778e6d36e..d0dc110b581 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java @@ -1,5 +1,6 @@ package org.jabref.gui.preferences.externalfiletypes; +import javafx.application.Platform; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; @@ -14,9 +15,11 @@ import org.jabref.gui.desktop.os.NativeDesktop; import org.jabref.gui.util.BaseDialog; import org.jabref.gui.util.FileDialogConfiguration; +import org.jabref.gui.util.IconValidationDecorator; import org.jabref.logic.util.OS; import com.airhacks.afterburner.views.ViewLoader; +import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer; import jakarta.inject.Inject; public class EditExternalFileTypeEntryDialog extends BaseDialog { @@ -39,6 +42,8 @@ public class EditExternalFileTypeEntryDialog extends BaseDialog { private final ObservableList fileTypes; private EditExternalFileTypeViewModel viewModel; + private final ControlsFxVisualizer visualizer = new ControlsFxVisualizer(); + public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, String dialogTitle, ObservableList fileTypes) { this.item = item; this.fileTypes = fileTypes; @@ -62,6 +67,8 @@ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, Strin @FXML public void initialize() { + visualizer.setDecoration(new IconValidationDecorator()); + viewModel = new EditExternalFileTypeViewModel(item, fileTypes); icon.setGraphic(viewModel.getIcon()); @@ -74,6 +81,12 @@ public void initialize() { name.textProperty().bindBidirectional(viewModel.nameProperty()); mimeType.textProperty().bindBidirectional(viewModel.mimeTypeProperty()); selectedApplication.textProperty().bindBidirectional(viewModel.selectedApplicationProperty()); + + Platform.runLater(() -> { + visualizer.initVisualization(viewModel.extensionValidation(), extension, true); + visualizer.initVisualization(viewModel.nameValidation(), name, true); + visualizer.initVisualization(viewModel.mimeTypeValidation(), mimeType, true); + }); } @FXML diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java index fc6489fa2e0..e56aa5e5114 100644 --- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java @@ -28,12 +28,9 @@ public class EditExternalFileTypeViewModel { private final String originalExtension; private final String originalName; private final String originalMimeType; - private Validator extensionValidator; - private Validator nameValidator; - private Validator mimeTypeValidator; - private Validator sameExtensionValidator; - private Validator sameNameValidator; - private Validator sameMimeTypeValidator; + private CompositeValidator extensionValidator; + private CompositeValidator nameValidator; + private CompositeValidator mimeTypeValidator; private CompositeValidator validator; public EditExternalFileTypeViewModel(ExternalFileTypeItemViewModel fileTypeViewModel, ObservableList fileTypes) { @@ -58,25 +55,14 @@ public EditExternalFileTypeViewModel(ExternalFileTypeItemViewModel fileTypeViewM private void setupValidation() { validator = new CompositeValidator(); - extensionValidator = new FunctionBasedValidator<>( + extensionValidator = new CompositeValidator(); + + Validator extensionisNotBlankValidator = new FunctionBasedValidator<>( extensionProperty, StringUtil::isNotBlank, ValidationMessage.error(Localization.lang("Please enter a name for the extension.")) ); - - nameValidator = new FunctionBasedValidator<>( - nameProperty, - StringUtil::isNotBlank, - ValidationMessage.error(Localization.lang("Please enter a name.")) - ); - - mimeTypeValidator = new FunctionBasedValidator<>( - mimeTypeProperty, - StringUtil::isNotBlank, - ValidationMessage.error(Localization.lang("Please enter a name for the MIME type.")) - ); - - sameExtensionValidator = new FunctionBasedValidator<>( + Validator sameExtensionValidator = new FunctionBasedValidator<>( extensionProperty, extension -> { for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { @@ -86,10 +72,12 @@ private void setupValidation() { } return true; }, - ValidationMessage.error(Localization.lang("There is already an same external file type with same extension exists")) + ValidationMessage.error(Localization.lang("There already exists an external file type with the same extension")) ); + extensionValidator.addValidators(sameExtensionValidator, extensionisNotBlankValidator); - sameNameValidator = new FunctionBasedValidator<>( + nameValidator = new CompositeValidator(); + Validator sameNameValidator = new FunctionBasedValidator<>( nameProperty, name -> { for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { @@ -99,10 +87,24 @@ private void setupValidation() { } return true; }, - ValidationMessage.error(Localization.lang("There is already an same external file type with same name exists")) + ValidationMessage.error(Localization.lang("There already exists an external file type with the same name")) + ); + + Validator nameIsNotBlankValidator = new FunctionBasedValidator<>( + nameProperty, + StringUtil::isNotBlank, + ValidationMessage.error(Localization.lang("Please enter a name.")) + ); + nameValidator.addValidators(sameNameValidator, nameIsNotBlankValidator); + + mimeTypeValidator = new CompositeValidator(); + Validator mimeTypeIsNotBlankValidator = new FunctionBasedValidator<>( + mimeTypeProperty, + StringUtil::isNotBlank, + ValidationMessage.error(Localization.lang("Please enter a name for the MIME type.")) ); - sameMimeTypeValidator = new FunctionBasedValidator<>( + Validator sameMimeTypeValidator = new FunctionBasedValidator<>( mimeTypeProperty, mimeType -> { for (ExternalFileTypeItemViewModel fileTypeItem : fileTypes) { @@ -112,8 +114,9 @@ private void setupValidation() { } return true; }, - ValidationMessage.error(Localization.lang("There is already an same external file type with same MIME type exists")) + ValidationMessage.error(Localization.lang("There already exists an external file type with the same MIME type")) ); + mimeTypeValidator.addValidators(sameMimeTypeValidator, mimeTypeIsNotBlankValidator); validator.addValidators(extensionValidator, sameExtensionValidator, nameValidator, sameNameValidator, mimeTypeValidator, sameMimeTypeValidator); } @@ -122,6 +125,18 @@ public ValidationStatus validationStatus() { return validator.getValidationStatus(); } + public ValidationStatus extensionValidation() { + return extensionValidator.getValidationStatus(); + } + + public ValidationStatus mimeTypeValidation() { + return mimeTypeValidator.getValidationStatus(); + } + + public ValidationStatus nameValidation() { + return nameValidator.getValidationStatus(); + } + public Node getIcon() { return fileTypeViewModel.iconProperty().getValue().getGraphicNode(); } diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles index a3d9a63426d..42f54b22d80 160000 --- a/src/main/resources/csl-styles +++ b/src/main/resources/csl-styles @@ -1 +1 @@ -Subproject commit a3d9a63426d2390068b4c98da6f48bd4ce73b257 +Subproject commit 42f54b22d8058c957404945b7dca5cc59c1fc68d diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 314e7721ae5..019dcfcd8ae 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2043,9 +2043,11 @@ Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=Allow integers in 'editi Please\ enter\ a\ name\ for\ the\ MIME\ type.=Please enter a name for the MIME type. Please\ enter\ a\ name\ for\ the\ extension.=Please enter a name for the extension. Please\ enter\ a\ name.=Please enter a name. -There\ is\ already\ an\ same\ external\ file\ type\ with\ same\ MIME\ type\ exists=There is already an same external file type with same MIME type exists -There\ is\ already\ an\ same\ external\ file\ type\ with\ same\ extension\ exists=There is already an same external file type with same extension exists -There\ is\ already\ an\ same\ external\ file\ type\ with\ same\ name\ exists=There is already an same external file type with same name exists + +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ MIME\ type=There already exists an external file type with the same MIME type +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ extension=There already exists an external file type with the same extension +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ name=There already exists an external file type with the same name + Search\ for\ citations\ in\ LaTeX\ files...=Search for citations in LaTeX files... LaTeX\ Citations\ Search\ Results=LaTeX Citations Search Results From 990ea975737d0b46366ca3f11ff85b2e6f3a7924 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:12:23 +0000 Subject: [PATCH 077/144] Bump org.glassfish.jersey.inject:jersey-hk2 from 3.1.4 to 3.1.5 (#10703) Bumps org.glassfish.jersey.inject:jersey-hk2 from 3.1.4 to 3.1.5. --- updated-dependencies: - dependency-name: org.glassfish.jersey.inject:jersey-hk2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1c14c9f49cf..c2beeea4c0e 100644 --- a/build.gradle +++ b/build.gradle @@ -223,7 +223,7 @@ dependencies { // Implementation of the API implementation 'org.glassfish.jersey.core:jersey-server:3.1.4' // injection framework - implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.4' + implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.5' implementation 'org.glassfish.hk2:hk2-api:3.0.5' // testImplementation 'org.glassfish.hk2:hk2-testing:3.0.4' // implementation 'org.glassfish.hk2:hk2-testing-jersey:3.0.4' From 3df27bbe362c8ff547862b9d2485ac251d844d45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:12:49 +0000 Subject: [PATCH 078/144] Bump org.apache.lucene:lucene-analysis-common from 9.9.0 to 9.9.1 (#10704) Bumps org.apache.lucene:lucene-analysis-common from 9.9.0 to 9.9.1. --- updated-dependencies: - dependency-name: org.apache.lucene:lucene-analysis-common dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c2beeea4c0e..eb35d52778e 100644 --- a/build.gradle +++ b/build.gradle @@ -120,7 +120,7 @@ dependencies { implementation 'org.apache.lucene:lucene-core:9.9.0' implementation 'org.apache.lucene:lucene-queryparser:9.9.0' implementation 'org.apache.lucene:lucene-queries:9.9.0' - implementation 'org.apache.lucene:lucene-analysis-common:9.9.0' + implementation 'org.apache.lucene:lucene-analysis-common:9.9.1' implementation 'org.apache.lucene:lucene-highlighter:9.9.0' implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.10.0' From 27a2df8e24cede5edae0be9c0a55ef6bba665c0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:16:55 +0000 Subject: [PATCH 079/144] Bump com.tngtech.archunit:archunit-junit5-engine from 1.2.0 to 1.2.1 (#10702) Bumps [com.tngtech.archunit:archunit-junit5-engine](https://github.com/TNG/ArchUnit) from 1.2.0 to 1.2.1. - [Release notes](https://github.com/TNG/ArchUnit/releases) - [Commits](https://github.com/TNG/ArchUnit/compare/v1.2.0...v1.2.1) --- updated-dependencies: - dependency-name: com.tngtech.archunit:archunit-junit5-engine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index eb35d52778e..309c524b12e 100644 --- a/build.gradle +++ b/build.gradle @@ -242,7 +242,7 @@ dependencies { testImplementation 'org.mockito:mockito-core:5.7.0' testImplementation 'org.xmlunit:xmlunit-core:2.9.1' testImplementation 'org.xmlunit:xmlunit-matchers:2.9.1' - testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.2.0' + testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.2.1' testImplementation 'com.tngtech.archunit:archunit-junit5-api:1.2.1' testImplementation "org.testfx:testfx-core:4.0.16-alpha" testImplementation "org.testfx:testfx-junit5:4.0.16-alpha" From 145b277ddd92d7d9e080f8b207bdd9784b01a9d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:33:46 +0000 Subject: [PATCH 080/144] Bump org.apache.lucene:lucene-queryparser from 9.9.0 to 9.9.1 (#10705) Bumps org.apache.lucene:lucene-queryparser from 9.9.0 to 9.9.1. --- updated-dependencies: - dependency-name: org.apache.lucene:lucene-queryparser dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 309c524b12e..5ca46c13169 100644 --- a/build.gradle +++ b/build.gradle @@ -118,7 +118,7 @@ dependencies { implementation 'org.apache.pdfbox:xmpbox:3.0.1' implementation 'org.apache.lucene:lucene-core:9.9.0' - implementation 'org.apache.lucene:lucene-queryparser:9.9.0' + implementation 'org.apache.lucene:lucene-queryparser:9.9.1' implementation 'org.apache.lucene:lucene-queries:9.9.0' implementation 'org.apache.lucene:lucene-analysis-common:9.9.1' implementation 'org.apache.lucene:lucene-highlighter:9.9.0' From d9ebef5116a3a417c9c7e4f36b10e38944da72b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:38:49 +0000 Subject: [PATCH 081/144] Bump actions/upload-artifact from 3 to 4 (#10706) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deployment-arm64.yml | 2 +- .github/workflows/deployment.yml | 4 ++-- .github/workflows/gource.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index 4f436d1fd30..02967c123ea 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -162,7 +162,7 @@ jobs: rsync -Pavz --itemize-changes --stats --partial-dir=/tmp/partial --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i ~/.ssh/id_rsa' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ - name: Upload to GitHub workflow artifacts store if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue') }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: JabRef-${{ matrix.displayName }} path: build/distribution diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 6fc6a5a6fef..d4e176743f6 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -220,14 +220,14 @@ jobs: rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ - name: Upload to GitHub workflow artifacts store (macOS) if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (startsWith(github.ref, 'refs/tags/') || inputs.notarization == true) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: # tbn = to-be-notarized name: JabRef-macOS-tbn path: build/distribution - name: Upload to GitHub workflow artifacts store if: (steps.checksecrets.outputs.secretspresent != 'YES') - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: # tbn = to-be-notarized name: JabRef-${{ matrix.os }} diff --git a/.github/workflows/gource.yml b/.github/workflows/gource.yml index ccde09af4f9..a1cb073f6f8 100644 --- a/.github/workflows/gource.yml +++ b/.github/workflows/gource.yml @@ -64,7 +64,7 @@ jobs: run: | mv gource/gource.mp4 gource-videos/jabref-complete.mp4 - name: 'Upload gource video' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Gource path: gource-videos/ From 2bed3d39d8ecdd37645ba8998114386551eba82e Mon Sep 17 00:00:00 2001 From: Gary Mejia <50064854+garymejia@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:49:13 -0500 Subject: [PATCH 082/144] Implement scrollable warning messages in alert boxes (#10700) * Set warning message content within a TextArea object to enable scrolling within the alertbox. * Modified getErrorMessage() to utilize a newline delimiter for the join function, displaying its contents on separate lines. * Updated order of import to comply with checkstyle. --- src/main/java/org/jabref/gui/JabRefDialogService.java | 7 ++++++- src/main/java/org/jabref/logic/importer/ParserResult.java | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefDialogService.java b/src/main/java/org/jabref/gui/JabRefDialogService.java index 6f3c333db13..6b595654d31 100644 --- a/src/main/java/org/jabref/gui/JabRefDialogService.java +++ b/src/main/java/org/jabref/gui/JabRefDialogService.java @@ -25,6 +25,7 @@ import javafx.scene.control.ChoiceDialog; import javafx.scene.control.DialogPane; import javafx.scene.control.Label; +import javafx.scene.control.TextArea; import javafx.scene.control.TextInputDialog; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; @@ -79,8 +80,12 @@ public JabRefDialogService(Window mainWindow) { private FXDialog createDialog(AlertType type, String title, String content) { FXDialog alert = new FXDialog(type, title, true); alert.setHeaderText(null); - alert.setContentText(content); alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); + alert.setResizable(true); + + TextArea area = new TextArea(content); + + alert.getDialogPane().setContent(area); alert.initOwner(mainWindow); return alert; } diff --git a/src/main/java/org/jabref/logic/importer/ParserResult.java b/src/main/java/org/jabref/logic/importer/ParserResult.java index 2f134224562..c5f61ef3513 100644 --- a/src/main/java/org/jabref/logic/importer/ParserResult.java +++ b/src/main/java/org/jabref/logic/importer/ParserResult.java @@ -132,7 +132,7 @@ public void setInvalid(boolean invalid) { } public String getErrorMessage() { - return String.join(" ", warnings()); + return String.join("\n", warnings()); } public BibDatabaseContext getDatabaseContext() { From 95325b2cd4035e68cf568cc43479c0d6d4b33da3 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Thu, 21 Dec 2023 21:04:27 +0100 Subject: [PATCH 083/144] Remove autohotkey part (#10708) --- docs/code-howtos/tools.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docs/code-howtos/tools.md b/docs/code-howtos/tools.md index f2b344da05a..a21bf399add 100644 --- a/docs/code-howtos/tools.md +++ b/docs/code-howtos/tools.md @@ -70,10 +70,3 @@ Then, each weak do `choco upgrade all` to ensure all tooling is kept updated. ### Tools for working with XMP * Validate XMP: [https://www.pdflib.com/pdf-knowledge-base/xmp/free-xmp-validator/](https://www.pdflib.com/pdf-knowledge-base/xmp/free-xmp-validator/) - -### Some useful keyboard shortcuts - -* [AutoHotkey](http://autohotkey.com) - Preparation for the next step -* [https://github.com/koppor/autohotkey-scripts](https://github.com/koppor/autohotkey-scripts) - Aim: Have Win+C opening ConEmu - 1. Clone the repository locally. - 2. Then link `ConEmu.ahk` and `WindowsExplorer.ahk` at the startup menu (Link creation works with drag'n'drop using the right mouse key and then choosing "Create link" when dropping). Hint: Startup is in the folder `Startup` (German: `Autostart`) at `%APPDATA%\Microsoft\Windows\Start Menu\Programs\` - accessible via Win+R: `shell:startup` From b6a2b21c2425fa3600a1161f5040342006ea3f75 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Thu, 21 Dec 2023 22:58:44 +0100 Subject: [PATCH 084/144] Update .lycheeignore - https://scholar.archive.org/ is too slow for us --- .lycheeignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.lycheeignore b/.lycheeignore index 8d3f2072f17..801ddc56e23 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -2,6 +2,7 @@ https://arxiv.org/ https://contribute.jabref.org/ https://donations.jabref.org/ https://pubs.acs.org/ +https://scholar.archive.org/ https://web.archive.org/ https://www.researchgate.net/privacy-policy https://www.sciencedirect.com/ From 60141726eabe4159e7f1020c47a020372f580b05 Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 22 Dec 2023 00:34:15 +0100 Subject: [PATCH 085/144] Fix fulltext of semantic scholar (#10709) * Fix fulltext of semantic scholar * Disable non-working test --------- Co-authored-by: Oliver Kopp --- .../importer/fetcher/SemanticScholar.java | 21 ++++++++----------- .../importer/fetcher/SemanticScholarTest.java | 5 +++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/jabref/logic/importer/fetcher/SemanticScholar.java b/src/main/java/org/jabref/logic/importer/fetcher/SemanticScholar.java index cde8ae98ece..b6f4aea83ab 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/SemanticScholar.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/SemanticScholar.java @@ -27,6 +27,7 @@ import org.jabref.model.entry.identifier.ArXivIdentifier; import org.jabref.model.entry.identifier.DOI; import org.jabref.model.entry.types.StandardEntryType; +import org.jabref.model.strings.StringUtil; import kong.unirest.json.JSONArray; import kong.unirest.json.JSONException; @@ -35,7 +36,6 @@ import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; -import org.jsoup.select.Elements; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -75,13 +75,13 @@ public Optional findFullText(BibEntry entry) throws IOException, FetcherExc String source = SOURCE_ID_SEARCH + doi.get().getDOI(); var jsoupRequest = Jsoup.connect(getURLBySource(source)) .userAgent(URLDownload.USER_AGENT) + .header("Accept", "text/html; charset=utf-8") .referrer("https://www.google.com") .ignoreHttpErrors(true); importerPreferences.getApiKey(getName()).ifPresent( key -> jsoupRequest.header("x-api-key", key)); html = jsoupRequest.get(); - } catch ( - IOException e) { + } catch (IOException e) { LOGGER.info("Error for pdf lookup with DOI"); } } @@ -95,6 +95,7 @@ public Optional findFullText(BibEntry entry) throws IOException, FetcherExc var jsoupRequest = Jsoup.connect(getURLBySource(source)) .userAgent(URLDownload.USER_AGENT) .referrer("https://www.google.com") + .header("Accept", "text/html; charset=utf-8") .ignoreHttpErrors(true); importerPreferences.getApiKey(getName()).ifPresent( key -> jsoupRequest.header("x-api-key", key)); @@ -104,16 +105,12 @@ public Optional findFullText(BibEntry entry) throws IOException, FetcherExc return Optional.empty(); } - // Retrieve PDF link from button on the webpage - // First checked is a drop-down menu, as it has the correct URL if present - // Else take the primary button - Elements metaLinks = html.getElementsByClass("flex-item alternate-sources__dropdown"); - String link = metaLinks.select("a").attr("href"); - if (link.length() < 10) { - metaLinks = html.getElementsByClass("flex-paper-actions__button--primary"); - link = metaLinks.select("a").attr("href"); + var metaTag = html.selectFirst("meta[name=citation_pdf_url]"); + if (metaTag == null) { + return Optional.empty(); } - if (link.isBlank()) { + String link = metaTag.attr("content"); + if (StringUtil.isNullOrEmpty(link)) { return Optional.empty(); } LOGGER.info("Fulltext PDF found @ SemanticScholar. Link: {}", link); diff --git a/src/test/java/org/jabref/logic/importer/fetcher/SemanticScholarTest.java b/src/test/java/org/jabref/logic/importer/fetcher/SemanticScholarTest.java index 806f400009c..b4b203a5aa3 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/SemanticScholarTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/SemanticScholarTest.java @@ -22,6 +22,7 @@ import org.apache.lucene.queryparser.flexible.core.parser.SyntaxParser; import org.apache.lucene.queryparser.flexible.standard.parser.StandardSyntaxParser; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -60,9 +61,10 @@ void getDocument() throws IOException, FetcherException { } @Test + @Disabled("Returns a DOI instead of the required link") @DisabledOnCIServer("CI server is unreliable") void fullTextFindByDOI() throws Exception { - entry.withField(StandardField.DOI, "10.1038/nrn3241"); + entry.setField(StandardField.DOI, "10.1038/nrn3241"); assertEquals( Optional.of(new URI("https://europepmc.org/articles/pmc4907333?pdf=render").toURL()), fetcher.findFullText(entry) @@ -81,7 +83,6 @@ void fullTextFindByDOIAlternate() throws Exception { @Test @DisabledOnCIServer("CI server is unreliable") void fullTextSearchOnEmptyEntry() throws IOException, FetcherException { - assertEquals(Optional.empty(), fetcher.findFullText(new BibEntry())); } From 11d90a17bda0f4df97403fbe4c0ac2241d47c17c Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 22 Dec 2023 03:25:29 +0100 Subject: [PATCH 086/144] Fix Copy cite command should respect preferences (#10707) * Fix Copy cite command should respect preferences Fixes #10615 * add changelog fix checsktyle * Fix CHANGELOG.md * Add missing "citation" to "key" * Add debug message at error in logic in CopyMoreAction * Add fallback if preference could not be parsed * Fix key binding setting * More relaxed parsing * Streamline code in AbstractPushToApplication * Add more test cases for configured citation command * Streamline code for copy action * Update src/main/java/org/jabref/gui/preferences/external/ExternalTabViewModel.java * Fix test --------- Co-authored-by: Oliver Kopp --- CHANGELOG.md | 2 + .../jabref/gui/actions/StandardActions.java | 2 +- .../org/jabref/gui/edit/CopyMoreAction.java | 97 +++++++++---------- .../org/jabref/gui/keyboard/KeyBinding.java | 5 +- .../external/ExternalTabViewModel.java | 8 +- .../gui/push/AbstractPushToApplication.java | 37 +------ .../logic/push/CitationCommandString.java | 34 +++++++ .../ExternalApplicationsPreferences.java | 24 +++-- .../jabref/preferences/JabRefPreferences.java | 24 ++--- src/main/resources/l10n/JabRef_en.properties | 2 +- .../jabref/gui/edit/CopyMoreActionTest.java | 6 ++ .../org/jabref/gui/push/PushToEmacsTest.java | 2 +- .../CitationCommandStringTest.java | 44 +++++++++ 13 files changed, 174 insertions(+), 113 deletions(-) create mode 100644 src/main/java/org/jabref/logic/push/CitationCommandString.java create mode 100644 src/test/java/org/jabref/preferences/CitationCommandStringTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 50053b9c270..606bf9aa981 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We upgraded to JavaFX 21.0.1. As a consequence JabRef requires now macOS 11 or later and GTK 3.8 or later on Linux [10627](https://github.com/JabRef/jabref/pull/10627). - A user-specific comment fields is not enabled by default, but can be enabled using the "Add" button. [#10424](https://github.com/JabRef/jabref/issues/10424) - We upgraded to Lucene 9.9 for the fulltext search. The search index will be rebuild. [#10686](https://github.com/JabRef/jabref/pull/10686) +- When using "Copy..." -> "Copy citation key", the delimiter configured at "Push applications" is respected. [#10707](https://github.com/JabRef/jabref/pull/10707) ### Fixed @@ -37,6 +38,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed some small inconsistencies in the user interface. [#10507](https://github.com/JabRef/jabref/issues/10507) [#10458](https://github.com/JabRef/jabref/issues/10458) [#10660](https://github.com/JabRef/jabref/issues/10660) - We fixed the issue where the Hayagriva YAML exporter would not include a parent field for the publisher/series. [#10596](https://github.com/JabRef/jabref/issues/10596) - We fixed issues in the external file type dialog w.r.t. duplicate entries in the case of a language switch. [#10271](https://github.com/JabRef/jabref/issues/10271) +- We fixed an issue where the right-click action "Copy cite..." did not respect the configured citation command under "External Programs" -> "[Push Applications](https://docs.jabref.org/cite/pushtoapplications)" [#10615](https://github.com/JabRef/jabref/issues/10615) ### Removed diff --git a/src/main/java/org/jabref/gui/actions/StandardActions.java b/src/main/java/org/jabref/gui/actions/StandardActions.java index eb0c94f866d..b53ecee6cc0 100644 --- a/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -13,7 +13,7 @@ public enum StandardActions implements Action { COPY_MORE(Localization.lang("Copy") + "..."), COPY_TITLE(Localization.lang("Copy title"), KeyBinding.COPY_TITLE), COPY_KEY(Localization.lang("Copy citation key"), KeyBinding.COPY_CITATION_KEY), - COPY_CITE_KEY(Localization.lang("Copy \\cite{citation key}"), KeyBinding.COPY_CITE_CITATION_KEY), + COPY_CITE_KEY(Localization.lang("Copy citation key with configured cite command"), KeyBinding.COPY_CITE_CITATION_KEY), COPY_KEY_AND_TITLE(Localization.lang("Copy citation key and title"), KeyBinding.COPY_CITATION_KEY_AND_TITLE), COPY_KEY_AND_LINK(Localization.lang("Copy citation key and link"), KeyBinding.COPY_CITATION_KEY_AND_LINK), COPY_CITATION_HTML(Localization.lang("Copy citation (html)"), KeyBinding.COPY_PREVIEW), diff --git a/src/main/java/org/jabref/gui/edit/CopyMoreAction.java b/src/main/java/org/jabref/gui/edit/CopyMoreAction.java index 0f9bbe3c103..36a770b4422 100644 --- a/src/main/java/org/jabref/gui/edit/CopyMoreAction.java +++ b/src/main/java/org/jabref/gui/edit/CopyMoreAction.java @@ -3,7 +3,7 @@ import java.io.IOException; import java.io.StringReader; import java.util.List; -import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import org.jabref.gui.ClipBoardManager; @@ -17,7 +17,9 @@ import org.jabref.logic.l10n.Localization; import org.jabref.logic.layout.Layout; import org.jabref.logic.layout.LayoutHelper; +import org.jabref.logic.push.CitationCommandString; import org.jabref.logic.util.OS; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.preferences.PreferencesService; @@ -58,13 +60,20 @@ public void execute() { } switch (action) { - case COPY_TITLE -> copyTitle(); - case COPY_KEY -> copyKey(); - case COPY_CITE_KEY -> copyCiteKey(); - case COPY_KEY_AND_TITLE -> copyKeyAndTitle(); - case COPY_KEY_AND_LINK -> copyKeyAndLink(); - case COPY_DOI, COPY_DOI_URL -> copyDoi(); - default -> LOGGER.info("Unknown copy command."); + case COPY_TITLE -> + copyTitle(); + case COPY_KEY -> + copyKey(); + case COPY_CITE_KEY -> + copyCiteKey(); + case COPY_KEY_AND_TITLE -> + copyKeyAndTitle(); + case COPY_KEY_AND_LINK -> + copyKeyAndLink(); + case COPY_DOI, COPY_DOI_URL -> + copyDoi(); + default -> + LOGGER.info("Unknown copy command."); } } @@ -94,47 +103,20 @@ private void copyTitle() { } } - private void copyKey() { - List entries = stateManager.getSelectedEntries(); - - // Collect all non-null keys. - List keys = entries.stream() - .filter(entry -> entry.getCitationKey().isPresent()) - .map(entry -> entry.getCitationKey().get()) - .collect(Collectors.toList()); - - if (keys.isEmpty()) { - dialogService.notify(Localization.lang("None of the selected entries have citation keys.")); - return; - } - - final String copiedKeys = String.join(",", keys); - clipBoardManager.setContent(copiedKeys); - - if (keys.size() == entries.size()) { - // All entries had keys. - dialogService.notify(Localization.lang("Copied '%0' to clipboard.", - JabRefDialogService.shortenDialogMessage(copiedKeys))); - } else { - dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined citation key.", - Integer.toString(entries.size() - keys.size()), Integer.toString(entries.size()))); - } - } - private void copyDoi() { List entries = stateManager.getSelectedEntries(); // Collect all non-null DOI or DOI urls if (action == StandardActions.COPY_DOI_URL) { copyDoiList(entries.stream() - .filter(entry -> entry.getDOI().isPresent()) - .map(entry -> entry.getDOI().get().getURIAsASCIIString()) - .collect(Collectors.toList()), entries.size()); + .filter(entry -> entry.getDOI().isPresent()) + .map(entry -> entry.getDOI().get().getURIAsASCIIString()) + .collect(Collectors.toList()), entries.size()); } else { copyDoiList(entries.stream() - .filter(entry -> entry.getDOI().isPresent()) - .map(entry -> entry.getDOI().get().getDOI()) - .collect(Collectors.toList()), entries.size()); + .filter(entry -> entry.getDOI().isPresent()) + .map(entry -> entry.getDOI().get().getDOI()) + .collect(Collectors.toList()), entries.size()); } } @@ -157,7 +139,7 @@ private void copyDoiList(List dois, int size) { } } - private void copyCiteKey() { + private void doCopyKey(Function, String> mapKeyList) { List entries = stateManager.getSelectedEntries(); // Collect all non-null keys. @@ -171,23 +153,31 @@ private void copyCiteKey() { return; } - String citeCommand = Optional.ofNullable(preferencesService.getExternalApplicationsPreferences().getCiteCommand()) - .filter(cite -> cite.contains("\\")) // must contain \ - .orElse("\\cite"); + String clipBoardContent = mapKeyList.apply(keys); - final String copiedCiteCommand = citeCommand + "{" + String.join(",", keys) + '}'; - clipBoardManager.setContent(copiedCiteCommand); + clipBoardManager.setContent(clipBoardContent); if (keys.size() == entries.size()) { // All entries had keys. dialogService.notify(Localization.lang("Copied '%0' to clipboard.", - JabRefDialogService.shortenDialogMessage(copiedCiteCommand))); + JabRefDialogService.shortenDialogMessage(clipBoardContent))); } else { dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined citation key.", Integer.toString(entries.size() - keys.size()), Integer.toString(entries.size()))); } } + private void copyCiteKey() { + doCopyKey(keys -> { + CitationCommandString citeCommand = preferencesService.getExternalApplicationsPreferences().getCiteCommand(); + return citeCommand.prefix() + String.join(citeCommand.delimiter(), keys) + citeCommand.suffix(); + }); + } + + private void copyKey() { + doCopyKey(keys -> String.join(preferencesService.getExternalApplicationsPreferences().getCiteCommand().delimiter(), keys)); + } + private void copyKeyAndTitle() { List entries = stateManager.getSelectedEntries(); @@ -208,7 +198,9 @@ private void copyKeyAndTitle() { for (BibEntry entry : entries) { if (entry.hasCitationKey()) { entriesWithKeys++; - keyAndTitle.append(layout.doLayout(entry, stateManager.getActiveDatabase().get().getDatabase())); + stateManager.getActiveDatabase() + .map(BibDatabaseContext::getDatabase) + .ifPresent(bibDatabase -> keyAndTitle.append(layout.doLayout(entry, bibDatabase))); } } @@ -242,7 +234,7 @@ private void copyKeyAndLink() { List entriesWithKey = entries.stream() .filter(BibEntry::hasCitationKey) - .collect(Collectors.toList()); + .toList(); if (entriesWithKey.isEmpty()) { dialogService.notify(Localization.lang("None of the selected entries have citation keys.")); @@ -250,7 +242,10 @@ private void copyKeyAndLink() { } for (BibEntry entry : entriesWithKey) { - String key = entry.getCitationKey().get(); + String key = entry.getCitationKey().orElse(""); + if (LOGGER.isDebugEnabled() && key.isEmpty()) { + LOGGER.debug("entry {} had no citation key, but it should have had one", entry); + } String url = entry.getField(StandardField.URL).orElse(""); keyAndLink.append(url.isEmpty() ? key : String.format("%s", url, key)); keyAndLink.append(OS.NEWLINE); diff --git a/src/main/java/org/jabref/gui/keyboard/KeyBinding.java b/src/main/java/org/jabref/gui/keyboard/KeyBinding.java index 5706d55cc7b..506d3fc40e4 100644 --- a/src/main/java/org/jabref/gui/keyboard/KeyBinding.java +++ b/src/main/java/org/jabref/gui/keyboard/KeyBinding.java @@ -32,7 +32,10 @@ public enum KeyBinding { CLOSE("Close dialog", Localization.lang("Close dialog"), "Esc", KeyBindingCategory.VIEW), COPY("Copy", Localization.lang("Copy"), "ctrl+C", KeyBindingCategory.EDIT), COPY_TITLE("Copy title", Localization.lang("Copy title"), "ctrl+shift+alt+T", KeyBindingCategory.EDIT), - COPY_CITE_CITATION_KEY("Copy \\cite{citation key}", Localization.lang("Copy \\cite{citation key}"), "ctrl+K", KeyBindingCategory.EDIT), + + // We migrated from "Copy \\cite{citation key}" to "Copy citation key with configured cite command", therefore we keep the "old string" for backwards comppatibility + COPY_CITE_CITATION_KEY("Copy \\cite{citation key}", Localization.lang("Copy citation key with configured cite command"), "ctrl+K", KeyBindingCategory.EDIT), + COPY_CITATION_KEY("Copy citation key", Localization.lang("Copy citation key"), "ctrl+shift+K", KeyBindingCategory.EDIT), COPY_CITATION_KEY_AND_TITLE("Copy citation key and title", Localization.lang("Copy citation key and title"), "ctrl+shift+alt+K", KeyBindingCategory.EDIT), COPY_CITATION_KEY_AND_LINK("Copy citation key and link", Localization.lang("Copy citation key and link"), "ctrl+alt+K", KeyBindingCategory.EDIT), diff --git a/src/main/java/org/jabref/gui/preferences/external/ExternalTabViewModel.java b/src/main/java/org/jabref/gui/preferences/external/ExternalTabViewModel.java index f8e7f96993c..365b7b694d0 100644 --- a/src/main/java/org/jabref/gui/preferences/external/ExternalTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/external/ExternalTabViewModel.java @@ -22,6 +22,7 @@ import org.jabref.gui.push.PushToEmacs; import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.logic.l10n.Localization; +import org.jabref.logic.push.CitationCommandString; import org.jabref.model.strings.StringUtil; import org.jabref.preferences.ExternalApplicationsPreferences; import org.jabref.preferences.PreferencesService; @@ -97,7 +98,8 @@ public void setValues() { PushToApplications.getApplicationByName(initialPushToApplicationPreferences.getActiveApplicationName(), dialogService, preferences) .orElse(new PushToEmacs(dialogService, preferences))); - citeCommandProperty.setValue(initialExternalApplicationPreferences.getCiteCommand()); + citeCommandProperty.setValue(initialExternalApplicationPreferences.getCiteCommand().toString()); + useCustomTerminalProperty.setValue(initialExternalApplicationPreferences.useCustomTerminal()); customTerminalCommandProperty.setValue(initialExternalApplicationPreferences.getCustomTerminalCommand()); useCustomFileBrowserProperty.setValue(initialExternalApplicationPreferences.useCustomFileBrowser()); @@ -110,7 +112,7 @@ public void storeSettings() { ExternalApplicationsPreferences externalPreferences = preferences.getExternalApplicationsPreferences(); externalPreferences.setEMailSubject(eMailReferenceSubjectProperty.getValue()); externalPreferences.setAutoOpenEmailAttachmentsFolder(autoOpenAttachedFoldersProperty.getValue()); - externalPreferences.setCiteCommand(citeCommandProperty.getValue()); + externalPreferences.setCiteCommand(CitationCommandString.from(citeCommandProperty.getValue())); externalPreferences.setUseCustomTerminal(useCustomTerminalProperty.getValue()); externalPreferences.setCustomTerminalCommand(customTerminalCommandProperty.getValue()); externalPreferences.setUseCustomFileBrowser(useCustomFileBrowserProperty.getValue()); @@ -229,6 +231,6 @@ public StringProperty customFileBrowserCommandProperty() { } public void resetCiteCommandToDefault() { - this.citeCommandProperty.setValue(preferences.getExternalApplicationsPreferences().getDefaultCiteCommand()); + this.citeCommandProperty.setValue(preferences.getExternalApplicationsPreferences().getDefaultCiteCommand().toString()); } } diff --git a/src/main/java/org/jabref/gui/push/AbstractPushToApplication.java b/src/main/java/org/jabref/gui/push/AbstractPushToApplication.java index aa84c74d3e9..3de3e190432 100644 --- a/src/main/java/org/jabref/gui/push/AbstractPushToApplication.java +++ b/src/main/java/org/jabref/gui/push/AbstractPushToApplication.java @@ -26,9 +26,6 @@ public abstract class AbstractPushToApplication implements PushToApplication { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPushToApplication.class); - private static final String CITE_KEY1 = "key1"; - private static final String CITE_KEY2 = "key2"; - protected boolean couldNotCall; // Set to true in case the command could not be executed, e.g., if the file is not found protected boolean couldNotPush; // Set to true in case the tunnel to the program (if one is used) does not operate protected boolean notDefined; // Set to true if the corresponding path is not defined in the preferences @@ -38,36 +35,11 @@ public abstract class AbstractPushToApplication implements PushToApplication { protected final DialogService dialogService; protected final PreferencesService preferencesService; - private String cachedCiteCommand; - private String cachedCitePrefix; - private String cachedCiteSuffix; - private String cachedCiteDelimiter; - public AbstractPushToApplication(DialogService dialogService, PreferencesService preferencesService) { this.dialogService = dialogService; this.preferencesService = preferencesService; } - private void dissectCiteCommand() { - String preferencesCiteCommand = preferencesService.getExternalApplicationsPreferences().getCiteCommand(); - - if (preferencesCiteCommand != null && preferencesCiteCommand.equals(cachedCiteCommand)) { - return; - } - - cachedCiteCommand = preferencesCiteCommand; - - int indexKey1 = cachedCiteCommand.indexOf(CITE_KEY1); - int indexKey2 = cachedCiteCommand.indexOf(CITE_KEY2); - if (indexKey1 < 0 || indexKey2 < 0 || indexKey2 < (indexKey1 + CITE_KEY1.length())) { - return; - } - - cachedCitePrefix = preferencesCiteCommand.substring(0, indexKey1); - cachedCiteDelimiter = preferencesCiteCommand.substring(preferencesCiteCommand.lastIndexOf(CITE_KEY1) + CITE_KEY1.length(), indexKey2); - cachedCiteSuffix = preferencesCiteCommand.substring(preferencesCiteCommand.lastIndexOf(CITE_KEY2) + CITE_KEY2.length()); - } - @Override public JabRefIcon getApplicationIcon() { return IconTheme.JabRefIcons.APPLICATION_GENERIC; @@ -170,18 +142,15 @@ protected String getCommandName() { } protected String getCitePrefix() { - dissectCiteCommand(); - return cachedCitePrefix; + return preferencesService.getExternalApplicationsPreferences().getCiteCommand().prefix(); } public String getDelimiter() { - dissectCiteCommand(); - return cachedCiteDelimiter; + return preferencesService.getExternalApplicationsPreferences().getCiteCommand().delimiter(); } protected String getCiteSuffix() { - dissectCiteCommand(); - return cachedCiteSuffix; + return preferencesService.getExternalApplicationsPreferences().getCiteCommand().suffix(); } @Override diff --git a/src/main/java/org/jabref/logic/push/CitationCommandString.java b/src/main/java/org/jabref/logic/push/CitationCommandString.java new file mode 100644 index 00000000000..8a645e51fba --- /dev/null +++ b/src/main/java/org/jabref/logic/push/CitationCommandString.java @@ -0,0 +1,34 @@ +package org.jabref.logic.push; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public record CitationCommandString(String prefix, String delimiter, String suffix) { + private static final Logger LOGGER = LoggerFactory.getLogger(CitationCommandString.class); + private static final String CITE_KEY1 = "key1"; + private static final String CITE_KEY2 = "key2"; + + @Override + public String toString() { + return prefix + CITE_KEY1 + delimiter + CITE_KEY2 + suffix; + } + + public static CitationCommandString from(String completeCiteCommand) { + int indexKey1 = completeCiteCommand.indexOf(CITE_KEY1); + int indexKey2 = completeCiteCommand.indexOf(CITE_KEY2); + if (indexKey1 < 0 || indexKey2 < 0 || indexKey2 < (indexKey1 + CITE_KEY1.length())) { + LOGGER.info("Wrong indexes {} {} for completeCiteCommand {}. Using default delimiter and suffix.", indexKey1, indexKey2, completeCiteCommand); + if (completeCiteCommand.isEmpty()) { + completeCiteCommand = "\\cite{"; + } else if (!completeCiteCommand.endsWith("{")) { + completeCiteCommand += "{"; + } + return new CitationCommandString(completeCiteCommand, ",", "}"); + } + + String prefix = completeCiteCommand.substring(0, indexKey1); + String delim = completeCiteCommand.substring(completeCiteCommand.lastIndexOf(CITE_KEY1) + CITE_KEY1.length(), indexKey2); + String suffix = completeCiteCommand.substring(completeCiteCommand.lastIndexOf(CITE_KEY2) + CITE_KEY2.length()); + return new CitationCommandString(prefix, delim, suffix); + } +} diff --git a/src/main/java/org/jabref/preferences/ExternalApplicationsPreferences.java b/src/main/java/org/jabref/preferences/ExternalApplicationsPreferences.java index 45670b68a28..992038c80f3 100644 --- a/src/main/java/org/jabref/preferences/ExternalApplicationsPreferences.java +++ b/src/main/java/org/jabref/preferences/ExternalApplicationsPreferences.java @@ -1,17 +1,21 @@ package org.jabref.preferences; import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; +import org.jabref.logic.push.CitationCommandString; + public class ExternalApplicationsPreferences { private final StringProperty eMailSubject; private final BooleanProperty shouldAutoOpenEmailAttachmentsFolder; - private final StringProperty citeCommand; + private final ObjectProperty citeCommand; - private final StringProperty defaultCiteCommand; + private final ObjectProperty defaultCiteCommand; private final BooleanProperty useCustomTerminal; private final StringProperty customTerminalCommand; @@ -21,8 +25,8 @@ public class ExternalApplicationsPreferences { public ExternalApplicationsPreferences(String eMailSubject, boolean shouldAutoOpenEmailAttachmentsFolder, - String citeCommand, - String defaultCiteCommand, + CitationCommandString citeCommand, + CitationCommandString defaultCiteCommand, boolean useCustomTerminal, String customTerminalCommand, boolean useCustomFileBrowser, @@ -31,8 +35,8 @@ public ExternalApplicationsPreferences(String eMailSubject, this.eMailSubject = new SimpleStringProperty(eMailSubject); this.shouldAutoOpenEmailAttachmentsFolder = new SimpleBooleanProperty(shouldAutoOpenEmailAttachmentsFolder); - this.citeCommand = new SimpleStringProperty(citeCommand); - this.defaultCiteCommand = new SimpleStringProperty(defaultCiteCommand); + this.citeCommand = new SimpleObjectProperty<>(citeCommand); + this.defaultCiteCommand = new SimpleObjectProperty<>(defaultCiteCommand); this.useCustomTerminal = new SimpleBooleanProperty(useCustomTerminal); this.customTerminalCommand = new SimpleStringProperty(customTerminalCommand); this.useCustomFileBrowser = new SimpleBooleanProperty(useCustomFileBrowser); @@ -64,15 +68,15 @@ public void setAutoOpenEmailAttachmentsFolder(boolean shouldAutoOpenEmailAttachm this.shouldAutoOpenEmailAttachmentsFolder.set(shouldAutoOpenEmailAttachmentsFolder); } - public String getCiteCommand() { + public CitationCommandString getCiteCommand() { return citeCommand.get(); } - public StringProperty citeCommandProperty() { + public ObjectProperty citeCommandProperty() { return citeCommand; } - public void setCiteCommand(String citeCommand) { + public void setCiteCommand(CitationCommandString citeCommand) { this.citeCommand.set(citeCommand); } @@ -136,7 +140,7 @@ public void setKindleEmail(String kindleEmail) { this.kindleEmail.set(kindleEmail); } - public String getDefaultCiteCommand() { + public CitationCommandString getDefaultCiteCommand() { return defaultCiteCommand.getValue(); } } diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index 28070ab4ed7..90c6bd7b87f 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -97,6 +97,7 @@ import org.jabref.logic.preview.PreviewLayout; import org.jabref.logic.protectedterms.ProtectedTermsLoader; import org.jabref.logic.protectedterms.ProtectedTermsPreferences; +import org.jabref.logic.push.CitationCommandString; import org.jabref.logic.remote.RemotePreferences; import org.jabref.logic.shared.prefs.SharedDatabasePreferences; import org.jabref.logic.shared.security.Password; @@ -1670,7 +1671,7 @@ public void storeGlobalCitationKeyPattern(GlobalCitationKeyPattern pattern) { || pattern.getDefaultValue().isEmpty()) { put(DEFAULT_CITATION_KEY_PATTERN, ""); } else { - put(DEFAULT_CITATION_KEY_PATTERN, pattern.getDefaultValue().get(0)); + put(DEFAULT_CITATION_KEY_PATTERN, pattern.getDefaultValue().getFirst()); } // Store overridden definitions to Preferences. @@ -1685,7 +1686,7 @@ public void storeGlobalCitationKeyPattern(GlobalCitationKeyPattern pattern) { for (EntryType entryType : pattern.getAllKeys()) { if (!pattern.isDefaultValue(entryType)) { // first entry in the map is the full pattern - preferences.put(entryType.getName(), pattern.getValue(entryType).get(0)); + preferences.put(entryType.getName(), pattern.getValue(entryType).getFirst()); } } } @@ -1810,8 +1811,8 @@ public ExternalApplicationsPreferences getExternalApplicationsPreferences() { externalApplicationsPreferences = new ExternalApplicationsPreferences( get(EMAIL_SUBJECT), getBoolean(OPEN_FOLDERS_OF_ATTACHED_FILES), - get(CITE_COMMAND), - (String) defaults.get(CITE_COMMAND), + CitationCommandString.from(get(CITE_COMMAND)), + CitationCommandString.from((String) defaults.get(CITE_COMMAND)), !getBoolean(USE_DEFAULT_CONSOLE_APPLICATION), // mind the ! get(CONSOLE_COMMAND), !getBoolean(USE_DEFAULT_FILE_BROWSER_APPLICATION), // mind the ! @@ -1823,7 +1824,7 @@ public ExternalApplicationsPreferences getExternalApplicationsPreferences() { EasyBind.listen(externalApplicationsPreferences.autoOpenEmailAttachmentsFolderProperty(), (obs, oldValue, newValue) -> putBoolean(OPEN_FOLDERS_OF_ATTACHED_FILES, newValue)); EasyBind.listen(externalApplicationsPreferences.citeCommandProperty(), - (obs, oldValue, newValue) -> put(CITE_COMMAND, newValue)); + (obs, oldValue, newValue) -> put(CITE_COMMAND, newValue.toString())); EasyBind.listen(externalApplicationsPreferences.useCustomTerminalProperty(), (obs, oldValue, newValue) -> putBoolean(USE_DEFAULT_CONSOLE_APPLICATION, !newValue)); // mind the ! EasyBind.listen(externalApplicationsPreferences.customTerminalCommandProperty(), @@ -1838,6 +1839,7 @@ public ExternalApplicationsPreferences getExternalApplicationsPreferences() { return externalApplicationsPreferences; } + //************************************************************************************************************* // Main table and search dialog preferences //************************************************************************************************************* @@ -2283,8 +2285,8 @@ private void storeExportSaveOrder(SaveOrder saveOrder) { long saveOrderCount = saveOrder.getSortCriteria().size(); if (saveOrderCount >= 1) { - put(EXPORT_PRIMARY_SORT_FIELD, saveOrder.getSortCriteria().get(0).field.getName()); - putBoolean(EXPORT_PRIMARY_SORT_DESCENDING, saveOrder.getSortCriteria().get(0).descending); + put(EXPORT_PRIMARY_SORT_FIELD, saveOrder.getSortCriteria().getFirst().field.getName()); + putBoolean(EXPORT_PRIMARY_SORT_DESCENDING, saveOrder.getSortCriteria().getFirst().descending); } else { put(EXPORT_PRIMARY_SORT_FIELD, ""); putBoolean(EXPORT_PRIMARY_SORT_DESCENDING, false); @@ -2337,7 +2339,7 @@ private List getCustomExportFormats() { for (String toImport : getSeries(CUSTOM_EXPORT_FORMAT)) { List formatData = convertStringToList(toImport); TemplateExporter format = new TemplateExporter( - formatData.get(EXPORTER_NAME_INDEX), + formatData.getFirst(), formatData.get(EXPORTER_FILENAME_INDEX), formatData.get(EXPORTER_EXTENSION_INDEX), layoutPreferences, @@ -2354,10 +2356,10 @@ private void storeCustomExportFormats(List exporters) { } else { for (int i = 0; i < exporters.size(); i++) { List exporterData = new ArrayList<>(); - exporterData.add(EXPORTER_NAME_INDEX, exporters.get(i).getName()); + exporterData.addFirst(exporters.get(i).getName()); exporterData.add(EXPORTER_FILENAME_INDEX, exporters.get(i).getLayoutFileName()); // Only stores the first extension associated with FileType - exporterData.add(EXPORTER_EXTENSION_INDEX, exporters.get(i).getFileType().getExtensions().get(0)); + exporterData.add(EXPORTER_EXTENSION_INDEX, exporters.get(i).getFileType().getExtensions().getFirst()); putStringList(CUSTOM_EXPORT_FORMAT + i, exporterData); } purgeSeries(CUSTOM_EXPORT_FORMAT, exporters.size()); @@ -2861,7 +2863,7 @@ private Set getCustomImportFormats() { importers.add(new CustomImporter(importerString.get(3), importerString.get(2))); } } catch (Exception e) { - LOGGER.warn("Could not load {} from preferences. Will ignore.", importerString.get(0), e); + LOGGER.warn("Could not load {} from preferences. Will ignore.", importerString.getFirst(), e); } } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 019dcfcd8ae..25dde6cd490 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -170,12 +170,12 @@ Copied=Copied Copy=Copy Copy\ title=Copy title -Copy\ \\cite{citation\ key}=Copy \\cite{citation key} Copy\ citation\ (html)=Copy citation (html) Copy\ citation\ (text)=Copy citation (text) Copy\ citation\ key=Copy citation key Copy\ citation\ key\ and\ link=Copy citation key and link Copy\ citation\ key\ and\ title=Copy citation key and title +Copy\ citation\ key\ with\ configured\ cite\ command=Copy citation key with configured cite command Copy\ to\ clipboard=Copy to clipboard diff --git a/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java b/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java index d5fe209a3a0..ba3c8465be1 100644 --- a/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java +++ b/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java @@ -14,11 +14,13 @@ import org.jabref.gui.actions.StandardActions; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.l10n.Localization; +import org.jabref.logic.push.CitationCommandString; import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.StandardEntryType; +import org.jabref.preferences.ExternalApplicationsPreferences; import org.jabref.preferences.PreferencesService; import org.junit.jupiter.api.BeforeEach; @@ -58,6 +60,10 @@ public void setUp() { titles.add(title); keys.add("abc"); dois.add("10.1145/3377811.3380330"); + + ExternalApplicationsPreferences externalApplicationsPreferences = mock(ExternalApplicationsPreferences.class); + when(externalApplicationsPreferences.getCiteCommand()).thenReturn(new CitationCommandString("\\cite{", ",", "}")); + when(preferencesService.getExternalApplicationsPreferences()).thenReturn(externalApplicationsPreferences); } @Test diff --git a/src/test/java/org/jabref/gui/push/PushToEmacsTest.java b/src/test/java/org/jabref/gui/push/PushToEmacsTest.java index 830cfff46b5..7e298a2ec6e 100644 --- a/src/test/java/org/jabref/gui/push/PushToEmacsTest.java +++ b/src/test/java/org/jabref/gui/push/PushToEmacsTest.java @@ -41,7 +41,7 @@ public void setup() { when(preferencesService.getPushToApplicationPreferences()).thenReturn(pushToApplicationPreferences); ExternalApplicationsPreferences externalApplicationsPreferences = mock(ExternalApplicationsPreferences.class); - when(externalApplicationsPreferences.getCiteCommand()).thenReturn("\\cite{key1,key2}"); + when(externalApplicationsPreferences.getCiteCommand().toString()).thenReturn("\\cite{key1,key2}"); when(preferencesService.getExternalApplicationsPreferences()).thenReturn(externalApplicationsPreferences); pushToEmacs = new PushToEmacs(dialogService, preferencesService); diff --git a/src/test/java/org/jabref/preferences/CitationCommandStringTest.java b/src/test/java/org/jabref/preferences/CitationCommandStringTest.java new file mode 100644 index 00000000000..0bca17e7eed --- /dev/null +++ b/src/test/java/org/jabref/preferences/CitationCommandStringTest.java @@ -0,0 +1,44 @@ +package org.jabref.preferences; + +import java.util.stream.Stream; + +import org.jabref.logic.push.CitationCommandString; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class CitationCommandStringTest { + + @Test + void testToString() { + assertEquals("\\cite{key1,key2}", new CitationCommandString("\\cite{", ",", "}").toString()); + } + + public static Stream from() { + return Stream.of( + Arguments.of( + new CitationCommandString("\\cite{", ",", "}"), + "\\cite{key1,key2}" + ), + Arguments.of( + new CitationCommandString("\\cite{", ",", "}"), + "\\cite" + ), + // We could do better, but this is very seldom + Arguments.of( + new CitationCommandString("\\cite[key1,key]{", ",", "}"), + "\\cite[key1,key]" + ) + ); + } + + @ParameterizedTest + @MethodSource + void from(CitationCommandString expected, String input) { + assertEquals(expected, CitationCommandString.from(input)); + } +} From 60ad7c14932cd39bd9ff6a8d4630078461ffd2ca Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 22 Dec 2023 16:30:07 +0100 Subject: [PATCH 087/144] New Crowdin updates (#10713) * New translations jabref_en.properties (French) * New translations jabref_en.properties (Spanish) * New translations jabref_en.properties (Arabic) * New translations jabref_en.properties (Danish) * New translations jabref_en.properties (German) * New translations jabref_en.properties (Greek) * New translations jabref_en.properties (Finnish) * New translations jabref_en.properties (Italian) * New translations jabref_en.properties (Japanese) * New translations jabref_en.properties (Korean) * New translations jabref_en.properties (Dutch) * New translations jabref_en.properties (Norwegian) * New translations jabref_en.properties (Polish) * New translations jabref_en.properties (Portuguese) * New translations jabref_en.properties (Russian) * New translations jabref_en.properties (Swedish) * New translations jabref_en.properties (Turkish) * New translations jabref_en.properties (Ukrainian) * New translations jabref_en.properties (Chinese Simplified) * New translations jabref_en.properties (Chinese Traditional) * New translations jabref_en.properties (Vietnamese) * New translations jabref_en.properties (Portuguese, Brazilian) * New translations jabref_en.properties (Indonesian) * New translations jabref_en.properties (Persian) * New translations jabref_en.properties (Tagalog) --- src/main/resources/l10n/JabRef_ar.properties | 11 ++++ src/main/resources/l10n/JabRef_da.properties | 13 ++++- src/main/resources/l10n/JabRef_de.properties | 42 ++++++++++++-- src/main/resources/l10n/JabRef_el.properties | 14 ++++- src/main/resources/l10n/JabRef_es.properties | 58 +++++++++++++++++-- src/main/resources/l10n/JabRef_fa.properties | 11 ++++ src/main/resources/l10n/JabRef_fi.properties | 11 ++++ src/main/resources/l10n/JabRef_fr.properties | 40 +++++++++++-- src/main/resources/l10n/JabRef_id.properties | 39 ++++++++++++- src/main/resources/l10n/JabRef_it.properties | 40 +++++++++++-- src/main/resources/l10n/JabRef_ja.properties | 14 ++++- src/main/resources/l10n/JabRef_ko.properties | 14 ++++- src/main/resources/l10n/JabRef_nl.properties | 15 +++-- src/main/resources/l10n/JabRef_no.properties | 13 ++++- src/main/resources/l10n/JabRef_pl.properties | 15 +++-- src/main/resources/l10n/JabRef_pt.properties | 14 ++++- .../resources/l10n/JabRef_pt_BR.properties | 15 +++-- src/main/resources/l10n/JabRef_ru.properties | 14 ++++- src/main/resources/l10n/JabRef_sv.properties | 14 ++++- src/main/resources/l10n/JabRef_tl.properties | 13 ++++- src/main/resources/l10n/JabRef_tr.properties | 15 +++-- src/main/resources/l10n/JabRef_uk.properties | 11 ++++ src/main/resources/l10n/JabRef_vi.properties | 13 ++++- .../resources/l10n/JabRef_zh_CN.properties | 15 +++-- .../resources/l10n/JabRef_zh_TW.properties | 12 +++- 25 files changed, 421 insertions(+), 65 deletions(-) diff --git a/src/main/resources/l10n/JabRef_ar.properties b/src/main/resources/l10n/JabRef_ar.properties index e8ee52e3281..fe50ef76cab 100644 --- a/src/main/resources/l10n/JabRef_ar.properties +++ b/src/main/resources/l10n/JabRef_ar.properties @@ -89,6 +89,7 @@ Cancel=إلغاء + Change\ case=غيّر حجم الأحرف Change\ entry\ type=تغيير نوع المرجع @@ -654,6 +655,10 @@ Import\ preferences\ from\ a\ file=استيراد الإعدادات من ملف + + + + Add\ to\ current\ library=إضافة إلى المكتبة الحالية @@ -729,6 +734,12 @@ Copy\ or\ move\ the\ content\ of\ one\ field\ to\ another=نسخ أو نقل م + + + + + + diff --git a/src/main/resources/l10n/JabRef_da.properties b/src/main/resources/l10n/JabRef_da.properties index 76c4ec3b2ea..042f435f82e 100644 --- a/src/main/resources/l10n/JabRef_da.properties +++ b/src/main/resources/l10n/JabRef_da.properties @@ -103,6 +103,7 @@ Case\ sensitive=Skeln mellem store og små bogstaver change\ assignment\ of\ entries=ændre tildeling af poster + Change\ case=Ændre store/små bogstaver Change\ entry\ type=Ændre posttype @@ -383,7 +384,6 @@ Language=Sprog Last\ modified=Sidst ændret -Listen\ for\ remote\ operation\ on\ port=Lyt efter fjernoperationer på port Manage\ custom\ exports=Opsæt eksterne eksportfiltre @@ -725,7 +725,6 @@ Error\ while\ fetching\ from\ %0=Fejl under hentning fra %0 Unable\ to\ open\ link.=Kan ikke åbne link. MIME\ type=MIME-type -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Denne funktion tillader, at flere filer kan åbnes eller importeres i en allerede kørende JabRef i stedet for at åbne programmet påny. For eksempel er dette praktisk, når du åbner filer i JabRef fra din web browser. Bemærk at dette vil forhindre dig i at køre mere end en instans af JabRef ad gangen. Rename\ field=Omdøb felt @@ -973,6 +972,10 @@ Find\ and\ replace=Søg og erstat + + + + Always\ add\ letter\ (a,\ b,\ ...)\ to\ generated\ keys=Tilføj altid bogstav (a, b, ...) til genererede nøgler Default\ pattern=Standardmønster @@ -1047,6 +1050,12 @@ Default\ pattern=Standardmønster + + + + + + diff --git a/src/main/resources/l10n/JabRef_de.properties b/src/main/resources/l10n/JabRef_de.properties index 4ef754ae16d..a540673fdbe 100644 --- a/src/main/resources/l10n/JabRef_de.properties +++ b/src/main/resources/l10n/JabRef_de.properties @@ -131,6 +131,8 @@ Case\ sensitive=Groß-/Kleinschreibung change\ assignment\ of\ entries=Änderung der zugewiesenen Einträge +Catalogues\ used\ for\ 'Search\ Selected'=Kataloge für 'In Ausgewählten suchen' + Change\ case=Groß- und Kleinschreibung Change\ entry\ type=Eintragstyp ändern @@ -168,12 +170,12 @@ Copied=Kopiert Copy=Kopieren Copy\ title=Kopiere Titel -Copy\ \\cite{citation\ key}=Kopiere \\cite{citation key} Copy\ citation\ (html)=Zitat kopieren (html) Copy\ citation\ (text)=Zitat kopieren (Text) Copy\ citation\ key=Kopiere Zitationsschlüssel Copy\ citation\ key\ and\ link=Zitationscchlüssel und Titel kopieren Copy\ citation\ key\ and\ title=Zitationscchlüssel und Titel kopieren +Copy\ citation\ key\ with\ configured\ cite\ command=Zitierschlüssel mit konfiguriertem Zitierbefehl kopieren Copy\ to\ clipboard=In die Zwischenablage kopieren @@ -477,7 +479,6 @@ Year=Jahr ISSN\ or\ journal\ name\ required\ for\ fetching\ journal\ information=ISSN oder Journalname erforderlich für das Abrufen von Journalinformationen Fetch\ journal\ information\ online\ to\ show=Journalinformationen online abrufen, um sie anzuzeigen Allow\ sending\ ISSN\ to\ a\ JabRef\ online\ service\ (SCimago)\ for\ fetching\ journal\ information=Erlaube das Senden der ISSN an einen JabRef Online-Dienst (SCimago) zum Abrufen von Journalinformationen -Please\ enable\ journal\ information\ fetching\ in\ %0\ >\ %1=Bitte aktivieren Sie das Abrufen von Zeitschrifteninformationen in %0 > %1 Categories=Kategorien ISSN=ISSN Publisher=Herausgeber @@ -520,7 +521,6 @@ Last\ modified=zuletzt geändert LaTeX\ AUX\ file\:=LaTeX AUX-Datei\: Link=Link -Listen\ for\ remote\ operation\ on\ port=Port nach externem Zugriff abhören Memory\ stick\ mode\ -\ Store\ preferences\ in\ 'jabref.xml'\ in\ the\ app\ folder.=Speicherstick-Modus - Speichere Einstellungen in 'jabref.xml' im App-Ordner. Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=Erweiterte Hinweise anzeigen (z.B. hilfreiche Tooltips, Vorschläge und Erklärungen) @@ -839,6 +839,7 @@ Search\ results\ from\ open\ libraries=Suchergebnisse aus offenen Bibliotheken Select\ all=Alle auswählen Select\ new\ encoding=Neue Kodierung auswählen +Select\ library=Bibliothek auswählen Select\ entry\ type=Eintragstyp auswählen Select\ file\ from\ ZIP-archive=Eintrag aus der ZIP-Archiv auswählen @@ -1008,7 +1009,6 @@ Citation\ key\ generator=Zitationsschlüsselgenerator Unable\ to\ open\ link.=Öffnen des Links nicht möglich MIME\ type=MIME-Typ -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Diese Funktion öffnet neue oder importierte Dateien in einer bereits laufenden Instanz von JabRef und nicht in einem neuen Fenster. Das ist beispielsweise nützlich, wenn Sie JabRef von einem Webbrowser aus starten. Beachten Sie, dass damit nicht mehr als eine Instanz von JabRef gestartet werden kann. Run\ fetcher=Fetcher ausführen Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=Zeile %0\: Beschädigter Zitationsschlüssel %1 gefunden. @@ -1557,6 +1557,7 @@ Looking\ up\ %0...\ -\ entry\ %1\ out\ of\ %2\ -\ found\ %3=Schlage %0 nach... - Audio\ CD=Audio CD British\ patent=Britisches Patent British\ patent\ request=Britischer Patentantrag +Bachelor's\ thesis=Bachelorarbeit Candidate\ thesis=Beliebige Abschlussarbeit Collaborator=Mitarbeiter Column=Spalte @@ -2039,12 +2040,28 @@ Matching=Passend Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=Wie --import, aber wird in den geöffneten Tab importiert Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=Ganzzahlen im Feld 'edition' im BibTeX-Modus erlauben +Please\ enter\ a\ name\ for\ the\ MIME\ type.=Bitte geben Sie einen Namen für den MIME-Typ ein. +Please\ enter\ a\ name\ for\ the\ extension.=Bitte geben Sie einen Namen für die Dateierweiterung ein. +Please\ enter\ a\ name.=Bitte geben Sie einen Namen ein. + +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ MIME\ type=Es existiert bereits ein externer Dateityp mit dem gleichen MIME-Typ +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ extension=Es existiert bereits ein externer Dateityp mit der gleichen Erweiterung +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ name=Es existiert bereits ein externer Dateityp mit dem gleichen Namen + + Search\ for\ citations\ in\ LaTeX\ files...=Suche nach Zitaten in LaTeX Dateien... LaTeX\ Citations\ Search\ Results=Suchergebnisse der Zitate aus LaTeX LaTeX\ files\ directory\:=LaTeX-Dateiverzeichnis\: LaTeX\ files\ found\:=LaTeX-Dateien gefunden\: files=Dateien Show\ 'LaTeX\ Citations'\ tab=Zeige 'LaTeX Zitate' Reiter +Show\ 'Scite'\ tab=Zeige 'Scite' Reiter +Search\ scite.ai\ for\ Smart\ Citations=Durchsuche scite.ai nach smarten Referenzen +See\ full\ report\ at\ [%0]=Vollständigen Bericht unter [%0] ansehen +No\ active\ entry=Kein aktiver Eintrag +This\ entry\ does\ not\ have\ a\ DOI=Dieser Eintrag hat keine DOI +Tallies\ for\ %0=Anzahl für %0 + LaTeX\ Citations=LaTeX-Zitate Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=Zitate für diesen Eintrag in LaTeX-Dateien suchen No\ citations\ found=Keine Zitate gefunden @@ -2084,6 +2101,7 @@ Font\ settings=Schrifteinstellungen Custom...=Benutzerdefiniert... Dark=Dunkel Light=Hell +Use\ System\ Preference=Systemeinstellung verwenden Please\ specify\ a\ css\ theme\ file.=Bitte geben Sie eine CSS-Design-Datei an. You\ must\ enter\ an\ integer\ value\ higher\ than\ 8.=Sie müssen einen Ganzzahlwert größer als 8 eingeben. Letters\ after\ duplicate\ generated\ keys=Buchstaben nach doppelten generierten Schlüsseln @@ -2602,3 +2620,19 @@ Help\ on\ external\ applications=Hilfe zu externen Anwendungen Identifier-based\ Web\ Search=Identifikator-basierte Websuche Pushing\ citations\ to\ TeXShop\ is\ only\ possible\ on\ macOS\!=Zitate in TeXShop zu übertragen ist nur auf macOS möglich\! + +Single\ instance=Einzelne Instanz +Enforce\ single\ JabRef\ instance\ (and\ allow\ remote\ operations)\ using\ port=Erzwinge eine einzige JabRef-Instanz (und erlaube Remote-Operationen) über Port + + +Enable\ Journal\ Information\ Fetching?=Journalinformationen abrufen? +Would\ you\ like\ to\ enable\ fetching\ of\ journal\ information?\ This\ can\ be\ changed\ later\ in\ %0\ >\ %1.=Möchten Sie das Abrufen von Journal-Informationen aktivieren? Dies kann später in %0 > %1 geändert werden. +Enable=Aktivieren +Keep\ disabled=Deaktiviert lassen + +Predatory\ journal\ %0\ found=Betrügerisches Journal %0 gefunden + +Hide\ user\ comments=Benutzerkommentare ausblenden +Show\ user\ comments\ field=Benutzerkommentarfeld anzeigen + +More\ options...=Weitere Optionen... diff --git a/src/main/resources/l10n/JabRef_el.properties b/src/main/resources/l10n/JabRef_el.properties index cd5f95e3942..8b1e418848f 100644 --- a/src/main/resources/l10n/JabRef_el.properties +++ b/src/main/resources/l10n/JabRef_el.properties @@ -131,6 +131,7 @@ Case\ sensitive=Διάκριση πεζών - κεφαλαίων change\ assignment\ of\ entries=αλλαγή ανάθεσης των καταχωρήσεων + Change\ case=Αλλαγή πεζών - κεφαλαίων Change\ entry\ type=Αλλαγή τύπου εγγραφής @@ -168,7 +169,6 @@ Copied=Αντιγράφηκε Copy=Αντιγραφή Copy\ title=Αντιγραφή τίτλου -Copy\ \\cite{citation\ key}=Αντιγραφή \\cite{citation key} Copy\ citation\ (html)=Αντιγραφή αναφοράς (σε μορφή HTML) Copy\ citation\ (text)=Αντιγραφή αναφοράς (σε μορφή κειμένου) Copy\ citation\ key=Αντιγραφή κλειδιού αναφοράς @@ -483,7 +483,6 @@ Last\ modified=Τελευταία τροποποίηση LaTeX\ AUX\ file\:=Αρχείο LaTeX AUX\: Link=Σύνδεση -Listen\ for\ remote\ operation\ on\ port=Αναμονή για απομακρυσμένη λειτουργία στη θύρα Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=Εμφάνιση προηγμένων υποδείξεων (π.χ. χρήσιμες συμβουλές, προτάσεις και επεξήγηση) @@ -958,7 +957,6 @@ Citation\ key\ generator=Δημιουργός κλειδιού αναφοράς Unable\ to\ open\ link.=Αδυναμία ανοίγματος συνδέσμου. MIME\ type=Τύπος MIME -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Το χαρακτηριστικό αυτό επιτρέπει το άνοιγμα ή την εισαγωγή νέων αρχείων σε ένα ήδη τρέχον παράθυρο JabRef αντί να ανοίγει νέο παράθυρο. Για παράδειγμα, αυτό είναι χρήσιμο όταν ανοίγετε ένα αρχείο JabRef από τον περιηγητή ιστού. Σημειώστε πως αυτό θα σας εμποδίσει από το να τρέξετε περισσότερα από ένα παράθυρα JabRef ταυτόχρονα. Run\ fetcher=Εκτέλεση ανακτήσεων Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=Γραμμή %0\: Βρέθηκε κατεστραμμένο κλειδί αναφοράς %1. @@ -1778,6 +1776,10 @@ Executing\ command\ "%0"...=Πραγματοποιείται εκτέλεση ε + + + + Always\ add\ letter\ (a,\ b,\ ...)\ to\ generated\ keys=Πάντα να προστίθεται γράμμα (a, b, ...) στα κλειδιά που δημιουργούνται Default\ pattern=Προεπιλεγμένο μοτίβο @@ -1874,3 +1876,9 @@ Use\ the\ field\ FJournal\ to\ store\ the\ full\ journal\ name\ for\ (un)abbrevi + + + + + + diff --git a/src/main/resources/l10n/JabRef_es.properties b/src/main/resources/l10n/JabRef_es.properties index 55445300901..8be3bf81603 100644 --- a/src/main/resources/l10n/JabRef_es.properties +++ b/src/main/resources/l10n/JabRef_es.properties @@ -131,6 +131,7 @@ Case\ sensitive=Sensible al uso de mayúsculas/minúsculas change\ assignment\ of\ entries=Cambiar asignación de entradas + Change\ case=Cambiar mayúsculas/minúsculas Change\ entry\ type=Cambiar tipo de entrada @@ -168,7 +169,6 @@ Copied=Copiado Copy=Copiar Copy\ title=Copiar título -Copy\ \\cite{citation\ key}=Copiar \\cite{citation key} Copy\ citation\ (html)=Copiar cita (html) Copy\ citation\ (text)=Copiar cita (texto) Copy\ citation\ key=Copiar clave de cita @@ -322,7 +322,13 @@ Export\ preferences\ to\ file=Exportar preferencias a archivo Export\ to\ clipboard=Exportar al portapapeles Export\ to\ text\ file.=Exportar a un archivo de texto. +Exporting\ %0=Exportando %0 +Could\ not\ export\ file\ '%0'\ (reason\:\ %1)=No se pudo exportar el archivo «%0» (motivo\: %1) +Unknown\ export\ format\ %0=Formato de exportación %0 desconocido +Importing\ %0=Importando %0 +Importing\ file\ %0\ as\ unknown\ format=Importando archivo %0 como formato desconocido +Format\ used\:\ %0=Formato utilizado\: %0 Extension=Extensión @@ -471,7 +477,6 @@ Year=Año ISSN\ or\ journal\ name\ required\ for\ fetching\ journal\ information=Se requiere ISSN o nombre de la revista para obtener la información de la revista Fetch\ journal\ information\ online\ to\ show=Obtenga información de la revista en línea para mostrarla Allow\ sending\ ISSN\ to\ a\ JabRef\ online\ service\ (SCimago)\ for\ fetching\ journal\ information=Permitir el envío de ISSN al servicio online de JabRef (SCimago) para obtener la información de la revista -Please\ enable\ journal\ information\ fetching\ in\ %0\ >\ %1=Por favor, habilite la obtención de información de revista en %0 > %1 Categories=Categorías ISSN=ISSN Publisher=Editor @@ -513,7 +518,6 @@ Last\ modified=Modificado por última vez LaTeX\ AUX\ file\:=Archivo AUX de LaTeX\: Link=Enlace -Listen\ for\ remote\ operation\ on\ port=Escuchar para operación remota en el puerto Memory\ stick\ mode\ -\ Store\ preferences\ in\ 'jabref.xml'\ in\ the\ app\ folder.=Modo de tarjeta de memoria - Almacena las preferencias en 'jabref.xml' en la carpeta de aplicaciones. Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=Mostrar consejos avanzados (es decir, consejos útiles, sugerencias y explicaciones) @@ -831,6 +835,7 @@ Search\ results\ from\ open\ libraries=Resultados de la búsqueda en las bibliot Select\ all=Seleccionar todo Select\ new\ encoding=Seleccionar nueva codificación +Select\ library=Seleccionar biblioteca Select\ entry\ type=Seleccionar tipo de entrada Select\ file\ from\ ZIP-archive=Seleccionar archivo desde archivo ZIP @@ -876,6 +881,9 @@ SourceTab\ error=Error de SourceTab User\ input\ via\ entry-editor\ in\ `{}bibtex\ source`\ tab\ led\ to\ failure.=Entrada de usuario a través del editor de entrada en la pestaña `{}bibtex source` condujo al error. Sort\ subgroups\ A-Z=Ordenar subgrupos A-Z +Sort\ subgroups\ Z-A=Ordenar subgrupos de Z a A +Sort\ subgroups\ by\ \#\ of\ entries\ (Descending)=Ordenar subgrupos por n.º de entradas (descendente) +Sort\ subgroups\ by\ \#\ of\ entries\ (Ascending)=Ordenar subgrupos por n.º de entradas (ascendente) source\ edit=editar fuente Special\ name\ formatters=Formateadores con nombre especial @@ -995,7 +1003,6 @@ Citation\ key\ generator=Generador de claves de cita Unable\ to\ open\ link.=No es posible abrir el enlace. MIME\ type=Tipo MIME -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Esta función permite que los nuevos archivos sean abiertos o importados en una instancia de JabRef que ya se esté ejecutando, en lugar de abrir una nueva instancia. Esto es útil, por ejemplo, cuando se abre un archivo en JabRef desde el navegador de internet. Tenga en cuenta que esto le impedirá ejecutar más de una instancia de JabRef a la vez. Run\ fetcher=Ejecutar fetcher Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=Renglón %0\: se encontró la clave de cita dañada %1. @@ -1010,6 +1017,7 @@ Cannot\ use\ port\ %0\ for\ remote\ operation;\ another\ application\ may\ be\ u Looking\ for\ full\ text\ document...=Buscando texto completo de documento... A\ local\ copy\ will\ be\ opened.=Se abrirá una copia local Error\ opening\ file=Error al abrir el archivo +Error\ opening\ file\ '%0'=Error al abrir el archivo «%0» Formatter\ not\ found\:\ %0=Formateador no encontrado\: %0 @@ -1058,7 +1066,7 @@ Please\ specify\ a\ file\ browser.=Especifique un explorador de archivos. Please\ specify\ a\ terminal\ application.=Especifique una aplicación de terminal. Use\ custom\ file\ browser=Usar el explorador de archivos personalizado Use\ custom\ terminal\ emulator=Usar el emulador de terminal personalizado -exportFormat=Formato de exportación +exportFormat=Formato de exportación Output\ file\ missing=Fichero de salida no encontrado The\ output\ option\ depends\ on\ a\ valid\ input\ option.=La opción de salida depende de una opción de entrada válida. Linked\ file\ name\ conventions=Convenciones de nombres de archivos vinculados @@ -1474,6 +1482,7 @@ changes\ all\ letters\ to\ lower\ case.=cambia todas las letras a minúsculas. CHANGES\ ALL\ LETTERS\ TO\ UPPER\ CASE.=CAMBIA TODAS LAS LETRAS A MAYÚSCULAS. Changes\ The\ First\ Letter\ Of\ All\ Words\ To\ Capital\ Case\ And\ The\ Remaining\ Letters\ To\ Lower\ Case.=Cambia la primera letra de todas las palabras a mayúscula y el resto a minúscula. Cleans\ up\ LaTeX\ code.=Limpia el código LaTeX +LaTeX\ Warning\:\ %0=Alerta de LaTeX\: %0 Converts\ HTML\ code\ to\ LaTeX\ code.=Convierte código HTML a código LaTeX HTML\ to\ Unicode=HTML a Unicode Converts\ HTML\ code\ to\ Unicode.=Convierte HTML a Unicode @@ -1516,6 +1525,7 @@ Identity=Identidad Clears\ the\ field\ completely.=Limpia el campo completamente. Main\ file\ directory=Carpeta del archivo principal +No\ library\ selected=No se seleccionó ninguna biblioteca This\ operation\ requires\ exactly\ one\ item\ to\ be\ selected.=Esta operación requiere seleccionar exactamente un elemento. Opening\ large\ number\ of\ files=A punto de abrir un número elevado de archivos @@ -1592,6 +1602,7 @@ Message=Mensaje MathSciNet\ Review=Revisión MathSciNet +Reset\ all=Restablecer todo Decryption\ not\ supported.=Desencriptación no soportada @@ -1703,6 +1714,7 @@ Check\ the\ event\ log\ to\ see\ all\ notifications=Revisa el registro de evento Host=Host/Servidor Port=Puerto Library=Biblioteca +Libraries=Bibliotecas User=Usuario Connect=Conectar Connection\ error=Error de conexión @@ -2007,12 +2019,16 @@ Matching=Emparejamiento Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=Igual que --import, pero se importará en la pestaña abierta Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=Permitir números enteros en el campo 'edition' en modo BibTeX + + + Search\ for\ citations\ in\ LaTeX\ files...=Buscar citas en archivos LaTeX... LaTeX\ Citations\ Search\ Results=Resultados de búsqueda de citas en los archivos LaTeX LaTeX\ files\ directory\:=Directorio de archivos de LaTeX\: LaTeX\ files\ found\:=Archivos de LaTeX encontrados\: files=archivos Show\ 'LaTeX\ Citations'\ tab=Mostrar pestaña 'Citas LaTeX' + LaTeX\ Citations=Citas LaTeX Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=Buscar citas a esta entrada en archivos LaTeX No\ citations\ found=No se han encontrado citas @@ -2043,11 +2059,15 @@ Add\ formatter\ to\ list=Añadir formateador a la lista Filter\ List=Lista de filtros Open\ files...=Abrir archivos... +Affected\ fields=Campos afectados Show\ preview\ as\ a\ tab\ in\ entry\ editor=Mostrar la vista previa como una pestaña en el editor de entradas Visual\ theme=Tema visual Overwrite\ existing\ keys=Sobrescribir las claves existentes Key\ patterns=Patrones clave Font\ settings=La configuración de la fuente +Custom...=Personalizado... +Dark=Oscuro +Light=Claro Please\ specify\ a\ css\ theme\ file.=Especifique un archivo CSS de tema. You\ must\ enter\ an\ integer\ value\ higher\ than\ 8.=Debe introducir un valor entero superior a 8. Letters\ after\ duplicate\ generated\ keys=Letras después de duplicar las claves generadas @@ -2286,6 +2306,7 @@ Custom\ import\ formats=Formatos de importación personalizados No\ list\ enabled=No hay lista habilitada Protect\ selection=Proteger selección +Unprotect\ selection=Selección no protegida Customized\ preview\ style=Estilo de previsualización personalizado Next\ preview\ style=Estilo de previsualización siguiente @@ -2312,6 +2333,8 @@ Error\ importing.\ See\ the\ error\ log\ for\ details.=Error al importar. Consul Error\ from\ import\:\ %0=Error de importación\: %0 Error\ reading\ PDF\ content\:\ %0=Error al leer el contenido PDF\: %0 +Bib\ entry\ was\ successfully\ imported=Se importó la entrada bibliográfica correctamente +File\ was\ successfully\ imported\ as\ a\ new\ entry=El archivo se importó correctamente como una entrada nueva Processing\ file\ %0=Procesando archivo %0 Export\ selected=Exportar seleccionados @@ -2357,6 +2380,9 @@ Others=Otros Recommended=Recomendado Authors\ and\ Title=Autoría y título +Catalog=Catálogo +Catalogs=Catálogos +Select\ Catalogs\:=Seleccionar catálogos\: Add\ Author\:=Añadir autor\: Add\ Query\:=Añadir consulta\: Add\ Research\ Question\:=Añadir pregunta de investigación\: @@ -2407,6 +2433,7 @@ Error\ downloading=Error al descargar No\ data\ was\ found\ for\ the\ identifier=No se encontró ningún dato correspondiente al identificador Server\ not\ available=Servidor no disponible +Look\ up\ identifier=Buscar identificador Identifier\ not\ found=Identificador no encontrado Custom\ API\ key=Clave API personalizada @@ -2460,7 +2487,10 @@ Do\ you\ want\ to\ recover\ the\ library\ from\ the\ backup\ file?=¿Quiere recu This\ could\ indicate\ that\ JabRef\ did\ not\ shut\ down\ cleanly\ last\ time\ the\ file\ was\ used.=Esto puede indicar que JabRef no se cerró correctamente la última vez que se usó ese archivo. +Library\ to\ import\ into=Biblioteca en la que importar +Enable\ web\ search=Activar búsqueda en la web +Web\ search\ disabled=Búsqueda en la web desactivada @@ -2473,7 +2503,10 @@ Delete\ %0\ files\ permanently\ from\ disk,\ or\ just\ remove\ the\ files\ from\ Error\ accessing\ file\ '%0'.=Error al acceder al archivo «%0». This\ operation\ requires\ selected\ linked\ files.=Esta operación requiere determinados archivos enlazados. +Create\ backup=Crear copia de respaldo +File\ "%0"\ cannot\ be\ added\!=No se puede añadir el archivo «%0». +Rename\ and\ add=Cambiar nombre y añadir Failed\ to\ download\ from\ URL=Error al descargar desde la URL @@ -2484,11 +2517,24 @@ Writing\ metadata\ to\ %0=Escribiendo metadatos a %0 Get\ more\ themes...=Obtener más temas... Add\ selected\ entries\ to\ database=Añadir entradas seleccionadas a la base de datos +The\ selected\ entry\ doesn't\ have\ a\ DOI\ linked\ to\ it.\ Lookup\ a\ DOI\ and\ try\ again.=La entrada seleccionada no tiene ningún DOI enlazado. Busque un DOI e inténtelo de nuevo. Cited\ By=Citado por Citing=Citando +No\ articles\ found=No se encontró ningún artículo Restart\ search=Reiniciar búsqueda Cancel\ search=Cancelar la búsqueda Select\ entry=Seleccionar entrada -Search\ aborted\!=¡Búsqueda cancelada\! +Search\ aborted\!=Se interrumpió la búsqueda. +Citation\ relations=Relaciones entre citas Show\ articles\ related\ by\ citation=Mostrar artículos relacionados por cita +Error\ while\ fetching\ citing\ entries\:\ %0=Error al recuperar las entradas de cita\: %0 +Jump\ to\ entry\ in\ database=Saltar a entrada en base de datos +Help\ on\ external\ applications=Ayuda sobre aplicaciones externas +Identifier-based\ Web\ Search=Búsqueda en la web basada en identificadores + + + + + + diff --git a/src/main/resources/l10n/JabRef_fa.properties b/src/main/resources/l10n/JabRef_fa.properties index 0e24b672115..16679d6e938 100644 --- a/src/main/resources/l10n/JabRef_fa.properties +++ b/src/main/resources/l10n/JabRef_fa.properties @@ -93,6 +93,7 @@ Case\ sensitive=حساس به کوچکی و بزرگی حروف + Clear=پاکسازی @@ -599,6 +600,10 @@ String\ constants=ثابت های رشته + + + + @@ -666,6 +671,12 @@ Auto\ complete\ enabled.=تکمیل خودکار غیرفعال شد. + + + + + + diff --git a/src/main/resources/l10n/JabRef_fi.properties b/src/main/resources/l10n/JabRef_fi.properties index 7acb45cd084..9df2bd6d5d2 100644 --- a/src/main/resources/l10n/JabRef_fi.properties +++ b/src/main/resources/l10n/JabRef_fi.properties @@ -602,6 +602,17 @@ About\ JabRef=Tietoja JabRef + + + + + + + + + + + diff --git a/src/main/resources/l10n/JabRef_fr.properties b/src/main/resources/l10n/JabRef_fr.properties index 6df2ca8b1aa..c6af7ef60be 100644 --- a/src/main/resources/l10n/JabRef_fr.properties +++ b/src/main/resources/l10n/JabRef_fr.properties @@ -131,6 +131,8 @@ Case\ sensitive=Sensible à la casse change\ assignment\ of\ entries=changer l'assignation des entrées +Catalogues\ used\ for\ 'Search\ Selected'=Catalogues utilisés pour la 'Recherche sélectionnée' + Change\ case=Changer la casse Change\ entry\ type=Changer le type d'entrée @@ -168,12 +170,12 @@ Copied=Copié Copy=Copier Copy\ title=Copier le titre -Copy\ \\cite{citation\ key}=Copier \\cite{citation key} Copy\ citation\ (html)=Copier la citation (html) Copy\ citation\ (text)=Copier la citation (texte) Copy\ citation\ key=Copier la clef de citation Copy\ citation\ key\ and\ link=Copier la clef de citation et le lien Copy\ citation\ key\ and\ title=Copier la clef de citation et le titre +Copy\ citation\ key\ with\ configured\ cite\ command=Copier la clef de citation avec la commande de citation configurée Copy\ to\ clipboard=Copier dans le presse-papier @@ -477,7 +479,6 @@ Year=Année ISSN\ or\ journal\ name\ required\ for\ fetching\ journal\ information=L'ISSN ou le nom du journal est requis pour la collecte des informations du journal Fetch\ journal\ information\ online\ to\ show=Récupérer en ligne les informations du journal à afficher Allow\ sending\ ISSN\ to\ a\ JabRef\ online\ service\ (SCimago)\ for\ fetching\ journal\ information=Autoriser l'envoi de l'ISSN à un service en ligne de JabRef (SCimago) pour collecter les informations sur le journal -Please\ enable\ journal\ information\ fetching\ in\ %0\ >\ %1=Veuillez activer la collecte des informations sur le journal dans %0 > %1 Categories=Catégories ISSN=ISSN Publisher=Maison d'édition @@ -520,7 +521,6 @@ Last\ modified=Dernier modifié LaTeX\ AUX\ file\:=Fichier AUX de LaTeX \: Link=Lien -Listen\ for\ remote\ operation\ on\ port=Écouter le port pour des opérations à distance Memory\ stick\ mode\ -\ Store\ preferences\ in\ 'jabref.xml'\ in\ the\ app\ folder.=Mode clef USB - Stocke les préférences dans le dossier de l'application (fichier 'jabref.xml'). Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=Afficher les astuces avancées (c'est-à-dire les astuces utiles, les suggestions et l'explication) @@ -1009,7 +1009,6 @@ Citation\ key\ generator=Générateur de clefs de citation Unable\ to\ open\ link.=Impossible d'ouvrir le lien. MIME\ type=Type MIME -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Cette fonction permet aux nouveaux fichiers d'être ouverts ou importés dans une fenêtre JabRef déjà active au lieu d'ouvrir une nouvelle fenêtre. Par exemple, c'est utile quand vous ouvrez un fichier dans JabRef à partir de notre navigateur internet. Notez que cela vous empêchera de lancer plus d'une fenêtre JabRef à la fois. Run\ fetcher=Lancer l'outil de collecte Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=Ligne %0 \: clef de citation corrompue %1. @@ -2041,12 +2040,28 @@ Matching=Correspondant Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=Comme --import, mais sera importé dans l'onglet ouvert Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=Autoriser les entiers dans le champ 'edition' en mode BibTeX +Please\ enter\ a\ name\ for\ the\ MIME\ type.=Veuillez entrer un nom pour le type MIME. +Please\ enter\ a\ name\ for\ the\ extension.=Veuillez entrer un nom pour l'extension. +Please\ enter\ a\ name.=Veuillez entrer un nom. + +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ MIME\ type=Il existe déjà un type de fichier externe avec le même type MIME +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ extension=Il existe déjà un type de fichier externe avec la même extension +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ name=Il existe déjà un type de fichier externe avec le même nom + + Search\ for\ citations\ in\ LaTeX\ files...=Recherche de citations dans les fichiers LaTeX... LaTeX\ Citations\ Search\ Results=Résultats de la recherche des appels à référence LaTeX LaTeX\ files\ directory\:=Répertoire des fichiers LaTeX \: LaTeX\ files\ found\:=Fichiers LaTeX trouvés \: files=fichiers Show\ 'LaTeX\ Citations'\ tab=Afficher l'onglet 'Appels à référence LaTeX' +Show\ 'Scite'\ tab=Afficher l'onglet 'Scite' +Search\ scite.ai\ for\ Smart\ Citations=Rechercher des Citations Intelligentes dans scite.ai +See\ full\ report\ at\ [%0]=Voir le rapport complet à [%0] +No\ active\ entry=Aucune entrée active +This\ entry\ does\ not\ have\ a\ DOI=Cette entrée n'a pas de DOI +Tallies\ for\ %0=Décomptes pour %0 + LaTeX\ Citations=Appels à référence LaTeX Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=Rechercher des appels à référence de cette entrée dans les fichiers LaTeX No\ citations\ found=Aucun appel à référence trouvé @@ -2086,6 +2101,7 @@ Font\ settings=Paramètres de police Custom...=Personnaliser... Dark=Sombre Light=Clair +Use\ System\ Preference=Utiliser les préférences du système Please\ specify\ a\ css\ theme\ file.=Veuillez spécifier le fichier CSS du thème. You\ must\ enter\ an\ integer\ value\ higher\ than\ 8.=Vous devez entrer un entier supérieur à 8. Letters\ after\ duplicate\ generated\ keys=Lettres après les clefs générées en double @@ -2604,3 +2620,19 @@ Help\ on\ external\ applications=Aide sur les logiciels externes Identifier-based\ Web\ Search=Recherche Web basée sur les identifiants Pushing\ citations\ to\ TeXShop\ is\ only\ possible\ on\ macOS\!=L'envoi des citations vers TeXShop n'est possible que sur macOS \! + +Single\ instance=Instance unique +Enforce\ single\ JabRef\ instance\ (and\ allow\ remote\ operations)\ using\ port=Forcer une seule instance de JabRef (et autoriser les opérations à distance) en utilisant le port + + +Enable\ Journal\ Information\ Fetching?=Activer la récupération des informations des journaux ? +Would\ you\ like\ to\ enable\ fetching\ of\ journal\ information?\ This\ can\ be\ changed\ later\ in\ %0\ >\ %1.=Voulez-vous activer la récupération des informations des journaux ? Cela peut être modifié plus tard dans %0 > %1. +Enable=Activer +Keep\ disabled=Garder désactivé + +Predatory\ journal\ %0\ found=Journal prédateur %0 trouvé + +Hide\ user\ comments=Masquer les commentaires de l'utilisateur +Show\ user\ comments\ field=Afficher le champ des commentaires de l'utilisateur + +More\ options...=Plus d'options... diff --git a/src/main/resources/l10n/JabRef_id.properties b/src/main/resources/l10n/JabRef_id.properties index 75aefb73a28..30863d1822c 100644 --- a/src/main/resources/l10n/JabRef_id.properties +++ b/src/main/resources/l10n/JabRef_id.properties @@ -1,5 +1,6 @@ Proxy\ requires\ password=Proxy membutuhkan password +Unable\ to\ monitor\ file\ changes.\ Please\ close\ files\ and\ processes\ and\ restart.\ You\ may\ encounter\ errors\ if\ you\ continue\ with\ this\ session.=Tidak dapat memantau perubahan file. Silakan tutup file dan proses dan mulai ulang program. Anda mungkin mengalami error/malfungsi jika melanjutkan sesi ini. %0\ contains\ the\ regular\ expression\ %1=%0 mengandung Ekspresi Reguler %1 %0\ contains\ the\ term\ %1=% mengandung istilah %1 @@ -9,21 +10,29 @@ Proxy\ requires\ password=Proxy membutuhkan password %0\ doesn't\ contain\ the\ term\ %1=%0 tidak mengandung istilah %1 +Export\ operation\ finished\ successfully.=Operasi eksport file selesai dengan sukses. +Reveal\ in\ File\ Explorer=Buka di File Explorer %0\ matches\ the\ regular\ expression\ %1=%0 sesuai dengan Ekspresi Reguler %1 %0\ matches\ the\ term\ %1=%0 sesuai dengan istilah %1 +Abbreviate\ journal\ names\ of\ the\ selected\ entries\ (DEFAULT\ abbreviation)=Buat singkatan nama jurnal dari entri pilihan (singkatan BAWAAN) +Abbreviate\ journal\ names\ of\ the\ selected\ entries\ (DOTLESS\ abbreviation)=Buat singkatan nama jurnal dari entri yang dipilih (singkatan DOTLESS) +Abbreviate\ journal\ names\ of\ the\ selected\ entries\ (SHORTEST\ UNIQUE\ abbreviation)=Buat singkatan nama jurnal dari entri yang dipilih (singkatan PALING SINGKAT dan UNIK) Abbreviate\ names=Singkat nama Abbreviated\ %0\ journal\ names.=Singkatan %0 nama jurnal. Abbreviation=Singkatan +Abbreviations=Singkatan Unabbreviate\ journal\ names\ of\ the\ selected\ entries=Nama jurnal tidak disingkat dari entri pilihan Unabbreviated\ %0\ journal\ names.=%0 nama jurnal tidak disingkat. +dotless=tanpa titik +shortest\ unique=paling singkat dan unik About\ JabRef=Tentang JabRef @@ -31,6 +40,7 @@ Abstract=Abstrak Accept=Terima +Accept\ recommendations\ from\ Mr.\ DLib=Terima rekomendasi Mr. DLib Action=Aksi @@ -41,6 +51,7 @@ The\ path\ need\ not\ be\ on\ the\ classpath\ of\ JabRef.=Lokasi tidak harus pad Add\ a\ regular\ expression\ for\ the\ key\ pattern.=Tambahkan ekspresi reguler untuk pola kunci. +Add\ entry\ manually=Masukkan entri secara mandiri Add\ selected\ entries\ to\ this\ group=Pilih tambahkan di entri ke grup ini @@ -48,10 +59,12 @@ Add\ subgroup=Tambah Anak Grup Added\ group\ "%0".=Grup ditambahkan "%0". +Added\ string\:\ '%0'=String yang ditambahkan\: '%0' Added\ string=Ditambahkan string All\ entries=Semua entri +Also\ remove\ subgroups=Juga Hapus sub-grup and=dan @@ -61,6 +74,7 @@ Appearance=Penampilan Application=Aplikasi +Application\ to\ push\ entries\ to=Aplikasi untuk memasukkan entri Apply=Terapkan @@ -72,8 +86,11 @@ Assigned\ %0\ entries\ to\ group\ "%1".=Diterapkan %0 entri ke grup "%1". Assigned\ 1\ entry\ to\ group\ "%0".=Diterapkan 1 entri ke grup "%0". +Autogenerate\ citation\ keys=Buat kunci sitasi secara otomatis +Autolink\ files\ with\ names\ starting\ with\ the\ citation\ key=Tautkan otomatis file dengan nama yang dimulai dengan kunci kutipan +Autolink\ only\ files\ that\ match\ the\ citation\ key=Tautkan otomatis file dengan nama yang persis dengan kunci kutipan Automatically\ create\ groups=Otomatis membuat grup @@ -87,6 +104,7 @@ Available\ import\ formats=Format impor yang dikenal %0\ source=Sumber %0 +Background\ Tasks=Tugas Latar Belakang Background\ Tasks\ are\ running=Tugas latarbelakang sedang berjalan @@ -95,10 +113,14 @@ Background\ Tasks\ are\ done=Tugas Latarbelakang telah selesai Browse=Jelajah by=oleh +The\ conflicting\ fields\ of\ these\ entries\ will\ be\ merged\ into\ the\ 'Comment'\ field.=Entri yang bertentangan pada entri ini akan digabungkan ke dalam entri 'Komentar'. Cancel=Batal +Cannot\ create\ group=Tidak dapat membuat grup +Cannot\ create\ group.\ Please\ create\ a\ library\ first.=Tidak dapat membuat grup, Tolong buat pustaka terlebih dahulu. +Cannot\ open\ folder\ as\ the\ file\ is\ an\ online\ link.=Tidak dapat membuka folder karena file tersebut adalah tautan online. case\ insensitive=huruf besar kecil tidak penting @@ -108,6 +130,7 @@ Case\ sensitive=Huruf besar kecil tidak penting change\ assignment\ of\ entries=merubah penugasan entri + Change\ case=Merubah huruf besar/kecil Change\ entry\ type=Merubah tipe entri @@ -117,6 +140,7 @@ Change\ of\ Grouping\ Method=Metubah Metode Grup change\ preamble=merubah preamble +Changed\ language=Bahasa yang di ubah Changed\ preamble=Preamble berubah @@ -124,6 +148,7 @@ Cite\ command=Perintah acuan Clear=Bersihkan +Open\ /\ close\ entry\ editor=Buka / tutup editor entri Close\ dialog=Tutup dialog @@ -131,6 +156,7 @@ Close\ the\ current\ library=Tutup basisdata yang sekarang Close\ window=Tutup jendela +Comments=Komentar Contained\ in=Terkandung di @@ -143,6 +169,7 @@ Copy=Salin Copy\ title=Kopi judul Copy\ citation\ (html)=Salin kutipan (html) +Copy\ citation\ (text)=Salin sitasi(text) Copy\ to\ clipboard=Salin ke papan klip @@ -404,7 +431,6 @@ Last\ modified=Terakhir diubah LaTeX\ AUX\ file\:=File LaTeX AUX\: Link=Tautan -Listen\ for\ remote\ operation\ on\ port=Menggunakan operasi jarak jauh pada port Manage\ custom\ exports=Mengatur ekspor atursendiri @@ -778,7 +804,6 @@ Error\ while\ fetching\ from\ %0=Kesalahan ketika mengambil dari %0 Unable\ to\ open\ link.=Tidak bisa membuka tautan. MIME\ type=Tipe MIME -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Fitur ini memungkinkan berkas baru atau impor ke jendela JabRef yang aktifbukan membuat baru. Hal ini berguna ketika anda membuka berkas di JabRef dari halaman web. Hal ini akan menghindari anda membuka beberapa JabRef pada saat yang sama. Download\ from\ URL=Unduh dari URL Rename\ field=Ganti nama bidang @@ -1505,6 +1530,10 @@ Executing\ command\ "%0"...=Perintah pelaksana "%0"... + + + + Group\ color=Warna kelompok @@ -1595,3 +1624,9 @@ This\ could\ indicate\ that\ JabRef\ did\ not\ shut\ down\ cleanly\ last\ time\ + + + + + + diff --git a/src/main/resources/l10n/JabRef_it.properties b/src/main/resources/l10n/JabRef_it.properties index 73d93d639a1..7cec81fef68 100644 --- a/src/main/resources/l10n/JabRef_it.properties +++ b/src/main/resources/l10n/JabRef_it.properties @@ -131,6 +131,8 @@ Case\ sensitive=Distingue maiuscole e minuscole change\ assignment\ of\ entries=modifica l'assegnazione delle voci +Catalogues\ used\ for\ 'Search\ Selected'=Cataloghi usati per 'Ricerca selezionata' + Change\ case=Inverti maiuscolo/minuscolo Change\ entry\ type=Cambia tipo di voce @@ -168,12 +170,12 @@ Copied=Copiato Copy=Copia Copy\ title=Copia titolo -Copy\ \\cite{citation\ key}=Copia \\cite{citation key} Copy\ citation\ (html)=Copia citazione (html) Copy\ citation\ (text)=Copia citazione (testo) Copy\ citation\ key=Copia la chiave BibTeX Copy\ citation\ key\ and\ link=Copia la chiave BibTeX ed il collegamento Copy\ citation\ key\ and\ title=Copia la chiave BibTeX ed il titolo +Copy\ citation\ key\ with\ configured\ cite\ command=Copia la chiave citazione con il comando di citazione configurato Copy\ to\ clipboard=Copia negli appunti @@ -477,7 +479,6 @@ Year=Anno ISSN\ or\ journal\ name\ required\ for\ fetching\ journal\ information=ISSN o nome della rivista richiesto per il recupero delle informazioni sulla rivista Fetch\ journal\ information\ online\ to\ show=Recupera informazioni sulla rivista online da mostrare Allow\ sending\ ISSN\ to\ a\ JabRef\ online\ service\ (SCimago)\ for\ fetching\ journal\ information=Consenti l'invio di ISSN a un servizio online JabRef (SCimago) per il recupero delle informazioni sulla rivista -Please\ enable\ journal\ information\ fetching\ in\ %0\ >\ %1=Si prega di abilitare il recupero informazioni sulla rivista in %0 > %1 Categories=Categorie ISSN=ISSN Publisher=Editore @@ -520,7 +521,6 @@ Last\ modified=Ultimo modificato LaTeX\ AUX\ file\:=File AUX LaTeX\: Link=Collegamento -Listen\ for\ remote\ operation\ on\ port=Porta in ascolto per operazioni remote Memory\ stick\ mode\ -\ Store\ preferences\ in\ 'jabref.xml'\ in\ the\ app\ folder.=Modalità Memory stick - Memorizza le preferenze in 'jabref.xml' nella cartella app. Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=Mostra suggerimenti avanzati (aiuto contestuale, suggerimenti e spiegazioni) @@ -1009,7 +1009,6 @@ Citation\ key\ generator=Generatore di chiavi BibTeX Unable\ to\ open\ link.=Impossibile aprire il collegamento. MIME\ type=Tipo MIME -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Questa funzione permette l'apertura o l'importazione di nuovi file in una istanza di JabRef già aperta invece di aprirne una nuova. Per esempio, ciò è utile quando un file viene aperto in JabRef da un browser web. Questo tuttavia impedisce di aprire più sessioni di JabRef contemporaneamente. Run\ fetcher=Esegui fetcher Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=Riga %0\: Trovata chiave BibTeX corrotta %1. @@ -2041,12 +2040,28 @@ Matching=Corrispondenza Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=Come --importa, ma verrà importato nella scheda aperta Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=Permetti interi nel campo 'edizione' in modalità BibTeX +Please\ enter\ a\ name\ for\ the\ MIME\ type.=Inserire un nome per il tipo MIME. +Please\ enter\ a\ name\ for\ the\ extension.=Inserisci un nome per l'estensione. +Please\ enter\ a\ name.=Inserisci un nome. + +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ MIME\ type=Esiste già un tipo di file esterno con lo stesso tipo MIME +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ extension=Esiste già un tipo di file esterno con la stessa estensione +There\ already\ exists\ an\ external\ file\ type\ with\ the\ same\ name=Esiste già un tipo di file esterno con lo stesso nome + + Search\ for\ citations\ in\ LaTeX\ files...=Cerca citazioni nei file LaTeX... LaTeX\ Citations\ Search\ Results=Risultati della ricerca delle citazioni LaTeX LaTeX\ files\ directory\:=Cartella dei file LaTeX\: LaTeX\ files\ found\:=File LaTeX trovati\: files=files Show\ 'LaTeX\ Citations'\ tab=Mostra scheda 'Citazioni LaTeX' +Show\ 'Scite'\ tab=Mostra la tab 'Scite' +Search\ scite.ai\ for\ Smart\ Citations=Cerca scite.ai per Smart Citations +See\ full\ report\ at\ [%0]=Vedi la relazione completa alla sezione [%0] +No\ active\ entry=Nessuna voce attiva +This\ entry\ does\ not\ have\ a\ DOI=Questa voce non ha un DOI +Tallies\ for\ %0=Conteggi per %0 + LaTeX\ Citations=Citazioni LaTeX Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=Cerca citazioni per questa voce nei file LaTeX No\ citations\ found=Nessuna citazione trovata @@ -2086,6 +2101,7 @@ Font\ settings=Impostazioni carattere Custom...=Personalizzato... Dark=Scuro Light=Chiaro +Use\ System\ Preference=Usa le preferenze di sistema Please\ specify\ a\ css\ theme\ file.=Specifica un file di tema css. You\ must\ enter\ an\ integer\ value\ higher\ than\ 8.=Devi inserire un valore intero superiore a 8. Letters\ after\ duplicate\ generated\ keys=Lettere dopo le chiavi generate duplicate @@ -2604,3 +2620,19 @@ Help\ on\ external\ applications=Aiuto su applicazioni esterne Identifier-based\ Web\ Search=Ricerca Web basata sull'Identificatore Pushing\ citations\ to\ TeXShop\ is\ only\ possible\ on\ macOS\!=Inviare le citazioni a TeXShop è possibile solo su macOS\! + +Single\ instance=Singola istanza +Enforce\ single\ JabRef\ instance\ (and\ allow\ remote\ operations)\ using\ port=Forza singola istanza JabRef (e permetti operazioni remote) usando la porta + + +Enable\ Journal\ Information\ Fetching?=Abilitare il recupero delle informazioni della rivista? +Would\ you\ like\ to\ enable\ fetching\ of\ journal\ information?\ This\ can\ be\ changed\ later\ in\ %0\ >\ %1.=Vuoi abilitare il recupero delle informazioni della rivista? Questo può essere modificato in seguito in %0 > %1. +Enable=Abilita +Keep\ disabled=Mantieni disabilitato + +Predatory\ journal\ %0\ found=Trovata la rivista predatoria %0 + +Hide\ user\ comments=Nascondi commenti utente +Show\ user\ comments\ field=Mostra il campo commenti utente + +More\ options...=Altre opzioni... diff --git a/src/main/resources/l10n/JabRef_ja.properties b/src/main/resources/l10n/JabRef_ja.properties index c60fb546fc8..a28aaafbe24 100644 --- a/src/main/resources/l10n/JabRef_ja.properties +++ b/src/main/resources/l10n/JabRef_ja.properties @@ -131,6 +131,7 @@ Case\ sensitive=大小文字を区別 change\ assignment\ of\ entries=項目の割り当てを変更 + Change\ case=大小文字を切り替え Change\ entry\ type=項目型を変更 @@ -168,7 +169,6 @@ Copied=コピーしました Copy=コピー Copy\ title=タイトルをコピー -Copy\ \\cite{citation\ key}=\\cite{citation key} をコピー Copy\ citation\ (html)=文献をコピー (html) Copy\ citation\ (text)=文献をコピー (テキスト) Copy\ citation\ key=文献キーをコピー @@ -496,7 +496,6 @@ Last\ modified=最終修正日時 LaTeX\ AUX\ file\:=LaTeX AUXファイル: Link=リンク -Listen\ for\ remote\ operation\ on\ port=以下のポートでリモート操作を待ち受ける Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=詳細なヒントを表示する (詳しいツールチップやおすすめ,説明など) @@ -962,7 +961,6 @@ Citation\ key\ generator=文献キーの生成 Unable\ to\ open\ link.=リンクを開くことができませんでした. MIME\ type=MIME型 -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=この機能は,新規ファイルを,新しいJabRefインスタンスを開かないで,すでに実行されているインスタンスに開いたり 読み込んだりするものです.たとえば,これは,ウェブブラウザからJabRefにファイルを開かせたい時に便利です.これによって,一度にJabRefのインスタンスを一つしか開けなくなることに注意してください Run\ fetcher=取得子を実行 Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=第%0行:破損した文献キー%1を検出しました. @@ -1959,12 +1957,16 @@ Matching=一致 Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=--importと同じですが,開かれているタブに読み込みます Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=BibTeXモードの「edition」フィールドを整数にすることを許可する + + + Search\ for\ citations\ in\ LaTeX\ files...=LaTeXファイルから引用を検索... LaTeX\ Citations\ Search\ Results=「LaTeXでの引用」の検索結果 LaTeX\ files\ directory\:=LaTeXファイルディレクトリ: LaTeX\ files\ found\:=検出されたLaTeXファイル: files=ファイル Show\ 'LaTeX\ Citations'\ tab=「LaTeXでの引用」タブを表示 + LaTeX\ Citations=LaTeXでの引用 Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=この項目を含む引用が他のLaTeXファイル中にないか検索 No\ citations\ found=引用はありません @@ -2455,3 +2457,9 @@ Processing...=処理中... + + + + + + diff --git a/src/main/resources/l10n/JabRef_ko.properties b/src/main/resources/l10n/JabRef_ko.properties index fb8e7e63f0c..6c3d70168f3 100644 --- a/src/main/resources/l10n/JabRef_ko.properties +++ b/src/main/resources/l10n/JabRef_ko.properties @@ -128,6 +128,7 @@ Case\ sensitive=대/소문자 구분 change\ assignment\ of\ entries=항목 할당 변경 + Change\ case=대소문자 변경 Change\ entry\ type=입력 유형 변경 @@ -165,7 +166,6 @@ Copied=복사 완료 Copy=복사 Copy\ title=제목 복사 -Copy\ \\cite{citation\ key}=\\cite{citation key} 복사 Copy\ citation\ key=인용 키 복사 Copy\ citation\ key\ and\ link=인용 키와 링크 복사 Copy\ citation\ key\ and\ title=인용 키와 제목 복사 @@ -473,7 +473,6 @@ Last\ modified=마지막으로 수정됨 LaTeX\ AUX\ file\:=LaTeX AUX 파일\: Link=링크 -Listen\ for\ remote\ operation\ on\ port=포트에서 원격 작업 수신 Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=상급 힌트 표시(예\: 유용한 툴팁, 제안 및 설명) @@ -916,7 +915,6 @@ Citation\ key\ generator=인용 키 생성기 Unable\ to\ open\ link.=링크를 열 수 없습니다. MIME\ type=MIME 유형 -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=이 기능을 사용하면 새 인스턴스를 열지 않고 이미 실행 중인 인스턴스에 새 파일을 열거나 가져올 수 있습니다. 예를 들어, 웹 브라우저에서 JabRef의 파일을 열 때 유용합니다. 이렇게 하면 한 번에 둘 이상의 JabRef 인스턴스를 실행할 수 없습니다. Run\ fetcher=가져오기 실행 Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=%0행\: 손상된 인용 키 %1을 찾았습니다. @@ -1866,12 +1864,16 @@ Matching=어울리는 Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=--import와 동일하지만 열린 탭으로 가져옵니다. Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=BibTeX 모드의 '에디션' 필드에 정수 허용 + + + Search\ for\ citations\ in\ LaTeX\ files...=LaTeX 파일에서 인용 검색... LaTeX\ Citations\ Search\ Results=LaTeX 인용 검색 결과 LaTeX\ files\ directory\:=LaTeX 파일 디렉토리\: LaTeX\ files\ found\:=찾은 LaTeX 파일\: files=파일 Show\ 'LaTeX\ Citations'\ tab='LaTeX 인용' 탭 표시 + LaTeX\ Citations=LaTeX 인용 Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=LaTeX 파일에서 이 항목에 대한 인용 검색 No\ citations\ found=검색된 인용이 없습니다 @@ -2288,3 +2290,9 @@ This\ could\ indicate\ that\ JabRef\ did\ not\ shut\ down\ cleanly\ last\ time\ + + + + + + diff --git a/src/main/resources/l10n/JabRef_nl.properties b/src/main/resources/l10n/JabRef_nl.properties index 123253e9cfb..05141702e2a 100644 --- a/src/main/resources/l10n/JabRef_nl.properties +++ b/src/main/resources/l10n/JabRef_nl.properties @@ -131,6 +131,7 @@ Case\ sensitive=Hoofdlettergevoelig change\ assignment\ of\ entries=verandering toewijzing van invoergegevens + Change\ case=Hoofdlettergebruik wijzigen Change\ entry\ type=Invoertype wijzigen @@ -168,7 +169,6 @@ Copied=Gekopieerd Copy=Kopiëren Copy\ title=Titel kopiëren -Copy\ \\cite{citation\ key}=Kopieer \\cite{citation key} Copy\ citation\ (html)=Kopieer citaat (html) Copy\ citation\ (text)=Kopieer citaat (tekst) Copy\ citation\ key=Kopiëer citatiesleutel @@ -471,7 +471,6 @@ Year=Jaar ISSN\ or\ journal\ name\ required\ for\ fetching\ journal\ information=ISSN of tijdschriftnaam vereist voor het ophalen van tijdschrift informatie Fetch\ journal\ information\ online\ to\ show=Tijdschriftinformatie online ophalen om te tonen Allow\ sending\ ISSN\ to\ a\ JabRef\ online\ service\ (SCimago)\ for\ fetching\ journal\ information=Sta het verzenden van ISSN naar een online JabRef dienst (SCimago) toe voor het ophalen van tijdschrift informatie -Please\ enable\ journal\ information\ fetching\ in\ %0\ >\ %1=Schakel tijdschrift informatie ophalen in %0 > %1 Categories=Categorieën ISSN=ISSN Publisher=Uitgever @@ -513,7 +512,6 @@ Last\ modified=Laatst gewijzigd LaTeX\ AUX\ file\:=LaTeX AUX bestand\: Link=Link -Listen\ for\ remote\ operation\ on\ port=Luister naar operatie vanop afstand op poort Memory\ stick\ mode\ -\ Store\ preferences\ in\ 'jabref.xml'\ in\ the\ app\ folder.=Geheugenstick modus - Sla instellingen op in 'jabref.xml' in de app map. Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=Toon geavanceerde hints (bijv. nuttige tooltips, suggesties en uitleg) @@ -997,7 +995,6 @@ Citation\ key\ generator=Citatiesleutel generator Unable\ to\ open\ link.=Link openen niet mogelijk. MIME\ type=MIME type -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Met deze functie kunnen nieuwe bestanden worden geopend of geïmporteerd in een reeds actieve instantie van JabRef in plaats van een nieuwe instantie te openen. Dit is bijvoorbeeld handig wanneer je een bestand in JabRef opent vanuit je webbrowser. Merk op dat dit u ervan weerhoudt meer dan één exemplaar van JabRef tegelijk te draaien. Run\ fetcher=Start fetcher Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=Lijn %0\: corrupte citatiesleutel %1 gevonden. @@ -2025,12 +2022,16 @@ Matching=Overeenkomend Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=Hetzelfde als --import, maar zal worden geïmporteerd in het geopende tabblad Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=Sta gehele getallen in het 'edition' veld toe in BibTeX modus + + + Search\ for\ citations\ in\ LaTeX\ files...=Zoek naar citaten in LaTeX bestanden... LaTeX\ Citations\ Search\ Results=LaTeX Citaten Zoekresultaten LaTeX\ files\ directory\:=LaTeX bestanden map\: LaTeX\ files\ found\:=LaTeX bestanden gevonden\: files=bestanden Show\ 'LaTeX\ Citations'\ tab=Toon 'LaTeX Citaten' tabblad + LaTeX\ Citations=LaTeX Citaten Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=Zoek citaten voor dit item in LaTeX bestanden No\ citations\ found=Geen citaten gevonden @@ -2570,3 +2571,9 @@ Processing...=Verwerken... + + + + + + diff --git a/src/main/resources/l10n/JabRef_no.properties b/src/main/resources/l10n/JabRef_no.properties index 4c746b486ff..63b50deb7fc 100644 --- a/src/main/resources/l10n/JabRef_no.properties +++ b/src/main/resources/l10n/JabRef_no.properties @@ -107,6 +107,7 @@ Case\ sensitive=Skill store og små bokstaver change\ assignment\ of\ entries=endre tilordning av oppføringer + Change\ case=Endre store/små bokstaver Change\ entry\ type=Endre enhetstype @@ -402,7 +403,6 @@ Language=Språk Last\ modified=Sist endret Link=Lenke -Listen\ for\ remote\ operation\ on\ port=Lytt etter fjernoperasjoner pÃ¥ port Manage\ custom\ exports=Sett opp eksterne eksportfiltre @@ -766,7 +766,6 @@ Error\ while\ fetching\ from\ %0=Feil ved henting fra %0 Unable\ to\ open\ link.=Kan ikke åpne link. MIME\ type=MIME-type -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Denne funksjonen lar deg åpne eller importere nye filer til en allerede kjørende instans av JabRef fra nettleseren din. Merk at dette vil hindre deg i å kjøre mer enn en instans av JabRef av gangen. Download\ from\ URL=Last ned fra URL Rename\ field=Endre navn på felt @@ -1086,6 +1085,10 @@ Import\ BibTeX=Importer BibTeX + + + + Always\ add\ letter\ (a,\ b,\ ...)\ to\ generated\ keys=Legg alltid til en bokstav (a, b, ...) til genererte nøkler Default\ pattern=Standardmønster @@ -1160,6 +1163,12 @@ Default\ pattern=Standardmønster + + + + + + diff --git a/src/main/resources/l10n/JabRef_pl.properties b/src/main/resources/l10n/JabRef_pl.properties index 5bfc22fc4f3..37353b0716c 100644 --- a/src/main/resources/l10n/JabRef_pl.properties +++ b/src/main/resources/l10n/JabRef_pl.properties @@ -127,6 +127,7 @@ Case\ sensitive=Rozróżniaj wielkość liter change\ assignment\ of\ entries=zmień przypisanie wpisów + Change\ case=Zmiana wielkości liter Change\ entry\ type=Zmień typ wpisu @@ -164,7 +165,6 @@ Copied=Skopiowano Copy=Kopiuj Copy\ title=Kopiuj tytuł -Copy\ \\cite{citation\ key}=Kopiuj \\cite{citation key} Copy\ citation\ (html)=Kopiuj cytowanie (html) Copy\ citation\ (text)=Kopiuj cytowanie (tekst) Copy\ citation\ key=Kopiuj klucz cytowania @@ -456,7 +456,6 @@ Year=Rok ISSN\ or\ journal\ name\ required\ for\ fetching\ journal\ information=ISSN lub tytuł czasopisma wymagane do pobrania informacji o czasopiśmie Fetch\ journal\ information\ online\ to\ show=Pobierz z sieci i wyświetl informacje o czasopiśmie Allow\ sending\ ISSN\ to\ a\ JabRef\ online\ service\ (SCimago)\ for\ fetching\ journal\ information=Zezwalaj na wysyłanie ISSN do usługi online JabRef (SCimago) w celu pobierania informacji o czasopiśmie -Please\ enable\ journal\ information\ fetching\ in\ %0\ >\ %1=Proszę włączyć pobieranie informacji o czasopiśmie w %0 > %1 Categories=Kategorie ISSN=ISSN Publisher=Wydawca @@ -499,7 +498,6 @@ Last\ modified=Data Modyfikacji LaTeX\ AUX\ file\:=Plik LaTeX AUX\: Link=Odnośnik -Listen\ for\ remote\ operation\ on\ port=Słuchaj zdalnych operacji na porcie Memory\ stick\ mode\ -\ Store\ preferences\ in\ 'jabref.xml'\ in\ the\ app\ folder.=Tryb przenośny - Przechowuj preferencje w pliku 'jabref.xml' w folderze aplikacji. Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=Pokaż zaawansowane wskazówki (np. pomocne podpowiedzi, sugestie i wyjaśnienia) @@ -966,7 +964,6 @@ Citation\ key\ generator=Generator klucza cytowania Unable\ to\ open\ link.=Nie można otworzyć linku. MIME\ type=Typ MIME -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Ta funkcja pozwala otwierać nowe pliki lub importować je do już uruchomionej instancji JabRef zamiast otwierać nową instancję. Na przykład jest to przydatne podczas otwierania pliku w JabRef z przeglądarki internetowej. Zauważ, że uniemożliwi to jednoczesne uruchomienie więcej niż jednej instancji JabRef. Run\ fetcher=Uruchom pobieranie Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=Wiersz %0\: Znaleziono uszkodzony klucz cytowania %1. @@ -1596,7 +1593,11 @@ Import\ BibTeX=Importuj BibTeX Import\ preferences\ from\ a\ file=Importuj preferencje z pliku Matching=Pasujące + + + files=pliki + No\ citations\ found=Nie znaleziono cytatów Current\ search\ directory\:=Aktualny katalog wyszukiwania\: Import\ entries\ from\ LaTeX\ files=Importuj wpisy z plików LaTeX @@ -1751,3 +1752,9 @@ Cancel\ search=Anuluj wyszukiwanie Select\ entry=Wybierz wpis Search\ aborted\!=Wyszukiwanie przerwane\! + + + + + + diff --git a/src/main/resources/l10n/JabRef_pt.properties b/src/main/resources/l10n/JabRef_pt.properties index 8f6f66a959e..eef2cff09e3 100644 --- a/src/main/resources/l10n/JabRef_pt.properties +++ b/src/main/resources/l10n/JabRef_pt.properties @@ -130,6 +130,7 @@ Case\ sensitive=Sensível ao caso change\ assignment\ of\ entries=Alterar atribuição de referências + Change\ case=Modificar caso Change\ entry\ type=Modificar tipo de referência @@ -167,7 +168,6 @@ Copied=Copiado Copy=Copiar Copy\ title=Copiar título -Copy\ \\cite{citation\ key}=Copiar \\cite{citation key} Copy\ citation\ (html)=Copiar citação (html) Copy\ citation\ (text)=Copiar citação (texto) Copy\ citation\ key=Copiar chave de citação @@ -485,7 +485,6 @@ Last\ modified=Última modificação LaTeX\ AUX\ file\:=Arquivo AUX LaTeX\: Link=Linkar -Listen\ for\ remote\ operation\ on\ port=Escutar operações remotas na porta Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=Mostrar dicas avançadas (ou seja, dicas úteis, sugestões e explicações) @@ -917,7 +916,6 @@ Error\ while\ fetching\ from\ %0=Erro ao recuperar do %0 Unable\ to\ open\ link.=Não foi possível abrir link. -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Esta funcionalidade permite que novos arquivos sejam abertos ou importados para uma instância do JabRef já aberta ao invés de abrir uma nova instância. Por exemplo, isto é útil quando você abre um arquivo no JabRef a partir de ser navegador web. Note que isto irá previnir que você execute uma ou mais instâncias do JabRef ao mesmo tempo. Rename\ field=Renomear campo @@ -1350,11 +1348,15 @@ Main\ layout\ file=Arquivo de leiaute principal + + + files=arquivos + Always\ add\ letter\ (a,\ b,\ ...)\ to\ generated\ keys=Sempre adicionar uma letra (a, b, ...)às chaves geradas Default\ pattern=Ppadrão predefinido @@ -1433,6 +1435,12 @@ plain\ text=texto sem formatação + + + + + + diff --git a/src/main/resources/l10n/JabRef_pt_BR.properties b/src/main/resources/l10n/JabRef_pt_BR.properties index 8efbf0f319c..8ab46c8ca1f 100644 --- a/src/main/resources/l10n/JabRef_pt_BR.properties +++ b/src/main/resources/l10n/JabRef_pt_BR.properties @@ -131,6 +131,7 @@ Case\ sensitive=Sensível ao caso change\ assignment\ of\ entries=Alterar atribuição de referências + Change\ case=Modificar caso Change\ entry\ type=Modificar tipo de referência @@ -168,7 +169,6 @@ Copied=Copiado Copy=Copiar Copy\ title=Copiar título -Copy\ \\cite{citation\ key}=Copiar \\cite{citation key} Copy\ citation\ (html)=Copiar citação (html) Copy\ citation\ (text)=Copiar citação (texto) Copy\ citation\ key=Copiar chave de citação @@ -477,7 +477,6 @@ Year=Ano ISSN\ or\ journal\ name\ required\ for\ fetching\ journal\ information=ISSN ou nome do periódico necessário para buscar informações do periódico Fetch\ journal\ information\ online\ to\ show=Obter informações de periódicos online para exibir Allow\ sending\ ISSN\ to\ a\ JabRef\ online\ service\ (SCimago)\ for\ fetching\ journal\ information=Permitir o envio de ISSN para um serviço on-line JabRef (SCimago) para obter informações do periódico -Please\ enable\ journal\ information\ fetching\ in\ %0\ >\ %1=Por favor, habilite as informações do periódico a buscar em %0 > %1 Categories=Categorias ISSN=ISSN Publisher=Editora @@ -520,7 +519,6 @@ Last\ modified=Última modificação LaTeX\ AUX\ file\:=Arquivo AUX LaTeX\: Link=Linkar -Listen\ for\ remote\ operation\ on\ port=Escutar operações remotas na porta Memory\ stick\ mode\ -\ Store\ preferences\ in\ 'jabref.xml'\ in\ the\ app\ folder.=Modo de Memória Usb - Armazene as preferências no arquivo 'jabref.xml' na pasta do aplicativo. Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=Mostrar dicas avançadas (ou seja, dicas úteis, sugestões e explicações) @@ -1009,7 +1007,6 @@ Citation\ key\ generator=Gerador de chaves de citação Unable\ to\ open\ link.=Não foi possível abrir link. MIME\ type=MIME type -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Esta funcionalidade permite que novos arquivos sejam abertos ou importados para uma instância do JabRef já aberta ao invés de abrir uma nova instância. Por exemplo, isto é útil quando você abre um arquivo no JabRef a partir de ser navegador web. Note que isto irá previnir que você execute uma ou mais instâncias do JabRef ao mesmo tempo. Run\ fetcher=Executar o fetcher Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=Linha %0\: Chave de citação corrompida encontrada %1. @@ -2041,12 +2038,16 @@ Matching=Comparando Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=Mesmo que o --import, mas será importado para a aba aberta Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=Permitir números inteiros no campo 'edition' no modo BibTeX + + + Search\ for\ citations\ in\ LaTeX\ files...=Procure por citações nos arquivos LaTeX... LaTeX\ Citations\ Search\ Results=Resultados da Pesquisa de Citações LaTeX LaTeX\ files\ directory\:=Diretório de arquivos LaTeX\: LaTeX\ files\ found\:=Arquivos LaTeX encontrados\: files=arquivos Show\ 'LaTeX\ Citations'\ tab=Mostrar aba 'Citações LaTeX' + LaTeX\ Citations=Citações LaTeX Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=Pesquisar citações a essa referência nos arquivos LaTeX No\ citations\ found=Nenhuma citação encontrada @@ -2604,3 +2605,9 @@ Help\ on\ external\ applications=Ajuda em aplicativos externos Identifier-based\ Web\ Search=Pesquisa da Web baseada em identificadores Pushing\ citations\ to\ TeXShop\ is\ only\ possible\ on\ macOS\!=Só é possível enviar referências para TeXShop no macOS\! + + + + + + diff --git a/src/main/resources/l10n/JabRef_ru.properties b/src/main/resources/l10n/JabRef_ru.properties index fc318078628..ed338ade0eb 100644 --- a/src/main/resources/l10n/JabRef_ru.properties +++ b/src/main/resources/l10n/JabRef_ru.properties @@ -126,6 +126,7 @@ Case\ sensitive=С учетом регистра change\ assignment\ of\ entries=изменить назначение для записей + Change\ case=Изменить регистр Change\ entry\ type=Изменить тип записи @@ -163,7 +164,6 @@ Copied=Скопированное Copy=Копировать Copy\ title=Копировать заглавие -Copy\ \\cite{citation\ key}=Копировать \\ цитировать{citation key} Copy\ citation\ (html)=Копировать цитирование (html) Copy\ citation\ (text)=Копировать цитирование (текст) Copy\ citation\ key=Копировать ключ цитаты @@ -481,7 +481,6 @@ Last\ modified=Последнее изменение LaTeX\ AUX\ file\:=AUX-файл LaTeX\: Link=Ссылка -Listen\ for\ remote\ operation\ on\ port=Прослушивать удаленные подключения для порта Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=Отобразить расширенные подсказки (т.е. всплывающие подсказки, предположения и объяснения) @@ -944,7 +943,6 @@ Citation\ key\ generator=Генератор ключей цитирования Unable\ to\ open\ link.=Не удалось перейти по ссылке. MIME\ type=MIME-тип -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Эта функция позволяет открывать или импортировать файлы в работающий экземпляр JabRef без запуска нового экземпляра приложения. Например, при передаче файла в JabRef из веб-браузера. Обратите внимание, что эта функция не даст запустить несколько экземпляров JabRef одновременно. Run\ fetcher=Запустить выборку Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=Строка %0\: Найден поврежденный ключ цитаты %1. @@ -1927,12 +1925,16 @@ Matching=Соответствие Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=То же, что и --import, но будет импортировано в открытую вкладку Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=Разрешить целые числа в поле 'редакция' в режиме BibTeX + + + Search\ for\ citations\ in\ LaTeX\ files...=Поиск цитат в файлах LaTeX... LaTeX\ Citations\ Search\ Results=Результаты поиска по цитатам LaTeX LaTeX\ files\ directory\:=Папка с файлами LaTeX\: LaTeX\ files\ found\:=Найдены файлы LaTeX\: files=файлы Show\ 'LaTeX\ Citations'\ tab=Показывать вкладку "Цитаты LaTeX" + LaTeX\ Citations=Цитаты LaTeX Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=Поиск цитат для этой записи в файлах LaTeX No\ citations\ found=Цитаты не найдены @@ -2386,3 +2388,9 @@ This\ could\ indicate\ that\ JabRef\ did\ not\ shut\ down\ cleanly\ last\ time\ + + + + + + diff --git a/src/main/resources/l10n/JabRef_sv.properties b/src/main/resources/l10n/JabRef_sv.properties index 8008a27b056..b6fe1763992 100644 --- a/src/main/resources/l10n/JabRef_sv.properties +++ b/src/main/resources/l10n/JabRef_sv.properties @@ -116,6 +116,7 @@ Case\ sensitive=Shiftlägeskänlig change\ assignment\ of\ entries=ändra tilldelning av poster + Change\ case=Ändra shiftläge Change\ entry\ type=Ändra posttyp @@ -152,6 +153,7 @@ Copied=Kopierade Copy=Kopiera Copy\ title=Kopiera titel +Copy\ citation\ (html)=Kopiera referensnyckel (html) Copy\ citation\ key=Kopiera referensnyckel Copy\ citation\ key\ and\ title=Kopiera referensnyckel och titel @@ -457,7 +459,6 @@ Last\ modified=Senast ändrad LaTeX\ AUX\ file\:=LaTeX AUX-fil\: Link=Länk -Listen\ for\ remote\ operation\ on\ port=Lyssna efter fjärrstyrning på port Memory\ stick\ mode\ -\ Store\ preferences\ in\ 'jabref.xml'\ in\ the\ app\ folder.=Portabelt läge - Spara inställningar i 'jabref.xml' i appmappen. Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=Visa avancerade tips (dvs. hjälpsamma verktygstips, förslag och förklaringar) @@ -1451,6 +1452,10 @@ New\ Filename=Nytt filnamn + + + + Group\ color=Gruppfärg @@ -1480,6 +1485,7 @@ Please\ provide\ a\ valid\ aux\ file.=Ange en giltig aux-fil. Starts\ the\ extraction\ and\ adds\ the\ resulting\ entries\ to\ the\ currently\ opened\ database=Startar extraktionen och lägger till de resulterande posterna till det öppna biblioteket +Citation\ key\ filters=Filter för referensnycklar Required=Obligatorisk @@ -1568,3 +1574,9 @@ Writing\ metadata\ to\ %0=Skriver metadata till %0 Select\ entry=Välj post Help\ on\ external\ applications=Hjälp med externa program + + + + + + diff --git a/src/main/resources/l10n/JabRef_tl.properties b/src/main/resources/l10n/JabRef_tl.properties index cd0b42509f6..11236d85373 100644 --- a/src/main/resources/l10n/JabRef_tl.properties +++ b/src/main/resources/l10n/JabRef_tl.properties @@ -106,6 +106,7 @@ Case\ sensitive=Sensitibo ang kaso change\ assignment\ of\ entries=palitan ang pagtatalaga ng mga entries + Change\ case=Palitan ang kaso Change\ entry\ type=Palitan ang uri ng entry @@ -397,7 +398,6 @@ Language=Wika Last\ modified=Ang huling nabago Link=Ang link -Listen\ for\ remote\ operation\ on\ port=Makinig para sa remote na operasyon sa port Manage\ custom\ exports=Pamahalaan ang kustom na pagpapalabas @@ -766,7 +766,6 @@ Error\ while\ fetching\ from\ %0=Error habang kinukuha mula sa %0 Unable\ to\ open\ link.=Hindi mabuksan ang link. MIME\ type=Uri ng MIME -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Ang tampok na ito ay nagbibigay-daan sa mga bagong file na mabuksan o ma-import sa isang tumatakbo na halimbawa ng JabRef sa halip ng pagbubukas ng isang bagong pagkakataon. Halimbawa, ito ay kapaki-pakinabang kapag binuksan mo ang isang file sa JabRef mula sa iyong web browser. Tandaan na ito ay pipigil sa iyo mula sa pagpapatakbo ng higit sa isang halimbawa ng JabRef sa isang pagkakataon. Download\ from\ URL=I-download mula sa URL Rename\ field=Palitan ang pangalan ng patlang @@ -1228,6 +1227,10 @@ Main\ layout\ file=Pangunahing layout ng file + + + + Always\ add\ letter\ (a,\ b,\ ...)\ to\ generated\ keys=Laging magdagdag ng titik (a, b, ...) sa mga nabuong key Default\ pattern=Default na pattern @@ -1306,6 +1309,12 @@ plain\ text=plain na teksto + + + + + + diff --git a/src/main/resources/l10n/JabRef_tr.properties b/src/main/resources/l10n/JabRef_tr.properties index 83a6b833b1a..43909d730bf 100644 --- a/src/main/resources/l10n/JabRef_tr.properties +++ b/src/main/resources/l10n/JabRef_tr.properties @@ -131,6 +131,7 @@ Case\ sensitive=Büyük/küçük harfe duyarlı change\ assignment\ of\ entries=girdilerin atanmasını değiştir + Change\ case=Büyük/küçük harf değiştir Change\ entry\ type=Girdi türünü değiştir @@ -168,7 +169,6 @@ Copied=Kopyalandı Copy=Kopyala Copy\ title=Başlığı kopyala -Copy\ \\cite{citation\ key}=\\cite{citation key}''nı kopyala Copy\ citation\ (html)=Atfı kopyala (html) Copy\ citation\ (text)=Atfı kopyala (metin) Copy\ citation\ key=Atıf anahtarını kopyala @@ -470,7 +470,6 @@ Journal\ Information=Dergi Bilgisi Year=Yıl ISSN\ or\ journal\ name\ required\ for\ fetching\ journal\ information=Dergi bilgisini getirmek için ISSN ya da dergi adı gerekiyor Allow\ sending\ ISSN\ to\ a\ JabRef\ online\ service\ (SCimago)\ for\ fetching\ journal\ information=Dergi bilgisini getirmek için ISSN'i bir JabRef çevrim içi servisine (SCimago) göndermeye izin ver -Please\ enable\ journal\ information\ fetching\ in\ %0\ >\ %1=Lütfen %0 > %1 'de dergi bilgisini getirmeyi etkinleştirin Categories=Kategoriler ISSN=ISSN Publisher=Yayımcı @@ -511,7 +510,6 @@ Last\ modified=Son değiştirme LaTeX\ AUX\ file\:=LaTeX AUX dosyası\: Link=Bağlantı -Listen\ for\ remote\ operation\ on\ port=Bağlantı noktasındaki uzak işlemi dinle Memory\ stick\ mode\ -\ Store\ preferences\ in\ 'jabref.xml'\ in\ the\ app\ folder.=Bellek çubuğu kipi - Tercihleri uygulama klasöründeki ''jabref.xml''''e kaydet. Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=İleri düzey ipuçlarını göster (yardımsever araç ipuçları, öneriler ve açıklama) @@ -993,7 +991,6 @@ Citation\ key\ generator=Atıf anahtarı oluşturucu Unable\ to\ open\ link.=Bağlantı açılamadı. MIME\ type=MIME türü -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Bu özellik yeni dosyaların yeni bir oturum açmaktansa halen çalışmakta olan bir
    JabRef oturumu içine açılması ya da aktarılmasını sağlar. Örneğin bu, tarayıcınızdan bir dosyayı
    JabRef içine açtığnızda kullanışlıdır.
    Bunun, birden fazla JabRef oturumunu aynı anda çalıştırmanızı önleyeceğini not ediniz. Run\ fetcher=Getiriciyi çalıştır Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=Satır %0\: Bozulmuş atıf anahtarı bulundu %1. @@ -2016,12 +2013,16 @@ Matching=Eşleştirme Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=İçe aktarma gibi, fakat açık sekmeye aktarılacak Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=BibTeX kipinin 'sürüm' alanında tam sayılara izin ver + + + Search\ for\ citations\ in\ LaTeX\ files...=LaTeX dosyalarında alıntıları ara... LaTeX\ Citations\ Search\ Results=LaTeX Alıntıları Arama Sonuçları LaTeX\ files\ directory\:=LaTeX dosyaları dizini\: LaTeX\ files\ found\:=Bulunan LaTeX dosyaları\: files=dosyalar Show\ 'LaTeX\ Citations'\ tab='LaTeX Alıntıları' sekmesini göster + LaTeX\ Citations=LaTeX Alıntıları Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=LaTeX dosyalarında bu girdi için alıntıları ara No\ citations\ found=Alıntı bulunamadı @@ -2547,3 +2548,9 @@ Automatically\ search\ and\ show\ unlinked\ files\ in\ the\ entry\ editor=Bağla + + + + + + diff --git a/src/main/resources/l10n/JabRef_uk.properties b/src/main/resources/l10n/JabRef_uk.properties index c92a52089e3..ee2eee21693 100644 --- a/src/main/resources/l10n/JabRef_uk.properties +++ b/src/main/resources/l10n/JabRef_uk.properties @@ -642,6 +642,17 @@ Application\ to\ push\ entries\ to=Заявка на push матеріалів + + + + + + + + + + + diff --git a/src/main/resources/l10n/JabRef_vi.properties b/src/main/resources/l10n/JabRef_vi.properties index 86d41a736a0..0e19694e10a 100644 --- a/src/main/resources/l10n/JabRef_vi.properties +++ b/src/main/resources/l10n/JabRef_vi.properties @@ -112,6 +112,7 @@ Case\ sensitive=Phân biệt chữ hoa/thường change\ assignment\ of\ entries=đổi phép gán các mục + Change\ case=Đổi chữ hoa/thường Change\ entry\ type=Đổi kiểu của mục @@ -409,7 +410,6 @@ Language=Ngôn ngữ Last\ modified=Thay đổi lần sau cùng Link=Liên kết -Listen\ for\ remote\ operation\ on\ port=Lắng nghe lệnh chạy từ xa tại cổng Manage\ custom\ exports=Quản lý các phép xuất tùy chọn @@ -762,7 +762,6 @@ Error\ while\ fetching\ from\ %0=Lỗi khi lấy về từ %0 Unable\ to\ open\ link.=Không thể mở liên kết. MIME\ type=Kiểu MIME -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=Tính chất này cho phép các tập tin mới có thể được mở hoặc nhập vào một phiên JabRef đang chạy thay vì phải mở một phiên làm việc mới. Điều này có ích, ví dụ như khi bạn mở một tập tin trong JabRef từ trình duyệt web của mình. Lưu ý rằng điều này sẽ không cho phép bạn chạy nhiều hơn một phiên làm việc của JabRef cùng lúc. Download\ from\ URL=Kéo từ URL Rename\ field=Đổi tên dữ liệu @@ -1016,6 +1015,10 @@ Main\ layout\ file=Tập tin trình bày chính + + + + Always\ add\ letter\ (a,\ b,\ ...)\ to\ generated\ keys=Luôn thêm mẫu tự (a, b, ...) để tạo ra các khóa Default\ pattern=Kiểu mặc định @@ -1099,3 +1102,9 @@ This\ could\ indicate\ that\ JabRef\ did\ not\ shut\ down\ cleanly\ last\ time\ + + + + + + diff --git a/src/main/resources/l10n/JabRef_zh_CN.properties b/src/main/resources/l10n/JabRef_zh_CN.properties index c94dad5d939..af03a733d76 100644 --- a/src/main/resources/l10n/JabRef_zh_CN.properties +++ b/src/main/resources/l10n/JabRef_zh_CN.properties @@ -131,6 +131,7 @@ Case\ sensitive=区分大小写 change\ assignment\ of\ entries=修改记录的组分配 + Change\ case=修改大小写 Change\ entry\ type=更改条目类型 @@ -168,7 +169,6 @@ Copied=已复制 Copy=复制 Copy\ title=复制标题 -Copy\ \\cite{citation\ key}=复制 \\cite{citation key} Copy\ citation\ (html)=复制引用 (html) Copy\ citation\ (text)=复制引用 (text) Copy\ citation\ key=复制 citationkey @@ -471,7 +471,6 @@ Year=年份 ISSN\ or\ journal\ name\ required\ for\ fetching\ journal\ information=ISSN 或者期刊名称以用于获取期刊信息 Fetch\ journal\ information\ online\ to\ show=在线获取期刊信息以显示 Allow\ sending\ ISSN\ to\ a\ JabRef\ online\ service\ (SCimago)\ for\ fetching\ journal\ information=允许将 ISSN 发送到一个 JabRef 在线服务 (SCimago) 以获取期刊信息 -Please\ enable\ journal\ information\ fetching\ in\ %0\ >\ %1=请在 %0 > %1 中启用获取期刊信息 Categories=分类 ISSN=ISSN Publisher=出版商 @@ -506,7 +505,6 @@ Last\ modified=上次修改的 LaTeX\ AUX\ file\:=LaTeX AUX 文件\: Link=链接 -Listen\ for\ remote\ operation\ on\ port=监听端口 Memory\ stick\ mode\ -\ Store\ preferences\ in\ 'jabref.xml'\ in\ the\ app\ folder.=记忆棒模式:将 jabref.xml 保存到程序所在目录 Show\ advanced\ hints\ (i.e.\ helpful\ tooltips,\ suggestions\ and\ explanation)=显示高级建议(如浮动提示、建议、解释说明) @@ -988,7 +986,6 @@ Citation\ key\ generator=Citationkey Unable\ to\ open\ link.=无法打开链接。 MIME\ type=MIME 类型 -This\ feature\ lets\ new\ files\ be\ opened\ or\ imported\ into\ an\ already\ running\ instance\ of\ JabRef\ instead\ of\ opening\ a\ new\ instance.\ For\ instance,\ this\ is\ useful\ when\ you\ open\ a\ file\ in\ JabRef\ from\ your\ web\ browser.\ Note\ that\ this\ will\ prevent\ you\ from\ running\ more\ than\ one\ instance\ of\ JabRef\ at\ a\ time.=在当前运行的 JabRef 中打开或者导入新文件,而非新建另一个 JabRef 窗口。例如,当通过浏览器调用 JabRef 打开文件时,这个选项会比较有用。注意:它将阻止同时运行多个 JabRef 实例。 Run\ fetcher=Run fetcher Line\ %0\:\ Found\ corrupted\ citation\ key\ %1.=第 %0 行\: 发现错误的Citationkey %1. @@ -2013,12 +2010,16 @@ Matching=Matching Same\ as\ --import,\ but\ will\ be\ imported\ to\ the\ opened\ tab=Same as --import, but will be imported to the opened tab Allow\ integers\ in\ 'edition'\ field\ in\ BibTeX\ mode=允许在 BibTeX 模式的 edition 字段中使用整数 + + + Search\ for\ citations\ in\ LaTeX\ files...=在LaTeX文件中搜索引文 LaTeX\ Citations\ Search\ Results=LaTeX 引文搜索结果 LaTeX\ files\ directory\:=LaTeX 文件目录: LaTeX\ files\ found\:=发现的 LaTeX 文件 files=files Show\ 'LaTeX\ Citations'\ tab=显示 “LaTeX Citations” 选项卡 + LaTeX\ Citations=LaTeX 引文 Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=在该条目的LaTeX文件中搜索引文 No\ citations\ found=没有找到引用 @@ -2553,3 +2554,9 @@ Processing...=处理中... + + + + + + diff --git a/src/main/resources/l10n/JabRef_zh_TW.properties b/src/main/resources/l10n/JabRef_zh_TW.properties index dd5e814920e..968b02e35e1 100644 --- a/src/main/resources/l10n/JabRef_zh_TW.properties +++ b/src/main/resources/l10n/JabRef_zh_TW.properties @@ -123,6 +123,7 @@ Case\ sensitive=區分大小寫 change\ assignment\ of\ entries=修改條目的分發組別 + Change\ case=修改大小寫 Change\ entry\ type=修改條目類型 @@ -159,7 +160,6 @@ Copied=已複製 Copy=複製 Copy\ title=複製標題 -Copy\ \\cite{citation\ key}=複製 \\cite{citation key} Copy\ citation\ key=複製引用鍵值(citation key) Copy\ citation\ key\ and\ link=複製引用鍵與連結 Copy\ citation\ key\ and\ title=複製引用鍵與標題 @@ -952,6 +952,10 @@ Set\ rank\ to\ five=設定評分為 5 級 + + + + Order=順序 @@ -1075,6 +1079,12 @@ Select\ directory=選擇資料夾 + + + + + + From 82f174978e412216fc02cc97ef5e93d8e2b4aa33 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 22 Dec 2023 18:21:45 +0100 Subject: [PATCH 088/144] Fix (some) fetcher tests (#10710) --- .../logic/importer/fetcher/DoiFetcher.java | 2 +- .../logic/importer/fetcher/TitleFetcher.java | 3 +- .../importer/fetcher/ArXivFetcherTest.java | 2 +- .../fetcher/AstrophysicsDataSystemTest.java | 66 +++++++++---------- .../importer/fetcher/DoiFetcherTest.java | 26 ++++---- .../importer/fetcher/LOBIDFetcherTest.java | 6 ++ .../logic/importer/fetcher/MedraTest.java | 4 +- .../fetcher/PagedSearchFetcherTest.java | 13 +++- .../importer/fetcher/TitleFetcherTest.java | 19 +++--- 9 files changed, 76 insertions(+), 65 deletions(-) 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 276b9883d4d..0c434ffc004 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java @@ -132,7 +132,7 @@ public Optional performSearchById(String identifier) throws FetcherExc URLConnection openConnection; try { openConnection = download.openConnection(); - bibtexString = URLDownload.asString(openConnection); + bibtexString = URLDownload.asString(openConnection).trim(); } catch (IOException 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) { diff --git a/src/main/java/org/jabref/logic/importer/fetcher/TitleFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/TitleFetcher.java index 04bde616060..ce10b69b6da 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/TitleFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/TitleFetcher.java @@ -36,8 +36,7 @@ public Optional performSearchById(String identifier) throws FetcherExc return Optional.empty(); } - BibEntry entry = new BibEntry(); - entry.setField(StandardField.TITLE, identifier); + BibEntry entry = new BibEntry().withField(StandardField.TITLE, identifier); Optional doi = WebFetchers.getIdFetcherForIdentifier(DOI.class).findIdentifier(entry); if (doi.isEmpty()) { diff --git a/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java index 25556bb9577..6f68a5d25d2 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java @@ -341,7 +341,7 @@ void searchEntryByOldId() throws Exception { .withField(StandardField.MONTH, "#oct#") .withField(StandardField.ISSN, "1434-6052") .withField(StandardField.ABSTRACT, "Multi-electron production is studied at high electron transverse momentum in positron- and electron-proton collisions using the H1 detector at HERA. The data correspond to an integrated luminosity of 115 pb-1. Di-electron and tri-electron event yields are measured. Cross sections are derived in a restricted phase space region dominated by photon-photon collisions. In general good agreement is found with the Standard Model predictions. However, for electron pair invariant masses above 100 GeV, three di-electron events and three tri-electron events are observed, compared to Standard Model expectations of 0.30 \\pm 0.04 and 0.23 \\pm 0.04, respectively.") - .withField(StandardField.PUBLISHER, "Springer Science and Business Media {LLC}") + .withField(StandardField.PUBLISHER, "Springer Science and Business Media LLC") .withField(StandardField.EPRINT, "hep-ex/0307015") .withField(StandardField.FILE, ":http\\://arxiv.org/pdf/hep-ex/0307015v1:PDF") .withField(StandardField.EPRINTTYPE, "arXiv") diff --git a/src/test/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystemTest.java b/src/test/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystemTest.java index 8ba31caec84..afd4d51e573 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystemTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystemTest.java @@ -29,11 +29,40 @@ public class AstrophysicsDataSystemTest implements PagedSearchFetcherTest { private AstrophysicsDataSystem fetcher; private BibEntry diezSliceTheoremEntry; - private BibEntry famaeyMcGaughEntry; + private BibEntry famaeyMcGaughEntry = new BibEntry(StandardEntryType.Article) + .withCitationKey("2012LRR....15...10F") + .withField(StandardField.AUTHOR, "Famaey, Beno{\\^\\i}t and McGaugh, Stacy S.") + .withField(StandardField.TITLE, "Modified Newtonian Dynamics (MOND): Observational Phenomenology and Relativistic Extensions") + .withField(StandardField.JOURNAL, "Living Reviews in Relativity") + .withField(StandardField.YEAR, "2012") + .withField(StandardField.VOLUME, "15") + .withField(StandardField.MONTH, "#dec#") + .withField(StandardField.NUMBER, "1") + .withField(StandardField.ARCHIVEPREFIX, "arXiv") + .withField(StandardField.DOI, "10.12942/lrr-2012-10") + .withField(StandardField.PRIMARYCLASS, "astro-ph.CO") + .withField(StandardField.EID, "10") + .withField(StandardField.EPRINT, "1112.3960") + .withField(StandardField.PAGES, "10") + .withField(StandardField.KEYWORDS, "astronomical observations, Newtonian limit, equations of motion, extragalactic astronomy, cosmology, theories of gravity, fundamental physics, astrophysics, Modified Newtonian Dynamics (MOND), Dark Matter, Baryonic Tully-Fisher Relation (BTFR), MOND Theories, Tidal Dwarf Galaxies (TDGs), Astrophysics - Cosmology and Nongalactic Astrophysics, Astrophysics - Astrophysics of Galaxies, General Relativity and Quantum Cosmology, High Energy Physics - Phenomenology, High Energy Physics - Theory") + .withField(StandardField.URL, "https://ui.adsabs.harvard.edu/abs/2012LRR....15...10F"); private BibEntry sunWelchEntry; private BibEntry xiongSunEntry; private BibEntry ingersollPollardEntry; - private BibEntry luceyPaulEntry; + private BibEntry luceyPaulEntry = new BibEntry(StandardEntryType.Article) + .withCitationKey("2000JGR...10520297L") + .withField(StandardField.AUTHOR, "Lucey, Paul G. and Blewett, David T. and Jolliff, Bradley L.") + .withField(StandardField.DOI, "10.1029/1999JE001117") + .withField(StandardField.JOURNAL, "\\jgr") + .withField(StandardField.KEYWORDS, "Planetology: Solid Surface Planets: Composition, Planetology: Solid Surface Planets: Remote sensing, Planetology: Solid Surface Planets: Surface materials and properties, Planetology: Solar System Objects: Moon (1221)") + .withField(StandardField.PAGES, "20297-20306") + .withField(StandardField.TITLE, "Lunar iron and titanium abundance algorithms based on final processing of Clementine ultraviolet-visible images") + .withField(StandardField.VOLUME, "105") + .withField(StandardField.YEAR, "2000") + .withField(StandardField.URL, "https://ui.adsabs.harvard.edu/abs/2000JGR...10520297L") + .withField(StandardField.MONTH, "#jan#") + .withField(StandardField.NUMBER, "E8") + .withField(StandardField.ABSTRACT, "The Clementine mission to the Moon returned global imaging data collected by the ultraviolet visible (UVVIS) camera. This data set is now in a final state of calibration, and a five-band multispectral digital image model (DIM) of the lunar surface will soon be available to the science community. We have used observations of the lunar sample-return sites and stations extracted from the final DIM in conjunction with compositional information for returned lunar soils to revise our previously published algorithms for the spectral determination of the FeO and TiO$_{2}$ content of the lunar surface. The algorithms successfully normalize the effects of space weathering so that composition may be determined without regard to a surface's state of maturity. These algorithms permit anyone with access to the standard archived DIM to construct high spatial resolution maps of FeO and TiO$_{2}$ abundance. Such maps will be of great utility in a variety of lunar geologic studies."); @BeforeEach public void setUp() throws Exception { @@ -61,24 +90,6 @@ public void setUp() throws Exception { .withField(StandardField.ABSTRACT, "We establish a general slice theorem for the action of a locally convex Lie group on a locally convex manifold, which generalizes the classical slice theorem of Palais to infinite dimensions. We discuss two important settings under which the assumptions of this theorem are fulfilled. First, using Gl{\\\"o}ckner's inverse function theorem, we show that the linear action of a compact Lie group on a Fr{\\'e}chet space admits a slice. Second, using the Nash--Moser theorem, we establish a slice theorem for the tame action of a tame Fr{\\'e}chet Lie group on a tame Fr{\\'e}chet manifold. For this purpose, we develop the concept of a graded Riemannian metric, which allows the construction of a path-length metric compatible with the manifold topology and of a local addition. Finally, generalizing a classical result in finite dimensions, we prove that the existence of a slice implies that the decomposition of the manifold into orbit types of the group action is a stratification."); - famaeyMcGaughEntry = new BibEntry(StandardEntryType.Article) - .withCitationKey("2012LRR....15...10F") - .withField(StandardField.AUTHOR, "Famaey, Beno{\\^\\i}t and McGaugh, Stacy S.") - .withField(StandardField.TITLE, "Modified Newtonian Dynamics (MOND): Observational Phenomenology and Relativistic Extensions") - .withField(StandardField.JOURNAL, "Living Reviews in Relativity") - .withField(StandardField.YEAR, "2012") - .withField(StandardField.VOLUME, "15") - .withField(StandardField.MONTH, "#sep#") - .withField(StandardField.NUMBER, "1") - .withField(StandardField.ARCHIVEPREFIX, "arXiv") - .withField(StandardField.DOI, "10.12942/lrr-2012-10") - .withField(StandardField.PRIMARYCLASS, "astro-ph.CO") - .withField(StandardField.EID, "10") - .withField(StandardField.EPRINT, "1112.3960") - .withField(StandardField.PAGES, "10") - .withField(StandardField.KEYWORDS, "astronomical observations, Newtonian limit, equations of motion, extragalactic astronomy, cosmology, theories of gravity, fundamental physics, astrophysics, Astrophysics - Cosmology and Nongalactic Astrophysics, Astrophysics - Astrophysics of Galaxies, General Relativity and Quantum Cosmology, High Energy Physics - Phenomenology, High Energy Physics - Theory") - .withField(StandardField.URL, "https://ui.adsabs.harvard.edu/abs/2012LRR....15...10F"); - sunWelchEntry = new BibEntry(StandardEntryType.Article) .withCitationKey("2012NatMa..11...44S") .withField(StandardField.AUTHOR, "Sun, Yanming and Welch, Gregory C. and Leong, Wei Lin and Takacs, Christopher J. and Bazan, Guillermo C. and Heeger, Alan J.") @@ -119,21 +130,6 @@ public void setUp() throws Exception { .withField(StandardField.VOLUME, "52") .withField(StandardField.YEAR, "1982") .withField(StandardField.URL, "https://ui.adsabs.harvard.edu/abs/1982Icar...52...62I"); - - luceyPaulEntry = new BibEntry(StandardEntryType.Article) - .withCitationKey("2000JGR...10520297L") - .withField(StandardField.AUTHOR, "Lucey, Paul G. and Blewett, David T. and Jolliff, Bradley L.") - .withField(StandardField.DOI, "10.1029/1999JE001117") - .withField(StandardField.JOURNAL, "\\jgr") - .withField(StandardField.KEYWORDS, "Planetology: Solid Surface Planets: Composition, Planetology: Solid Surface Planets: Remote sensing, Planetology: Solid Surface Planets: Surface materials and properties, Planetology: Solar System Objects: Moon (1221), Earth Science") - .withField(StandardField.PAGES, "20297-20306") - .withField(StandardField.TITLE, "Lunar iron and titanium abundance algorithms based on final processing of Clementine ultraviolet-visible images") - .withField(StandardField.VOLUME, "105") - .withField(StandardField.YEAR, "2000") - .withField(StandardField.URL, "https://ui.adsabs.harvard.edu/abs/2000JGR...10520297L") - .withField(StandardField.MONTH, "#jan#") - .withField(StandardField.NUMBER, "E8") - .withField(StandardField.ABSTRACT, "The Clementine mission to the Moon returned global imaging data collected by the ultraviolet visible (UVVIS) camera. This data set is now in a final state of calibration, and a five-band multispectral digital image model (DIM) of the lunar surface will soon be available to the science community. We have used observations of the lunar sample-return sites and stations extracted from the final DIM in conjunction with compositional information for returned lunar soils to revise our previously published algorithms for the spectral determination of the FeO and TiO$_{2}$ content of the lunar surface. The algorithms successfully normalize the effects of space weathering so that composition may be determined without regard to a surface's state of maturity. These algorithms permit anyone with access to the standard archived DIM to construct high spatial resolution maps of FeO and TiO$_{2}$ abundance. Such maps will be of great utility in a variety of lunar geologic studies."); } @Test 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 da157b42347..e434ad1da99 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/DoiFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/DoiFetcherTest.java @@ -23,19 +23,20 @@ public class DoiFetcherTest { private final BibEntry bibEntryBurd2011 = new BibEntry(StandardEntryType.Book) .withCitationKey("Burd_2011") - .withField(StandardField.TITLE, "Java{\\textregistered} For Dummies{\\textregistered}") + .withField(StandardField.TITLE, "Java® For Dummies®") .withField(StandardField.PUBLISHER, "Wiley") .withField(StandardField.YEAR, "2011") - .withField(StandardField.AUTHOR, "Barry Burd") - .withField(StandardField.MONTH, "jul") - .withField(StandardField.DOI, "10.1002/9781118257517"); + .withField(StandardField.AUTHOR, "Burd, Barry") + .withField(StandardField.MONTH, "#jul#") + .withField(StandardField.DOI, "10.1002/9781118257517") + .withField(StandardField.ISBN, "9781118257517"); 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)") - .withField(StandardField.MONTH, "jul") - .withField(StandardField.PUBLISHER, "{IEEE}") - .withField(StandardField.TITLE, "{BPEL}4Chor: Extending {BPEL} for Modeling Choreographies") + .withField(StandardField.AUTHOR, "Decker, Gero and Kopp, Oliver and Leymann, Frank and Weske, Mathias") + .withField(StandardField.BOOKTITLE, "IEEE International Conference on Web Services (ICWS 2007)") + .withField(StandardField.MONTH, "#jul#") + .withField(StandardField.PUBLISHER, "IEEE") + .withField(StandardField.TITLE, "BPEL4Chor: Extending BPEL for Modeling Choreographies") .withField(StandardField.YEAR, "2007") .withField(StandardField.DOI, "10.1109/icws.2007.59"); private final BibEntry bibEntryIannarelli2019 = new BibEntry(StandardEntryType.Article) @@ -57,14 +58,15 @@ public class DoiFetcherTest { .withField(StandardField.VOLUME, "77"); 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.AUTHOR, "Stenzel, L. and Hayward, A. L. C. and Schollwöck, U. and Heidrich-Meisner, F.") .withField(StandardField.JOURNAL, "Physical Review A") .withField(StandardField.TITLE, "Topological phases in the Fermi-Hofstadter-Hubbard model on hybrid-space ladders") .withField(StandardField.YEAR, "2020") - .withField(StandardField.MONTH, "aug") + .withField(StandardField.MONTH, "#aug#") .withField(StandardField.VOLUME, "102") .withField(StandardField.DOI, "10.1103/physreva.102.023315") - .withField(StandardField.PUBLISHER, "American Physical Society ({APS})") + .withField(StandardField.ISSN, "2469-9934") + .withField(StandardField.PUBLISHER, "American Physical Society (APS)") .withField(StandardField.PAGES, "023315") .withField(StandardField.NUMBER, "2"); diff --git a/src/test/java/org/jabref/logic/importer/fetcher/LOBIDFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/LOBIDFetcherTest.java index 978dd23c096..9b5de6f84f0 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/LOBIDFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/LOBIDFetcherTest.java @@ -29,6 +29,12 @@ void setUp() { fetcher = new LOBIDFetcher(importerPreferences); } + @Override + public String queryForUniqueResultsPerPage() { + // LOBID does not have unique entries per page for "Software"; + return "Discussion"; + } + @Test void searchByQueryFindsEntry() throws Exception { BibEntry firstArticle = new BibEntry(StandardEntryType.Book) 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 3d940312fa1..ae5a867ee76 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/MedraTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/MedraTest.java @@ -3,8 +3,8 @@ import java.util.Optional; import java.util.stream.Stream; -import org.jabref.logic.importer.FetcherClientException; import org.jabref.logic.importer.FetcherException; +import org.jabref.logic.importer.FetcherServerException; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.StandardEntryType; @@ -83,7 +83,7 @@ public void testPerformSearchEmptyDOI() throws FetcherException { @Test public void testPerformNonExistent() throws FetcherException { - assertThrows(FetcherClientException.class, () -> fetcher.performSearchById("10.1016/j.bjoms.2007.08.004")); + assertThrows(FetcherServerException.class, () -> fetcher.performSearchById("10.1016/j.bjoms.2007.08.004")); } @ParameterizedTest diff --git a/src/test/java/org/jabref/logic/importer/fetcher/PagedSearchFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/PagedSearchFetcherTest.java index 43902fe9fa2..dad5618923c 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/PagedSearchFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/PagedSearchFetcherTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; /** @@ -13,17 +14,25 @@ */ public interface PagedSearchFetcherTest { + default String queryForUniqueResultsPerPage() { + return "Software"; + } + /** * Ensure that different page return different entries */ @Test default void pageSearchReturnsUniqueResultsPerPage() throws Exception { - String query = "Software"; + String query = queryForUniqueResultsPerPage(); Page firstPage = getPagedFetcher().performSearchPaged(query, 0); Page secondPage = getPagedFetcher().performSearchPaged(query, 1); + // Both pages need to be filled with contents to be valid for checking containment + assertEquals(20, firstPage.getSize()); + assertEquals(20, secondPage.getSize()); + for (BibEntry entry : firstPage.getContent()) { - assertFalse(secondPage.getContent().contains(entry)); + assertFalse(secondPage.getContent().contains(entry), "%s contained in %s".formatted(entry, secondPage.getContent())); } } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/TitleFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/TitleFetcherTest.java index 9a4d229c0e2..5048ff20efb 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/TitleFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/TitleFetcherTest.java @@ -26,16 +26,15 @@ public class TitleFetcherTest { public void setUp() { fetcher = new TitleFetcher(mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS)); - bibEntryBischof2009 = new BibEntry(); - bibEntryBischof2009.setType(StandardEntryType.InProceedings); - bibEntryBischof2009.setCitationKey("Bischof_2009"); - bibEntryBischof2009.setField(StandardField.AUTHOR, "Marc Bischof and Oliver Kopp and Tammo van Lessen and Frank Leymann"); - bibEntryBischof2009.setField(StandardField.BOOKTITLE, "2009 35th Euromicro Conference on Software Engineering and Advanced Applications"); - bibEntryBischof2009.setField(StandardField.PUBLISHER, "{IEEE}"); - bibEntryBischof2009.setField(StandardField.TITLE, "{BPELscript}: A Simplified Script Syntax for {WS}-{BPEL} 2.0"); - bibEntryBischof2009.setField(StandardField.YEAR, "2009"); - bibEntryBischof2009.setField(StandardField.MONTH, "aug"); - bibEntryBischof2009.setField(StandardField.DOI, "10.1109/seaa.2009.21"); + bibEntryBischof2009 = new BibEntry(StandardEntryType.InProceedings) + .withCitationKey("Bischof_2009") + .withField(StandardField.AUTHOR, "Bischof, Marc and Kopp, Oliver and van Lessen, Tammo and Leymann, Frank") + .withField(StandardField.BOOKTITLE, "2009 35th Euromicro Conference on Software Engineering and Advanced Applications") + .withField(StandardField.PUBLISHER, "IEEE") + .withField(StandardField.TITLE, "BPELscript: A Simplified Script Syntax for WS-BPEL 2.0") + .withField(StandardField.YEAR, "2009") + .withField(StandardField.MONTH, "#aug#") + .withField(StandardField.DOI, "10.1109/seaa.2009.21"); } @Test From 31467beb777ce8cd7e5d9db7961f7a0a6b7878e1 Mon Sep 17 00:00:00 2001 From: Ziyao-Jin <141754585+Ziyao-Jin@users.noreply.github.com> Date: Sat, 23 Dec 2023 22:31:17 +0800 Subject: [PATCH 089/144] Add auto group colour assignment (#10521) * Add auto group colour assignment * Add clickable option for auto colour assignment * Change button to checkbox * Start working on using the context of the current group for coloring Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> * First short for colorful subgroups * When creating a new group, it inhertics the icon of the parent group * Add checkbox for coloring of groups Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> * Help button leaves dialog opened Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> * Fix missing "Collect by" header 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 + build.gradle | 3 + src/main/java/module-info.java | 2 + .../jabref/gui/actions/StandardActions.java | 1 + .../jabref/gui/groups/GroupColorPicker.java | 62 ++++++++++++++ .../org/jabref/gui/groups/GroupDialog.fxml | 37 ++++----- .../jabref/gui/groups/GroupDialogView.java | 81 ++++++++++++++++--- .../gui/groups/GroupDialogViewModel.java | 48 ++++++++--- .../jabref/gui/groups/GroupTreeViewModel.java | 2 + .../logic/groups/DefaultGroupsFactory.java | 5 +- .../jabref/model/groups/AbstractGroup.java | 2 +- .../gui/groups/GroupColorPickerTest.java | 49 +++++++++++ .../gui/groups/GroupDialogViewModelTest.java | 6 +- 13 files changed, 258 insertions(+), 41 deletions(-) create mode 100644 src/main/java/org/jabref/gui/groups/GroupColorPicker.java create mode 100644 src/test/java/org/jabref/gui/groups/GroupColorPickerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 606bf9aa981..20899f15b4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added [scholar.archive.org](https://scholar.archive.org/) as a new fetcher. [#10498](https://github.com/JabRef/jabref/issues/10498) - We integrated predatory journal checking as part of the Integrity Checker based on the [check-bib-for-predatory](https://github.com/CfKu/check-bib-for-predatory). [koppor#348](https://github.com/koppor/jabref/issues/348) - We added a 'More options' section in the main table right click menu opening the preferences dialog. [#9432](https://github.com/JabRef/jabref/issues/9432) +- When creating a new group, it inherits the icon of the parent group. [#10521](https://github.com/JabRef/jabref/pull/10521) ### Changed diff --git a/build.gradle b/build.gradle index 5ca46c13169..868dd933e7f 100644 --- a/build.gradle +++ b/build.gradle @@ -235,6 +235,9 @@ dependencies { // Allow objects "magically" to be mapped to JSON using GSON // implementation 'org.glassfish.jersey.media:jersey-media-json-gson:3.1.1' + // Because of GraalVM quirks, we need to ship that. See https://github.com/jspecify/jspecify/issues/389#issuecomment-1661130973 for details + implementation 'org.jspecify:jspecify:0.3.0' + testImplementation 'io.github.classgraph:classgraph:4.8.165' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.10.1' diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 0ac40fe2aa4..48267ada078 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -136,6 +136,8 @@ uses org.eclipse.jgit.transport.SshSessionFactory; uses org.eclipse.jgit.lib.GpgSigner; + requires transitive org.jspecify; + // other libraries requires org.antlr.antlr4.runtime; requires org.libreoffice.uno; diff --git a/src/main/java/org/jabref/gui/actions/StandardActions.java b/src/main/java/org/jabref/gui/actions/StandardActions.java index b53ecee6cc0..ee4e324373b 100644 --- a/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -158,6 +158,7 @@ public enum StandardActions implements Action { DELETE_FILE(Localization.lang("Permanently delete local file"), IconTheme.JabRefIcons.DELETE_FILE, KeyBinding.DELETE_ENTRY), HELP(Localization.lang("Online help"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), + HELP_GROUPS(Localization.lang("Open Help page"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), HELP_KEY_PATTERNS(Localization.lang("Help on key patterns"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), HELP_REGEX_SEARCH(Localization.lang("Help on regular expression search"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), HELP_NAME_FORMATTER(Localization.lang("Help on Name Formatting"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), diff --git a/src/main/java/org/jabref/gui/groups/GroupColorPicker.java b/src/main/java/org/jabref/gui/groups/GroupColorPicker.java new file mode 100644 index 00000000000..78fcafe65cf --- /dev/null +++ b/src/main/java/org/jabref/gui/groups/GroupColorPicker.java @@ -0,0 +1,62 @@ +package org.jabref.gui.groups; + +import java.util.List; + +import javafx.scene.paint.Color; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +public class GroupColorPicker { + + // Generate color for top groups + public static Color generateColor(List siblingColors) { + return generateColor(siblingColors, null); + } + + /** + * Algorithm optimized for colors, not for gray-scale (where it does not work) + */ + public static Color generateColor(List siblingColors, @Nullable Color parentColor) { + if (siblingColors.isEmpty()) { + if (parentColor == null) { + // We need something colorful to derive other colors based on the color + return Color.hsb(Math.random() * 360.0, .50, .75); + } + return generateSubGroupColor(parentColor); + } + + double sumSin = 0; + double sumCos = 0; + + // Calculate the mean angle + for (Color color : siblingColors) { + double hue = color.getHue(); + sumSin += Math.sin(Math.toRadians(hue)); + sumCos += Math.cos(Math.toRadians(hue)); + } + + double meanAngle = Math.toDegrees(Math.atan2(sumSin, sumCos)); + meanAngle = (meanAngle + 360) % 360; + + // The opposite angle is potentially the point of maximum average distance + double newHue = (meanAngle + 180) % 360; + + double sumSaturation = 0; + double sumBrightness = 0; + for (Color color : siblingColors) { + sumSaturation += color.getSaturation(); + sumBrightness += color.getBrightness(); + } + + double averageSaturation = sumSaturation / siblingColors.size(); + double averageBrightness = sumBrightness / siblingColors.size(); + + return Color.hsb(newHue, averageSaturation, averageBrightness); + } + + private static Color generateSubGroupColor(Color baseColor) { + return baseColor.deriveColor(0.0, 1.0, .9, 1.0); + } +} diff --git a/src/main/java/org/jabref/gui/groups/GroupDialog.fxml b/src/main/java/org/jabref/gui/groups/GroupDialog.fxml index 3d12f80abbc..02b19876250 100644 --- a/src/main/java/org/jabref/gui/groups/GroupDialog.fxml +++ b/src/main/java/org/jabref/gui/groups/GroupDialog.fxml @@ -37,7 +37,7 @@ - + - - - + + + + + + + + + + + - -