diff --git a/.github/outdatedDependencies.md b/.github/outdatedDependencies.md index c750c81fb06..62b87165631 100644 --- a/.github/outdatedDependencies.md +++ b/.github/outdatedDependencies.md @@ -1,5 +1,5 @@ --- title: Outdated dependencies -labels: code-quality, dependencies +labels: t: dependencies --- [There are outdated dependencies!](https://github.com/JabRef/jabref/actions?query=is%3Afailure+workflow%3A%22Check+dependencies%22) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 001004798d3..572c836fe90 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -205,7 +205,7 @@ jobs: if: ${{ steps.checksecrets.outputs.secretspresent }} run: xvfb-run --auto-servernum ./gradlew jacocoTestReport && bash <(curl -s https://codecov.io/bash); env: - CI: "false" # we pretend to run locally - even if tests fail on the CI, they count towards test coverage + CI: "true" CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} DBMS: "postgresql" # This is https://github.com/marketplace/actions/gradle-wrapper-validation diff --git a/CHANGELOG.md b/CHANGELOG.md index bb224287a6e..97c5b199eb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed an issue where the INSPIRE fetcher was no longer working [#6229](https://github.com/JabRef/jabref/issues/6229) - We fixed an issue where custom exports with an uppercase file extension could not be selected for "Copy...-> Export to Clipboard" [#6285](https://github.com/JabRef/jabref/issues/6285) +- We fixed the display of icon both in the main table and linked file editor. [#6169](https://github.com/JabRef/jabref/issues/6169) ### Removed @@ -87,6 +88,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed an issue where citation styles except the default "Preview" could not be used. [#56220](https://github.com/JabRef/jabref/issues/5622) - We fixed an issue where a warning was displayed when the title content is made up of two sentences. [#5832](https://github.com/JabRef/jabref/issues/5832) - We fixed an issue where an exception was thrown when adding a save action without a selected formatter in the library properties [#6069](https://github.com/JabRef/jabref/issues/6069) +- We fixed an issue when an "Abstract field" was duplicating text, when importing from RIS file (Neurons) [#6065](https://github.com/JabRef/jabref/issues/6065) ### Removed diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index d57f19c6853..2de4703981a 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -12,7 +12,7 @@ - + diff --git a/docs/guidelines-for-setting-up-a-local-workspace.md b/docs/guidelines-for-setting-up-a-local-workspace.md index 431d4aa410c..b04a13fb208 100644 --- a/docs/guidelines-for-setting-up-a-local-workspace.md +++ b/docs/guidelines-for-setting-up-a-local-workspace.md @@ -29,9 +29,9 @@ If you do not yet have a GitHub account, please [create one](https://github.com/ Proposals for account names: -- Login similar to your university account. Example: `koppor` -- Use your last name prefixed by the first letter of your first name. Example: `okopp` -- Use `firstname.lastname`. Example: `oliver.kopp` +* Login similar to your university account. Example: `koppor` +* Use your last name prefixed by the first letter of your first name. Example: `okopp` +* Use `firstname.lastname`. Example: `oliver.kopp` You can hide your email adress by following the recommendations at . @@ -41,9 +41,9 @@ That account then be used for develoment mailing lists, mail exchange with other Examples: -- Same login as in GitHub (see above). Example: `koppor@gmail.com` -- "`it`" in the name. Example: `kopp.it@gmail.com` -- Use the university login. Example: `st342435@stud.uni-stuttgart.de` +* Same login as in GitHub (see above). Example: `koppor@gmail.com` +* "`it`" in the name. Example: `kopp.it@gmail.com` +* Use the university login. Example: `st342435@stud.uni-stuttgart.de` ### git @@ -172,15 +172,18 @@ Contributions to JabRef's source code need to have a code formatting that is con 2. Close the settings afterwards and restart IntelliJ * Go to **File \| Settings \| Editor \| Code Style** * Click on the settings wheel \(next to the scheme chooser\), then click "Import Scheme" -* Select the IntelliJ configuration file `config/IntelliJ Code Style.xml`. +* Select the IntelliJ configuration file `config/IntelliJ Code Style.xml` * Go to **File \| Settings \| Checkstyle \| Configuration File** 1. Import the CheckStyle configuration file by clicking the \[+\] button - 2. For the description provide e.g. "JabRef" + 2. For the description provide "JabRef" 3. Click "Browse" and choose `config/checkstyle/checkstyle.xml` 4. Check "Store relative to project location" 5. Click "Next" and "Finish" 6. Activate the CheckStyle configuration file by ticking it in the list - 7. Save settings by clicking "OK" + 7. Ensure that CheckStyle version 8.31 (or higher) is selected + 8. Save settings by clicking "OK" + +![checkstyle settings](images/intellij-checkstyle-settings.png) #### Troubleshooting when using both IDEA and Eclipse @@ -200,7 +203,7 @@ For Eclipse 2020-03 you need to install [jdk14 support](https://marketplace.ecli 1. Run `./gradlew run` to generate all resources and to check if JabRef runs. * The JabRef GUI should finally appear. * This step is only required once. -2. Run `./gradlew eclipse` +2. Run `./gradlew eclipse` * **This must always be executed, when there are new upstream changes.** 3. Copy the file `Log4jPlugins.java` from `build/generated/sources/annotationProcessor/java/main/org/jabref/gui/logging/plugins` to `src/main/java/org/jabref/gui/logging/plugins/` * Usually, the folder `plugins` must be created for that. @@ -210,29 +213,28 @@ For Eclipse 2020-03 you need to install [jdk14 support](https://marketplace.ecli 5. Create a run/debug configuration for the main class `org.jabref.JabRefLauncher` and/or for `org.jabref.JabRefMain` \(both can be used equivalently\) * In the tab "Arguments" of the run/debug configuration, enter the following runtime VM arguments: - * Set "VM Arguments" to: - - ```text - --patch-module test=fastparse_2.12-1.0.0.jar - --patch-module test2=fastparse-utils_2.12-1.0.0.jar - --patch-module test3=sourcecode_2.12-0.1.4.jar - --add-exports javafx.controls/com.sun.javafx.scene.control=org.jabref - --add-exports org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref - --add-exports javafx.graphics/com.sun.javafx.scene=org.controlsfx.controls - --add-exports javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls - --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls - --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls - --add-exports javafx.controls/com.sun.javafx.scene.control=org.controlsfx.controls - --add-exports javafx.controls/com.sun.javafx.scene.control.inputmap=org.controlsfx.controls - --add-exports javafx.base/com.sun.javafx.event=org.controlsfx.controls - --add-exports javafx.base/com.sun.javafx.collections=org.controlsfx.controls - --add-exports javafx.base/com.sun.javafx.runtime=org.controlsfx.controls - --add-exports javafx.web/com.sun.webkit=org.controlsfx.controls - --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls - --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix - --add-exports com.oracle.truffle.regex/com.oracle.truffle.regex=org.graalvm.truffle - --patch-module org.jabref=build\resources\main - ``` + ```text + --patch-module test=fastparse_2.12-1.0.0.jar + --patch-module test2=fastparse-utils_2.12-1.0.0.jar + --patch-module test3=sourcecode_2.12-0.1.4.jar + --add-exports javafx.controls/com.sun.javafx.scene.control=org.jabref + --add-exports org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref + --add-exports javafx.graphics/com.sun.javafx.scene=org.controlsfx.controls + --add-exports javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls + --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control.inputmap=org.controlsfx.controls + --add-exports javafx.base/com.sun.javafx.event=org.controlsfx.controls + --add-exports javafx.base/com.sun.javafx.collections=org.controlsfx.controls + --add-exports javafx.base/com.sun.javafx.runtime=org.controlsfx.controls + --add-exports javafx.web/com.sun.webkit=org.controlsfx.controls + --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix + --add-exports com.oracle.truffle.regex/com.oracle.truffle.regex=org.graalvm.truffle + --patch-module org.jabref=build\resources\main + ``` + 6. Optional: Install the [e\(fx\)clipse plugin](http://www.eclipse.org/efxclipse/index.html) from the Eclipse marketplace: 1. Help -> Eclipse Marketplace... -> Search tab 2. Enter "e\(fx\)clipse" in the search dialogue 3. Click "Go" 4. Click "Install" button next to the plugin 5. Click "Finish" 7. Now you can build and run/debug the application by either using "JabRefLauncher" or "JabRefMain". This is the recommended way, since the application starts quite fast. diff --git a/docs/images/intellij-checkstyle-settings.png b/docs/images/intellij-checkstyle-settings.png new file mode 100644 index 00000000000..c605c04cd20 Binary files /dev/null and b/docs/images/intellij-checkstyle-settings.png differ diff --git a/src/main/java/org/jabref/gui/EntryTypeView.java b/src/main/java/org/jabref/gui/EntryTypeView.java index e7ffea9fe14..657fe4b20f0 100644 --- a/src/main/java/org/jabref/gui/EntryTypeView.java +++ b/src/main/java/org/jabref/gui/EntryTypeView.java @@ -12,7 +12,9 @@ import javafx.scene.control.ComboBox; import javafx.scene.control.TextField; import javafx.scene.control.TitledPane; +import javafx.scene.control.Tooltip; import javafx.scene.layout.FlowPane; +import javafx.stage.Screen; import org.jabref.Globals; import org.jabref.gui.util.BaseDialog; @@ -26,6 +28,8 @@ import org.jabref.model.entry.types.BibtexEntryTypeDefinitions; import org.jabref.model.entry.types.EntryType; import org.jabref.model.entry.types.IEEETranEntryTypeDefinitions; +import org.jabref.model.entry.types.StandardEntryType; +import org.jabref.model.strings.StringUtil; import org.jabref.preferences.JabRefPreferences; import com.airhacks.afterburner.views.ViewLoader; @@ -92,6 +96,17 @@ private void addEntriesToPane(FlowPane pane, Collection entryButton.setUserData(entryType); entryButton.setOnAction(event -> setEntryTypeForReturnAndClose(Optional.of(entryType))); pane.getChildren().add(entryButton); + + EntryType selectedType = entryType.getType(); + String description = getDescription(selectedType); + if (StringUtil.isNotBlank(description)) { + Screen currentScreen = Screen.getPrimary(); + double maxWidth = currentScreen.getBounds().getWidth(); + Tooltip tooltip = new Tooltip(description); + tooltip.setMaxWidth((maxWidth * 2) / 3); + tooltip.setWrapText(true); + entryButton.setTooltip(tooltip); + } } } @@ -168,4 +183,119 @@ private void setEntryTypeForReturnAndClose(Optional entryType) { viewModel.stopFetching(); this.close(); } + + //The description is coming from biblatex manual chapter 2 + //Biblatex documentation is favored over the bibtex, + //since bibtex is a subset of biblatex and biblatex is better documented. + public static String getDescription(EntryType selectedType) { + //CHECKSTYLE:OFF + if (selectedType instanceof StandardEntryType) { + switch ((StandardEntryType) selectedType) { + case Article -> { + return Localization.lang("An article in a journal, magazine, newspaper, or other periodical which forms a self-contained unit with its own title."); + } + case Book -> { + return Localization.lang("A single-volume book with one or more authors where the authors share credit for the work as a whole."); + } + case Booklet -> { + return Localization.lang("A book-like work without a formal publisher or sponsoring institution."); + } + case Collection -> { + return Localization.lang("A single-volume collection with multiple, self-contained contributions by distinct authors which have their own title. The work as a whole has no overall author but it will usually have an editor."); + } + case Conference -> { + return Localization.lang("A legacy alias for \"InProceedings\"."); + } + case InBook -> { + return Localization.lang("A part of a book which forms a self-contained unit with its own title."); + } + case InCollection -> { + return Localization.lang("A contribution to a collection which forms a self-contained unit with a distinct author and title."); + } + case InProceedings -> { + return Localization.lang("An article in a conference proceedings."); + } + case Manual -> { + return Localization.lang("Technical or other documentation, not necessarily in printed form."); + } + case MastersThesis -> { + return Localization.lang("Similar to \"Thesis\" except that the type field is optional and defaults to the localised term Master's thesis."); + } + case Misc -> { + return Localization.lang("A fallback type for entries which do not fit into any other category."); + } + case PhdThesis -> { + return Localization.lang("Similar to \"Thesis\" except that the type field is optional and defaults to the localised term PhD thesis."); + } + case Proceedings -> { + return Localization.lang("A single-volume conference proceedings. This type is very similar to \"Collection\"."); + } + case TechReport -> { + return Localization.lang("Similar to \"Report\" except that the type field is optional and defaults to the localised term technical report."); + } + case Unpublished -> { + return Localization.lang("A work with an author and a title which has not been formally published, such as a manuscript or the script of a talk."); + } + case BookInBook -> { + return Localization.lang("This type is similar to \"InBook\" but intended for works originally published as a stand-alone book."); + } + case InReference -> { + return Localization.lang("An article in a work of reference. This is a more specific variant of the generic \"InCollection\" entry type."); + } + case MvBook -> { + return Localization.lang("A multi-volume \"Book\"."); + } + case MvCollection -> { + return Localization.lang("A multi-volume \"Collection\"."); + } + case MvProceedings -> { + return Localization.lang("A multi-volume \"Proceedings\" entry."); + } + case MvReference -> { + return Localization.lang("A multi-volume \"Reference\" entry. The standard styles will treat this entry type as an alias for \"MvCollection\"."); + } + case Online -> { + return Localization.lang("This entry type is intended for sources such as web sites which are intrinsically online resources."); + } + case Reference -> { + return Localization.lang("A single-volume work of reference such as an encyclopedia or a dictionary."); + } + case Report -> { + return Localization.lang("A technical report, research report, or white paper published by a university or some other institution."); + } + case Set -> { + return Localization.lang("An entry set is a group of entries which are cited as a single reference and listed as a single item in the bibliography."); + } + case SuppBook -> { + return Localization.lang("Supplemental material in a \"Book\". This type is provided for elements such as prefaces, introductions, forewords, afterwords, etc. which often have a generic title only."); + } + case SuppCollection -> { + return Localization.lang("Supplemental material in a \"Collection\"."); + } + case SuppPeriodical -> { + return Localization.lang("Supplemental material in a \"Periodical\". This type may be useful when referring to items such as regular columns, obituaries, letters to the editor, etc. which only have a generic title."); + } + case Thesis -> { + return Localization.lang("A thesis written for an educational institution to satisfy the requirements for a degree."); + } + case WWW -> { + return Localization.lang("An alias for \"Online\", provided for jurabib compatibility."); + } + case Software -> { + return Localization.lang("Computer software. The standard styles will treat this entry type as an alias for \"Misc\"."); + } + case Dataset -> { + return Localization.lang("A data set or a similar collection of (mostly) raw data."); + } + default -> { + return ""; + } + } + } else { + return ""; + } + //CHECKSTYLE:ON + + } + } diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index b32e35ceb4e..7cdf41d2891 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -831,7 +831,7 @@ private MenuBar createMenu() { new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.SHOW_PDF_VIEWER, new ShowDocumentViewerAction()), + factory.createMenuItem(StandardActions.SHOW_PDF_VIEWER, new ShowDocumentViewerAction(stateManager, prefs)), factory.createMenuItem(StandardActions.EDIT_ENTRY, new OpenEntryEditorAction(this, stateManager)), factory.createMenuItem(StandardActions.OPEN_CONSOLE, new OpenConsoleAction(stateManager)) ); diff --git a/src/main/java/org/jabref/gui/actions/ActionHelper.java b/src/main/java/org/jabref/gui/actions/ActionHelper.java index 01e23d67e22..06f294de5d2 100644 --- a/src/main/java/org/jabref/gui/actions/ActionHelper.java +++ b/src/main/java/org/jabref/gui/actions/ActionHelper.java @@ -1,14 +1,20 @@ package org.jabref.gui.actions; +import java.nio.file.Path; import java.util.Collections; import java.util.List; +import java.util.Optional; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanExpression; import org.jabref.gui.StateManager; import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.LinkedFile; import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.StandardField; +import org.jabref.model.util.FileHelper; +import org.jabref.preferences.PreferencesService; public class ActionHelper { public static BooleanExpression needsDatabase(StateManager stateManager) { @@ -36,4 +42,20 @@ public static BooleanExpression isAnyFieldSetForSelectedEntry(List fields entry.getFieldsObservable(), stateManager.getSelectedEntries()); } + + public static BooleanExpression isFilePresentForSelectedEntry(StateManager stateManager, PreferencesService preferencesService) { + return Bindings.createBooleanBinding(() -> { + List files = stateManager.getSelectedEntries().get(0).getFiles(); + if ((files.size() > 0) && stateManager.getActiveDatabase().isPresent()) { + Optional filename = FileHelper.expandFilename( + stateManager.getActiveDatabase().get(), + files.get(0).getLink(), + preferencesService.getFilePreferences()); + return filename.isPresent(); + } else { + return false; + } + }, stateManager.getSelectedEntries(), + stateManager.getSelectedEntries().get(0).getFieldBinding(StandardField.FILE)); + } } diff --git a/src/main/java/org/jabref/gui/documentviewer/ShowDocumentViewerAction.java b/src/main/java/org/jabref/gui/documentviewer/ShowDocumentViewerAction.java index 9c91ca7c472..444bf3840e9 100644 --- a/src/main/java/org/jabref/gui/documentviewer/ShowDocumentViewerAction.java +++ b/src/main/java/org/jabref/gui/documentviewer/ShowDocumentViewerAction.java @@ -1,12 +1,18 @@ package org.jabref.gui.documentviewer; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; +import org.jabref.preferences.PreferencesService; public class ShowDocumentViewerAction extends SimpleCommand { + public ShowDocumentViewerAction(StateManager stateManager, PreferencesService preferences) { + this.executable.bind(ActionHelper.isFilePresentForSelectedEntry(stateManager, preferences)); + } + @Override public void execute() { new DocumentViewerView().show(); } - } diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.css b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.css index e53c688a081..61981cde6cb 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.css +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.css @@ -86,6 +86,10 @@ -fx-background-color: -jr-error; } +.list-cell:invalid { + -fx-background-color: -jr-warn; +} + .code-area .context-menu { -fx-font-family: sans-serif; } diff --git a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java index 1c24f739ffd..ed26d3d8373 100644 --- a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java +++ b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java @@ -56,7 +56,7 @@ public Set getExternalFileTypeSelection() { * @return The ExternalFileType registered, or null if none. */ public Optional getExternalFileTypeByName(String name) { - Optional externalFileType = externalFileTypes.stream().filter(type -> type.getExtension().equals(name)).findFirst(); + Optional externalFileType = externalFileTypes.stream().filter(type -> type.getName().equals(name)).findFirst(); if (externalFileType.isPresent()) { return externalFileType; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java index e57513db0b7..62994a86a20 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java @@ -46,8 +46,13 @@ import org.jabref.model.entry.LinkedFile; import org.jabref.model.metadata.FilePreferences; import org.jabref.model.strings.StringUtil; +import org.jabref.model.util.FileHelper; import org.jabref.model.util.OptionalUtil; +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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,6 +74,8 @@ public class LinkedFileViewModel extends AbstractViewModel { private final LinkedFileHandler linkedFileHandler; private final ExternalFileTypes externalFileTypes; + private final Validator fileExistsValidator; + public LinkedFileViewModel(LinkedFile linkedFile, BibEntry entry, BibDatabaseContext databaseContext, @@ -88,6 +95,18 @@ public LinkedFileViewModel(LinkedFile linkedFile, this.externalFileTypes = externalFileTypes; this.xmpPreferences = xmpPreferences; + fileExistsValidator = new FunctionBasedValidator<>( + linkedFile.linkProperty(), + link -> { + if (linkedFile.isOnlineLink()) { + return true; + } else { + Optional path = FileHelper.expandFilename(databaseContext, link, filePreferences); + return path.isPresent() && Files.exists(path.get()); + } + }, + ValidationMessage.warning(Localization.lang("Could not find file '%0'.", linkedFile.getLink()))); + downloadOngoing.bind(downloadProgress.greaterThanOrEqualTo(0).and(downloadProgress.lessThan(1))); canWriteXMPMetadata.setValue(!linkedFile.isOnlineLink() && linkedFile.getFileType().equalsIgnoreCase("pdf")); } @@ -136,12 +155,10 @@ public Optional findIn(List directories) { return linkedFile.findIn(directories); } - /** - * TODO: Be a bit smarter and try to infer correct icon, for example using {@link - * org.jabref.gui.externalfiletype.ExternalFileTypes#getExternalFileTypeByName(String)} - */ public JabRefIcon getTypeIcon() { - return IconTheme.JabRefIcons.PDF_FILE; + return externalFileTypes.fromLinkedFile(linkedFile, false) + .map(ExternalFileType::getIcon) + .orElse(IconTheme.JabRefIcons.FILE); } public void markAsAutomaticallyFound() { @@ -174,24 +191,19 @@ public void open() { public void openFolder() { try { - Path path = null; - // absolute path - if (Paths.get(linkedFile.getLink()).isAbsolute()) { - path = Paths.get(linkedFile.getLink()); - } else { - // relative to file folder - for (Path folder : databaseContext.getFileDirectoriesAsPaths(filePreferences)) { - Path file = folder.resolve(linkedFile.getLink()); - if (Files.exists(file)) { - path = file; - break; - } + if (!linkedFile.isOnlineLink()) { + Optional resolvedPath = FileHelper.expandFilename( + databaseContext, + linkedFile.getLink(), + filePreferences); + + if (resolvedPath.isPresent()) { + JabRefDesktop.openFolderAndSelectFile(resolvedPath.get()); + } else { + dialogService.showErrorDialogAndWait(Localization.lang("File not found")); } - } - if (path != null) { - JabRefDesktop.openFolderAndSelectFile(path); } else { - dialogService.showErrorDialogAndWait(Localization.lang("File not found")); + dialogService.showErrorDialogAndWait(Localization.lang("Cannot open folder as the file is an online link.")); } } catch (IOException ex) { LOGGER.debug("Cannot open folder", ex); @@ -253,7 +265,7 @@ public void moveToDefaultDirectory() { // Get target folder Optional fileDir = databaseContext.getFirstExistingFileDir(filePreferences); - if (!fileDir.isPresent()) { + if (fileDir.isEmpty()) { dialogService.showErrorDialogAndWait(Localization.lang("Move file"), Localization.lang("File directory is not set or does not exist!")); return; } @@ -322,7 +334,7 @@ public void moveToDefaultDirectoryAndRename() { public boolean delete() { Optional file = linkedFile.findIn(databaseContext, filePreferences); - if (!file.isPresent()) { + if (file.isEmpty()) { LOGGER.warn("Could not find file " + linkedFile.getLink()); return true; } @@ -367,7 +379,7 @@ public void writeXMPMetadata() { // Localization.lang("Writing XMP metadata...") BackgroundTask writeTask = BackgroundTask.wrap(() -> { Optional file = linkedFile.findIn(databaseContext, filePreferences); - if (!file.isPresent()) { + if (file.isEmpty()) { // TODO: Print error message // Localization.lang("PDF does not exist"); } else { @@ -457,4 +469,6 @@ private Optional inferFileTypeFromURL(String url) { public LinkedFile getFile() { return linkedFile; } + + public ValidationStatus fileExistsValidationStatus() { return fileExistsValidator.getValidationStatus(); } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java index 537a17fff08..7dbacc13bc8 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java @@ -72,7 +72,8 @@ public LinkedFilesEditor(Field field, DialogService dialogService, BibDatabaseCo .withOnMouseClickedEvent(this::handleItemMouseClick) .setOnDragDetected(this::handleOnDragDetected) .setOnDragDropped(this::handleOnDragDropped) - .setOnDragOver(this::handleOnDragOver); + .setOnDragOver(this::handleOnDragOver) + .withValidation(LinkedFileViewModel::fileExistsValidationStatus); listView.setCellFactory(cellFactory); diff --git a/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java b/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java index 6579eed3102..eb7658f1b28 100644 --- a/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java +++ b/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java @@ -11,7 +11,6 @@ import org.jabref.gui.fieldeditors.LinkedFileViewModel; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.field.StandardField; import org.jabref.preferences.PreferencesService; public class OpenExternalFileAction extends SimpleCommand { @@ -25,7 +24,7 @@ public OpenExternalFileAction(DialogService dialogService, StateManager stateMan this.stateManager = stateManager; this.preferencesService = preferencesService; - this.executable.bind(ActionHelper.isFieldSetForSelectedEntry(StandardField.FILE, stateManager) + this.executable.bind(ActionHelper.isFilePresentForSelectedEntry(stateManager, preferencesService) .and(ActionHelper.needsEntriesSelected(1, stateManager))); } diff --git a/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java b/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java index a2c3c211785..d3afc796ad0 100644 --- a/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java +++ b/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java @@ -7,7 +7,6 @@ import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.fieldeditors.LinkedFileViewModel; -import org.jabref.model.entry.field.StandardField; import org.jabref.preferences.PreferencesService; public class OpenFolderAction extends SimpleCommand { @@ -21,7 +20,7 @@ public OpenFolderAction(DialogService dialogService, StateManager stateManager, this.stateManager = stateManager; this.preferencesService = preferencesService; - this.executable.bind(ActionHelper.isFieldSetForSelectedEntry(StandardField.FILE, stateManager)); + this.executable.bind(ActionHelper.isFilePresentForSelectedEntry(stateManager, preferencesService)); } @Override diff --git a/src/main/java/org/jabref/gui/menus/ChangeEntryTypeMenu.java b/src/main/java/org/jabref/gui/menus/ChangeEntryTypeMenu.java index bf574cf411a..e5625178f81 100644 --- a/src/main/java/org/jabref/gui/menus/ChangeEntryTypeMenu.java +++ b/src/main/java/org/jabref/gui/menus/ChangeEntryTypeMenu.java @@ -7,11 +7,15 @@ import javafx.collections.ObservableList; import javafx.scene.control.ContextMenu; +import javafx.scene.control.CustomMenuItem; +import javafx.scene.control.Label; import javafx.scene.control.Menu; import javafx.scene.control.MenuItem; import javafx.scene.control.SeparatorMenuItem; +import javafx.scene.control.Tooltip; import org.jabref.Globals; +import org.jabref.gui.EntryTypeView; import org.jabref.gui.undo.CountingUndoManager; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableChangeType; @@ -23,6 +27,7 @@ import org.jabref.model.entry.types.BibtexEntryTypeDefinitions; import org.jabref.model.entry.types.EntryType; import org.jabref.model.entry.types.IEEETranEntryTypeDefinitions; +import org.jabref.model.strings.StringUtil; public class ChangeEntryTypeMenu { @@ -31,13 +36,18 @@ public ChangeEntryTypeMenu() { } public static MenuItem createMenuItem(EntryType type, BibEntry entry, UndoManager undoManager) { - MenuItem menuItem = new MenuItem(type.getDisplayName()); + CustomMenuItem menuItem = new CustomMenuItem(new Label(type.getDisplayName())); menuItem.setOnAction(event -> { NamedCompound compound = new NamedCompound(Localization.lang("Change entry type")); entry.setType(type) .ifPresent(change -> compound.addEdit(new UndoableChangeType(change))); undoManager.addEdit(compound); }); + String description = EntryTypeView.getDescription(type); + if (StringUtil.isNotBlank(description)) { + Tooltip tooltip = new Tooltip(description); + Tooltip.install(menuItem.getContent(), tooltip); + } return menuItem; } @@ -92,4 +102,5 @@ private void populate(ObservableList items, Collection t private void populate(Menu menu, Collection types, BibEntry entry, UndoManager undoManager) { populate(menu.getItems(), types, entry, undoManager); } + } diff --git a/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java b/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java index 72188027e8a..59743c49248 100644 --- a/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java +++ b/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java @@ -22,6 +22,7 @@ import org.jabref.gui.icon.JabRefIcon; import org.jabref.model.strings.StringUtil; +import de.saxsys.mvvmfx.utils.validation.ValidationStatus; import org.fxmisc.easybind.Subscription; /** @@ -31,6 +32,8 @@ */ public class ViewModelListCellFactory implements Callback, ListCell> { + private static final PseudoClass INVALID_PSEUDO_CLASS = PseudoClass.getPseudoClass("invalid"); + private Callback toText; private Callback toGraphic; private Callback toTooltip; @@ -43,6 +46,7 @@ public class ViewModelListCellFactory implements Callback, ListCe private BiConsumer toOnDragExited; private BiConsumer toOnDragOver; private Map>> pseudoClasses = new HashMap<>(); + private Callback validationStatusProperty; public ViewModelListCellFactory withText(Callback toText) { this.toText = toText; @@ -66,10 +70,7 @@ public ViewModelListCellFactory withIcon(Callback toIcon) { } public ViewModelListCellFactory withIcon(Callback toIcon, Callback toColor) { - this.toGraphic = viewModel -> { - - return toIcon.call(viewModel).withColor(toColor.call(viewModel)).getGraphicNode(); - }; + this.toGraphic = viewModel -> toIcon.call(viewModel).withColor(toColor.call(viewModel)).getGraphicNode(); return this; } @@ -134,6 +135,11 @@ public ViewModelListCellFactory withPseudoClass(PseudoClass pseudoClass, Call return this; } + public ViewModelListCellFactory withValidation(Callback validationStatusProperty) { + this.validationStatusProperty = validationStatusProperty; + return this; + } + public void install(ComboBox comboBox) { comboBox.setButtonCell(this.call(null)); comboBox.setCellFactory(this); @@ -146,7 +152,7 @@ public void install(ListView listView) { @Override public ListCell call(ListView param) { - return new ListCell() { + return new ListCell<>() { List subscriptions = new ArrayList<>(); @@ -203,6 +209,12 @@ protected void updateItem(T item, boolean empty) { Subscription subscription = BindingsHelper.includePseudoClassWhen(this, pseudoClassWithCondition.getKey(), condition); subscriptions.add(subscription); } + if (validationStatusProperty != null) { + validationStatusProperty.call(viewModel).getHighestMessage().ifPresent(message -> { + setTooltip(new Tooltip(message.getMessage())); + subscriptions.add(BindingsHelper.includePseudoClassWhen(this, INVALID_PSEUDO_CLASS, validationStatusProperty.call(viewModel).validProperty().not())); + }); + } } } }; diff --git a/src/main/java/org/jabref/logic/bibtexkeypattern/BibtexKeyGenerator.java b/src/main/java/org/jabref/logic/bibtexkeypattern/BibtexKeyGenerator.java index d887ac16a15..63279fcfece 100644 --- a/src/main/java/org/jabref/logic/bibtexkeypattern/BibtexKeyGenerator.java +++ b/src/main/java/org/jabref/logic/bibtexkeypattern/BibtexKeyGenerator.java @@ -157,7 +157,7 @@ public String generateKey(BibEntry entry) { } String oldKey = entry.getCiteKeyOptional().orElse(null); - int occurrences = database.getDuplicationChecker().getNumberOfKeyOccurrences(key); + long occurrences = database.getNumberOfKeyOccurrences(key); if (Objects.equals(oldKey, key)) { occurrences--; // No change, so we can accept one dupe. @@ -178,7 +178,7 @@ public String generateKey(BibEntry entry) { moddedKey = key + getAppendix(number); number++; - occurrences = database.getDuplicationChecker().getNumberOfKeyOccurrences(moddedKey); + occurrences = database.getNumberOfKeyOccurrences(moddedKey); // only happens if #getAddition() is buggy if (Objects.equals(oldKey, moddedKey)) { occurrences--; diff --git a/src/main/java/org/jabref/logic/exporter/XmpExporter.java b/src/main/java/org/jabref/logic/exporter/XmpExporter.java index 9a045f8ebc8..59f7c445b6b 100644 --- a/src/main/java/org/jabref/logic/exporter/XmpExporter.java +++ b/src/main/java/org/jabref/logic/exporter/XmpExporter.java @@ -24,7 +24,7 @@ */ public class XmpExporter extends Exporter { - private static final String XMP_SPLIT_PATTERN = "split"; + public static final String XMP_SPLIT_DIRECTORY_INDICATOR = "split"; private final XmpPreferences xmpPreferences; @@ -33,6 +33,12 @@ public XmpExporter(XmpPreferences xmpPreferences) { this.xmpPreferences = xmpPreferences; } + /** + * @param databaseContext the database to export from + * @param file the file to write to. If it contains "split", then the output is split into different files + * @param encoding the encoding to use + * @param entries a list containing all entries that should be exported + */ @Override public void export(BibDatabaseContext databaseContext, Path file, Charset encoding, List entries) throws Exception { Objects.requireNonNull(databaseContext); @@ -45,18 +51,16 @@ public void export(BibDatabaseContext databaseContext, Path file, Charset encodi // This is a distinction between writing all entries from the supplied list to a single .xmp file, // or write every entry to a separate file. - if (file.getFileName().toString().trim().equals(XMP_SPLIT_PATTERN)) { - + if (file.getFileName().toString().trim().equals(XMP_SPLIT_DIRECTORY_INDICATOR)) { for (BibEntry entry : entries) { // Avoid situations, where two cite keys are null Path entryFile; - String suffix = entry.getId() + "_" + entry.getField(InternalField.KEY_FIELD).orElse("") + ".xmp"; + String suffix = entry.getId() + "_" + entry.getField(InternalField.KEY_FIELD).orElse("null") + ".xmp"; if (file.getParent() == null) { entryFile = Paths.get(suffix); } else { entryFile = Paths.get(file.getParent().toString() + "/" + suffix); } - this.writeBibToXmp(entryFile, Collections.singletonList(entry), encoding); } } else { @@ -66,7 +70,6 @@ public void export(BibDatabaseContext databaseContext, Path file, Charset encodi private void writeBibToXmp(Path file, List entries, Charset encoding) throws IOException { String xmpContent = XmpUtilWriter.generateXmpStringWithoutXmpDeclaration(entries, this.xmpPreferences); - try (BufferedWriter writer = Files.newBufferedWriter(file, encoding)) { writer.write(xmpContent); writer.flush(); diff --git a/src/main/java/org/jabref/logic/importer/fileformat/RisImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/RisImporter.java index 59b896a2242..35e84f9296b 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/RisImporter.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/RisImporter.java @@ -199,7 +199,7 @@ public ParserResult importDatabase(BufferedReader reader) throws IOException { String oldAb = fields.get(StandardField.ABSTRACT); if (oldAb == null) { fields.put(StandardField.ABSTRACT, value); - } else { + } else if (!oldAb.equals(value) && !value.isEmpty()) { fields.put(StandardField.ABSTRACT, oldAb + OS.NEWLINE + value); } } else if ("UR".equals(tag) || "L2".equals(tag) || "LK".equals(tag)) { diff --git a/src/main/java/org/jabref/logic/integrity/BibtexKeyDuplicationChecker.java b/src/main/java/org/jabref/logic/integrity/BibtexKeyDuplicationChecker.java index 3448e799d08..7112653709e 100644 --- a/src/main/java/org/jabref/logic/integrity/BibtexKeyDuplicationChecker.java +++ b/src/main/java/org/jabref/logic/integrity/BibtexKeyDuplicationChecker.java @@ -3,6 +3,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Optional; import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.l10n.Localization; @@ -20,7 +21,12 @@ public BibtexKeyDuplicationChecker(BibDatabase database) { @Override public List check(BibEntry entry) { - boolean isDuplicate = database.getDuplicationChecker().isDuplicateCiteKeyExisting(entry); + Optional citeKey = entry.getCiteKeyOptional(); + if (citeKey.isEmpty()) { + return Collections.emptyList(); + } + + boolean isDuplicate = database.isDuplicateCiteKeyExisting(citeKey.get()); if (isDuplicate) { return Collections.singletonList( new IntegrityMessage(Localization.lang("Duplicate BibTeX key"), entry, StandardField.KEY)); diff --git a/src/main/java/org/jabref/model/database/BibDatabase.java b/src/main/java/org/jabref/model/database/BibDatabase.java index 814925ee55f..6496283130a 100644 --- a/src/main/java/org/jabref/model/database/BibDatabase.java +++ b/src/main/java/org/jabref/model/database/BibDatabase.java @@ -53,16 +53,6 @@ public class BibDatabase { private final ObservableList entries = FXCollections.synchronizedObservableList(FXCollections.observableArrayList(BibEntry::getObservables)); private Map bibtexStrings = new ConcurrentHashMap<>(); - /** - * this is kept in sync with the database (upon adding/removing an entry, it is updated as well) - */ - private final DuplicationChecker duplicationChecker = new DuplicationChecker(); - - /** - * contains all entry.getID() of the current database - */ - private final Set internalIDs = new HashSet<>(); - private final EventBus eventBus = new EventBus(); private String preamble; @@ -72,7 +62,6 @@ public class BibDatabase { private String sharedDatabaseID; public BibDatabase() { - this.eventBus.register(duplicationChecker); this.registerListener(new KeyChangeListener(this)); } @@ -125,7 +114,7 @@ public synchronized List getEntriesSorted(Comparator compara * Returns whether an entry with the given ID exists (-> entry_type + hashcode). */ public boolean containsEntryWithId(String id) { - return internalIDs.contains(id); + return entries.stream().anyMatch(entry -> entry.getId().equals(id)); } public ObservableList getEntries() { @@ -179,16 +168,6 @@ public synchronized List getEntriesByKey(String key) { return result; } - /** - * Finds the entry with a specified ID. - * - * @param id - * @return The entry that has the given id - */ - public synchronized Optional getEntryById(String id) { - return entries.stream().filter(entry -> entry.getId().equals(id)).findFirst(); - } - /** * Inserts the entry, given that its ID is not already in use. * use Util.createId(...) to make up a unique ID for an entry. @@ -211,7 +190,7 @@ public synchronized boolean insertEntry(BibEntry entry) throws KeyCollisionExcep */ public synchronized boolean insertEntry(BibEntry entry, EntriesEventSource eventSource) throws KeyCollisionException { insertEntries(Collections.singletonList(entry), eventSource); - return duplicationChecker.isDuplicateCiteKeyExisting(entry); + return entry.getCiteKeyOptional().map(this::isDuplicateCiteKeyExisting).orElse(false); } public synchronized void insertEntries(BibEntry... entries) throws KeyCollisionException { @@ -230,7 +209,6 @@ public synchronized void insertEntries(List newEntries, EntriesEventSo throw new KeyCollisionException("ID is already in use, please choose another", id); } - internalIDs.add(id); entry.registerListener(this); } if (newEntries.isEmpty()) { @@ -251,7 +229,8 @@ public synchronized void removeEntry(BibEntry bibEntry, EntriesEventSource event /** * Removes the given entries. - * The entries removed based on the id {@link BibEntry#id} + * The entries removed based on the id {@link BibEntry#getId()} + * * @param toBeDeleted Entries to delete */ public synchronized void removeEntries(List toBeDeleted) { @@ -260,7 +239,7 @@ public synchronized void removeEntries(List toBeDeleted) { /** * Removes the given entries. - * The entries are removed based on the id {@link BibEntry#id} + * The entries are removed based on the id {@link BibEntry#getId()} * * @param toBeDeleted Entry to delete * @param eventSource Source the event is sent from @@ -274,7 +253,6 @@ public synchronized void removeEntries(List toBeDeleted, EntriesEventS } boolean anyRemoved = entries.removeIf(entry -> ids.contains(entry.getId())); if (anyRemoved) { - internalIDs.removeAll(ids); eventBus.post(new EntriesRemovedEvent(toBeDeleted, eventSource)); } } @@ -640,7 +618,20 @@ public String generateSharedDatabaseID() { return this.sharedDatabaseID; } - public DuplicationChecker getDuplicationChecker() { - return duplicationChecker; + /** + * Returns the number of occurrences of the given key in this database. + */ + public long getNumberOfKeyOccurrences(String citeKey) { + return entries.stream() + .flatMap(entry -> entry.getCiteKeyOptional().stream()) + .filter(key -> key.equals(citeKey)) + .count(); + } + + /** + * Checks if there is more than one occurrence of the cite key + */ + public boolean isDuplicateCiteKeyExisting(String citeKey) { + return getNumberOfKeyOccurrences(citeKey) > 1; } } diff --git a/src/main/java/org/jabref/model/database/DuplicationChecker.java b/src/main/java/org/jabref/model/database/DuplicationChecker.java deleted file mode 100644 index 4667cdbc1da..00000000000 --- a/src/main/java/org/jabref/model/database/DuplicationChecker.java +++ /dev/null @@ -1,107 +0,0 @@ -package org.jabref.model.database; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import org.jabref.model.database.event.EntriesAddedEvent; -import org.jabref.model.database.event.EntriesRemovedEvent; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.event.FieldChangedEvent; -import org.jabref.model.entry.field.InternalField; - -import com.google.common.eventbus.Subscribe; - -/** - * Determines which bibtex cite keys are duplicates in a single {@link BibDatabase}. - */ -public class DuplicationChecker { - - /** use a map instead of a set since I need to know how many of each key is in there */ - private final Map allKeys = new HashMap<>(); - - /** - * Checks if there is more than one occurrence of this key - */ - private boolean isDuplicateCiteKeyExisting(String citeKey) { - return getNumberOfKeyOccurrences(citeKey) > 1; - } - - /** - * Checks if there is more than one occurrence of the cite key - */ - public boolean isDuplicateCiteKeyExisting(BibEntry entry) { - return isDuplicateCiteKeyExisting(entry.getCiteKeyOptional().orElse(null)); - } - - /** - * Returns the number of occurrences of the given key in this database. - */ - public int getNumberOfKeyOccurrences(String citeKey) { - return allKeys.getOrDefault(citeKey, 0); - } - - /** - * Helper function for counting the number of the key usages. - * Adds the given key to the internal keyset together with the count of it. - * The counter is increased if the key already exists, otherwise set to 1. - *
- * Special case: If a null or empty key is passed, it is not counted and thus not added. - * - * Reasoning: - * Consider this: I add a key xxx, then I add another key xxx. I get a warning. I delete the key xxx. - * Consider JabRef simply removing this key from a set of allKeys. - * Then I add another key xxx. I don't get a warning! - * Thus, I need a way to count the number of keys of each type. - * Solution: hashmap=>int (increment each time at add and decrement each time at remove) - */ - private void addKeyToSet(String key) { - if (key == null || key.isEmpty()) { - return; - } - - allKeys.put(key, getNumberOfKeyOccurrences(key) + 1); - } - - /** - * Helper function for counting the number of the key usages. - * Removes the given key from the internal keyset together with the count of it, if the key is set to 1. - * If it is not set to 1, the counter will be decreased. - *
- * Special case: If a null or empty key is passed, it is not counted and thus not removed. - */ - private void removeKeyFromSet(String key) { - if (key == null || key.isEmpty()) { - return; - } - - int numberOfKeyOccurrences = getNumberOfKeyOccurrences(key); - if (numberOfKeyOccurrences > 1) { - allKeys.put(key, numberOfKeyOccurrences - 1); - } else { - allKeys.remove(key); - } - } - - @Subscribe - public void listen(FieldChangedEvent fieldChangedEvent) { - if (fieldChangedEvent.getField().equals(InternalField.KEY_FIELD)) { - removeKeyFromSet(fieldChangedEvent.getOldValue()); - addKeyToSet(fieldChangedEvent.getNewValue()); - } - } - - @Subscribe - public void listen(EntriesRemovedEvent entriesRemovedEvent) { - List entries = entriesRemovedEvent.getBibEntries(); - entries.stream().map(BibEntry::getCiteKeyOptional).flatMap(Optional::stream).forEach(citeKey -> removeKeyFromSet(citeKey)); - } - - @Subscribe - public void listen(EntriesAddedEvent entriesAddedEvent) { - List entries = entriesAddedEvent.getBibEntries(); - entries.stream().map(BibEntry::getCiteKeyOptional).flatMap(Optional::stream).forEach(citeKey -> addKeyToSet(citeKey)); - } - -} diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java index 2f9ffec5133..0bfe4f7b11b 100644 --- a/src/main/java/org/jabref/model/entry/BibEntry.java +++ b/src/main/java/org/jabref/model/entry/BibEntry.java @@ -918,7 +918,7 @@ public Optional setFiles(List files) { public List getFiles() { // Extract the path Optional oldValue = getField(StandardField.FILE); - if (!oldValue.isPresent()) { + if (oldValue.isEmpty()) { return new ArrayList<>(); // Return new ArrayList because emptyList is immutable } diff --git a/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java index 874db26a41a..3f23b717932 100644 --- a/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java @@ -427,7 +427,7 @@ public class BiblatexEntryTypeDefinitions { .build(); private static final BibEntryType DATASET = new BibEntryTypeBuilder() - .withType(StandardEntryType.DATESET) + .withType(StandardEntryType.Dataset) .withImportantFields( StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.HOWPUBLISHED, StandardField.LOCATION, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) diff --git a/src/main/java/org/jabref/model/entry/types/StandardEntryType.java b/src/main/java/org/jabref/model/entry/types/StandardEntryType.java index 9b2f87d1674..8a01a87cb32 100644 --- a/src/main/java/org/jabref/model/entry/types/StandardEntryType.java +++ b/src/main/java/org/jabref/model/entry/types/StandardEntryType.java @@ -36,7 +36,7 @@ public enum StandardEntryType implements EntryType { Thesis("Thesis"), WWW("WWW"), Software("Software"), - DATESET("DataSet"); + Dataset("Dataset"); diff --git a/src/main/resources/csl-styles/amsterdam-university-press-academic.csl b/src/main/resources/csl-styles/amsterdam-university-press-academic.csl new file mode 100644 index 00000000000..7a302c202af --- /dev/null +++ b/src/main/resources/csl-styles/amsterdam-university-press-academic.csl @@ -0,0 +1,459 @@ + + diff --git a/src/main/resources/csl-styles/apa-fr-provost.csl b/src/main/resources/csl-styles/apa-fr-provost.csl index f48c3ac9379..237cf67409e 100644 --- a/src/main/resources/csl-styles/apa-fr-provost.csl +++ b/src/main/resources/csl-styles/apa-fr-provost.csl @@ -1,8 +1,8 @@ - diff --git a/src/main/resources/csl-styles/instituto-brasileiro-de-informacao-em-ciencia-e-tecnologia-abnt-initials.csl b/src/main/resources/csl-styles/instituto-brasileiro-de-informacao-em-ciencia-e-tecnologia-abnt-initials.csl index b01dad805cd..7ca74716cac 100644 --- a/src/main/resources/csl-styles/instituto-brasileiro-de-informacao-em-ciencia-e-tecnologia-abnt-initials.csl +++ b/src/main/resources/csl-styles/instituto-brasileiro-de-informacao-em-ciencia-e-tecnologia-abnt-initials.csl @@ -26,7 +26,7 @@ The Brazilian Standard Style by IBICT - 2020-04-09T14:37:00+00:00 + 2020-04-11T15:24:32+00:00 This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License @@ -87,7 +87,7 @@ - + @@ -155,33 +155,29 @@ - + - - - - - - - - + - + - - + + + + + + + + + - - - - - + @@ -306,18 +302,19 @@ - + - + - + + @@ -333,7 +330,7 @@ - + @@ -392,7 +389,7 @@ - + @@ -442,7 +439,7 @@ - + @@ -483,15 +480,16 @@ - + + - - + + @@ -505,9 +503,9 @@ - - - + + + @@ -524,7 +522,7 @@ - + @@ -532,9 +530,9 @@ - - + + @@ -545,8 +543,8 @@ - - + + @@ -560,7 +558,7 @@ - + diff --git a/src/main/resources/csl-styles/instituto-brasileiro-de-informacao-em-ciencia-e-tecnologia-abnt.csl b/src/main/resources/csl-styles/instituto-brasileiro-de-informacao-em-ciencia-e-tecnologia-abnt.csl index c6226cb1ff9..536ca319594 100644 --- a/src/main/resources/csl-styles/instituto-brasileiro-de-informacao-em-ciencia-e-tecnologia-abnt.csl +++ b/src/main/resources/csl-styles/instituto-brasileiro-de-informacao-em-ciencia-e-tecnologia-abnt.csl @@ -26,7 +26,7 @@ The Brazilian Standard Style by IBICT - 2020-04-09T14:38:00+00:00 + 2020-04-11T15:20:00+00:00 This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License @@ -87,7 +87,7 @@ - + @@ -155,33 +155,29 @@ - + - - - - - - - - + - + - - + + + + + + + + + - - - - - + @@ -306,18 +302,19 @@ - + - + - + + @@ -333,7 +330,7 @@ - + @@ -392,7 +389,7 @@ - + @@ -442,7 +439,7 @@ - + @@ -483,15 +480,16 @@ - + + - - + + @@ -505,9 +503,9 @@ - - - + + + @@ -524,7 +522,7 @@ - + @@ -532,9 +530,9 @@ - - + + @@ -545,8 +543,8 @@ - - + + @@ -560,7 +558,7 @@ - + diff --git a/src/main/resources/csl-styles/isnad-dipnotlu.csl b/src/main/resources/csl-styles/isnad-dipnotlu.csl new file mode 100644 index 00000000000..03ac1faaaba --- /dev/null +++ b/src/main/resources/csl-styles/isnad-dipnotlu.csl @@ -0,0 +1,1819 @@ + + diff --git a/src/main/resources/csl-styles/isnad-metinici.csl b/src/main/resources/csl-styles/isnad-metinici.csl new file mode 100644 index 00000000000..f8c478ffd93 --- /dev/null +++ b/src/main/resources/csl-styles/isnad-metinici.csl @@ -0,0 +1,1277 @@ + + diff --git a/src/main/resources/csl-styles/iso690-author-date-pt-br.csl b/src/main/resources/csl-styles/iso690-author-date-pt-br.csl new file mode 100644 index 00000000000..c95e9474ed8 --- /dev/null +++ b/src/main/resources/csl-styles/iso690-author-date-pt-br.csl @@ -0,0 +1,576 @@ + + diff --git a/src/main/resources/csl-styles/iso690-full-note-cs.csl b/src/main/resources/csl-styles/iso690-full-note-cs.csl index f7adf459017..5a774d3c967 100644 --- a/src/main/resources/csl-styles/iso690-full-note-cs.csl +++ b/src/main/resources/csl-styles/iso690-full-note-cs.csl @@ -2,26 +2,19 @@ diff --git a/src/main/resources/csl-styles/multidisciplinary-digital-publishing-institute.csl b/src/main/resources/csl-styles/multidisciplinary-digital-publishing-institute.csl index c4823a3dc84..d50ab1ea0e4 100644 --- a/src/main/resources/csl-styles/multidisciplinary-digital-publishing-institute.csl +++ b/src/main/resources/csl-styles/multidisciplinary-digital-publishing-institute.csl @@ -94,6 +94,9 @@ + + + diff --git a/src/main/resources/csl-styles/norois.csl b/src/main/resources/csl-styles/norois.csl index 829dcacc9a8..01f793c4c9c 100644 --- a/src/main/resources/csl-styles/norois.csl +++ b/src/main/resources/csl-styles/norois.csl @@ -13,7 +13,7 @@ 0029-182X 1760-8546 - 2020-04-06T09:41:00+00:00 + 2020-04-14T10:19:50+00:00 This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License @@ -32,7 +32,7 @@ - + @@ -52,7 +52,7 @@ - + - + @@ -508,8 +504,10 @@ - - + + + + diff --git a/src/main/resources/csl-styles/pravnik.csl b/src/main/resources/csl-styles/pravnik.csl new file mode 100644 index 00000000000..a77f4b9a362 --- /dev/null +++ b/src/main/resources/csl-styles/pravnik.csl @@ -0,0 +1,586 @@ + + diff --git a/src/main/resources/csl-styles/rmit-university-harvard.csl b/src/main/resources/csl-styles/rmit-university-harvard.csl index cc6ce25f919..1e19a8031fa 100644 --- a/src/main/resources/csl-styles/rmit-university-harvard.csl +++ b/src/main/resources/csl-styles/rmit-university-harvard.csl @@ -12,7 +12,7 @@ - 2020-01-13T00:52:05+00:00 + 2020-04-13T04:40:00+00:00 This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License @@ -86,7 +86,7 @@ - + @@ -98,6 +98,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -148,14 +173,14 @@ - + - + @@ -210,6 +235,8 @@ + + diff --git a/src/main/resources/csl-styles/tagungsberichte-der-historischen-kommission-fur-ost-und-westpreussische-landesforschung.csl b/src/main/resources/csl-styles/tagungsberichte-der-historischen-kommission-fur-ost-und-westpreussische-landesforschung.csl new file mode 100644 index 00000000000..c5c78739a40 --- /dev/null +++ b/src/main/resources/csl-styles/tagungsberichte-der-historischen-kommission-fur-ost-und-westpreussische-landesforschung.csl @@ -0,0 +1,238 @@ + + diff --git a/src/main/resources/journals/journalList.csv b/src/main/resources/journals/journalList.csv index 993a4650bc5..0ebc39f583d 100644 --- a/src/main/resources/journals/journalList.csv +++ b/src/main/resources/journals/journalList.csv @@ -450,6 +450,7 @@ Advanced Materials;Adv. Mater.;; Advanced Materials and Processes;Adv. Mater. Processes;; Advanced Materials for Optics and Electronics;Adv. Mater. Opt. Electron.;; Advanced Materials Forum;Adv. Mater. Forum;; +Advanced Modeling and Simulation in Engineering Sciences;Adv. Model. Simul. Eng. Sci.;; Advanced Optical Materials;Adv. Opt. Mater.;; Advanced Powder Technology;Adv. Powder Technol.;; Advanced Practice Nursing Quarterly;Adv. Pract. Nurs. Q.;; @@ -1345,6 +1346,7 @@ Appled Entomology and Zoology;Appl. Entomol. Zool.;; Applicable Algebra in Engineering, Communication and Computing;Appl. Algebra Eng. Commun. Comput.;; Applications and Applied Mathematics;Appl. Appl. Math.;; Applications in Plant Sciences;Appl. Plant Sci.;; +Applications of Mathematics;Appl. Math.;; Applied Accoustics;Appl. Acoust.;; Applied Adhesion Science;Appl. Adhes. Sci.;; Applied and Computational Harmonic Analysis;Appl. Comput. Harmon. Anal.;; @@ -3308,7 +3310,7 @@ Climate Dynamics;Climate Dyn.;; Climate of the Past;Clim. Past;; Climate of the Past Discussion;Clim. Past Discuss.;; Climate Risk Management;Clim. Risk Manage.;; -Climatic Change;Climatic Change;; +Climatic Change;Clim. Change;; Climatological Bulletin;Climatol. Bull.;; Clinic;Clinic (Paris);; Clinic Odontologia;Clin. Odontol.;; @@ -3645,6 +3647,7 @@ Computational Condensed Matter;Comput. Condens. Matter;; Computational Geometry: Theory and Applications;Comput. Geom. Theory Appl.;; Computational Geosciences;Comput. Geosci.;; Computational Intelligence;Comput. Intell.;; +Computational Materials Science;Comput. Mater. Sci.;; Computational Mathematics and Mathematical Physics;Comput. Math. Math. Phys.;; Computational Mathematics and Modeling;Comput. Math. Model.;; Computational Mechanics;Comput. Mech.;; @@ -4576,6 +4579,7 @@ Engineering Analysis with Boundary Elements;Eng. Anal. Boundary Elem.;; Engineering and Mining Journal;Eng. Min. J.;; Engineering and Mining Journal Press;Eng. Min. J. Press;; Engineering Applications of Artificial Intelligence;Eng. Appl. Artif. Intell.;; +Engineering Computations;Eng. Comput.;; Engineering Failure Analysis;Eng. Fail. Anal.;; Engineering Fracture Mechanics;Eng. Fract. Mech.;; Engineering Geology;Eng. Geol.;; @@ -6352,6 +6356,7 @@ Instruments and Experiment Techniques;Instrum. Exp. Tech.;; Integrated Environmental Assessment and Management;Integr. Environ. Assess. Manage.;; Integrated Ferroelectrics;Integr. Ferroelectr.;; Integrated Healthcare Report;Integr. Healthc. Rep.;; +Integrating Materials and Manufacturing Innovation;Integr. Mater. Manuf. Innov.;; Integrative and Comparative Biology;Integr. Comp. Biol.;; Integrative Biology;Integr. Biol.;; Integrative Cancer Therapies;Integr. Cancer Ther.;; @@ -7701,6 +7706,7 @@ Journal of Computational Electronics;J. Comput. Electron.;; Journal of Computational Methods in Science and Engineering;J. Comput. Methods Sci. Eng.;; Journal of Computational Neuroscience;J. Comput. Neurosci.;; Journal of Computational Physics;J. Comput. Phys.;; +Journal of Computational Science;J. Comput. Sci.;; Journal of Computed Tomography;J. Comput. Tomogr.;; Journal of Computer and System Sciences International;J. Comput. Syst. Sci. Int.;; Journal of Computer Assisted Tomography;J. Comput. Assisted Tomogr.;; @@ -10524,7 +10530,7 @@ MRS Internet Journal of Nitride Semiconductor Research;MRS Internet J. Nitride S MSDA Journal;MSDA J.;; Mucopolysaccharides Biochimica et Biophysica Acta, Specialized Section on Mucoproteins and Mucopolysaccharides;Biochim. Biophys. Acta, Spec. Sect. Mucoproteins;BBASFB; Mull und Abfall;Mull Abfall;; -Multibody System Dynamics;Multibody Sys.Dyn.;; +Multibody System Dynamics;Multibody Sys. Dyn.;; Multidimensional Systems and Signal Processing;Multidimension. Syst. Signal Process.;; Multiple Sclerosis;Mult. Scler.;; Multis;Multis;; @@ -11112,6 +11118,7 @@ Nukleonika;Nukleonika;; Numerical Analysis and Applications;Numer. Anal. Appl.;; Numerical Heat Transfer Part A;Numer. Heat Transfer, Part A;; Numerical Heat Transfer Part B;Numer. Heat Transfer, Part B;; +Numerical Linear Algebra with Applications;Numer. Linear Algebra Appl.;; Nuntius Radiologicus;Nunt. Radiol.;; Nuova Rivista di Neurologia;Nuova Riv. Neurol.;; Nuovi Annali d'Igiene e Microbiologia;Nuovi Ann. Ig. Microbiol.;; @@ -13660,6 +13667,7 @@ SIAM Journal on Mathematical Analysis;SIAM J. Math. Anal.;; SIAM Journal on Matrix Analysis and Applications;SIAM J. Matrix Anal. Appl.;; SIAM Journal on Numerical Analysis;SIAM J. Numer. Anal.;; SIAM Journal on Optimization;SIAM J. Optim.;; +SIAM Journal on Scientific and Statistical Computing;SIAM J. Sci. Stat. Comput.;; SIAM Review;SIAM Rev.;; Sichuan Da Xue Xue Bao. Yi Xue Ban (Journal of Sichuan University. Medical Science Edition);Sichuan Da Xue Xue Bao Yi Xue Ban;; Sichuan Yi Xue Yuan Xue Bao (Acta Academiae Medicinae Sichuan);Sichuan Yi Xue Yuan Xue Bao;; diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 51c0637f60d..777acd7b11c 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -102,6 +102,8 @@ Cannot\ create\ group=Cannot create group Cannot\ create\ group.\ Please\ create\ a\ library\ first.=Cannot create group. Please create a library first. +Cannot\ open\ folder\ as\ the\ file\ is\ an\ online\ link.=Cannot open folder as the file is an online link. + case\ insensitive=case insensitive case\ sensitive=case sensitive @@ -2199,4 +2201,38 @@ User-specific\ relevance\ flag,\ in\ case\ the\ entry\ is\ relevant.=User-specif Remove\ formatter\ for\ %0=Remove formatter for %0 Remove\ formatter\ '%0'=Remove formatter '%0' + +An\ article\ in\ a\ journal,\ magazine,\ newspaper,\ or\ other\ periodical\ which\ forms\ a\ self-contained\ unit\ with\ its\ own\ title.=An article in a journal, magazine, newspaper, or other periodical which forms a self-contained unit with its own title. +A\ single-volume\ book\ with\ one\ or\ more\ authors\ where\ the\ authors\ share\ credit\ for\ the\ work\ as\ a\ whole.=A single-volume book with one or more authors where the authors share credit for the work as a whole. +A\ book-like\ work\ without\ a\ formal\ publisher\ or\ sponsoring\ institution.=A book-like work without a formal publisher or sponsoring institution. +A\ single-volume\ collection\ with\ multiple,\ self-contained\ contributions\ by\ distinct\ authors\ which\ have\ their\ own\ title.\ The\ work\ as\ a\ whole\ has\ no\ overall\ author\ but\ it\ will\ usually\ have\ an\ editor.=A single-volume collection with multiple, self-contained contributions by distinct authors which have their own title. The work as a whole has no overall author but it will usually have an editor. +A\ legacy\ alias\ for\ "InProceedings".=A legacy alias for "InProceedings". +A\ part\ of\ a\ book\ which\ forms\ a\ self-contained\ unit\ with\ its\ own\ title.=A part of a book which forms a self-contained unit with its own title. +A\ contribution\ to\ a\ collection\ which\ forms\ a\ self-contained\ unit\ with\ a\ distinct\ author\ and\ title.=A contribution to a collection which forms a self-contained unit with a distinct author and title. +An\ article\ in\ a\ conference\ proceedings.=An article in a conference proceedings. +Technical\ or\ other\ documentation,\ not\ necessarily\ in\ printed\ form.=Technical or other documentation, not necessarily in printed form. +A\ fallback\ type\ for\ entries\ which\ do\ not\ fit\ into\ any\ other\ category.=A fallback type for entries which do not fit into any other category. +Similar\ to\ "Thesis"\ except\ that\ the\ type\ field\ is\ optional\ and\ defaults\ to\ the\ localised\ term\ \ Master's\ thesis.=Similar to "Thesis" except that the type field is optional and defaults to the localised term Master's thesis. +Similar\ to\ "Thesis"\ except\ that\ the\ type\ field\ is\ optional\ and\ defaults\ to\ the\ localised\ term\ PhD\ thesis.=Similar to "Thesis" except that the type field is optional and defaults to the localised term PhD thesis. +A\ single-volume\ conference\ proceedings.\ This\ type\ is\ very\ similar\ to\ "Collection".=A single-volume conference proceedings. This type is very similar to "Collection". +Similar\ to\ "Report"\ except\ that\ the\ type\ field\ is\ optional\ and\ defaults\ to\ the\ localised\ term\ technical\ report.=Similar to "Report" except that the type field is optional and defaults to the localised term technical report. +A\ work\ with\ an\ author\ and\ a\ title\ which\ has\ not\ been\ formally\ published,\ such\ as\ a\ manuscript\ or\ the\ script\ of\ a\ talk.=A work with an author and a title which has not been formally published, such as a manuscript or the script of a talk. +This\ type\ is\ similar\ to\ "InBook"\ but\ intended\ for\ works\ originally\ published\ as\ a\ stand-alone\ book.=This type is similar to "InBook" but intended for works originally published as a stand-alone book. +An\ article\ in\ a\ work\ of\ reference.\ This\ is\ a\ more\ specific\ variant\ of\ the\ generic\ "InCollection"\ entry\ type.=An article in a work of reference. This is a more specific variant of the generic "InCollection" entry type. +A\ multi-volume\ "Book".=A multi-volume "Book". +A\ multi-volume\ "Collection".=A multi-volume "Collection". +A\ multi-volume\ "Proceedings"\ entry.=A multi-volume "Proceedings" entry. +A\ multi-volume\ "Reference"\ entry.\ The\ standard\ styles\ will\ treat\ this\ entry\ type\ as\ an\ alias\ for\ "MvCollection".=A multi-volume "Reference" entry. The standard styles will treat this entry type as an alias for "MvCollection". +This\ entry\ type\ is\ intended\ for\ sources\ such\ as\ web\ sites\ which\ are\ intrinsically\ online\ resources.=This entry type is intended for sources such as web sites which are intrinsically online resources. +A\ single-volume\ work\ of\ reference\ such\ as\ an\ encyclopedia\ or\ a\ dictionary.=A single-volume work of reference such as an encyclopedia or a dictionary. +A\ technical\ report,\ research\ report,\ or\ white\ paper\ published\ by\ a\ university\ or\ some\ other\ institution.=A technical report, research report, or white paper published by a university or some other institution. +An\ entry\ set\ is\ a\ group\ of\ entries\ which\ are\ cited\ as\ a\ single\ reference\ and\ listed\ as\ a\ single\ item\ in\ the\ bibliography.=An entry set is a group of entries which are cited as a single reference and listed as a single item in the bibliography. +Supplemental\ material\ in\ a\ "Book".\ This\ type\ is\ provided\ for\ elements\ such\ as\ prefaces,\ introductions,\ forewords,\ afterwords,\ etc.\ which\ often\ have\ a\ generic\ title\ only.=Supplemental material in a "Book". This type is provided for elements such as prefaces, introductions, forewords, afterwords, etc. which often have a generic title only. +Supplemental\ material\ in\ a\ "Collection".=Supplemental material in a "Collection". +Supplemental\ material\ in\ a\ "Periodical".\ This\ type\ may\ be\ useful\ when\ referring\ to\ items\ such\ as\ regular\ columns,\ obituaries,\ letters\ to\ the\ editor,\ etc.\ which\ only\ have\ a\ generic\ title.=Supplemental material in a "Periodical". This type may be useful when referring to items such as regular columns, obituaries, letters to the editor, etc. which only have a generic title. +A\ thesis\ written\ for\ an\ educational\ institution\ to\ satisfy\ the\ requirements\ for\ a\ degree.=A thesis written for an educational institution to satisfy the requirements for a degree. +An\ alias\ for\ "Online",\ provided\ for\ jurabib\ compatibility.=An alias for "Online", provided for jurabib compatibility. +Computer\ software.\ The\ standard\ styles\ will\ treat\ this\ entry\ type\ as\ an\ alias\ for\ "Misc".=Computer software. The standard styles will treat this entry type as an alias for "Misc". +A\ data\ set\ or\ a\ similar\ collection\ of\ (mostly)\ raw\ data.=A data set or a similar collection of (mostly) raw data. + Display\ count\ of\ items\ in\ group=Display count of items in group diff --git a/src/test/java/org/jabref/logic/exporter/XmpExporterTest.java b/src/test/java/org/jabref/logic/exporter/XmpExporterTest.java index d102c01aa39..120d27f8638 100644 --- a/src/test/java/org/jabref/logic/exporter/XmpExporterTest.java +++ b/src/test/java/org/jabref/logic/exporter/XmpExporterTest.java @@ -119,22 +119,23 @@ public void writeMultipleEntriesInASingleFile(@TempDir Path testFolder) throws E @Test public void writeMultipleEntriesInDifferentFiles(@TempDir Path testFolder) throws Exception { - Path file = testFolder.resolve("split"); + // set path to the one where the exporter produces several files + Path file = testFolder.resolve(XmpExporter.XMP_SPLIT_DIRECTORY_INDICATOR); Files.createFile(file); - BibEntry entryTuring = new BibEntry(); - entryTuring.setField(StandardField.AUTHOR, "Alan Turing"); + BibEntry entryTuring = new BibEntry() + .withField(StandardField.AUTHOR, "Alan Turing"); - BibEntry entryArmbrust = new BibEntry(); - entryArmbrust.setField(StandardField.AUTHOR, "Michael Armbrust"); - entryArmbrust.setCiteKey("Armbrust2010"); + BibEntry entryArmbrust = new BibEntry() + .withField(StandardField.AUTHOR, "Michael Armbrust") + .withCiteKey("Armbrust2010"); - exporter.export(databaseContext, file, encoding, Arrays.asList(entryTuring, entryArmbrust)); + exporter.export(databaseContext, file, encoding, List.of(entryTuring, entryArmbrust)); List lines = Files.readAllLines(file); assertEquals(Collections.emptyList(), lines); - Path fileTuring = Paths.get(file.getParent().toString() + "/" + entryTuring.getId() + "_null.xmp"); + Path fileTuring = Paths.get(file.getParent().toString(), entryTuring.getId() + "_null.xmp"); String actualTuring = String.join("\n", Files.readAllLines(fileTuring)); // we are using \n to join, so we need it in the expected string as well, \r\n would fail String expectedTuring = " \n" + @@ -155,7 +156,7 @@ public void writeMultipleEntriesInDifferentFiles(@TempDir Path testFolder) throw assertEquals(expectedTuring, actualTuring); - Path fileArmbrust = Paths.get(file.getParent().toString() + "/" + entryArmbrust.getId() + "_Armbrust2010.xmp"); + Path fileArmbrust = Paths.get(file.getParent().toString(), entryArmbrust.getId() + "_Armbrust2010.xmp"); String actualArmbrust = String.join("\n", Files.readAllLines(fileArmbrust)); // we are using \n to join, so we need it in the expected string as well, \r\n would fail String expectedArmbrust = " \n" + diff --git a/src/test/java/org/jabref/model/database/BibDatabaseTest.java b/src/test/java/org/jabref/model/database/BibDatabaseTest.java index 8f30691b299..5f417a8c486 100644 --- a/src/test/java/org/jabref/model/database/BibDatabaseTest.java +++ b/src/test/java/org/jabref/model/database/BibDatabaseTest.java @@ -26,17 +26,17 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -public class BibDatabaseTest { +class BibDatabaseTest { private BibDatabase database; @BeforeEach - public void setUp() { + void setUp() { database = new BibDatabase(); } @Test - public void insertEntryAddsEntryToEntriesList() { + void insertEntryAddsEntryToEntriesList() { BibEntry entry = new BibEntry(); database.insertEntry(entry); assertEquals(1, database.getEntries().size()); @@ -45,7 +45,7 @@ public void insertEntryAddsEntryToEntriesList() { } @Test - public void containsEntryIdFindsEntry() { + void containsEntryIdFindsEntry() { BibEntry entry = new BibEntry(); assertFalse(database.containsEntryWithId(entry.getId())); database.insertEntry(entry); @@ -53,7 +53,7 @@ public void containsEntryIdFindsEntry() { } @Test - public void insertEntryWithSameIdThrowsException() { + void insertEntryWithSameIdThrowsException() { BibEntry entry0 = new BibEntry(); database.insertEntry(entry0); @@ -63,7 +63,7 @@ public void insertEntryWithSameIdThrowsException() { } @Test - public void removeEntryRemovesEntryFromEntriesList() { + void removeEntryRemovesEntryFromEntriesList() { BibEntry entry = new BibEntry(); database.insertEntry(entry); @@ -73,7 +73,7 @@ public void removeEntryRemovesEntryFromEntriesList() { } @Test - public void removeSomeEntriesRemovesThoseEntriesFromEntriesList() { + void removeSomeEntriesRemovesThoseEntriesFromEntriesList() { BibEntry entry1 = new BibEntry(); BibEntry entry2 = new BibEntry(); BibEntry entry3 = new BibEntry(); @@ -88,7 +88,7 @@ public void removeSomeEntriesRemovesThoseEntriesFromEntriesList() { } @Test - public void removeAllEntriesRemovesAllEntriesFromEntriesList() { + void removeAllEntriesRemovesAllEntriesFromEntriesList() { List allEntries = new ArrayList<>(); BibEntry entry1 = new BibEntry(); BibEntry entry2 = new BibEntry(); @@ -105,23 +105,23 @@ public void removeAllEntriesRemovesAllEntriesFromEntriesList() { } @Test - public void insertNullEntryThrowsException() { + void insertNullEntryThrowsException() { assertThrows(NullPointerException.class, () -> database.insertEntry(null)); } @Test - public void removeNullEntryThrowsException() { + void removeNullEntryThrowsException() { assertThrows(NullPointerException.class, () -> database.removeEntry(null)); } @Test - public void emptyDatabaseHasNoStrings() { + void emptyDatabaseHasNoStrings() { assertEquals(Collections.emptySet(), database.getStringKeySet()); assertTrue(database.hasNoStrings()); } @Test - public void insertStringUpdatesStringList() { + void insertStringUpdatesStringList() { BibtexString string = new BibtexString("DSP", "Digital Signal Processing"); database.addString(string); assertFalse(database.hasNoStrings()); @@ -133,7 +133,7 @@ public void insertStringUpdatesStringList() { } @Test - public void removeStringUpdatesStringList() { + void removeStringUpdatesStringList() { BibtexString string = new BibtexString("DSP", "Digital Signal Processing"); database.addString(string); database.removeString(string.getId()); @@ -146,7 +146,7 @@ public void removeStringUpdatesStringList() { } @Test - public void hasStringLabelFindsString() { + void hasStringLabelFindsString() { BibtexString string = new BibtexString("DSP", "Digital Signal Processing"); database.addString(string); assertTrue(database.hasStringByName("DSP")); @@ -154,7 +154,7 @@ public void hasStringLabelFindsString() { } @Test - public void setSingleStringAsCollection() { + void setSingleStringAsCollection() { BibtexString string = new BibtexString("DSP", "Digital Signal Processing"); List strings = Arrays.asList(string); database.setStrings(strings); @@ -162,7 +162,7 @@ public void setSingleStringAsCollection() { } @Test - public void setStringAsCollectionWithUpdatedContentThrowsKeyCollisionException() { + void setStringAsCollectionWithUpdatedContentThrowsKeyCollisionException() { BibtexString string = new BibtexString("DSP", "Digital Signal Processing"); BibtexString newContent = new BibtexString("DSP", "ABCD"); List strings = Arrays.asList(string, newContent); @@ -170,7 +170,7 @@ public void setStringAsCollectionWithUpdatedContentThrowsKeyCollisionException() } @Test - public void setStringAsCollectionWithNewContent() { + void setStringAsCollectionWithNewContent() { BibtexString string = new BibtexString("DSP", "Digital Signal Processing"); BibtexString vlsi = new BibtexString("VLSI", "Very Large Scale Integration"); List strings = Arrays.asList(string, vlsi); @@ -180,7 +180,7 @@ public void setStringAsCollectionWithNewContent() { } @Test - public void addSameStringLabelTwiceThrowsKeyCollisionException() { + void addSameStringLabelTwiceThrowsKeyCollisionException() { BibtexString string = new BibtexString("DSP", "Digital Signal Processing"); database.addString(string); final BibtexString finalString = new BibtexString("DSP", "Digital Signal Processor"); @@ -189,7 +189,7 @@ public void addSameStringLabelTwiceThrowsKeyCollisionException() { } @Test - public void addSameStringIdTwiceThrowsKeyCollisionException() { + void addSameStringIdTwiceThrowsKeyCollisionException() { BibtexString string = new BibtexString("DSP", "Digital Signal Processing"); string.setId("duplicateid"); database.addString(string); @@ -200,7 +200,7 @@ public void addSameStringIdTwiceThrowsKeyCollisionException() { } @Test - public void insertEntryPostsAddedEntryEvent() { + void insertEntryPostsAddedEntryEvent() { BibEntry expectedEntry = new BibEntry(); TestEventListener tel = new TestEventListener(); database.registerListener(tel); @@ -210,7 +210,7 @@ public void insertEntryPostsAddedEntryEvent() { } @Test - public void insertMultipleEntriesPostsAddedEntryEvent() { + void insertMultipleEntriesPostsAddedEntryEvent() { BibEntry firstEntry = new BibEntry(); BibEntry secondEntry = new BibEntry(); TestEventListener tel = new TestEventListener(); @@ -221,7 +221,7 @@ public void insertMultipleEntriesPostsAddedEntryEvent() { } @Test - public void removeEntriesPostsRemovedEntriesEvent() { + void removeEntriesPostsRemovedEntriesEvent() { BibEntry entry1 = new BibEntry(); BibEntry entry2 = new BibEntry(); List expectedEntries = Arrays.asList(entry1, entry2); @@ -234,7 +234,7 @@ public void removeEntriesPostsRemovedEntriesEvent() { } @Test - public void changingEntryPostsChangeEntryEvent() { + void changingEntryPostsChangeEntryEvent() { BibEntry entry = new BibEntry(); TestEventListener tel = new TestEventListener(); database.insertEntry(entry); @@ -246,26 +246,26 @@ public void changingEntryPostsChangeEntryEvent() { } @Test - public void correctKeyCountOne() { + void correctKeyCountOne() { BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(1, database.getNumberOfKeyOccurrences("AAA")); } @Test - public void correctKeyCountTwo() { + void correctKeyCountTwo() { BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(2, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(2, database.getNumberOfKeyOccurrences("AAA")); } @Test - public void correctKeyCountAfterRemoving() { + void correctKeyCountAfterRemoving() { BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); @@ -273,11 +273,11 @@ public void correctKeyCountAfterRemoving() { entry.setCiteKey("AAA"); database.insertEntry(entry); database.removeEntry(entry); - assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(1, database.getNumberOfKeyOccurrences("AAA")); } @Test - public void circularStringResolving() { + void circularStringResolving() { BibtexString string = new BibtexString("AAA", "#BBB#"); database.addString(string); string = new BibtexString("BBB", "#AAA#"); @@ -287,7 +287,7 @@ public void circularStringResolving() { } @Test - public void circularStringResolvingLongerCycle() { + void circularStringResolvingLongerCycle() { BibtexString string = new BibtexString("AAA", "#BBB#"); database.addString(string); string = new BibtexString("BBB", "#CCC#"); @@ -303,26 +303,26 @@ public void circularStringResolvingLongerCycle() { } @Test - public void resolveForStringsMonth() { + void resolveForStringsMonth() { assertEquals("January", database.resolveForStrings("#jan#")); } @Test - public void resolveForStringsSurroundingContent() { + void resolveForStringsSurroundingContent() { BibtexString string = new BibtexString("AAA", "aaa"); database.addString(string); assertEquals("aaaaaAAA", database.resolveForStrings("aa#AAA#AAA")); } @Test - public void resolveForStringsOddHashMarkAtTheEnd() { + void resolveForStringsOddHashMarkAtTheEnd() { BibtexString string = new BibtexString("AAA", "aaa"); database.addString(string); assertEquals("AAAaaaAAA#", database.resolveForStrings("AAA#AAA#AAA#")); } @Test - public void getUsedStrings() { + void getUsedStrings() { BibEntry entry = new BibEntry(); entry.setField(StandardField.AUTHOR, "#AAA#"); BibtexString tripleA = new BibtexString("AAA", "Some other #BBB#"); @@ -342,7 +342,7 @@ public void getUsedStrings() { } @Test - public void getUsedStringsSingleString() { + void getUsedStringsSingleString() { BibEntry entry = new BibEntry(); entry.setField(StandardField.AUTHOR, "#AAA#"); BibtexString tripleA = new BibtexString("AAA", "Some other text"); @@ -359,7 +359,7 @@ public void getUsedStringsSingleString() { } @Test - public void getUsedStringsNoString() { + void getUsedStringsNoString() { BibEntry entry = new BibEntry(); entry.setField(StandardField.AUTHOR, "Oscar Gustafsson"); BibtexString string = new BibtexString("AAA", "Some other text"); @@ -370,7 +370,7 @@ public void getUsedStringsNoString() { } @Test - public void getEntriesSortedWithTwoEntries() { + void getEntriesSortedWithTwoEntries() { BibEntry entryB = new BibEntry(StandardEntryType.Article); entryB.setId("2"); BibEntry entryA = new BibEntry(StandardEntryType.Article); @@ -380,12 +380,12 @@ public void getEntriesSortedWithTwoEntries() { } @Test - public void preambleIsEmptyIfNotSet() { + void preambleIsEmptyIfNotSet() { assertEquals(Optional.empty(), database.getPreamble()); } @Test - public void setPreambleWorks() { + void setPreambleWorks() { database.setPreamble("Oh yeah!"); assertEquals(Optional.of("Oh yeah!"), database.getPreamble()); } diff --git a/src/test/java/org/jabref/model/database/DuplicationCheckerTest.java b/src/test/java/org/jabref/model/database/DuplicationCheckerTest.java index 28782f83642..ebfb036eaaf 100644 --- a/src/test/java/org/jabref/model/database/DuplicationCheckerTest.java +++ b/src/test/java/org/jabref/model/database/DuplicationCheckerTest.java @@ -7,62 +7,62 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -public class DuplicationCheckerTest { +class DuplicationCheckerTest { private BibDatabase database; @BeforeEach - public void setUp() { + void setUp() { database = new BibDatabase(); } @Test - public void addEntry() { + void addEntry() { BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(1, database.getNumberOfKeyOccurrences("AAA")); } @Test - public void addAndRemoveEntry() { + void addAndRemoveEntry() { BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(1, database.getNumberOfKeyOccurrences("AAA")); database.removeEntry(entry); - assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(0, database.getNumberOfKeyOccurrences("AAA")); } @Test - public void changeCiteKey() { + void changeCiteKey() { BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(1, database.getNumberOfKeyOccurrences("AAA")); entry.setCiteKey("BBB"); - assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); - assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("BBB")); + assertEquals(0, database.getNumberOfKeyOccurrences("AAA")); + assertEquals(1, database.getNumberOfKeyOccurrences("BBB")); } @Test - public void setCiteKeySameKeyDifferentEntries() { + void setCiteKeySameKeyDifferentEntries() { BibEntry entry0 = new BibEntry(); entry0.setCiteKey("AAA"); database.insertEntry(entry0); BibEntry entry1 = new BibEntry(); entry1.setCiteKey("BBB"); database.insertEntry(entry1); - assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); - assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("BBB")); + assertEquals(1, database.getNumberOfKeyOccurrences("AAA")); + assertEquals(1, database.getNumberOfKeyOccurrences("BBB")); entry1.setCiteKey("AAA"); - assertEquals(2, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); - assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("BBB")); + assertEquals(2, database.getNumberOfKeyOccurrences("AAA")); + assertEquals(0, database.getNumberOfKeyOccurrences("BBB")); } @Test - public void removeMultipleCiteKeys() { + void removeMultipleCiteKeys() { BibEntry entry0 = new BibEntry(); entry0.setCiteKey("AAA"); database.insertEntry(entry0); @@ -72,36 +72,36 @@ public void removeMultipleCiteKeys() { BibEntry entry2 = new BibEntry(); entry2.setCiteKey("AAA"); database.insertEntry(entry2); - assertEquals(3, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(3, database.getNumberOfKeyOccurrences("AAA")); database.removeEntry(entry2); - assertEquals(2, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(2, database.getNumberOfKeyOccurrences("AAA")); database.removeEntry(entry1); - assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(1, database.getNumberOfKeyOccurrences("AAA")); database.removeEntry(entry0); - assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(0, database.getNumberOfKeyOccurrences("AAA")); } @Test - public void addEmptyCiteKey() { + void addEmptyCiteKey() { BibEntry entry = new BibEntry(); entry.setCiteKey(""); database.insertEntry(entry); - assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("")); + assertEquals(0, database.getNumberOfKeyOccurrences("")); } @Test - public void removeEmptyCiteKey() { + void removeEmptyCiteKey() { BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(1, database.getNumberOfKeyOccurrences("AAA")); entry.setCiteKey(""); database.removeEntry(entry); - assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(0, database.getNumberOfKeyOccurrences("AAA")); } } diff --git a/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTest9.bib b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTest9.bib new file mode 100644 index 00000000000..7555e5422d1 --- /dev/null +++ b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTest9.bib @@ -0,0 +1,18 @@ +% Encoding: UTF-8 + +@Article{, + author = {Kriegeskorte, Nikolaus and Storrs, Katherine R.}, + title = {Grid Cells for Conceptual Spaces?}, + doi = {10.1016/j.neuron.2016.10.006}, + issn = {0896-6273}, + number = {2}, + pages = {280--284}, + url = {https://doi.org/10.1016/j.neuron.2016.10.006}, + volume = {92}, + abstract = {location and direction of movement in 2D physical environments via regularly repeating receptive fields.}, + comment = {doi: 10.1016/j.neuron.2016.10.006}, + journal = {Neuron}, + month = oct, + publisher = {Elsevier}, + year = {2016}, +} diff --git a/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTest9.ris b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTest9.ris new file mode 100644 index 00000000000..496e17caa88 --- /dev/null +++ b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTest9.ris @@ -0,0 +1,23 @@ +TY - JOUR +T1 - Grid Cells for Conceptual Spaces? +AU - Kriegeskorte, Nikolaus +AU - Storrs, Katherine R. +Y1 - 2016/10/19 +PY - 2016 +N1 - doi: 10.1016/j.neuron.2016.10.006 +DO - 10.1016/j.neuron.2016.10.006 +T2 - Neuron +JF - Neuron +SP - 280 +EP - 284 +VL - 92 +IS - 2 +PB - Elsevier +N2 - location and direction of movement in 2D physical environments via regularly repeating receptive fields. +AB - location and direction of movement in 2D physical environments via regularly repeating receptive fields. +SN - 0896-6273 +M3 - doi: 10.1016/j.neuron.2016.10.006 +UR - https://doi.org/10.1016/j.neuron.2016.10.006 +Y2 - 2020/03/04 +ER - +