diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index 9f8f4c99917d..a505062a065c 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -66,12 +66,12 @@ jobs: submodules: 'true' show-progress: 'false' - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.13.2 + uses: gittools/actions/gitversion/setup@v0.13.4 with: versionSpec: "5.x" - name: Run GitVersion id: gitversion - uses: gittools/actions/gitversion/execute@v0.13.2 + uses: gittools/actions/gitversion/execute@v0.13.4 - name: Setup JDK uses: actions/setup-java@v4 with: diff --git a/.github/workflows/deployment-jdk-ea.yml b/.github/workflows/deployment-jdk-ea.yml index 5978e568342e..20c018e79687 100644 --- a/.github/workflows/deployment-jdk-ea.yml +++ b/.github/workflows/deployment-jdk-ea.yml @@ -87,12 +87,12 @@ jobs: packages: pigz version: 1.0 - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.13.2 + uses: gittools/actions/gitversion/setup@v0.13.4 with: versionSpec: "5.x" - name: Run GitVersion id: gitversion - uses: gittools/actions/gitversion/execute@v0.13.2 + uses: gittools/actions/gitversion/execute@v0.13.4 - name: 'Set up JDK ${{ matrix.jdk }}' uses: oracle-actions/setup-java@v1 with: diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 02f625040130..a18391321edb 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -79,12 +79,12 @@ jobs: packages: pigz version: 1.0 - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.13.2 + uses: gittools/actions/gitversion/setup@v0.13.4 with: versionSpec: "5.x" - name: Run GitVersion id: gitversion - uses: gittools/actions/gitversion/execute@v0.13.2 + uses: gittools/actions/gitversion/execute@v0.13.4 - name: Setup JDK uses: actions/setup-java@v4 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3070c02b51c9..1016762e4aa9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -171,11 +171,15 @@ jobs: run: xvfb-run --auto-servernum ./gradlew check -x checkstyleJmh -x checkstyleMain -x checkstyleTest -x modernizer env: CI: "true" + - name: Prepare format failed test results + if: failure() + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: xml-twig-tools xsltproc + version: 1.0 - name: Format failed test results if: failure() - run: | - sudo apt-get install -qq -y xml-twig-tools xsltproc - scripts/after-failure.sh + run: scripts/after-failure.sh databasetests: name: Database tests runs-on: ubuntu-latest diff --git a/.github/workflows/upload-release.yml b/.github/workflows/upload-release.yml index 41a73d8d6f84..d7020264bc52 100644 --- a/.github/workflows/upload-release.yml +++ b/.github/workflows/upload-release.yml @@ -18,7 +18,7 @@ jobs: cd build wget -q -m -r -nH --cut-dirs 2 --no-parent --accept=tar.gz,dmg,pkg,deb,rpm,zip,msi https://builds.jabref.org/tags/ - name: Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: draft: true files: build/** diff --git a/CHANGELOG.md b/CHANGELOG.md index ac6f98c6f78b..d4f9d41546e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,10 +24,12 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - When pasting HTML into the abstract or a comment field, the hypertext is automatically converted to Markdown. [#10558](https://github.com/JabRef/jabref/issues/10558) - We added the possibility to redownload files that had been present but are no longer in the specified location. [#10848](https://github.com/JabRef/jabref/issues/10848) - We added the citation key pattern `[camelN]`. Equivalent to the first N words of the `[camel]` pattern. +- We added importing of static groups and linked files from BibDesk .bib files. [#10381](https://github.com/JabRef/jabref/issues/10381) - We added ability to export in CFF (Citation File Format) [#10661](https://github.com/JabRef/jabref/issues/10661). - We added ability to push entries to TeXworks. [#3197](https://github.com/JabRef/jabref/issues/3197) - We added the ability to zoom in and out in the document viewer using Ctrl + Scroll. [#10964](https://github.com/JabRef/jabref/pull/10964) - We added a Cleanup for removing non-existent files and grouped the related options [#10929](https://github.com/JabRef/jabref/issues/10929) +- We added the functionality to parse the bibliography of PDFs using the GROBID online service. [#10200](https://github.com/JabRef/jabref/issues/10200) ### Changed @@ -41,6 +43,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We changed the duplicate handling in the Import entries dialog. Potential duplicate entries are marked with an icon and importing will now trigger the merge dialog [#10914](https://github.com/JabRef/jabref/pull/10914) - We made the command "Push to TexShop" more robust to allow cite commands with a character before the first slash. [forum#2699](https://discourse.jabref.org/t/push-to-texshop-mac/2699/17?u=siedlerchr) - We only show the notification "Saving library..." if the library contains more than 2000 entries. [#9803](https://github.com/JabRef/jabref/issues/9803) +- JabRef now keeps previous log files upon start. [#11023](https://github.com/JabRef/jabref/pull/11023) - We enhanced the dialog for adding new fields in the content selector with a selection box containing a list of standard fields. [#10912](https://github.com/JabRef/jabref/pull/10912) - We store the citation relations in an LRU cache to avoid bloating the memory and out-of-memory exceptions. [#10958](https://github.com/JabRef/jabref/issues/10958) - Keywords filed are now displayed as tags. [#10910](https://github.com/JabRef/jabref/pull/10910) @@ -53,6 +56,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where the preview panel showing the wrong entry (an entry that is not selected in the entry table). [#9172](https://github.com/JabRef/jabref/issues/9172) - We fixed an issue where HTML-reserved characters like '&' and '<', in addition to HTML entities like '&' were not rendered correctly in entry preview. [#10677](https://github.com/JabRef/jabref/issues/10677) - The last page of a PDF is now indexed by the full text search. [#10193](https://github.com/JabRef/jabref/issues/10193) +- The entry editor respects the configured custom tabs when showing "Other fields". [#11012](https://github.com/JabRef/jabref/pull/11012) - The default owner of an entry can be changed again. [#10924](https://github.com/JabRef/jabref/issues/10924) - We fixed an issue where the duplicate check did not take umlauts or other LaTeX-encoded characters into account. [#10744](https://github.com/JabRef/jabref/pull/10744) - We fixed the colors of the icon on hover for unset special fields. [#10431](https://github.com/JabRef/jabref/issues/10431) @@ -61,6 +65,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where the `CommentsTab` was not properly formatted when the `defaultOwner` contained capital or special letters. [#10870](https://github.com/JabRef/jabref/issues/10870) - We fixed an issue where the `File -> Close library` menu item was not disabled when no library was open. [#10948](https://github.com/JabRef/jabref/issues/10948) - We fixed an issue where the Document Viewer would show the PDF in only half the window when maximized. [#10934](https://github.com/JabRef/jabref/issues/10934) +- Clicking on the crossref and related tags in the entry editor jumps to the linked entry. [#5484](https://github.com/JabRef/jabref/issues/5484) [#9369](https://github.com/JabRef/jabref/issues/9369) ### Removed diff --git a/build.gradle b/build.gradle index 146f6bfc54fa..61eeaafc33f5 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ plugins { id 'me.champeau.jmh' version '0.7.2' - id 'org.javamodularity.moduleplugin' version '1.8.14' + id 'org.javamodularity.moduleplugin' version '1.8.15' id 'org.openjfx.javafxplugin' version '0.1.0' @@ -196,14 +196,14 @@ dependencies { // route all requests to java.util.logging to SLF4J (which in turn routes to tinylog) implementation 'org.slf4j:jul-to-slf4j:2.0.9' // route all requests to log4j to SLF4J - implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.23.0' + implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.23.1' implementation('de.undercouch:citeproc-java:3.0.0-beta.2') { exclude group: 'org.antlr' } // jakarta.activation is already dependency of glassfish - implementation group: 'jakarta.xml.bind', name: 'jakarta.xml.bind-api', version: '4.0.1' + implementation group: 'jakarta.xml.bind', name: 'jakarta.xml.bind-api', version: '4.0.2' implementation group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '4.0.3' implementation ('com.github.tomtung:latex2unicode_2.13:0.3.2') { @@ -228,7 +228,7 @@ dependencies { implementation 'org.glassfish.jersey.core:jersey-server:3.1.5' // injection framework implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.5' - implementation 'org.glassfish.hk2:hk2-api:3.0.6' + implementation 'org.glassfish.hk2:hk2-api:3.1.0' // 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' @@ -242,7 +242,10 @@ dependencies { // 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' + // parse plist files + implementation 'com.googlecode.plist:dd-plist:1.23' + + testImplementation 'io.github.classgraph:classgraph:4.8.168' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testImplementation 'org.junit.platform:junit-platform-launcher:1.10.2' diff --git a/config/README.md b/config/README.md index d0a6bf89e979..0bea959d5f22 100644 --- a/config/README.md +++ b/config/README.md @@ -20,7 +20,7 @@ Style-checks are done for each pull request and installing this code style confi # Eclipse: The Eclipse code formatter style is stored in the `eclipse.gradle` file and gets imported automatically. -In case the formatter style needs to be adapted, configure it and export in in eclipse. +In case the formatter style needs to be adapted, configure it and export in eclipse. 1. Right click on the eclipse project "JabRef" 2. Select "Export > General > Preferences" diff --git a/docs/code-howtos/custom-svg-icons.md b/docs/code-howtos/custom-svg-icons.md index cd20685a80a9..5f6070c57009 100644 --- a/docs/code-howtos/custom-svg-icons.md +++ b/docs/code-howtos/custom-svg-icons.md @@ -23,7 +23,14 @@ Good icon design requires years of experience and cannot be covered here. Adapti ## Step 2. Packing the icons into a font -Use the [IcoMoon](https://icomoon.io) tool for packing the icons. Create a new set and import _all_ icons. Rearrange them so that they have the same order as in `org.jabref.gui.JabRefMaterialDesignIcon`. This will avoid that you have to change the code points for the existing glyphs. In the settings for your icon set, set the _Grid_ to 24. This is important to get the correct spacing. The name of the font is `JabRefMaterialDesign`. When your icon-set is ready, select all of them and download the font-package. +Use the [IcoMoon](https://icomoon.io) tool for packing the icons. + +1. Create a new set by importing the json file +2. Next to the icons, click on the hamburger menu, chose "Import to Set" to add a new icon (it will be added to the front) +Rearrange them so that they have the same order as in `org.jabref.gui.JabRefMaterialDesignIcon`. This will avoid that you have to change the code points for the existing glyphs. In the settings for your icon set, set the _Grid_ to 24. This is important to get the correct spacing. The name of the font is `JabRefMaterialDesign`. +3. Next to the icons, click on the hamburger menu and click "Select all". +4. Proceed with the font creating, set the font property name to `JabRefMaterialDesign` +When your icon-set is ready, select all of them and download the font-package. ## Step 3. Replace the existing `JabRefMaterialDesign.ttf` diff --git a/docs/teaching.md b/docs/teaching.md index 93e5fb79e257..f5c4b5670978 100644 --- a/docs/teaching.md +++ b/docs/teaching.md @@ -83,8 +83,6 @@ Course: BSc Computer Science Individual Project * Summary: Students experience the procedure of finding and fixing small and medium issues in an open source project. * Successfully run in 2022/2023 -Do you have additions/suggestions for improvement? - #### Northern Arizona University (NAU), USA Course [CS499 - Open Source Software Development](https://github.com/igorsteinmacher/CS499-OSS) @@ -129,7 +127,7 @@ Course "Programming and Software Development" as part of the [BSc Software Engin Course [DD2480 Software Engineering Fundamentals](https://www.kth.se/student/kurser/kurs/DD2480?l=en) * Summary: Groups of students from three to five persons experienced the whole software engineering process within a week: From the requirements' specification to the final pull request. -* Successfully run in 2020 +* Successfully run in 2020, 2024, and other years. ### Portuguese diff --git a/licenses/com.googlecode.plist_ddplist.txt b/licenses/com.googlecode.plist_ddplist.txt new file mode 100644 index 000000000000..ab9e4668533e --- /dev/null +++ b/licenses/com.googlecode.plist_ddplist.txt @@ -0,0 +1,20 @@ +dd-plist - An open source library to parse and generate property lists +Copyright (C) 2016 Daniel Dreibrodt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/rewrite.yml b/rewrite.yml index 41738ae1ecab..fd8ccb02f1bf 100644 --- a/rewrite.yml +++ b/rewrite.yml @@ -103,8 +103,6 @@ recipeList: - org.openrewrite.java.migrate.lang.StringFormatted - org.openrewrite.java.migrate.util.SequencedCollection - - org.openrewrite.java.recipes.UseJavaParserBuilderInJavaTemplate - - org.openrewrite.java.RemoveObjectsIsNull - org.openrewrite.java.ShortenFullyQualifiedTypeReferences diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 17a0f833a573..fd4dbe1b0c21 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -144,4 +144,5 @@ requires org.libreoffice.uno; requires de.saxsys.mvvmfx.validation; requires com.jthemedetector; + requires dd.plist; } diff --git a/src/main/java/org/jabref/Launcher.java b/src/main/java/org/jabref/Launcher.java index ba015659d033..8f1ea3229c72 100644 --- a/src/main/java/org/jabref/Launcher.java +++ b/src/main/java/org/jabref/Launcher.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.Map; -import org.jabref.architecture.AllowedToUseStandardStreams; import org.jabref.cli.ArgumentProcessor; import org.jabref.cli.JabRefCLI; import org.jabref.gui.Globals; @@ -19,7 +18,6 @@ import org.jabref.logic.UiCommand; 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; import org.jabref.logic.net.ProxyRegisterer; @@ -48,7 +46,6 @@ * - Handle the command line arguments * - Start the JavaFX application (if not in cli mode) */ -@AllowedToUseStandardStreams("Direct output to the user") public class Launcher { private static Logger LOGGER; private static boolean isDebugEnabled; @@ -140,11 +137,14 @@ private static void addLogToDisk() { // The "Shared File Writer" is explained at // https://tinylog.org/v2/configuration/#shared-file-writer Map configuration = Map.of( - "writerFile", "shared file", - "writerFile.level", isDebugEnabled ? "debug" : "info", "level", isDebugEnabled ? "debug" : "info", - "writerFile.file", directory.resolve("log.txt").toString(), - "writerFile.charset", "UTF-8"); + "writerFile", "rolling file", + "writerFile.level", isDebugEnabled ? "debug" : "info", + // We need to manually join the path, because ".resolve" does not work on Windows, because ":" is not allowed in file names on Windows + "writerFile.file", directory + File.separator + "log_{date:yyyy-MM-dd_HH-mm-ss}.txt", + "writerFile.charset", "UTF-8", + "writerFile.policies", "startup", + "writerFile.backups", "30"); configuration.forEach(Configuration::set); initializeLogger(); @@ -169,9 +169,8 @@ private static boolean handleMultipleAppInstances(String[] args, RemotePreferenc LOGGER.debug("Passing arguments passed on to running JabRef..."); if (remoteClient.sendCommandLineArguments(args)) { // So we assume it's all taken care of, and quit. - LOGGER.debug("Arguments passed on to running JabRef instance."); - // Used for script-use output etc. to the user - System.out.println(Localization.lang("Arguments passed on to running JabRef instance. Shutting down.")); + // Output to both to the log and the screen. Therefore, we do not have an additional System.out.println. + LOGGER.info("Arguments passed on to running JabRef instance. Shutting down."); return false; } else { LOGGER.warn("Could not communicate with other running JabRef instance."); diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 1265cac4d31f..6f66180350b6 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -574,9 +574,11 @@ public void init() { EasyBind.subscribe(tabbedPane.getSelectionModel().selectedItemProperty(), selectedTab -> { if (selectedTab instanceof LibraryTab libraryTab) { stateManager.setActiveDatabase(libraryTab.getBibDatabaseContext()); + stateManager.activeTabProperty().set(Optional.of(libraryTab)); } else if (selectedTab == null) { // All databases are closed stateManager.setActiveDatabase(null); + stateManager.activeTabProperty().set(Optional.empty()); } }); diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 8b9488b1559b..cec2adb16443 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -282,8 +282,9 @@ private void setDatabaseContext(BibDatabaseContext bibDatabaseContext) { return; } if (tabPane.getSelectionModel().selectedItemProperty().get().equals(this)) { - LOGGER.debug("This case should not happen."); + LOGGER.warn("This case should not happen."); stateManager.setActiveDatabase(bibDatabaseContext); + stateManager.activeTabProperty().set(Optional.of(this)); } // Remove existing dummy BibDatabaseContext and add correct BibDatabaseContext from ParserResult to trigger changes in the openDatabases list in the stateManager @@ -597,8 +598,8 @@ private void setupAutoCompletion() { if (autoCompletePreferences.shouldAutoComplete()) { suggestionProviders = new SuggestionProviders(getDatabase(), Globals.journalAbbreviationRepository, autoCompletePreferences); } else { - // Create suggestion providers with database for crossref if auto-completion is deactivated - suggestionProviders = new SuggestionProviders(getDatabase()); + // Create empty suggestion providers if auto-completion is deactivated + suggestionProviders = new SuggestionProviders(); } searchAutoCompleter = new PersonNameSuggestionProvider(FieldFactory.getPersonNameFields(), getDatabase()); } diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 743ad0ef4737..bdc1659ac759 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -54,6 +54,7 @@ public class StateManager { private final CustomLocalDragboard localDragboard = new CustomLocalDragboard(); private final ObservableList openDatabases = FXCollections.observableArrayList(); private final OptionalObjectProperty activeDatabase = OptionalObjectProperty.empty(); + private final OptionalObjectProperty activeTab = OptionalObjectProperty.empty(); private final ReadOnlyListWrapper activeGroups = new ReadOnlyListWrapper<>(FXCollections.observableArrayList()); private final ObservableList selectedEntries = FXCollections.observableArrayList(); private final ObservableMap> selectedGroups = FXCollections.observableHashMap(); @@ -91,6 +92,10 @@ public OptionalObjectProperty activeDatabaseProperty() { return activeDatabase; } + public OptionalObjectProperty activeTabProperty() { + return activeTab; + } + public OptionalObjectProperty activeSearchQueryProperty() { return activeSearchQuery; } diff --git a/src/main/java/org/jabref/gui/actions/StandardActions.java b/src/main/java/org/jabref/gui/actions/StandardActions.java index 6f74e2caa1f4..949dac28b292 100644 --- a/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -32,6 +32,7 @@ public enum StandardActions implements Action { REBUILD_FULLTEXT_SEARCH_INDEX(Localization.lang("Rebuild fulltext search index"), IconTheme.JabRefIcons.FILE), REDOWNLOAD_MISSING_FILES(Localization.lang("Redownload missing files"), IconTheme.JabRefIcons.DOWNLOAD), OPEN_EXTERNAL_FILE(Localization.lang("Open file"), IconTheme.JabRefIcons.FILE, KeyBinding.OPEN_FILE), + EXTRACT_FILE_REFERENCES(Localization.lang("Extract references from file"), IconTheme.JabRefIcons.FILE_STAR), OPEN_URL(Localization.lang("Open URL or DOI"), IconTheme.JabRefIcons.WWW, KeyBinding.OPEN_URL_OR_DOI), SEARCH_SHORTSCIENCE(Localization.lang("Search ShortScience")), MERGE_WITH_FETCHED_ENTRY(Localization.lang("Get bibliographic data from %0", "DOI/ISBN/...")), diff --git a/src/main/java/org/jabref/gui/autocompleter/ContentSelectorSuggestionProvider.java b/src/main/java/org/jabref/gui/autocompleter/ContentSelectorSuggestionProvider.java index 01e56ff53507..ebb6ec048371 100644 --- a/src/main/java/org/jabref/gui/autocompleter/ContentSelectorSuggestionProvider.java +++ b/src/main/java/org/jabref/gui/autocompleter/ContentSelectorSuggestionProvider.java @@ -1,7 +1,6 @@ package org.jabref.gui.autocompleter; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.stream.Stream; @@ -26,7 +25,7 @@ public Stream getSource() { } @Override - public Collection getPossibleSuggestions() { + public List getPossibleSuggestions() { List suggestions = new ArrayList<>(); if (suggestionProvider != null) { suggestions.addAll(suggestionProvider.getPossibleSuggestions()); diff --git a/src/main/java/org/jabref/gui/autocompleter/SuggestionProvider.java b/src/main/java/org/jabref/gui/autocompleter/SuggestionProvider.java index 341c9ef8f5e6..fb64bb372b7f 100644 --- a/src/main/java/org/jabref/gui/autocompleter/SuggestionProvider.java +++ b/src/main/java/org/jabref/gui/autocompleter/SuggestionProvider.java @@ -29,6 +29,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -60,7 +61,7 @@ public final Collection provideSuggestions(ISuggestionRequest request) { protected abstract Equivalence getEquivalence(); - public Collection getPossibleSuggestions() { + public List getPossibleSuggestions() { Comparator comparator = getComparator().reversed(); Equivalence equivalence = getEquivalence(); return getSource().map(equivalence::wrap) // Need to do a bit of acrobatic as there is no distinctBy method diff --git a/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java b/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java index 7d023f4d8156..06d2abef8a8e 100644 --- a/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java +++ b/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java @@ -22,21 +22,12 @@ public SuggestionProviders(BibDatabase database, JournalAbbreviationRepository a this.isEmpty = false; } - public SuggestionProviders(BibDatabase database) { - this.database = database; - this.isEmpty = true; - } - public SuggestionProviders() { this.isEmpty = true; } public SuggestionProvider getForField(Field field) { if (isEmpty || !autoCompletePreferences.getCompleteFields().contains(field)) { - Set fieldProperties = field.getProperties(); - if (fieldProperties.contains(FieldProperty.SINGLE_ENTRY_LINK)) { - return new BibEntrySuggestionProvider(database); - } return new EmptySuggestionProvider(); } diff --git a/src/main/java/org/jabref/gui/entryeditor/OptionalFields2Tab.java b/src/main/java/org/jabref/gui/entryeditor/DetailOptionalFieldsTab.java similarity index 57% rename from src/main/java/org/jabref/gui/entryeditor/OptionalFields2Tab.java rename to src/main/java/org/jabref/gui/entryeditor/DetailOptionalFieldsTab.java index c63c145cfba9..e884d5b297de 100644 --- a/src/main/java/org/jabref/gui/entryeditor/OptionalFields2Tab.java +++ b/src/main/java/org/jabref/gui/entryeditor/DetailOptionalFieldsTab.java @@ -14,21 +14,21 @@ import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.preferences.PreferencesService; -public class OptionalFields2Tab extends OptionalFieldsTabBase { +public class DetailOptionalFieldsTab extends OptionalFieldsTabBase { public static final String NAME = "Optional fields 2"; - public OptionalFields2Tab(BibDatabaseContext databaseContext, - SuggestionProviders suggestionProviders, - UndoManager undoManager, - DialogService dialogService, - PreferencesService preferences, - StateManager stateManager, - ThemeManager themeManager, - IndexingTaskManager indexingTaskManager, - BibEntryTypesManager entryTypesManager, - TaskExecutor taskExecutor, - JournalAbbreviationRepository journalAbbreviationRepository) { + public DetailOptionalFieldsTab(BibDatabaseContext databaseContext, + SuggestionProviders suggestionProviders, + UndoManager undoManager, + DialogService dialogService, + PreferencesService preferences, + StateManager stateManager, + ThemeManager themeManager, + IndexingTaskManager indexingTaskManager, + BibEntryTypesManager entryTypesManager, + TaskExecutor taskExecutor, + JournalAbbreviationRepository journalAbbreviationRepository) { super( Localization.lang("Optional fields 2"), false, diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index 436f62cd3035..e31448fbb1ef 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -249,45 +249,45 @@ private void navigateToNextEntry() { private List createTabs() { entryEditorTabs.add(new PreviewTab(databaseContext, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), taskExecutor)); - // Required, optional, deprecated, and "other" fields + // Required, optional (important+detail), deprecated, and "other" fields entryEditorTabs.add(new RequiredFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository)); - entryEditorTabs.add(new OptionalFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository)); - entryEditorTabs.add(new OptionalFields2Tab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository)); + entryEditorTabs.add(new ImportantOptionalFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository)); + entryEditorTabs.add(new DetailOptionalFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository)); entryEditorTabs.add(new DeprecatedFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository)); entryEditorTabs.add(new OtherFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository)); // Comment Tab: Tab for general and user-specific comments entryEditorTabs.add(new CommentsTab(preferencesService, databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), taskExecutor, journalAbbreviationRepository)); - // General fields from preferences - // First, remove all tabs that are already handled above or below; except for the source tab (which has different titles for BibTeX and BibLaTeX mode) + // The preferences allow to configure tabs to show (e.g.,"General", "Abstract") + // These should be shown. Already hard-coded ones should be removed. Map> entryEditorTabList = new HashMap<>(entryEditorPreferences.getEntryEditorTabs()); entryEditorTabList.remove(PreviewTab.NAME); entryEditorTabList.remove(RequiredFieldsTab.NAME); - entryEditorTabList.remove(OptionalFieldsTab.NAME); - entryEditorTabList.remove(OptionalFields2Tab.NAME); + entryEditorTabList.remove(ImportantOptionalFieldsTab.NAME); + entryEditorTabList.remove(DetailOptionalFieldsTab.NAME); entryEditorTabList.remove(DeprecatedFieldsTab.NAME); + entryEditorTabList.remove(OtherFieldsTab.NAME); entryEditorTabList.remove(CommentsTab.NAME); entryEditorTabList.remove(MathSciNetTab.NAME); entryEditorTabList.remove(FileAnnotationTab.NAME); + entryEditorTabList.remove(SciteTab.NAME); + // CitationRelationsTab entryEditorTabList.remove(RelatedArticlesTab.NAME); + // SourceTab -- not listed, because it has different names for BibTeX and biblatex mode entryEditorTabList.remove(LatexCitationsTab.NAME); entryEditorTabList.remove(FulltextSearchResultsTab.NAME); - entryEditorTabList.remove(SciteTab.NAME); - entryEditorTabList.remove("Comments"); - // Then show the remaining configured + for (Map.Entry> tab : entryEditorTabList.entrySet()) { entryEditorTabs.add(new UserDefinedFieldsTab(tab.getKey(), tab.getValue(), databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), taskExecutor, journalAbbreviationRepository)); } - // "Special" tabs entryEditorTabs.add(new MathSciNetTab()); entryEditorTabs.add(new FileAnnotationTab(libraryTab.getAnnotationCache())); entryEditorTabs.add(new SciteTab(preferencesService, taskExecutor, dialogService)); entryEditorTabs.add(new CitationRelationsTab(entryEditorPreferences, dialogService, databaseContext, undoManager, stateManager, fileMonitor, preferencesService, libraryTab, taskExecutor)); entryEditorTabs.add(new RelatedArticlesTab(entryEditorPreferences, preferencesService, dialogService, taskExecutor)); - sourceTab = new SourceTab( databaseContext, undoManager, @@ -299,9 +299,7 @@ private List createTabs() { bibEntryTypesManager, keyBindingRepository); entryEditorTabs.add(sourceTab); - entryEditorTabs.add(new LatexCitationsTab(databaseContext, preferencesService, taskExecutor, dialogService)); - entryEditorTabs.add(new FulltextSearchResultsTab(stateManager, preferencesService, dialogService, taskExecutor)); return entryEditorTabs; diff --git a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/ImportantOptionalFieldsTab.java similarity index 56% rename from src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTab.java rename to src/main/java/org/jabref/gui/entryeditor/ImportantOptionalFieldsTab.java index ad522d2411fb..5258477d3f48 100644 --- a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/ImportantOptionalFieldsTab.java @@ -14,21 +14,21 @@ import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.preferences.PreferencesService; -public class OptionalFieldsTab extends OptionalFieldsTabBase { +public class ImportantOptionalFieldsTab extends OptionalFieldsTabBase { public static final String NAME = "Optional fields"; - public OptionalFieldsTab(BibDatabaseContext databaseContext, - SuggestionProviders suggestionProviders, - UndoManager undoManager, - DialogService dialogService, - PreferencesService preferences, - StateManager stateManager, - ThemeManager themeManager, - IndexingTaskManager indexingTaskManager, - BibEntryTypesManager entryTypesManager, - TaskExecutor taskExecutor, - JournalAbbreviationRepository journalAbbreviationRepository) { + public ImportantOptionalFieldsTab(BibDatabaseContext databaseContext, + SuggestionProviders suggestionProviders, + UndoManager undoManager, + DialogService dialogService, + PreferencesService preferences, + StateManager stateManager, + ThemeManager themeManager, + IndexingTaskManager indexingTaskManager, + BibEntryTypesManager entryTypesManager, + TaskExecutor taskExecutor, + JournalAbbreviationRepository journalAbbreviationRepository) { super( Localization.lang("Optional fields"), true, diff --git a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java b/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java index 5f61a0279ab5..5a95c3ddb8be 100644 --- a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java +++ b/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java @@ -27,10 +27,10 @@ public class OptionalFieldsTabBase extends FieldsEditorTab { private final BibEntryTypesManager entryTypesManager; - private final boolean isPrimaryOptionalFields; + private final boolean isImportantOptionalFields; public OptionalFieldsTabBase(String title, - boolean isPrimaryOptionalFields, + boolean isImportantOptionalFields, BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, @@ -54,7 +54,7 @@ public OptionalFieldsTabBase(String title, journalAbbreviationRepository, indexingTaskManager); this.entryTypesManager = entryTypesManager; - this.isPrimaryOptionalFields = isPrimaryOptionalFields; + this.isImportantOptionalFields = isImportantOptionalFields; setText(title); setTooltip(new Tooltip(Localization.lang("Show optional fields"))); setGraphic(IconTheme.JabRefIcons.OPTIONAL.getGraphicNode()); @@ -65,10 +65,10 @@ protected SequencedSet determineFieldsToShow(BibEntry entry) { BibDatabaseMode mode = databaseContext.getMode(); Optional entryType = entryTypesManager.enrich(entry.getType(), mode); if (entryType.isPresent()) { - if (isPrimaryOptionalFields) { - return entryType.get().getPrimaryOptionalFields(); + if (isImportantOptionalFields) { + return entryType.get().getImportantOptionalFields(); } else { - return entryType.get().getSecondaryOptionalNotDeprecatedFields(mode); + return entryType.get().getDetailOptionalNotDeprecatedFields(mode); } } else { // Entry type unknown -> treat all fields as required (thus no optional fields) diff --git a/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java index 4ed6b1434ad1..52882b29f5a2 100644 --- a/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java @@ -26,7 +26,6 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryType; import org.jabref.model.entry.BibEntryTypesManager; -import org.jabref.model.entry.field.BibField; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.InternalField; import org.jabref.model.entry.field.StandardField; @@ -36,7 +35,7 @@ public class OtherFieldsTab extends FieldsEditorTab { public static final String NAME = "Other fields"; - private final List customTabFieldNames; + private final List customTabsFieldNames; private final BibEntryTypesManager entryTypesManager; public OtherFieldsTab(BibDatabaseContext databaseContext, @@ -63,8 +62,8 @@ public OtherFieldsTab(BibDatabaseContext databaseContext, indexingTaskManager); this.entryTypesManager = entryTypesManager; - this.customTabFieldNames = new ArrayList<>(); - preferences.getEntryEditorPreferences().getDefaultEntryEditorTabs().values().forEach(customTabFieldNames::addAll); + this.customTabsFieldNames = new ArrayList<>(); + preferences.getEntryEditorPreferences().getEntryEditorTabs().values().forEach(customTabsFieldNames::addAll); setText(Localization.lang("Other fields")); setTooltip(new Tooltip(Localization.lang("Show remaining fields"))); @@ -76,15 +75,21 @@ protected SequencedSet determineFieldsToShow(BibEntry entry) { BibDatabaseMode mode = databaseContext.getMode(); Optional entryType = entryTypesManager.enrich(entry.getType(), mode); if (entryType.isPresent()) { + // Get all required and optional fields configured for the entry Set allKnownFields = entryType.get().getAllFields(); + // Remove all fields being required or optional SequencedSet otherFields = entry.getFields().stream() - .filter(field -> !allKnownFields.contains(field) && - !(field.equals(StandardField.COMMENT) || field instanceof UserSpecificCommentField)) + .filter(field -> !allKnownFields.contains(field)) .collect(Collectors.toCollection(LinkedHashSet::new)); - otherFields.removeAll(entryType.get().getDeprecatedFields(mode)); - otherFields.removeAll(entryType.get().getOptionalFields().stream().map(BibField::field).toList()); + // The key field is in the required tab, but has a special treatment otherFields.remove(InternalField.KEY_FIELD); - customTabFieldNames.forEach(otherFields::remove); + // Remove all fields contained in JabRef's tab "Deprecated" + otherFields.removeAll(entryType.get().getDeprecatedFields(mode)); + // Remove all fields contained in the custom tabs + customTabsFieldNames.forEach(otherFields::remove); + // Remove all user-comment fields (tab org.jabref.gui.entryeditor.CommentsTab) + otherFields.removeIf(field -> field.equals(StandardField.COMMENT)); + otherFields.removeIf(field -> field instanceof UserSpecificCommentField); return otherFields; } else { // Entry type unknown -> treat all fields as required (thus no other fields) diff --git a/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java index 9e7d03eae64e..a4939e66d792 100644 --- a/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java @@ -58,7 +58,7 @@ protected SequencedSet determineFieldsToShow(BibEntry entry) { for (OrFields orFields : entryType.get().getRequiredFields()) { fields.addAll(orFields.getFields()); } - // Add the edit field for Bibtex-key. + // Add the edit field for BibTeX key (AKA citation key) fields.add(InternalField.KEY_FIELD); } else { // Entry type unknown -> treat all fields as required diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java index ec921b2f523f..bbdefc05f1c6 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java @@ -15,7 +15,6 @@ import org.jabref.logic.integrity.FieldCheckers; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.model.database.BibDatabaseContext; -import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldFactory; import org.jabref.model.entry.field.FieldProperty; @@ -89,10 +88,8 @@ public static FieldEditorFX getForField(final Field field, } else { return new OptionEditor<>(new TypeEditorViewModel(field, suggestionProvider, fieldCheckers, undoManager)); } - } else if (fieldProperties.contains(FieldProperty.SINGLE_ENTRY_LINK)) { - return new LinkedEntriesEditor(field, databaseContext, (SuggestionProvider) suggestionProvider, fieldCheckers); - } else if (fieldProperties.contains(FieldProperty.MULTIPLE_ENTRY_LINK)) { - return new LinkedEntriesEditor(field, databaseContext, (SuggestionProvider) suggestionProvider, fieldCheckers); + } else if (fieldProperties.contains(FieldProperty.SINGLE_ENTRY_LINK) || fieldProperties.contains(FieldProperty.MULTIPLE_ENTRY_LINK)) { + return new LinkedEntriesEditor(field, databaseContext, suggestionProvider, fieldCheckers); } else if (fieldProperties.contains(FieldProperty.PERSON_NAMES)) { return new PersonsEditor(field, suggestionProvider, preferences, fieldCheckers, isMultiLine, undoManager); } else if (StandardField.KEYWORDS == field) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java index 3c530084dd31..0ac0a2e064d6 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java @@ -1,7 +1,6 @@ package org.jabref.gui.fieldeditors; import java.util.Comparator; -import java.util.stream.Collectors; import javax.swing.undo.UndoManager; @@ -18,6 +17,7 @@ import org.jabref.gui.ClipBoardManager; import org.jabref.gui.DialogService; import org.jabref.gui.JabRefDialogService; +import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionFactory; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.actions.StandardActions; @@ -48,24 +48,20 @@ public class LinkedEntriesEditor extends HBox implements FieldEditorFX { @Inject private ClipBoardManager clipBoardManager; @Inject private KeyBindingRepository keyBindingRepository; @Inject private UndoManager undoManager; + @Inject private StateManager stateManager; private final LinkedEntriesEditorViewModel viewModel; - public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { + public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { ViewLoader.view(this) .root(this) .load(); - this.viewModel = new LinkedEntriesEditorViewModel(field, suggestionProvider, databaseContext, fieldCheckers, undoManager); + this.viewModel = new LinkedEntriesEditorViewModel(field, suggestionProvider, databaseContext, fieldCheckers, undoManager, stateManager); entryLinkField.setCellFactory(new ViewModelListCellFactory().withText(ParsedEntryLink::getKey)); - // Mind the .collect(Collectors.toList()) as the list needs to be mutable - entryLinkField.setSuggestionProvider(request -> - suggestionProvider.getPossibleSuggestions().stream() - .filter(suggestion -> suggestion.getCitationKey().orElse("").toLowerCase() - .contains(request.getUserText().toLowerCase())) - .map(ParsedEntryLink::new) - .collect(Collectors.toList())); + entryLinkField.setSuggestionProvider(request -> viewModel.getSuggestions(request.getUserText())); + entryLinkField.setTagViewFactory(this::createTag); entryLinkField.setConverter(viewModel.getStringConverter()); entryLinkField.setNewItemProducer(searchText -> viewModel.getStringConverter().fromString(searchText)); @@ -85,7 +81,7 @@ private Node createTag(ParsedEntryLink entryLink) { tagLabel.getGraphic().setOnMouseClicked(event -> entryLinkField.removeTags(entryLink)); tagLabel.setContentDisplay(ContentDisplay.RIGHT); tagLabel.setOnMouseClicked(event -> { - if (event.getClickCount() == 2 && event.getButton().equals(MouseButton.PRIMARY)) { + if ((event.getClickCount() == 2 || event.isControlDown()) && event.getButton().equals(MouseButton.PRIMARY)) { viewModel.jumpToEntry(entryLink); } }); diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java index 28048defa978..a61599e9a30a 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java @@ -1,5 +1,9 @@ package org.jabref.gui.fieldeditors; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + import javax.swing.undo.UndoManager; import javafx.beans.property.ListProperty; @@ -7,10 +11,12 @@ import javafx.collections.FXCollections; import javafx.util.StringConverter; +import org.jabref.gui.StateManager; import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.gui.util.BindingsHelper; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.EntryLinkList; import org.jabref.model.entry.ParsedEntryLink; import org.jabref.model.entry.field.Field; @@ -18,12 +24,22 @@ public class LinkedEntriesEditorViewModel extends AbstractEditorViewModel { private final BibDatabaseContext databaseContext; + private final SuggestionProvider suggestionProvider; private final ListProperty linkedEntries; + private final StateManager stateManager; - public LinkedEntriesEditorViewModel(Field field, SuggestionProvider suggestionProvider, BibDatabaseContext databaseContext, FieldCheckers fieldCheckers, UndoManager undoManager) { + public LinkedEntriesEditorViewModel(Field field, + SuggestionProvider suggestionProvider, + BibDatabaseContext databaseContext, + FieldCheckers fieldCheckers, + UndoManager undoManager, + StateManager stateManager) { super(field, suggestionProvider, fieldCheckers, undoManager); this.databaseContext = databaseContext; + this.suggestionProvider = suggestionProvider; + this.stateManager = stateManager; + linkedEntries = new SimpleListProperty<>(FXCollections.observableArrayList()); BindingsHelper.bindContentBidirectional( linkedEntries, @@ -53,14 +69,26 @@ public ParsedEntryLink fromString(String key) { }; } + public List getSuggestions(String request) { + List suggestions = suggestionProvider + .getPossibleSuggestions() + .stream() + .map(suggestion -> suggestion instanceof BibEntry bibEntry ? bibEntry.getCitationKey().orElse("") : (String) suggestion) + .filter(suggestion -> suggestion.toLowerCase(Locale.ROOT).contains(request.toLowerCase(Locale.ROOT))) + .map(suggestion -> new ParsedEntryLink(suggestion, databaseContext.getDatabase())) + .distinct() + .collect(Collectors.toList()); + + ParsedEntryLink requestedLink = new ParsedEntryLink(request, databaseContext.getDatabase()); + if (!suggestions.contains(requestedLink)) { + suggestions.addFirst(requestedLink); + } + + return suggestions; + } + public void jumpToEntry(ParsedEntryLink parsedEntryLink) { - // TODO: Implement jump to entry - The implementation can be based on JabRefFrame.jumpToEntry - // TODO: Add toolitp for tag: Localization.lang("Jump to entry") - // This feature was removed while converting the linked entries editor to JavaFX - // Right now there is no nice way to re-implement it as we have no good interface to control the focus of the main table - // (except directly using the JabRefFrame class as below) - // parsedEntryLink.getLinkedEntry().ifPresent( - // e -> frame.getCurrentBasePanel().highlightEntry(e) - // ); + parsedEntryLink.getLinkedEntry().ifPresent(entry -> + stateManager.activeTabProperty().get().ifPresent(tab -> tab.clearAndSelect(entry))); } } diff --git a/src/main/java/org/jabref/gui/icon/IconTheme.java b/src/main/java/org/jabref/gui/icon/IconTheme.java index 10be9e160908..cc9bad0528bd 100644 --- a/src/main/java/org/jabref/gui/icon/IconTheme.java +++ b/src/main/java/org/jabref/gui/icon/IconTheme.java @@ -193,6 +193,7 @@ public enum JabRefIcons implements JabRefIcon { DELETE_ENTRY(MaterialDesignD.DELETE), SEARCH(MaterialDesignM.MAGNIFY), FILE_SEARCH(MaterialDesignF.FILE_FIND), + FILE_STAR(MaterialDesignF.FILE_STAR), PDF_METADATA_READ(MaterialDesignF.FORMAT_ALIGN_TOP), PDF_METADATA_WRITE(MaterialDesignF.FORMAT_ALIGN_BOTTOM), ADVANCED_SEARCH(Color.CYAN, MaterialDesignM.MAGNIFY), @@ -278,6 +279,7 @@ public enum JabRefIcons implements JabRefIcon { APPLICATION_WINEDT(JabRefMaterialDesignIcon.WINEDT), APPLICATION_SUBLIMETEXT(JabRefMaterialDesignIcon.SUBLIME_TEXT), APPLICATION_TEXSHOP(JabRefMaterialDesignIcon.TEXSHOP), + APPLICATION_TEXWORS(JabRefMaterialDesignIcon.TEXWORKS), KEY_BINDINGS(MaterialDesignK.KEYBOARD), FIND_DUPLICATES(MaterialDesignC.CODE_EQUAL), CONNECT_DB(MaterialDesignC.CLOUD_UPLOAD), diff --git a/src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIcon.java b/src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIcon.java index 6f697cc1e4aa..5d8deaa5d914 100644 --- a/src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIcon.java +++ b/src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIcon.java @@ -5,7 +5,7 @@ /** * Provides the same true-type font interface as MaterialDesignIcon itself, but uses a font we created ourselves that * contains icons that are not available in MaterialDesignIcons. - * + *

* The glyphs of the ttf (speak: the icons) were created with Illustrator and a template from the material design icons * web-page. The art boards for each icon was exported as SVG and then converted with * IcoMoon. The final TTF font is located in the resource folder. @@ -31,7 +31,8 @@ public enum JabRefMaterialDesignIcon implements Ikon { VSCODE("jab-vsvode", '\ue90d'), CANCEL("jab-cancel", '\ue90e'), SUBLIME_TEXT("jab-sublime-text", '\ue90f'), - TEXSHOP("jab-texshop", '\ue910'); + TEXSHOP("jab-texshop", '\ue910'), + TEXWORKS("jab-texworks", '\ue911'); private String description; private int code; diff --git a/src/main/java/org/jabref/gui/maintable/ExtractReferencesAction.java b/src/main/java/org/jabref/gui/maintable/ExtractReferencesAction.java new file mode 100644 index 000000000000..d46854c5f55b --- /dev/null +++ b/src/main/java/org/jabref/gui/maintable/ExtractReferencesAction.java @@ -0,0 +1,101 @@ +package org.jabref.gui.maintable; + +import java.nio.file.Path; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; + +import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.importer.ImportEntriesDialog; +import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.TaskExecutor; +import org.jabref.logic.importer.ParserResult; +import org.jabref.logic.importer.util.GrobidService; +import org.jabref.logic.l10n.Localization; +import org.jabref.logic.util.io.FileUtil; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.LinkedFile; +import org.jabref.preferences.PreferencesService; + +public class ExtractReferencesAction extends SimpleCommand { + private final int FILES_LIMIT = 10; + + private final DialogService dialogService; + private final StateManager stateManager; + private final PreferencesService preferencesService; + private final BibEntry entry; + private final LinkedFile linkedFile; + private final TaskExecutor taskExecutor; + + public ExtractReferencesAction(DialogService dialogService, + StateManager stateManager, + PreferencesService preferencesService, + TaskExecutor taskExecutor) { + this(dialogService, stateManager, preferencesService, null, null, taskExecutor); + } + + public ExtractReferencesAction(DialogService dialogService, + StateManager stateManager, + PreferencesService preferencesService, + BibEntry entry, + LinkedFile linkedFile, + TaskExecutor taskExecutor) { + this.dialogService = dialogService; + this.stateManager = stateManager; + this.preferencesService = preferencesService; + this.entry = entry; + this.linkedFile = linkedFile; + this.taskExecutor = taskExecutor; + + if (this.linkedFile == null) { + this.executable.bind( + ActionHelper.needsEntriesSelected(stateManager) + .and(ActionHelper.hasLinkedFileForSelectedEntries(stateManager)) + .and(this.preferencesService.getGrobidPreferences().grobidEnabledProperty()) + ); + } else { + this.setExecutable(true); + } + } + + @Override + public void execute() { + extractReferences(); + } + + private void extractReferences() { + stateManager.getActiveDatabase().ifPresent(databaseContext -> { + List selectedEntries = new LinkedList<>(); + if (entry == null) { + selectedEntries = stateManager.getSelectedEntries(); + } else { + selectedEntries.add(entry); + } + + List fileList = FileUtil.getListOfLinkedFiles(selectedEntries, databaseContext.getFileDirectories(preferencesService.getFilePreferences())); + if (fileList.size() > FILES_LIMIT) { + boolean continueOpening = dialogService.showConfirmationDialogAndWait(Localization.lang("Processing a large number of files"), + Localization.lang("You are about to process %0 files. Continue?", fileList.size()), + Localization.lang("Continue"), Localization.lang("Cancel")); + if (!continueOpening) { + return; + } + } + + Callable parserResultCallable = () -> new ParserResult( + new GrobidService(this.preferencesService.getGrobidPreferences()).processReferences(fileList, preferencesService.getImportFormatPreferences()) + ); + BackgroundTask task = BackgroundTask.wrap(parserResultCallable) + .withInitialMessage(Localization.lang("Processing PDF(s)")); + + task.onFailure(dialogService::showErrorDialogAndWait); + + ImportEntriesDialog dialog = new ImportEntriesDialog(stateManager.getActiveDatabase().get(), task); + dialog.setTitle(Localization.lang("Extract References")); + dialogService.showCustomDialogAndWait(dialog); + }); + } +} diff --git a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java index 6e65da6ab134..aa8a5477902d 100644 --- a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java +++ b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java @@ -75,6 +75,7 @@ public static ContextMenu create(BibEntryTableViewModel entry, factory.createMenuItem(StandardActions.ATTACH_FILE_FROM_URL, new AttachFileFromURLAction(dialogService, stateManager, taskExecutor, preferencesService)), factory.createMenuItem(StandardActions.OPEN_FOLDER, new OpenFolderAction(dialogService, stateManager, preferencesService, taskExecutor)), factory.createMenuItem(StandardActions.OPEN_EXTERNAL_FILE, new OpenExternalFileAction(dialogService, stateManager, preferencesService, taskExecutor)), + factory.createMenuItem(StandardActions.EXTRACT_FILE_REFERENCES, new ExtractReferencesAction(dialogService, stateManager, preferencesService, taskExecutor)), factory.createMenuItem(StandardActions.OPEN_URL, new OpenUrlAction(dialogService, stateManager, preferencesService)), factory.createMenuItem(StandardActions.SEARCH_SHORTSCIENCE, new SearchShortScienceAction(dialogService, stateManager, preferencesService)), diff --git a/src/main/java/org/jabref/gui/push/PushToTeXworks.java b/src/main/java/org/jabref/gui/push/PushToTeXworks.java index fbc052a64c74..af1be1241be8 100644 --- a/src/main/java/org/jabref/gui/push/PushToTeXworks.java +++ b/src/main/java/org/jabref/gui/push/PushToTeXworks.java @@ -26,8 +26,7 @@ public String getDisplayName() { @Override public JabRefIcon getApplicationIcon() { - // TODO: replace the placeholder icon with the real one. - return IconTheme.JabRefIcons.APPLICATION_GENERIC; + return IconTheme.JabRefIcons.APPLICATION_TEXWORS; } @Override diff --git a/src/main/java/org/jabref/logic/bibtex/comparator/BibDatabaseDiff.java b/src/main/java/org/jabref/logic/bibtex/comparator/BibDatabaseDiff.java index 7645ed85d1c6..b807124cfc3b 100644 --- a/src/main/java/org/jabref/logic/bibtex/comparator/BibDatabaseDiff.java +++ b/src/main/java/org/jabref/logic/bibtex/comparator/BibDatabaseDiff.java @@ -49,6 +49,11 @@ private static EntryComparator getEntryComparator() { private static List compareEntries(List originalEntries, List newEntries, BibDatabaseMode mode) { List differences = new ArrayList<>(); + // Prevent IndexOutOfBoundException + if (newEntries.isEmpty()) { + return differences; + } + // Create a HashSet where we can put references to entries in the new // database that we have matched. This is to avoid matching them twice. Set used = new HashSet<>(newEntries.size()); @@ -88,7 +93,6 @@ private static List compareEntries(List originalEntries, } } } - BibEntry bestEntry = newEntries.get(bestMatchIndex); if (bestMatch > MATCH_THRESHOLD || hasEqualCitationKey(originalEntry, bestEntry) diff --git a/src/main/java/org/jabref/logic/importer/fetcher/DBLPFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/DBLPFetcher.java index 1eef08ff5f6d..2c67443bb6a4 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/DBLPFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/DBLPFetcher.java @@ -68,7 +68,7 @@ public void doPostCleanup(BibEntry entry) { FieldFormatterCleanups cleanups = new FieldFormatterCleanups(true, List.of( new FieldFormatterCleanup(StandardField.TIMESTAMP, new ClearFormatter()), - // unescape the the contents of the URL field, e.g., some\_url\_part becomes some_url_part + // unescape the contents of the URL field, e.g., some\_url\_part becomes some_url_part new FieldFormatterCleanup(StandardField.URL, new LayoutFormatterBasedFormatter(new RemoveLatexCommandsFormatter())) )); cleanups.applySaveActions(entry); diff --git a/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java b/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java index 33cb3f6ba7fd..deb32598663f 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java @@ -1,10 +1,13 @@ package org.jabref.logic.importer.fileformat; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PushbackReader; import java.io.Reader; import java.io.StringWriter; +import java.nio.file.Path; +import java.util.Base64; import java.util.Collection; import java.util.Deque; import java.util.HashMap; @@ -16,12 +19,17 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import java.util.regex.Pattern; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + import org.jabref.logic.bibtex.FieldContentFormatter; import org.jabref.logic.bibtex.FieldWriter; import org.jabref.logic.exporter.BibtexDatabaseWriter; import org.jabref.logic.exporter.SaveConfiguration; +import org.jabref.logic.groups.DefaultGroupsFactory; import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.logic.importer.Importer; import org.jabref.logic.importer.ParseException; @@ -35,17 +43,31 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryType; import org.jabref.model.entry.BibtexString; +import org.jabref.model.entry.LinkedFile; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldFactory; import org.jabref.model.entry.field.FieldProperty; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.EntryTypeFactory; +import org.jabref.model.groups.ExplicitGroup; +import org.jabref.model.groups.GroupHierarchyType; +import org.jabref.model.groups.GroupTreeNode; import org.jabref.model.metadata.MetaData; import org.jabref.model.util.DummyFileUpdateMonitor; import org.jabref.model.util.FileUpdateMonitor; +import com.dd.plist.BinaryPropertyListParser; +import com.dd.plist.NSDictionary; +import com.dd.plist.NSString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import static org.jabref.logic.util.MetadataSerializationConfiguration.GROUP_QUOTE_CHAR; +import static org.jabref.logic.util.MetadataSerializationConfiguration.GROUP_TYPE_SUFFIX; /** * Class for importing BibTeX-files. @@ -68,8 +90,8 @@ */ public class BibtexParser implements Parser { private static final Logger LOGGER = LoggerFactory.getLogger(BibtexParser.class); - private static final Integer LOOKAHEAD = 1024; + private static final String BIB_DESK_ROOT_GROUP_NAME = "BibDeskGroups"; private final FieldContentFormatter fieldContentFormatter; private final Deque pureTextFromFile = new LinkedList<>(); private final ImportFormatPreferences importFormatPreferences; @@ -80,11 +102,16 @@ public class BibtexParser implements Parser { private int line = 1; private ParserResult parserResult; private final MetaDataParser metaDataParser; + private final Map parsedBibdeskGroups; + + private GroupTreeNode bibDeskGroupTreeNode; + private final DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance(); public BibtexParser(ImportFormatPreferences importFormatPreferences, FileUpdateMonitor fileMonitor) { this.importFormatPreferences = Objects.requireNonNull(importFormatPreferences); this.fieldContentFormatter = new FieldContentFormatter(importFormatPreferences.fieldPreferences()); this.metaDataParser = new MetaDataParser(fileMonitor); + this.parsedBibdeskGroups = new HashMap<>(); } public BibtexParser(ImportFormatPreferences importFormatPreferences) { @@ -213,28 +240,51 @@ private ParserResult parseFileContent() throws IOException { // Try to read the entry type String entryType = parseTextToken().toLowerCase(Locale.ROOT).trim(); - if ("preamble".equals(entryType)) { - database.setPreamble(parsePreamble()); - // Consume a new line which separates the preamble from the next part (if the file was written with JabRef) - skipOneNewline(); - // the preamble is saved verbatim anyway, so the text read so far can be dropped - dumpTextReadSoFarToString(); - } else if ("string".equals(entryType)) { - parseBibtexString(); - } else if ("comment".equals(entryType)) { - parseJabRefComment(meta); - } else { - // Not a comment, preamble, or string. Thus, it is an entry - parseAndAddEntry(entryType); + switch (entryType) { + case "preamble" -> { + database.setPreamble(parsePreamble()); + // Consume a new line which separates the preamble from the next part (if the file was written with JabRef) + skipOneNewline(); + // the preamble is saved verbatim anyway, so the text read so far can be dropped + dumpTextReadSoFarToString(); + } + case "string" -> + parseBibtexString(); + case "comment" -> + parseJabRefComment(meta); + default -> + // Not a comment, preamble, or string. Thus, it is an entry + parseAndAddEntry(entryType); } skipWhitespace(); } + addBibDeskGroupEntriesToJabRefGroups(); + try { - parserResult.setMetaData(metaDataParser.parse( + MetaData metaData = metaDataParser.parse( meta, - importFormatPreferences.bibEntryPreferences().getKeywordSeparator())); + importFormatPreferences.bibEntryPreferences().getKeywordSeparator()); + if (bibDeskGroupTreeNode != null) { + metaData.getGroups().ifPresentOrElse(existingGroupTree -> { + var existingGroups = meta.get(MetaData.GROUPSTREE); + // We only have one Group BibDeskGroup with n children + // instead of iterating through the whole group structure every time we just search in the metadata for the group name + var groupsToAdd = bibDeskGroupTreeNode.getChildren() + .stream(). + filter(Predicate.not(groupTreeNode -> existingGroups.contains(GROUP_TYPE_SUFFIX + groupTreeNode.getName() + GROUP_QUOTE_CHAR))); + groupsToAdd.forEach(existingGroupTree::addChild); + }, + // metadata does not contain any groups, so we need to create an AllEntriesGroup and add the other groups as children + () -> { + GroupTreeNode rootNode = new GroupTreeNode(DefaultGroupsFactory.getAllEntriesGroup()); + bibDeskGroupTreeNode.moveTo(rootNode); + metaData.setGroups(rootNode); + } + ); + } + parserResult.setMetaData(metaData); } catch (ParseException exception) { parserResult.addException(exception); } @@ -286,7 +336,6 @@ private void parseAndAddEntry(String type) { } catch (IOException ex) { // This makes the parser more robust: // If an exception is thrown when parsing an entry, drop the entry and try to resume parsing. - LOGGER.warn("Could not parse entry", ex); parserResult.addWarning(Localization.lang("Error occurred when parsing entry") + ": '" + ex.getMessage() + "'. " + "\n\n" + Localization.lang("JabRef skipped the entry.")); @@ -334,6 +383,73 @@ private void parseJabRefComment(Map meta) { // custom entry types are always re-written by JabRef and not stored in the file dumpTextReadSoFarToString(); + } else if (comment.startsWith(MetaData.BIBDESK_STATIC_FLAG)) { + try { + parseBibDeskComment(comment, meta); + } catch (ParseException ex) { + parserResult.addException(ex); + } + } + } + + /** + * Adds BibDesk group entries to the JabRef database + */ + private void addBibDeskGroupEntriesToJabRefGroups() { + for (String groupName : parsedBibdeskGroups.keySet()) { + String[] citationKeys = parsedBibdeskGroups.get(groupName).split(","); + for (String citation : citationKeys) { + Optional bibEntry = database.getEntryByCitationKey(citation); + Optional groupValue = bibEntry.flatMap(entry -> entry.getField(StandardField.GROUPS)); + if (groupValue.isEmpty()) { // if the citation does not belong to a group already + bibEntry.flatMap(entry -> entry.setField(StandardField.GROUPS, groupName)); + } else if (!groupValue.get().contains(groupName)) { + // if the citation does belong to a group already and is not yet assigned to the same group, we concatenate + String concatGroup = groupValue.get() + "," + groupName; + bibEntry.flatMap(entryByCitationKey -> entryByCitationKey.setField(StandardField.GROUPS, concatGroup)); + } + } + } + } + + /** + * Parses comment types found in BibDesk, to migrate BibDesk Static Groups to JabRef. + */ + private void parseBibDeskComment(String comment, Map meta) throws ParseException { + String xml = comment.substring(MetaData.BIBDESK_STATIC_FLAG.length() + 1, comment.length() - 1); + try { + // Build a document to handle the xml tags + Document doc = builder.newDocumentBuilder().parse(new ByteArrayInputStream(xml.getBytes())); + doc.getDocumentElement().normalize(); + + NodeList dictList = doc.getElementsByTagName("dict"); + meta.putIfAbsent(MetaData.DATABASE_TYPE, "bibtex;"); + bibDeskGroupTreeNode = GroupTreeNode.fromGroup(new ExplicitGroup(BIB_DESK_ROOT_GROUP_NAME, GroupHierarchyType.INDEPENDENT, importFormatPreferences.bibEntryPreferences().getKeywordSeparator())); + + // Since each static group has their own dict element, we iterate through them + for (int i = 0; i < dictList.getLength(); i++) { + Element dictElement = (Element) dictList.item(i); + NodeList keyList = dictElement.getElementsByTagName("key"); + NodeList stringList = dictElement.getElementsByTagName("string"); + + String groupName = null; + String citationKeys = null; + + // Retrieves group name and group entries and adds these to the metadata + for (int j = 0; j < keyList.getLength(); j++) { + if (keyList.item(j).getTextContent().matches("group name")) { + groupName = stringList.item(j).getTextContent(); + var staticGroup = new ExplicitGroup(groupName, GroupHierarchyType.INDEPENDENT, importFormatPreferences.bibEntryPreferences().getKeywordSeparator()); + bibDeskGroupTreeNode.addSubgroup(staticGroup); + } else if (keyList.item(j).getTextContent().matches("keys")) { + citationKeys = stringList.item(j).getTextContent(); // adds group entries + } + } + // Adds the group name and citation keys to the field so all the entries can be added in the groups once parsed + parsedBibdeskGroups.putIfAbsent(groupName, citationKeys); + } + } catch (ParserConfigurationException | IOException | SAXException e) { + throw new ParseException(e); } } @@ -622,13 +738,29 @@ private void parseField(BibEntry entry) throws IOException { // it inconvenient // for users if JabRef did not accept it. if (field.getProperties().contains(FieldProperty.PERSON_NAMES)) { - entry.setField(field, entry.getField(field).get() + " and " + content); + entry.setField(field, entry.getField(field).orElse("") + " and " + content); } else if (StandardField.KEYWORDS == field) { // multiple keywords fields should be combined to one entry.addKeyword(content, importFormatPreferences.bibEntryPreferences().getKeywordSeparator()); } } else { - entry.setField(field, content); + // If a BibDesk File Field is encountered + if (field.getName().length() > 10 && field.getName().startsWith("bdsk-file-")) { + byte[] decodedBytes = Base64.getDecoder().decode(content); + try { + // Parse the base64 encoded binary plist to get the relative (to the .bib file) path + NSDictionary plist = (NSDictionary) BinaryPropertyListParser.parse(decodedBytes); + NSString relativePath = (NSString) plist.objectForKey("relativePath"); + Path path = Path.of(relativePath.getContent()); + + LinkedFile file = new LinkedFile("", path, ""); + entry.addFile(file); + } catch (Exception e) { + throw new IOException(); + } + } else { + entry.setField(field, content); + } } } } @@ -778,7 +910,6 @@ private String fixKey() throws IOException { /** * returns a new StringBuilder which corresponds to toRemove without whitespaces - * */ private StringBuilder removeWhitespaces(StringBuilder toRemove) { StringBuilder result = new StringBuilder(); @@ -923,20 +1054,16 @@ private StringBuilder parseBracketedFieldContent() throws IOException { // Check for "\},\n" - Example context: ` path = {c:\temp\},\n` // On Windows, it could be "\},\r\n", thus we rely in OS.NEWLINE.charAt(0) (which returns '\r' or '\n'). // In all cases, we should check for '\n' as the file could be encoded with Linux line endings on Windows. - if ((nextTwoCharacters[0] == ',') && ((nextTwoCharacters[1] == OS.NEWLINE.charAt(0)) || (nextTwoCharacters[1] == '\n'))) { - // We hit '\}\r` or `\}\n` - // Heuristics: Unwanted escaping of } - // - // Two consequences: - // - // 1. Keep `\` as read - // This is already done - // - // 2. Treat `}` as closing bracket - isClosingBracket = true; - } else { - isClosingBracket = false; - } + // We hit '\}\r` or `\}\n` + // Heuristics: Unwanted escaping of } + // + // Two consequences: + // + // 1. Keep `\` as read + // This is already done + // + // 2. Treat `}` as closing bracket + isClosingBracket = (nextTwoCharacters[0] == ',') && ((nextTwoCharacters[1] == OS.NEWLINE.charAt(0)) || (nextTwoCharacters[1] == '\n')); } else { isClosingBracket = true; } diff --git a/src/main/java/org/jabref/logic/importer/util/GrobidService.java b/src/main/java/org/jabref/logic/importer/util/GrobidService.java index 514e43d05130..f3449f68be0a 100644 --- a/src/main/java/org/jabref/logic/importer/util/GrobidService.java +++ b/src/main/java/org/jabref/logic/importer/util/GrobidService.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -84,6 +85,34 @@ public List processPDF(Path filePath, ImportFormatPreferences importFo String httpResponse = response.body(); + return getBibEntries(importFormatPreferences, httpResponse); + } + + public List processReferences(List pathList, ImportFormatPreferences importFormatPreferences) throws IOException, ParseException { + List entries = new ArrayList<>(); + for (Path filePath: pathList) { + entries.addAll(processReferences(filePath, importFormatPreferences)); + } + + return entries; + } + + public List processReferences(Path filePath, ImportFormatPreferences importFormatPreferences) throws IOException, ParseException { + Connection.Response response = Jsoup.connect(grobidPreferences.getGrobidURL() + "/api/processReferences") + .header("Accept", MediaTypes.APPLICATION_BIBTEX) + .data("input", filePath.toString(), Files.newInputStream(filePath)) + .data("consolidateCitations", String.valueOf(ConsolidateCitations.WITH_METADATA)) + .method(Connection.Method.POST) + .ignoreContentType(true) + .timeout(20000) + .execute(); + + String httpResponse = response.body(); + + return getBibEntries(importFormatPreferences, httpResponse); + } + + private static List getBibEntries(ImportFormatPreferences importFormatPreferences, String httpResponse) throws IOException, ParseException { if (httpResponse == null || "@misc{-1,\n author = {}\n}\n".equals(httpResponse)) { // This filters empty BibTeX entries throw new IOException("The GROBID server response does not contain anything."); } diff --git a/src/main/java/org/jabref/logic/util/MetadataSerializationConfiguration.java b/src/main/java/org/jabref/logic/util/MetadataSerializationConfiguration.java index 8ca88821f120..db99eb45882a 100644 --- a/src/main/java/org/jabref/logic/util/MetadataSerializationConfiguration.java +++ b/src/main/java/org/jabref/logic/util/MetadataSerializationConfiguration.java @@ -18,6 +18,11 @@ public class MetadataSerializationConfiguration { */ public static final char GROUP_QUOTE_CHAR = '\\'; + /** + * Group Type suffix (part of the GroupType) + */ + public static final String GROUP_TYPE_SUFFIX = ":"; + /** * For separating units (e.g. name and hierarchic context) in the string representation */ diff --git a/src/main/java/org/jabref/migrations/CustomEntryTypePreferenceMigration.java b/src/main/java/org/jabref/migrations/CustomEntryTypePreferenceMigration.java index e2973ea207a6..59db3eb61dda 100644 --- a/src/main/java/org/jabref/migrations/CustomEntryTypePreferenceMigration.java +++ b/src/main/java/org/jabref/migrations/CustomEntryTypePreferenceMigration.java @@ -1,6 +1,7 @@ package org.jabref.migrations; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -57,18 +58,18 @@ private static Optional getBibEntryType(int number, JabRefPreferen BibEntryTypeBuilder entryTypeBuilder = new BibEntryTypeBuilder() .withType(EntryTypeFactory.parse(name)) - .withRequiredFields(req.stream().map(FieldFactory::parseOrFields).collect(Collectors.toList())); + .withRequiredFields(req.stream().map(FieldFactory::parseOrFields).collect(Collectors.toCollection(LinkedHashSet::new))); if (priOpt.isEmpty()) { entryTypeBuilder = entryTypeBuilder - .withImportantFields(opt.stream().map(FieldFactory::parseField).collect(Collectors.toSet())); + .withImportantFields(opt.stream().map(FieldFactory::parseField).collect(Collectors.toCollection(LinkedHashSet::new))); return Optional.of(entryTypeBuilder.build()); } else { List secondary = new ArrayList<>(opt); secondary.removeAll(priOpt); entryTypeBuilder = entryTypeBuilder - .withImportantFields(priOpt.stream().map(FieldFactory::parseField).collect(Collectors.toSet())) - .withDetailFields(secondary.stream().map(FieldFactory::parseField).collect(Collectors.toSet())); + .withImportantFields(priOpt.stream().map(FieldFactory::parseField).collect(Collectors.toCollection(LinkedHashSet::new))) + .withDetailFields(secondary.stream().map(FieldFactory::parseField).collect(Collectors.toCollection(LinkedHashSet::new))); return Optional.of(entryTypeBuilder.build()); } } diff --git a/src/main/java/org/jabref/migrations/PreferencesMigrations.java b/src/main/java/org/jabref/migrations/PreferencesMigrations.java index e9a673f4b3b9..e9f30abc0506 100644 --- a/src/main/java/org/jabref/migrations/PreferencesMigrations.java +++ b/src/main/java/org/jabref/migrations/PreferencesMigrations.java @@ -16,6 +16,8 @@ import javafx.scene.control.TableColumn; +import org.jabref.gui.entryeditor.CommentsTab; +import org.jabref.gui.entryeditor.EntryEditor; import org.jabref.gui.maintable.ColumnPreferences; import org.jabref.gui.maintable.MainTableColumnModel; import org.jabref.logic.citationkeypattern.GlobalCitationKeyPattern; @@ -57,13 +59,14 @@ public static void runMigrations(JabRefPreferences preferences, BibEntryTypesMan addCrossRefRelatedFieldsForAutoComplete(preferences); upgradePreviewStyle(preferences); // changeColumnVariableNamesFor51 needs to be run before upgradeColumnPre50Preferences to ensure - // backwardcompatibility, as it copies the old values to new variable names and keeps th old sored with the old + // backward compatibility, as it copies the old values to new variable names and keeps th old sored with the old // variable names. However, the variables from 5.0 need to be copied to the new variable name too. changeColumnVariableNamesFor51(preferences); upgradeColumnPreferences(preferences); restoreVariablesForBackwardCompatibility(preferences); upgradeCleanups(preferences); moveApiKeysToKeyring(preferences); + removeCommentsFromCustomEditorTabs(preferences); } /** @@ -545,4 +548,12 @@ static void moveApiKeysToKeyring(JabRefPreferences preferences) { } } } + + /** + * The tab "Comments" is hard coded using {@link CommentsTab} since v5.10 (and thus hard-wired in {@link EntryEditor#createTabs()}. + * Thus, the configuration ih the preferences is obsolete + */ + static void removeCommentsFromCustomEditorTabs(JabRefPreferences preferences) { + preferences.getEntryEditorPreferences().getEntryEditorTabs().remove("Comments"); + } } diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java index a8c940fb3bbb..56fc9302796b 100644 --- a/src/main/java/org/jabref/model/entry/BibEntry.java +++ b/src/main/java/org/jabref/model/entry/BibEntry.java @@ -572,7 +572,7 @@ public Optional getFieldOrAlias(Field field) { } /** - * Return the LaTeX-free contents of the given field or its alias an an Optional + * Return the LaTeX-free contents of the given field or its alias an Optional *

* For details see also {@link #getFieldOrAlias(Field)} * diff --git a/src/main/java/org/jabref/model/entry/BibEntryType.java b/src/main/java/org/jabref/model/entry/BibEntryType.java index b58a9e890981..164029fa7999 100644 --- a/src/main/java/org/jabref/model/entry/BibEntryType.java +++ b/src/main/java/org/jabref/model/entry/BibEntryType.java @@ -2,6 +2,7 @@ import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Objects; import java.util.SequencedSet; @@ -21,8 +22,8 @@ public class BibEntryType implements Comparable { private final EntryType type; - private final LinkedHashSet requiredFields; - private final LinkedHashSet fields; + private final SequencedSet allFields; + private final SequencedSet requiredFields; /** * Provides an enriched EntryType with information about defined standards as mandatory fields etc. @@ -30,12 +31,12 @@ public class BibEntryType implements Comparable { * A builder is available at {@link BibEntryTypeBuilder} * * @param type The EntryType this BibEntryType is wrapped around. - * @param fields A BibFields list of all fields, including the required fields + * @param allFields A BibFields list of all fields, including the required fields * @param requiredFields A OrFields list of just the required fields */ - public BibEntryType(EntryType type, Collection fields, Collection requiredFields) { + public BibEntryType(EntryType type, Collection allFields, Collection requiredFields) { this.type = Objects.requireNonNull(type); - this.fields = new LinkedHashSet<>(fields); + this.allFields = new LinkedHashSet<>(allFields); this.requiredFields = new LinkedHashSet<>(requiredFields); } @@ -43,7 +44,7 @@ public EntryType getType() { return type; } - public Set getOptionalFields() { + public SequencedSet getOptionalFields() { return getAllBibFields().stream() .filter(field -> !isRequired(field.field())) .collect(Collectors.toCollection(LinkedHashSet::new)); @@ -61,40 +62,40 @@ public boolean isRequired(Field field) { * * @return a Set of required field name Strings */ - public Set getRequiredFields() { - return Collections.unmodifiableSet(requiredFields); + public SequencedSet getRequiredFields() { + return Collections.unmodifiableSequencedSet(requiredFields); } /** * Returns all defined fields. */ - public Set getAllBibFields() { - return Collections.unmodifiableSet(fields); + public SequencedSet getAllBibFields() { + return Collections.unmodifiableSequencedSet(allFields); } public Set getAllFields() { - return fields.stream().map(BibField::field).collect(Collectors.toCollection(LinkedHashSet::new)); + return allFields.stream().map(BibField::field).collect(Collectors.toCollection(LinkedHashSet::new)); } - public SequencedSet getPrimaryOptionalFields() { + public SequencedSet getImportantOptionalFields() { return getOptionalFields().stream() .filter(field -> field.priority() == FieldPriority.IMPORTANT) .map(BibField::field) .collect(Collectors.toCollection(LinkedHashSet::new)); } - public SequencedSet getSecondaryOptionalFields() { + public SequencedSet getDetailOptionalFields() { return getOptionalFields().stream() .filter(field -> field.priority() == FieldPriority.DETAIL) .map(BibField::field) .collect(Collectors.toCollection(LinkedHashSet::new)); } - public SequencedSet getDeprecatedFields(BibDatabaseMode mode) { + public Set getDeprecatedFields(BibDatabaseMode mode) { if (mode == BibDatabaseMode.BIBTEX) { - return new LinkedHashSet<>(); + return Set.of(); } - SequencedSet deprecatedFields = new LinkedHashSet<>(EntryConverter.FIELD_ALIASES_BIBTEX_TO_BIBLATEX.keySet()); + Set deprecatedFields = new HashSet<>(EntryConverter.FIELD_ALIASES_BIBTEX_TO_BIBLATEX.keySet()); // Only the optional fields which are mapped to another BibLaTeX name should be shown as "deprecated" deprecatedFields.retainAll(getOptionalFieldsAndAliases()); @@ -107,8 +108,8 @@ public SequencedSet getDeprecatedFields(BibDatabaseMode mode) { return deprecatedFields; } - public SequencedSet getSecondaryOptionalNotDeprecatedFields(BibDatabaseMode mode) { - SequencedSet optionalFieldsNotPrimaryOrDeprecated = new LinkedHashSet<>(getSecondaryOptionalFields()); + public SequencedSet getDetailOptionalNotDeprecatedFields(BibDatabaseMode mode) { + SequencedSet optionalFieldsNotPrimaryOrDeprecated = new LinkedHashSet<>(getDetailOptionalFields()); optionalFieldsNotPrimaryOrDeprecated.removeAll(getDeprecatedFields(mode)); return optionalFieldsNotPrimaryOrDeprecated; } @@ -138,12 +139,12 @@ public boolean equals(Object o) { BibEntryType that = (BibEntryType) o; return type.equals(that.type) && Objects.equals(requiredFields, that.requiredFields) && - Objects.equals(fields, that.fields); + Objects.equals(allFields, that.allFields); } @Override public int hashCode() { - return Objects.hash(type, requiredFields, fields); + return Objects.hash(type, requiredFields, allFields); } /** @@ -158,7 +159,7 @@ public int hashCode() { public String toString() { return "BibEntryType{" + "type=" + type + - ", allFields=" + fields + + ", allFields=" + allFields + ", requiredFields=" + requiredFields + '}'; } diff --git a/src/main/java/org/jabref/model/entry/BibEntryTypeBuilder.java b/src/main/java/org/jabref/model/entry/BibEntryTypeBuilder.java index b8613157ac6e..b1ef56433487 100644 --- a/src/main/java/org/jabref/model/entry/BibEntryTypeBuilder.java +++ b/src/main/java/org/jabref/model/entry/BibEntryTypeBuilder.java @@ -2,8 +2,11 @@ import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.SequencedCollection; +import java.util.SequencedSet; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -20,31 +23,40 @@ public class BibEntryTypeBuilder { private EntryType type = StandardEntryType.Misc; - private Set fields = new LinkedHashSet<>(); - private Set requiredFields = new LinkedHashSet<>(); + + private SequencedSet requiredFields = new LinkedHashSet<>(); + private SequencedSet optionalFields = new LinkedHashSet<>(); + + private Set seenFields = new HashSet<>(); public BibEntryTypeBuilder withType(EntryType type) { this.type = type; return this; } - public BibEntryTypeBuilder withImportantFields(Set newFields) { - return withImportantFields(newFields.stream().map(BibField::field).collect(Collectors.toCollection(LinkedHashSet::new))); - } - - public BibEntryTypeBuilder withImportantFields(Collection newFields) { - this.fields = Streams.concat(fields.stream(), newFields.stream().map(field -> new BibField(field, FieldPriority.IMPORTANT))) - .collect(Collectors.toCollection(LinkedHashSet::new)); + public BibEntryTypeBuilder withImportantFields(SequencedSet newFields) { + List containedFields = containedInSeenFields(newFields); + if (!containedFields.isEmpty()) { + throw new IllegalArgumentException("Fields " + containedFields + " already added"); + } + this.seenFields.addAll(newFields); + this.optionalFields = Streams.concat(optionalFields.stream(), newFields.stream().map(field -> new BibField(field, FieldPriority.IMPORTANT))) + .collect(Collectors.toCollection(LinkedHashSet::new)); return this; } public BibEntryTypeBuilder withImportantFields(Field... newFields) { - return withImportantFields(Arrays.asList(newFields)); + return withImportantFields(Arrays.stream(newFields).collect(Collectors.toCollection(LinkedHashSet::new))); } - public BibEntryTypeBuilder withDetailFields(Collection newFields) { - this.fields = Streams.concat(fields.stream(), newFields.stream().map(field -> new BibField(field, FieldPriority.DETAIL))) - .collect(Collectors.toCollection(LinkedHashSet::new)); + public BibEntryTypeBuilder withDetailFields(SequencedCollection newFields) { + List containedFields = containedInSeenFields(newFields); + if (!containedFields.isEmpty()) { + throw new IllegalArgumentException("Fields " + containedFields + " already added"); + } + this.seenFields.addAll(newFields); + this.optionalFields = Streams.concat(optionalFields.stream(), newFields.stream().map(field -> new BibField(field, FieldPriority.DETAIL))) + .collect(Collectors.toCollection(LinkedHashSet::new)); return this; } @@ -52,24 +64,39 @@ public BibEntryTypeBuilder withDetailFields(Field... fields) { return withDetailFields(Arrays.asList(fields)); } - public BibEntryTypeBuilder withRequiredFields(Set requiredFields) { - this.requiredFields = requiredFields; + public BibEntryTypeBuilder withRequiredFields(SequencedSet requiredFields) { + return addRequiredFields(requiredFields); + } + + public BibEntryTypeBuilder addRequiredFields(SequencedSet requiredFields) { + Set fieldsToAdd = requiredFields.stream().map(OrFields::getFields).flatMap(Set::stream).collect(Collectors.toSet()); + List containedFields = containedInSeenFields(fieldsToAdd); + if (!containedFields.isEmpty()) { + throw new IllegalArgumentException("Fields " + containedFields + " already added"); + } + this.seenFields.addAll(fieldsToAdd); + this.requiredFields.addAll(requiredFields); return this; } + public BibEntryTypeBuilder addRequiredFields(OrFields... requiredFields) { + return addRequiredFields(Arrays.asList(requiredFields).stream().collect(Collectors.toCollection(LinkedHashSet::new))); + } + + public BibEntryTypeBuilder addRequiredFields(Field... requiredFields) { + return addRequiredFields(Arrays.stream(requiredFields).map(OrFields::new).collect(Collectors.toCollection(LinkedHashSet::new))); + } + public BibEntryTypeBuilder withRequiredFields(Field... requiredFields) { - this.requiredFields = Arrays.stream(requiredFields).map(OrFields::new).collect(Collectors.toCollection(LinkedHashSet::new)); - return this; + return addRequiredFields(requiredFields); } public BibEntryTypeBuilder withRequiredFields(OrFields first, Field... requiredFields) { - this.requiredFields = Stream.concat(Stream.of(first), Arrays.stream(requiredFields).map(OrFields::new)).collect(Collectors.toCollection(LinkedHashSet::new)); - return this; + return addRequiredFields(Stream.concat(Stream.of(first), Arrays.stream(requiredFields).map(OrFields::new)).collect(Collectors.toCollection(LinkedHashSet::new))); } - public BibEntryTypeBuilder withRequiredFields(List first, Field... requiredFields) { - this.requiredFields = Stream.concat(first.stream(), Arrays.stream(requiredFields).map(OrFields::new)).collect(Collectors.toCollection(LinkedHashSet::new)); - return this; + public BibEntryTypeBuilder withRequiredFields(SequencedSet first, Field... requiredFields) { + return addRequiredFields(Stream.concat(first.stream(), Arrays.stream(requiredFields).map(OrFields::new)).collect(Collectors.toCollection(LinkedHashSet::new))); } public BibEntryType build() { @@ -78,7 +105,11 @@ public BibEntryType build() { .map(OrFields::getFields) .flatMap(Set::stream) .map(field -> new BibField(field, FieldPriority.IMPORTANT)); - Set allFields = Stream.concat(fields.stream(), requiredAsImportant).collect(Collectors.toCollection(LinkedHashSet::new)); + SequencedSet allFields = Stream.concat(optionalFields.stream(), requiredAsImportant).collect(Collectors.toCollection(LinkedHashSet::new)); return new BibEntryType(type, allFields, requiredFields); } + + private List containedInSeenFields(Collection fields) { + return fields.stream().filter(seenFields::contains).toList(); + } } diff --git a/src/main/java/org/jabref/model/entry/BibEntryTypesManager.java b/src/main/java/org/jabref/model/entry/BibEntryTypesManager.java index eb25ad23972c..71a077acf016 100644 --- a/src/main/java/org/jabref/model/entry/BibEntryTypesManager.java +++ b/src/main/java/org/jabref/model/entry/BibEntryTypesManager.java @@ -18,7 +18,12 @@ import org.jabref.model.entry.types.EntryTypeFactory; import org.jabref.model.entry.types.IEEETranEntryTypeDefinitions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class BibEntryTypesManager { + private static final Logger LOGGER = LoggerFactory.getLogger(BibEntryTypesManager.class); + private final InternalEntryTypes BIBTEX_ENTRYTYPES = new InternalEntryTypes( Stream.concat(BibtexEntryTypeDefinitions.ALL.stream(), IEEETranEntryTypeDefinitions.ALL.stream()) .collect(Collectors.toList())); @@ -132,6 +137,7 @@ private Optional enrich(EntryType type) { .filter(typeEquals(type)) .findFirst(); if (enrichedType.isPresent()) { + LOGGER.debug("Using customized entry type for {}", type.getName()); return enrichedType; } else { return standardTypes.stream() diff --git a/src/main/java/org/jabref/model/entry/EntryConverter.java b/src/main/java/org/jabref/model/entry/EntryConverter.java index 0a6f6621dd9e..162afb219d1d 100644 --- a/src/main/java/org/jabref/model/entry/EntryConverter.java +++ b/src/main/java/org/jabref/model/entry/EntryConverter.java @@ -8,7 +8,7 @@ import org.jabref.model.entry.field.StandardField; /** - * Converts Entry models from BibTex to biblatex and back. + * Converts Entry models from BibTeX to biblatex and back. */ public class EntryConverter { diff --git a/src/main/java/org/jabref/model/entry/field/FieldFactory.java b/src/main/java/org/jabref/model/entry/field/FieldFactory.java index 645a1295221c..5c0a9036d2b4 100644 --- a/src/main/java/org/jabref/model/entry/field/FieldFactory.java +++ b/src/main/java/org/jabref/model/entry/field/FieldFactory.java @@ -8,6 +8,7 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.SequencedSet; import java.util.Set; import java.util.TreeSet; import java.util.function.Predicate; @@ -64,14 +65,14 @@ public static OrFields parseOrFields(String fieldNames) { return new OrFields(fields); } - public static Set parseOrFieldsList(String fieldNames) { + public static SequencedSet parseOrFieldsList(String fieldNames) { return Arrays.stream(fieldNames.split(FieldFactory.DELIMITER)) .filter(StringUtil::isNotBlank) .map(FieldFactory::parseOrFields) .collect(Collectors.toCollection(LinkedHashSet::new)); } - public static Set parseFieldList(String fieldNames) { + public static SequencedSet parseFieldList(String fieldNames) { return Arrays.stream(fieldNames.split(FieldFactory.DELIMITER)) .filter(StringUtil::isNotBlank) .map(FieldFactory::parseField) diff --git a/src/main/java/org/jabref/model/entry/field/FieldPriority.java b/src/main/java/org/jabref/model/entry/field/FieldPriority.java index 199151eeaf0c..7c44b53af285 100644 --- a/src/main/java/org/jabref/model/entry/field/FieldPriority.java +++ b/src/main/java/org/jabref/model/entry/field/FieldPriority.java @@ -3,10 +3,10 @@ import java.util.Locale; /** - * Determines whether the field is in the Optional1 or Optional2 tab + * Determines whether the field is in the {@link org.jabref.gui.entryeditor.ImportantOptionalFieldsTab} or {@link org.jabref.gui.entryeditor.DetailOptionalFieldsTab} tab * - * See {@link org.jabref.model.entry.BibEntryType#getPrimaryOptionalFields()} - * and {@link org.jabref.model.entry.BibEntryType#getSecondaryOptionalFields()}. + * See {@link org.jabref.model.entry.BibEntryType#getImportantOptionalFields()} + * and {@link org.jabref.model.entry.BibEntryType#getDetailOptionalFields()}. */ public enum FieldPriority { IMPORTANT, diff --git a/src/main/java/org/jabref/model/entry/field/OrFields.java b/src/main/java/org/jabref/model/entry/field/OrFields.java index 253a7257cfba..a8de739eb7dc 100644 --- a/src/main/java/org/jabref/model/entry/field/OrFields.java +++ b/src/main/java/org/jabref/model/entry/field/OrFields.java @@ -4,12 +4,23 @@ import java.util.Collection; import java.util.LinkedHashSet; import java.util.Objects; -import java.util.Set; +import java.util.SequencedSet; import java.util.StringJoiner; +/** + * Represents a choice between two (or more) fields or any combination of them. + *

+ * The idea of OrFields originates from BibLaTeX, where the manual lists following + *
+ * Required fields: author, title, journaltitle, year/date + *
+ * The class OrFields is used to model "year/date" in this case. + *

+ * Example is that a BibEntry requires either an author or an editor, but both can be present. + */ public class OrFields implements Comparable { - private LinkedHashSet fields = new LinkedHashSet<>(); + private SequencedSet fields = new LinkedHashSet<>(); public OrFields(Field field) { fields.add(field); @@ -35,7 +46,7 @@ public Field getPrimary() { return fields.getFirst(); } - public Set getFields() { + public SequencedSet getFields() { return this.fields; } 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 87db17e110d0..40c79b8b31d7 100644 --- a/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java @@ -10,322 +10,297 @@ /** * This class defines entry types for biblatex support. - * - * @see biblatex documentation + * It is based on the biblatex documentation + *

+ * The definitions for BibTeX are done at {@link BibtexEntryTypeDefinitions} */ public class BiblatexEntryTypeDefinitions { private static final BibEntryType ARTICLE = new BibEntryTypeBuilder() .withType(StandardEntryType.Article) + .withRequiredFields( + StandardField.AUTHOR, StandardField.TITLE, StandardField.JOURNALTITLE, StandardField.DATE) .withImportantFields( StandardField.SUBTITLE, StandardField.EDITOR, StandardField.SERIES, StandardField.VOLUME, StandardField.NUMBER, StandardField.EID, StandardField.ISSUE, StandardField.PAGES, StandardField.NOTE, StandardField.ISSN, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) .withDetailFields( - StandardField.TRANSLATOR, StandardField.ANNOTATOR, StandardField.COMMENTATOR, StandardField.SUBTITLE, - StandardField.TITLEADDON, StandardField.EDITOR, StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, + StandardField.TRANSLATOR, StandardField.ANNOTATOR, StandardField.COMMENTATOR, + StandardField.TITLEADDON, StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, StandardField.JOURNALSUBTITLE, StandardField.ISSUETITLE, StandardField.ISSUESUBTITLE, StandardField.LANGUAGE, - StandardField.ORIGLANGUAGE, StandardField.SERIES, StandardField.VOLUME, StandardField.NUMBER, StandardField.EID, - StandardField.ISSUE, StandardField.PAGES, StandardField.VERSION, StandardField.NOTE, - StandardField.ISSN, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, - StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields( - StandardField.AUTHOR, StandardField.TITLE, StandardField.JOURNALTITLE, StandardField.DATE) + StandardField.ORIGLANGUAGE, StandardField.VERSION, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType BOOK = new BibEntryTypeBuilder() .withType(StandardEntryType.Book) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.DATE) .withImportantFields(StandardField.EDITOR, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.MAINTITLE, StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.VOLUME, StandardField.EDITION, StandardField.PUBLISHER, StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.EDITOR, StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, + .withDetailFields(StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, StandardField.TRANSLATOR, StandardField.ANNOTATOR, StandardField.COMMENTATOR, StandardField.INTRODUCTION, - StandardField.FOREWORD, StandardField.AFTERWORD, StandardField.SUBTITLE, StandardField.TITLEADDON, - StandardField.MAINTITLE, StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.LANGUAGE, - StandardField.ORIGLANGUAGE, StandardField.VOLUME, StandardField.PART, StandardField.EDITION, StandardField.VOLUMES, - StandardField.SERIES, StandardField.NUMBER, StandardField.NOTE, StandardField.PUBLISHER, StandardField.LOCATION, - StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.ADDENDUM, - StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, - StandardField.URL, StandardField.URLDATE) + StandardField.FOREWORD, StandardField.AFTERWORD, + StandardField.LANGUAGE, + StandardField.ORIGLANGUAGE, StandardField.PART, StandardField.VOLUMES, + StandardField.SERIES, StandardField.NUMBER, StandardField.NOTE, StandardField.LOCATION, + StandardField.ADDENDUM, + StandardField.PUBSTATE) .build(); private static final BibEntryType MVBOOK = new BibEntryTypeBuilder() .withType(StandardEntryType.MvBook) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.DATE) .withImportantFields(StandardField.EDITOR, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.EDITION, StandardField.PUBLISHER, StandardField.ISBN, StandardField.PAGETOTAL, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.EDITOR, StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, + .withDetailFields(StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, StandardField.TRANSLATOR, StandardField.ANNOTATOR, StandardField.COMMENTATOR, StandardField.INTRODUCTION, - StandardField.FOREWORD, StandardField.AFTERWORD, StandardField.SUBTITLE, StandardField.TITLEADDON, - StandardField.LANGUAGE, StandardField.ORIGLANGUAGE, StandardField.EDITION, StandardField.VOLUMES, StandardField.SERIES, - StandardField.NUMBER, StandardField.NOTE, StandardField.PUBLISHER, StandardField.LOCATION, StandardField.ISBN, - StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, - StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + StandardField.FOREWORD, StandardField.AFTERWORD, + StandardField.LANGUAGE, StandardField.ORIGLANGUAGE, StandardField.VOLUMES, StandardField.SERIES, + StandardField.NUMBER, StandardField.NOTE, StandardField.LOCATION, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType INBOOK = new BibEntryTypeBuilder() .withType(StandardEntryType.InBook) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.BOOKTITLE, StandardField.DATE) .withImportantFields( StandardField.BOOKAUTHOR, StandardField.EDITOR, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.MAINTITLE, StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.BOOKSUBTITLE, StandardField.BOOKTITLEADDON, StandardField.VOLUME, StandardField.EDITION, StandardField.PUBLISHER, StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.BOOKTITLE, StandardField.DATE) - .withDetailFields(StandardField.BOOKAUTHOR, StandardField.EDITOR, StandardField.EDITORA, StandardField.EDITORB, + .withDetailFields(StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, StandardField.TRANSLATOR, StandardField.ANNOTATOR, StandardField.COMMENTATOR, - StandardField.INTRODUCTION, StandardField.FOREWORD, StandardField.AFTERWORD, StandardField.SUBTITLE, - StandardField.TITLEADDON, StandardField.MAINTITLE, StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, - StandardField.BOOKSUBTITLE, StandardField.BOOKTITLEADDON, StandardField.LANGUAGE, StandardField.ORIGLANGUAGE, - StandardField.VOLUME, StandardField.PART, StandardField.EDITION, StandardField.VOLUMES, StandardField.SERIES, - StandardField.NUMBER, StandardField.NOTE, StandardField.PUBLISHER, StandardField.LOCATION, StandardField.ISBN, - StandardField.CHAPTER, StandardField.PAGES, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, - StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + StandardField.INTRODUCTION, StandardField.FOREWORD, StandardField.AFTERWORD, + StandardField.LANGUAGE, StandardField.ORIGLANGUAGE, + StandardField.PART, StandardField.VOLUMES, StandardField.SERIES, + StandardField.NUMBER, StandardField.NOTE, StandardField.LOCATION, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType BOOKINBOOK = new BibEntryTypeBuilder() .withType(StandardEntryType.BookInBook) - .withImportantFields(INBOOK.getPrimaryOptionalFields()) - .withDetailFields(INBOOK.getSecondaryOptionalFields()) .withRequiredFields(INBOOK.getRequiredFields()) + .withImportantFields(INBOOK.getImportantOptionalFields()) + .withDetailFields(INBOOK.getDetailOptionalFields()) .build(); private static final BibEntryType SUPPBOOK = new BibEntryTypeBuilder() .withType(StandardEntryType.SuppBook) - .withImportantFields(INBOOK.getPrimaryOptionalFields()) - .withDetailFields(INBOOK.getSecondaryOptionalFields()) + .withImportantFields(INBOOK.getImportantOptionalFields()) + .withDetailFields(INBOOK.getDetailOptionalFields()) .withRequiredFields(INBOOK.getRequiredFields()) .build(); private static final BibEntryType BOOKLET = new BibEntryTypeBuilder() .withType(StandardEntryType.Booklet) + .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) .withImportantFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.HOWPUBLISHED, StandardField.CHAPTER, StandardField.PAGES, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.HOWPUBLISHED, - StandardField.TYPE, StandardField.NOTE, StandardField.LOCATION, StandardField.CHAPTER, StandardField.PAGES, - StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, - StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + .withDetailFields(StandardField.LANGUAGE, + StandardField.TYPE, StandardField.NOTE, StandardField.LOCATION, + StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType COLLECTION = new BibEntryTypeBuilder() .withType(StandardEntryType.Collection) + .withRequiredFields(StandardField.EDITOR, StandardField.TITLE, StandardField.DATE) .withImportantFields( StandardField.TRANSLATOR, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.MAINTITLE, StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.VOLUME, StandardField.EDITION, StandardField.PUBLISHER, StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.EDITOR, StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, StandardField.TRANSLATOR, + .withDetailFields(StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, StandardField.ANNOTATOR, StandardField.COMMENTATOR, StandardField.INTRODUCTION, StandardField.FOREWORD, - StandardField.AFTERWORD, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.MAINTITLE, - StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.LANGUAGE, StandardField.ORIGLANGUAGE, - StandardField.VOLUME, StandardField.PART, StandardField.EDITION, StandardField.VOLUMES, StandardField.SERIES, - StandardField.NUMBER, StandardField.NOTE, StandardField.PUBLISHER, StandardField.LOCATION, StandardField.ISBN, - StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE, - StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, - StandardField.URLDATE) + StandardField.AFTERWORD, + StandardField.LANGUAGE, StandardField.ORIGLANGUAGE, + StandardField.PART, StandardField.VOLUMES, StandardField.SERIES, + StandardField.NUMBER, StandardField.NOTE, StandardField.LOCATION, + StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType MVCOLLECTION = new BibEntryTypeBuilder() .withType(StandardEntryType.MvCollection) + .withRequiredFields(StandardField.EDITOR, StandardField.TITLE, StandardField.DATE) .withImportantFields(StandardField.TRANSLATOR, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.EDITION, StandardField.PUBLISHER, StandardField.ISBN, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.EDITOR, StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, StandardField.TRANSLATOR, + .withDetailFields(StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, StandardField.ANNOTATOR, StandardField.COMMENTATOR, StandardField.INTRODUCTION, StandardField.FOREWORD, - StandardField.AFTERWORD, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, - StandardField.ORIGLANGUAGE, StandardField.EDITION, StandardField.VOLUMES, StandardField.SERIES, StandardField.NUMBER, - StandardField.NOTE, StandardField.PUBLISHER, StandardField.LOCATION, StandardField.ISBN, StandardField.PAGETOTAL, - StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, - StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + StandardField.AFTERWORD, StandardField.LANGUAGE, + StandardField.ORIGLANGUAGE, StandardField.VOLUMES, StandardField.SERIES, StandardField.NUMBER, + StandardField.NOTE, StandardField.LOCATION, StandardField.PAGETOTAL, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType INCOLLECTION = new BibEntryTypeBuilder() .withType(StandardEntryType.InCollection) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.BOOKTITLE, StandardField.DATE) .withImportantFields(StandardField.TRANSLATOR, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.MAINTITLE, StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.BOOKSUBTITLE, StandardField.BOOKTITLEADDON, StandardField.VOLUME, StandardField.EDITION, StandardField.PUBLISHER, StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.BOOKTITLE, StandardField.DATE) .withDetailFields(StandardField.EDITOR, StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, - StandardField.TRANSLATOR, StandardField.ANNOTATOR, StandardField.COMMENTATOR, StandardField.INTRODUCTION, - StandardField.FOREWORD, StandardField.AFTERWORD, StandardField.SUBTITLE, StandardField.TITLEADDON, - StandardField.MAINTITLE, StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.BOOKSUBTITLE, - StandardField.BOOKTITLEADDON, StandardField.LANGUAGE, StandardField.ORIGLANGUAGE, StandardField.VOLUME, - StandardField.PART, StandardField.EDITION, StandardField.VOLUMES, StandardField.SERIES, StandardField.NUMBER, - StandardField.NOTE, StandardField.PUBLISHER, StandardField.LOCATION, StandardField.ISBN, StandardField.CHAPTER, - StandardField.PAGES, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, - StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + StandardField.ANNOTATOR, StandardField.COMMENTATOR, StandardField.INTRODUCTION, + StandardField.FOREWORD, StandardField.AFTERWORD, + StandardField.LANGUAGE, StandardField.ORIGLANGUAGE, + StandardField.PART, StandardField.VOLUMES, StandardField.SERIES, StandardField.NUMBER, + StandardField.NOTE, StandardField.LOCATION, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType SUPPCOLLECTION = new BibEntryTypeBuilder() .withType(StandardEntryType.SuppCollection) - .withImportantFields(INCOLLECTION.getPrimaryOptionalFields()) - .withDetailFields(INCOLLECTION.getSecondaryOptionalFields()) .withRequiredFields(INCOLLECTION.getRequiredFields()) + .withImportantFields(INCOLLECTION.getImportantOptionalFields()) + .withDetailFields(INCOLLECTION.getDetailOptionalFields()) .build(); private static final BibEntryType MANUAL = new BibEntryTypeBuilder() .withType(StandardEntryType.Manual) + .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) .withImportantFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.EDITION, StandardField.PUBLISHER, StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.EDITION, + .withDetailFields(StandardField.LANGUAGE, StandardField.TYPE, StandardField.SERIES, StandardField.NUMBER, StandardField.VERSION, StandardField.NOTE, - StandardField.ORGANIZATION, StandardField.PUBLISHER, StandardField.LOCATION, StandardField.ISBN, StandardField.CHAPTER, - StandardField.PAGES, StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, - StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + StandardField.ORGANIZATION, StandardField.LOCATION, + StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType MISC = new BibEntryTypeBuilder() .withType(StandardEntryType.Misc) + .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) .withImportantFields( StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.HOWPUBLISHED, StandardField.LOCATION, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.HOWPUBLISHED, - StandardField.TYPE, StandardField.VERSION, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.LOCATION, - StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, - StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + .withDetailFields(StandardField.LANGUAGE, + StandardField.TYPE, StandardField.VERSION, StandardField.NOTE, StandardField.ORGANIZATION, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType ONLINE = new BibEntryTypeBuilder() .withType(StandardEntryType.Online) + .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE, StandardField.URL) .withImportantFields( StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.URLDATE) - .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE, StandardField.URL) - .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.VERSION, - StandardField.NOTE, StandardField.ORGANIZATION, StandardField.ADDENDUM, StandardField.PUBSTATE, - StandardField.URLDATE) + .withDetailFields(StandardField.LANGUAGE, StandardField.VERSION, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType PATENT = new BibEntryTypeBuilder() .withType(IEEETranEntryType.Patent) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.NUMBER, StandardField.DATE) .withImportantFields(StandardField.HOLDER, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.NUMBER, StandardField.DATE) - .withDetailFields(StandardField.HOLDER, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.TYPE, + .withDetailFields(StandardField.TYPE, StandardField.VERSION, StandardField.LOCATION, StandardField.NOTE, StandardField.ADDENDUM, - StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, - StandardField.URL, StandardField.URLDATE) + StandardField.PUBSTATE) .build(); private static final BibEntryType PERIODICAL = new BibEntryTypeBuilder() .withType(IEEETranEntryType.Periodical) + .withRequiredFields(StandardField.EDITOR, StandardField.TITLE, StandardField.DATE) .withImportantFields( StandardField.SUBTITLE, StandardField.ISSUETITLE, StandardField.ISSUESUBTITLE, StandardField.ISSN, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.EDITOR, StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, StandardField.SUBTITLE, - StandardField.ISSUETITLE, StandardField.ISSUESUBTITLE, StandardField.LANGUAGE, StandardField.SERIES, + .withDetailFields(StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC, + StandardField.LANGUAGE, StandardField.SERIES, StandardField.VOLUME, StandardField.NUMBER, StandardField.ISSUE, StandardField.NOTE, - StandardField.ISSN, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, - StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType SUPPPERIODICAL = new BibEntryTypeBuilder() .withType(StandardEntryType.SuppPeriodical) - .withImportantFields(ARTICLE.getPrimaryOptionalFields()) - .withDetailFields(ARTICLE.getSecondaryOptionalFields()) .withRequiredFields(ARTICLE.getRequiredFields()) + .withImportantFields(ARTICLE.getImportantOptionalFields()) + .withDetailFields(ARTICLE.getDetailOptionalFields()) .build(); private static final BibEntryType PROCEEDINGS = new BibEntryTypeBuilder() .withType(StandardEntryType.Proceedings) + .withRequiredFields(StandardField.TITLE, StandardField.DATE) .withImportantFields( StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.MAINTITLE, StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.EVENTTITLE, StandardField.VOLUME, StandardField.PUBLISHER, StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.EDITOR, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.MAINTITLE, - StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.EVENTTITLE, StandardField.EVENTTITLEADDON, - StandardField.EVENTDATE, StandardField.VENUE, StandardField.LANGUAGE, StandardField.VOLUME, StandardField.PART, + .withDetailFields(StandardField.EDITOR, StandardField.EVENTTITLEADDON, + StandardField.EVENTDATE, StandardField.VENUE, StandardField.LANGUAGE, StandardField.PART, StandardField.VOLUMES, StandardField.SERIES, StandardField.NUMBER, StandardField.NOTE, StandardField.ORGANIZATION, - StandardField.PUBLISHER, StandardField.LOCATION, StandardField.ISBN, - StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE, - StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, - StandardField.URLDATE) + StandardField.LOCATION, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType MVPROCEEDINGS = new BibEntryTypeBuilder() .withType(StandardEntryType.MvProceedings) + .withRequiredFields(StandardField.TITLE, StandardField.DATE) .withImportantFields( StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.MAINTITLE, StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.EVENTTITLE, StandardField.VOLUME, StandardField.PUBLISHER, StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.EDITOR, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.EVENTTITLE, + .withDetailFields(StandardField.EDITOR, StandardField.EVENTTITLEADDON, StandardField.EVENTDATE, StandardField.VENUE, StandardField.LANGUAGE, StandardField.VOLUMES, StandardField.SERIES, StandardField.NUMBER, StandardField.NOTE, StandardField.ORGANIZATION, - StandardField.PUBLISHER, StandardField.LOCATION, StandardField.ISBN, StandardField.PAGETOTAL, - StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, - StandardField.EPRINTTYPE, StandardField.URL, - StandardField.URLDATE) + StandardField.LOCATION, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType INPROCEEDINGS = new BibEntryTypeBuilder() .withType(StandardEntryType.InProceedings) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.BOOKTITLE, StandardField.DATE) .withImportantFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.MAINTITLE, StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.BOOKSUBTITLE, StandardField.BOOKTITLEADDON, StandardField.EVENTTITLE, StandardField.VOLUME, StandardField.PUBLISHER, StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.BOOKTITLE, StandardField.DATE) - .withDetailFields(StandardField.EDITOR, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.MAINTITLE, - StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.BOOKSUBTITLE, StandardField.BOOKTITLEADDON, - StandardField.EVENTTITLE, StandardField.EVENTTITLEADDON, StandardField.EVENTDATE, StandardField.VENUE, - StandardField.LANGUAGE, StandardField.VOLUME, StandardField.PART, StandardField.VOLUMES, StandardField.SERIES, - StandardField.NUMBER, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.PUBLISHER, StandardField.LOCATION, - StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, StandardField.ADDENDUM, - StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, - StandardField.URL, StandardField.URLDATE) + .withDetailFields(StandardField.EDITOR, + StandardField.EVENTTITLEADDON, StandardField.EVENTDATE, StandardField.VENUE, + StandardField.LANGUAGE, StandardField.PART, StandardField.VOLUMES, StandardField.SERIES, + StandardField.NUMBER, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.LOCATION, + StandardField.ADDENDUM, + StandardField.PUBSTATE) .build(); private static final BibEntryType REFERENCE = new BibEntryTypeBuilder() .withType(StandardEntryType.Reference) - .withImportantFields(COLLECTION.getPrimaryOptionalFields()) - .withDetailFields(COLLECTION.getSecondaryOptionalFields()) .withRequiredFields(COLLECTION.getRequiredFields()) + .withImportantFields(COLLECTION.getImportantOptionalFields()) + .withDetailFields(COLLECTION.getDetailOptionalFields()) .build(); private static final BibEntryType MVREFERENCE = new BibEntryTypeBuilder() .withType(StandardEntryType.MvReference) - .withImportantFields(MVCOLLECTION.getPrimaryOptionalFields()) - .withDetailFields(MVCOLLECTION.getSecondaryOptionalFields()) .withRequiredFields(MVCOLLECTION.getRequiredFields()) + .withImportantFields(MVCOLLECTION.getImportantOptionalFields()) + .withDetailFields(MVCOLLECTION.getDetailOptionalFields()) .build(); private static final BibEntryType INREFERENCE = new BibEntryTypeBuilder() .withType(StandardEntryType.InReference) - .withImportantFields(INCOLLECTION.getPrimaryOptionalFields()) - .withDetailFields(INCOLLECTION.getSecondaryOptionalFields()) .withRequiredFields(INCOLLECTION.getRequiredFields()) + .withImportantFields(INCOLLECTION.getImportantOptionalFields()) + .withDetailFields(INCOLLECTION.getDetailOptionalFields()) .build(); private static final BibEntryType REPORT = new BibEntryTypeBuilder() .withType(StandardEntryType.Report) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.TYPE, StandardField.INSTITUTION, StandardField.DATE) .withImportantFields( StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.NUMBER, StandardField.ISRN, StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.TYPE, StandardField.INSTITUTION, StandardField.DATE) - .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.NUMBER, - StandardField.VERSION, StandardField.NOTE, StandardField.LOCATION, StandardField.ISRN, - StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE, - StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, - StandardField.URLDATE) + .withDetailFields(StandardField.LANGUAGE, + StandardField.VERSION, StandardField.NOTE, StandardField.LOCATION, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType SET = new BibEntryTypeBuilder() @@ -335,107 +310,101 @@ public class BiblatexEntryTypeDefinitions { private static final BibEntryType THESIS = new BibEntryTypeBuilder() .withType(StandardEntryType.Thesis) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.TYPE, StandardField.INSTITUTION, StandardField.DATE) .withImportantFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.TYPE, StandardField.INSTITUTION, StandardField.DATE) - .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.NOTE, - StandardField.LOCATION, StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, - StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, - StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + .withDetailFields(StandardField.LANGUAGE, StandardField.NOTE, + StandardField.LOCATION, StandardField.ISBN, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType UNPUBLISHED = new BibEntryTypeBuilder() .withType(StandardEntryType.Unpublished) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.DATE) .withImportantFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.HOWPUBLISHED, StandardField.PUBSTATE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.HOWPUBLISHED, - StandardField.NOTE, StandardField.LOCATION, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.EVENTTITLE, - StandardField.EVENTDATE, StandardField.VENUE, StandardField.URL, StandardField.URLDATE) + .withDetailFields(StandardField.LANGUAGE, + StandardField.NOTE, StandardField.LOCATION, StandardField.ADDENDUM, StandardField.EVENTTITLE, + StandardField.EVENTDATE, StandardField.VENUE) .build(); private static final BibEntryType CONFERENCE = new BibEntryTypeBuilder() .withType(StandardEntryType.Conference) - .withImportantFields(INPROCEEDINGS.getPrimaryOptionalFields()) - .withDetailFields(INPROCEEDINGS.getSecondaryOptionalFields()) .withRequiredFields(INPROCEEDINGS.getRequiredFields()) + .withImportantFields(INPROCEEDINGS.getImportantOptionalFields()) + .withDetailFields(INPROCEEDINGS.getDetailOptionalFields()) .build(); private static final BibEntryType ELECTRONIC = new BibEntryTypeBuilder() .withType(IEEETranEntryType.Electronic) - .withImportantFields(ONLINE.getPrimaryOptionalFields()) - .withDetailFields(ONLINE.getSecondaryOptionalFields()) .withRequiredFields(ONLINE.getRequiredFields()) + .withImportantFields(ONLINE.getImportantOptionalFields()) + .withDetailFields(ONLINE.getDetailOptionalFields()) .build(); private static final BibEntryType MASTERSTHESIS = new BibEntryTypeBuilder() .withType(StandardEntryType.MastersThesis) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.INSTITUTION, StandardField.DATE) .withImportantFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.TYPE, StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.INSTITUTION, StandardField.DATE) - .withDetailFields(StandardField.TYPE, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.NOTE, - StandardField.LOCATION, StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, - StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, - StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + .withDetailFields(StandardField.LANGUAGE, StandardField.NOTE, + StandardField.LOCATION, StandardField.ISBN, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType PHDTHESIS = new BibEntryTypeBuilder() .withType(StandardEntryType.PhdThesis) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.INSTITUTION, StandardField.DATE) .withImportantFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.TYPE, StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.INSTITUTION, StandardField.DATE) - .withDetailFields(StandardField.TYPE, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.NOTE, - StandardField.LOCATION, StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, - StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, - StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + .withDetailFields(StandardField.LANGUAGE, StandardField.NOTE, + StandardField.LOCATION, StandardField.ISBN, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType TECHREPORT = new BibEntryTypeBuilder() .withType(StandardEntryType.TechReport) + .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.INSTITUTION, StandardField.DATE) .withImportantFields( StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.TYPE, StandardField.NUMBER, StandardField.ISRN, StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.INSTITUTION, StandardField.DATE) - .withDetailFields(StandardField.TYPE, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, - StandardField.NUMBER, StandardField.VERSION, StandardField.NOTE, StandardField.LOCATION, - StandardField.ISRN, StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.ADDENDUM, - StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, - StandardField.URL, StandardField.URLDATE) + .withDetailFields(StandardField.LANGUAGE, + StandardField.VERSION, StandardField.NOTE, StandardField.LOCATION, + StandardField.ADDENDUM, + StandardField.PUBSTATE) .build(); private static final BibEntryType WWW = new BibEntryTypeBuilder() .withType(StandardEntryType.WWW) - .withImportantFields(ONLINE.getPrimaryOptionalFields()) - .withDetailFields(ONLINE.getSecondaryOptionalFields()) .withRequiredFields(ONLINE.getRequiredFields()) + .withImportantFields(ONLINE.getImportantOptionalFields()) + .withDetailFields(ONLINE.getDetailOptionalFields()) .build(); private static final BibEntryType SOFTWARE = new BibEntryTypeBuilder() .withType(StandardEntryType.Software) + .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) .withImportantFields( StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.HOWPUBLISHED, StandardField.LOCATION, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.HOWPUBLISHED, - StandardField.TYPE, StandardField.VERSION, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.LOCATION, - StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, - StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + .withDetailFields(StandardField.LANGUAGE, + StandardField.TYPE, StandardField.VERSION, StandardField.NOTE, StandardField.ORGANIZATION, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); private static final BibEntryType DATASET = new BibEntryTypeBuilder() .withType(StandardEntryType.Dataset) + .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) .withImportantFields( StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.HOWPUBLISHED, StandardField.LOCATION, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) - .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.EDITION, StandardField.HOWPUBLISHED, - StandardField.TYPE, StandardField.VERSION, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.LOCATION, - StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, - StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + .withDetailFields(StandardField.LANGUAGE, StandardField.EDITION, + StandardField.TYPE, StandardField.VERSION, StandardField.NOTE, StandardField.ORGANIZATION, + StandardField.ADDENDUM, StandardField.PUBSTATE) .build(); public static final List ALL = Arrays.asList(ARTICLE, BOOK, MVBOOK, INBOOK, BOOKINBOOK, SUPPBOOK, diff --git a/src/main/java/org/jabref/model/entry/types/BiblatexSoftwareEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BiblatexSoftwareEntryTypeDefinitions.java index 6a2e7f0851ea..ff5e3e6599f5 100644 --- a/src/main/java/org/jabref/model/entry/types/BiblatexSoftwareEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BiblatexSoftwareEntryTypeDefinitions.java @@ -12,42 +12,38 @@ public class BiblatexSoftwareEntryTypeDefinitions { private static final BibEntryType SOFTWARE = new BibEntryTypeBuilder() .withType(StandardEntryType.Software) + .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.URL, StandardField.VERSION, StandardField.YEAR) .withImportantFields(StandardField.DATE, StandardField.DOI, StandardField.EPRINTTYPE, StandardField.EPRINTCLASS, StandardField.EPRINT, - StandardField.EDITOR, StandardField.FILE, BiblatexSoftwareField.HALID, BiblatexSoftwareField.HALVERSION, StandardField.INSTITUTION, BiblatexSoftwareField.INTRODUCEDIN, + StandardField.FILE, BiblatexSoftwareField.HALID, BiblatexSoftwareField.HALVERSION, StandardField.INSTITUTION, BiblatexSoftwareField.INTRODUCEDIN, BiblatexSoftwareField.LICENSE, StandardField.MONTH, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.PUBLISHER, StandardField.RELATED, - BiblatexSoftwareField.RELATEDSTRING, BiblatexSoftwareField.REPOSITORY, BiblatexSoftwareField.SWHID, StandardField.URLDATE, StandardField.VERSION) - .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.URL, StandardField.VERSION, StandardField.YEAR) + BiblatexSoftwareField.RELATEDSTRING, BiblatexSoftwareField.REPOSITORY, BiblatexSoftwareField.SWHID, StandardField.URLDATE) .build(); private static final BibEntryType SOFTWAREVERSION = new BibEntryTypeBuilder() .withType(BiblatexSoftwareEntryType.SoftwareVersion) - .withImportantFields(StandardField.DATE, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, BiblatexSoftwareField.HALID, BiblatexSoftwareField.HALVERSION, - StandardField.INSTITUTION, BiblatexSoftwareField.INTRODUCEDIN, BiblatexSoftwareField.LICENSE, StandardField.MONTH, StandardField.NOTE, StandardField.ORGANIZATION, - StandardField.PUBLISHER, StandardField.RELATED, BiblatexSoftwareField.RELATEDTYPE, BiblatexSoftwareField.RELATEDSTRING, - BiblatexSoftwareField.REPOSITORY, BiblatexSoftwareField.SWHID, StandardField.SUBTITLE, StandardField.URLDATE) .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.URL, StandardField.YEAR, StandardField.VERSION) - .withDetailFields(StandardField.DATE, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, BiblatexSoftwareField.HALID, BiblatexSoftwareField.HALVERSION, + .withImportantFields(StandardField.DATE, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, BiblatexSoftwareField.HALID, BiblatexSoftwareField.HALVERSION, StandardField.INSTITUTION, BiblatexSoftwareField.INTRODUCEDIN, BiblatexSoftwareField.LICENSE, StandardField.MONTH, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.PUBLISHER, StandardField.RELATED, BiblatexSoftwareField.RELATEDTYPE, BiblatexSoftwareField.RELATEDSTRING, BiblatexSoftwareField.REPOSITORY, BiblatexSoftwareField.SWHID, StandardField.SUBTITLE, StandardField.URLDATE) - .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.URL, StandardField.YEAR) .build(); + private static final BibEntryType SOFTWAREMODULE = new BibEntryTypeBuilder() .withType(BiblatexSoftwareEntryType.SoftwareModule) + .withRequiredFields(StandardField.AUTHOR, StandardField.SUBTITLE, StandardField.URL, StandardField.YEAR) .withImportantFields(StandardField.DATE, StandardField.DOI, StandardField.EPRINTTYPE, StandardField.EPRINTCLASS, StandardField.EPRINT, StandardField.EDITOR, StandardField.FILE, BiblatexSoftwareField.HALID, BiblatexSoftwareField.HALVERSION, StandardField.INSTITUTION, BiblatexSoftwareField.INTRODUCEDIN, BiblatexSoftwareField.LICENSE, StandardField.MONTH, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.PUBLISHER, StandardField.RELATED, BiblatexSoftwareField.RELATEDSTRING, BiblatexSoftwareField.REPOSITORY, BiblatexSoftwareField.SWHID, StandardField.URLDATE, StandardField.VERSION) - .withRequiredFields(StandardField.AUTHOR, StandardField.SUBTITLE, StandardField.URL, StandardField.YEAR) .build(); private static final BibEntryType CODEFRAGMENT = new BibEntryTypeBuilder() .withType(BiblatexSoftwareEntryType.CodeFragment) + .withRequiredFields(StandardField.URL) .withImportantFields(StandardField.DATE, StandardField.DOI, StandardField.EPRINTTYPE, StandardField.EPRINTCLASS, StandardField.EPRINT, StandardField.EDITOR, StandardField.FILE, BiblatexSoftwareField.HALID, BiblatexSoftwareField.HALVERSION, StandardField.INSTITUTION, BiblatexSoftwareField.INTRODUCEDIN, BiblatexSoftwareField.LICENSE, StandardField.MONTH, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.PUBLISHER, StandardField.RELATED, BiblatexSoftwareField.RELATEDSTRING, BiblatexSoftwareField.REPOSITORY, BiblatexSoftwareField.SWHID, StandardField.URLDATE, StandardField.VERSION) - .withRequiredFields(StandardField.URL) .build(); public static final List ALL = Arrays.asList(SOFTWAREVERSION, SOFTWARE, SOFTWAREMODULE, CODEFRAGMENT); diff --git a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java index 594301642b76..885235958f89 100644 --- a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java @@ -10,6 +10,9 @@ /** * This class represents all supported BibTeX entry types. + * It is based on the information of BibTeXing, a manual + * by the original BibTeX author. Also enriched by new fields not existing back then (e.g., ISSN). + *

* The BibLaTeX entry types are defined at {@link BiblatexEntryTypeDefinitions}. */ public class BibtexEntryTypeDefinitions { @@ -69,7 +72,9 @@ public class BibtexEntryTypeDefinitions { */ private static final BibEntryType INBOOK = new BibEntryTypeBuilder() .withType(StandardEntryType.InBook) - .withRequiredFields(Arrays.asList(new OrFields(StandardField.CHAPTER, StandardField.PAGES), new OrFields(StandardField.AUTHOR, StandardField.EDITOR)), StandardField.TITLE, StandardField.PUBLISHER, StandardField.YEAR) + .addRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR)) + .addRequiredFields(StandardField.TITLE, StandardField.PUBLISHER, StandardField.YEAR) + .addRequiredFields(new OrFields(StandardField.CHAPTER, StandardField.PAGES)) .withImportantFields(StandardField.VOLUME, StandardField.NUMBER, StandardField.SERIES, StandardField.TYPE, StandardField.ADDRESS, StandardField.EDITION, StandardField.MONTH, StandardField.ISBN, StandardField.NOTE) .build(); diff --git a/src/main/java/org/jabref/model/entry/types/EntryTypeFactory.java b/src/main/java/org/jabref/model/entry/types/EntryTypeFactory.java index ae879fabafb8..0f30ca5e6bf5 100644 --- a/src/main/java/org/jabref/model/entry/types/EntryTypeFactory.java +++ b/src/main/java/org/jabref/model/entry/types/EntryTypeFactory.java @@ -28,7 +28,7 @@ public static boolean nameAndFieldsAreEqual(BibEntryType type1, BibEntryType typ return Objects.equals(type1.getType(), type2.getType()) && Objects.equals(type1.getRequiredFields(), type2.getRequiredFields()) && Objects.equals(type1.getOptionalFields(), type2.getOptionalFields()) - && Objects.equals(type1.getSecondaryOptionalFields(), type2.getSecondaryOptionalFields()); + && Objects.equals(type1.getDetailOptionalFields(), type2.getDetailOptionalFields()); } } diff --git a/src/main/java/org/jabref/model/entry/types/IEEETranEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/IEEETranEntryTypeDefinitions.java index 600c9f67bdb4..790dea24746a 100644 --- a/src/main/java/org/jabref/model/entry/types/IEEETranEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/IEEETranEntryTypeDefinitions.java @@ -62,7 +62,7 @@ public class IEEETranEntryTypeDefinitions { .withType(IEEETranEntryType.Patent) .withRequiredFields(new OrFields(StandardField.YEAR, StandardField.YEARFILED), StandardField.NATIONALITY, StandardField.NUMBER) .withImportantFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.LANGUAGE, StandardField.ASSIGNEE, StandardField.ADDRESS, - StandardField.TYPE, StandardField.NUMBER, StandardField.DAY, StandardField.DAYFILED, StandardField.MONTH, + StandardField.TYPE, StandardField.DAY, StandardField.DAYFILED, StandardField.MONTH, StandardField.MONTHFILED, StandardField.NOTE, StandardField.URL) .build(); diff --git a/src/main/java/org/jabref/model/groups/AllEntriesGroup.java b/src/main/java/org/jabref/model/groups/AllEntriesGroup.java index 320b3306ce8a..1e3ef85fa538 100644 --- a/src/main/java/org/jabref/model/groups/AllEntriesGroup.java +++ b/src/main/java/org/jabref/model/groups/AllEntriesGroup.java @@ -23,6 +23,12 @@ public boolean equals(Object o) { return o instanceof AllEntriesGroup aeg && Objects.equals(aeg.getName(), getName()); } + /** + * Always returns true for any BibEntry! + * + * @param entry The @{@link BibEntry} to check + * @return Always returns true + */ @Override public boolean contains(BibEntry entry) { return true; diff --git a/src/main/java/org/jabref/model/groups/GroupTreeNode.java b/src/main/java/org/jabref/model/groups/GroupTreeNode.java index efe4d233fee2..d5954181d9dc 100644 --- a/src/main/java/org/jabref/model/groups/GroupTreeNode.java +++ b/src/main/java/org/jabref/model/groups/GroupTreeNode.java @@ -135,6 +135,13 @@ public int hashCode() { return Objects.hash(group); } + /** + * Get only groups containing all the entries or just groups containing any of the + * + * @param entries List of {@link BibEntry} to search for + * @param requireAll Whether to return only groups that must contain all entries + * @return List of {@link GroupTreeNode} containing the matches. {@link AllEntriesGroup} is always contained} + */ public List getContainingGroups(List entries, boolean requireAll) { List groups = new ArrayList<>(); @@ -197,6 +204,11 @@ public List getEntriesInGroup(List entries) { return result; } + /** + * Get the name of the underlying group + * + * @return String the name of the group + */ public String getName() { return group.getName(); } diff --git a/src/main/java/org/jabref/model/metadata/MetaData.java b/src/main/java/org/jabref/model/metadata/MetaData.java index 0479400337ae..5c7293487ad4 100644 --- a/src/main/java/org/jabref/model/metadata/MetaData.java +++ b/src/main/java/org/jabref/model/metadata/MetaData.java @@ -48,6 +48,7 @@ public class MetaData { public static final String FILE_DIRECTORY_LATEX = "fileDirectoryLatex"; public static final String PROTECTED_FLAG_META = "protectedFlag"; public static final String SELECTOR_META_PREFIX = "selector_"; + public static final String BIBDESK_STATIC_FLAG = "BibDesk Static Groups"; public static final char ESCAPE_CHARACTER = '\\'; public static final char SEPARATOR_CHARACTER = ';'; diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index d4a9e64016c5..1da58ce09899 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.SequencedMap; import java.util.Set; import java.util.TreeSet; import java.util.UUID; @@ -835,10 +836,6 @@ public void setLanguageDependentDefaultValues() { defaults.put(CUSTOM_TAB_FIELDS + "_def1", StandardField.ABSTRACT.getName()); defaults.put(CUSTOM_TAB_NAME + "_def1", Localization.lang("Abstract")); - // Entry editor tab 2: Comments Field - used for research comments, etc. - defaults.put(CUSTOM_TAB_FIELDS + "_def2", StandardField.COMMENT.getName()); - defaults.put(CUSTOM_TAB_NAME + "_def2", Localization.lang("Comments")); - defaults.put(EMAIL_SUBJECT, Localization.lang("References")); } @@ -1508,7 +1505,7 @@ private Map> getEntryEditorTabs() { List tabFields = getSeries(CUSTOM_TAB_FIELDS); if (tabNames.isEmpty() || (tabNames.size() != tabFields.size())) { - // Nothing set, so we use the default values + // Nothing set (or wrong configuration), then we use default values tabNames = getSeries(CUSTOM_TAB_NAME + "_def"); tabFields = getSeries(CUSTOM_TAB_FIELDS + "_def"); } @@ -1543,8 +1540,8 @@ private void storeEntryEditorTabs(Map> customTabs) { getEntryEditorTabs(); } - private Map> getDefaultEntryEditorTabs() { - Map> customTabsMap = new LinkedHashMap<>(); + private SequencedMap> getDefaultEntryEditorTabs() { + SequencedMap> customTabsMap = new LinkedHashMap<>(); int defNumber = 0; while (true) { diff --git a/src/main/resources/csl-locales b/src/main/resources/csl-locales index f6f859c21230..7b5a477f2d9a 160000 --- a/src/main/resources/csl-locales +++ b/src/main/resources/csl-locales @@ -1 +1 @@ -Subproject commit f6f859c2123087ac51c574cd1ed3a64204ae73c7 +Subproject commit 7b5a477f2d9a8882b52bcecdc50f08d4422cc822 diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles index fab696b6f365..5c999d601964 160000 --- a/src/main/resources/csl-styles +++ b/src/main/resources/csl-styles @@ -1 +1 @@ -Subproject commit fab696b6f365a770547df90b84a54691462b7c21 +Subproject commit 5c999d6019645fc48ae0ac07c8c5d462c767914f diff --git a/src/main/resources/fonts/JabRefMaterialDesign.ttf b/src/main/resources/fonts/JabRefMaterialDesign.ttf index 1dad4686d10f..21525c7a697f 100644 Binary files a/src/main/resources/fonts/JabRefMaterialDesign.ttf and b/src/main/resources/fonts/JabRefMaterialDesign.ttf differ diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index e8dff158caf9..edf020e74ea7 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -77,8 +77,6 @@ Application\ to\ push\ entries\ to=Application to push entries to Apply=Apply -Arguments\ passed\ on\ to\ running\ JabRef\ instance.\ Shutting\ down.=Arguments passed on to running JabRef instance. Shutting down. - Assign\ the\ original\ group's\ entries\ to\ this\ group?=Assign the original group's entries to this group? Assigned\ %0\ entries\ to\ group\ "%1".=Assigned %0 entries to group "%1". @@ -311,6 +309,12 @@ Export\ preferences\ to\ file=Export preferences to file Export\ to\ clipboard=Export to clipboard Export\ to\ text\ file.=Export to text file. +Extract\ references\ from\ file=Extract references from file +Extract\ References=Extract References +Processing\ PDF(s)=Processing PDF(s) +Processing\ a\ large\ number\ of\ files=Processing a large number of files +You\ are\ about\ to\ process\ %0\ files.\ Continue?=You are about to process %0 files. Continue? + Exporting\ %0=Exporting %0 Could\ not\ export\ file\ '%0'\ (reason\:\ %1)=Could not export file '%0' (reason: %1) Unknown\ export\ format\ %0=Unknown export format %0 @@ -1926,7 +1930,6 @@ Group\ view\ mode\ set\ to\ union=Group view mode set to union Open\ file\ %0=Open file %0 Toggle\ intersection=Toggle intersection Toggle\ union=Toggle union -Jump\ to\ entry=Jump to entry The\ group\ name\ contains\ the\ keyword\ separator\ "%0"\ and\ thus\ probably\ does\ not\ work\ as\ expected.=The group name contains the keyword separator "%0" and thus probably does not work as expected. Blog=Blog diff --git a/src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatterTest.java b/src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatterTest.java index 45a418094bce..37eff160e67a 100644 --- a/src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatterTest.java +++ b/src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatterTest.java @@ -1,7 +1,8 @@ package org.jabref.logic.formatter.bibtexfields; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -10,67 +11,25 @@ */ public class RemoveBracesFormatterTest { - private RemoveBracesFormatter formatter; - - @BeforeEach - public void setUp() { - formatter = new RemoveBracesFormatter(); - } - - @Test - public void formatRemovesSingleEnclosingBraces() { - assertEquals("test", formatter.format("{test}")); - } - - @Test - public void formatKeepsUnmatchedBracesAtBeginning() { - assertEquals("{test", formatter.format("{test")); - } - - @Test - public void formatKeepsUnmatchedBracesAtEnd() { - assertEquals("test}", formatter.format("test}")); - } - - @Test - public void formatKeepsShortString() { - assertEquals("t", formatter.format("t")); - } - - @Test - public void formatRemovesBracesOnly() { - assertEquals("", formatter.format("{}")); - } - - @Test - public void formatKeepsEmptyString() { - assertEquals("", formatter.format("")); - } - - @Test - public void formatRemovesDoubleEnclosingBraces() { - assertEquals("test", formatter.format("{{test}}")); - } - - @Test - public void formatRemovesTripleEnclosingBraces() { - assertEquals("test", formatter.format("{{{test}}}")); - } - - @Test - public void formatKeepsNonMatchingBraces() { - assertEquals("{A} and {B}", formatter.format("{A} and {B}")); - } - - @Test - public void formatRemovesOnlyMatchingBraces() { - assertEquals("{A} and {B}", formatter.format("{{A} and {B}}")); - } - - @Test - public void formatDoesNotRemoveBracesInBrokenString() { - // We opt here for a conservative approach although one could argue that "A} and {B}" is also a valid return - assertEquals("{A} and {B}}", formatter.format("{A} and {B}}")); + private final RemoveBracesFormatter formatter = new RemoveBracesFormatter(); + + @ParameterizedTest + @CsvSource({ + "test, {test}", // formatRemovesSingleEnclosingBraces + "{test, {test", // formatKeepsUnmatchedBracesAtBeginning + "test}, test}", // formatKeepsUnmatchedBracesAtEnd + "t, t", // formatKeepsShortString + "'', {}", // formatRemovesBracesOnly + "test, {{test}}", // formatKeepsEmptyString + "test, {{{test}}}", // formatRemovesDoubleEnclosingBraces + "{A} and {B}, {A} and {B}", // formatRemovesTripleEnclosingBraces + "{A} and {B}, {{A} and {B}}", // formatKeepsNonMatchingBraces + "{A} and {B}}, {A} and {B}}", // formatRemovesOnlyMatchingBraces + "Vall{\\'e}e Poussin, {Vall{\\'e}e Poussin}", // formatDoesNotRemoveBracesInBrokenString + "Vall{\\'e}e Poussin, Vall{\\'e}e Poussin" + }) + public void format(String expected, String input) { + assertEquals(expected, formatter.format(input)); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java b/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java index 9ec42a04d899..3c84de494c21 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java @@ -31,6 +31,8 @@ import org.jabref.logic.importer.ParseException; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.util.OS; +import org.jabref.model.TreeNode; +import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryType; @@ -56,6 +58,7 @@ import org.jabref.model.metadata.SaveOrder; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; import org.junit.jupiter.params.ParameterizedTest; @@ -72,10 +75,10 @@ /** * Tests for reading whole bib files can be found at {@link org.jabref.logic.importer.fileformat.BibtexImporterTest} *

- * Tests cannot be executed concurrently, because Localization is used at {@link BibtexParser#parseAndAddEntry(String)} + * Tests cannot be executed concurrently, because Localization is used at {@link BibtexParser#sparseAndAddEntry(String)} */ class BibtexParserTest { - + private static final String BIB_DESK_ROOT_GROUP_NAME = "BibDeskGroups"; private ImportFormatPreferences importFormatPreferences; private BibtexParser parser; @@ -87,7 +90,7 @@ void setUp() { } @Test - void parseWithNullThrowsNullPointerException() throws Exception { + void parseWithNullThrowsNullPointerException() { Executable toBeTested = () -> parser.parse(null); assertThrows(NullPointerException.class, toBeTested); } @@ -1380,6 +1383,311 @@ void integrationTestGroupTree() throws IOException, ParseException { ((ExplicitGroup) root.getChildren().get(2).getGroup()).getLegacyEntryKeys()); } + /** + * Checks that BibDesk Static Groups are available after parsing the library + */ + @Test + void integrationTestBibDeskStaticGroup() throws Exception { + ParserResult result = parser.parse(new StringReader(""" + @article{Swain:2023aa, + author = {Subhashree Swain and P. Shalima and K.V.P. Latha}, + date-added = {2023-09-14 20:09:08 +0200}, + date-modified = {2023-09-14 20:09:08 +0200}, + eprint = {2309.06758}, + month = {09}, + title = {Unravelling the Nuclear Dust Morphology of NGC 1365: A Two Phase Polar - RAT Model for the Ultraviolet to Infrared Spectral Energy Distribution}, + url = {https://arxiv.org/pdf/2309.06758.pdf}, + year = {2023}, + bdsk-url-1 = {https://arxiv.org/pdf/2309.06758.pdf}, + bdsk-url-2 = {https://arxiv.org/abs/2309.06758}} + + @article{Heyl:2023aa, + author = {Johannes Heyl and Joshua Butterworth and Serena Viti}, + date-added = {2023-09-14 20:09:08 +0200}, + date-modified = {2023-09-14 20:09:08 +0200}, + eprint = {2309.06784}, + month = {09}, + title = {Understanding Molecular Abundances in Star-Forming Regions Using Interpretable Machine Learning}, + url = {https://arxiv.org/pdf/2309.06784.pdf}, + year = {2023}, + bdsk-url-1 = {https://arxiv.org/pdf/2309.06784.pdf}, + bdsk-url-2 = {https://arxiv.org/abs/2309.06784}} + + @comment{BibDesk Static Groups{ + + + + + + group name + firstTestGroup + keys + Swain:2023aa,Heyl:2023aa + + + group name + secondTestGroup + keys + Swain:2023aa + + + + }} + """)); + + GroupTreeNode root = result.getMetaData().getGroups().get(); + assertEquals(new AllEntriesGroup("All entries"), root.getGroup()); + assertEquals(Optional.of(BIB_DESK_ROOT_GROUP_NAME), root.getFirstChild().map(GroupTreeNode::getName)); + + ExplicitGroup firstTestGroupExpected = new ExplicitGroup("firstTestGroup", GroupHierarchyType.INDEPENDENT, ','); + firstTestGroupExpected.setExpanded(true); + + assertEquals(Optional.of(firstTestGroupExpected), root.getFirstChild().flatMap(TreeNode::getFirstChild).map(GroupTreeNode::getGroup)); + + ExplicitGroup secondTestGroupExpected = new ExplicitGroup("secondTestGroup", GroupHierarchyType.INDEPENDENT, ','); + secondTestGroupExpected.setExpanded(true); + assertEquals(Optional.of(secondTestGroupExpected), root.getFirstChild().flatMap(TreeNode::getLastChild).map(GroupTreeNode::getGroup)); + + BibDatabase db = result.getDatabase(); + + assertEquals(List.of(root.getGroup(), firstTestGroupExpected), root.getContainingGroups(db.getEntries(), true).stream().map(GroupTreeNode::getGroup).toList()); + assertEquals(List.of(root.getGroup(), firstTestGroupExpected), root.getContainingGroups(db.getEntryByCitationKey("Heyl:2023aa").stream().toList(), false).stream().map(GroupTreeNode::getGroup).toList()); + } + + /** + * Checks that BibDesk Smart Groups are available after parsing the library + */ + @Test + @Disabled("Not yet supported") + void integrationTestBibDeskSmartGroup() throws Exception { + ParserResult result = parser.parse(new StringReader(""" + @article{Kraljic:2023aa, + author = {Katarina Kraljic and Florent Renaud and Yohan Dubois and Christophe Pichon and Oscar Agertz and Eric Andersson and Julien Devriendt and Jonathan Freundlich and Sugata Kaviraj and Taysun Kimm and Garreth Martin and S{\\'e}bastien Peirani and {\\'A}lvaro Segovia Otero and Marta Volonteri and Sukyoung K. Yi}, + date-added = {2023-09-14 20:09:10 +0200}, + date-modified = {2023-09-14 20:09:10 +0200}, + eprint = {2309.06485}, + month = {09}, + title = {Emergence and cosmic evolution of the Kennicutt-Schmidt relation driven by interstellar turbulence}, + url = {https://arxiv.org/pdf/2309.06485.pdf}, + year = {2023}, + bdsk-url-1 = {https://arxiv.org/pdf/2309.06485.pdf}, + bdsk-url-2 = {https://arxiv.org/abs/2309.06485}} + + @article{Swain:2023aa, + author = {Subhashree Swain and P. Shalima and K.V.P. Latha}, + date-added = {2023-09-14 20:09:08 +0200}, + date-modified = {2023-09-14 20:09:08 +0200}, + eprint = {2309.06758}, + month = {09}, + title = {Unravelling the Nuclear Dust Morphology of NGC 1365: A Two Phase Polar - RAT Model for the Ultraviolet to Infrared Spectral Energy Distribution}, + url = {https://arxiv.org/pdf/2309.06758.pdf}, + year = {2023}, + bdsk-url-1 = {https://arxiv.org/pdf/2309.06758.pdf}, + bdsk-url-2 = {https://arxiv.org/abs/2309.06758}} + + @article{Heyl:2023aa, + author = {Johannes Heyl and Joshua Butterworth and Serena Viti}, + date-added = {2023-09-14 20:09:08 +0200}, + date-modified = {2023-09-14 20:09:08 +0200}, + eprint = {2309.06784}, + month = {09}, + title = {Understanding Molecular Abundances in Star-Forming Regions Using Interpretable Machine Learning}, + url = {https://arxiv.org/pdf/2309.06784.pdf}, + year = {2023}, + bdsk-url-1 = {https://arxiv.org/pdf/2309.06784.pdf}, + bdsk-url-2 = {https://arxiv.org/abs/2309.06784}} + + @comment{BibDesk Smart Groups{ + + + + + + conditions + + + comparison + 4 + key + BibTeX Type + value + article + version + 1 + + + comparison + 2 + key + Title + value + the + version + 1 + + + conjunction + 0 + group name + article + + + conditions + + + comparison + 3 + key + Author + value + Swain + version + 1 + + + conjunction + 0 + group name + Swain + + + + }} + """)); + + GroupTreeNode root = result.getMetaData().getGroups().get(); + assertEquals(new AllEntriesGroup("All entries"), root.getGroup()); + assertEquals(2, root.getNumberOfChildren()); + ExplicitGroup firstTestGroupExpected = new ExplicitGroup("article", GroupHierarchyType.INDEPENDENT, ','); + firstTestGroupExpected.setExpanded(false); + assertEquals(firstTestGroupExpected, root.getChildren().get(0).getGroup()); + ExplicitGroup secondTestGroupExpected = new ExplicitGroup("Swain", GroupHierarchyType.INDEPENDENT, ','); + secondTestGroupExpected.setExpanded(false); + assertEquals(secondTestGroupExpected, root.getChildren().get(1).getGroup()); + + BibDatabase db = result.getDatabase(); + List firstTestGroupEntriesExpected = new ArrayList<>(); + firstTestGroupEntriesExpected.add(db.getEntryByCitationKey("Kraljic:2023aa").get()); + firstTestGroupEntriesExpected.add(db.getEntryByCitationKey("Swain:2023aa").get()); + assertTrue(root.getChildren().get(0).getGroup().containsAll(firstTestGroupEntriesExpected)); + assertFalse(root.getChildren().get(1).getGroup().contains(db.getEntryByCitationKey("Swain:2023aa").get())); + } + + /** + * Checks that both BibDesk Static Groups and Smart Groups are available after parsing the library + */ + @Test + @Disabled("Not yet supported") + void integrationTestBibDeskMultipleGroup() throws Exception { + ParserResult result = parser.parse(new StringReader(""" + @article{Kraljic:2023aa, + author = {Katarina Kraljic and Florent Renaud and Yohan Dubois and Christophe Pichon and Oscar Agertz and Eric Andersson and Julien Devriendt and Jonathan Freundlich and Sugata Kaviraj and Taysun Kimm and Garreth Martin and S{\\'e}bastien Peirani and {\\'A}lvaro Segovia Otero and Marta Volonteri and Sukyoung K. Yi}, + date-added = {2023-09-14 20:09:10 +0200}, + date-modified = {2023-09-14 20:09:10 +0200}, + eprint = {2309.06485}, + month = {09}, + title = {Emergence and cosmic evolution of the Kennicutt-Schmidt relation driven by interstellar turbulence}, + url = {https://arxiv.org/pdf/2309.06485.pdf}, + year = {2023}, + bdsk-url-1 = {https://arxiv.org/pdf/2309.06485.pdf}, + bdsk-url-2 = {https://arxiv.org/abs/2309.06485}} + + @article{Swain:2023aa, + author = {Subhashree Swain and P. Shalima and K.V.P. Latha}, + date-added = {2023-09-14 20:09:08 +0200}, + date-modified = {2023-09-14 20:09:08 +0200}, + eprint = {2309.06758}, + month = {09}, + title = {Unravelling the Nuclear Dust Morphology of NGC 1365: A Two Phase Polar - RAT Model for the Ultraviolet to Infrared Spectral Energy Distribution}, + url = {https://arxiv.org/pdf/2309.06758.pdf}, + year = {2023}, + bdsk-url-1 = {https://arxiv.org/pdf/2309.06758.pdf}, + bdsk-url-2 = {https://arxiv.org/abs/2309.06758}} + + @article{Heyl:2023aa, + author = {Johannes Heyl and Joshua Butterworth and Serena Viti}, + date-added = {2023-09-14 20:09:08 +0200}, + date-modified = {2023-09-14 20:09:08 +0200}, + eprint = {2309.06784}, + month = {09}, + title = {Understanding Molecular Abundances in Star-Forming Regions Using Interpretable Machine Learning}, + url = {https://arxiv.org/pdf/2309.06784.pdf}, + year = {2023}, + bdsk-url-1 = {https://arxiv.org/pdf/2309.06784.pdf}, + bdsk-url-2 = {https://arxiv.org/abs/2309.06784}} + + @comment{BibDesk Static Groups{ + + + + + + group name + firstTestGroup + keys + Swain:2023aa,Heyl:2023aa + + + + }} + + @comment{BibDesk Smart Groups{ + + + + + + conditions + + + comparison + 4 + key + BibTeX Type + value + article + version + 1 + + + comparison + 2 + key + Title + value + the + version + 1 + + + conjunction + 0 + group name + article + + + + }} + """)); + + GroupTreeNode root = result.getMetaData().getGroups().get(); + assertEquals(new AllEntriesGroup("All entries"), root.getGroup()); + assertEquals(2, root.getNumberOfChildren()); + ExplicitGroup firstTestGroupExpected = new ExplicitGroup("firstTestGroup", GroupHierarchyType.INDEPENDENT, ','); + firstTestGroupExpected.setExpanded(false); + assertEquals(firstTestGroupExpected, root.getChildren().get(0).getGroup()); + ExplicitGroup secondTestGroupExpected = new ExplicitGroup("article", GroupHierarchyType.INDEPENDENT, ','); + secondTestGroupExpected.setExpanded(false); + assertEquals(secondTestGroupExpected, root.getChildren().get(1).getGroup()); + + BibDatabase db = result.getDatabase(); + assertTrue(root.getChildren().get(0).getGroup().containsAll(db.getEntries())); + List smartGroupEntriesExpected = new ArrayList<>(); + smartGroupEntriesExpected.add(db.getEntryByCitationKey("Kraljic:2023aa").get()); + smartGroupEntriesExpected.add(db.getEntryByCitationKey("Swain:2023aa").get()); + assertTrue(root.getChildren().get(0).getGroup().containsAll(smartGroupEntriesExpected)); + } + /** * Checks that a TexGroup finally gets the required data, after parsing the library. */ @@ -1863,4 +2171,39 @@ void parseDuplicateKeywordsWithTwoEntries() throws Exception { ParserResult result = parser.parse(new StringReader(entries)); assertEquals(List.of(expectedEntryFirst, expectedEntrySecond), result.getDatabase().getEntries()); } + + @Test + void parseBibDeskLinkedFiles() throws IOException { + + BibEntry expectedEntry = new BibEntry(StandardEntryType.Article); + expectedEntry.withCitationKey("Kovakkuni:2023aa") + .withField(StandardField.AUTHOR, "Navyasree Kovakkuni and Federico Lelli and Pierre-alain Duc and M{\\'e}d{\\'e}ric Boquien and Jonathan Braine and Elias Brinks and Vassilis Charmandaris and Francoise Combes and Jeremy Fensch and Ute Lisenfeld and Stacy McGaugh and J. Chris Mihos and Marcel. S. Pawlowski and Yves. Revaz and Peter. M. Weilbacher") + .withField(new UnknownField("date-added"), "2023-09-14 20:09:12 +0200") + .withField(new UnknownField("date-modified"), "2023-09-14 20:09:12 +0200") + .withField(StandardField.EPRINT, "2309.06478") + .withField(StandardField.MONTH, "09") + .withField(StandardField.TITLE, "Molecular and Ionized Gas in Tidal Dwarf Galaxies: The Spatially Resolved Star-Formation Relation") + .withField(StandardField.URL, "https://arxiv.org/pdf/2309.06478.pdf") + .withField(StandardField.YEAR, "2023") + .withField(new UnknownField("bdsk-url-1"), "https://arxiv.org/abs/2309.06478") + .withField(StandardField.FILE, ":../../Downloads/2309.06478.pdf:"); + + ParserResult result = parser.parse(new StringReader(""" + @article{Kovakkuni:2023aa, + author = {Navyasree Kovakkuni and Federico Lelli and Pierre-alain Duc and M{\\'e}d{\\'e}ric Boquien and Jonathan Braine and Elias Brinks and Vassilis Charmandaris and Francoise Combes and Jeremy Fensch and Ute Lisenfeld and Stacy McGaugh and J. Chris Mihos and Marcel. S. Pawlowski and Yves. Revaz and Peter. M. Weilbacher}, + date-added = {2023-09-14 20:09:12 +0200}, + date-modified = {2023-09-14 20:09:12 +0200}, + eprint = {2309.06478}, + month = {09}, + title = {Molecular and Ionized Gas in Tidal Dwarf Galaxies: The Spatially Resolved Star-Formation Relation}, + url = {https://arxiv.org/pdf/2309.06478.pdf}, + year = {2023}, + bdsk-file-1 = {YnBsaXN0MDDSAQIDBFxyZWxhdGl2ZVBhdGhZYWxpYXNEYXRhXxAeLi4vLi4vRG93bmxvYWRzLzIzMDkuMDY0NzgucGRmTxEBUgAAAAABUgACAAAMTWFjaW50b3NoIEhEAAAAAAAAAAAAAAAAAAAA4O/yLkJEAAH/////DjIzMDkuMDY0NzgucGRmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/////hKRkeAAAAAAAAAAAAAgACAAAKIGN1AAAAAAAAAAAAAAAAAAlEb3dubG9hZHMAAAIAKy86VXNlcnM6Y2hyaXN0b3BoczpEb3dubG9hZHM6MjMwOS4wNjQ3OC5wZGYAAA4AHgAOADIAMwAwADkALgAwADYANAA3ADgALgBwAGQAZgAPABoADABNAGEAYwBpAG4AdABvAHMAaAAgAEgARAASAClVc2Vycy9jaHJpc3RvcGhzL0Rvd25sb2Fkcy8yMzA5LjA2NDc4LnBkZgAAEwABLwAAFQACABH//wAAAAgADQAaACQARQAAAAAAAAIBAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAGb}, + bdsk-url-1 = {https://arxiv.org/abs/2309.06478}} + } + """)); + BibDatabase database = result.getDatabase(); + + assertEquals(Collections.singletonList(expectedEntry), database.getEntries()); + } } diff --git a/src/test/java/org/jabref/logic/importer/util/GrobidServiceTest.java b/src/test/java/org/jabref/logic/importer/util/GrobidServiceTest.java index 9898ec9fba23..daca2f700c25 100644 --- a/src/test/java/org/jabref/logic/importer/util/GrobidServiceTest.java +++ b/src/test/java/org/jabref/logic/importer/util/GrobidServiceTest.java @@ -4,6 +4,7 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.util.List; +import java.util.Objects; import java.util.Optional; import org.jabref.logic.importer.ImportFormatPreferences; @@ -100,4 +101,22 @@ public void processPdfTest() throws IOException, ParseException, URISyntaxExcept // assertEquals(Optional.of("Paper Title"), be0.getField(StandardField.TITLE)); // assertEquals(Optional.of("2014-10-05"), be0.getField(StandardField.DATE)); } + + @Test + public void extractsReferencesFromPdf() throws IOException, ParseException, URISyntaxException { + BibEntry ref1 = new BibEntry(StandardEntryType.Article) + .withField(StandardField.AUTHOR, "Kopp, O") + .withField(StandardField.ADDRESS, "Berlin Heidelberg") + .withField(StandardField.DATE, "2013") + .withField(StandardField.JOURNAL, "All links were last followed on October") + .withField(StandardField.PAGES, "700--704") + .withField(StandardField.PUBLISHER, "Springer") + .withField(StandardField.TITLE, "Winery -A Modeling Tool for TOSCA-based Cloud Applications") + .withField(StandardField.VOLUME, "8274") + .withField(StandardField.YEAR, "2013"); + + Path file = Path.of(Objects.requireNonNull(PdfGrobidImporterTest.class.getResource("LNCS-minimal.pdf")).toURI()); + List extractedReferences = grobidService.processReferences(file, importFormatPreferences); + assertEquals(List.of(ref1), extractedReferences); + } } diff --git a/src/test/java/org/jabref/model/entry/BibEntryTypeBuilderTest.java b/src/test/java/org/jabref/model/entry/BibEntryTypeBuilderTest.java new file mode 100644 index 000000000000..44794f55412d --- /dev/null +++ b/src/test/java/org/jabref/model/entry/BibEntryTypeBuilderTest.java @@ -0,0 +1,41 @@ +package org.jabref.model.entry; + +import java.util.LinkedHashSet; +import java.util.List; + +import org.jabref.model.entry.field.StandardField; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class BibEntryTypeBuilderTest { + + @Test + void fieldAlreadySeenSameCategory() { + assertThrows(IllegalArgumentException.class, () -> + new BibEntryTypeBuilder() + .withImportantFields(StandardField.AUTHOR) + .withImportantFields(StandardField.AUTHOR) + .build()); + } + + @Test + void detailOptionalWorks() { + BibEntryType bibEntryType = new BibEntryTypeBuilder() + .withImportantFields(StandardField.AUTHOR) + .withDetailFields(StandardField.NOTE) + .build(); + assertEquals(new LinkedHashSet<>(List.of(StandardField.NOTE)), bibEntryType.getDetailOptionalFields()); + } + + @Test + void fieldAlreadySeenDifferentCategories() { + assertThrows(IllegalArgumentException.class, () -> + new BibEntryTypeBuilder() + .withRequiredFields(StandardField.AUTHOR) + .withImportantFields(StandardField.AUTHOR) + .build()); + } +} diff --git a/src/test/java/org/jabref/model/entry/BibEntryTypesManagerTest.java b/src/test/java/org/jabref/model/entry/BibEntryTypesManagerTest.java index e736a1b8b902..e227b2263930 100644 --- a/src/test/java/org/jabref/model/entry/BibEntryTypesManagerTest.java +++ b/src/test/java/org/jabref/model/entry/BibEntryTypesManagerTest.java @@ -140,7 +140,7 @@ void standardTypeIsStillAccessibleIfOverwritten(BibDatabaseMode mode) { @ParameterizedTest @EnumSource(BibDatabaseMode.class) - void sModifyingArticle(BibDatabaseMode mode) { + void modifyingArticle(BibDatabaseMode mode) { overwrittenStandardType = new BibEntryType( StandardEntryType.Article, List.of(new BibField(StandardField.TITLE, FieldPriority.IMPORTANT), @@ -155,7 +155,7 @@ void sModifyingArticle(BibDatabaseMode mode) { @ParameterizedTest @EnumSource(BibDatabaseMode.class) - void sModifyingArticleWithParsing(BibDatabaseMode mode) { + void modifyingArticleWithParsing(BibDatabaseMode mode) { overwrittenStandardType = new BibEntryType( StandardEntryType.Article, List.of(new BibField(StandardField.TITLE, FieldPriority.IMPORTANT), @@ -173,7 +173,7 @@ void sModifyingArticleWithParsing(BibDatabaseMode mode) { @ParameterizedTest @EnumSource(BibDatabaseMode.class) - void sModifyingArticleWithParsingKeepsListOrder(BibDatabaseMode mode) { + void modifyingArticleWithParsingKeepsListOrder(BibDatabaseMode mode) { overwrittenStandardType = new BibEntryType( StandardEntryType.Article, List.of(new BibField(StandardField.TITLE, FieldPriority.IMPORTANT), @@ -188,4 +188,10 @@ void sModifyingArticleWithParsingKeepsListOrder(BibDatabaseMode mode) { assertEquals(overwrittenStandardType.getOptionalFields(), type.get().getOptionalFields()); } + + @Test + void translatorDetailOptionalAtArticle() { + BibEntryType entryType = entryTypesManager.enrich(StandardEntryType.Article, BibDatabaseMode.BIBLATEX).get(); + assertTrue(entryType.getDetailOptionalFields().contains(StandardField.TRANSLATOR)); + } } diff --git a/src/test/java/org/jabref/model/entry/types/BiblatexAPAEntryTypeDefinitionsTest.java b/src/test/java/org/jabref/model/entry/types/BiblatexAPAEntryTypeDefinitionsTest.java new file mode 100644 index 000000000000..3ca45fbaa3fe --- /dev/null +++ b/src/test/java/org/jabref/model/entry/types/BiblatexAPAEntryTypeDefinitionsTest.java @@ -0,0 +1,13 @@ +package org.jabref.model.entry.types; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class BiblatexAPAEntryTypeDefinitionsTest { + + @Test + void all() { + assertNotNull(BiblatexAPAEntryTypeDefinitions.ALL); + } +} diff --git a/src/test/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitionsTest.java b/src/test/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitionsTest.java new file mode 100644 index 000000000000..a2511daa9406 --- /dev/null +++ b/src/test/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitionsTest.java @@ -0,0 +1,13 @@ +package org.jabref.model.entry.types; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class BiblatexEntryTypeDefinitionsTest { + + @Test + void all() { + assertNotNull(BiblatexEntryTypeDefinitions.ALL); + } +} diff --git a/src/test/java/org/jabref/model/entry/types/BiblatexSoftwareEntryTypeDefinitionsTest.java b/src/test/java/org/jabref/model/entry/types/BiblatexSoftwareEntryTypeDefinitionsTest.java new file mode 100644 index 000000000000..cb8f1789101d --- /dev/null +++ b/src/test/java/org/jabref/model/entry/types/BiblatexSoftwareEntryTypeDefinitionsTest.java @@ -0,0 +1,12 @@ +package org.jabref.model.entry.types; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class BiblatexSoftwareEntryTypeDefinitionsTest { + @Test + void all() { + assertNotNull(BiblatexSoftwareEntryTypeDefinitions.ALL); + } +} diff --git a/src/test/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitionsTest.java b/src/test/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitionsTest.java new file mode 100644 index 000000000000..05aef2a503fc --- /dev/null +++ b/src/test/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitionsTest.java @@ -0,0 +1,26 @@ +package org.jabref.model.entry.types; + +import org.jabref.model.entry.BibEntryType; +import org.jabref.model.entry.field.StandardField; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class BibtexEntryTypeDefinitionsTest { + + @Test + void all() { + assertNotNull(BibtexEntryTypeDefinitions.ALL); + } + + @Test + void languageContained() { + BibEntryType articleEntryType = BiblatexEntryTypeDefinitions.ALL.stream() + .filter(type -> type.getType().equals(StandardEntryType.Article)) + .findFirst() + .get(); + assertTrue(articleEntryType.getDetailOptionalFields().contains(StandardField.LANGUAGE)); + } +} diff --git a/src/test/java/org/jabref/model/entry/types/IEEETranEntryTypeDefinitionsTest.java b/src/test/java/org/jabref/model/entry/types/IEEETranEntryTypeDefinitionsTest.java new file mode 100644 index 000000000000..2bb58af26b7f --- /dev/null +++ b/src/test/java/org/jabref/model/entry/types/IEEETranEntryTypeDefinitionsTest.java @@ -0,0 +1,12 @@ +package org.jabref.model.entry.types; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class IEEETranEntryTypeDefinitionsTest { + @Test + void all() { + assertNotNull(IEEETranEntryTypeDefinitions.ALL); + } +} diff --git a/src/test/java/org/jabref/model/entry/types/SystematicLiteratureReviewStudyEntryTypeDefinitionsTest.java b/src/test/java/org/jabref/model/entry/types/SystematicLiteratureReviewStudyEntryTypeDefinitionsTest.java new file mode 100644 index 000000000000..553b5125ad92 --- /dev/null +++ b/src/test/java/org/jabref/model/entry/types/SystematicLiteratureReviewStudyEntryTypeDefinitionsTest.java @@ -0,0 +1,12 @@ +package org.jabref.model.entry.types; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class SystematicLiteratureReviewStudyEntryTypeDefinitionsTest { + @Test + void all() { + assertNotNull(SystematicLiteratureReviewStudyEntryTypeDefinitions.ALL); + } +} diff --git a/src/test/resources/pdfs/IEEE/.gitignore b/src/test/resources/pdfs/IEEE/.gitignore new file mode 100644 index 000000000000..0509c5988563 --- /dev/null +++ b/src/test/resources/pdfs/IEEE/.gitignore @@ -0,0 +1,314 @@ +# Created by https://www.toptal.com/developers/gitignore/api/latex +# Edit at https://www.toptal.com/developers/gitignore?templates=latex + +### LaTeX ### +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib + +### LaTeX Patch ### +# LIPIcs / OASIcs +*.vtc + +# glossaries +*.glstex + +# End of https://www.toptal.com/developers/gitignore/api/latex diff --git a/src/test/resources/pdfs/IEEE/extract-references-test.bib b/src/test/resources/pdfs/IEEE/extract-references-test.bib new file mode 100644 index 000000000000..eaddd5e3f667 --- /dev/null +++ b/src/test/resources/pdfs/IEEE/extract-references-test.bib @@ -0,0 +1,8 @@ +@Article{, + title = {JabRef Example for References Parsing}, + file = {:ieee-paper.pdf:PDF}, +} + +@Comment{jabref-meta: databaseType:bibtex;} + +@Comment{jabref-meta: fileDirectory:.;} diff --git a/src/test/resources/pdfs/IEEE/ieee-paper.bib b/src/test/resources/pdfs/IEEE/ieee-paper.bib new file mode 100644 index 000000000000..38533080cc0f --- /dev/null +++ b/src/test/resources/pdfs/IEEE/ieee-paper.bib @@ -0,0 +1,51 @@ +@Article{Alver2007, + author = {Alver, Morten Omholt and Tenn{\o}y, Torodd and Alfredsen, Jo Arve and {\O}ie, Gunvor}, + journal = {Aquacultural engineering}, + title = {Automatic measurement of rotifer Brachionus plicatilis densities in first feeding tanks}, + year = {2007}, + number = {2}, + pages = {115--121}, + volume = {36}, + publisher = {Elsevier}, +} + +@Article{Alver2007a, + author = {Alver, Morten Omholt and others}, + journal = {Aquaculture}, + title = {Estimating larval density in cod (Gadus morhua) first feeding tanks using measurements of feed density and larval growth rates}, + year = {2007}, + number = {1}, + pages = {216--226}, + volume = {268}, + publisher = {Elsevier}, +} + +@InProceedings{Kopp2018, + author = {Kopp, Oliver and Armbruster, Anita and Zimmermann, Olaf}, + booktitle = {ZEUS}, + title = {Markdown Architectural Decision Records: Format and Tool Support}, + year = {2018}, + publisher = {CEUR-WS.org}, +} + +@InProceedings{Kopp2012, + author = {Oliver Kopp and others}, + booktitle = {Business Process Model and Notation}, + title = {{BPMN4TOSCA:} {A} Domain-Specific Language to Model Management Plans for Composite Applications}, + year = {2012}, + publisher = {Springer}, + series = {LNCS}, + volume = {125}, + doi = {10.1007/978-3-642-33155-8_4}, +} + +@InProceedings{Koenig2023, + author = {Simone König and others}, + booktitle = {INDIN}, + title = {{BPMN4Cars}: A Car-Tailored Workflow Engine}, + year = {2023}, + publisher = {IEEE}, + doi = {10.1109/indin51400.2023.10218082}, +} + +@Comment{jabref-meta: databaseType:bibtex;} diff --git a/src/test/resources/pdfs/IEEE/ieee-paper.pdf b/src/test/resources/pdfs/IEEE/ieee-paper.pdf new file mode 100644 index 000000000000..408d16f82545 Binary files /dev/null and b/src/test/resources/pdfs/IEEE/ieee-paper.pdf differ diff --git a/src/test/resources/pdfs/IEEE/ieee-paper.tex b/src/test/resources/pdfs/IEEE/ieee-paper.tex new file mode 100644 index 000000000000..62de1f28d8d2 --- /dev/null +++ b/src/test/resources/pdfs/IEEE/ieee-paper.tex @@ -0,0 +1,39 @@ +\documentclass[conference,a4paper,english]{IEEEtran}[2015/08/26] + +\usepackage{lipsum} +\usepackage{hyperref} +\usepackage[keeplastbox]{flushend} + +\begin{document} +\title{JabRef Example for Reference Parsing} +\author{% + \IEEEauthorblockN{First Author} + \IEEEauthorblockA{University of Examples, Germany\\ + \{lastname\}@example.org} +} + +\maketitle + +\begin{abstract} +\lipsum[1] +\end{abstract} + +\section{Introduction} +\lipsum[2] + +\section{Related Work} +\lipsum[3] +\cite{Alver2007,Alver2007a,Kopp2012,Kopp2018,Koenig2023} + +\section{Contribution} +\lipsum[4-7] + +\section{Conclusion and Outlook} +\lipsum[4] + +\atColsEnd{\vfil} + +\bibliographystyle{IEEEtran} +\bibliography{IEEEabrv,ieee-paper} + +\end{document}