From d710123ad553da1abc231f83eb3b0d46439ceb4d Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Thu, 7 Sep 2023 10:53:21 +0100 Subject: [PATCH 01/94] check total number of export saving order before store the preferences --- CHANGELOG.md | 1 + .../jabref/preferences/JabRefPreferences.java | 44 ++++++++++++++----- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c9f52321f1..4f501311986 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -349,6 +349,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed the shared database opening dialog to remember autosave folder and tick. [#7516](https://github.com/JabRef/jabref/issues/7516) - We fixed an issue where name formatter could not be saved. [#9120](https://github.com/JabRef/jabref/issues/9120) - We fixed a bug where after the export of Preferences, custom exports were duplicated. [#10176](https://github.com/JabRef/jabref/issues/10176) +- we fixed a bug where exception was raised when saving less than 3 export save order in preference. [#10157](https://github.com/JabRef/jabref/issues/10157) ### Removed diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index 129beb48274..21302218fc3 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -2237,11 +2237,17 @@ public ExportPreferences getExportPreferences() { } private SaveOrder getExportSaveOrder() { - List sortCriteria = List.of( - new SaveOrder.SortCriterion(FieldFactory.parseField(get(EXPORT_PRIMARY_SORT_FIELD)), getBoolean(EXPORT_PRIMARY_SORT_DESCENDING)), - new SaveOrder.SortCriterion(FieldFactory.parseField(get(EXPORT_SECONDARY_SORT_FIELD)), getBoolean(EXPORT_SECONDARY_SORT_DESCENDING)), - new SaveOrder.SortCriterion(FieldFactory.parseField(get(EXPORT_TERTIARY_SORT_FIELD)), getBoolean(EXPORT_TERTIARY_SORT_DESCENDING)) - ); + List sortCriteria = new ArrayList<>(); + + if (!get(EXPORT_PRIMARY_SORT_FIELD).equals("")) { + sortCriteria.add(new SaveOrder.SortCriterion(FieldFactory.parseField(get(EXPORT_PRIMARY_SORT_FIELD)), getBoolean(EXPORT_PRIMARY_SORT_DESCENDING))); + } + if (!get(EXPORT_SECONDARY_SORT_FIELD).equals("")) { + sortCriteria.add(new SaveOrder.SortCriterion(FieldFactory.parseField(get(EXPORT_SECONDARY_SORT_FIELD)), getBoolean(EXPORT_SECONDARY_SORT_DESCENDING))); + } + if (!get(EXPORT_TERTIARY_SORT_FIELD).equals("")) { + sortCriteria.add(new SaveOrder.SortCriterion(FieldFactory.parseField(get(EXPORT_TERTIARY_SORT_FIELD)), getBoolean(EXPORT_TERTIARY_SORT_DESCENDING))); + } return new SaveOrder( SaveOrder.OrderType.fromBooleans(getBoolean(EXPORT_IN_SPECIFIED_ORDER), getBoolean(EXPORT_IN_ORIGINAL_ORDER)), @@ -2253,12 +2259,28 @@ private void storeExportSaveOrder(SaveOrder saveOrder) { putBoolean(EXPORT_IN_ORIGINAL_ORDER, saveOrder.getOrderType() == SaveOrder.OrderType.ORIGINAL); putBoolean(EXPORT_IN_SPECIFIED_ORDER, saveOrder.getOrderType() == SaveOrder.OrderType.SPECIFIED); - put(EXPORT_PRIMARY_SORT_FIELD, saveOrder.getSortCriteria().get(0).field.getName()); - put(EXPORT_SECONDARY_SORT_FIELD, saveOrder.getSortCriteria().get(1).field.getName()); - put(EXPORT_TERTIARY_SORT_FIELD, saveOrder.getSortCriteria().get(2).field.getName()); - putBoolean(EXPORT_PRIMARY_SORT_DESCENDING, saveOrder.getSortCriteria().get(0).descending); - putBoolean(EXPORT_SECONDARY_SORT_DESCENDING, saveOrder.getSortCriteria().get(1).descending); - putBoolean(EXPORT_TERTIARY_SORT_DESCENDING, saveOrder.getSortCriteria().get(2).descending); + long saveOrderCount = saveOrder.getSortCriteria().stream().count(); + if (saveOrderCount >= 1) { + put(EXPORT_PRIMARY_SORT_FIELD, saveOrder.getSortCriteria().get(0).field.getName()); + putBoolean(EXPORT_PRIMARY_SORT_DESCENDING, saveOrder.getSortCriteria().get(0).descending); + } else { + put(EXPORT_PRIMARY_SORT_FIELD, ""); + putBoolean(EXPORT_PRIMARY_SORT_DESCENDING, false); + } + if (saveOrderCount >= 2) { + put(EXPORT_SECONDARY_SORT_FIELD, saveOrder.getSortCriteria().get(1).field.getName()); + putBoolean(EXPORT_SECONDARY_SORT_DESCENDING, saveOrder.getSortCriteria().get(1).descending); + } else { + put(EXPORT_SECONDARY_SORT_FIELD, ""); + putBoolean(EXPORT_SECONDARY_SORT_DESCENDING, false); + } + if (saveOrderCount >= 3) { + put(EXPORT_TERTIARY_SORT_FIELD, saveOrder.getSortCriteria().get(2).field.getName()); + putBoolean(EXPORT_TERTIARY_SORT_DESCENDING, saveOrder.getSortCriteria().get(2).descending); + } else { + put(EXPORT_TERTIARY_SORT_FIELD, ""); + putBoolean(EXPORT_TERTIARY_SORT_DESCENDING, false); + } } /** From 26dd5a36819304b08b9282816a45d311e6e262d7 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Thu, 7 Sep 2023 12:29:11 +0100 Subject: [PATCH 02/94] refactor to comply with JabRef code style --- src/main/java/org/jabref/preferences/JabRefPreferences.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index 21302218fc3..fde4524c273 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -2239,13 +2239,13 @@ public ExportPreferences getExportPreferences() { private SaveOrder getExportSaveOrder() { List sortCriteria = new ArrayList<>(); - if (!get(EXPORT_PRIMARY_SORT_FIELD).equals("")) { + if (!"".equals(get(EXPORT_PRIMARY_SORT_FIELD))) { sortCriteria.add(new SaveOrder.SortCriterion(FieldFactory.parseField(get(EXPORT_PRIMARY_SORT_FIELD)), getBoolean(EXPORT_PRIMARY_SORT_DESCENDING))); } - if (!get(EXPORT_SECONDARY_SORT_FIELD).equals("")) { + if (!"".equals(get(EXPORT_SECONDARY_SORT_FIELD))) { sortCriteria.add(new SaveOrder.SortCriterion(FieldFactory.parseField(get(EXPORT_SECONDARY_SORT_FIELD)), getBoolean(EXPORT_SECONDARY_SORT_DESCENDING))); } - if (!get(EXPORT_TERTIARY_SORT_FIELD).equals("")) { + if (!"".equals(get(EXPORT_TERTIARY_SORT_FIELD))) { sortCriteria.add(new SaveOrder.SortCriterion(FieldFactory.parseField(get(EXPORT_TERTIARY_SORT_FIELD)), getBoolean(EXPORT_TERTIARY_SORT_DESCENDING))); } From 7c6d61d5840f0b49b2490be1433545ecad3b3c2c Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 9 Sep 2023 00:22:34 +0200 Subject: [PATCH 03/94] Add initial usage of heylogs --- .github/workflows/tests.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b51c88d9a8f..f26de20a760 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -73,6 +73,14 @@ jobs: with: args: CHANGELOG.md CONTRIBUTING.md README.md config: '.markdownlint.yml' + - name: Lint CHANGELOG.md + run: | + set -e + curl -Ls https://sh.jbang.dev | bash -s - app setup + source ~/.zshrc + jbang https://raw.githubusercontent.com/nbbrd/jbang-catalog/master/heylogs.java check CHANGELOG.md > build/heylogs.txt + # We have 7 "valid" issues in CHANGELOG.md + awk '$0 ~ "7 problems" { exit 1; }' < build/heylogs.txt tests: name: Unit tests runs-on: ubuntu-latest From 65c8073943f8afceb49d858dd60159738ab2737f Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 9 Sep 2023 00:28:47 +0200 Subject: [PATCH 04/94] Add separate job for markdown checks --- .github/workflows/tests.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f26de20a760..f748ffef778 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -68,6 +68,15 @@ jobs: # enable failing of this task if modernizer complains sed -i "s/failOnViolations = false/failOnViolations = true/" build.gradle ./gradlew modernizer + markdown-checks: + name: Markdown style check + runs-on: ubuntu-latest + steps: + - name: Checkout source + uses: actions/checkout@v4 + with: + submodules: 'false' + show-progress: 'false' - name: Run markdown-lint uses: avto-dev/markdown-lint@v1 with: From 6d82db7ab7e129aa66ff89835ae33c1164c8c9ab Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 9 Sep 2023 00:30:49 +0200 Subject: [PATCH 05/94] Add some debug (and fix paths) --- .github/workflows/tests.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f748ffef778..07c0fd8e406 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -87,9 +87,10 @@ jobs: set -e curl -Ls https://sh.jbang.dev | bash -s - app setup source ~/.zshrc - jbang https://raw.githubusercontent.com/nbbrd/jbang-catalog/master/heylogs.java check CHANGELOG.md > build/heylogs.txt + jband --version + jbang https://raw.githubusercontent.com/nbbrd/jbang-catalog/master/heylogs.java check CHANGELOG.md > heylogs.txt # We have 7 "valid" issues in CHANGELOG.md - awk '$0 ~ "7 problems" { exit 1; }' < build/heylogs.txt + awk '$0 ~ "7 problems" { exit 1; }' < heylogs.txt tests: name: Unit tests runs-on: ubuntu-latest From 855ff9b596490e440a799137a4097fdab48fa91b Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 9 Sep 2023 00:36:10 +0200 Subject: [PATCH 06/94] Fix typo --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 07c0fd8e406..2e7b6fb20d4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -87,7 +87,7 @@ jobs: set -e curl -Ls https://sh.jbang.dev | bash -s - app setup source ~/.zshrc - jband --version + jbang --version jbang https://raw.githubusercontent.com/nbbrd/jbang-catalog/master/heylogs.java check CHANGELOG.md > heylogs.txt # We have 7 "valid" issues in CHANGELOG.md awk '$0 ~ "7 problems" { exit 1; }' < heylogs.txt From 408e129377d465017a6b8e76e43a51cb81390d59 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 9 Sep 2023 00:43:27 +0200 Subject: [PATCH 07/94] Include minimal heylogs.java (to pin heylogs-cli version) --- .github/heylogs.java | 6 ++++++ .github/workflows/tests.yml | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .github/heylogs.java diff --git a/.github/heylogs.java b/.github/heylogs.java new file mode 100644 index 00000000000..3f70faf52a0 --- /dev/null +++ b/.github/heylogs.java @@ -0,0 +1,6 @@ +//DEPS com.github.nbbrd.heylogs:heylogs-cli:0.6.0 +public class heylogs { + public static void main(String... args) throws Exception { + nbbrd.heylogs.cli.HeylogsCommand.main(args); + } +} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2e7b6fb20d4..a0d222d4705 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -87,8 +87,7 @@ jobs: set -e curl -Ls https://sh.jbang.dev | bash -s - app setup source ~/.zshrc - jbang --version - jbang https://raw.githubusercontent.com/nbbrd/jbang-catalog/master/heylogs.java check CHANGELOG.md > heylogs.txt + jbang .github/heylogs.java check CHANGELOG.md > heylogs.txt # We have 7 "valid" issues in CHANGELOG.md awk '$0 ~ "7 problems" { exit 1; }' < heylogs.txt tests: From 9135e168e4085a2855c60981dc021a9633807a70 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 9 Sep 2023 00:48:13 +0200 Subject: [PATCH 08/94] Output report --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a0d222d4705..0251ef8b473 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -88,6 +88,7 @@ jobs: curl -Ls https://sh.jbang.dev | bash -s - app setup source ~/.zshrc jbang .github/heylogs.java check CHANGELOG.md > heylogs.txt + cat heylogs.txt # We have 7 "valid" issues in CHANGELOG.md awk '$0 ~ "7 problems" { exit 1; }' < heylogs.txt tests: From f704bb242e4bd413ed8bbdf421a8f6f215869715 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 9 Sep 2023 00:57:43 +0200 Subject: [PATCH 09/94] Simple grep is enough? --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0251ef8b473..2819ae4fe7d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -90,7 +90,7 @@ jobs: jbang .github/heylogs.java check CHANGELOG.md > heylogs.txt cat heylogs.txt # We have 7 "valid" issues in CHANGELOG.md - awk '$0 ~ "7 problems" { exit 1; }' < heylogs.txt + grep -q "7 problems" heylogs.txt || exit 1 tests: name: Unit tests runs-on: ubuntu-latest From e7fbad223c0253b79f36f567514fdff4b2052cd4 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 9 Sep 2023 01:01:13 +0200 Subject: [PATCH 10/94] Fix linting issue in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e6690bba86..c15b8da75e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - It is possible again to use "current table sort order" for the order of entries when saving. [#9869](https://github.com/JabRef/jabref/issues/9869) - Passwords can be stored in GNOME key ring. [#10274](https://github.com/JabRef/jabref/issues/10274) -- We fixed an issue were groups based on an aux file could not be created due to an exception [#10350](https://github.com/JabRef/jabref/issues/10352) +- We fixed an issue where groups based on an aux file could not be created due to an exception [#10350](https://github.com/JabRef/jabref/issues/10350) ### Removed From 2b150c40602b0d7acfc277390cad50d69d32aa6b Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 9 Sep 2023 01:16:11 +0200 Subject: [PATCH 11/94] Fix casing "macOS" and "JabRef", vendor: JabRef e.V. --- .github/workflows/deployment-arm64.yml | 20 +++++++------- .github/workflows/deployment.yml | 36 +++++++++++++------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index 9105597c42f..f4bba52a273 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -58,13 +58,13 @@ jobs: - name: Run GitVersion id: gitversion uses: gittools/actions/gitversion/execute@v0.10.2 - - name: Set up JDK + - name: Setup JDK uses: actions/setup-java@v3 with: java-version: 20 distribution: 'temurin' cache: 'gradle' - - name: setup jdk JabRef-fix mac + - name: Setup JDK jabref-fix macOS shell: bash run: | mkdir ${{runner.temp}}/jdk @@ -81,7 +81,7 @@ jobs: - name: Clean up keychain run: | security delete-keychain signing_temp.keychain ${{runner.temp}}/keychain/notarization.keychain || true - - name: Setup OSX key chain on OSX + - name: Setup OSX key chain on macOS uses: apple-actions/import-codesign-certs@v2 with: p12-file-base64: ${{ secrets.OSX_SIGNING_CERT }} @@ -99,9 +99,9 @@ jobs: mkdir ${{runner.temp}}/keychain security create-keychain -p jabref ${{runner.temp}}/keychain/notarization.keychain security set-keychain-settings ${{runner.temp}}/keychain/notarization.keychain - - name: Prepare merged jars and modules dir (macos) + - name: Prepare merged jars and modules dir (macOS) run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" prepareModulesDir - - name: Build dmg (macos) + - name: Build dmg (macOS) shell: bash run: | ${{env.JDK21}}/Contents/Home/bin/jpackage \ @@ -113,8 +113,8 @@ jobs: --app-version ${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }} \ --verbose \ --mac-sign \ - --vendor JabRef \ - --mac-package-identifier Jabref \ + --vendor "JabRef e.V." \ + --mac-package-identifier JabRef \ --mac-package-name JabRef \ --type dmg --mac-signing-key-user-name "JabRef e.V. (6792V39SK3)" \ --mac-package-signing-prefix org.jabref \ @@ -123,7 +123,7 @@ jobs: --resource-dir buildres/mac \ --file-associations buildres/mac/bibtexAssociations.properties \ --jlink-options --bind-services - - name: Build pkg (macos) + - name: Build pkg (macOS) shell: bash run: | ${{env.JDK21}}/Contents/Home/bin/jpackage \ @@ -135,8 +135,8 @@ jobs: --app-version ${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }} \ --verbose \ --mac-sign \ - --vendor JabRef \ - --mac-package-identifier Jabref \ + --vendor "JabRef e.V." \ + --mac-package-identifier JabRef \ --mac-package-name JabRef \ --type pkg --mac-signing-key-user-name "JabRef e.V. (6792V39SK3)" \ --mac-package-signing-prefix org.jabref \ diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 9f0e27108b4..6a1589161ff 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -85,13 +85,13 @@ jobs: - name: Run GitVersion id: gitversion uses: gittools/actions/gitversion/execute@v0.10.2 - - name: Set up JDK + - name: Setup JDK uses: actions/setup-java@v3 with: java-version: 20 distribution: 'temurin' cache: 'gradle' - - name: setup jdk JabRef-fix (windows) + - name: Setup JDK jabref-fix (Windows) if: (matrix.os == 'windows-latest') shell: bash run: | @@ -105,7 +105,7 @@ jobs: cat gradle.properties sed -i "s/JavaLanguageVersion.of(20)/JavaLanguageVersion.of(21)/" build.gradle - - name: setup jdk JabRef-fix (ubuntu) + - name: Setup JDK jabref-fix (ubuntu) if: (matrix.os == 'ubuntu-latest') shell: bash run: | @@ -119,7 +119,7 @@ jobs: cat gradle.properties sed -i "s/JavaLanguageVersion.of(20)/JavaLanguageVersion.of(21)/" build.gradle - - name: setup jdk JabRef-fix (macos) + - name: Setup JDK jabref-fix (macOS) if: (matrix.os == 'macos-latest') shell: bash run: | @@ -134,14 +134,14 @@ jobs: cat gradle.properties sed -i'.bak' -e "s/JavaLanguageVersion.of(20)/JavaLanguageVersion.of(21)/" build.gradle - - name: Setup OSX key chain (macos) + - name: Setup OSX key chain (macOS) if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') uses: apple-actions/import-codesign-certs@v2 with: p12-file-base64: ${{ secrets.OSX_SIGNING_CERT }} p12-password: ${{ secrets.OSX_CERT_PWD }} keychain-password: jabref - - name: Setup OSX key chain on OSX for app id cert (macos) + - name: Setup OSX key chain on OSX for app id cert (macOS) if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') uses: apple-actions/import-codesign-certs@v2 with: @@ -149,7 +149,7 @@ jobs: p12-password: ${{ secrets.OSX_CERT_PWD }} create-keychain: false keychain-password: jabref - - name: Build runtime image (non-macos) + - name: Build runtime image (non-macOS) if: (matrix.os != 'macos-latest') run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" jlinkZip - name: Prepare merged jars and modules dir (macos) @@ -159,7 +159,7 @@ jobs: if: (matrix.os != 'macos-latest') shell: bash run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" jpackage - - name: Build dmg (macos) + - name: Build dmg (macOS) if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') shell: bash run: | @@ -172,8 +172,8 @@ jobs: --app-version ${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }} \ --verbose \ --mac-sign \ - --vendor JabRef \ - --mac-package-identifier Jabref \ + --vendor "JabRef e.V." \ + --mac-package-identifier JabRef \ --mac-package-name JabRef \ --type dmg --mac-signing-key-user-name "JabRef e.V. (6792V39SK3)" \ --mac-package-signing-prefix org.jabref \ @@ -182,7 +182,7 @@ jobs: --resource-dir buildres/mac \ --file-associations buildres/mac/bibtexAssociations.properties \ --jlink-options --bind-services - - name: Build pkg (macos) + - name: Build pkg (macOS) if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') shell: bash run: | @@ -195,8 +195,8 @@ jobs: --app-version ${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }} \ --verbose \ --mac-sign \ - --vendor JabRef \ - --mac-package-identifier Jabref \ + --vendor "JabRef e.V." \ + --mac-package-identifier JabRef \ --mac-package-name JabRef \ --type pkg --mac-signing-key-user-name "JabRef e.V. (6792V39SK3)" \ --mac-package-signing-prefix org.jabref \ @@ -205,7 +205,7 @@ jobs: --resource-dir buildres/mac \ --file-associations buildres/mac/bibtexAssociations.properties \ --jlink-options --bind-services - - name: Package application image (non-macos) + - name: Package application image (non-macOS) if: (matrix.os != 'macos-latest') shell: bash run: ${{ matrix.archivePortable }} @@ -237,13 +237,13 @@ jobs: ssh_options: '-p 9922' src: 'build/distribution/' dest: jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ - - name: Upload to GitHub workflow artifacts store (windows) + - name: Upload to GitHub workflow artifacts store (Windows) if: (matrix.os == 'windows-latest') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) uses: actions/upload-artifact@v3 with: name: JabRef-${{ matrix.displayName }} path: build/distribution - - name: Upload to GitHub workflow artifacts store (macos) + - name: Upload to GitHub workflow artifacts store (macOS) if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) uses: actions/upload-artifact@v3 with: @@ -251,7 +251,7 @@ jobs: name: JabRef-macOS-tbn path: build/distribution notarize: # outsourced in a separate job to be able to rerun if this fails for timeouts - name: Notarize and package Mac OS binaries + name: Notarize and package macOS binaries runs-on: macos-latest needs: [build] if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue') }} @@ -348,7 +348,7 @@ jobs: if: steps.checksecrets.outputs.secretspresent == 'YES' id: gitversion uses: gittools/actions/gitversion/execute@v0.10.2 - - name: Get windows binaries + - name: Get Windows binaries if: steps.checksecrets.outputs.secretspresent == 'YES' uses: actions/download-artifact@master with: From c76046c1040912130d891424da334e9383592fad Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 9 Sep 2023 01:19:44 +0200 Subject: [PATCH 12/94] Reviewdog should ignore heylogs.java --- config/checkstyle/checkstyle_reviewdog.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/checkstyle/checkstyle_reviewdog.xml b/config/checkstyle/checkstyle_reviewdog.xml index 0f5e3ebefa8..510baff905f 100644 --- a/config/checkstyle/checkstyle_reviewdog.xml +++ b/config/checkstyle/checkstyle_reviewdog.xml @@ -15,7 +15,7 @@ - + From 8d4e8d8dd1ec76fe75759ab1d15c6e08b935b215 Mon Sep 17 00:00:00 2001 From: Luggas <127773292+Luggas4you@users.noreply.github.com> Date: Sat, 9 Sep 2023 15:19:44 +0200 Subject: [PATCH 13/94] Replacement localization (#10358) * Change key * Fix L10 * Update src/main/resources/l10n/JabRef_en.properties Co-authored-by: Christoph --------- Co-authored-by: Christoph --- .../jabref/gui/exporter/WriteMetadataToLinkedPdfsAction.java | 2 +- src/main/resources/l10n/JabRef_en.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/exporter/WriteMetadataToLinkedPdfsAction.java b/src/main/java/org/jabref/gui/exporter/WriteMetadataToLinkedPdfsAction.java index fb6945d2b85..95f8eb2c7d0 100644 --- a/src/main/java/org/jabref/gui/exporter/WriteMetadataToLinkedPdfsAction.java +++ b/src/main/java/org/jabref/gui/exporter/WriteMetadataToLinkedPdfsAction.java @@ -164,7 +164,7 @@ protected Void call() throws Exception { skipped++; } else { for (Path file : files) { - updateMessage(Localization.lang("Writing metadata to {}", file.getFileName())); + updateMessage(Localization.lang("Writing metadata to %0", file.getFileName())); if (Files.exists(file)) { try { diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 54fb6d7fdfb..2c9d4162851 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2579,4 +2579,4 @@ Failed\ to\ download\ from\ URL=Failed to download from URL Finished=Finished Finished\ writing\ metadata\ for\ library\ %0\ (%1\ succeeded,\ %2\ skipped,\ %3\ errors).=Finished writing metadata for library %0 (%1 succeeded, %2 skipped, %3 errors). Processing...=Processing... -Writing\ metadata\ to\ {}=Writing metadata to {} +Writing\ metadata\ to\ %0=Writing metadata to %0 From 367eb9f74467088dfe11153ce8978e174ab98ae1 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Sat, 9 Sep 2023 17:18:05 +0100 Subject: [PATCH 14/94] move item to unreleased section in changelog.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f501311986..a798f998e11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - It is possible again to use "current table sort order" for the order of entries when saving. [#9869](https://github.com/JabRef/jabref/issues/9869) - Passwords can be stored in GNOME key ring. [#10274](https://github.com/JabRef/jabref/issues/10274) +- we fixed a bug where exception was raised when saving less than 3 export save order in preference. [#10157](https://github.com/JabRef/jabref/issues/10157) ### Removed @@ -349,7 +350,6 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed the shared database opening dialog to remember autosave folder and tick. [#7516](https://github.com/JabRef/jabref/issues/7516) - We fixed an issue where name formatter could not be saved. [#9120](https://github.com/JabRef/jabref/issues/9120) - We fixed a bug where after the export of Preferences, custom exports were duplicated. [#10176](https://github.com/JabRef/jabref/issues/10176) -- we fixed a bug where exception was raised when saving less than 3 export save order in preference. [#10157](https://github.com/JabRef/jabref/issues/10157) ### Removed From 1cb4e160af80bf7af9339c4cf8d90f388bfa3de3 Mon Sep 17 00:00:00 2001 From: Allan Yip Date: Sat, 9 Sep 2023 17:42:30 +0100 Subject: [PATCH 15/94] replace getSortCriteria().stream.count() to getSortCriteria().size() --- src/main/java/org/jabref/preferences/JabRefPreferences.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index fde4524c273..ba88549b5b0 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -2259,7 +2259,7 @@ private void storeExportSaveOrder(SaveOrder saveOrder) { putBoolean(EXPORT_IN_ORIGINAL_ORDER, saveOrder.getOrderType() == SaveOrder.OrderType.ORIGINAL); putBoolean(EXPORT_IN_SPECIFIED_ORDER, saveOrder.getOrderType() == SaveOrder.OrderType.SPECIFIED); - long saveOrderCount = saveOrder.getSortCriteria().stream().count(); + long saveOrderCount = saveOrder.getSortCriteria().size(); if (saveOrderCount >= 1) { put(EXPORT_PRIMARY_SORT_FIELD, saveOrder.getSortCriteria().get(0).field.getName()); putBoolean(EXPORT_PRIMARY_SORT_DESCENDING, saveOrder.getSortCriteria().get(0).descending); From b37036b744bf050bd56b67195fa4439ff01af14e Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 9 Sep 2023 19:27:35 +0200 Subject: [PATCH 16/94] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46e2e455a4d..ca90aba82bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - It is possible again to use "current table sort order" for the order of entries when saving. [#9869](https://github.com/JabRef/jabref/issues/9869) - Passwords can be stored in GNOME key ring. [#10274](https://github.com/JabRef/jabref/issues/10274) - we fixed a bug where exception was raised when saving less than 3 export save order in preference. [#10157](https://github.com/JabRef/jabref/issues/10157) -- We fixed an issue were groups based on an aux file could not be created due to an exception [#10350](https://github.com/JabRef/jabref/issues/10352) +- We fixed an issue where groups based on an aux file could not be created due to an exception [#10350](https://github.com/JabRef/jabref/issues/10352) ### Removed From ed543e92cddd650212f20c71007029d96bef8f95 Mon Sep 17 00:00:00 2001 From: Christoph Date: Sun, 10 Sep 2023 17:26:54 +0200 Subject: [PATCH 17/94] Copy browser extension files for mac (#10340) * Copy browser extension files for mac Fixes https://github.com/JabRef/jabref/issues/10308 * fix path for jabref detection * add changelog entry * specify pgk --- .github/workflows/deployment-arm64.yml | 4 ++++ .github/workflows/deployment.yml | 4 ++++ CHANGELOG.md | 3 ++- buildres/mac/jabrefHost.py | 4 ++++ .../chromium/org.jabref.jabref.json | 2 +- .../firefox/org.jabref.jabref.json | 7 ++----- buildres/mac/postinstall | 10 +++++----- 7 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index 9105597c42f..53463dd71f3 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -109,6 +109,8 @@ jobs: --module-path ${{env.JDK21}}/Contents/Home/jmods/:build/jlinkbase/jlinkjars \ --add-modules org.jabref,org.jabref.merged.module \ --dest build/distribution \ + --app-content buildres/mac/jabrefHost.py \ + --app-content buildres/mac/native-messaging-host \ --name JabRef \ --app-version ${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }} \ --verbose \ @@ -131,6 +133,8 @@ jobs: --module-path ${{env.JDK21}}/Contents/Home/jmods/:build/jlinkbase/jlinkjars \ --add-modules org.jabref,org.jabref.merged.module \ --dest build/distribution \ + --app-content buildres/mac/jabrefHost.py \ + --app-content buildres/mac/native-messaging-host \ --name JabRef \ --app-version ${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }} \ --verbose \ diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 9f0e27108b4..31246a1c034 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -168,6 +168,8 @@ jobs: --module-path ${{env.JDK21}}/Contents/Home/jmods/:build/jlinkbase/jlinkjars \ --add-modules org.jabref,org.jabref.merged.module \ --dest build/distribution \ + --app-content buildres/mac/jabrefHost.py \ + --app-content buildres/mac/native-messaging-host \ --name JabRef \ --app-version ${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }} \ --verbose \ @@ -191,6 +193,8 @@ jobs: --module-path ${{env.JDK21}}/Contents/Home/jmods/:build/jlinkbase/jlinkjars \ --add-modules org.jabref,org.jabref.merged.module \ --dest build/distribution \ + --app-content buildres/mac/jabrefHost.py \ + --app-content buildres/mac/native-messaging-host \ --name JabRef \ --app-version ${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }} \ --verbose \ diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e6690bba86..c547305feec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,8 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - It is possible again to use "current table sort order" for the order of entries when saving. [#9869](https://github.com/JabRef/jabref/issues/9869) - Passwords can be stored in GNOME key ring. [#10274](https://github.com/JabRef/jabref/issues/10274) -- We fixed an issue were groups based on an aux file could not be created due to an exception [#10350](https://github.com/JabRef/jabref/issues/10352) +- We fixed an issue where groups based on an aux file could not be created due to an exception [#10350](https://github.com/JabRef/jabref/issues/10350) +- We fixed an issue where the JabRef browser extension could not communicate with JabRef under macOS due to missing files. You should use the `.pkg` for the first installation as it updates all necessary files for the extension [#10308](https://github.com/JabRef/jabref/issues/10308) ### Removed diff --git a/buildres/mac/jabrefHost.py b/buildres/mac/jabrefHost.py index 85e62f8a791..b9d53de1344 100755 --- a/buildres/mac/jabrefHost.py +++ b/buildres/mac/jabrefHost.py @@ -14,6 +14,10 @@ # Note that the package structure is different when installed as a .app bundle on MacOs, so the path must be altered. script_dir = Path(__file__).resolve().parent.parent JABREF_PATH = script_dir / "bin/JabRef" + +# on mac we must only go one folder upwards +if sys.platform.startswith('darwin'): + script_dir = Path(__file__).resolve().parent if not JABREF_PATH.exists(): JABREF_PATH = script_dir / "MacOS/JabRef" diff --git a/buildres/mac/native-messaging-host/chromium/org.jabref.jabref.json b/buildres/mac/native-messaging-host/chromium/org.jabref.jabref.json index e07d6237487..a075a368409 100644 --- a/buildres/mac/native-messaging-host/chromium/org.jabref.jabref.json +++ b/buildres/mac/native-messaging-host/chromium/org.jabref.jabref.json @@ -1,7 +1,7 @@ { "name": "org.jabref.jabref", "description": "JabRef", - "path": "/Applications/JabRef.app/Contents/Resources/jabrefHost.py", + "path": "/Applications/JabRef.app/Contents/jabrefHost.py", "type": "stdio", "allowed_origins": [ "chrome-extension://bifehkofibaamoeaopjglfkddgkijdlh/", diff --git a/buildres/mac/native-messaging-host/firefox/org.jabref.jabref.json b/buildres/mac/native-messaging-host/firefox/org.jabref.jabref.json index df296d37c84..d6c6541f3d3 100644 --- a/buildres/mac/native-messaging-host/firefox/org.jabref.jabref.json +++ b/buildres/mac/native-messaging-host/firefox/org.jabref.jabref.json @@ -1,10 +1,7 @@ { "name": "org.jabref.jabref", "description": "JabRef", - "path": "/Applications/JabRef.app/Contents/Resources/jabrefHost.py", + "path": "/Applications/JabRef.app/Contents/jabrefHost.py", "type": "stdio", - "allowed_extensions": [ - "browserextension@jabref.org", - "@jabfox" - ] + "allowed_extensions": ["browserextension@jabref.org", "@jabfox"] } diff --git a/buildres/mac/postinstall b/buildres/mac/postinstall index 7fd28171458..9c646441516 100755 --- a/buildres/mac/postinstall +++ b/buildres/mac/postinstall @@ -6,15 +6,15 @@ chmod +r "APP_LOCATION/"*.jar # Trigger an auto-install of the browser addon for chrome/chromium browsers # First create the necessary path, then copy the autoinstall file. install -d /Library/Application\ Support/Google/Chrome/External\ Extensions/ -install -m0644 /Applications/JabRef.app/Contents/Resources/native-messaging-host/chromium/bifehkofibaamoeaopjglfkddgkijdlh.json /Library/Application\ Support/Google/Chrome/External\ Extensions/bifehkofibaamoeaopjglfkddgkijdlh.json +install -m0644 /Applications/JabRef.app/Contents/native-messaging-host/chromium/bifehkofibaamoeaopjglfkddgkijdlh.json /Library/Application\ Support/Google/Chrome/External\ Extensions/bifehkofibaamoeaopjglfkddgkijdlh.json # Install the native-messaging host script for firefox/chrome/chromium install -d /Library/Application\ Support/Mozilla/NativeMessagingHosts/ -install -m0755 /Applications/JabRef.app/Contents/Resources/native-messaging-host/firefox/org.jabref.jabref.json /Library/Application\ Support/Mozilla/NativeMessagingHosts/org.jabref.jabref.json +install -m0755 /Applications/JabRef.app/Contents/native-messaging-host/firefox/org.jabref.jabref.json /Library/Application\ Support/Mozilla/NativeMessagingHosts/org.jabref.jabref.json install -d /Library/Application\ Support/Chromium/NativeMessagingHosts/ -install -m0755 /Applications/JabRef.app/Contents/Resources/native-messaging-host/chromium/org.jabref.jabref.json /Library/Application\ Support/Chromium/NativeMessagingHosts/org.jabref.jabref.json +install -m0755 /Applications/JabRef.app/Contents/native-messaging-host/chromium/org.jabref.jabref.json /Library/Application\ Support/Chromium/NativeMessagingHosts/org.jabref.jabref.json install -d /Library/Google/Chrome/NativeMessagingHosts/ -install -m0755 /Applications/JabRef.app/Contents/Resources/native-messaging-host/chromium/org.jabref.jabref.json /Library/Google/Chrome/NativeMessagingHosts/org.jabref.jabref.json +install -m0755 /Applications/JabRef.app/Contents/native-messaging-host/chromium/org.jabref.jabref.json /Library/Google/Chrome/NativeMessagingHosts/org.jabref.jabref.json install -d /Library/Microsoft/Edge/NativeMessagingHosts/ -install -m0755 /Applications/JabRef.app/Contents/Resources/native-messaging-host/chromium/org.jabref.jabref.json /Library/Microsoft/Edge/NativeMessagingHosts/org.jabref.jabref.json +install -m0755 /Applications/JabRef.app/Contents/native-messaging-host/chromium/org.jabref.jabref.json /Library/Microsoft/Edge/NativeMessagingHosts/org.jabref.jabref.json exit 0 From 122e17b9fb25b87537d2e6840b0ffef732cdbbc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:47:21 +0000 Subject: [PATCH 18/94] Bump org.slf4j:slf4j-api from 2.0.7 to 2.0.9 Bumps org.slf4j:slf4j-api from 2.0.7 to 2.0.9. --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b7acdad747e..97686a42530 100644 --- a/build.gradle +++ b/build.gradle @@ -186,7 +186,7 @@ dependencies { implementation 'org.jsoup:jsoup:1.16.1' implementation 'com.konghq:unirest-java:3.14.5' - implementation 'org.slf4j:slf4j-api:2.0.7' + implementation 'org.slf4j:slf4j-api:2.0.9' implementation "org.tinylog:tinylog-api:2.6.2" implementation "org.tinylog:slf4j-tinylog:2.6.2" implementation "org.tinylog:tinylog-impl:2.6.2" From 42790843cb6d993226909668cb7518ea33e7574a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:47:39 +0000 Subject: [PATCH 19/94] Bump org.mockito:mockito-core from 5.4.0 to 5.5.0 Bumps [org.mockito:mockito-core](https://github.com/mockito/mockito) from 5.4.0 to 5.5.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.4.0...v5.5.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b7acdad747e..deda341bf7f 100644 --- a/build.gradle +++ b/build.gradle @@ -238,7 +238,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' testImplementation 'org.junit.platform:junit-platform-launcher:1.10.0' - testImplementation 'org.mockito:mockito-core:5.4.0' + testImplementation 'org.mockito:mockito-core:5.5.0' testImplementation 'org.xmlunit:xmlunit-core:2.9.1' testImplementation 'org.xmlunit:xmlunit-matchers:2.9.1' testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.1.0' From 98c0f6bf3436ee1891662859b36002327a016454 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:47:52 +0000 Subject: [PATCH 20/94] Bump org.slf4j:jul-to-slf4j from 2.0.7 to 2.0.9 Bumps org.slf4j:jul-to-slf4j from 2.0.7 to 2.0.9. --- updated-dependencies: - dependency-name: org.slf4j:jul-to-slf4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b7acdad747e..9b0b8bbc653 100644 --- a/build.gradle +++ b/build.gradle @@ -192,7 +192,7 @@ dependencies { implementation "org.tinylog:tinylog-impl:2.6.2" // route all requests to java.util.logging to SLF4J (which in turn routes to tinylog) - implementation 'org.slf4j:jul-to-slf4j:2.0.7' + implementation 'org.slf4j:jul-to-slf4j:2.0.9' implementation('de.undercouch:citeproc-java:3.0.0-beta.2') { exclude group: 'org.antlr' From 9055aabeb39cd796a9cf5bacc1df8db8c05579f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:48:07 +0000 Subject: [PATCH 21/94] Bump org.eclipse.jgit:org.eclipse.jgit Bumps org.eclipse.jgit:org.eclipse.jgit from 6.6.0.202305301015-r to 6.7.0.202309050840-r. --- updated-dependencies: - dependency-name: org.eclipse.jgit:org.eclipse.jgit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b7acdad747e..b692e3b6a68 100644 --- a/build.gradle +++ b/build.gradle @@ -146,7 +146,7 @@ dependencies { antlr4 'org.antlr:antlr4:4.13.0' implementation 'org.antlr:antlr4-runtime:4.13.0' - implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '6.6.0.202305301015-r' + implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '6.7.0.202309050840-r' implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.15.2' implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.15.2' From a0e2564806050247bce6056a6c108b87bab315b3 Mon Sep 17 00:00:00 2001 From: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:00:26 +0200 Subject: [PATCH 22/94] Extracted many callbacks to JabRefGUI and Globals, extracted Telemetry class (#10360) * Extracted dialogservice * Extracted call to JabRefGUI * Extracted call to JabRefGUI * Extracted call to JabRefGUI * Fixed some NPEs * Extracted Telemetry out of Globals * Extracted Telemetry out of Globals * Privatize Telemetry class * l10n --- .../jabref/gui/FallbackExceptionHandler.java | 11 ++-- src/main/java/org/jabref/gui/Globals.java | 44 +--------------- src/main/java/org/jabref/gui/JabRefFrame.java | 14 ++++- src/main/java/org/jabref/gui/JabRefGUI.java | 3 +- src/main/java/org/jabref/gui/LibraryTab.java | 21 +++++--- src/main/java/org/jabref/gui/MainMenu.java | 28 +++++----- src/main/java/org/jabref/gui/MainToolBar.java | 2 +- .../org/jabref/gui/SendAsEMailAction.java | 10 +++- .../jabref/gui/SendAsKindleEmailAction.java | 5 +- .../jabref/gui/SendAsStandardEmailAction.java | 6 ++- src/main/java/org/jabref/gui/Telemetry.java | 51 +++++++++++++++++++ .../org/jabref/gui/actions/JabRefAction.java | 8 +-- .../BibtexExtractorViewModel.java | 4 +- .../org/jabref/gui/cleanup/CleanupAction.java | 12 +++-- .../DatabaseChangeDetailsViewFactory.java | 14 ++++- .../collab/DatabaseChangesResolverDialog.java | 6 ++- .../entrychange/EntryChangeDetailsView.java | 14 ++++- .../jabref/gui/copyfiles/CopyFilesAction.java | 11 ++-- .../duplicationFinder/DuplicateSearch.java | 20 ++++++-- .../org/jabref/gui/edit/CopyDoiUrlAction.java | 14 ++--- .../jabref/gui/entryeditor/EntryEditor.java | 10 ++-- .../gui/entryeditor/FieldsEditorTab.java | 10 +++- .../jabref/gui/entryeditor/PreviewTab.java | 8 ++- .../gui/entryeditor/RelatedArticlesTab.java | 10 +++- .../FulltextSearchResultsTab.java | 14 +++-- .../jabref/gui/exporter/ExportCommand.java | 21 ++++++-- .../externalfiles/DownloadFullTextAction.java | 27 ++++------ .../fieldeditors/AbstractEditorViewModel.java | 6 +-- .../gui/fieldeditors/CitationKeyEditor.java | 26 +++++----- .../CitationKeyEditorViewModel.java | 10 +++- .../jabref/gui/fieldeditors/DateEditor.java | 12 +++-- .../gui/fieldeditors/DateEditorViewModel.java | 6 ++- .../EditorTypeEditorViewModel.java | 6 ++- .../jabref/gui/fieldeditors/FieldEditors.java | 38 +++++++------- .../fieldeditors/GenderEditorViewModel.java | 6 ++- .../jabref/gui/fieldeditors/GroupEditor.java | 7 ++- .../jabref/gui/fieldeditors/ISSNEditor.java | 30 ++++++----- .../gui/fieldeditors/ISSNEditorViewModel.java | 7 ++- .../gui/fieldeditors/JournalEditor.java | 32 ++++++------ .../fieldeditors/JournalEditorViewModel.java | 7 ++- .../gui/fieldeditors/KeywordsEditor.java | 7 ++- .../gui/fieldeditors/LinkedEntriesEditor.java | 5 +- .../LinkedEntriesEditorViewModel.java | 6 ++- .../gui/fieldeditors/LinkedFilesEditor.java | 13 ++++- .../LinkedFilesEditorViewModel.java | 7 ++- .../fieldeditors/MapBasedEditorViewModel.java | 6 ++- .../fieldeditors/MonthEditorViewModel.java | 8 +-- .../jabref/gui/fieldeditors/OptionEditor.java | 4 +- .../fieldeditors/OptionEditorViewModel.java | 6 ++- .../jabref/gui/fieldeditors/OwnerEditor.java | 14 +++-- .../fieldeditors/OwnerEditorViewModel.java | 7 ++- .../PaginationEditorViewModel.java | 6 ++- .../PatentTypeEditorViewModel.java | 6 ++- .../gui/fieldeditors/PersonsEditor.java | 11 ++-- .../fieldeditors/PersonsEditorViewModel.java | 6 ++- .../jabref/gui/fieldeditors/SimpleEditor.java | 12 +++-- .../fieldeditors/SimpleEditorViewModel.java | 6 ++- .../gui/fieldeditors/TypeEditorViewModel.java | 6 ++- .../jabref/gui/fieldeditors/UrlEditor.java | 15 ++++-- .../gui/fieldeditors/UrlEditorViewModel.java | 10 +++- .../fieldeditors/YesNoEditorViewModel.java | 6 ++- .../fieldeditors/contextmenu/EditorMenus.java | 7 +-- .../BaseIdentifierEditorViewModel.java | 12 ++++- .../DoiIdentifierEditorViewModel.java | 25 +++++++-- .../EprintIdentifierEditorViewModel.java | 11 +++- .../ISBNIdentifierEditorViewModel.java | 26 ++++++++-- .../identifier/IdentifierEditor.java | 32 ++++++++---- .../jabref/gui/importer/NewEntryAction.java | 4 +- .../importer/actions/OpenDatabaseAction.java | 13 +++-- .../fetcher/LookupIdentifierAction.java | 40 +++++---------- .../fetcher/WebSearchPaneViewModel.java | 4 +- .../jabref/gui/journals/AbbreviateAction.java | 18 +++++-- .../LibraryPropertiesAction.java | 2 +- .../logging/ApplicationInsightsWriter.java | 4 +- .../org/jabref/gui/maintable/MainTable.java | 16 +++--- .../gui/maintable/MainTableColumnFactory.java | 14 +++-- .../gui/maintable/OpenExternalFileAction.java | 22 +++++--- .../gui/maintable/OpenFolderAction.java | 22 +++++--- .../jabref/gui/maintable/RightClickMenu.java | 17 ++++--- .../gui/maintable/columns/FileColumn.java | 16 ++++-- .../gui/mergeentries/FetchAndMergeEntry.java | 21 +++++--- .../MergeWithFetchedEntryAction.java | 19 +++++-- .../gui/openoffice/OpenOfficePanel.java | 27 +++++++++- .../gui/openoffice/StyleSelectDialogView.java | 6 ++- .../gui/preferences/preview/PreviewTab.java | 2 +- .../org/jabref/gui/preview/PreviewPanel.java | 6 ++- .../org/jabref/gui/preview/PreviewViewer.java | 6 ++- .../gui/search/GlobalSearchResultDialog.java | 6 ++- .../RebuildFulltextSearchIndexAction.java | 12 +++-- .../jabref/gui/search/SearchResultsTable.java | 17 ++++--- .../shared/SharedDatabaseLoginDialogView.java | 5 +- .../SharedDatabaseLoginDialogViewModel.java | 16 +++++- .../gui/shared/SharedDatabaseUIManager.java | 9 +++- .../org/jabref/gui/sidepane/SidePane.java | 19 ++++++- .../gui/sidepane/SidePaneContentFactory.java | 17 ++++++- .../gui/sidepane/SidePaneViewModel.java | 11 +++- src/main/resources/l10n/JabRef_en.properties | 3 +- .../LinkedFilesEditorViewModelTest.java | 5 +- .../gui/sidepane/SidePaneViewModelTest.java | 17 ++++++- 99 files changed, 861 insertions(+), 426 deletions(-) create mode 100644 src/main/java/org/jabref/gui/Telemetry.java diff --git a/src/main/java/org/jabref/gui/FallbackExceptionHandler.java b/src/main/java/org/jabref/gui/FallbackExceptionHandler.java index b053a5f9be2..73eaa39e849 100644 --- a/src/main/java/org/jabref/gui/FallbackExceptionHandler.java +++ b/src/main/java/org/jabref/gui/FallbackExceptionHandler.java @@ -2,6 +2,7 @@ import org.jabref.gui.util.DefaultTaskExecutor; +import com.airhacks.afterburner.injection.Injector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,11 +20,9 @@ public static void installExceptionHandler() { @Override public void uncaughtException(Thread thread, Throwable exception) { LOGGER.error("Uncaught exception occurred in " + thread, exception); - - DefaultTaskExecutor.runInJavaFXThread(() -> - JabRefGUI.getMainFrame() - .getDialogService() - .showErrorDialogAndWait("Uncaught exception occurred in " + thread, exception) - ); + DefaultTaskExecutor.runInJavaFXThread(() -> { + DialogService dialogService = Injector.instantiateModelOrService(DialogService.class); + dialogService.showErrorDialogAndWait("Uncaught exception occurred in " + thread, exception); + }); } } diff --git a/src/main/java/org/jabref/gui/Globals.java b/src/main/java/org/jabref/gui/Globals.java index 117361bb72f..c78330180b7 100644 --- a/src/main/java/org/jabref/gui/Globals.java +++ b/src/main/java/org/jabref/gui/Globals.java @@ -1,10 +1,5 @@ package org.jabref.gui; -import java.util.Optional; -import java.util.UUID; - -import javafx.stage.Screen; - import org.jabref.architecture.AllowedToUseAwt; import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.remote.CLIMessageHandler; @@ -19,14 +14,9 @@ import org.jabref.logic.remote.server.RemoteListenerServerManager; import org.jabref.logic.util.BuildInfo; import org.jabref.model.entry.BibEntryTypesManager; -import org.jabref.model.strings.StringUtil; import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.JabRefPreferences; -import com.google.common.base.StandardSystemProperty; -import com.microsoft.applicationinsights.TelemetryClient; -import com.microsoft.applicationinsights.TelemetryConfiguration; -import com.microsoft.applicationinsights.telemetry.SessionState; import kong.unirest.Unirest; /** @@ -77,7 +67,6 @@ public class Globals { private static ThemeManager themeManager; private static DefaultFileUpdateMonitor fileUpdateMonitor; - private static TelemetryClient telemetryClient; private Globals() { } @@ -119,7 +108,7 @@ public static synchronized FileUpdateMonitor getFileUpdateMonitor() { public static void startBackgroundTasks() { // TODO Currently deactivated due to incompatibilities in XML /* if (Globals.prefs.getTelemetryPreferences().shouldCollectTelemetry() && !GraphicsEnvironment.isHeadless()) { - startTelemetryClient(); + Telemetry.start(prefs.getTelemetryPreferences()); } */ RemotePreferences remotePreferences = prefs.getRemotePreferences(); if (remotePreferences.useRemoteServer()) { @@ -127,31 +116,6 @@ public static void startBackgroundTasks() { } } - private static void stopTelemetryClient() { - getTelemetryClient().ifPresent(client -> { - client.trackSessionState(SessionState.End); - client.flush(); - }); - } - - private static void startTelemetryClient() { - TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.getActive(); - if (!StringUtil.isNullOrEmpty(Globals.BUILD_INFO.azureInstrumentationKey)) { - telemetryConfiguration.setInstrumentationKey(Globals.BUILD_INFO.azureInstrumentationKey); - } - telemetryConfiguration.setTrackingIsDisabled(!Globals.prefs.getTelemetryPreferences().shouldCollectTelemetry()); - telemetryClient = new TelemetryClient(telemetryConfiguration); - telemetryClient.getContext().getProperties().put("JabRef version", Globals.BUILD_INFO.version.toString()); - telemetryClient.getContext().getProperties().put("Java version", StandardSystemProperty.JAVA_VERSION.value()); - telemetryClient.getContext().getUser().setId(Globals.prefs.getTelemetryPreferences().getUserId()); - telemetryClient.getContext().getSession().setId(UUID.randomUUID().toString()); - telemetryClient.getContext().getDevice().setOperatingSystem(StandardSystemProperty.OS_NAME.value()); - telemetryClient.getContext().getDevice().setOperatingSystemVersion(StandardSystemProperty.OS_VERSION.value()); - telemetryClient.getContext().getDevice().setScreenResolution(Screen.getPrimary().getVisualBounds().toString()); - - telemetryClient.trackSessionState(SessionState.Start); - } - public static void shutdownThreadPools() { TASK_EXECUTOR.shutdown(); if (fileUpdateMonitor != null) { @@ -161,11 +125,7 @@ public static void shutdownThreadPools() { } public static void stopBackgroundTasks() { - stopTelemetryClient(); + Telemetry.shutdown(); Unirest.shutDown(); } - - public static Optional getTelemetryClient() { - return Optional.ofNullable(telemetryClient); - } } diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 13c14ccfea4..936f4003687 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -565,7 +565,16 @@ public void showLibraryTab(LibraryTab libraryTab) { } public void init() { - sidePane = new SidePane(prefs, Globals.journalAbbreviationRepository, taskExecutor, dialogService, stateManager, undoManager); + sidePane = new SidePane( + this, + prefs, + Globals.journalAbbreviationRepository, + taskExecutor, + dialogService, + stateManager, + fileUpdateMonitor, + entryTypesManager, + undoManager); tabbedPane = new TabPane(); tabbedPane.setTabDragPolicy(TabPane.TabDragPolicy.REORDER); @@ -735,7 +744,8 @@ public void addTab(BibDatabaseContext databaseContext, boolean raisePanel) { stateManager, fileUpdateMonitor, entryTypesManager, - undoManager); + undoManager, + taskExecutor); addTab(libraryTab, raisePanel); } diff --git a/src/main/java/org/jabref/gui/JabRefGUI.java b/src/main/java/org/jabref/gui/JabRefGUI.java index 967b5025a89..0345881c5b7 100644 --- a/src/main/java/org/jabref/gui/JabRefGUI.java +++ b/src/main/java/org/jabref/gui/JabRefGUI.java @@ -220,7 +220,8 @@ private void openDatabases() { Globals.stateManager, Globals.entryTypesManager, fileUpdateMonitor, - mainFrame.getUndoManager()); + mainFrame.getUndoManager(), + Globals.TASK_EXECUTOR); } catch (SQLException | DatabaseNotSupportedException | InvalidDBMSConnectionPropertiesException | diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index cdfea93a054..0ca5bc7d9cf 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -121,7 +121,8 @@ public class LibraryTab extends Tab { private BackgroundTask dataLoadingTask; - private final IndexingTaskManager indexingTaskManager = new IndexingTaskManager(Globals.TASK_EXECUTOR); + private final IndexingTaskManager indexingTaskManager; + private final TaskExecutor taskExecutor; public LibraryTab(BibDatabaseContext bibDatabaseContext, JabRefFrame frame, @@ -130,7 +131,8 @@ public LibraryTab(BibDatabaseContext bibDatabaseContext, StateManager stateManager, FileUpdateMonitor fileUpdateMonitor, BibEntryTypesManager entryTypesManager, - CountingUndoManager undoManager) { + CountingUndoManager undoManager, + TaskExecutor taskExecutor) { this.frame = Objects.requireNonNull(frame); this.bibDatabaseContext = Objects.requireNonNull(bibDatabaseContext); this.undoManager = undoManager; @@ -139,6 +141,8 @@ public LibraryTab(BibDatabaseContext bibDatabaseContext, this.stateManager = Objects.requireNonNull(stateManager); this.fileUpdateMonitor = fileUpdateMonitor; this.entryTypesManager = entryTypesManager; + this.indexingTaskManager = new IndexingTaskManager(taskExecutor); + this.taskExecutor = taskExecutor; bibDatabaseContext.getDatabase().registerListener(this); bibDatabaseContext.getMetaData().registerListener(this); @@ -511,7 +515,7 @@ private void createMainTable() { Globals.getKeyPrefs(), Globals.getClipboardManager(), entryTypesManager, - Globals.TASK_EXECUTOR, + taskExecutor, fileUpdateMonitor); // Add the listener that binds selection to state manager (TODO: should be replaced by proper JavaFX binding as soon as table is implemented in JavaFX) @@ -775,7 +779,7 @@ public void resetChangeMonitor() { changeMonitor.ifPresent(DatabaseChangeMonitor::unregister); changeMonitor = Optional.of(new DatabaseChangeMonitor(bibDatabaseContext, fileUpdateMonitor, - Globals.TASK_EXECUTOR, + taskExecutor, dialogService, preferencesService, databaseNotificationPane)); @@ -850,7 +854,8 @@ public static LibraryTab createLibraryTab(BackgroundTask dataLoadi stateManager, fileUpdateMonitor, entryTypesManager, - undoManager); + undoManager, + taskExecutor); newTab.setDataLoadingTask(dataLoadingTask); dataLoadingTask.onRunning(newTab::onDatabaseLoadingStarted) @@ -868,7 +873,8 @@ public static LibraryTab createLibraryTab(BibDatabaseContext databaseContext, StateManager stateManager, FileUpdateMonitor fileUpdateMonitor, BibEntryTypesManager entryTypesManager, - UndoManager undoManager) { + UndoManager undoManager, + TaskExecutor taskExecutor) { Objects.requireNonNull(databaseContext); LibraryTab libraryTab = new LibraryTab( @@ -879,7 +885,8 @@ public static LibraryTab createLibraryTab(BibDatabaseContext databaseContext, stateManager, fileUpdateMonitor, entryTypesManager, - (CountingUndoManager) undoManager); + (CountingUndoManager) undoManager, + taskExecutor); return libraryTab; } diff --git a/src/main/java/org/jabref/gui/MainMenu.java b/src/main/java/org/jabref/gui/MainMenu.java index 60d454473ba..28a08a31a0d 100644 --- a/src/main/java/org/jabref/gui/MainMenu.java +++ b/src/main/java/org/jabref/gui/MainMenu.java @@ -141,8 +141,8 @@ private void createMenu() { factory.createMenuItem(StandardActions.IMPORT_INTO_NEW_LIBRARY, new ImportCommand(frame, ImportCommand.ImportMethod.AS_NEW, preferencesService, stateManager, fileUpdateMonitor, taskExecutor, dialogService))), factory.createSubMenu(StandardActions.EXPORT, - factory.createMenuItem(StandardActions.EXPORT_ALL, new ExportCommand(ExportCommand.ExportMethod.EXPORT_ALL, frame, stateManager, dialogService, preferencesService)), - factory.createMenuItem(StandardActions.EXPORT_SELECTED, new ExportCommand(ExportCommand.ExportMethod.EXPORT_SELECTED, frame, stateManager, dialogService, preferencesService)), + factory.createMenuItem(StandardActions.EXPORT_ALL, new ExportCommand(ExportCommand.ExportMethod.EXPORT_ALL, frame, stateManager, dialogService, preferencesService, entryTypesManager, abbreviationRepository, taskExecutor)), + factory.createMenuItem(StandardActions.EXPORT_SELECTED, new ExportCommand(ExportCommand.ExportMethod.EXPORT_SELECTED, frame, stateManager, dialogService, preferencesService, entryTypesManager, abbreviationRepository, taskExecutor)), factory.createMenuItem(StandardActions.SAVE_SELECTED_AS_PLAIN_BIBTEX, new SaveAction(SaveAction.SaveMethod.SAVE_SELECTED, frame, dialogService, preferencesService, stateManager))), new SeparatorMenuItem(), @@ -222,10 +222,10 @@ private void createMenu() { ); quality.getItems().addAll( - factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(frame, dialogService, stateManager, preferencesService)), + factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(frame, dialogService, stateManager, preferencesService, entryTypesManager, taskExecutor)), factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(dialogService, stateManager, preferencesService)), factory.createMenuItem(StandardActions.CHECK_INTEGRITY, new IntegrityCheckAction(frame, preferencesService, dialogService, stateManager, taskExecutor, abbreviationRepository)), - factory.createMenuItem(StandardActions.CLEANUP_ENTRIES, new CleanupAction(frame, preferencesService, dialogService, stateManager)), + factory.createMenuItem(StandardActions.CLEANUP_ENTRIES, new CleanupAction(frame, preferencesService, dialogService, stateManager, taskExecutor)), new SeparatorMenuItem(), @@ -234,22 +234,22 @@ private void createMenu() { new SeparatorMenuItem(), factory.createSubMenu(StandardActions.ABBREVIATE, - factory.createMenuItem(StandardActions.ABBREVIATE_DEFAULT, new AbbreviateAction(StandardActions.ABBREVIATE_DEFAULT, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences())), - factory.createMenuItem(StandardActions.ABBREVIATE_DOTLESS, new AbbreviateAction(StandardActions.ABBREVIATE_DOTLESS, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences())), - factory.createMenuItem(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, new AbbreviateAction(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences()))), + factory.createMenuItem(StandardActions.ABBREVIATE_DEFAULT, new AbbreviateAction(StandardActions.ABBREVIATE_DEFAULT, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor)), + factory.createMenuItem(StandardActions.ABBREVIATE_DOTLESS, new AbbreviateAction(StandardActions.ABBREVIATE_DOTLESS, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor)), + factory.createMenuItem(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, new AbbreviateAction(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor))), - factory.createMenuItem(StandardActions.UNABBREVIATE, new AbbreviateAction(StandardActions.UNABBREVIATE, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences())) + factory.createMenuItem(StandardActions.UNABBREVIATE, new AbbreviateAction(StandardActions.UNABBREVIATE, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor)) ); Menu lookupIdentifiers = factory.createSubMenu(StandardActions.LOOKUP_DOC_IDENTIFIER); for (IdFetcher fetcher : WebFetchers.getIdFetchers(preferencesService.getImportFormatPreferences())) { - LookupIdentifierAction identifierAction = new LookupIdentifierAction<>(frame, fetcher, stateManager, undoManager); + LookupIdentifierAction identifierAction = new LookupIdentifierAction<>(frame, fetcher, stateManager, undoManager, taskExecutor); lookupIdentifiers.getItems().add(factory.createMenuItem(identifierAction.getAction(), identifierAction)); } lookup.getItems().addAll( lookupIdentifiers, - factory.createMenuItem(StandardActions.DOWNLOAD_FULL_TEXT, new DownloadFullTextAction(dialogService, stateManager, preferencesService)), + factory.createMenuItem(StandardActions.DOWNLOAD_FULL_TEXT, new DownloadFullTextAction(dialogService, stateManager, preferencesService, taskExecutor)), new SeparatorMenuItem(), @@ -267,7 +267,7 @@ private void createMenu() { factory.createMenuItem(StandardActions.WRITE_METADATA_TO_PDF, new WriteMetadataToLinkedPdfsAction(dialogService, preferencesService.getFieldPreferences(), preferencesService.getFilePreferences(), preferencesService.getXmpPreferences(), entryTypesManager, abbreviationRepository, taskExecutor, stateManager)), - factory.createMenuItem(StandardActions.COPY_LINKED_FILES, new CopyFilesAction(dialogService, preferencesService, stateManager)), + factory.createMenuItem(StandardActions.COPY_LINKED_FILES, new CopyFilesAction(dialogService, preferencesService, stateManager, taskExecutor)), new SeparatorMenuItem(), @@ -283,7 +283,7 @@ private void createMenu() { new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.REBUILD_FULLTEXT_SEARCH_INDEX, new RebuildFulltextSearchIndexAction(stateManager, frame::getCurrentLibraryTab, dialogService, preferencesService.getFilePreferences())) + factory.createMenuItem(StandardActions.REBUILD_FULLTEXT_SEARCH_INDEX, new RebuildFulltextSearchIndexAction(stateManager, frame::getCurrentLibraryTab, dialogService, preferencesService.getFilePreferences(), taskExecutor)) ); SidePaneType webSearchPane = SidePaneType.WEB_SEARCH; SidePaneType groupsPane = SidePaneType.GROUPS; @@ -352,8 +352,8 @@ private Menu createSendSubMenu(ActionFactory factory, PreferencesService preferencesService) { Menu sendMenu = factory.createMenu(StandardActions.SEND); sendMenu.getItems().addAll( - factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new SendAsStandardEmailAction(dialogService, preferencesService, stateManager, entryTypesManager)), - factory.createMenuItem(StandardActions.SEND_TO_KINDLE, new SendAsKindleEmailAction(dialogService, preferencesService, stateManager)) + factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new SendAsStandardEmailAction(dialogService, preferencesService, stateManager, entryTypesManager, taskExecutor)), + factory.createMenuItem(StandardActions.SEND_TO_KINDLE, new SendAsKindleEmailAction(dialogService, preferencesService, stateManager, taskExecutor)) ); return sendMenu; diff --git a/src/main/java/org/jabref/gui/MainToolBar.java b/src/main/java/org/jabref/gui/MainToolBar.java index 502e7d47447..1e89db1b724 100644 --- a/src/main/java/org/jabref/gui/MainToolBar.java +++ b/src/main/java/org/jabref/gui/MainToolBar.java @@ -126,7 +126,7 @@ private void createToolBar() { new HBox( pushToApplicationButton, factory.createIconButton(StandardActions.GENERATE_CITE_KEYS, new GenerateCitationKeyAction(frame, dialogService, stateManager, taskExecutor, preferencesService)), - factory.createIconButton(StandardActions.CLEANUP_ENTRIES, new CleanupAction(frame, preferencesService, dialogService, stateManager))), + factory.createIconButton(StandardActions.CLEANUP_ENTRIES, new CleanupAction(frame, preferencesService, dialogService, stateManager, taskExecutor))), new Separator(Orientation.VERTICAL), diff --git a/src/main/java/org/jabref/gui/SendAsEMailAction.java b/src/main/java/org/jabref/gui/SendAsEMailAction.java index f8a9bba6ad6..fad6fe8aa7c 100644 --- a/src/main/java/org/jabref/gui/SendAsEMailAction.java +++ b/src/main/java/org/jabref/gui/SendAsEMailAction.java @@ -12,6 +12,7 @@ import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.desktop.JabRefDesktop; import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.io.FileUtil; import org.jabref.model.database.BibDatabaseContext; @@ -38,11 +39,16 @@ public abstract class SendAsEMailAction extends SimpleCommand { private final DialogService dialogService; private final PreferencesService preferencesService; private final StateManager stateManager; + private final TaskExecutor taskExecutor; - public SendAsEMailAction(DialogService dialogService, PreferencesService preferencesService, StateManager stateManager) { + public SendAsEMailAction(DialogService dialogService, + PreferencesService preferencesService, + StateManager stateManager, + TaskExecutor taskExecutor) { this.dialogService = dialogService; this.preferencesService = preferencesService; this.stateManager = stateManager; + this.taskExecutor = taskExecutor; } @Override @@ -54,7 +60,7 @@ public void execute() { LOGGER.warn(message, e); dialogService.notify(message); }) - .executeWith(Globals.TASK_EXECUTOR); + .executeWith(taskExecutor); } private String sendEmail() throws Exception { diff --git a/src/main/java/org/jabref/gui/SendAsKindleEmailAction.java b/src/main/java/org/jabref/gui/SendAsKindleEmailAction.java index 113f6cf3c50..f96944a4fa2 100644 --- a/src/main/java/org/jabref/gui/SendAsKindleEmailAction.java +++ b/src/main/java/org/jabref/gui/SendAsKindleEmailAction.java @@ -1,6 +1,7 @@ package org.jabref.gui; import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; import org.jabref.preferences.PreferencesService; @@ -11,8 +12,8 @@ public class SendAsKindleEmailAction extends SendAsEMailAction { private final PreferencesService preferencesService; - public SendAsKindleEmailAction(DialogService dialogService, PreferencesService preferencesService, StateManager stateManager) { - super(dialogService, preferencesService, stateManager); + public SendAsKindleEmailAction(DialogService dialogService, PreferencesService preferencesService, StateManager stateManager, TaskExecutor taskExecutor) { + super(dialogService, preferencesService, stateManager, taskExecutor); this.preferencesService = preferencesService; this.executable.bind(ActionHelper.needsEntriesSelected(stateManager).and(ActionHelper.hasLinkedFileForSelectedEntries(stateManager))); } diff --git a/src/main/java/org/jabref/gui/SendAsStandardEmailAction.java b/src/main/java/org/jabref/gui/SendAsStandardEmailAction.java index c62fa266c33..a60fb256820 100644 --- a/src/main/java/org/jabref/gui/SendAsStandardEmailAction.java +++ b/src/main/java/org/jabref/gui/SendAsStandardEmailAction.java @@ -5,6 +5,7 @@ import java.util.List; import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.bibtex.BibEntryWriter; import org.jabref.logic.bibtex.FieldWriter; import org.jabref.logic.exporter.BibWriter; @@ -30,8 +31,9 @@ public class SendAsStandardEmailAction extends SendAsEMailAction { public SendAsStandardEmailAction(DialogService dialogService, PreferencesService preferencesService, StateManager stateManager, - BibEntryTypesManager entryTypesManager) { - super(dialogService, preferencesService, stateManager); + BibEntryTypesManager entryTypesManager, + TaskExecutor taskExecutor) { + super(dialogService, preferencesService, stateManager, taskExecutor); this.preferencesService = preferencesService; this.stateManager = stateManager; this.entryTypesManager = entryTypesManager; diff --git a/src/main/java/org/jabref/gui/Telemetry.java b/src/main/java/org/jabref/gui/Telemetry.java new file mode 100644 index 00000000000..e68985690b8 --- /dev/null +++ b/src/main/java/org/jabref/gui/Telemetry.java @@ -0,0 +1,51 @@ +package org.jabref.gui; + +import java.util.Optional; +import java.util.UUID; + +import javafx.stage.Screen; + +import org.jabref.logic.util.BuildInfo; +import org.jabref.model.strings.StringUtil; +import org.jabref.preferences.TelemetryPreferences; + +import com.google.common.base.StandardSystemProperty; +import com.microsoft.applicationinsights.TelemetryClient; +import com.microsoft.applicationinsights.TelemetryConfiguration; +import com.microsoft.applicationinsights.telemetry.SessionState; + +public class Telemetry { + private static TelemetryClient telemetryClient; + + private Telemetry() { + } + + public static Optional getTelemetryClient() { + return Optional.ofNullable(telemetryClient); + } + + private static void start(TelemetryPreferences telemetryPreferences, BuildInfo buildInfo) { + TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.getActive(); + if (!StringUtil.isNullOrEmpty(buildInfo.azureInstrumentationKey)) { + telemetryConfiguration.setInstrumentationKey(buildInfo.azureInstrumentationKey); + } + telemetryConfiguration.setTrackingIsDisabled(!telemetryPreferences.shouldCollectTelemetry()); + telemetryClient = new TelemetryClient(telemetryConfiguration); + telemetryClient.getContext().getProperties().put("JabRef version", buildInfo.version.toString()); + telemetryClient.getContext().getProperties().put("Java version", StandardSystemProperty.JAVA_VERSION.value()); + telemetryClient.getContext().getUser().setId(telemetryPreferences.getUserId()); + telemetryClient.getContext().getSession().setId(UUID.randomUUID().toString()); + telemetryClient.getContext().getDevice().setOperatingSystem(StandardSystemProperty.OS_NAME.value()); + telemetryClient.getContext().getDevice().setOperatingSystemVersion(StandardSystemProperty.OS_VERSION.value()); + telemetryClient.getContext().getDevice().setScreenResolution(Screen.getPrimary().getVisualBounds().toString()); + + telemetryClient.trackSessionState(SessionState.Start); + } + + public static void shutdown() { + getTelemetryClient().ifPresent(client -> { + client.trackSessionState(SessionState.End); + client.flush(); + }); + } +} diff --git a/src/main/java/org/jabref/gui/actions/JabRefAction.java b/src/main/java/org/jabref/gui/actions/JabRefAction.java index abf49e3f48a..e20b7650d90 100644 --- a/src/main/java/org/jabref/gui/actions/JabRefAction.java +++ b/src/main/java/org/jabref/gui/actions/JabRefAction.java @@ -4,7 +4,7 @@ import javafx.beans.binding.Bindings; -import org.jabref.gui.Globals; +import org.jabref.gui.Telemetry; import org.jabref.gui.keyboard.KeyBindingRepository; import de.saxsys.mvvmfx.utils.commands.Command; @@ -70,12 +70,12 @@ private String getActionName(Action action, Command command) { } private void trackExecute(String actionName) { - Globals.getTelemetryClient() - .ifPresent(telemetryClient -> telemetryClient.trackEvent(actionName)); + Telemetry.getTelemetryClient() + .ifPresent(telemetryClient -> telemetryClient.trackEvent(actionName)); } private void trackUserActionSource(String actionName, Sources source) { - Globals.getTelemetryClient().ifPresent(telemetryClient -> telemetryClient.trackEvent( + Telemetry.getTelemetryClient().ifPresent(telemetryClient -> telemetryClient.trackEvent( actionName, Map.of("Source", source.toString()), Map.of())); diff --git a/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java b/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java index a8674044ecf..7e58dff849e 100644 --- a/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java +++ b/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java @@ -10,8 +10,8 @@ import javafx.beans.property.StringProperty; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.StateManager; +import org.jabref.gui.Telemetry; import org.jabref.gui.externalfiles.ImportHandler; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.TaskExecutor; @@ -100,6 +100,6 @@ private void parseUsingGrobid() { private void trackNewEntry(BibEntry bibEntry, String eventMessage) { Map properties = new HashMap<>(); properties.put("EntryType", bibEntry.typeProperty().getValue().getName()); - Globals.getTelemetryClient().ifPresent(client -> client.trackEvent(eventMessage, properties, new HashMap<>())); + Telemetry.getTelemetryClient().ifPresent(client -> client.trackEvent(eventMessage, properties, new HashMap<>())); } } diff --git a/src/main/java/org/jabref/gui/cleanup/CleanupAction.java b/src/main/java/org/jabref/gui/cleanup/CleanupAction.java index eff956906e7..653e499d259 100644 --- a/src/main/java/org/jabref/gui/cleanup/CleanupAction.java +++ b/src/main/java/org/jabref/gui/cleanup/CleanupAction.java @@ -4,7 +4,6 @@ import java.util.Optional; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.JabRefFrame; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionHelper; @@ -12,6 +11,7 @@ import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableFieldChange; import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.cleanup.CleanupWorker; import org.jabref.logic.l10n.Localization; import org.jabref.model.FieldChange; @@ -26,15 +26,21 @@ public class CleanupAction extends SimpleCommand { private final PreferencesService preferences; private final DialogService dialogService; private final StateManager stateManager; + private final TaskExecutor taskExecutor; private boolean isCanceled; private int modifiedEntriesCount; - public CleanupAction(JabRefFrame frame, PreferencesService preferences, DialogService dialogService, StateManager stateManager) { + public CleanupAction(JabRefFrame frame, + PreferencesService preferences, + DialogService dialogService, + StateManager stateManager, + TaskExecutor taskExecutor) { this.frame = frame; this.preferences = preferences; this.dialogService = dialogService; this.stateManager = stateManager; + this.taskExecutor = taskExecutor; this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); } @@ -83,7 +89,7 @@ public void execute() { BackgroundTask.wrap(() -> cleanup(stateManager.getActiveDatabase().get(), preset)) .onSuccess(result -> showResults()) - .executeWith(Globals.TASK_EXECUTOR); + .executeWith(taskExecutor); }); } diff --git a/src/main/java/org/jabref/gui/collab/DatabaseChangeDetailsViewFactory.java b/src/main/java/org/jabref/gui/collab/DatabaseChangeDetailsViewFactory.java index d72d0fb0cb8..fddbdd3b973 100644 --- a/src/main/java/org/jabref/gui/collab/DatabaseChangeDetailsViewFactory.java +++ b/src/main/java/org/jabref/gui/collab/DatabaseChangeDetailsViewFactory.java @@ -23,6 +23,7 @@ import org.jabref.gui.collab.stringrename.BibTexStringRenameDetailsView; import org.jabref.gui.preview.PreviewViewer; import org.jabref.gui.theme.ThemeManager; +import org.jabref.gui.util.TaskExecutor; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.preferences.PreferencesService; @@ -35,8 +36,16 @@ public class DatabaseChangeDetailsViewFactory { private final PreferencesService preferencesService; private final BibEntryTypesManager entryTypesManager; private final PreviewViewer previewViewer; + private final TaskExecutor taskExecutor; - public DatabaseChangeDetailsViewFactory(BibDatabaseContext databaseContext, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService, BibEntryTypesManager entryTypesManager, PreviewViewer previewViewer) { + public DatabaseChangeDetailsViewFactory(BibDatabaseContext databaseContext, + DialogService dialogService, + StateManager stateManager, + ThemeManager themeManager, + PreferencesService preferencesService, + BibEntryTypesManager entryTypesManager, + PreviewViewer previewViewer, + TaskExecutor taskExecutor) { this.databaseContext = databaseContext; this.dialogService = dialogService; this.stateManager = stateManager; @@ -44,12 +53,13 @@ public DatabaseChangeDetailsViewFactory(BibDatabaseContext databaseContext, Dial this.preferencesService = preferencesService; this.entryTypesManager = entryTypesManager; this.previewViewer = previewViewer; + this.taskExecutor = taskExecutor; } public DatabaseChangeDetailsView create(DatabaseChange databaseChange) { // TODO: Use Pattern Matching for switch once it's out of preview if (databaseChange instanceof EntryChange entryChange) { - return new EntryChangeDetailsView(entryChange.getOldEntry(), entryChange.getNewEntry(), databaseContext, dialogService, stateManager, themeManager, preferencesService, entryTypesManager, previewViewer); + return new EntryChangeDetailsView(entryChange.getOldEntry(), entryChange.getNewEntry(), databaseContext, dialogService, stateManager, themeManager, preferencesService, entryTypesManager, previewViewer, taskExecutor); } else if (databaseChange instanceof EntryAdd entryAdd) { return new EntryWithPreviewAndSourceDetailsView(entryAdd.getAddedEntry(), databaseContext, preferencesService, entryTypesManager, previewViewer); } else if (databaseChange instanceof EntryDelete entryDelete) { diff --git a/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.java b/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.java index 7dcb21b0bf4..89167f9f828 100644 --- a/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.java +++ b/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.java @@ -19,6 +19,7 @@ import org.jabref.gui.preview.PreviewViewer; import org.jabref.gui.theme.ThemeManager; import org.jabref.gui.util.BaseDialog; +import org.jabref.gui.util.TaskExecutor; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.preferences.PreferencesService; @@ -58,6 +59,7 @@ public class DatabaseChangesResolverDialog extends BaseDialog { @Inject private PreferencesService preferencesService; @Inject private ThemeManager themeManager; @Inject private BibEntryTypesManager entryTypesManager; + @Inject private TaskExecutor taskExecutor; /** * A dialog going through given changes, which are diffs to the provided database. @@ -88,8 +90,8 @@ public DatabaseChangesResolverDialog(List changes, BibDatabaseCo @FXML private void initialize() { - PreviewViewer previewViewer = new PreviewViewer(database, dialogService, preferencesService, stateManager, themeManager); - DatabaseChangeDetailsViewFactory databaseChangeDetailsViewFactory = new DatabaseChangeDetailsViewFactory(database, dialogService, stateManager, themeManager, preferencesService, entryTypesManager, previewViewer); + PreviewViewer previewViewer = new PreviewViewer(database, dialogService, preferencesService, stateManager, themeManager, taskExecutor); + DatabaseChangeDetailsViewFactory databaseChangeDetailsViewFactory = new DatabaseChangeDetailsViewFactory(database, dialogService, stateManager, themeManager, preferencesService, entryTypesManager, previewViewer, taskExecutor); viewModel = new ExternalChangesResolverViewModel(changes, undoManager); diff --git a/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java index 880341921ef..332057548a8 100644 --- a/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java +++ b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java @@ -11,6 +11,7 @@ import org.jabref.gui.collab.DatabaseChangeDetailsView; import org.jabref.gui.preview.PreviewViewer; import org.jabref.gui.theme.ThemeManager; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; @@ -23,14 +24,23 @@ public final class EntryChangeDetailsView extends DatabaseChangeDetailsView { private final PreviewWithSourceTab oldPreviewWithSourcesTab = new PreviewWithSourceTab(); private final PreviewWithSourceTab newPreviewWithSourcesTab = new PreviewWithSourceTab(); - public EntryChangeDetailsView(BibEntry oldEntry, BibEntry newEntry, BibDatabaseContext databaseContext, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService, BibEntryTypesManager entryTypesManager, PreviewViewer previewViewer) { + public EntryChangeDetailsView(BibEntry oldEntry, + BibEntry newEntry, + BibDatabaseContext databaseContext, + DialogService dialogService, + StateManager stateManager, + ThemeManager themeManager, + PreferencesService preferencesService, + BibEntryTypesManager entryTypesManager, + PreviewViewer previewViewer, + TaskExecutor taskExecutor) { Label inJabRef = new Label(Localization.lang("In JabRef")); inJabRef.getStyleClass().add("lib-change-header"); Label onDisk = new Label(Localization.lang("On disk")); onDisk.getStyleClass().add("lib-change-header"); // we need a copy here as we otherwise would set the same entry twice - PreviewViewer previewClone = new PreviewViewer(databaseContext, dialogService, preferencesService, stateManager, themeManager); + PreviewViewer previewClone = new PreviewViewer(databaseContext, dialogService, preferencesService, stateManager, themeManager, taskExecutor); TabPane oldEntryTabPane = oldPreviewWithSourcesTab.getPreviewWithSourceTab(oldEntry, databaseContext, preferencesService, entryTypesManager, previewClone, Localization.lang("Entry Preview")); TabPane newEntryTabPane = newPreviewWithSourcesTab.getPreviewWithSourceTab(newEntry, databaseContext, preferencesService, entryTypesManager, previewViewer, Localization.lang("Entry Preview")); diff --git a/src/main/java/org/jabref/gui/copyfiles/CopyFilesAction.java b/src/main/java/org/jabref/gui/copyfiles/CopyFilesAction.java index 41b47510acd..621a319ca75 100644 --- a/src/main/java/org/jabref/gui/copyfiles/CopyFilesAction.java +++ b/src/main/java/org/jabref/gui/copyfiles/CopyFilesAction.java @@ -7,10 +7,10 @@ import javafx.concurrent.Task; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.StateManager; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.util.DirectoryDialogConfiguration; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; @@ -24,11 +24,16 @@ public class CopyFilesAction extends SimpleCommand { private final DialogService dialogService; private final PreferencesService preferencesService; private final StateManager stateManager; + private final TaskExecutor taskExecutor; - public CopyFilesAction(DialogService dialogService, PreferencesService preferencesService, StateManager stateManager) { + public CopyFilesAction(DialogService dialogService, + PreferencesService preferencesService, + StateManager stateManager, + TaskExecutor taskExecutor) { this.dialogService = dialogService; this.preferencesService = preferencesService; this.stateManager = stateManager; + this.taskExecutor = taskExecutor; this.executable.bind(needsDatabase(stateManager).and(needsEntriesSelected(stateManager))); } @@ -56,7 +61,7 @@ public void execute() { Localization.lang("Copy linked files to folder..."), Localization.lang("Copy linked files to folder..."), exportTask); - Globals.TASK_EXECUTOR.execute(exportTask); + taskExecutor.execute(exportTask); exportTask.setOnSucceeded(e -> showDialog(exportTask.getValue())); }); } diff --git a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java index 29d91ec02d1..0beda09fdf5 100644 --- a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java +++ b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java @@ -16,7 +16,6 @@ import javafx.beans.property.SimpleStringProperty; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.JabRefExecutorService; import org.jabref.gui.JabRefFrame; import org.jabref.gui.LibraryTab; @@ -29,11 +28,13 @@ import org.jabref.gui.undo.UndoableRemoveEntries; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.DefaultTaskExecutor; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.database.DuplicateCheck; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.preferences.PreferencesService; import static org.jabref.gui.actions.ActionHelper.needsDatabase; @@ -53,12 +54,21 @@ public class DuplicateSearch extends SimpleCommand { private final StateManager stateManager; private final PreferencesService prefs; - - public DuplicateSearch(JabRefFrame frame, DialogService dialogService, StateManager stateManager, PreferencesService prefs) { + private final BibEntryTypesManager entryTypesManager; + private final TaskExecutor taskExecutor; + + public DuplicateSearch(JabRefFrame frame, + DialogService dialogService, + StateManager stateManager, + PreferencesService prefs, + BibEntryTypesManager entryTypesManager, + TaskExecutor taskExecutor) { this.frame = frame; this.dialogService = dialogService; this.stateManager = stateManager; this.prefs = prefs; + this.entryTypesManager = entryTypesManager; + this.taskExecutor = taskExecutor; this.executable.bind(needsDatabase(stateManager)); } @@ -83,7 +93,7 @@ public void execute() { JabRefExecutorService.INSTANCE.executeInterruptableTask(() -> searchPossibleDuplicates(entries, database.getMode()), "DuplicateSearcher"); BackgroundTask.wrap(this::verifyDuplicates) .onSuccess(this::handleDuplicates) - .executeWith(Globals.TASK_EXECUTOR); + .executeWith(taskExecutor); } private void searchPossibleDuplicates(List entries, BibDatabaseMode databaseMode) { @@ -96,7 +106,7 @@ private void searchPossibleDuplicates(List entries, BibDatabaseMode da BibEntry first = entries.get(i); BibEntry second = entries.get(j); - if (new DuplicateCheck(Globals.entryTypesManager).isDuplicate(first, second, databaseMode)) { + if (new DuplicateCheck(entryTypesManager).isDuplicate(first, second, databaseMode)) { duplicates.add(Arrays.asList(first, second)); duplicateCountObservable.set(String.valueOf(duplicateCount.incrementAndGet())); } diff --git a/src/main/java/org/jabref/gui/edit/CopyDoiUrlAction.java b/src/main/java/org/jabref/gui/edit/CopyDoiUrlAction.java index 3b836070a69..3ef0e61bd90 100644 --- a/src/main/java/org/jabref/gui/edit/CopyDoiUrlAction.java +++ b/src/main/java/org/jabref/gui/edit/CopyDoiUrlAction.java @@ -4,8 +4,8 @@ import javafx.scene.control.TextArea; +import org.jabref.gui.DialogService; import org.jabref.gui.Globals; -import org.jabref.gui.JabRefGUI; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.actions.StandardActions; import org.jabref.logic.l10n.Localization; @@ -16,12 +16,14 @@ */ public class CopyDoiUrlAction extends SimpleCommand { - private TextArea component; - private StandardActions action; + private final TextArea component; + private final StandardActions action; + private final DialogService dialogService; - public CopyDoiUrlAction(TextArea component, StandardActions action) { + public CopyDoiUrlAction(TextArea component, StandardActions action, DialogService dialogService) { this.component = component; this.action = action; + this.dialogService = dialogService; } @Override @@ -38,9 +40,9 @@ public void execute() { private void copy(Optional urlOptional, String identifier) { if (urlOptional.isPresent()) { Globals.getClipboardManager().setContent(urlOptional.get()); - JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("The link has been copied to the clipboard.")); + dialogService.notify(Localization.lang("The link has been copied to the clipboard.")); } else { - JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("Invalid DOI: '%0'.", identifier)); + dialogService.notify(Localization.lang("Invalid DOI: '%0'.", identifier)); } } } diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index 349f01cded2..6f67d62103a 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -246,7 +246,7 @@ private void navigateToNextEntry() { } private List createTabs() { - entryEditorTabs.add(new PreviewTab(databaseContext, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager())); + entryEditorTabs.add(new PreviewTab(databaseContext, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), taskExecutor)); // Required, optional, deprecated, and "other" fields entryEditorTabs.add(new RequiredFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository)); @@ -281,7 +281,7 @@ private List createTabs() { // "Special" tabs entryEditorTabs.add(new MathSciNetTab()); entryEditorTabs.add(new FileAnnotationTab(libraryTab.getAnnotationCache())); - entryEditorTabs.add(new RelatedArticlesTab(this, entryEditorPreferences, preferencesService, dialogService)); + entryEditorTabs.add(new RelatedArticlesTab(entryEditorPreferences, preferencesService, dialogService, taskExecutor)); sourceTab = new SourceTab( databaseContext, @@ -297,7 +297,7 @@ private List createTabs() { entryEditorTabs.add(new LatexCitationsTab(databaseContext, preferencesService, taskExecutor, dialogService)); - entryEditorTabs.add(new FulltextSearchResultsTab(stateManager, preferencesService, dialogService)); + entryEditorTabs.add(new FulltextSearchResultsTab(stateManager, preferencesService, dialogService, taskExecutor)); return entryEditorTabs; } @@ -312,7 +312,7 @@ private void recalculateVisibleTabs() { // This hack is required since tabbed.getTabs().setAll(visibleTabs) changes the order of the tabs in the editor // First, remove tabs that we do not want to show - List toBeRemoved = tabs.stream().filter(tab -> !tab.shouldShow(entry)).collect(Collectors.toList()); + List toBeRemoved = tabs.stream().filter(tab -> !tab.shouldShow(entry)).toList(); tabbed.getTabs().removeAll(toBeRemoved); // Next add all the visible tabs (if not already present) at the right position @@ -415,7 +415,7 @@ private void setupToolBar() { } private void fetchAndMerge(EntryBasedFetcher fetcher) { - new FetchAndMergeEntry(libraryTab, taskExecutor, preferencesService, dialogService).fetchAndMerge(entry, fetcher); + new FetchAndMergeEntry(libraryTab.getBibDatabaseContext(), taskExecutor, preferencesService, dialogService, undoManager).fetchAndMerge(entry, fetcher); } public void setFocusToField(Field field) { diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java index 8ab2b955eae..780af05c3c4 100644 --- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java @@ -238,7 +238,15 @@ private void initPanel() { scrollPane.setFitToHeight(true); SplitPane container = new SplitPane(scrollPane); - previewPanel = new PreviewPanel(databaseContext, dialogService, preferences.getKeyBindingRepository(), preferences, stateManager, themeManager, indexingTaskManager); + previewPanel = new PreviewPanel( + databaseContext, + dialogService, + preferences.getKeyBindingRepository(), + preferences, + stateManager, + themeManager, + indexingTaskManager, + taskExecutor); EasyBind.subscribe(preferences.getPreviewPreferences().showPreviewAsExtraTabProperty(), show -> { if (show) { container.getItems().remove(previewPanel); diff --git a/src/main/java/org/jabref/gui/entryeditor/PreviewTab.java b/src/main/java/org/jabref/gui/entryeditor/PreviewTab.java index 908349bc0ad..1555f577c45 100644 --- a/src/main/java/org/jabref/gui/entryeditor/PreviewTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/PreviewTab.java @@ -5,6 +5,7 @@ import org.jabref.gui.icon.IconTheme; import org.jabref.gui.preview.PreviewPanel; import org.jabref.gui.theme.ThemeManager; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; import org.jabref.logic.pdf.search.indexing.IndexingTaskManager; import org.jabref.model.database.BibDatabaseContext; @@ -19,6 +20,7 @@ public class PreviewTab extends EntryEditorTab { private final StateManager stateManager; private final ThemeManager themeManager; private final IndexingTaskManager indexingTaskManager; + private final TaskExecutor taskExecutor; private PreviewPanel previewPanel; public PreviewTab(BibDatabaseContext databaseContext, @@ -26,13 +28,15 @@ public PreviewTab(BibDatabaseContext databaseContext, PreferencesService preferences, StateManager stateManager, ThemeManager themeManager, - IndexingTaskManager indexingTaskManager) { + IndexingTaskManager indexingTaskManager, + TaskExecutor taskExecutor) { this.databaseContext = databaseContext; this.dialogService = dialogService; this.preferences = preferences; this.stateManager = stateManager; this.themeManager = themeManager; this.indexingTaskManager = indexingTaskManager; + this.taskExecutor = taskExecutor; setGraphic(IconTheme.JabRefIcons.TOGGLE_ENTRY_PREVIEW.getGraphicNode()); setText(Localization.lang("Preview")); @@ -60,7 +64,7 @@ public boolean shouldShow(BibEntry entry) { @Override protected void bindToEntry(BibEntry entry) { if (previewPanel == null) { - previewPanel = new PreviewPanel(databaseContext, dialogService, preferences.getKeyBindingRepository(), preferences, stateManager, themeManager, indexingTaskManager); + previewPanel = new PreviewPanel(databaseContext, dialogService, preferences.getKeyBindingRepository(), preferences, stateManager, themeManager, indexingTaskManager, taskExecutor); setContent(previewPanel); } diff --git a/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java b/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java index 08047d1aae9..baf22123c35 100644 --- a/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java @@ -22,6 +22,7 @@ import org.jabref.gui.Globals; import org.jabref.gui.desktop.JabRefDesktop; import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.importer.ImportCleanup; import org.jabref.logic.importer.fetcher.MrDLibFetcher; import org.jabref.logic.l10n.Localization; @@ -45,8 +46,13 @@ public class RelatedArticlesTab extends EntryEditorTab { private final EntryEditorPreferences preferences; private final DialogService dialogService; private final PreferencesService preferencesService; + private final TaskExecutor taskExecutor; - public RelatedArticlesTab(EntryEditor entryEditor, EntryEditorPreferences preferences, PreferencesService preferencesService, DialogService dialogService) { + public RelatedArticlesTab(EntryEditorPreferences preferences, + PreferencesService preferencesService, + DialogService dialogService, + TaskExecutor taskExecutor) { + this.taskExecutor = taskExecutor; setText(Localization.lang("Related articles")); setTooltip(new Tooltip(Localization.lang("Related articles"))); this.preferences = preferences; @@ -82,7 +88,7 @@ private StackPane getRelatedArticlesPane(BibEntry entry) { progress.setVisible(false); root.getChildren().add(getErrorInfo()); }) - .executeWith(Globals.TASK_EXECUTOR); + .executeWith(taskExecutor); root.getChildren().add(progress); diff --git a/src/main/java/org/jabref/gui/entryeditor/fileannotationtab/FulltextSearchResultsTab.java b/src/main/java/org/jabref/gui/entryeditor/fileannotationtab/FulltextSearchResultsTab.java index 9732e15b3a4..00f7612f6fb 100644 --- a/src/main/java/org/jabref/gui/entryeditor/fileannotationtab/FulltextSearchResultsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/fileannotationtab/FulltextSearchResultsTab.java @@ -24,6 +24,7 @@ import org.jabref.gui.entryeditor.EntryEditorTab; import org.jabref.gui.maintable.OpenExternalFileAction; import org.jabref.gui.maintable.OpenFolderAction; +import org.jabref.gui.util.TaskExecutor; import org.jabref.gui.util.TooltipTextUtil; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; @@ -45,6 +46,7 @@ public class FulltextSearchResultsTab extends EntryEditorTab { private final PreferencesService preferencesService; private final DialogService dialogService; private final ActionFactory actionFactory; + private final TaskExecutor taskExecutor; private final TextFlow content; @@ -52,11 +54,15 @@ public class FulltextSearchResultsTab extends EntryEditorTab { private DocumentViewerView documentViewerView; - public FulltextSearchResultsTab(StateManager stateManager, PreferencesService preferencesService, DialogService dialogService) { + public FulltextSearchResultsTab(StateManager stateManager, + PreferencesService preferencesService, + DialogService dialogService, + TaskExecutor taskExecutor) { this.stateManager = stateManager; this.preferencesService = preferencesService; this.dialogService = dialogService; this.actionFactory = new ActionFactory(preferencesService.getKeyBindingRepository()); + this.taskExecutor = taskExecutor; content = new TextFlow(); ScrollPane scrollPane = new ScrollPane(content); @@ -154,8 +160,10 @@ private Text createPageLink(int pageNumber) { private ContextMenu getFileContextMenu(LinkedFile file) { ContextMenu fileContextMenu = new ContextMenu(); - fileContextMenu.getItems().add(actionFactory.createMenuItem(StandardActions.OPEN_FOLDER, new OpenFolderAction(dialogService, stateManager, preferencesService, entry, file))); - fileContextMenu.getItems().add(actionFactory.createMenuItem(StandardActions.OPEN_EXTERNAL_FILE, new OpenExternalFileAction(dialogService, stateManager, preferencesService))); + fileContextMenu.getItems().add(actionFactory.createMenuItem( + StandardActions.OPEN_FOLDER, new OpenFolderAction(dialogService, stateManager, preferencesService, entry, file, taskExecutor))); + fileContextMenu.getItems().add(actionFactory.createMenuItem( + StandardActions.OPEN_EXTERNAL_FILE, new OpenExternalFileAction(dialogService, stateManager, preferencesService, taskExecutor))); return fileContextMenu; } diff --git a/src/main/java/org/jabref/gui/exporter/ExportCommand.java b/src/main/java/org/jabref/gui/exporter/ExportCommand.java index 96eebc80160..02e034ae763 100644 --- a/src/main/java/org/jabref/gui/exporter/ExportCommand.java +++ b/src/main/java/org/jabref/gui/exporter/ExportCommand.java @@ -11,7 +11,6 @@ import javafx.util.Duration; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.JabRefFrame; import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; @@ -22,12 +21,15 @@ import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.gui.util.FileFilterConverter; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.exporter.Exporter; import org.jabref.logic.exporter.ExporterFactory; +import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.io.FileUtil; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.preferences.PreferencesService; import org.controlsfx.control.action.Action; @@ -48,17 +50,26 @@ public enum ExportMethod { EXPORT_ALL, EXPORT_SELECTED } private final StateManager stateManager; private final PreferencesService preferences; private final DialogService dialogService; + private final BibEntryTypesManager entryTypesManager; + private final JournalAbbreviationRepository abbreviationRepository; + private final TaskExecutor taskExecutor; public ExportCommand(ExportMethod exportMethod, JabRefFrame frame, StateManager stateManager, DialogService dialogService, - PreferencesService preferences) { + PreferencesService preferences, + BibEntryTypesManager entryTypesManager, + JournalAbbreviationRepository abbreviationRepository, + TaskExecutor taskExecutor) { this.exportMethod = exportMethod; this.frame = frame; this.stateManager = stateManager; this.preferences = preferences; this.dialogService = dialogService; + this.entryTypesManager = entryTypesManager; + this.abbreviationRepository = abbreviationRepository; + this.taskExecutor = taskExecutor; this.executable.bind(exportMethod == ExportMethod.EXPORT_SELECTED ? ActionHelper.needsEntriesSelected(stateManager) @@ -70,7 +81,7 @@ public void execute() { // Get list of exporters and sort before adding to file dialog ExporterFactory exporterFactory = ExporterFactory.create( preferences, - Globals.entryTypesManager); + entryTypesManager); List exporters = exporterFactory.getExporters().stream() .sorted(Comparator.comparing(Exporter::getName)) .collect(Collectors.toList()); @@ -120,7 +131,7 @@ private void export(Path file, FileChooser.ExtensionFilter selectedExtensionFilt file, finEntries, fileDirForDatabase, - Globals.journalAbbreviationRepository); + abbreviationRepository); return null; // can not use BackgroundTask.wrap(Runnable) because Runnable.run() can't throw Exceptions }) .onSuccess(save -> { @@ -139,7 +150,7 @@ private void export(Path file, FileChooser.ExtensionFilter selectedExtensionFilt Duration.seconds(5)); }) .onFailure(this::handleError) - .executeWith(Globals.TASK_EXECUTOR); + .executeWith(taskExecutor); } private void handleError(Exception ex) { diff --git a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java index df950b7e893..321f31bc31f 100644 --- a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java +++ b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java @@ -1,7 +1,6 @@ package org.jabref.gui.externalfiles; import java.net.URL; -import java.nio.file.Path; import java.util.List; import java.util.Map; import java.util.Optional; @@ -10,11 +9,11 @@ import javafx.concurrent.Task; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.fieldeditors.LinkedFileViewModel; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.importer.FulltextFetchers; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; @@ -37,11 +36,16 @@ public class DownloadFullTextAction extends SimpleCommand { private final DialogService dialogService; private final StateManager stateManager; private final PreferencesService preferences; + private final TaskExecutor taskExecutor; - public DownloadFullTextAction(DialogService dialogService, StateManager stateManager, PreferencesService preferences) { + public DownloadFullTextAction(DialogService dialogService, + StateManager stateManager, + PreferencesService preferences, + TaskExecutor taskExecutor) { this.dialogService = dialogService; this.stateManager = stateManager; this.preferences = preferences; + this.taskExecutor = taskExecutor; this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); } @@ -102,7 +106,7 @@ protected Map> call() { Localization.lang("Looking for full text document..."), findFullTextsTask); - Globals.TASK_EXECUTOR.execute(findFullTextsTask); + taskExecutor.execute(findFullTextsTask); } private void downloadFullTexts(Map> downloads, BibDatabaseContext databaseContext) { @@ -110,15 +114,7 @@ private void downloadFullTexts(Map> downloads, BibDataba BibEntry entry = download.getKey(); Optional result = download.getValue(); if (result.isPresent()) { - Optional dir = databaseContext.getFirstExistingFileDir(preferences.getFilePreferences()); - if (dir.isEmpty()) { - dialogService.showErrorDialogAndWait(Localization.lang("Directory not found"), - Localization.lang("Main file directory not set. Check the preferences (\"Linked files\") or modify the library properties (\"Override default file directories\").")); - return; - } - - // Download and link full text - addLinkedFileFromURL(databaseContext, result.get(), entry, dir.get()); + addLinkedFileFromURL(databaseContext, result.get(), entry); } else { dialogService.notify(Localization.lang("No full text document found for entry %0.", entry.getCitationKey().orElse(Localization.lang("undefined")))); @@ -133,9 +129,8 @@ private void downloadFullTexts(Map> downloads, BibDataba * @param databaseContext the active database * @param url the url "key" * @param entry the entry "value" - * @param targetDirectory the target directory for the downloaded file */ - private void addLinkedFileFromURL(BibDatabaseContext databaseContext, URL url, BibEntry entry, Path targetDirectory) { + private void addLinkedFileFromURL(BibDatabaseContext databaseContext, URL url, BibEntry entry) { LinkedFile newLinkedFile = new LinkedFile(url, ""); if (!entry.getFiles().contains(newLinkedFile)) { @@ -143,7 +138,7 @@ private void addLinkedFileFromURL(BibDatabaseContext databaseContext, URL url, B newLinkedFile, entry, databaseContext, - Globals.TASK_EXECUTOR, + taskExecutor, dialogService, preferences); diff --git a/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java index a2d47b08a68..17f16649ba5 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java @@ -8,7 +8,6 @@ import javafx.beans.property.StringProperty; import org.jabref.gui.AbstractViewModel; -import org.jabref.gui.JabRefGUI; import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.gui.undo.UndoableFieldChange; import org.jabref.gui.util.BindingsHelper; @@ -30,12 +29,14 @@ public class AbstractEditorViewModel extends AbstractViewModel { protected StringProperty text = new SimpleStringProperty(""); protected BibEntry entry; private final SuggestionProvider suggestionProvider; + private final UndoManager undoManager; private final CompositeValidator fieldValidator; private EasyObservableValue fieldBinding; - public AbstractEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { + public AbstractEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, UndoManager undoManager) { this.field = field; this.suggestionProvider = suggestionProvider; + this.undoManager = undoManager; this.fieldValidator = new CompositeValidator(); for (ValueChecker checker : fieldCheckers.getForField(field)) { @@ -70,7 +71,6 @@ public void bindToEntry(BibEntry entry) { // check for changes here, otherwise the cursor position is annoyingly reset every few seconds if (!(newValue.trim()).equals(oldValue)) { entry.setField(field, newValue); - UndoManager undoManager = JabRefGUI.getMainFrame().getUndoManager(); undoManager.addEdit(new UndoableFieldChange(entry, field, oldValue, newValue)); } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/CitationKeyEditor.java b/src/main/java/org/jabref/gui/fieldeditors/CitationKeyEditor.java index 9dc9a5db436..6466296e017 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/CitationKeyEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/CitationKeyEditor.java @@ -20,41 +20,41 @@ import org.jabref.preferences.PreferencesService; import com.airhacks.afterburner.views.ViewLoader; +import jakarta.inject.Inject; public class CitationKeyEditor extends HBox implements FieldEditorFX { - private final PreferencesService preferences; @FXML private final CitationKeyEditorViewModel viewModel; @FXML private Button generateCitationKeyButton; @FXML private EditorTextField textField; + @Inject private PreferencesService preferencesService; + @Inject private DialogService dialogService; + @Inject private UndoManager undoManager; + public CitationKeyEditor(Field field, - PreferencesService preferences, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, - BibDatabaseContext databaseContext, - UndoManager undoManager, - DialogService dialogService) { + BibDatabaseContext databaseContext) { + + ViewLoader.view(this) + .root(this) + .load(); - this.preferences = preferences; this.viewModel = new CitationKeyEditorViewModel( field, suggestionProvider, fieldCheckers, - preferences, + preferencesService, databaseContext, undoManager, dialogService); - ViewLoader.view(this) - .root(this) - .load(); - textField.textProperty().bindBidirectional(viewModel.textProperty()); textField.initContextMenu(Collections::emptyList); - new EditorValidator(preferences).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textField); + new EditorValidator(preferencesService).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textField); } public CitationKeyEditorViewModel getViewModel() { @@ -66,7 +66,7 @@ public void bindToEntry(BibEntry entry) { viewModel.bindToEntry(entry); // Configure button to generate citation key - new ActionFactory(preferences.getKeyBindingRepository()) + new ActionFactory(preferencesService.getKeyBindingRepository()) .configureIconButton( StandardActions.GENERATE_CITE_KEY, viewModel.getGenerateCiteKeyCommand(), diff --git a/src/main/java/org/jabref/gui/fieldeditors/CitationKeyEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/CitationKeyEditorViewModel.java index 2ffe4a3fb0d..fd495bd0206 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/CitationKeyEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/CitationKeyEditorViewModel.java @@ -18,8 +18,14 @@ public class CitationKeyEditorViewModel extends AbstractEditorViewModel { private final UndoManager undoManager; private final DialogService dialogService; - public CitationKeyEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, PreferencesService preferencesService, BibDatabaseContext databaseContext, UndoManager undoManager, DialogService dialogService) { - super(field, suggestionProvider, fieldCheckers); + public CitationKeyEditorViewModel(Field field, + SuggestionProvider suggestionProvider, + FieldCheckers fieldCheckers, + PreferencesService preferencesService, + BibDatabaseContext databaseContext, + UndoManager undoManager, + DialogService dialogService) { + super(field, suggestionProvider, fieldCheckers, undoManager); this.preferencesService = preferencesService; this.databaseContext = databaseContext; this.undoManager = undoManager; diff --git a/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java b/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java index 8c25cdc1a97..eb3a39e585e 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java @@ -2,6 +2,8 @@ import java.time.format.DateTimeFormatter; +import javax.swing.undo.UndoManager; + import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.layout.HBox; @@ -14,23 +16,27 @@ import org.jabref.preferences.PreferencesService; import com.airhacks.afterburner.views.ViewLoader; +import jakarta.inject.Inject; public class DateEditor extends HBox implements FieldEditorFX { @FXML private DateEditorViewModel viewModel; @FXML private TemporalAccessorPicker datePicker; - public DateEditor(Field field, DateTimeFormatter dateFormatter, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, PreferencesService preferences) { - this.viewModel = new DateEditorViewModel(field, suggestionProvider, dateFormatter, fieldCheckers); + @Inject private UndoManager undoManager; + @Inject private PreferencesService preferencesService; + public DateEditor(Field field, DateTimeFormatter dateFormatter, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { ViewLoader.view(this) .root(this) .load(); + this.viewModel = new DateEditorViewModel(field, suggestionProvider, dateFormatter, fieldCheckers, undoManager); + datePicker.setStringConverter(viewModel.getDateToStringConverter()); datePicker.getEditor().textProperty().bindBidirectional(viewModel.textProperty()); - new EditorValidator(preferences).configureValidation(viewModel.getFieldValidator().getValidationStatus(), datePicker.getEditor()); + new EditorValidator(preferencesService).configureValidation(viewModel.getFieldValidator().getValidationStatus(), datePicker.getEditor()); } public DateEditorViewModel getViewModel() { diff --git a/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java index d0f5af4d291..d8bc6e37e12 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java @@ -5,6 +5,8 @@ import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAccessor; +import javax.swing.undo.UndoManager; + import javafx.util.StringConverter; import org.jabref.gui.autocompleter.SuggestionProvider; @@ -21,8 +23,8 @@ public class DateEditorViewModel extends AbstractEditorViewModel { private static final Logger LOGGER = LoggerFactory.getLogger(DateEditorViewModel.class); private final DateTimeFormatter dateFormatter; - public DateEditorViewModel(Field field, SuggestionProvider suggestionProvider, DateTimeFormatter dateFormatter, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public DateEditorViewModel(Field field, SuggestionProvider suggestionProvider, DateTimeFormatter dateFormatter, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); this.dateFormatter = dateFormatter; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/EditorTypeEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/EditorTypeEditorViewModel.java index 1de303d3f8d..0bd8999d003 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/EditorTypeEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/EditorTypeEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.logic.l10n.Localization; @@ -12,8 +14,8 @@ public class EditorTypeEditorViewModel extends MapBasedEditorViewModel { private BiMap itemMap = HashBiMap.create(7); - public EditorTypeEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public EditorTypeEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); itemMap.put("editor", Localization.lang("Editor")); itemMap.put("compiler", Localization.lang("Compiler")); diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java index 1ff28bdbb55..5148990b3be 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java @@ -56,52 +56,52 @@ public static FieldEditorFX getForField(final Field field, boolean isMultiLine = FieldFactory.isMultiLineField(field, preferences.getFieldPreferences().getNonWrappableFields()); if (preferences.getTimestampPreferences().getTimestampField().equals(field)) { - return new DateEditor(field, DateTimeFormatter.ofPattern(preferences.getTimestampPreferences().getTimestampFormat()), suggestionProvider, fieldCheckers, preferences); + return new DateEditor(field, DateTimeFormatter.ofPattern(preferences.getTimestampPreferences().getTimestampFormat()), suggestionProvider, fieldCheckers); } else if (fieldProperties.contains(FieldProperty.DATE)) { - return new DateEditor(field, DateTimeFormatter.ofPattern("[uuuu][-MM][-dd]"), suggestionProvider, fieldCheckers, preferences); + return new DateEditor(field, DateTimeFormatter.ofPattern("[uuuu][-MM][-dd]"), suggestionProvider, fieldCheckers); } else if (fieldProperties.contains(FieldProperty.EXTERNAL)) { - return new UrlEditor(field, dialogService, suggestionProvider, fieldCheckers, preferences); + return new UrlEditor(field, suggestionProvider, fieldCheckers); } else if (fieldProperties.contains(FieldProperty.JOURNAL_NAME)) { - return new JournalEditor(field, taskExecutor, dialogService, journalAbbreviationRepository, preferences, suggestionProvider, fieldCheckers); + return new JournalEditor(field, suggestionProvider, fieldCheckers); } else if (fieldProperties.contains(FieldProperty.DOI) || fieldProperties.contains(FieldProperty.EPRINT) || fieldProperties.contains(FieldProperty.ISBN)) { - return new IdentifierEditor(field, taskExecutor, dialogService, suggestionProvider, fieldCheckers, preferences); + return new IdentifierEditor(field, suggestionProvider, fieldCheckers); } else if (field == StandardField.OWNER) { - return new OwnerEditor(field, preferences, suggestionProvider, fieldCheckers); + return new OwnerEditor(field, suggestionProvider, fieldCheckers); } else if (field == StandardField.GROUPS) { - return new GroupEditor(field, suggestionProvider, fieldCheckers, preferences, isMultiLine); + return new GroupEditor(field, suggestionProvider, fieldCheckers, preferences, isMultiLine, undoManager); } else if (fieldProperties.contains(FieldProperty.FILE_EDITOR)) { return new LinkedFilesEditor(field, databaseContext, suggestionProvider, fieldCheckers); } else if (fieldProperties.contains(FieldProperty.YES_NO)) { - return new OptionEditor<>(new YesNoEditorViewModel(field, suggestionProvider, fieldCheckers)); + return new OptionEditor<>(new YesNoEditorViewModel(field, suggestionProvider, fieldCheckers, undoManager)); } else if (fieldProperties.contains(FieldProperty.MONTH)) { - return new OptionEditor<>(new MonthEditorViewModel(field, suggestionProvider, databaseContext.getMode(), fieldCheckers)); + return new OptionEditor<>(new MonthEditorViewModel(field, suggestionProvider, databaseContext.getMode(), fieldCheckers, undoManager)); } else if (fieldProperties.contains(FieldProperty.GENDER)) { - return new OptionEditor<>(new GenderEditorViewModel(field, suggestionProvider, fieldCheckers)); + return new OptionEditor<>(new GenderEditorViewModel(field, suggestionProvider, fieldCheckers, undoManager)); } else if (fieldProperties.contains(FieldProperty.EDITOR_TYPE)) { - return new OptionEditor<>(new EditorTypeEditorViewModel(field, suggestionProvider, fieldCheckers)); + return new OptionEditor<>(new EditorTypeEditorViewModel(field, suggestionProvider, fieldCheckers, undoManager)); } else if (fieldProperties.contains(FieldProperty.PAGINATION)) { - return new OptionEditor<>(new PaginationEditorViewModel(field, suggestionProvider, fieldCheckers)); + return new OptionEditor<>(new PaginationEditorViewModel(field, suggestionProvider, fieldCheckers, undoManager)); } else if (fieldProperties.contains(FieldProperty.TYPE)) { if (entryType.equals(IEEETranEntryType.Patent)) { - return new OptionEditor<>(new PatentTypeEditorViewModel(field, suggestionProvider, fieldCheckers)); + return new OptionEditor<>(new PatentTypeEditorViewModel(field, suggestionProvider, fieldCheckers, undoManager)); } else { - return new OptionEditor<>(new TypeEditorViewModel(field, suggestionProvider, fieldCheckers)); + 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.PERSON_NAMES)) { - return new PersonsEditor(field, suggestionProvider, preferences, fieldCheckers, isMultiLine); + return new PersonsEditor(field, suggestionProvider, preferences, fieldCheckers, isMultiLine, undoManager); } else if (StandardField.KEYWORDS == field) { - return new KeywordsEditor(field, suggestionProvider, fieldCheckers, preferences); + return new KeywordsEditor(field, suggestionProvider, fieldCheckers, preferences, undoManager); } else if (field == InternalField.KEY_FIELD) { - return new CitationKeyEditor(field, preferences, suggestionProvider, fieldCheckers, databaseContext, undoManager, dialogService); + return new CitationKeyEditor(field, suggestionProvider, fieldCheckers, databaseContext); } else if (field == StandardField.ISSN) { - return new ISSNEditor(field, suggestionProvider, fieldCheckers, preferences, taskExecutor, dialogService); + return new ISSNEditor(field, suggestionProvider, fieldCheckers); } else { // default - return new SimpleEditor(field, suggestionProvider, fieldCheckers, preferences, isMultiLine); + return new SimpleEditor(field, suggestionProvider, fieldCheckers, preferences, isMultiLine, undoManager); } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/GenderEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/GenderEditorViewModel.java index c324d3fbbcb..fd7300e5e65 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/GenderEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/GenderEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.logic.l10n.Localization; @@ -12,8 +14,8 @@ public class GenderEditorViewModel extends MapBasedEditorViewModel { private final BiMap itemMap = HashBiMap.create(7); - public GenderEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public GenderEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); itemMap.put("sf", Localization.lang("Female name")); itemMap.put("sm", Localization.lang("Male name")); diff --git a/src/main/java/org/jabref/gui/fieldeditors/GroupEditor.java b/src/main/java/org/jabref/gui/fieldeditors/GroupEditor.java index db516806b03..584e9fcd85e 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/GroupEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/GroupEditor.java @@ -3,6 +3,8 @@ import java.util.List; import java.util.Optional; +import javax.swing.undo.UndoManager; + import javafx.scene.input.TransferMode; import org.jabref.gui.DragAndDropDataFormats; @@ -21,8 +23,9 @@ public GroupEditor(final Field field, final SuggestionProvider suggestionProvider, final FieldCheckers fieldCheckers, final PreferencesService preferences, - final boolean isMultiLine) { - super(field, suggestionProvider, fieldCheckers, preferences, isMultiLine); + final boolean isMultiLine, + final UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, preferences, isMultiLine, undoManager); this.setOnDragOver(event -> { if (event.getDragboard().hasContent(DragAndDropDataFormats.GROUP)) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.java b/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.java index 55dea620477..db100d37416 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.control.Button; @@ -15,38 +17,38 @@ import org.jabref.preferences.PreferencesService; import com.airhacks.afterburner.views.ViewLoader; +import jakarta.inject.Inject; public class ISSNEditor extends HBox implements FieldEditorFX { @FXML private ISSNEditorViewModel viewModel; @FXML private EditorTextArea textArea; @FXML private Button journalInfoButton; - private final DialogService dialogService; - private final PreferencesService preferencesService; + + @Inject private DialogService dialogService; + @Inject private PreferencesService preferencesService; + @Inject private UndoManager undoManager; + @Inject private TaskExecutor taskExecutor; public ISSNEditor(Field field, SuggestionProvider suggestionProvider, - FieldCheckers fieldCheckers, - PreferencesService preferences, - TaskExecutor taskExecutor, - DialogService dialogService) { - this.preferencesService = preferences; - this.dialogService = dialogService; + FieldCheckers fieldCheckers) { + + ViewLoader.view(this) + .root(this) + .load(); this.viewModel = new ISSNEditorViewModel( field, suggestionProvider, fieldCheckers, taskExecutor, - dialogService); - - ViewLoader.view(this) - .root(this) - .load(); + dialogService, + undoManager); textArea.textProperty().bindBidirectional(viewModel.textProperty()); textArea.initContextMenu(new DefaultMenu(textArea)); - new EditorValidator(preferences).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textArea); + new EditorValidator(preferencesService).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textArea); } public ISSNEditorViewModel getViewModel() { diff --git a/src/main/java/org/jabref/gui/fieldeditors/ISSNEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/ISSNEditorViewModel.java index 0ce1eb1862d..4784580409d 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/ISSNEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/ISSNEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import javafx.scene.control.Button; import org.jabref.gui.DialogService; @@ -17,8 +19,9 @@ public ISSNEditorViewModel( SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, TaskExecutor taskExecutor, - DialogService dialogService) { - super(field, suggestionProvider, fieldCheckers); + DialogService dialogService, + UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); this.taskExecutor = taskExecutor; this.dialogService = dialogService; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.java b/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.java index 5c12abccd71..4ab6f29f159 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.control.Button; @@ -17,43 +19,43 @@ import org.jabref.preferences.PreferencesService; import com.airhacks.afterburner.views.ViewLoader; +import jakarta.inject.Inject; public class JournalEditor extends HBox implements FieldEditorFX { @FXML private JournalEditorViewModel viewModel; @FXML private EditorTextField textField; @FXML private Button journalInfoButton; - private final DialogService dialogService; - private final PreferencesService preferencesService; + + @Inject private DialogService dialogService; + @Inject private PreferencesService preferencesService; + @Inject private TaskExecutor taskExecutor; + @Inject private JournalAbbreviationRepository abbreviationRepository; + @Inject private UndoManager undoManager; public JournalEditor(Field field, - TaskExecutor taskExecutor, - DialogService dialogService, - JournalAbbreviationRepository journalAbbreviationRepository, - PreferencesService preferences, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { - this.dialogService = dialogService; - this.preferencesService = preferences; + + ViewLoader.view(this) + .root(this) + .load(); this.viewModel = new JournalEditorViewModel( field, suggestionProvider, - journalAbbreviationRepository, + abbreviationRepository, fieldCheckers, taskExecutor, - dialogService); - - ViewLoader.view(this) - .root(this) - .load(); + dialogService, + undoManager); textField.textProperty().bindBidirectional(viewModel.textProperty()); textField.initContextMenu(new DefaultMenu(textField)); AutoCompletionTextInputBinding.autoComplete(textField, viewModel::complete); - new EditorValidator(preferences).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textField); + new EditorValidator(preferencesService).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textField); } public JournalEditorViewModel getViewModel() { diff --git a/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java index 3e6267e1eed..580803cb28b 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import javafx.scene.control.Button; import org.jabref.gui.DialogService; @@ -21,8 +23,9 @@ public JournalEditorViewModel( JournalAbbreviationRepository journalAbbreviationRepository, FieldCheckers fieldCheckers, TaskExecutor taskExecutor, - DialogService dialogService) { - super(field, suggestionProvider, fieldCheckers); + DialogService dialogService, + UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); this.journalAbbreviationRepository = journalAbbreviationRepository; this.taskExecutor = taskExecutor; this.dialogService = dialogService; diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java index 542afde97b1..f11f7d1a362 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.model.entry.field.Field; @@ -10,8 +12,9 @@ public class KeywordsEditor extends SimpleEditor implements FieldEditorFX { public KeywordsEditor(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, - PreferencesService preferences) { - super(field, suggestionProvider, fieldCheckers, preferences); + PreferencesService preferences, + UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, preferences, undoManager); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java index 8c54685b210..3c530084dd3 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java @@ -3,6 +3,8 @@ import java.util.Comparator; import java.util.stream.Collectors; +import javax.swing.undo.UndoManager; + import javafx.beans.binding.Bindings; import javafx.fxml.FXML; import javafx.scene.Node; @@ -45,6 +47,7 @@ public class LinkedEntriesEditor extends HBox implements FieldEditorFX { @Inject private DialogService dialogService; @Inject private ClipBoardManager clipBoardManager; @Inject private KeyBindingRepository keyBindingRepository; + @Inject private UndoManager undoManager; private final LinkedEntriesEditorViewModel viewModel; @@ -53,7 +56,7 @@ public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, Sugg .root(this) .load(); - this.viewModel = new LinkedEntriesEditorViewModel(field, suggestionProvider, databaseContext, fieldCheckers); + this.viewModel = new LinkedEntriesEditorViewModel(field, suggestionProvider, databaseContext, fieldCheckers, undoManager); entryLinkField.setCellFactory(new ViewModelListCellFactory().withText(ParsedEntryLink::getKey)); // Mind the .collect(Collectors.toList()) as the list needs to be mutable diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java index f5d89d80321..1b46b1d6c6b 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import javafx.beans.property.ListProperty; import javafx.beans.property.SimpleListProperty; import javafx.collections.FXCollections; @@ -18,8 +20,8 @@ public class LinkedEntriesEditorViewModel extends AbstractEditorViewModel { private final BibDatabaseContext databaseContext; private final ListProperty linkedEntries; - public LinkedEntriesEditorViewModel(Field field, SuggestionProvider suggestionProvider, BibDatabaseContext databaseContext, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public LinkedEntriesEditorViewModel(Field field, SuggestionProvider suggestionProvider, BibDatabaseContext databaseContext, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); this.databaseContext = databaseContext; linkedEntries = new SimpleListProperty<>(FXCollections.observableArrayList()); diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java index 88aef70a41d..c6dcee9bceb 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java @@ -2,6 +2,8 @@ import java.util.Optional; +import javax.swing.undo.UndoManager; + import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.ObservableList; @@ -76,6 +78,7 @@ public class LinkedFilesEditor extends HBox implements FieldEditorFX { @Inject private BibEntryTypesManager bibEntryTypesManager; @Inject private JournalAbbreviationRepository abbreviationRepository; @Inject private TaskExecutor taskExecutor; + @Inject private UndoManager undoManager; private LinkedFilesEditorViewModel viewModel; @@ -101,7 +104,15 @@ public LinkedFilesEditor(Field field, @FXML private void initialize() { - this.viewModel = new LinkedFilesEditorViewModel(field, suggestionProvider, dialogService, databaseContext, taskExecutor, fieldCheckers, preferencesService); + this.viewModel = new LinkedFilesEditorViewModel( + field, + suggestionProvider, + dialogService, + databaseContext, + taskExecutor, + fieldCheckers, + preferencesService, + undoManager); new ViewModelListCellFactory() .withStringTooltip(LinkedFileViewModel::getDescriptionAndLink) diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java index d95543abc2e..91d0dbf6690 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java @@ -10,6 +10,8 @@ import java.util.Optional; import java.util.stream.Collectors; +import javax.swing.undo.UndoManager; + import javafx.beans.property.BooleanProperty; import javafx.beans.property.ListProperty; import javafx.beans.property.ReadOnlyBooleanProperty; @@ -63,9 +65,10 @@ public LinkedFilesEditorViewModel(Field field, SuggestionProvider suggestionP BibDatabaseContext databaseContext, TaskExecutor taskExecutor, FieldCheckers fieldCheckers, - PreferencesService preferences) { + PreferencesService preferences, + UndoManager undoManager) { - super(field, suggestionProvider, fieldCheckers); + super(field, suggestionProvider, fieldCheckers, undoManager); this.dialogService = dialogService; this.databaseContext = databaseContext; diff --git a/src/main/java/org/jabref/gui/fieldeditors/MapBasedEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/MapBasedEditorViewModel.java index 3f03b961cf3..ada4791afd0 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/MapBasedEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/MapBasedEditorViewModel.java @@ -3,6 +3,8 @@ import java.util.ArrayList; import java.util.List; +import javax.swing.undo.UndoManager; + import javafx.util.StringConverter; import org.jabref.gui.autocompleter.SuggestionProvider; @@ -20,8 +22,8 @@ public abstract class MapBasedEditorViewModel extends OptionEditorViewModel suggestionProvider, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public MapBasedEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); } protected abstract BiMap getItemMap(); diff --git a/src/main/java/org/jabref/gui/fieldeditors/MonthEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/MonthEditorViewModel.java index 0e13805984a..eb3b78a5679 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/MonthEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/MonthEditorViewModel.java @@ -3,6 +3,8 @@ import java.util.Arrays; import java.util.List; +import javax.swing.undo.UndoManager; + import javafx.util.StringConverter; import org.jabref.gui.autocompleter.SuggestionProvider; @@ -15,14 +17,14 @@ public class MonthEditorViewModel extends OptionEditorViewModel { private BibDatabaseMode databaseMode; - public MonthEditorViewModel(Field field, SuggestionProvider suggestionProvider, BibDatabaseMode databaseMode, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public MonthEditorViewModel(Field field, SuggestionProvider suggestionProvider, BibDatabaseMode databaseMode, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); this.databaseMode = databaseMode; } @Override public StringConverter getStringConverter() { - return new StringConverter() { + return new StringConverter<>() { @Override public String toString(Month object) { if (object == null) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/OptionEditor.java b/src/main/java/org/jabref/gui/fieldeditors/OptionEditor.java index 9de9856149b..1907f6a5d2d 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/OptionEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/OptionEditor.java @@ -22,12 +22,12 @@ public class OptionEditor extends HBox implements FieldEditorFX { @FXML private ComboBox comboBox; public OptionEditor(OptionEditorViewModel viewModel) { - this.viewModel = viewModel; - ViewLoader.view(this) .root(this) .load(); + this.viewModel = viewModel; + comboBox.setConverter(viewModel.getStringConverter()); comboBox.setCellFactory(new ViewModelListCellFactory().withText(viewModel::convertToDisplayText)); comboBox.getItems().setAll(viewModel.getItems()); diff --git a/src/main/java/org/jabref/gui/fieldeditors/OptionEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/OptionEditorViewModel.java index c98d7a0d28c..df3f339ca64 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/OptionEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/OptionEditorViewModel.java @@ -2,6 +2,8 @@ import java.util.List; +import javax.swing.undo.UndoManager; + import javafx.util.StringConverter; import org.jabref.gui.autocompleter.SuggestionProvider; @@ -10,8 +12,8 @@ public abstract class OptionEditorViewModel extends AbstractEditorViewModel { - public OptionEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public OptionEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); } public abstract StringConverter getStringConverter(); diff --git a/src/main/java/org/jabref/gui/fieldeditors/OwnerEditor.java b/src/main/java/org/jabref/gui/fieldeditors/OwnerEditor.java index 2c9ba1d1dfb..48992341443 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/OwnerEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/OwnerEditor.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.layout.HBox; @@ -12,27 +14,29 @@ import org.jabref.preferences.PreferencesService; import com.airhacks.afterburner.views.ViewLoader; +import jakarta.inject.Inject; public class OwnerEditor extends HBox implements FieldEditorFX { @FXML private OwnerEditorViewModel viewModel; @FXML private EditorTextArea textArea; + @Inject private PreferencesService preferencesService; + @Inject private UndoManager undoManager; + public OwnerEditor(Field field, - PreferencesService preferences, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { - this.viewModel = new OwnerEditorViewModel(field, suggestionProvider, preferences, fieldCheckers); - ViewLoader.view(this) .root(this) .load(); - textArea.textProperty().bindBidirectional(viewModel.textProperty()); + this.viewModel = new OwnerEditorViewModel(field, suggestionProvider, preferencesService, fieldCheckers, undoManager); + textArea.textProperty().bindBidirectional(viewModel.textProperty()); textArea.initContextMenu(EditorMenus.getNameMenu(textArea)); - new EditorValidator(preferences).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textArea); + new EditorValidator(preferencesService).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textArea); } public OwnerEditorViewModel getViewModel() { diff --git a/src/main/java/org/jabref/gui/fieldeditors/OwnerEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/OwnerEditorViewModel.java index 50173078c3a..a849489ad96 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/OwnerEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/OwnerEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.model.entry.field.Field; @@ -11,8 +13,9 @@ public class OwnerEditorViewModel extends AbstractEditorViewModel { public OwnerEditorViewModel(Field field, SuggestionProvider suggestionProvider, PreferencesService preferences, - FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + FieldCheckers fieldCheckers, + UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); this.preferences = preferences; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/PaginationEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/PaginationEditorViewModel.java index 991f3d2ac96..639d8a36745 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/PaginationEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/PaginationEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.logic.l10n.Localization; @@ -12,8 +14,8 @@ public class PaginationEditorViewModel extends MapBasedEditorViewModel { private BiMap itemMap = HashBiMap.create(7); - public PaginationEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public PaginationEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); itemMap.put("page", Localization.lang("Page")); itemMap.put("column", Localization.lang("Column")); diff --git a/src/main/java/org/jabref/gui/fieldeditors/PatentTypeEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/PatentTypeEditorViewModel.java index 89b586c253c..a49281ecc86 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/PatentTypeEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/PatentTypeEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.logic.l10n.Localization; @@ -12,8 +14,8 @@ public class PatentTypeEditorViewModel extends MapBasedEditorViewModel { private BiMap itemMap = HashBiMap.create(12); - public PatentTypeEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public PatentTypeEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); itemMap.put("patent", Localization.lang("Patent")); itemMap.put("patentde", Localization.lang("German patent")); diff --git a/src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java b/src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java index aa49582bb6b..226f85bcf6a 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import javafx.scene.Parent; import javafx.scene.control.TextInputControl; import javafx.scene.layout.HBox; @@ -21,10 +23,11 @@ public class PersonsEditor extends HBox implements FieldEditorFX { public PersonsEditor(final Field field, final SuggestionProvider suggestionProvider, - final PreferencesService preferences, + final PreferencesService preferencesService, final FieldCheckers fieldCheckers, - final boolean isMultiLine) { - this.viewModel = new PersonsEditorViewModel(field, suggestionProvider, preferences.getAutoCompletePreferences(), fieldCheckers); + final boolean isMultiLine, + final UndoManager undoManager) { + this.viewModel = new PersonsEditorViewModel(field, suggestionProvider, preferencesService.getAutoCompletePreferences(), fieldCheckers, undoManager); textInput = isMultiLine ? new EditorTextArea() : new EditorTextField(); @@ -35,7 +38,7 @@ public PersonsEditor(final Field field, AutoCompletionTextInputBinding.autoComplete(textInput, viewModel::complete, viewModel.getAutoCompletionConverter(), viewModel.getAutoCompletionStrategy()); - new EditorValidator(preferences).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textInput); + new EditorValidator(preferencesService).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textInput); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/PersonsEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/PersonsEditorViewModel.java index e1354a1ac5f..fb995bef63f 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/PersonsEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/PersonsEditorViewModel.java @@ -2,6 +2,8 @@ import java.util.Collection; +import javax.swing.undo.UndoManager; + import javafx.util.StringConverter; import org.jabref.gui.autocompleter.AppendPersonNamesStrategy; @@ -19,8 +21,8 @@ public class PersonsEditorViewModel extends AbstractEditorViewModel { private final AutoCompletePreferences preferences; - public PersonsEditorViewModel(Field field, SuggestionProvider suggestionProvider, AutoCompletePreferences preferences, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public PersonsEditorViewModel(Field field, SuggestionProvider suggestionProvider, AutoCompletePreferences preferences, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); this.preferences = preferences; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/SimpleEditor.java b/src/main/java/org/jabref/gui/fieldeditors/SimpleEditor.java index bf720d0e72b..57bce3f9eea 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/SimpleEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/SimpleEditor.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import javafx.scene.Parent; import javafx.scene.control.TextInputControl; import javafx.scene.layout.HBox; @@ -23,8 +25,9 @@ public SimpleEditor(final Field field, final SuggestionProvider suggestionProvider, final FieldCheckers fieldCheckers, final PreferencesService preferences, - final boolean isMultiLine) { - this.viewModel = new SimpleEditorViewModel(field, suggestionProvider, fieldCheckers); + final boolean isMultiLine, + final UndoManager undoManager) { + this.viewModel = new SimpleEditorViewModel(field, suggestionProvider, fieldCheckers, undoManager); textInput = isMultiLine ? new EditorTextArea() : new EditorTextField(); HBox.setHgrow(textInput, Priority.ALWAYS); @@ -47,8 +50,9 @@ public SimpleEditor(final Field field, public SimpleEditor(final Field field, final SuggestionProvider suggestionProvider, final FieldCheckers fieldCheckers, - final PreferencesService preferences) { - this(field, suggestionProvider, fieldCheckers, preferences, false); + final PreferencesService preferences, + UndoManager undoManager) { + this(field, suggestionProvider, fieldCheckers, preferences, false, undoManager); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/SimpleEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/SimpleEditorViewModel.java index 52b4f6b2f07..acae084f6fc 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/SimpleEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/SimpleEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import org.jabref.gui.autocompleter.AppendWordsStrategy; import org.jabref.gui.autocompleter.AutoCompletionStrategy; import org.jabref.gui.autocompleter.SuggestionProvider; @@ -8,8 +10,8 @@ public class SimpleEditorViewModel extends AbstractEditorViewModel { - public SimpleEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public SimpleEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); } public AutoCompletionStrategy getAutoCompletionStrategy() { diff --git a/src/main/java/org/jabref/gui/fieldeditors/TypeEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/TypeEditorViewModel.java index c4f64880e55..57ab99edadb 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/TypeEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/TypeEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.logic.l10n.Localization; @@ -12,8 +14,8 @@ public class TypeEditorViewModel extends MapBasedEditorViewModel { private BiMap itemMap = HashBiMap.create(8); - public TypeEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public TypeEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); itemMap.put("mathesis", Localization.lang("Master's thesis")); itemMap.put("phdthesis", Localization.lang("PhD thesis")); diff --git a/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java b/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java index 4a71d689c6f..fd30c7a92b8 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java @@ -3,6 +3,8 @@ import java.util.List; import java.util.function.Supplier; +import javax.swing.undo.UndoManager; + import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.Parent; @@ -20,23 +22,26 @@ import org.jabref.preferences.PreferencesService; import com.airhacks.afterburner.views.ViewLoader; +import jakarta.inject.Inject; public class UrlEditor extends HBox implements FieldEditorFX { @FXML private final UrlEditorViewModel viewModel; @FXML private EditorTextArea textArea; + @Inject private DialogService dialogService; + @Inject private PreferencesService preferencesService; + @Inject private UndoManager undoManager; + public UrlEditor(Field field, - DialogService dialogService, SuggestionProvider suggestionProvider, - FieldCheckers fieldCheckers, - PreferencesService preferencesService) { - this.viewModel = new UrlEditorViewModel(field, suggestionProvider, dialogService, preferencesService, fieldCheckers); - + FieldCheckers fieldCheckers) { ViewLoader.view(this) .root(this) .load(); + this.viewModel = new UrlEditorViewModel(field, suggestionProvider, dialogService, preferencesService, fieldCheckers, undoManager); + textArea.textProperty().bindBidirectional(viewModel.textProperty()); Supplier> contextMenuSupplier = EditorMenus.getCleanupUrlMenu(textArea); textArea.initContextMenu(contextMenuSupplier); diff --git a/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java index f8f6670ec77..5741ba4ed95 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java @@ -2,6 +2,8 @@ import java.io.IOException; +import javax.swing.undo.UndoManager; + import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -21,8 +23,12 @@ public class UrlEditorViewModel extends AbstractEditorViewModel { private final PreferencesService preferencesService; private final BooleanProperty validUrlIsNotPresent = new SimpleBooleanProperty(true); - public UrlEditorViewModel(Field field, SuggestionProvider suggestionProvider, DialogService dialogService, PreferencesService preferencesService, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public UrlEditorViewModel(Field field, + SuggestionProvider suggestionProvider, + DialogService dialogService, + PreferencesService preferencesService, + FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); this.dialogService = dialogService; this.preferencesService = preferencesService; diff --git a/src/main/java/org/jabref/gui/fieldeditors/YesNoEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/YesNoEditorViewModel.java index a1210b1d2eb..8210208853e 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/YesNoEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/YesNoEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import javax.swing.undo.UndoManager; + import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.model.entry.field.Field; @@ -11,8 +13,8 @@ public class YesNoEditorViewModel extends MapBasedEditorViewModel { private BiMap itemMap = HashBiMap.create(2); - public YesNoEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { - super(field, suggestionProvider, fieldCheckers); + public YesNoEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); itemMap.put("yes", "Yes"); itemMap.put("no", "No"); diff --git a/src/main/java/org/jabref/gui/fieldeditors/contextmenu/EditorMenus.java b/src/main/java/org/jabref/gui/fieldeditors/contextmenu/EditorMenus.java index 7a31eec6f45..8cf8ebc0fb9 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/contextmenu/EditorMenus.java +++ b/src/main/java/org/jabref/gui/fieldeditors/contextmenu/EditorMenus.java @@ -9,6 +9,7 @@ import javafx.scene.control.TextArea; import javafx.scene.control.TextInputControl; +import org.jabref.gui.DialogService; import org.jabref.gui.Globals; import org.jabref.gui.actions.ActionFactory; import org.jabref.gui.actions.StandardActions; @@ -52,11 +53,11 @@ public static Supplier> getNameMenu(final TextInputControl textIn * @param textArea text-area that this menu will be connected to * @return menu containing items of the default menu and an item for copying a DOI/DOI URL */ - public static Supplier> getDOIMenu(TextArea textArea) { + public static Supplier> getDOIMenu(TextArea textArea, DialogService dialogService) { return () -> { ActionFactory factory = new ActionFactory(Globals.getKeyPrefs()); - MenuItem copyDoiMenuItem = factory.createMenuItem(StandardActions.COPY_DOI, new CopyDoiUrlAction(textArea, StandardActions.COPY_DOI)); - MenuItem copyDoiUrlMenuItem = factory.createMenuItem(StandardActions.COPY_DOI_URL, new CopyDoiUrlAction(textArea, StandardActions.COPY_DOI_URL)); + MenuItem copyDoiMenuItem = factory.createMenuItem(StandardActions.COPY_DOI, new CopyDoiUrlAction(textArea, StandardActions.COPY_DOI, dialogService)); + MenuItem copyDoiUrlMenuItem = factory.createMenuItem(StandardActions.COPY_DOI_URL, new CopyDoiUrlAction(textArea, StandardActions.COPY_DOI_URL, dialogService)); List menuItems = new ArrayList<>(); menuItems.add(copyDoiMenuItem); menuItems.add(copyDoiUrlMenuItem); diff --git a/src/main/java/org/jabref/gui/fieldeditors/identifier/BaseIdentifierEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/identifier/BaseIdentifierEditorViewModel.java index 457df38e15d..3d77efb0beb 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/identifier/BaseIdentifierEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/identifier/BaseIdentifierEditorViewModel.java @@ -3,6 +3,8 @@ import java.io.IOException; import java.util.Optional; +import javax.swing.undo.UndoManager; + import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -40,8 +42,14 @@ public abstract class BaseIdentifierEditorViewModel extend protected TaskExecutor taskExecutor; protected PreferencesService preferences; - public BaseIdentifierEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, DialogService dialogService, TaskExecutor taskExecutor, PreferencesService preferences) { - super(field, suggestionProvider, fieldCheckers); + public BaseIdentifierEditorViewModel(Field field, + SuggestionProvider suggestionProvider, + FieldCheckers fieldCheckers, + DialogService dialogService, + TaskExecutor taskExecutor, + PreferencesService preferences, + UndoManager undoManager) { + super(field, suggestionProvider, fieldCheckers, undoManager); this.dialogService = dialogService; this.taskExecutor = taskExecutor; this.preferences = preferences; diff --git a/src/main/java/org/jabref/gui/fieldeditors/identifier/DoiIdentifierEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/identifier/DoiIdentifierEditorViewModel.java index ac432e74230..319de49cc22 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/identifier/DoiIdentifierEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/identifier/DoiIdentifierEditorViewModel.java @@ -1,7 +1,9 @@ package org.jabref.gui.fieldeditors.identifier; +import javax.swing.undo.UndoManager; + import org.jabref.gui.DialogService; -import org.jabref.gui.JabRefGUI; +import org.jabref.gui.StateManager; import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.gui.desktop.JabRefDesktop; import org.jabref.gui.mergeentries.FetchAndMergeEntry; @@ -21,8 +23,19 @@ public class DoiIdentifierEditorViewModel extends BaseIdentifierEditorViewModel { public static final Logger LOGGER = LoggerFactory.getLogger(DoiIdentifierEditorViewModel.class); - public DoiIdentifierEditorViewModel(SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, DialogService dialogService, TaskExecutor taskExecutor, PreferencesService preferences) { - super(StandardField.DOI, suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferences); + private final UndoManager undoManager; + private final StateManager stateManager; + + public DoiIdentifierEditorViewModel(SuggestionProvider suggestionProvider, + FieldCheckers fieldCheckers, + DialogService dialogService, + TaskExecutor taskExecutor, + PreferencesService preferences, + UndoManager undoManager, + StateManager stateManager) { + super(StandardField.DOI, suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferences, undoManager); + this.undoManager = undoManager; + this.stateManager = stateManager; configure(true, true); } @@ -44,7 +57,11 @@ public void lookupIdentifier(BibEntry bibEntry) { @Override public void fetchBibliographyInformation(BibEntry bibEntry) { - new FetchAndMergeEntry(JabRefGUI.getMainFrame().getCurrentLibraryTab(), taskExecutor, preferences, dialogService).fetchAndMerge(entry, field); + stateManager.getActiveDatabase().ifPresentOrElse( + databaseContext -> new FetchAndMergeEntry(databaseContext, taskExecutor, preferences, dialogService, undoManager) + .fetchAndMerge(entry, field), + () -> dialogService.notify(Localization.lang("No library selected")) + ); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/identifier/EprintIdentifierEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/identifier/EprintIdentifierEditorViewModel.java index 7b3c6a3c363..59c34f2cea5 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/identifier/EprintIdentifierEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/identifier/EprintIdentifierEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors.identifier; +import javax.swing.undo.UndoManager; + import javafx.collections.MapChangeListener; import javafx.collections.WeakMapChangeListener; @@ -29,8 +31,13 @@ public class EprintIdentifierEditorViewModel extends BaseIdentifierEditorViewMod } }; - public EprintIdentifierEditorViewModel(SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, DialogService dialogService, TaskExecutor taskExecutor, PreferencesService preferences) { - super(StandardField.EPRINT, suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferences); + public EprintIdentifierEditorViewModel(SuggestionProvider suggestionProvider, + FieldCheckers fieldCheckers, + DialogService dialogService, + TaskExecutor taskExecutor, + PreferencesService preferences, + UndoManager undoManager) { + super(StandardField.EPRINT, suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferences, undoManager); configure(false, false); EasyBind.subscribe(identifier, newIdentifier -> { newIdentifier.ifPresent(id -> { diff --git a/src/main/java/org/jabref/gui/fieldeditors/identifier/ISBNIdentifierEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/identifier/ISBNIdentifierEditorViewModel.java index 91fb56d8893..2317f8183e4 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/identifier/ISBNIdentifierEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/identifier/ISBNIdentifierEditorViewModel.java @@ -1,24 +1,42 @@ package org.jabref.gui.fieldeditors.identifier; +import javax.swing.undo.UndoManager; + import org.jabref.gui.DialogService; -import org.jabref.gui.JabRefGUI; +import org.jabref.gui.StateManager; import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.gui.mergeentries.FetchAndMergeEntry; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.integrity.FieldCheckers; +import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.ISBN; import org.jabref.preferences.PreferencesService; public class ISBNIdentifierEditorViewModel extends BaseIdentifierEditorViewModel { - public ISBNIdentifierEditorViewModel(SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, DialogService dialogService, TaskExecutor taskExecutor, PreferencesService preferences) { - super(StandardField.ISBN, suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferences); + private final UndoManager undoManager; + private final StateManager stateManager; + + public ISBNIdentifierEditorViewModel(SuggestionProvider suggestionProvider, + FieldCheckers fieldCheckers, + DialogService dialogService, + TaskExecutor taskExecutor, + PreferencesService preferences, + UndoManager undoManager, + StateManager stateManager) { + super(StandardField.ISBN, suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferences, undoManager); + this.undoManager = undoManager; + this.stateManager = stateManager; configure(true, false); } @Override public void fetchBibliographyInformation(BibEntry bibEntry) { - new FetchAndMergeEntry(JabRefGUI.getMainFrame().getCurrentLibraryTab(), taskExecutor, preferences, dialogService).fetchAndMerge(entry, field); + stateManager.getActiveDatabase().ifPresentOrElse( + databaseContext -> new FetchAndMergeEntry(databaseContext, taskExecutor, preferences, dialogService, undoManager) + .fetchAndMerge(entry, field), + () -> dialogService.notify(Localization.lang("No library selected")) + ); } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java b/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java index 8f9e67ff6d4..b7bb7bc9934 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java @@ -2,6 +2,8 @@ import java.util.Optional; +import javax.swing.undo.UndoManager; + import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.control.Button; @@ -9,6 +11,7 @@ import javafx.scene.layout.HBox; import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.gui.fieldeditors.EditorTextArea; import org.jabref.gui.fieldeditors.EditorValidator; @@ -23,7 +26,9 @@ import org.jabref.model.entry.field.StandardField; import org.jabref.preferences.PreferencesService; +import com.airhacks.afterburner.injection.Injector; import com.airhacks.afterburner.views.ViewLoader; +import jakarta.inject.Inject; public class IdentifierEditor extends HBox implements FieldEditorFX { @@ -31,20 +36,29 @@ public class IdentifierEditor extends HBox implements FieldEditorFX { @FXML private EditorTextArea textArea; @FXML private Button fetchInformationByIdentifierButton; @FXML private Button lookupIdentifierButton; + + @Inject private DialogService dialogService; + @Inject private TaskExecutor taskExecutor; + @Inject private PreferencesService preferencesService; + @Inject private UndoManager undoManager; + @Inject private StateManager stateManager; + private Optional entry; public IdentifierEditor(Field field, - TaskExecutor taskExecutor, - DialogService dialogService, SuggestionProvider suggestionProvider, - FieldCheckers fieldCheckers, - PreferencesService preferences) { + FieldCheckers fieldCheckers) { + + // Viewloader must be called after the viewmodel is loaded, + // but we need the injected vars to create the viewmodels. + Injector.registerExistingAndInject(this); + if (StandardField.DOI == field) { - this.viewModel = new DoiIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferences); + this.viewModel = new DoiIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferencesService, undoManager, stateManager); } else if (StandardField.ISBN == field) { - this.viewModel = new ISBNIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferences); + this.viewModel = new ISBNIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferencesService, undoManager, stateManager); } else if (StandardField.EPRINT == field) { - this.viewModel = new EprintIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferences); + this.viewModel = new EprintIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferencesService, undoManager); } else { throw new IllegalStateException(String.format("Unable to instantiate a view model for identifier field editor '%s'", field.getDisplayName())); } @@ -61,12 +75,12 @@ public IdentifierEditor(Field field, new Tooltip(Localization.lang("Look up %0", field.getDisplayName()))); if (field.equals(StandardField.DOI)) { - textArea.initContextMenu(EditorMenus.getDOIMenu(textArea)); + textArea.initContextMenu(EditorMenus.getDOIMenu(textArea, dialogService)); } else { textArea.initContextMenu(new DefaultMenu(textArea)); } - new EditorValidator(preferences).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textArea); + new EditorValidator(preferencesService).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textArea); } public BaseIdentifierEditorViewModel getViewModel() { diff --git a/src/main/java/org/jabref/gui/importer/NewEntryAction.java b/src/main/java/org/jabref/gui/importer/NewEntryAction.java index fcb25a991bc..4086cec912f 100644 --- a/src/main/java/org/jabref/gui/importer/NewEntryAction.java +++ b/src/main/java/org/jabref/gui/importer/NewEntryAction.java @@ -6,9 +6,9 @@ import org.jabref.gui.DialogService; import org.jabref.gui.EntryTypeView; -import org.jabref.gui.Globals; import org.jabref.gui.JabRefFrame; import org.jabref.gui.StateManager; +import org.jabref.gui.Telemetry; import org.jabref.gui.actions.SimpleCommand; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.types.EntryType; @@ -74,6 +74,6 @@ private void trackNewEntry(EntryType type) { Map properties = new HashMap<>(); properties.put("EntryType", type.getName()); - Globals.getTelemetryClient().ifPresent(client -> client.trackEvent("NewEntry", properties, new HashMap<>())); + Telemetry.getTelemetryClient().ifPresent(client -> client.trackEvent("NewEntry", properties, new HashMap<>())); } } diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java index 2e9522f2509..4e2854ba764 100644 --- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java +++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java @@ -14,10 +14,10 @@ import javax.swing.undo.UndoManager; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.JabRefFrame; import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; +import org.jabref.gui.Telemetry; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.autosaveandbackup.BackupManager; import org.jabref.gui.dialogs.BackupUIManager; @@ -250,13 +250,14 @@ private ParserResult loadDatabase(Path file) throws Exception { stateManager, entryTypesManager, fileUpdateMonitor, - undoManager); + undoManager, + taskExecutor); } return parserResult; } private void trackOpenNewDatabase(LibraryTab libraryTab) { - Globals.getTelemetryClient().ifPresent(client -> client.trackEvent( + Telemetry.getTelemetryClient().ifPresent(client -> client.trackEvent( "OpenNewDatabase", Map.of(), Map.of("NumberOfEntries", (double) libraryTab.getBibDatabaseContext().getDatabase().getEntryCount()))); @@ -269,7 +270,8 @@ public static void openSharedDatabase(ParserResult parserResult, StateManager stateManager, BibEntryTypesManager entryTypesManager, FileUpdateMonitor fileUpdateMonitor, - UndoManager undoManager) + UndoManager undoManager, + TaskExecutor taskExecutor) throws SQLException, DatabaseNotSupportedException, InvalidDBMSConnectionPropertiesException, NotASharedDatabaseException { try { new SharedDatabaseUIManager( @@ -279,7 +281,8 @@ public static void openSharedDatabase(ParserResult parserResult, stateManager, entryTypesManager, fileUpdateMonitor, - undoManager) + undoManager, + taskExecutor) .openSharedDatabaseFromParserResult(parserResult); } catch (SQLException | DatabaseNotSupportedException | InvalidDBMSConnectionPropertiesException | NotASharedDatabaseException e) { diff --git a/src/main/java/org/jabref/gui/importer/fetcher/LookupIdentifierAction.java b/src/main/java/org/jabref/gui/importer/fetcher/LookupIdentifierAction.java index 314781ec9a1..745006517fa 100644 --- a/src/main/java/org/jabref/gui/importer/fetcher/LookupIdentifierAction.java +++ b/src/main/java/org/jabref/gui/importer/fetcher/LookupIdentifierAction.java @@ -5,18 +5,16 @@ import javax.swing.undo.UndoManager; -import org.jabref.gui.Globals; import org.jabref.gui.JabRefFrame; import org.jabref.gui.StateManager; import org.jabref.gui.actions.Action; import org.jabref.gui.actions.SimpleCommand; -import org.jabref.gui.icon.JabRefIcon; -import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableFieldChange; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.BindingsHelper; import org.jabref.gui.util.DefaultTaskExecutor; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.IdFetcher; import org.jabref.logic.l10n.Localization; @@ -38,13 +36,19 @@ public class LookupIdentifierAction extends SimpleCommand private final IdFetcher fetcher; private final StateManager stateManager; - private UndoManager undoManager; - - public LookupIdentifierAction(JabRefFrame frame, IdFetcher fetcher, StateManager stateManager, UndoManager undoManager) { + private final UndoManager undoManager; + private final TaskExecutor taskExecutor; + + public LookupIdentifierAction(JabRefFrame frame, + IdFetcher fetcher, + StateManager stateManager, + UndoManager undoManager, + TaskExecutor taskExecutor) { this.frame = frame; this.fetcher = fetcher; this.stateManager = stateManager; this.undoManager = undoManager; + this.taskExecutor = taskExecutor; this.executable.bind(needsDatabase(this.stateManager).and(needsEntriesSelected(this.stateManager))); this.statusMessage.bind(BindingsHelper.ifThenElse(executable, "", Localization.lang("This operation requires one or more entries to be selected."))); @@ -55,34 +59,14 @@ public void execute() { try { BackgroundTask.wrap(() -> lookupIdentifiers(stateManager.getSelectedEntries())) .onSuccess(frame.getDialogService()::notify) - .executeWith(Globals.TASK_EXECUTOR); + .executeWith(taskExecutor); } catch (Exception e) { LOGGER.error("Problem running ID Worker", e); } } public Action getAction() { - return new Action() { - @Override - public Optional getIcon() { - return Optional.empty(); - } - - @Override - public Optional getKeyBinding() { - return Optional.empty(); - } - - @Override - public String getText() { - return fetcher.getIdentifierName(); - } - - @Override - public String getDescription() { - return ""; - } - }; + return fetcher::getIdentifierName; } private String lookupIdentifiers(List bibEntries) { diff --git a/src/main/java/org/jabref/gui/importer/fetcher/WebSearchPaneViewModel.java b/src/main/java/org/jabref/gui/importer/fetcher/WebSearchPaneViewModel.java index eefb0944c61..d4a199a2948 100644 --- a/src/main/java/org/jabref/gui/importer/fetcher/WebSearchPaneViewModel.java +++ b/src/main/java/org/jabref/gui/importer/fetcher/WebSearchPaneViewModel.java @@ -14,8 +14,8 @@ import javafx.collections.ObservableList; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.StateManager; +import org.jabref.gui.Telemetry; import org.jabref.gui.importer.ImportEntriesDialog; import org.jabref.gui.util.BackgroundTask; import org.jabref.logic.importer.CompositeIdFetcher; @@ -158,7 +158,7 @@ public void search() { } final String finalFetcherName = fetcherName; - Globals.getTelemetryClient().ifPresent(client -> + Telemetry.getTelemetryClient().ifPresent(client -> client.trackEvent("search", Map.of("fetcher", finalFetcherName), Map.of())); BackgroundTask task = BackgroundTask.wrap(parserResultCallable) diff --git a/src/main/java/org/jabref/gui/journals/AbbreviateAction.java b/src/main/java/org/jabref/gui/journals/AbbreviateAction.java index 93be6a06c39..a1a723287d9 100644 --- a/src/main/java/org/jabref/gui/journals/AbbreviateAction.java +++ b/src/main/java/org/jabref/gui/journals/AbbreviateAction.java @@ -17,7 +17,9 @@ import org.jabref.gui.actions.StandardActions; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationPreferences; +import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; @@ -38,6 +40,8 @@ public class AbbreviateAction extends SimpleCommand { private final DialogService dialogService; private final StateManager stateManager; private final JournalAbbreviationPreferences journalAbbreviationPreferences; + private final JournalAbbreviationRepository abbreviationRepository; + private final TaskExecutor taskExecutor; private AbbreviationType abbreviationType; @@ -45,12 +49,16 @@ public AbbreviateAction(StandardActions action, JabRefFrame frame, DialogService dialogService, StateManager stateManager, - JournalAbbreviationPreferences journalAbbreviationPreferences) { + JournalAbbreviationPreferences abbreviationPreferences, + JournalAbbreviationRepository abbreviationRepository, + TaskExecutor taskExecutor) { this.action = action; this.frame = frame; this.dialogService = dialogService; this.stateManager = stateManager; - this.journalAbbreviationPreferences = journalAbbreviationPreferences; + this.journalAbbreviationPreferences = abbreviationPreferences; + this.abbreviationRepository = abbreviationRepository; + this.taskExecutor = taskExecutor; switch (action) { case ABBREVIATE_DEFAULT -> abbreviationType = AbbreviationType.DEFAULT; @@ -71,13 +79,13 @@ public void execute() { stateManager.getActiveDatabase().ifPresent(databaseContext -> BackgroundTask.wrap(() -> abbreviate(stateManager.getActiveDatabase().get(), stateManager.getSelectedEntries())) .onSuccess(dialogService::notify) - .executeWith(Globals.TASK_EXECUTOR)); + .executeWith(taskExecutor)); } else if (action == StandardActions.UNABBREVIATE) { dialogService.notify(Localization.lang("Unabbreviating...")); stateManager.getActiveDatabase().ifPresent(databaseContext -> BackgroundTask.wrap(() -> unabbreviate(stateManager.getActiveDatabase().get(), stateManager.getSelectedEntries())) .onSuccess(dialogService::notify) - .executeWith(Globals.TASK_EXECUTOR)); + .executeWith(taskExecutor)); } else { LOGGER.debug("Unknown action: " + action.name()); } @@ -85,7 +93,7 @@ public void execute() { private String abbreviate(BibDatabaseContext databaseContext, List entries) { UndoableAbbreviator undoableAbbreviator = new UndoableAbbreviator( - Globals.journalAbbreviationRepository, + abbreviationRepository, abbreviationType, journalAbbreviationPreferences.shouldUseFJournalField()); diff --git a/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesAction.java b/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesAction.java index 1f1e0d250d7..1632c696d43 100644 --- a/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesAction.java +++ b/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesAction.java @@ -39,7 +39,7 @@ public void execute() { if (stateManager.getActiveDatabase().isPresent()) { dialogService.showCustomDialogAndWait(new LibraryPropertiesView(stateManager.getActiveDatabase().get())); } else { - LOGGER.warn("No database selected."); + LOGGER.warn("No library selected."); } } } diff --git a/src/main/java/org/jabref/gui/logging/ApplicationInsightsWriter.java b/src/main/java/org/jabref/gui/logging/ApplicationInsightsWriter.java index 47fb4bcc15f..fbcb43670e8 100644 --- a/src/main/java/org/jabref/gui/logging/ApplicationInsightsWriter.java +++ b/src/main/java/org/jabref/gui/logging/ApplicationInsightsWriter.java @@ -5,8 +5,6 @@ import java.util.EnumSet; import java.util.Map; -import org.jabref.gui.Globals; - import com.microsoft.applicationinsights.telemetry.ExceptionTelemetry; import com.microsoft.applicationinsights.telemetry.Telemetry; import com.microsoft.applicationinsights.telemetry.TraceTelemetry; @@ -46,7 +44,7 @@ public void write(LogEntry logEntry) throws Exception { } telemetry.getContext().getProperties().putAll(event.getCustomParameters()); - Globals.getTelemetryClient().ifPresent(client -> client.track(telemetry)); + org.jabref.gui.Telemetry.getTelemetryClient().ifPresent(client -> client.track(telemetry)); } @Override diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index 10f9f68022e..7c4e2b2e329 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -71,6 +71,7 @@ public class MainTable extends TableView { private final CustomLocalDragboard localDragboard; private final ClipBoardManager clipBoardManager; private final BibEntryTypesManager entryTypesManager; + private final TaskExecutor taskExecutor; private long lastKeyPressTime; private String columnSearchTerm; @@ -94,6 +95,7 @@ public MainTable(MainTableDataModel model, this.model = model; this.clipBoardManager = clipBoardManager; this.entryTypesManager = entryTypesManager; + this.taskExecutor = taskExecutor; UndoManager undoManager = libraryTab.getUndoManager(); MainTablePreferences mainTablePreferences = preferencesService.getMainTablePreferences(); @@ -118,7 +120,8 @@ public MainTable(MainTableDataModel model, preferencesService.getMainTableColumnPreferences(), libraryTab.getUndoManager(), dialogService, - stateManager).createColumns()); + stateManager, + taskExecutor).createColumns()); this.getColumns().removeIf(LibraryColumn.class::isInstance); @@ -136,9 +139,9 @@ public MainTable(MainTableDataModel model, preferencesService, undoManager, clipBoardManager, - Globals.TASK_EXECUTOR, + taskExecutor, Globals.journalAbbreviationRepository, - Globals.entryTypesManager)) + entryTypesManager)) .setOnDragDetected(this::handleOnDragDetected) .setOnDragDropped(this::handleOnDragDropped) .setOnDragOver(this::handleOnDragOver) @@ -191,7 +194,8 @@ public MainTable(MainTableDataModel model, preferencesService.getMainTableColumnPreferences(), libraryTab.getUndoManager(), dialogService, - stateManager); + stateManager, + taskExecutor); // Enable the header right-click menu. new MainTableHeaderContextMenu(this, rightClickMenuFactory).show(true); @@ -417,7 +421,7 @@ private void handleOnDragDropped(TableRow row, BibEntryT // Center -> link files to entry // Depending on the pressed modifier, move/copy/link files to drop target switch (ControlHelper.getDroppingMouseLocation(row, event)) { - case TOP, BOTTOM -> importHandler.importFilesInBackground(files).executeWith(Globals.TASK_EXECUTOR); + case TOP, BOTTOM -> importHandler.importFilesInBackground(files).executeWith(taskExecutor); case CENTER -> { BibEntry entry = target.getEntry(); switch (event.getTransferMode()) { @@ -449,7 +453,7 @@ private void handleOnDragDroppedTableView(DragEvent event) { if (event.getDragboard().hasFiles()) { List files = event.getDragboard().getFiles().stream().map(File::toPath).collect(Collectors.toList()); - importHandler.importFilesInBackground(files).executeWith(Globals.TASK_EXECUTOR); + importHandler.importFilesInBackground(files).executeWith(taskExecutor); success = true; } diff --git a/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java b/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java index 0ca2908b4d4..8e37d4f91a4 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java +++ b/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java @@ -31,6 +31,7 @@ import org.jabref.gui.maintable.columns.MainTableColumn; import org.jabref.gui.maintable.columns.SpecialFieldColumn; import org.jabref.gui.specialfields.SpecialFieldValueViewModel; +import org.jabref.gui.util.TaskExecutor; import org.jabref.gui.util.ValueTableCellFactory; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; @@ -57,6 +58,7 @@ public class MainTableColumnFactory { private final CellFactory cellFactory; private final UndoManager undoManager; private final DialogService dialogService; + private final TaskExecutor taskExecutor; private final StateManager stateManager; public MainTableColumnFactory(BibDatabaseContext database, @@ -64,11 +66,13 @@ public MainTableColumnFactory(BibDatabaseContext database, ColumnPreferences abstractColumnPrefs, UndoManager undoManager, DialogService dialogService, - StateManager stateManager) { + StateManager stateManager, + TaskExecutor taskExecutor) { this.database = Objects.requireNonNull(database); this.preferencesService = Objects.requireNonNull(preferencesService); this.columnPreferences = abstractColumnPrefs; this.dialogService = dialogService; + this.taskExecutor = taskExecutor; this.cellFactory = new CellFactory(preferencesService, undoManager); this.undoManager = undoManager; this.stateManager = stateManager; @@ -176,7 +180,7 @@ private TableColumn createIndexColumn(MainTableC private Node createGroupColorRegion(BibEntryTableViewModel entry, List matchedGroups) { List groupColors = matchedGroups.stream() .flatMap(group -> OptionalUtil.toStream(group.getColor())) - .collect(Collectors.toList()); + .toList(); if (!groupColors.isEmpty()) { HBox container = new HBox(); @@ -235,7 +239,8 @@ private TableColumn> createFilesColumn( return new FileColumn(columnModel, database, dialogService, - preferencesService); + preferencesService, + taskExecutor); } /** @@ -246,7 +251,8 @@ private TableColumn> createExtraFileCol database, dialogService, preferencesService, - columnModel.getQualifier()); + columnModel.getQualifier(), + taskExecutor); } /** diff --git a/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java b/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java index 499f07a3526..deece3ec936 100644 --- a/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java +++ b/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java @@ -4,11 +4,11 @@ import java.util.List; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.fieldeditors.LinkedFileViewModel; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; @@ -24,17 +24,27 @@ public class OpenExternalFileAction extends SimpleCommand { private final BibEntry entry; private final LinkedFile linkedFile; + private final TaskExecutor taskExecutor; - public OpenExternalFileAction(DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) { - this(dialogService, stateManager, preferencesService, null, null); + public OpenExternalFileAction(DialogService dialogService, + StateManager stateManager, + PreferencesService preferencesService, + TaskExecutor taskExecutor) { + this(dialogService, stateManager, preferencesService, null, null, taskExecutor); } - public OpenExternalFileAction(DialogService dialogService, StateManager stateManager, PreferencesService preferencesService, BibEntry entry, LinkedFile linkedFile) { + public OpenExternalFileAction(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.hasLinkedFileForSelectedEntries(stateManager) @@ -65,7 +75,7 @@ public void execute() { linkedFile, entry, databaseContext, - Globals.TASK_EXECUTOR, + taskExecutor, dialogService, preferencesService); @@ -89,7 +99,7 @@ public void execute() { linkedFile, entry, databaseContext, - Globals.TASK_EXECUTOR, + taskExecutor, dialogService, preferencesService); linkedFileViewModel.open(); diff --git a/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java b/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java index b7da7f5be6e..97e9063c76f 100644 --- a/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java +++ b/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java @@ -1,11 +1,11 @@ package org.jabref.gui.maintable; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.fieldeditors.LinkedFileViewModel; +import org.jabref.gui.util.TaskExecutor; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; import org.jabref.preferences.PreferencesService; @@ -18,17 +18,27 @@ public class OpenFolderAction extends SimpleCommand { private final BibEntry entry; private final LinkedFile linkedFile; + private final TaskExecutor taskExecutor; - public OpenFolderAction(DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) { - this(dialogService, stateManager, preferencesService, null, null); + public OpenFolderAction(DialogService dialogService, + StateManager stateManager, + PreferencesService preferencesService, + TaskExecutor taskExecutor) { + this(dialogService, stateManager, preferencesService, null, null, taskExecutor); } - public OpenFolderAction(DialogService dialogService, StateManager stateManager, PreferencesService preferencesService, BibEntry entry, LinkedFile linkedFile) { + public OpenFolderAction(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.isFilePresentForSelectedEntry(stateManager, preferencesService)); @@ -46,7 +56,7 @@ public void execute() { entry.getFiles().get(0), entry, databaseContext, - Globals.TASK_EXECUTOR, + taskExecutor, dialogService, preferencesService); linkedFileViewModel.openFolder(); @@ -56,7 +66,7 @@ public void execute() { linkedFile, entry, databaseContext, - Globals.TASK_EXECUTOR, + taskExecutor, dialogService, preferencesService); linkedFileViewModel.openFolder(); diff --git a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java index fca260455dc..42686574b3e 100644 --- a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java +++ b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java @@ -60,7 +60,7 @@ public static ContextMenu create(BibEntryTableViewModel entry, new SeparatorMenuItem(), - createSendSubMenu(factory, dialogService, stateManager, preferencesService, entryTypesManager), + createSendSubMenu(factory, dialogService, stateManager, preferencesService, entryTypesManager, taskExecutor), SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.RANKING, factory, libraryTab.frame(), dialogService, preferencesService, undoManager, stateManager), SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.RELEVANCE, factory, libraryTab.frame(), dialogService, preferencesService, undoManager, stateManager), @@ -73,16 +73,16 @@ public static ContextMenu create(BibEntryTableViewModel entry, factory.createMenuItem(StandardActions.ATTACH_FILE, new AttachFileAction(libraryTab, dialogService, stateManager, preferencesService.getFilePreferences())), factory.createMenuItem(StandardActions.ATTACH_FILE_FROM_URL, new AttachFileFromURLAction(dialogService, stateManager, taskExecutor, preferencesService)), - factory.createMenuItem(StandardActions.OPEN_FOLDER, new OpenFolderAction(dialogService, stateManager, preferencesService)), - factory.createMenuItem(StandardActions.OPEN_EXTERNAL_FILE, new OpenExternalFileAction(dialogService, stateManager, 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.OPEN_URL, new OpenUrlAction(dialogService, stateManager, preferencesService)), factory.createMenuItem(StandardActions.SEARCH_SHORTSCIENCE, new SearchShortScienceAction(dialogService, stateManager, preferencesService)), new SeparatorMenuItem(), - new ChangeEntryTypeMenu(libraryTab.getSelectedEntries(), libraryTab.getBibDatabaseContext(), libraryTab.getUndoManager(), keyBindingRepository, entryTypesManager).asSubMenu(), - factory.createMenuItem(StandardActions.MERGE_WITH_FETCHED_ENTRY, new MergeWithFetchedEntryAction(libraryTab, dialogService, stateManager, taskExecutor, preferencesService)) + new ChangeEntryTypeMenu(libraryTab.getSelectedEntries(), libraryTab.getBibDatabaseContext(), undoManager, keyBindingRepository, entryTypesManager).asSubMenu(), + factory.createMenuItem(StandardActions.MERGE_WITH_FETCHED_ENTRY, new MergeWithFetchedEntryAction(dialogService, stateManager, taskExecutor, preferencesService, undoManager)) ); return contextMenu; @@ -129,11 +129,12 @@ private static Menu createSendSubMenu(ActionFactory factory, DialogService dialogService, StateManager stateManager, PreferencesService preferencesService, - BibEntryTypesManager entryTypesManager) { + BibEntryTypesManager entryTypesManager, + TaskExecutor taskExecutor) { Menu sendMenu = factory.createMenu(StandardActions.SEND); sendMenu.getItems().addAll( - factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new SendAsStandardEmailAction(dialogService, preferencesService, stateManager, entryTypesManager)), - factory.createMenuItem(StandardActions.SEND_TO_KINDLE, new SendAsKindleEmailAction(dialogService, preferencesService, stateManager)), + factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new SendAsStandardEmailAction(dialogService, preferencesService, stateManager, entryTypesManager, taskExecutor)), + factory.createMenuItem(StandardActions.SEND_TO_KINDLE, new SendAsKindleEmailAction(dialogService, preferencesService, stateManager, taskExecutor)), new SeparatorMenuItem() ); diff --git a/src/main/java/org/jabref/gui/maintable/columns/FileColumn.java b/src/main/java/org/jabref/gui/maintable/columns/FileColumn.java index 7764498e4c4..6dd1634c1a5 100644 --- a/src/main/java/org/jabref/gui/maintable/columns/FileColumn.java +++ b/src/main/java/org/jabref/gui/maintable/columns/FileColumn.java @@ -11,7 +11,6 @@ import javafx.scene.input.MouseButton; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.externalfiletype.ExternalFileType; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.fieldeditors.LinkedFileViewModel; @@ -20,6 +19,7 @@ import org.jabref.gui.maintable.ColumnPreferences; import org.jabref.gui.maintable.MainTableColumnFactory; import org.jabref.gui.maintable.MainTableColumnModel; +import org.jabref.gui.util.TaskExecutor; import org.jabref.gui.util.ValueTableCellFactory; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; @@ -35,6 +35,7 @@ public class FileColumn extends MainTableColumn> { private final DialogService dialogService; private final BibDatabaseContext database; private final PreferencesService preferencesService; + private final TaskExecutor taskExecutor; /** * Creates a joined column for all the linked files. @@ -42,11 +43,13 @@ public class FileColumn extends MainTableColumn> { public FileColumn(MainTableColumnModel model, BibDatabaseContext database, DialogService dialogService, - PreferencesService preferencesService) { + PreferencesService preferencesService, + TaskExecutor taskExecutor) { super(model); this.database = Objects.requireNonNull(database); this.dialogService = dialogService; this.preferencesService = preferencesService; + this.taskExecutor = taskExecutor; setCommonSettings(); @@ -63,7 +66,8 @@ public FileColumn(MainTableColumnModel model, // Only one linked file -> open directly LinkedFileViewModel linkedFileViewModel = new LinkedFileViewModel(linkedFiles.get(0), entry.getEntry(), - database, Globals.TASK_EXECUTOR, + database, + taskExecutor, dialogService, preferencesService); linkedFileViewModel.open(); @@ -79,11 +83,13 @@ public FileColumn(MainTableColumnModel model, BibDatabaseContext database, DialogService dialogService, PreferencesService preferencesService, - String fileType) { + String fileType, + TaskExecutor taskExecutor) { super(model); this.database = Objects.requireNonNull(database); this.dialogService = dialogService; this.preferencesService = preferencesService; + this.taskExecutor = taskExecutor; setCommonSettings(); @@ -122,7 +128,7 @@ private ContextMenu createFileMenu(BibEntryTableViewModel entry, List SUPPORTED_FIELDS = Arrays.asList(StandardField.DOI, StandardField.EPRINT, StandardField.ISBN); private static final Logger LOGGER = LoggerFactory.getLogger(FetchAndMergeEntry.class); private final DialogService dialogService; - private final TaskExecutor taskExecutor; + private final UndoManager undoManager; private final BibDatabaseContext bibDatabaseContext; + private final TaskExecutor taskExecutor; private final PreferencesService preferencesService; - private final LibraryTab libraryTab; - public FetchAndMergeEntry(LibraryTab libraryTab, TaskExecutor taskExecutor, PreferencesService preferencesService, DialogService dialogService) { - this.libraryTab = libraryTab; - this.bibDatabaseContext = libraryTab.getBibDatabaseContext(); + public FetchAndMergeEntry(BibDatabaseContext bibDatabaseContext, + TaskExecutor taskExecutor, + PreferencesService preferencesService, + DialogService dialogService, + UndoManager undoManager) { + this.bibDatabaseContext = bibDatabaseContext; this.taskExecutor = taskExecutor; this.preferencesService = preferencesService; this.dialogService = dialogService; + this.undoManager = undoManager; } public void fetchAndMerge(BibEntry entry) { @@ -150,7 +155,7 @@ private void showMergeDialog(BibEntry originalEntry, BibEntry fetchedEntry, WebF if (edited) { ce.end(); - libraryTab.getUndoManager().addEdit(ce); + undoManager.addEdit(ce); dialogService.notify(Localization.lang("Updated entry with info from %0", fetcher.getName())); } else { dialogService.notify(Localization.lang("No information added")); @@ -164,7 +169,7 @@ public void fetchAndMerge(BibEntry entry, EntryBasedFetcher fetcher) { BackgroundTask.wrap(() -> fetcher.performSearch(entry).stream().findFirst()) .onSuccess(fetchedEntry -> { if (fetchedEntry.isPresent()) { - ImportCleanup cleanup = new ImportCleanup(libraryTab.getBibDatabaseContext().getMode()); + ImportCleanup cleanup = new ImportCleanup(bibDatabaseContext.getMode()); cleanup.doPostCleanup(fetchedEntry.get()); showMergeDialog(entry, fetchedEntry.get(), fetcher); } else { diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java b/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java index 8c4d88aec5d..a4702e075a8 100644 --- a/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java +++ b/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java @@ -1,7 +1,8 @@ package org.jabref.gui.mergeentries; +import javax.swing.undo.UndoManager; + import org.jabref.gui.DialogService; -import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; @@ -14,18 +15,22 @@ public class MergeWithFetchedEntryAction extends SimpleCommand { - private final LibraryTab libraryTab; private final DialogService dialogService; private final StateManager stateManager; private final PreferencesService preferencesService; + private final UndoManager undoManager; private final TaskExecutor taskExecutor; - public MergeWithFetchedEntryAction(LibraryTab libraryTab, DialogService dialogService, StateManager stateManager, TaskExecutor taskExecutor, PreferencesService preferencesService) { - this.libraryTab = libraryTab; + public MergeWithFetchedEntryAction(DialogService dialogService, + StateManager stateManager, + TaskExecutor taskExecutor, + PreferencesService preferencesService, + UndoManager undoManager) { this.dialogService = dialogService; this.stateManager = stateManager; this.taskExecutor = taskExecutor; this.preferencesService = preferencesService; + this.undoManager = undoManager; this.executable.bind(ActionHelper.needsEntriesSelected(1, stateManager) .and(ActionHelper.isAnyFieldSetForSelectedEntry(FetchAndMergeEntry.SUPPORTED_FIELDS, stateManager))); @@ -33,6 +38,10 @@ public MergeWithFetchedEntryAction(LibraryTab libraryTab, DialogService dialogSe @Override public void execute() { + if (stateManager.getActiveDatabase().isEmpty()) { + return; + } + if (stateManager.getSelectedEntries().size() != 1) { dialogService.showInformationDialogAndWait( Localization.lang("Merge entry with %0 information", new OrFields(StandardField.DOI, StandardField.ISBN, StandardField.EPRINT).getDisplayName()), @@ -40,6 +49,6 @@ public void execute() { } BibEntry originalEntry = stateManager.getSelectedEntries().get(0); - new FetchAndMergeEntry(libraryTab, taskExecutor, preferencesService, dialogService).fetchAndMerge(originalEntry); + new FetchAndMergeEntry(stateManager.getActiveDatabase().get(), taskExecutor, preferencesService, dialogService, undoManager).fetchAndMerge(originalEntry); } } diff --git a/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java b/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java index 94c54289125..9e8b49e7d6a 100644 --- a/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java +++ b/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java @@ -28,6 +28,8 @@ import org.jabref.gui.DialogService; import org.jabref.gui.JabRefGUI; +import org.jabref.gui.LibraryTab; +import org.jabref.gui.LibraryTabContainer; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionFactory; import org.jabref.gui.actions.StandardActions; @@ -52,8 +54,10 @@ import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.model.openoffice.style.CitationType; import org.jabref.model.openoffice.uno.CreationException; +import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; import com.sun.star.comp.helper.BootstrapException; @@ -90,16 +94,25 @@ public class OpenOfficePanel { private final UndoManager undoManager; private final TaskExecutor taskExecutor; private final StyleLoader loader; + private final LibraryTabContainer tabContainer; + private final FileUpdateMonitor fileUpdateMonitor; + private final BibEntryTypesManager entryTypesManager; private OOBibBase ooBase; private OOBibStyle style; - public OpenOfficePanel(PreferencesService preferencesService, + public OpenOfficePanel(LibraryTabContainer tabContainer, + PreferencesService preferencesService, KeyBindingRepository keyBindingRepository, JournalAbbreviationRepository abbreviationRepository, TaskExecutor taskExecutor, DialogService dialogService, StateManager stateManager, + FileUpdateMonitor fileUpdateMonitor, + BibEntryTypesManager entryTypesManager, UndoManager undoManager) { + this.tabContainer = tabContainer; + this.fileUpdateMonitor = fileUpdateMonitor; + this.entryTypesManager = entryTypesManager; ActionFactory factory = new ActionFactory(keyBindingRepository); this.preferencesService = preferencesService; this.taskExecutor = taskExecutor; @@ -259,7 +272,17 @@ private void exportEntries() { Optional newDatabase = ooBase.exportCitedHelper(databases, returnPartialResult); if (newDatabase.isPresent()) { BibDatabaseContext databaseContext = new BibDatabaseContext(newDatabase.get()); - JabRefGUI.getMainFrame().addTab(databaseContext, true); + LibraryTab libraryTab = LibraryTab.createLibraryTab( + databaseContext, + JabRefGUI.getMainFrame(), + dialogService, + preferencesService, + stateManager, + fileUpdateMonitor, + entryTypesManager, + undoManager, + taskExecutor); + tabContainer.addTab(libraryTab, true); } } diff --git a/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogView.java b/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogView.java index f2c8a835345..2dd29a449e1 100644 --- a/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogView.java +++ b/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogView.java @@ -15,6 +15,7 @@ import org.jabref.gui.preview.PreviewViewer; import org.jabref.gui.theme.ThemeManager; import org.jabref.gui.util.BaseDialog; +import org.jabref.gui.util.TaskExecutor; import org.jabref.gui.util.ValueTableCellFactory; import org.jabref.gui.util.ViewModelTableRowFactory; import org.jabref.logic.l10n.Localization; @@ -48,6 +49,7 @@ public class StyleSelectDialogView extends BaseDialog { @Inject private DialogService dialogService; @Inject private StateManager stateManager; @Inject private ThemeManager themeManager; + @Inject private TaskExecutor taskExecutor; private StyleSelectDialogViewModel viewModel; private PreviewViewer previewArticle; @@ -74,11 +76,11 @@ public StyleSelectDialogView(StyleLoader loader) { private void initialize() { viewModel = new StyleSelectDialogViewModel(dialogService, loader, preferencesService); - previewArticle = new PreviewViewer(new BibDatabaseContext(), dialogService, preferencesService, stateManager, themeManager); + previewArticle = new PreviewViewer(new BibDatabaseContext(), dialogService, preferencesService, stateManager, themeManager, taskExecutor); previewArticle.setEntry(TestEntry.getTestEntry()); vbox.getChildren().add(previewArticle); - previewBook = new PreviewViewer(new BibDatabaseContext(), dialogService, preferencesService, stateManager, themeManager); + previewBook = new PreviewViewer(new BibDatabaseContext(), dialogService, preferencesService, stateManager, themeManager, taskExecutor); previewBook.setEntry(TestEntry.getTestEntryBook()); vbox.getChildren().add(previewBook); diff --git a/src/main/java/org/jabref/gui/preferences/preview/PreviewTab.java b/src/main/java/org/jabref/gui/preferences/preview/PreviewTab.java index cd50d49cfdb..90d6aa1f7ef 100644 --- a/src/main/java/org/jabref/gui/preferences/preview/PreviewTab.java +++ b/src/main/java/org/jabref/gui/preferences/preview/PreviewTab.java @@ -156,7 +156,7 @@ public void initialize() { sortUpButton.disableProperty().bind(viewModel.chosenSelectionModelProperty().getValue().selectedItemProperty().isNull()); sortDownButton.disableProperty().bind(viewModel.chosenSelectionModelProperty().getValue().selectedItemProperty().isNull()); - PreviewViewer previewViewer = new PreviewViewer(new BibDatabaseContext(), dialogService, preferencesService, stateManager, themeManager); + PreviewViewer previewViewer = new PreviewViewer(new BibDatabaseContext(), dialogService, preferencesService, stateManager, themeManager, taskExecutor); previewViewer.setEntry(TestEntry.getTestEntry()); EasyBind.subscribe(viewModel.selectedLayoutProperty(), previewViewer::setLayout); previewViewer.visibleProperty().bind(viewModel.chosenSelectionModelProperty().getValue().selectedItemProperty().isNotNull() diff --git a/src/main/java/org/jabref/gui/preview/PreviewPanel.java b/src/main/java/org/jabref/gui/preview/PreviewPanel.java index eea343507d3..a2416d43098 100644 --- a/src/main/java/org/jabref/gui/preview/PreviewPanel.java +++ b/src/main/java/org/jabref/gui/preview/PreviewPanel.java @@ -23,6 +23,7 @@ import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.theme.ThemeManager; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; import org.jabref.logic.pdf.search.indexing.IndexingTaskManager; import org.jabref.logic.preview.PreviewLayout; @@ -53,7 +54,8 @@ public PreviewPanel(BibDatabaseContext database, PreferencesService preferencesService, StateManager stateManager, ThemeManager themeManager, - IndexingTaskManager indexingTaskManager) { + IndexingTaskManager indexingTaskManager, + TaskExecutor taskExecutor) { this.keyBindingRepository = keyBindingRepository; this.dialogService = dialogService; this.stateManager = stateManager; @@ -62,7 +64,7 @@ public PreviewPanel(BibDatabaseContext database, this.fileLinker = new ExternalFilesEntryLinker(preferencesService.getFilePreferences(), database, dialogService); PreviewPreferences previewPreferences = preferencesService.getPreviewPreferences(); - previewView = new PreviewViewer(database, dialogService, preferencesService, stateManager, themeManager); + previewView = new PreviewViewer(database, dialogService, preferencesService, stateManager, themeManager, taskExecutor); previewView.setLayout(previewPreferences.getSelectedPreviewLayout()); previewView.setContextMenu(createPopupMenu()); previewView.setOnDragDetected(event -> { diff --git a/src/main/java/org/jabref/gui/preview/PreviewViewer.java b/src/main/java/org/jabref/gui/preview/PreviewViewer.java index 979c30a5049..544275f741a 100644 --- a/src/main/java/org/jabref/gui/preview/PreviewViewer.java +++ b/src/main/java/org/jabref/gui/preview/PreviewViewer.java @@ -122,7 +122,7 @@ function getSelectionHtml() { private final DialogService dialogService; private final PreferencesService preferencesService; - private final TaskExecutor taskExecutor = Globals.TASK_EXECUTOR; + private final TaskExecutor taskExecutor; private final WebView previewView; private PreviewLayout layout; @@ -147,11 +147,13 @@ public PreviewViewer(BibDatabaseContext database, DialogService dialogService, PreferencesService preferencesService, StateManager stateManager, - ThemeManager themeManager) { + ThemeManager themeManager, + TaskExecutor taskExecutor) { this.database = Objects.requireNonNull(database); this.dialogService = dialogService; this.preferencesService = preferencesService; this.clipBoardManager = Globals.getClipboardManager(); + this.taskExecutor = taskExecutor; setFitToHeight(true); setFitToWidth(true); diff --git a/src/main/java/org/jabref/gui/search/GlobalSearchResultDialog.java b/src/main/java/org/jabref/gui/search/GlobalSearchResultDialog.java index 14de1499f95..338ed960023 100644 --- a/src/main/java/org/jabref/gui/search/GlobalSearchResultDialog.java +++ b/src/main/java/org/jabref/gui/search/GlobalSearchResultDialog.java @@ -16,6 +16,7 @@ import org.jabref.gui.preview.PreviewViewer; import org.jabref.gui.theme.ThemeManager; import org.jabref.gui.util.BaseDialog; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; import org.jabref.preferences.PreferencesService; @@ -34,6 +35,7 @@ public class GlobalSearchResultDialog extends BaseDialog { @Inject private StateManager stateManager; @Inject private DialogService dialogService; @Inject private ThemeManager themeManager; + @Inject private TaskExecutor taskExecutor; private GlobalSearchResultDialogViewModel viewModel; @@ -51,11 +53,11 @@ public GlobalSearchResultDialog(UndoManager undoManager) { private void initialize() { viewModel = new GlobalSearchResultDialogViewModel(preferencesService); - PreviewViewer previewViewer = new PreviewViewer(viewModel.getSearchDatabaseContext(), dialogService, preferencesService, stateManager, themeManager); + PreviewViewer previewViewer = new PreviewViewer(viewModel.getSearchDatabaseContext(), dialogService, preferencesService, stateManager, themeManager, taskExecutor); previewViewer.setLayout(preferencesService.getPreviewPreferences().getSelectedPreviewLayout()); SearchResultsTableDataModel model = new SearchResultsTableDataModel(viewModel.getSearchDatabaseContext(), preferencesService, stateManager); - SearchResultsTable resultsTable = new SearchResultsTable(model, viewModel.getSearchDatabaseContext(), preferencesService, undoManager, dialogService, stateManager); + SearchResultsTable resultsTable = new SearchResultsTable(model, viewModel.getSearchDatabaseContext(), preferencesService, undoManager, dialogService, stateManager, taskExecutor); resultsTable.getColumns().removeIf(SpecialFieldColumn.class::isInstance); resultsTable.getSelectionModel().selectFirst(); diff --git a/src/main/java/org/jabref/gui/search/RebuildFulltextSearchIndexAction.java b/src/main/java/org/jabref/gui/search/RebuildFulltextSearchIndexAction.java index 90d4fe5f1bd..4fb2a575ab6 100644 --- a/src/main/java/org/jabref/gui/search/RebuildFulltextSearchIndexAction.java +++ b/src/main/java/org/jabref/gui/search/RebuildFulltextSearchIndexAction.java @@ -3,11 +3,11 @@ import java.io.IOException; import org.jabref.gui.DialogService; -import org.jabref.gui.Globals; import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; import org.jabref.logic.pdf.search.indexing.PdfIndexer; import org.jabref.model.database.BibDatabaseContext; @@ -26,16 +26,22 @@ public class RebuildFulltextSearchIndexAction extends SimpleCommand { private final GetCurrentLibraryTab currentLibraryTab; private final DialogService dialogService; private final FilePreferences filePreferences; + private final TaskExecutor taskExecutor; private BibDatabaseContext databaseContext; private boolean shouldContinue = true; - public RebuildFulltextSearchIndexAction(StateManager stateManager, GetCurrentLibraryTab currentLibraryTab, DialogService dialogService, FilePreferences filePreferences) { + public RebuildFulltextSearchIndexAction(StateManager stateManager, + GetCurrentLibraryTab currentLibraryTab, + DialogService dialogService, + FilePreferences filePreferences, + TaskExecutor taskExecutor) { this.stateManager = stateManager; this.currentLibraryTab = currentLibraryTab; this.dialogService = dialogService; this.filePreferences = filePreferences; + this.taskExecutor = taskExecutor; this.executable.bind(needsDatabase(stateManager)); } @@ -44,7 +50,7 @@ public RebuildFulltextSearchIndexAction(StateManager stateManager, GetCurrentLib public void execute() { init(); BackgroundTask.wrap(this::rebuildIndex) - .executeWith(Globals.TASK_EXECUTOR); + .executeWith(taskExecutor); } public void init() { diff --git a/src/main/java/org/jabref/gui/search/SearchResultsTable.java b/src/main/java/org/jabref/gui/search/SearchResultsTable.java index 5d839d2a4a1..2e4a3c710f5 100644 --- a/src/main/java/org/jabref/gui/search/SearchResultsTable.java +++ b/src/main/java/org/jabref/gui/search/SearchResultsTable.java @@ -18,6 +18,7 @@ import org.jabref.gui.maintable.SmartConstrainedResizePolicy; import org.jabref.gui.maintable.columns.LibraryColumn; import org.jabref.gui.maintable.columns.MainTableColumn; +import org.jabref.gui.util.TaskExecutor; import org.jabref.model.database.BibDatabaseContext; import org.jabref.preferences.PreferencesService; @@ -28,18 +29,20 @@ public SearchResultsTable(SearchResultsTableDataModel model, PreferencesService preferencesService, UndoManager undoManager, DialogService dialogService, - StateManager stateManager) { + StateManager stateManager, + TaskExecutor taskExecutor) { super(); MainTablePreferences mainTablePreferences = preferencesService.getMainTablePreferences(); List> allCols = new MainTableColumnFactory( - database, - preferencesService, - preferencesService.getSearchDialogColumnPreferences(), - undoManager, - dialogService, - stateManager).createColumns(); + database, + preferencesService, + preferencesService.getSearchDialogColumnPreferences(), + undoManager, + dialogService, + stateManager, + taskExecutor).createColumns(); if (allCols.stream().noneMatch(LibraryColumn.class::isInstance)) { allCols.add(0, new LibraryColumn()); diff --git a/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogView.java b/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogView.java index d2a5bbe0911..a3307e7dad4 100644 --- a/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogView.java +++ b/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogView.java @@ -18,6 +18,7 @@ import org.jabref.gui.util.BaseDialog; import org.jabref.gui.util.ControlHelper; import org.jabref.gui.util.IconValidationDecorator; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; import org.jabref.logic.shared.DBMSType; import org.jabref.model.entry.BibEntryTypesManager; @@ -53,6 +54,7 @@ public class SharedDatabaseLoginDialogView extends BaseDialog { @Inject private BibEntryTypesManager entryTypesManager; @Inject private FileUpdateMonitor fileUpdateMonitor; @Inject private UndoManager undoManager; + @Inject private TaskExecutor taskExecutor; private final JabRefFrame frame; private SharedDatabaseLoginDialogViewModel viewModel; @@ -93,7 +95,8 @@ private void initialize() { stateManager, entryTypesManager, fileUpdateMonitor, - undoManager); + undoManager, + taskExecutor); databaseType.getItems().addAll(DBMSType.values()); databaseType.getSelectionModel().select(0); diff --git a/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogViewModel.java b/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogViewModel.java index a20b8c9458d..0e13f6bd2a4 100644 --- a/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogViewModel.java +++ b/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogViewModel.java @@ -31,6 +31,7 @@ import org.jabref.gui.help.HelpAction; import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.gui.util.FileFilterConverter; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.help.HelpFile; import org.jabref.logic.l10n.Localization; import org.jabref.logic.shared.DBMSConnectionProperties; @@ -84,6 +85,7 @@ public class SharedDatabaseLoginDialogViewModel extends AbstractViewModel { private final BibEntryTypesManager entryTypesManager; private final FileUpdateMonitor fileUpdateMonitor; private final UndoManager undoManager; + private final TaskExecutor taskExecutor; private final Validator databaseValidator; private final Validator hostValidator; @@ -99,7 +101,8 @@ public SharedDatabaseLoginDialogViewModel(JabRefFrame frame, StateManager stateManager, BibEntryTypesManager entryTypesManager, FileUpdateMonitor fileUpdateMonitor, - UndoManager undoManager) { + UndoManager undoManager, + TaskExecutor taskExecutor) { this.frame = frame; this.dialogService = dialogService; this.preferencesService = preferencesService; @@ -107,6 +110,7 @@ public SharedDatabaseLoginDialogViewModel(JabRefFrame frame, this.entryTypesManager = entryTypesManager; this.fileUpdateMonitor = fileUpdateMonitor; this.undoManager = undoManager; + this.taskExecutor = taskExecutor; EasyBind.subscribe(selectedDBMSType, selected -> port.setValue(Integer.toString(selected.getDefaultPort()))); @@ -176,7 +180,15 @@ private boolean openSharedDatabase(DBMSConnectionProperties connectionProperties loading.set(true); try { - SharedDatabaseUIManager manager = new SharedDatabaseUIManager(frame, dialogService, preferencesService, stateManager, entryTypesManager, fileUpdateMonitor, undoManager); + SharedDatabaseUIManager manager = new SharedDatabaseUIManager( + frame, + dialogService, + preferencesService, + stateManager, + entryTypesManager, + fileUpdateMonitor, + undoManager, + taskExecutor); LibraryTab libraryTab = manager.openNewSharedDatabaseTab(connectionProperties); setPreferences(); diff --git a/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java b/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java index 1508f5651a0..44d92e9c216 100644 --- a/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java +++ b/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java @@ -20,6 +20,7 @@ import org.jabref.gui.mergeentries.EntriesMergeResult; import org.jabref.gui.mergeentries.MergeEntriesDialog; import org.jabref.gui.undo.UndoableRemoveEntries; +import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; import org.jabref.logic.shared.DBMSConnection; @@ -51,6 +52,7 @@ public class SharedDatabaseUIManager { private final BibEntryTypesManager entryTypesManager; private final FileUpdateMonitor fileUpdateMonitor; private final UndoManager undoManager; + private final TaskExecutor taskExecutor; public SharedDatabaseUIManager(JabRefFrame frame, DialogService dialogService, @@ -58,7 +60,8 @@ public SharedDatabaseUIManager(JabRefFrame frame, StateManager stateManager, BibEntryTypesManager entryTypesManager, FileUpdateMonitor fileUpdateMonitor, - UndoManager undoManager) { + UndoManager undoManager, + TaskExecutor taskExecutor) { this.frame = frame; this.dialogService = dialogService; this.preferencesService = preferencesService; @@ -66,6 +69,7 @@ public SharedDatabaseUIManager(JabRefFrame frame, this.entryTypesManager = entryTypesManager; this.fileUpdateMonitor = fileUpdateMonitor; this.undoManager = undoManager; + this.taskExecutor = taskExecutor; } @Subscribe @@ -175,7 +179,8 @@ public LibraryTab openNewSharedDatabaseTab(DBMSConnectionProperties dbmsConnecti stateManager, fileUpdateMonitor, entryTypesManager, - undoManager); + undoManager, + taskExecutor); frame.addTab(libraryTab, true); return libraryTab; } diff --git a/src/main/java/org/jabref/gui/sidepane/SidePane.java b/src/main/java/org/jabref/gui/sidepane/SidePane.java index f9eb7e89631..fa8b74e170a 100644 --- a/src/main/java/org/jabref/gui/sidepane/SidePane.java +++ b/src/main/java/org/jabref/gui/sidepane/SidePane.java @@ -11,10 +11,13 @@ import javafx.scene.layout.VBox; import org.jabref.gui.DialogService; +import org.jabref.gui.LibraryTabContainer; import org.jabref.gui.StateManager; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; public class SidePane extends VBox { @@ -26,15 +29,27 @@ public class SidePane extends VBox { @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private final Map visibleBindings = new HashMap<>(); - public SidePane(PreferencesService preferencesService, + public SidePane(LibraryTabContainer tabContainer, + PreferencesService preferencesService, JournalAbbreviationRepository abbreviationRepository, TaskExecutor taskExecutor, DialogService dialogService, StateManager stateManager, + FileUpdateMonitor fileUpdateMonitor, + BibEntryTypesManager entryTypesManager, UndoManager undoManager) { this.stateManager = stateManager; this.preferencesService = preferencesService; - this.viewModel = new SidePaneViewModel(preferencesService, abbreviationRepository, stateManager, taskExecutor, dialogService, undoManager); + this.viewModel = new SidePaneViewModel( + tabContainer, + preferencesService, + abbreviationRepository, + stateManager, + taskExecutor, + dialogService, + fileUpdateMonitor, + entryTypesManager, + undoManager); stateManager.getVisibleSidePaneComponents().addListener((ListChangeListener) c -> updateView()); updateView(); diff --git a/src/main/java/org/jabref/gui/sidepane/SidePaneContentFactory.java b/src/main/java/org/jabref/gui/sidepane/SidePaneContentFactory.java index 7fe1f43fcc5..8a8c5a03af4 100644 --- a/src/main/java/org/jabref/gui/sidepane/SidePaneContentFactory.java +++ b/src/main/java/org/jabref/gui/sidepane/SidePaneContentFactory.java @@ -5,33 +5,45 @@ import javafx.scene.Node; import org.jabref.gui.DialogService; +import org.jabref.gui.LibraryTabContainer; import org.jabref.gui.StateManager; import org.jabref.gui.groups.GroupTreeView; import org.jabref.gui.importer.fetcher.WebSearchPaneView; import org.jabref.gui.openoffice.OpenOfficePanel; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; public class SidePaneContentFactory { + private final LibraryTabContainer tabContainer; private final PreferencesService preferences; private final JournalAbbreviationRepository abbreviationRepository; private final TaskExecutor taskExecutor; private final DialogService dialogService; private final StateManager stateManager; + private final FileUpdateMonitor fileUpdateMonitor; + private final BibEntryTypesManager entryTypesManager; private final UndoManager undoManager; - public SidePaneContentFactory(PreferencesService preferences, + public SidePaneContentFactory(LibraryTabContainer tabContainer, + PreferencesService preferences, JournalAbbreviationRepository abbreviationRepository, TaskExecutor taskExecutor, DialogService dialogService, StateManager stateManager, + FileUpdateMonitor fileUpdateMonitor, + BibEntryTypesManager entryTypesManager, UndoManager undoManager) { + this.tabContainer = tabContainer; this.preferences = preferences; this.abbreviationRepository = abbreviationRepository; this.taskExecutor = taskExecutor; this.dialogService = dialogService; this.stateManager = stateManager; + this.fileUpdateMonitor = fileUpdateMonitor; + this.entryTypesManager = entryTypesManager; this.undoManager = undoManager; } @@ -43,12 +55,15 @@ public Node create(SidePaneType sidePaneType) { preferences, dialogService); case OPEN_OFFICE -> new OpenOfficePanel( + tabContainer, preferences, preferences.getKeyBindingRepository(), abbreviationRepository, taskExecutor, dialogService, stateManager, + fileUpdateMonitor, + entryTypesManager, undoManager).getContent(); case WEB_SEARCH -> new WebSearchPaneView( preferences, diff --git a/src/main/java/org/jabref/gui/sidepane/SidePaneViewModel.java b/src/main/java/org/jabref/gui/sidepane/SidePaneViewModel.java index e27d5c22d08..16d500b57b1 100644 --- a/src/main/java/org/jabref/gui/sidepane/SidePaneViewModel.java +++ b/src/main/java/org/jabref/gui/sidepane/SidePaneViewModel.java @@ -15,10 +15,13 @@ import org.jabref.gui.AbstractViewModel; import org.jabref.gui.DialogService; +import org.jabref.gui.LibraryTabContainer; import org.jabref.gui.StateManager; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; import org.jabref.preferences.SidePanePreferences; @@ -35,21 +38,27 @@ public class SidePaneViewModel extends AbstractViewModel { private final SidePaneContentFactory sidePaneContentFactory; private final DialogService dialogService; - public SidePaneViewModel(PreferencesService preferencesService, + public SidePaneViewModel(LibraryTabContainer tabContainer, + PreferencesService preferencesService, JournalAbbreviationRepository abbreviationRepository, StateManager stateManager, TaskExecutor taskExecutor, DialogService dialogService, + FileUpdateMonitor fileUpdateMonitor, + BibEntryTypesManager entryTypesManager, UndoManager undoManager) { this.preferencesService = preferencesService; this.stateManager = stateManager; this.dialogService = dialogService; this.sidePaneContentFactory = new SidePaneContentFactory( + tabContainer, preferencesService, abbreviationRepository, taskExecutor, dialogService, stateManager, + fileUpdateMonitor, + entryTypesManager, undoManager); preferencesService.getSidePanePreferences().visiblePanes().forEach(this::show); diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 2c9d4162851..24b1931b1c9 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1529,11 +1529,10 @@ UPPER\ CASE=UPPER CASE Does\ nothing.=Does nothing. Identity=Identity Clears\ the\ field\ completely.=Clears the field completely. -Directory\ not\ found=Directory not found Main\ file\ directory=Main file directory -Main\ file\ directory\ not\ set.\ Check\ the\ preferences\ ("Linked\ files")\ or\ modify\ the\ library\ properties\ ("Override\ default\ file\ directories").=Main file directory not set. Check the preferences ("Linked files") or modify the library properties ("Override default file directories"). Main\ file\ directory\ '%0'\ not\ found.\nCheck\ the\ tab\ "Linked\ files".=Main file directory '%0' not found.\nCheck the tab "Linked files". +No\ library\ selected=No library selected This\ operation\ requires\ exactly\ one\ item\ to\ be\ selected.=This operation requires exactly one item to be selected. Opening\ large\ number\ of\ files=Opening large number of files diff --git a/src/test/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModelTest.java b/src/test/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModelTest.java index 089987ac428..584fdcdadf0 100644 --- a/src/test/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModelTest.java +++ b/src/test/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModelTest.java @@ -4,6 +4,8 @@ import java.nio.file.Path; import java.util.Optional; +import javax.swing.undo.UndoManager; + import javafx.collections.FXCollections; import org.jabref.gui.DialogService; @@ -33,6 +35,7 @@ class LinkedFilesEditorViewModelTest { private final PreferencesService preferencesService = mock(PreferencesService.class, Answers.RETURNS_DEEP_STUBS); private final FilePreferences filePreferences = mock(FilePreferences.class, Answers.RETURNS_DEEP_STUBS); private final BibDatabaseContext bibDatabaseContext = mock(BibDatabaseContext.class); + private final UndoManager undoManager = mock(UndoManager.class); @Test void urlFieldShouldDownloadFile(@TempDir Path tempDir) { @@ -43,7 +46,7 @@ void urlFieldShouldDownloadFile(@TempDir Path tempDir) { when(bibDatabaseContext.getFirstExistingFileDir(any())).thenReturn(Optional.of(tempDir)); viewModel = new LinkedFilesEditorViewModel(StandardField.FILE, new EmptySuggestionProvider(), mock(DialogService.class), bibDatabaseContext, - new CurrentThreadTaskExecutor(), mock(FieldCheckers.class), preferencesService); + new CurrentThreadTaskExecutor(), mock(FieldCheckers.class), preferencesService, undoManager); BibEntry entry = new BibEntry().withCitationKey("test") .withField(StandardField.URL, "https://ceur-ws.org/Vol-847/paper6.pdf"); diff --git a/src/test/java/org/jabref/gui/sidepane/SidePaneViewModelTest.java b/src/test/java/org/jabref/gui/sidepane/SidePaneViewModelTest.java index 49bd0c0073b..27db78fe64d 100644 --- a/src/test/java/org/jabref/gui/sidepane/SidePaneViewModelTest.java +++ b/src/test/java/org/jabref/gui/sidepane/SidePaneViewModelTest.java @@ -10,11 +10,14 @@ import javafx.collections.ObservableList; import org.jabref.gui.DialogService; +import org.jabref.gui.LibraryTabContainer; import org.jabref.gui.StateManager; import org.jabref.gui.util.CustomLocalDragboard; import org.jabref.gui.util.OptionalObjectProperty; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; import org.jabref.preferences.SidePanePreferences; @@ -31,11 +34,14 @@ @ExtendWith(ApplicationExtension.class) class SidePaneViewModelTest { + LibraryTabContainer tabContainer = mock(LibraryTabContainer.class); PreferencesService preferencesService = mock(PreferencesService.class); JournalAbbreviationRepository abbreviationRepository = mock(JournalAbbreviationRepository.class); StateManager stateManager = mock(StateManager.class); TaskExecutor taskExecutor = mock(TaskExecutor.class); DialogService dialogService = mock(DialogService.class); + FileUpdateMonitor fileUpdateMonitor = mock(FileUpdateMonitor.class); + BibEntryTypesManager entryTypesManager = mock(BibEntryTypesManager.class); UndoManager undoManager = mock(UndoManager.class); SidePanePreferences sidePanePreferences = new SidePanePreferences(new HashSet<>(), new HashMap<>(), 0); @@ -54,7 +60,16 @@ void setUp() { sidePanePreferences.getPreferredPositions().put(SidePaneType.WEB_SEARCH, 1); sidePanePreferences.getPreferredPositions().put(SidePaneType.OPEN_OFFICE, 2); - sidePaneViewModel = new SidePaneViewModel(preferencesService, abbreviationRepository, stateManager, taskExecutor, dialogService, undoManager); + sidePaneViewModel = new SidePaneViewModel( + tabContainer, + preferencesService, + abbreviationRepository, + stateManager, + taskExecutor, + dialogService, + fileUpdateMonitor, + entryTypesManager, + undoManager); } @Test From 1dfa17dca65e44c0a5fd67c9740b68ca03ebd086 Mon Sep 17 00:00:00 2001 From: Christoph Date: Mon, 11 Sep 2023 22:19:48 +0200 Subject: [PATCH 23/94] Fix groups validator (#10369) * Fix groups * changelog --- CHANGELOG.md | 1 + .../java/org/jabref/gui/groups/GroupDialogViewModel.java | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ece9696cc3a..c194def4d6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where groups based on an aux file could not be created due to an exception [#10350](https://github.com/JabRef/jabref/issues/10350) - We fixed an issue where the JabRef browser extension could not communicate with JabRef under macOS due to missing files. You should use the `.pkg` for the first installation as it updates all necessary files for the extension [#10308](https://github.com/JabRef/jabref/issues/10308) - We fixed a bug where an exception was raised when saving less than three export save orders in the preference. [#10157](https://github.com/JabRef/jabref/issues/10157) +- We fixed an issue where it was possible to create a group with no name or with a group separator inside the name [#9776](https://github.com/JabRef/jabref/issues/9776) ### Removed diff --git a/src/main/java/org/jabref/gui/groups/GroupDialogViewModel.java b/src/main/java/org/jabref/gui/groups/GroupDialogViewModel.java index e18d431d0bd..72f98979fc0 100644 --- a/src/main/java/org/jabref/gui/groups/GroupDialogViewModel.java +++ b/src/main/java/org/jabref/gui/groups/GroupDialogViewModel.java @@ -103,7 +103,7 @@ public class GroupDialogViewModel { private Validator searchRegexValidator; private Validator searchSearchTermEmptyValidator; private Validator texGroupFilePathValidator; - private final CompositeValidator validator = new CompositeValidator(); + private CompositeValidator validator; private final DialogService dialogService; private final PreferencesService preferencesService; @@ -127,6 +127,8 @@ public GroupDialogViewModel(DialogService dialogService, } private void setupValidation() { + validator = new CompositeValidator(); + nameValidator = new FunctionBasedValidator<>( nameProperty, StringUtil::isNotBlank, @@ -270,6 +272,10 @@ private void setupValidation() { validator.removeValidators(texGroupFilePathValidator); } }); + + validator.addValidators(nameValidator, + nameContainsDelimiterValidator, + sameNameValidator); } /** From 82017e09a86c8bd3a4c5e176f1c2c232e1571a46 Mon Sep 17 00:00:00 2001 From: Christoph Date: Mon, 11 Sep 2023 22:20:23 +0200 Subject: [PATCH 24/94] FIX NPE in inspire fetcher (#10368) --- .../jabref/logic/importer/fetcher/INSPIREFetcher.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/jabref/logic/importer/fetcher/INSPIREFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/INSPIREFetcher.java index 177c95f7e71..67f9e84afc0 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/INSPIREFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/INSPIREFetcher.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.net.MalformedURLException; +import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; @@ -93,20 +94,20 @@ public List performSearch(BibEntry entry) throws FetcherException { Optional eprint = entry.getField(StandardField.EPRINT); String url; - if ("arXiv".equals(archiveprefix.get()) && !eprint.isEmpty()) { + if (archiveprefix.filter("arxiv"::equals).isPresent() && eprint.isPresent()) { url = INSPIRE_ARXIV_HOST + eprint.get(); - } else if (!doi.isEmpty()) { + } else if (doi.isPresent()) { url = INSPIRE_DOI_HOST + doi.get(); } else { return results; } try { - URLDownload download = getUrlDownload(new URL(url)); + URLDownload download = getUrlDownload(new URI(url).toURL()); results = getParser().parseEntries(download.asInputStream()); results.forEach(this::doPostCleanup); return results; - } catch (IOException | ParseException e) { + } catch (IOException | ParseException | URISyntaxException e) { throw new FetcherException("Error occurred during fetching", e); } } From 8d7d772f610fda49addf6b28661ba675f181ad17 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 11 Sep 2023 23:05:07 +0200 Subject: [PATCH 25/94] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c194def4d6a..7d4d4431835 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Added -- We added an error-specific message for when a download from a URL fails[#9826](https://github.com/JabRef/jabref/issues/9826). +- We added an error-specific message for when a download from a URL fails. [#9826](https://github.com/JabRef/jabref/issues/9826) +- We added support for customizating the citation command (e.g., `[@key1,@key2]`) when [pushing to external applications](https://docs.jabref.org/cite/pushtoapplications). [#10133](https://github.com/JabRef/jabref/issues/10133) - We added protected terms described as "Computer science". [#10222](https://github.com/JabRef/jabref/pull/10222) ### Changed From 0c6c0a9136547cc5dd18a16d9ecee934e9f7180a Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Mon, 11 Sep 2023 22:16:59 +0100 Subject: [PATCH 26/94] Enable the exploration of bib entries' relations (cited by and citing) (#10324) Co-authored-by: Tim Bachmann <58782027+timbachmann@users.noreply.github.com> Co-authored-by: Raphael Kreft Co-authored-by: Marcel Luethi Co-authored-by: olivierM Co-authored-by: matiascg --- CHANGELOG.md | 1 + .../jabref/gui/entryeditor/EntryEditor.java | 3 + .../BibEntryRelationsCache.java | 38 ++ .../BibEntryRelationsRepository.java | 56 +++ .../citationrelationtab/BibEntryView.java | 74 ++++ .../CitationRelationItem.java | 24 + .../CitationRelationsTab.css | 20 + .../CitationRelationsTab.java | 415 ++++++++++++++++++ .../semanticscholar/AuthorResponse.java | 22 + .../semanticscholar/CitationDataItem.java | 13 + .../semanticscholar/CitationFetcher.java | 50 +++ .../semanticscholar/CitationsResponse.java | 33 ++ .../semanticscholar/PaperDetails.java | 86 ++++ .../semanticscholar/ReferenceDataItem.java | 13 + .../semanticscholar/ReferencesResponse.java | 33 ++ .../SemanticScholarFetcher.java | 78 ++++ src/main/resources/l10n/JabRef_en.properties | 14 + 17 files changed, 973 insertions(+) create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryRelationsCache.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryRelationsRepository.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryView.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationItem.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.css create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/AuthorResponse.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationDataItem.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationFetcher.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationsResponse.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/PaperDetails.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/ReferenceDataItem.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/ReferencesResponse.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d4d4431835..9ecf850684d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Added +- We added the possibility to find (and add) papers that cite or are cited by a given paper. [#6187](https://github.com/JabRef/jabref/issues/6187) - We added an error-specific message for when a download from a URL fails. [#9826](https://github.com/JabRef/jabref/issues/9826) - We added support for customizating the citation command (e.g., `[@key1,@key2]`) when [pushing to external applications](https://docs.jabref.org/cite/pushtoapplications). [#10133](https://github.com/JabRef/jabref/issues/10133) - We added protected terms described as "Computer science". [#10222](https://github.com/JabRef/jabref/pull/10222) diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index 6f67d62103a..3d42d562a26 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -31,6 +31,7 @@ import org.jabref.gui.StateManager; import org.jabref.gui.citationkeypattern.GenerateCitationKeySingleAction; import org.jabref.gui.cleanup.CleanupSingleAction; +import org.jabref.gui.entryeditor.citationrelationtab.CitationRelationsTab; import org.jabref.gui.entryeditor.fileannotationtab.FileAnnotationTab; import org.jabref.gui.entryeditor.fileannotationtab.FulltextSearchResultsTab; import org.jabref.gui.externalfiles.ExternalFilesEntryLinker; @@ -282,6 +283,8 @@ private List createTabs() { entryEditorTabs.add(new MathSciNetTab()); entryEditorTabs.add(new FileAnnotationTab(libraryTab.getAnnotationCache())); entryEditorTabs.add(new RelatedArticlesTab(entryEditorPreferences, preferencesService, dialogService, taskExecutor)); + entryEditorTabs.add(new CitationRelationsTab(entryEditorPreferences, dialogService, databaseContext, + undoManager, stateManager, fileMonitor, preferencesService, libraryTab)); sourceTab = new SourceTab( databaseContext, diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryRelationsCache.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryRelationsCache.java new file mode 100644 index 00000000000..3f2f9f53b7b --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryRelationsCache.java @@ -0,0 +1,38 @@ +package org.jabref.gui.entryeditor.citationrelationtab; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.identifier.DOI; + +public class BibEntryRelationsCache { + private static final Map> CITATIONS_MAP = new HashMap<>(); + private static final Map> REFERENCES_MAP = new HashMap<>(); + + public List getCitations(BibEntry entry) { + return CITATIONS_MAP.getOrDefault(entry.getDOI().map(DOI::getDOI).orElse(""), Collections.emptyList()); + } + + public List getReferences(BibEntry entry) { + return REFERENCES_MAP.getOrDefault(entry.getDOI().map(DOI::getDOI).orElse(""), Collections.emptyList()); + } + + public void cacheOrMergeCitations(BibEntry entry, List citations) { + entry.getDOI().ifPresent(doi -> CITATIONS_MAP.put(doi.getDOI(), citations)); + } + + public void cacheOrMergeReferences(BibEntry entry, List references) { + entry.getDOI().ifPresent(doi -> REFERENCES_MAP.putIfAbsent(doi.getDOI(), references)); + } + + public boolean citationsCached(BibEntry entry) { + return CITATIONS_MAP.containsKey(entry.getDOI().map(DOI::getDOI).orElse("")); + } + + public boolean referencesCached(BibEntry entry) { + return REFERENCES_MAP.containsKey(entry.getDOI().map(DOI::getDOI).orElse("")); + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryRelationsRepository.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryRelationsRepository.java new file mode 100644 index 00000000000..d042c41df50 --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryRelationsRepository.java @@ -0,0 +1,56 @@ +package org.jabref.gui.entryeditor.citationrelationtab; + +import java.util.List; + +import org.jabref.gui.entryeditor.citationrelationtab.semanticscholar.SemanticScholarFetcher; +import org.jabref.logic.importer.FetcherException; +import org.jabref.model.entry.BibEntry; + +public class BibEntryRelationsRepository { + private final SemanticScholarFetcher fetcher; + private final BibEntryRelationsCache cache; + + public BibEntryRelationsRepository(SemanticScholarFetcher fetcher, BibEntryRelationsCache cache) { + this.fetcher = fetcher; + this.cache = cache; + } + + public List getCitations(BibEntry entry) { + if (needToRefreshCitations(entry)) { + forceRefreshCitations(entry); + } + + return cache.getCitations(entry); + } + + public List getReferences(BibEntry entry) { + if (needToRefreshReferences(entry)) { + List references = fetcher.searchCiting(entry); + cache.cacheOrMergeReferences(entry, references); + } + + return cache.getReferences(entry); + } + + public void forceRefreshCitations(BibEntry entry) { + try { + List citations = fetcher.searchCitedBy(entry); + cache.cacheOrMergeCitations(entry, citations); + } catch (FetcherException e) { + throw new RuntimeException(e); + } + } + + public boolean needToRefreshCitations(BibEntry entry) { + return !cache.citationsCached(entry); + } + + public boolean needToRefreshReferences(BibEntry entry) { + return !cache.referencesCached(entry); + } + + public void forceRefreshReferences(BibEntry entry) { + List references = fetcher.searchCiting(entry); + cache.cacheOrMergeReferences(entry, references); + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryView.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryView.java new file mode 100644 index 00000000000..de5bb165576 --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/BibEntryView.java @@ -0,0 +1,74 @@ +package org.jabref.gui.entryeditor.citationrelationtab; + +import java.util.EnumSet; + +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; + +import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.util.TextFlowLimited; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.types.EntryType; +import org.jabref.model.entry.types.StandardEntryType; + +/** + * Class to unify the display method of BibEntries in ListViews. + */ +public class BibEntryView { + + /** + * Creates a layout for a given {@link BibEntry} to be displayed in a List + * + * @param entry {@link BibEntry} to display + * @return layout container displaying the entry + */ + public static Node getEntryNode(BibEntry entry) { + Node entryType = getIcon(entry.getType()).getGraphicNode(); + entryType.getStyleClass().add("type"); + Label authors = new Label(entry.getFieldOrAliasLatexFree(StandardField.AUTHOR).orElse("")); + authors.getStyleClass().add("authors"); + authors.setWrapText(true); + Label title = new Label(entry.getFieldOrAliasLatexFree(StandardField.TITLE).orElse("")); + title.getStyleClass().add("title"); + title.setWrapText(true); + Label year = new Label(entry.getFieldOrAliasLatexFree(StandardField.YEAR).orElse("")); + year.getStyleClass().add("year"); + Label journal = new Label(entry.getFieldOrAliasLatexFree(StandardField.JOURNAL).orElse("")); + journal.getStyleClass().add("journal"); + + VBox entryContainer = new VBox( + new HBox(10, entryType, title), + new HBox(5, year, journal), + authors + ); + entry.getFieldOrAliasLatexFree(StandardField.ABSTRACT).ifPresent(summaryText -> { + TextFlowLimited summary = new TextFlowLimited(new Text(summaryText)); + summary.getStyleClass().add("summary"); + entryContainer.getChildren().add(summary); + }); + + entryContainer.getStyleClass().add("bibEntry"); + return entryContainer; + } + + /** + * Gets the correct Icon for a given {@link EntryType} + * + * @param type {@link EntryType} to get Icon for + * @return Icon corresponding to {@link EntryType} + */ + private static IconTheme.JabRefIcons getIcon(EntryType type) { + EnumSet crossRefTypes = EnumSet.of(StandardEntryType.InBook, + StandardEntryType.InProceedings, StandardEntryType.InCollection); + if (type == StandardEntryType.Book) { + return IconTheme.JabRefIcons.BOOK; + } else if (crossRefTypes.contains(type)) { + return IconTheme.JabRefIcons.OPEN_LINK; + } + return IconTheme.JabRefIcons.ARTICLE; + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationItem.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationItem.java new file mode 100644 index 00000000000..aacbc90cc5f --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationItem.java @@ -0,0 +1,24 @@ +package org.jabref.gui.entryeditor.citationrelationtab; + +import org.jabref.model.entry.BibEntry; + +/** + * Class to hold a BibEntry and a boolean value whether it is already in the current database or not. + */ +public class CitationRelationItem { + private final BibEntry entry; + private final boolean isLocal; + + public CitationRelationItem(BibEntry entry, boolean isLocal) { + this.entry = entry; + this.isLocal = isLocal; + } + + public BibEntry getEntry() { + return entry; + } + + public boolean isLocal() { + return isLocal; + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.css b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.css new file mode 100644 index 00000000000..d4ba330cdaa --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.css @@ -0,0 +1,20 @@ +.addEntryButton { + -fx-font-size: 2em; +} + +.addEntryButton:selected { + -fx-background-color: transparent; + -fx-fill: -jr-selected; +} + +.entry-container { + -fx-padding: 0.5em 0em 0.5em 0em; +} + +.list-cell:default { + -fx-padding: 0.5em 0.1em 0.5em 0em; +} + +.list-cell:selected { + -fx-background-color: derive(-jr-selected, 60%); +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java new file mode 100644 index 00000000000..d3c6862aaf6 --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java @@ -0,0 +1,415 @@ +package org.jabref.gui.entryeditor.citationrelationtab; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import javax.swing.undo.UndoManager; + +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.css.PseudoClass; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.SplitPane; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; + +import org.jabref.gui.DialogService; +import org.jabref.gui.Globals; +import org.jabref.gui.LibraryTab; +import org.jabref.gui.StateManager; +import org.jabref.gui.entryeditor.EntryEditorPreferences; +import org.jabref.gui.entryeditor.EntryEditorTab; +import org.jabref.gui.entryeditor.citationrelationtab.semanticscholar.CitationFetcher; +import org.jabref.gui.entryeditor.citationrelationtab.semanticscholar.SemanticScholarFetcher; +import org.jabref.gui.externalfiles.ImportHandler; +import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.NoSelectionModel; +import org.jabref.gui.util.ViewModelListCellFactory; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.util.FileUpdateMonitor; +import org.jabref.preferences.PreferencesService; + +import com.tobiasdiez.easybind.EasyBind; +import org.controlsfx.control.CheckListView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * GUI for tab displaying an articles citation relations in two lists based on the currently selected BibEntry + */ +public class CitationRelationsTab extends EntryEditorTab { + + private static final Logger LOGGER = LoggerFactory.getLogger(CitationRelationsTab.class); + + // Tasks used to implement asynchronous fetching of related articles + private static BackgroundTask> citingTask; + private static BackgroundTask> citedByTask; + private final EntryEditorPreferences preferences; + private final DialogService dialogService; + private final BibDatabaseContext databaseContext; + private final UndoManager undoManager; + private final StateManager stateManager; + private final FileUpdateMonitor fileUpdateMonitor; + private final PreferencesService preferencesService; + private final LibraryTab libraryTab; + private final BibEntryRelationsRepository bibEntryRelationsRepository; + + public CitationRelationsTab(EntryEditorPreferences preferences, DialogService dialogService, + BibDatabaseContext databaseContext, UndoManager undoManager, + StateManager stateManager, FileUpdateMonitor fileUpdateMonitor, + PreferencesService preferencesService, LibraryTab lTab) { + this.preferences = preferences; + this.dialogService = dialogService; + this.databaseContext = databaseContext; + this.undoManager = undoManager; + this.stateManager = stateManager; + this.fileUpdateMonitor = fileUpdateMonitor; + this.preferencesService = preferencesService; + this.libraryTab = lTab; + setText(Localization.lang("Citation relations")); + setTooltip(new Tooltip(Localization.lang("Show articles related by citation"))); + + this.bibEntryRelationsRepository = new BibEntryRelationsRepository(new SemanticScholarFetcher(), + new BibEntryRelationsCache()); + } + + /** + * Method to create main SplitPane holding all lists, buttons and labels for tab and starts search + * + * @param entry BibEntry which is currently selected in JabRef Database + * @return SplitPane to display + */ + private SplitPane getPaneAndStartSearch(BibEntry entry) { + // Create Layout Containers + VBox citingVBox = new VBox(); + VBox citedByVBox = new VBox(); + citingVBox.setFillWidth(true); + citedByVBox.setFillWidth(true); + citingVBox.setAlignment(Pos.TOP_CENTER); + citedByVBox.setAlignment(Pos.TOP_CENTER); + AnchorPane citingHBox = new AnchorPane(); + citingHBox.setPrefHeight(40); + AnchorPane citedByHBox = new AnchorPane(); + citedByHBox.setPrefHeight(40); + + // Create Heading Lab + Label citingLabel = new Label(Localization.lang("Citing")); + styleLabel(citingLabel); + Label citedByLabel = new Label(Localization.lang("Cited By")); + styleLabel(citedByLabel); + + // Create ListViews + CheckListView citingListView = new CheckListView<>(); + CheckListView citedByListView = new CheckListView<>(); + + // Create refresh Buttons for both sides + Button refreshCitingButton = IconTheme.JabRefIcons.REFRESH.asButton(); + refreshCitingButton.setTooltip(new Tooltip(Localization.lang("Restart search"))); + styleTopBarNode(refreshCitingButton, 15.0); + Button refreshCitedByButton = IconTheme.JabRefIcons.REFRESH.asButton(); + refreshCitedByButton.setTooltip(new Tooltip(Localization.lang("Restart search"))); + styleTopBarNode(refreshCitedByButton, 15.0); + + // Create abort buttons for both sides + Button abortCitingButton = IconTheme.JabRefIcons.CLOSE.asButton(); + abortCitingButton.getGraphic().resize(30, 30); + abortCitingButton.setTooltip(new Tooltip(Localization.lang("Cancel search"))); + styleTopBarNode(abortCitingButton, 15.0); + Button abortCitedButton = IconTheme.JabRefIcons.CLOSE.asButton(); + abortCitedButton.getGraphic().resize(30, 30); + abortCitedButton.setTooltip(new Tooltip(Localization.lang("Cancel search"))); + styleTopBarNode(abortCitedButton, 15.0); + + ProgressIndicator citingProgress = new ProgressIndicator(); + citingProgress.setMaxSize(25, 25); + styleTopBarNode(citingProgress, 50.0); + ProgressIndicator citedByProgress = new ProgressIndicator(); + citedByProgress.setMaxSize(25, 25); + styleTopBarNode(citedByProgress, 50.0); + + // Create import buttons for both sides + Button importCitingButton = IconTheme.JabRefIcons.ADD_ENTRY.asButton(); + importCitingButton.setTooltip(new Tooltip(Localization.lang("Add selected entries to database"))); + styleTopBarNode(importCitingButton, 50.0); + Button importCitedByButton = IconTheme.JabRefIcons.ADD_ENTRY.asButton(); + importCitedByButton.setTooltip(new Tooltip(Localization.lang("Add selected entries to database"))); + styleTopBarNode(importCitedByButton, 50.0); + hideNodes(importCitingButton, importCitedByButton); + + citingHBox.getChildren().addAll(citingLabel, refreshCitingButton, importCitingButton, citingProgress, abortCitingButton); + citedByHBox.getChildren().addAll(citedByLabel, refreshCitedByButton, importCitedByButton, citedByProgress, abortCitedButton); + + VBox.setVgrow(citingListView, Priority.ALWAYS); + VBox.setVgrow(citedByListView, Priority.ALWAYS); + citingVBox.getChildren().addAll(citingHBox, citingListView); + citedByVBox.getChildren().addAll(citedByHBox, citedByListView); + + refreshCitingButton.setOnMouseClicked(event -> { + searchForRelations(entry, citingListView, abortCitingButton, + refreshCitingButton, CitationFetcher.SearchType.CITING, importCitingButton, citingProgress, true); + }); + + refreshCitedByButton.setOnMouseClicked(event -> searchForRelations(entry, citedByListView, abortCitedButton, + refreshCitedByButton, CitationFetcher.SearchType.CITED_BY, importCitedByButton, citedByProgress, true)); + + // Create SplitPane to hold all nodes above + SplitPane container = new SplitPane(citedByVBox, citingVBox); + + styleFetchedListView(citingListView); + styleFetchedListView(citedByListView); + + searchForRelations(entry, citingListView, abortCitingButton, refreshCitingButton, + CitationFetcher.SearchType.CITING, importCitingButton, citingProgress, false); + + searchForRelations(entry, citedByListView, abortCitedButton, refreshCitedByButton, + CitationFetcher.SearchType.CITED_BY, importCitedByButton, citedByProgress, false); + + return container; + } + + /** + * Styles a given CheckListView to display BibEntries either with a hyperlink or an add button + * + * @param listView CheckListView to style + */ + private void styleFetchedListView(CheckListView listView) { + PseudoClass entrySelected = PseudoClass.getPseudoClass("selected"); + new ViewModelListCellFactory() + .withGraphic(entry -> { + + HBox separator = new HBox(); + HBox.setHgrow(separator, Priority.SOMETIMES); + Node entryNode = BibEntryView.getEntryNode(entry.getEntry()); + HBox.setHgrow(entryNode, Priority.ALWAYS); + HBox hContainer = new HBox(); + hContainer.prefWidthProperty().bind(listView.widthProperty().subtract(25)); + + if (entry.isLocal()) { + Button jumpTo = IconTheme.JabRefIcons.LINK.asButton(); + jumpTo.setTooltip(new Tooltip(Localization.lang("Jump to entry in database"))); + jumpTo.getStyleClass().add("addEntryButton"); + jumpTo.setOnMouseClicked(event -> { + libraryTab.showAndEdit(entry.getEntry()); + libraryTab.clearAndSelect(entry.getEntry()); + citingTask.cancel(); + citedByTask.cancel(); + }); + hContainer.getChildren().addAll(entryNode, separator, jumpTo); + } else { + ToggleButton addToggle = IconTheme.JabRefIcons.ADD.asToggleButton(); + addToggle.setTooltip(new Tooltip(Localization.lang("Select entry"))); + EasyBind.subscribe(addToggle.selectedProperty(), selected -> { + if (selected) { + addToggle.setGraphic(IconTheme.JabRefIcons.ADD_FILLED.withColor(IconTheme.SELECTED_COLOR).getGraphicNode()); + } else { + addToggle.setGraphic(IconTheme.JabRefIcons.ADD.getGraphicNode()); + } + }); + addToggle.getStyleClass().add("addEntryButton"); + addToggle.selectedProperty().bindBidirectional(listView.getItemBooleanProperty(entry)); + hContainer.getChildren().addAll(entryNode, separator, addToggle); + } + hContainer.getStyleClass().add("entry-container"); + + return hContainer; + }) + .withOnMouseClickedEvent((ee, event) -> { + if (!ee.isLocal()) { + listView.getCheckModel().toggleCheckState(ee); + } + }) + .withPseudoClass(entrySelected, listView::getItemBooleanProperty) + .install(listView); + + listView.setSelectionModel(new NoSelectionModel<>()); + } + + /** + * Method to style heading labels + * + * @param label label to style + */ + private void styleLabel(Label label) { + label.setStyle("-fx-padding: 5px"); + label.setAlignment(Pos.CENTER); + AnchorPane.setTopAnchor(label, 0.0); + AnchorPane.setLeftAnchor(label, 0.0); + AnchorPane.setBottomAnchor(label, 0.0); + AnchorPane.setRightAnchor(label, 0.0); + } + + /** + * Method to style refresh buttons + * + * @param node node to style + */ + private void styleTopBarNode(Node node, double offset) { + AnchorPane.setTopAnchor(node, 0.0); + AnchorPane.setBottomAnchor(node, 0.0); + AnchorPane.setRightAnchor(node, offset); + } + + /** + * Determines if tab should be shown according to preferences + * + * @param entry Currently selected BibEntry + * @return whether tab should be shown + */ + @Override + public boolean shouldShow(BibEntry entry) { + // TODO: Create a preference and show tab only if preference is enabled + return true; + } + + @Override + protected void bindToEntry(BibEntry entry) { + setContent(getPaneAndStartSearch(entry)); + } + + /** + * Method to start search for relations and display them in the associated ListView + * + * @param entry BibEntry currently selected in Jabref Database + * @param listView ListView to use + * @param abortButton Button to stop the search + * @param refreshButton refresh Button to use + * @param searchType type of search (CITING / CITEDBY) + */ + private void searchForRelations(BibEntry entry, CheckListView listView, Button abortButton, + Button refreshButton, CitationFetcher.SearchType searchType, Button importButton, + ProgressIndicator progress, boolean shouldRefresh) { + if (entry.getDOI().isEmpty()) { + hideNodes(abortButton, progress); + showNodes(refreshButton); + listView.getItems().clear(); + listView.setPlaceholder( + new Label(Localization.lang("The selected entry doesn't have a DOI linked to it. Lookup a DOI and try again."))); + return; + } + + ObservableList observableList = FXCollections.observableArrayList(); + + listView.setItems(observableList); + + if (citingTask != null && !citingTask.isCanceled() && searchType == CitationFetcher.SearchType.CITING) { + citingTask.cancel(); + } else if (citedByTask != null && !citedByTask.isCanceled() && searchType == CitationFetcher.SearchType.CITED_BY) { + citedByTask.cancel(); + } + + BackgroundTask> task; + + if (searchType == CitationFetcher.SearchType.CITING) { + task = BackgroundTask.wrap(() -> { + if (shouldRefresh) { + bibEntryRelationsRepository.forceRefreshReferences(entry); + } + return bibEntryRelationsRepository.getReferences(entry); + }); + citingTask = task; + } else { + task = BackgroundTask.wrap(() -> { + if (shouldRefresh) { + bibEntryRelationsRepository.forceRefreshCitations(entry); + } + return bibEntryRelationsRepository.getCitations(entry); + }); + citedByTask = task; + } + + task.onRunning(() -> prepareToSearchForRelations(abortButton, refreshButton, importButton, progress, task)) + .onSuccess(fetchedList -> onSearchForRelationsSucceed(entry, listView, abortButton, refreshButton, + searchType, importButton, progress, fetchedList, observableList)) + .onFailure(exception -> { + LOGGER.error("Error while fetching citing Articles", exception); + hideNodes(abortButton, progress, importButton); + listView.setPlaceholder(new Label(Localization.lang("Error while fetching citing entries: %0", + exception.getMessage()))); + + refreshButton.setVisible(true); + dialogService.notify(exception.getMessage()); + }) + .executeWith(Globals.TASK_EXECUTOR); + } + + private void onSearchForRelationsSucceed(BibEntry entry, CheckListView listView, + Button abortButton, Button refreshButton, + CitationFetcher.SearchType searchType, Button importButton, + ProgressIndicator progress, List fetchedList, + ObservableList observableList) { + hideNodes(abortButton, progress); + + observableList.setAll(fetchedList.stream().map(entr -> new CitationRelationItem(entr, false)) + .collect(Collectors.toList())); + + if (!observableList.isEmpty()) { + listView.refresh(); + } else { + Label placeholder = new Label(Localization.lang("No articles found")); + listView.setPlaceholder(placeholder); + } + BooleanBinding booleanBind = Bindings.isEmpty(listView.getCheckModel().getCheckedItems()); + importButton.disableProperty().bind(booleanBind); + importButton.setOnMouseClicked(event -> importEntries(listView.getCheckModel().getCheckedItems(), searchType, entry)); + showNodes(refreshButton, importButton); + } + + private void prepareToSearchForRelations(Button abortButton, Button refreshButton, Button importButton, + ProgressIndicator progress, BackgroundTask> task) { + showNodes(abortButton, progress); + hideNodes(refreshButton, importButton); + + abortButton.setOnAction(event -> { + hideNodes(abortButton, progress, importButton); + showNodes(refreshButton); + task.cancel(); + dialogService.notify(Localization.lang("Search aborted!")); + }); + } + + private void hideNodes(Node... nodes) { + Arrays.stream(nodes).forEach(node -> node.setVisible(false)); + } + + private void showNodes(Node... nodes) { + Arrays.stream(nodes).forEach(node -> node.setVisible(true)); + } + + // Absolute-phase phenomena in photoionization with few-cycle laser pulses + + /** + * Function to import selected entries to the database. Also writes the entries to import to the CITING/CITED field + * + * @param entriesToImport entries to import + */ + private void importEntries(List entriesToImport, CitationFetcher.SearchType searchType, BibEntry entry) { + citingTask.cancel(); + citedByTask.cancel(); + List entries = entriesToImport.stream().map(CitationRelationItem::getEntry).collect(Collectors.toList()); + ImportHandler importHandler = new ImportHandler( + databaseContext, + preferencesService, + fileUpdateMonitor, + undoManager, + stateManager, + dialogService, + Globals.TASK_EXECUTOR); + importHandler.importEntries(entries); + + dialogService.notify(Localization.lang("Number of entries successfully imported") + ": " + entriesToImport.size()); + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/AuthorResponse.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/AuthorResponse.java new file mode 100644 index 00000000000..f23ba10a54f --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/AuthorResponse.java @@ -0,0 +1,22 @@ +package org.jabref.gui.entryeditor.citationrelationtab.semanticscholar; + +public class AuthorResponse { + private String authorId; + private String name; + + public String getAuthorId() { + return authorId; + } + + public void setAuthorId(String authorId) { + this.authorId = authorId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationDataItem.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationDataItem.java new file mode 100644 index 00000000000..50a3143bf6c --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationDataItem.java @@ -0,0 +1,13 @@ +package org.jabref.gui.entryeditor.citationrelationtab.semanticscholar; + +public class CitationDataItem { + private PaperDetails citingPaper; + + public PaperDetails getCitingPaper() { + return citingPaper; + } + + public void setCitingPaper(PaperDetails citingPaper) { + this.citingPaper = citingPaper; + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationFetcher.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationFetcher.java new file mode 100644 index 00000000000..4b10cadce4f --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationFetcher.java @@ -0,0 +1,50 @@ +package org.jabref.gui.entryeditor.citationrelationtab.semanticscholar; + +import java.util.List; + +import org.jabref.logic.importer.FetcherException; +import org.jabref.model.entry.BibEntry; + +/** + * Searches web resources for citing related articles based on a {@link BibEntry}. + */ +public interface CitationFetcher { + + /** + * Possible search methods + */ + enum SearchType { + CITING("reference"), + CITED_BY("citation"); + + public final String label; + + SearchType(String label) { + this.label = label; + } + } + + /** + * Looks for hits which are citing the given {@link BibEntry}. + * + * @param entry entry to search articles for + * @return a list of {@link BibEntry}, which are matched by the query (may be empty) + */ + List searchCitedBy(BibEntry entry) throws FetcherException; + + /** + * Looks for hits which are cited by the given {@link BibEntry}. + * + * @param entry entry to search articles for + * @return a list of {@link BibEntry}, which are matched by the query (may be empty) + */ + List searchCiting(BibEntry entry) throws FetcherException; + + /** + * Returns the localized name of this fetcher. + * The title can be used to display the fetcher in the menu and in the side pane. + * + * @return the localized name + */ + String getName(); +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationsResponse.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationsResponse.java new file mode 100644 index 00000000000..c87eb9346c2 --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/CitationsResponse.java @@ -0,0 +1,33 @@ +package org.jabref.gui.entryeditor.citationrelationtab.semanticscholar; + +import java.util.List; + +public class CitationsResponse { + private int offset; + private int next; + private List data; + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + public int getNext() { + return next; + } + + public void setNext(int next) { + this.next = next; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/PaperDetails.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/PaperDetails.java new file mode 100644 index 00000000000..48db00777cf --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/PaperDetails.java @@ -0,0 +1,86 @@ +package org.jabref.gui.entryeditor.citationrelationtab.semanticscholar; + +import java.util.List; +import java.util.stream.Collectors; + +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; + +public class PaperDetails { + private String paperId; + private String title; + private String year; + private int citationCount; + private int referenceCount; + private List authors; + + public String getPaperId() { + return paperId; + } + + public void setPaperId(String paperId) { + this.paperId = paperId; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getYear() { + return year; + } + + public void setYear(String year) { + this.year = year; + } + + public int getCitationCount() { + return citationCount; + } + + public void setCitationCount(int citationCount) { + this.citationCount = citationCount; + } + + public int getReferenceCount() { + return referenceCount; + } + + public void setReferenceCount(int referenceCount) { + this.referenceCount = referenceCount; + } + + public List getAuthors() { + return authors; + } + + public BibEntry toBibEntry() { + BibEntry bibEntry = new BibEntry(); + bibEntry.setField(StandardField.TITLE, getTitle()); + if (getYear() != null) { + bibEntry.setField(StandardField.YEAR, getYear()); + } + + String authors = getAuthors().stream() + .map(AuthorResponse::getName) + .collect(Collectors.joining(" and ")); + bibEntry.setField(StandardField.AUTHOR, authors); + + return bibEntry; + } + + @Override + public String toString() { + return "PaperDetails{" + + "paperId='" + paperId + '\'' + + ", title='" + title + '\'' + + ", year='" + year + '\'' + + ", citationCount=" + citationCount + + ", referenceCount=" + referenceCount + + '}'; + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/ReferenceDataItem.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/ReferenceDataItem.java new file mode 100644 index 00000000000..4df4c64ea6a --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/ReferenceDataItem.java @@ -0,0 +1,13 @@ +package org.jabref.gui.entryeditor.citationrelationtab.semanticscholar; + +public class ReferenceDataItem { + private PaperDetails citedPaper; + + public PaperDetails getCitedPaper() { + return citedPaper; + } + + public void setCitedPaper(PaperDetails citedPaper) { + this.citedPaper = citedPaper; + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/ReferencesResponse.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/ReferencesResponse.java new file mode 100644 index 00000000000..8f5506d9cb6 --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/ReferencesResponse.java @@ -0,0 +1,33 @@ +package org.jabref.gui.entryeditor.citationrelationtab.semanticscholar; + +import java.util.List; + +public class ReferencesResponse { + private int offset; + private int next; + private List data; + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + public int getNext() { + return next; + } + + public void setNext(int next) { + this.next = next; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java new file mode 100644 index 00000000000..6699596a7be --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java @@ -0,0 +1,78 @@ +package org.jabref.gui.entryeditor.citationrelationtab.semanticscholar; + +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.jabref.logic.importer.FetcherException; +import org.jabref.logic.net.URLDownload; +import org.jabref.model.entry.BibEntry; + +import com.google.gson.Gson; + +public class SemanticScholarFetcher implements CitationFetcher { + private static final String SEMANTIC_SCHOLAR_API = "https://api.semanticscholar.org/graph/v1/"; + + @Override + public List searchCitedBy(BibEntry entry) throws FetcherException { + if (entry.getDOI().isPresent()) { + StringBuilder urlBuilder = new StringBuilder(SEMANTIC_SCHOLAR_API) + .append("paper/") + .append("DOI:").append(entry.getDOI().get().getDOI()) + .append("/citations") + .append("?fields=").append("title,authors,year,citationCount,referenceCount") + .append("&limit=1000"); + + try { + URL citationsUrl = URI.create(urlBuilder.toString()).toURL(); + + URLDownload urlDownload = new URLDownload(citationsUrl); + CitationsResponse citationsResponse = new Gson() + .fromJson(urlDownload.asString(), CitationsResponse.class); + + return citationsResponse.getData() + .stream().filter(citationDataItem -> citationDataItem.getCitingPaper() != null) + .map(citationDataItem -> citationDataItem.getCitingPaper().toBibEntry()).toList(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return new ArrayList<>(); + } + + @Override + public List searchCiting(BibEntry entry) { + if (entry.getDOI().isPresent()) { + StringBuilder urlBuilder = new StringBuilder(SEMANTIC_SCHOLAR_API) + .append("paper/") + .append("DOI:").append(entry.getDOI().get().getDOI()) + .append("/references") + .append("?fields=").append("title,authors,year,citationCount,referenceCount") + .append("&limit=1000"); + try { + URL referencesUrl = URI.create(urlBuilder.toString()).toURL(); + + URLDownload urlDownload = new URLDownload(referencesUrl); + ReferencesResponse referencesResponse = new Gson() + .fromJson(urlDownload.asString(), ReferencesResponse.class); + + return referencesResponse.getData() + .stream() + .filter(citationDataItem -> citationDataItem.getCitedPaper() != null) + .map(referenceDataItem -> referenceDataItem.getCitedPaper().toBibEntry()).toList(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return new ArrayList<>(); + } + + @Override + public String getName() { + return "Semantic Scholar Citations Fetcher"; + } +} diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 24b1931b1c9..d9e72a11e2f 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2579,3 +2579,17 @@ Finished=Finished Finished\ writing\ metadata\ for\ library\ %0\ (%1\ succeeded,\ %2\ skipped,\ %3\ errors).=Finished writing metadata for library %0 (%1 succeeded, %2 skipped, %3 errors). Processing...=Processing... Writing\ metadata\ to\ %0=Writing metadata to %0 + +Add\ selected\ entries\ to\ database=Add selected entries to database +The\ selected\ entry\ doesn't\ have\ a\ DOI\ linked\ to\ it.\ Lookup\ a\ DOI\ and\ try\ again.=The selected entry doesn't have a DOI linked to it. Lookup a DOI and try again. +Cited\ By=Cited By +Citing=Citing +No\ articles\ found=No articles found +Restart\ search=Restart search +Cancel\ search=Cancel search +Select\ entry=Select entry +Search\ aborted!=Search aborted! +Citation\ relations=Citation relations +Show\ articles\ related\ by\ citation=Show articles related by citation +Error\ while\ fetching\ citing\ entries\:\ %0=Error while fetching citing entries: %0 +Jump\ to\ entry\ in\ database=Jump to entry in database From 9bed170c4d291446cdd4bae71399180d005dd0e6 Mon Sep 17 00:00:00 2001 From: Christoph Date: Mon, 11 Sep 2023 22:22:34 +0200 Subject: [PATCH 27/94] Fix isbn fetcher returning misc (#10359) * Fix isbn fetcher returning misc Fixes #10348 use arrayList instead of linkedlist * remove else case --- CHANGELOG.md | 1 + .../logic/importer/fetcher/GvkFetcher.java | 4 + .../fetcher/isbntobibtex/IsbnFetcher.java | 3 +- .../importer/fileformat/PicaXmlParser.java | 78 +++++++++---------- .../isbntobibtex/GVKIsbnFetcherTest.java | 7 +- 5 files changed, 51 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ecf850684d..c96ff22e6d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - Passwords can be stored in GNOME key ring. [#10274](https://github.com/JabRef/jabref/issues/10274) - We fixed an issue where groups based on an aux file could not be created due to an exception [#10350](https://github.com/JabRef/jabref/issues/10350) - We fixed an issue where the JabRef browser extension could not communicate with JabRef under macOS due to missing files. You should use the `.pkg` for the first installation as it updates all necessary files for the extension [#10308](https://github.com/JabRef/jabref/issues/10308) +- We fixed an issue where the ISBN fetcher returned the entrytype `misc` for certain ISBN numbers [#10348](https://github.com/JabRef/jabref/issues/10348) - We fixed a bug where an exception was raised when saving less than three export save orders in the preference. [#10157](https://github.com/JabRef/jabref/issues/10157) - We fixed an issue where it was possible to create a group with no name or with a group separator inside the name [#9776](https://github.com/JabRef/jabref/issues/9776) diff --git a/src/main/java/org/jabref/logic/importer/fetcher/GvkFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/GvkFetcher.java index 5ae34a92fa8..3105cdc6423 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/GvkFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/GvkFetcher.java @@ -56,6 +56,8 @@ public URL getURLForQuery(QueryNode luceneQuery) throws URISyntaxException, Malf uriBuilder.addParameter("maximumRecords", "50"); uriBuilder.addParameter("recordSchema", "picaxml"); uriBuilder.addParameter("sortKeys", "Year,,1"); + + LOGGER.debug("Using URL {}", uriBuilder.build()); return uriBuilder.build().toURL(); } @@ -69,6 +71,8 @@ public URL getUrlForIdentifier(String identifier) throws URISyntaxException, Mal uriBuilder.addParameter("maximumRecords", "50"); uriBuilder.addParameter("recordSchema", "picaxml"); uriBuilder.addParameter("sortKeys", "Year,,1"); + + LOGGER.debug("Using URL {}", uriBuilder.build()); return uriBuilder.build().toURL(); } diff --git a/src/main/java/org/jabref/logic/importer/fetcher/isbntobibtex/IsbnFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/isbntobibtex/IsbnFetcher.java index 8b9fcd56e50..23344ddbba9 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/isbntobibtex/IsbnFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/isbntobibtex/IsbnFetcher.java @@ -72,10 +72,11 @@ public Optional performSearchById(String identifier) throws FetcherExc throw ex; } } finally { - LOGGER.debug("Trying using the alternate ISBN fetchers to find an entry."); // do not move the iterator in the loop as this would always return a new one and thus create and endless loop Iterator iterator = retryIsbnFetcher.iterator(); while (bibEntry.isEmpty() && iterator.hasNext()) { + LOGGER.debug("Trying using the alternate ISBN fetchers to find an entry."); + AbstractIsbnFetcher fetcher = iterator.next(); LOGGER.debug("No entry found for ISBN {}; trying {} next.", identifier, fetcher.getName()); bibEntry = fetcher.performSearchById(identifier); diff --git a/src/main/java/org/jabref/logic/importer/fileformat/PicaXmlParser.java b/src/main/java/org/jabref/logic/importer/fileformat/PicaXmlParser.java index 9b0fd526a3d..61679778d5d 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/PicaXmlParser.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/PicaXmlParser.java @@ -2,7 +2,7 @@ import java.io.IOException; import java.io.InputStream; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; @@ -16,8 +16,8 @@ import org.jabref.model.entry.field.UnknownField; import org.jabref.model.entry.types.EntryType; import org.jabref.model.entry.types.StandardEntryType; +import org.jabref.model.strings.StringUtil; -import com.google.common.base.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -35,21 +35,20 @@ public List parseEntries(InputStream inputStream) throws ParseExceptio DocumentBuilder dbuild = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document content = dbuild.parse(inputStream); return this.parseEntries(content); - } catch (ParserConfigurationException | SAXException | IOException exception) { + } catch ( + ParserConfigurationException | + SAXException | + IOException exception) { throw new ParseException(exception); } } private List parseEntries(Document content) { - List result = new LinkedList<>(); + List result = new ArrayList<>(); // used for creating test cases // XMLUtil.printDocument(content); - // Namespace srwNamespace = Namespace.getNamespace("srw","http://www.loc.gov/zing/srw/"); - - // Schleife ueber alle Teilergebnisse - // Element root = content.getDocumentElement(); Element root = (Element) content.getElementsByTagName("zs:searchRetrieveResponse").item(0); Element srwrecords = getChild("zs:records", root); if (srwrecords == null) { @@ -92,24 +91,22 @@ private BibEntry parseEntry(Element e) { String url = null; String note = null; - String quelle = ""; - String mak = ""; + String source = ""; + String bibliographicGenre = ""; String subtitle = ""; EntryType entryType = StandardEntryType.Book; // Default - // Alle relevanten Informationen einsammeln - List datafields = getChildren("datafield", e); for (Element datafield : datafields) { String tag = datafield.getAttribute("tag"); LOGGER.debug("tag: " + tag); - // mak + // genre/type of the entry https://swbtools.bsz-bw.de/cgi-bin/k10plushelp.pl?cmd=kat&val=0500&katalog=Standard if ("002@".equals(tag)) { - mak = getSubfield("0", datafield); - if (mak == null) { - mak = ""; + bibliographicGenre = getSubfield("0", datafield); + if (bibliographicGenre == null) { + bibliographicGenre = ""; } } @@ -285,7 +282,7 @@ private BibEntry parseEntry(Element e) { if ("030F".equals(tag)) { address = getSubfield("k", datafield); - if (!"proceedings".equals(entryType)) { + if (!"proceedings".equals(entryType.getName())) { subtitle = getSubfield("a", datafield); } @@ -305,10 +302,10 @@ private BibEntry parseEntry(Element e) { // Quelle unvollständig sind (z.B. nicht Serie // und Nummer angegeben werden) if ("039B".equals(tag)) { - quelle = getSubfield("8", datafield); + source = getSubfield("8", datafield); } - if ("046R".equals(tag) && ((quelle == null) || quelle.isEmpty())) { - quelle = getSubfield("a", datafield); + if ("046R".equals(tag) && ((source == null) || source.isEmpty())) { + source = getSubfield("a", datafield); } // URLs behandeln @@ -318,12 +315,10 @@ private BibEntry parseEntry(Element e) { } } - // Abfangen von Nulleintraegen - if (quelle == null) { - quelle = ""; + if (source == null) { + source = ""; } - // Nichtsortierzeichen entfernen if (author != null) { author = removeSortCharacters(author); } @@ -337,23 +332,26 @@ private BibEntry parseEntry(Element e) { subtitle = removeSortCharacters(subtitle); } - // Dokumenttyp bestimmen und Eintrag anlegen - - if (mak.startsWith("As")) { + if (bibliographicGenre.startsWith("As")) { entryType = BibEntry.DEFAULT_TYPE; - if (quelle.contains("ISBN")) { + if (source.contains("ISBN")) { entryType = StandardEntryType.InCollection; } - if (quelle.contains("ZDB-ID")) { + if (source.contains("ZDB-ID")) { entryType = StandardEntryType.Article; } - } else if (mak.isEmpty()) { - entryType = BibEntry.DEFAULT_TYPE; - } else if (mak.startsWith("O")) { + } else if (bibliographicGenre.isEmpty()) { entryType = BibEntry.DEFAULT_TYPE; - // FIXME: online only available in Biblatex - // entryType = "online"; + } else if (bibliographicGenre.startsWith("O")) { + // Oa is standalone so we assume we have a book + if (bibliographicGenre.startsWith("Oa") && isbn != null) { + entryType = StandardEntryType.Book; + } + // 0b is journal + if (bibliographicGenre.startsWith("Ob")) { + entryType = StandardEntryType.Article; + } } /* @@ -375,7 +373,7 @@ private BibEntry parseEntry(Element e) { if (title != null) { result.setField(StandardField.TITLE, title); } - if (!Strings.isNullOrEmpty(subtitle)) { + if (!StringUtil.isNullOrEmpty(subtitle)) { // ensure that first letter is an upper case letter // there could be the edge case that the string is only one character long, therefore, this special treatment // this is Apache commons lang StringUtils.capitalize (https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringUtils.html#capitalize%28java.lang.String%29), but we don't want to add an additional dependency ('org.apache.commons:commons-lang3:3.4') @@ -432,9 +430,9 @@ private BibEntry parseEntry(Element e) { result.setField(StandardField.NOTE, note); } - if ("article".equals(entryType) && (journal != null)) { + if ("article".equals(entryType.getName()) && (journal != null)) { result.setField(StandardField.JOURNAL, journal); - } else if ("incollection".equals(entryType) && (booktitle != null)) { + } else if ("incollection".equals(entryType.getName()) && (booktitle != null)) { result.setField(StandardField.BOOKTITLE, booktitle); } @@ -442,9 +440,9 @@ private BibEntry parseEntry(Element e) { } private String getSubfield(String a, Element datafield) { - List liste = getChildren("subfield", datafield); + List subfields = getChildren("subfield", datafield); - for (Element subfield : liste) { + for (Element subfield : subfields) { if (subfield.getAttribute("code").equalsIgnoreCase(a)) { return subfield.getTextContent(); } @@ -472,7 +470,7 @@ private Element getChild(String name, Element e) { } private List getChildren(String name, Element e) { - List result = new LinkedList<>(); + List result = new ArrayList<>(); NodeList children = e.getChildNodes(); int j = children.getLength(); diff --git a/src/test/java/org/jabref/logic/importer/fetcher/isbntobibtex/GVKIsbnFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/isbntobibtex/GVKIsbnFetcherTest.java index 0f8e7f76368..fbdd60630a7 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/isbntobibtex/GVKIsbnFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/isbntobibtex/GVKIsbnFetcherTest.java @@ -72,7 +72,7 @@ public void searchByIdSuccessfulWithLongISBN() throws FetcherException { @Test @Override public void authorsAreCorrectlyFormatted() throws Exception { - BibEntry bibEntry = new BibEntry(StandardEntryType.Misc) + BibEntry bibEntry = new BibEntry(StandardEntryType.Book) .withField(StandardField.TITLE, "Repository") .withField(StandardField.SUBTITLE, "Eine Einführung") .withField(StandardField.PUBLISHER, "De Gruyter Oldenbourg") @@ -100,4 +100,9 @@ public void testIsbnNeitherAvailableOnEbookDeNorOrViaOpenLibrary() throws Except // However, the ISBN number must not be assigned to a real book assertEquals(Optional.empty(), fetcher.performSearchById("9785646216541")); } + + @Test + void testEResourceIsbnIsReturnedAsBoook() throws Exception { + assertEquals(Optional.of(StandardEntryType.Book), fetcher.performSearchById("978-0-8229-4557-4").map(BibEntry::getType)); + } } From 30d337b29af857e14e6a613315aee8c742a7d46a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:29:42 +0000 Subject: [PATCH 28/94] Bump org.antlr:antlr4 from 4.13.0 to 4.13.1 (#10365) Bumps [org.antlr:antlr4](https://github.com/antlr/antlr4) from 4.13.0 to 4.13.1. - [Release notes](https://github.com/antlr/antlr4/releases) - [Changelog](https://github.com/antlr/antlr4/blob/dev/doc/go-changes.md) - [Commits](https://github.com/antlr/antlr4/compare/4.13.0...4.13.1) --- updated-dependencies: - dependency-name: org.antlr:antlr4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d102422f41c..8ef72b66c2d 100644 --- a/build.gradle +++ b/build.gradle @@ -143,8 +143,8 @@ dependencies { implementation 'info.debatty:java-string-similarity:2.0.0' implementation 'com.github.javakeyring:java-keyring:1.0.4' - antlr4 'org.antlr:antlr4:4.13.0' - implementation 'org.antlr:antlr4-runtime:4.13.0' + antlr4 'org.antlr:antlr4:4.13.1' + implementation 'org.antlr:antlr4-runtime:4.13.1' implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '6.7.0.202309050840-r' From ca1b54c67df01837668f9ba500863933ad2335ca Mon Sep 17 00:00:00 2001 From: Zylence Date: Tue, 12 Sep 2023 15:11:15 +0200 Subject: [PATCH 29/94] Adds LaTeX integrity check based on SnuggleTeX --- CHANGELOG.md | 1 + build.gradle | 5 + src/main/java/module-info.java | 3 +- .../logic/integrity/IntegrityCheck.java | 3 +- .../integrity/LatexIntegrityChecker.java | 79 +++++ src/main/resources/l10n/JabRef_en.properties | 1 + .../integrity/LatexIntegrityCheckerTest.java | 292 ++++++++++++++++++ 7 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/jabref/logic/integrity/LatexIntegrityChecker.java create mode 100644 src/test/java/org/jabref/logic/integrity/LatexIntegrityCheckerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index c96ff22e6d2..5432ff309fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added the possibility to find (and add) papers that cite or are cited by a given paper. [#6187](https://github.com/JabRef/jabref/issues/6187) - We added an error-specific message for when a download from a URL fails. [#9826](https://github.com/JabRef/jabref/issues/9826) - We added support for customizating the citation command (e.g., `[@key1,@key2]`) when [pushing to external applications](https://docs.jabref.org/cite/pushtoapplications). [#10133](https://github.com/JabRef/jabref/issues/10133) +- We added an integrity check for more special characters. [#8712](https://github.com/JabRef/jabref/issues/8712) - We added protected terms described as "Computer science". [#10222](https://github.com/JabRef/jabref/pull/10222) ### Changed diff --git a/build.gradle b/build.gradle index 8ef72b66c2d..ac1a3c81826 100644 --- a/build.gradle +++ b/build.gradle @@ -206,6 +206,11 @@ dependencies { exclude module: 'fastparse_2.13' } + implementation "de.rototor.snuggletex:snuggletex:1.3.0" + implementation ("de.rototor.snuggletex:snuggletex-jeuclid:1.3.0") { + exclude group: "org.apache.xmlgraphics" + } + implementation group: 'com.microsoft.azure', name: 'applicationinsights-core', version: '2.4.1' implementation (group: 'com.microsoft.azure', name: 'applicationinsights-logging-log4j2', version: '2.4.1') { exclude module: "log4j-core" diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 11e6cf9374e..bdb442a3f75 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -104,6 +104,8 @@ requires jbibtex; requires citeproc.java; + requires snuggletex.core; + requires org.apache.pdfbox; requires org.apache.xmpbox; requires com.ibm.icu; @@ -141,5 +143,4 @@ requires org.antlr.antlr4.runtime; requires org.libreoffice.uno; requires de.saxsys.mvvmfx.validation; - } diff --git a/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java b/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java index b98ecb6a595..d72aab5933e 100644 --- a/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java +++ b/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java @@ -38,7 +38,8 @@ public IntegrityCheck(BibDatabaseContext bibDatabaseContext, new EntryLinkChecker(bibDatabaseContext.getDatabase()), new CitationKeyDeviationChecker(bibDatabaseContext, citationKeyPatternPreferences), new CitationKeyDuplicationChecker(bibDatabaseContext.getDatabase()), - new AmpersandChecker() + new AmpersandChecker(), + new LatexIntegrityChecker() )); if (bibDatabaseContext.isBiblatexMode()) { entryCheckers.addAll(List.of( diff --git a/src/main/java/org/jabref/logic/integrity/LatexIntegrityChecker.java b/src/main/java/org/jabref/logic/integrity/LatexIntegrityChecker.java new file mode 100644 index 00000000000..1898712ea8b --- /dev/null +++ b/src/main/java/org/jabref/logic/integrity/LatexIntegrityChecker.java @@ -0,0 +1,79 @@ +package org.jabref.logic.integrity; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.Set; + +import org.jabref.logic.l10n.Localization; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.Field; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import uk.ac.ed.ph.snuggletex.ErrorCode; +import uk.ac.ed.ph.snuggletex.InputError; +import uk.ac.ed.ph.snuggletex.SnuggleEngine; +import uk.ac.ed.ph.snuggletex.SnuggleInput; +import uk.ac.ed.ph.snuggletex.SnuggleSession; +import uk.ac.ed.ph.snuggletex.definitions.CoreErrorCode; +import uk.ac.ed.ph.snuggletex.definitions.CoreErrorGroup; + +public class LatexIntegrityChecker implements EntryChecker { + + private static final Logger LOGGER = LoggerFactory.getLogger(SnuggleSession.class); + private static final SnuggleEngine ENGINE = new SnuggleEngine(); + private static final SnuggleSession SESSION = ENGINE.createSession(); + private static final ResourceBundle ERROR_MESSAGES = ENGINE.getPackages().get(0).getErrorMessageBundle(); + private static final Set EXCLUDED_ERRORS = new HashSet<>(); + + static { + // Static configuration. + SESSION.getConfiguration().setFailingFast(true); + + // '#' only allowed inside and command/environment definitions. + EXCLUDED_ERRORS.add(CoreErrorCode.TTEG04); + } + + @Override + public List check(BibEntry entry) { + List results = new ArrayList<>(); + + for (Map.Entry field : entry.getFieldMap().entrySet()) { + SnuggleInput input = new SnuggleInput(field.getValue()); + try { + SESSION.parseInput(input); + } catch (IOException e) { + LOGGER.error("Error at parsing", e); + } + if (!SESSION.getErrors().isEmpty()) { + // Retrieve the first error only because it is likely to be more meaningful. + // Displaying all (subsequent) faults may lead to confusion. + // We further get a slight performance benefit from failing fast (see static config in class header). + InputError error = SESSION.getErrors().get(0); + ErrorCode errorCode = error.getErrorCode(); + // Exclude all DOM building errors as this functionality is not used. + // Further, exclude individual errors. + if (!errorCode.getErrorGroup().equals(CoreErrorGroup.TDE) && !EXCLUDED_ERRORS.contains(errorCode)) { + String jabrefMessageWrapper = errorMessageFormatHelper(errorCode, error.getArguments()); + results.add(new IntegrityMessage(jabrefMessageWrapper, entry, field.getKey())); + } + } + SESSION.reset(); + } + return results; + } + + public static String errorMessageFormatHelper(ErrorCode snuggleTexErrorCode, Object... arguments) { + String snuggletexMessagePattern = LatexIntegrityChecker.ERROR_MESSAGES.getString(snuggleTexErrorCode.getName()); + String snuggletexErrorMessage = MessageFormat.format(snuggletexMessagePattern, arguments); + return Localization.lang("LaTeX Parsing Error: %0", snuggletexErrorMessage); + } +} + + + diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index d9e72a11e2f..22f95d6936c 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1489,6 +1489,7 @@ changes\ all\ letters\ to\ lower\ case.=changes all letters to lower case. CHANGES\ ALL\ LETTERS\ TO\ UPPER\ CASE.=CHANGES ALL LETTERS TO UPPER CASE. Changes\ The\ First\ Letter\ Of\ All\ Words\ To\ Capital\ Case\ And\ The\ Remaining\ Letters\ To\ Lower\ Case.=Changes The First Letter Of All Words To Capital Case And The Remaining Letters To Lower Case. Cleans\ up\ LaTeX\ code.=Cleans up LaTeX code. +LaTeX\ Parsing\ Error: %0\:=LaTeX Parsing Error: %0 Converts\ HTML\ code\ to\ LaTeX\ code.=Converts HTML code to LaTeX code. HTML\ to\ Unicode=HTML to Unicode Converts\ HTML\ code\ to\ Unicode.=Converts HTML code to Unicode. diff --git a/src/test/java/org/jabref/logic/integrity/LatexIntegrityCheckerTest.java b/src/test/java/org/jabref/logic/integrity/LatexIntegrityCheckerTest.java new file mode 100644 index 00000000000..9107b523356 --- /dev/null +++ b/src/test/java/org/jabref/logic/integrity/LatexIntegrityCheckerTest.java @@ -0,0 +1,292 @@ +package org.jabref.logic.integrity; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.StandardField; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import uk.ac.ed.ph.snuggletex.definitions.CoreErrorCode; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class LatexIntegrityCheckerTest { + + private final LatexIntegrityChecker checker = new LatexIntegrityChecker(); + private final BibEntry entry = new BibEntry(); + + @ParameterizedTest + @MethodSource("provideAcceptedInputs") + void acceptsAllowedInputs(List expected, Field field, String value) { + entry.setField(field, value); + assertEquals(expected, checker.check(entry)); + } + + /** + * This method provides inputs containing valid LaTeX Syntax which a proper parser + * should be able to parse without errors. + */ + private static Stream provideAcceptedInputs() { + return Stream.of( + // Basic text inputs + Arguments.of(Collections.emptyList(), StandardField.TITLE, "Simple Text"), + + // Simple commands + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\section{X}"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\newline"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\par"), + + // Text decorations + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\underline{Underlined}"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\texttt{Monospace}"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\textit{Italic}"), + + // Special characters and symbols + Arguments.of(Collections.emptyList(), StandardField.TITLE, "Café"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "αβγδε"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\# \\$ \\% \\& \\{ \\} \\_ \\^ \\\\"), + + // Fonts and sizes + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\tiny Tiny Text"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\small Small Text"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\large Large Text"), + + // Verbatim and special characters + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\verb|Verbatim|"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "$\\widetilde{i}$"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\ldots"), + + // Simple environments + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{quote}Quoted Text\\end{quote}\n"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{center}Centered Text\\end{center}\n"), + + // Math environment inputs + Arguments.of(Collections.emptyList(), StandardField.TITLE, "$x + y = z$"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\(a^2 + b^2 = c^2\\)"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\[E = mc^2\\]"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{math} V = I \\cdot R \\end{math}"), + + // Equations and alignment + // Currently Unsupported + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{align} x + y &= z \\\\ a &= b + c \\end{align}"), + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{align*} A &:= B \\\\ C &\\rightarrow D \\end{align*}"), + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{equation} E = mc^2 \\end{equation}"), + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{align*} x + y &= z \\\\ a &= b + c \\\\ p &= \\frac{q}{r} \\end{align*}"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{eqnarray} x + y &= z \\\\ a &= b + c \\\\ p &= \\frac{q}{r} \\end{eqnarray}"), + + // Equations and matrices + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\[ \\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix} \\]"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\[ \\begin{bmatrix} x & y & z \\\\ u & v & w \\end{bmatrix} \\]"), + + // Tables + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{tabular}{|c|c|} \\hline 1 & 2 \\\\ 3 & 4 \\\\ \\hline \\end{tabular}"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{tabular}{cc} A & B \\\\ C & D \\end{tabular}"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{tabular}{|l|r|} \\hline Item & Quantity \\\\ \\hline Apple & 3 \\\\ Banana & 5 \\\\ \\hline \\end{tabular}"), + + // Lists and enumerations + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{itemize} \\item Item 1 \\item Item 2 \\item Item 3 \\end{itemize}"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{enumerate} \\item First \\item Second \\item Third \\end{enumerate}"), + + // Line breaks and spacing + Arguments.of(Collections.emptyList(), StandardField.TITLE, "First Line \\\\ Second Line"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "Some \\hspace{2cm} Space"), + // Currently Unsupported + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "Some \\vspace{1cm} Space"), + + // Multiple commands and environments + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\textbf{\\emph{Bold and Emphasized Text}} $5-3_k$"), + Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{itemize} \\item\\begin{quote} \\textbf{Quoted} \\emph{Text} \\end{quote} \\end{itemize}") + + // More currently unsupported operations + + // Figures and Graphics + // Arguments.of(Collections.emptyList(), StandardField.TITLE, } "\\includegraphics[width=0.5\\textwidth]{image.jpg}"), + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\begin{figure} \\centering \\includegraphics[width=0.8\\textwidth]{plot.png} \\caption{Plot Caption} \\label{fig:plot} \\end{figure}"), + + // Citations and references + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\cite{key}"), + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\label{sec:intro}"), + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "As shown in \\ref{fig:plot}"), + + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "\\input{chapter1.tex}"), + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "Footnote\\footnote{This is a footnote}"), + // Arguments.of(Collections.emptyList(), StandardField.TITLE, "$\text{in math}$"), + ); + } + + @ParameterizedTest + @MethodSource("provideUnacceptedInputs") + void rejectsDisallowedInputs(String expectedMessage, Field field, String value) { + entry.setField(field, value); + assertEquals(List.of(new IntegrityMessage(expectedMessage, entry, field)), checker.check(entry)); + } + + /** + * This method provides inputs containing invalid LaTeX syntax which no LaTeX parser should be able to parse + * without errors. The inputs are bundled with the {@link uk.ac.ed.ph.snuggletex.ErrorCode} output by the internal + * LaTeX parsers {@link uk.ac.ed.ph.snuggletex.SnuggleSession}. + */ + private static Stream provideUnacceptedInputs() { + return Stream.of( + + // ------------------------------------ LATEX PARSING/TOKENISATION ERRORS ------------------------------------ + // TTEG00: Finished reading document before finding required terminator "{0}" + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEG00, "}"), StandardField.ABSTRACT, "Unbalanced braces {"), + + // TTEG01: Nothing following \ + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEG01), StandardField.ABSTRACT, "\\"), + + // TTEG02: Non-ASCII character {0} (Unicode U+{1}) at offset {2} in input document - replaced with ’x’ + // todo find out how to trigger / activate - is this even implemented in snuggletex? + // Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEG02, "€", "20AC", "3"), StandardField.TITLE, "A title with € symbol"), + + // TTEG03: Delimiter {0} closing Math mode followed no matching opener + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEG03, "\\]"), StandardField.TITLE, "1+1=2\\]"), + + // TTEG04: Argument placeholder tokens (e.g. #1) may only appear in command and environment definitions + // Excluded + // Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEG04), StandardField.FOREWORD, "Text with #1 placeholder"), + + // TTEM00: Already in math mode - cannot use \( or \[ + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEM00), StandardField.ABSTRACT, "Braces inside: $\\(1+1\\)=2$"), + + // TTEM01: $ was ended by $$ + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEM01), StandardField.TITLE, "$1+1=2$$"), + + // TTEM02: Math mode opened by {0} but matching {1} was never found + // SKipped + + // TTEM03: Math superscript (^) and subscript (_) characters are not allowed in text mode + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEM03), StandardField.SUBTITLE, "_ or ^ Outside Math-Environment"), + + // TTEM04: $ characters cannot be used inside math mode + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEM04), StandardField.TITLE, "\\$ inside math mode \\($1+1=2$\\)"), + + // TTEV00: \verb or \verb* must be followed by a non-whitespace delimiter character + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEV00), StandardField.KEYWORDS, "\\verb "), + + // TTEV01: Line ended before the end delimiter of \verb or \verb* was found + // Skipped + + // TTEC00: Undefined command \{0} + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEC00, "undefinedCommand"), StandardField.ABSTRACT, "\\undefinedCommand"), + + // TTEC01: Command \{0} cannot be used in {1} mode + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEC01, "par", "MATH"), StandardField.TITLE, "$\\par$"), + + // TTEC02: Command \{0} is missing required argument #{1} + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEC02, "textbf", "1"), StandardField.KEYWORDS, "\\textbf"), + + // TTEC03: Could not find target for combining command \{0} + // Skipped + + // TTEC04: Inappropriate target for combining command \{0} + // Skipped + + // TTEE00: Found \end of environment {0} instead of {1} + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEE00, "align", "itemize"), StandardField.ABSTRACT, "\\begin{itemize} \\end{align}"), + + // TTEE01: Expected to read valid environment name enclosed in braces without whitespace + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEE01), StandardField.TITLE, "\\begin{ align }"), + + // TTEE02: Undefined environment {0} + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEE02, "undefinedEnv"), StandardField.ABSTRACT, "\\begin{undefinedEnv}"), + + // TTEE03: Environment {0} cannot be used in {1} mode + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEE03, "itemize", "MATH"), StandardField.TITLE, "$\\begin{itemize}$"), + + // TTEE04: Environment {0} was still open at end of input document + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEE04, "itemize"), StandardField.ABSTRACT, "\\begin{itemize}"), + + // TTEE05: Unexpected \end - no environment is currently open + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEE05), StandardField.KEYWORDS, "\\end{itemize}"), + + // TTEE06: Environment {0} is missing required argument #{1} + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEE06, "tabular", "1"), StandardField.TITLE, "\\begin{tabular}\\end{tabular}"), + + // TTEU00: Expansion limit ({0}) for user-defined commands and environments has been exceeded. Possible recursion? + // Skipped + + // TTEUC0: Input ended before name of new command was found + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUC0), StandardField.TITLE, "\\newcommand{\\"), + + // TTEUC1: Name of new command must be preceded by \ + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUC1), StandardField.TITLE, "\\newcommand{MyCommand}"), + + // TTEUC2: Input ended before end of new command definition + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUC2), StandardField.TITLE, "\\newcommand{\\MyCommand}{"), + + // TTEUC3: No definition provided for new command {0} + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUC3, "MyCommand"), StandardField.TITLE, "\\newcommand{\\MyCommand}"), + + // TTEUC4: Command \{0} has not already been defined so cannot be renewed + // Skipped + + // TTEUC5: Command \{0} already exists - use \renewcommand to redefine it + // Skipped + + // TTEUC6: No ’}’ found after new command name + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUC6), StandardField.TITLE, "\\newcommand{\\MyCommand"), + + // TTEUC7: Number of arguments specified in command or environment definition {0} must be an integer between 1 and 9 - not {1} + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUC7, "MyCommand", "0"), StandardField.TITLE, "\\newcommand{\\MyCommand}[0]{Text}"), + + // TTEUC8: Reserved command {0} cannot be redefined + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUC8, "begin"), StandardField.TITLE, "\\renewcommand{\\begin}{}"), + + // TTEUC9: Input ended before end of argument count specification + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUC9), StandardField.TITLE, "\\newcommand{\\MyCommand}["), + + // TTEUCA: Definition of command {0} refers to argument #{1} but only {2} have been declared + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUCA, "MyCommand", "2", "1"), StandardField.TITLE, "\\newcommand{\\MyCommand}[1]{#2}"), + + // TTEUE0: Expected to read name of new environment enclosed in braces + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUE0), StandardField.TITLE, "\\newenvironment{}"), + + // TTEUE1: No {0} definition provided for new environment {1} + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUE1, "begin", "MyEnvironment"), StandardField.TITLE, "\\newenvironment{MyEnvironment}"), + + // TTEUE2: Environment {0} has not already been defined so cannot be renewed + // Skipped + + // TTEUE3: Environment {0} already exists - use \renewenvironment to redefine it + // Skipped + + // TTEUE5: Definition of begin of environment {0} refers to argument #{1} but only {2} have been declared + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUE5, "MyEnvironment", "1", "0"), StandardField.TITLE, "\\newenvironment{MyEnvironment}{#1}{}"), + + // TTEUE6: Definition of end of environment {0} refers to argument #{1} but arguments may not be used here + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TTEUE6, "MyEnvironment", "2"), StandardField.TITLE, "\\newenvironment{MyEnvironment}[1]{#1}{#2}"), + + // ------------------------------------ TOKEN FIX-UP ERRORS ------------------------------------ + // TFEG00: Block token {0} cannot be used in LR mode + // Skipped + + // TFEL00: Found content before first \item + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TFEL00), StandardField.TITLE, "\\begin{itemize}content before first \\item{}\\end{itemize}"), + + // TFEM00: Ambiguous multiple use of \over at current level + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TFEM00), StandardField.TITLE, "$1 \\over \\over 3$"), + + // TFEM01: Trailing subscript/superscript token + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TFEM01), StandardField.TITLE, "$x_$"), + + // TFEM02: Double subscript/superscript token is ambiguous - use curly brackets + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TFEM02), StandardField.TITLE, "$x_i_j$"), + + // TFEM03: \right had no preceding \left + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TFEM03), StandardField.TITLE, "$\\right)$"), + + // TFEM04: \left had no following \right + Arguments.of(LatexIntegrityChecker.errorMessageFormatHelper(CoreErrorCode.TFEM04), StandardField.TITLE, "$\\left($") + + // TFETB0: \hline must be the only token in table row + // Skipped + ); + } +} From 929f9c56069e72038190ca814a4408df26371ee9 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 12 Sep 2023 15:39:39 +0200 Subject: [PATCH 30/94] Fix localization --- src/main/resources/l10n/JabRef_en.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 22f95d6936c..2e8c314d89b 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1489,7 +1489,7 @@ changes\ all\ letters\ to\ lower\ case.=changes all letters to lower case. CHANGES\ ALL\ LETTERS\ TO\ UPPER\ CASE.=CHANGES ALL LETTERS TO UPPER CASE. Changes\ The\ First\ Letter\ Of\ All\ Words\ To\ Capital\ Case\ And\ The\ Remaining\ Letters\ To\ Lower\ Case.=Changes The First Letter Of All Words To Capital Case And The Remaining Letters To Lower Case. Cleans\ up\ LaTeX\ code.=Cleans up LaTeX code. -LaTeX\ Parsing\ Error: %0\:=LaTeX Parsing Error: %0 +LaTeX\ Parsing\ Error\:\ %0=LaTeX Parsing Error: %0 Converts\ HTML\ code\ to\ LaTeX\ code.=Converts HTML code to LaTeX code. HTML\ to\ Unicode=HTML to Unicode Converts\ HTML\ code\ to\ Unicode.=Converts HTML code to Unicode. From 51cbb2265a019a894cb298a6eba05c9f364ae416 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 12 Sep 2023 22:33:49 +0200 Subject: [PATCH 31/94] Refine IntelliJ howto --- .../intellij-89-run-with-intellij.md | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md index f67ae77d7a8..29f88cfc7fd 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md @@ -12,22 +12,24 @@ Due to [IDEA-119280](https://youtrack.jetbrains.com/issue/IDEA-119280), it is a 1. Navigate to **File > Settings... > Build, Execution, Deployment > Build Tools > Gradle**. 2. Change the setting "Build an run using:" to "IntelliJ IDEA". -3. **Build > Build Project** -4. Open the project view (Alt+1) -5. Copy all build resources to the folder of the build classes +3. Navigate to **File > Settings... > Build, Execution, Deployment > Compiler > Java Compiler**. +4. Uncheck `--Use 'release' option for cross-compilation`. +5. **Build > Build Project** +6. Open the project view (Alt+1) +7. Copy all build resources to the folder of the build classes 1. Navigate to the folder `out/production/resources` 2. Select all folders below (`bst`, `csl-locales`, ...) 3. Press Ctrl+C to mark them for copying 4. Select the folder `classes` 5. Press Ctrl+V to start the copy process -6. Locate the class `Launcher` (e.g., by ctrl+N and then typing `Launcher`) -7. Click on the green play button next to the `main` method to create a Launch configuration. IntelliJ will fail in launching. -8. On the top right of the IntelliJ window, next to the newly created launch configuration, click on the drop down -9. Click on "Edit Configurations..." -10. On the right, click on "Modify options" -11. Ensure that "Use classpath of module" is checked -12. Select "Add VM options" -13. In the newly appearing field for VM options, insert: +8. Locate the class `Launcher` (e.g., by ctrl+N and then typing `Launcher`) +9. Click on the green play button next to the `main` method to create a Launch configuration. IntelliJ will fail in launching. +10. On the top right of the IntelliJ window, next to the newly created launch configuration, click on the drop down +11. Click on "Edit Configurations..." +12. On the right, click on "Modify options" +13. Ensure that "Use classpath of module" is checked +14. Select "Add VM options" +15. In the newly appearing field for VM options, insert: ```text --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref From ef82869c39add149ccdd62fee5cd4e6fd68d1cc1 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 12 Sep 2023 22:46:40 +0200 Subject: [PATCH 32/94] Add some more exports --- .../intellij-89-run-with-intellij.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md index 29f88cfc7fd..f522447c0ef 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md @@ -35,6 +35,22 @@ Due to [IDEA-119280](https://youtrack.jetbrains.com/issue/IDEA-119280), it is a --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref --add-opens=org.controlsfx.controls/org.controlsfx.control.textfield=org.jabref --add-exports=org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref + --add-exports javafx.controls/com.sun.javafx.scene.control=org.jabref + --add-exports org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref + --add-exports javafx.graphics/com.sun.javafx.scene=org.controlsfx.controls + --add-exports javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls + --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control.inputmap=org.controlsfx.controls + --add-exports javafx.base/com.sun.javafx.event=org.controlsfx.controls + --add-exports javafx.base/com.sun.javafx.collections=org.controlsfx.controls + --add-exports javafx.base/com.sun.javafx.runtime=org.controlsfx.controls + --add-exports javafx.web/com.sun.webkit=org.controlsfx.controls + --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix + --add-exports javafx.graphics/com.sun.javafx.stage=com.jfoenix + --add-exports com.oracle.truffle.regex/com.oracle.truffle.regex=org.graalvm.truffle --add-reads org.jabref=org.fxmisc.flowless --add-reads org.jabref=org.apache.commons.csv ``` From 8bcf078104397c37c62b3321cecd183ce90a810a Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 12 Sep 2023 22:50:30 +0200 Subject: [PATCH 33/94] Update docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md Co-authored-by: Christoph --- .../intellij-89-run-with-intellij.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md index f522447c0ef..613e8d48fc4 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md @@ -15,7 +15,7 @@ Due to [IDEA-119280](https://youtrack.jetbrains.com/issue/IDEA-119280), it is a 3. Navigate to **File > Settings... > Build, Execution, Deployment > Compiler > Java Compiler**. 4. Uncheck `--Use 'release' option for cross-compilation`. 5. **Build > Build Project** -6. Open the project view (Alt+1) +6. Open the project view (Alt+1 , on mac cmd>+1) 7. Copy all build resources to the folder of the build classes 1. Navigate to the folder `out/production/resources` 2. Select all folders below (`bst`, `csl-locales`, ...) From fd162d1126ba93f6cdf36a8517ae2e8c7b1ec0ef Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 12 Sep 2023 22:54:29 +0200 Subject: [PATCH 34/94] Refinements --- .../guidelines-for-setting-up-a-local-workspace/eclipse.md | 4 +--- .../intellij-89-run-with-intellij.md | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/eclipse.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/eclipse.md index 19fcd469db1..68f0a8dcc44 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/eclipse.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/eclipse.md @@ -39,6 +39,7 @@ Always make sure your Eclipse installation us up to date. --add-exports javafx.controls/com.sun.javafx.scene.control=org.jabref --add-exports org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref --add-exports javafx.graphics/com.sun.javafx.scene=org.controlsfx.controls + --add-opens javafx.graphics/javafx.scene=org.controlsfx.controls --add-exports javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls @@ -49,9 +50,6 @@ Always make sure your Eclipse installation us up to date. --add-exports javafx.base/com.sun.javafx.runtime=org.controlsfx.controls --add-exports javafx.web/com.sun.webkit=org.controlsfx.controls --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls - --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix - --add-exports javafx.graphics/com.sun.javafx.stage=com.jfoenix - --add-exports com.oracle.truffle.regex/com.oracle.truffle.regex=org.graalvm.truffle --patch-module org.jabref=build/resources/main ``` diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md index 613e8d48fc4..4e620044a32 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md @@ -38,6 +38,7 @@ Due to [IDEA-119280](https://youtrack.jetbrains.com/issue/IDEA-119280), it is a --add-exports javafx.controls/com.sun.javafx.scene.control=org.jabref --add-exports org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref --add-exports javafx.graphics/com.sun.javafx.scene=org.controlsfx.controls + --add-opens javafx.graphics/javafx.scene=org.controlsfx.controls --add-exports javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls @@ -48,9 +49,6 @@ Due to [IDEA-119280](https://youtrack.jetbrains.com/issue/IDEA-119280), it is a --add-exports javafx.base/com.sun.javafx.runtime=org.controlsfx.controls --add-exports javafx.web/com.sun.webkit=org.controlsfx.controls --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls - --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix - --add-exports javafx.graphics/com.sun.javafx.stage=com.jfoenix - --add-exports com.oracle.truffle.regex/com.oracle.truffle.regex=org.graalvm.truffle --add-reads org.jabref=org.fxmisc.flowless --add-reads org.jabref=org.apache.commons.csv ``` From eb92d63751797114cd47b7669915cb52dd073398 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 12 Sep 2023 23:01:06 +0200 Subject: [PATCH 35/94] One more --- .../intellij-89-run-with-intellij.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md index 4e620044a32..cd0b746d7ed 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md @@ -43,6 +43,7 @@ Due to [IDEA-119280](https://youtrack.jetbrains.com/issue/IDEA-119280), it is a --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls --add-exports javafx.controls/com.sun.javafx.scene.control=org.controlsfx.controls + --add-opens=javafx.controls/javafx.scene.control.skin=org.controlsfx.controls --add-exports javafx.controls/com.sun.javafx.scene.control.inputmap=org.controlsfx.controls --add-exports javafx.base/com.sun.javafx.event=org.controlsfx.controls --add-exports javafx.base/com.sun.javafx.collections=org.controlsfx.controls From 96b5a83c71664dd7c94173bd77b9c53f59e171b8 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Wed, 13 Sep 2023 10:02:14 +0200 Subject: [PATCH 36/94] Remove empty lines --- .../java/org/jabref/logic/integrity/LatexIntegrityChecker.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/jabref/logic/integrity/LatexIntegrityChecker.java b/src/main/java/org/jabref/logic/integrity/LatexIntegrityChecker.java index 1898712ea8b..06dae56304b 100644 --- a/src/main/java/org/jabref/logic/integrity/LatexIntegrityChecker.java +++ b/src/main/java/org/jabref/logic/integrity/LatexIntegrityChecker.java @@ -74,6 +74,3 @@ public static String errorMessageFormatHelper(ErrorCode snuggleTexErrorCode, Obj return Localization.lang("LaTeX Parsing Error: %0", snuggletexErrorMessage); } } - - - From cb05f3197705542d84e9b40906d22105ed1d6bf9 Mon Sep 17 00:00:00 2001 From: Siedlerchr Date: Fri, 15 Sep 2023 02:12:33 +0000 Subject: [PATCH 37/94] Update CSL styles --- buildres/csl/csl-styles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildres/csl/csl-styles b/buildres/csl/csl-styles index 2a122e3fa0c..15283ef7a67 160000 --- a/buildres/csl/csl-styles +++ b/buildres/csl/csl-styles @@ -1 +1 @@ -Subproject commit 2a122e3fa0c8bf5a84d1b64d9110c7f89abc6e46 +Subproject commit 15283ef7a679fae0e78e6559e5270434bb0a3aba From 4e92e9f9b4dda2039649942036565c72620284ed Mon Sep 17 00:00:00 2001 From: Luggas <127773292+Luggas4you@users.noreply.github.com> Date: Fri, 15 Sep 2023 18:21:48 +0200 Subject: [PATCH 38/94] Add tests for EntryComperatorTest (#10357) --- .../bibtex/comparator/EntryComparatorTest.java | 18 ++++++++++++++++++ .../bibtex/comparator/FieldComparatorTest.java | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/jabref/logic/bibtex/comparator/EntryComparatorTest.java b/src/test/java/org/jabref/logic/bibtex/comparator/EntryComparatorTest.java index cfaa38b42da..96c80c04b4a 100644 --- a/src/test/java/org/jabref/logic/bibtex/comparator/EntryComparatorTest.java +++ b/src/test/java/org/jabref/logic/bibtex/comparator/EntryComparatorTest.java @@ -129,6 +129,24 @@ void compareWithMixedLineEndings() { .withField(StandardField.COMMENT, "line1\r\n\r\nline3\r\n\r\nline5"); assertEquals(-1, new EntryComparator(false, false, InternalField.KEY_FIELD).compare(e1, e2)); } + + @Test + void testPresentFieldsWithoutKeyField() { + BibEntry e1 = new BibEntry() + .withField(StandardField.AUTHOR, "Stephen King"); + BibEntry e2 = new BibEntry() + .withField(StandardField.AUTHOR, ""); + assertEquals(-1, new EntryComparator(true, false, InternalField.KEY_FIELD).compare(e1, e2)); + } + + @Test + void testPresentFieldsWithKeyField() { + BibEntry e1 = new BibEntry() + .withField(StandardField.AUTHOR, "Stephen King"); + BibEntry e2 = new BibEntry() + .withField(StandardField.AUTHOR, ""); + assertEquals(-1, new EntryComparator(true, false, StandardField.AUTHOR).compare(e1, e2)); + } } diff --git a/src/test/java/org/jabref/logic/bibtex/comparator/FieldComparatorTest.java b/src/test/java/org/jabref/logic/bibtex/comparator/FieldComparatorTest.java index 8700fbe5d35..447f038fa74 100644 --- a/src/test/java/org/jabref/logic/bibtex/comparator/FieldComparatorTest.java +++ b/src/test/java/org/jabref/logic/bibtex/comparator/FieldComparatorTest.java @@ -257,7 +257,7 @@ public void compareAuthorField() throws Exception { BibEntry bigger = new BibEntry() .withField(StandardField.AUTHOR, "Freund, Lucas"); BibEntry smaller = new BibEntry() - .withField(StandardField.AUTHOR, "Gärtner, Lara"); + .withField(StandardField.AUTHOR, "Mustermann, Max"); assertEquals(1, comparator.compare(smaller, bigger)); } From 94bc5c2c74adb7f034855bc4a362433bd2523294 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 16 Sep 2023 10:16:21 +0200 Subject: [PATCH 39/94] Fix hang when exporting at the CLI (#10383) * Some code refactorings - pass RemotePreferences to handleMultipleAppInstances - Introduce separate call to processArguments() - Add test case convertBibtexToTablerefsabsbib * Fix issue * Fix typo * Replace sourceforge.net link to www.jabref.org link (and use ISO date format) * Fix localization strings * Fix typo - and remove no-additional-value-comment * Somre more localiaztion improvements * Fix scope * Fix hanging * Fix strings * More fixes * More fixes * The ArgumentProcessor seems to be independent from Globals.prefs * Remove dot * Update abbreviations * better safe than sorry * Insert System.exit(0); --- CHANGELOG.md | 5 +- buildres/abbrv.jabref.org | 2 +- .../org/jabref/cli/ArgumentProcessor.java | 62 +++++++++---------- src/main/java/org/jabref/cli/Launcher.java | 22 ++++--- src/main/java/org/jabref/gui/Globals.java | 4 +- src/main/java/org/jabref/gui/JabRefGUI.java | 2 +- .../gui/fieldeditors/LinkedFileViewModel.java | 2 +- .../jabref/gui/importer/ImportCommand.java | 2 +- .../jabref/gui/remote/CLIMessageHandler.java | 2 +- .../jabref/logic/bst/BstPreviewLayout.java | 4 +- .../logic/exporter/TemplateExporter.java | 1 - .../java/org/jabref/logic/layout/Layout.java | 2 +- .../jabref/preferences/ExportPreferences.java | 1 - .../jabref/preferences/JabRefPreferences.java | 2 +- src/main/resources/l10n/JabRef_en.properties | 25 ++++---- .../layout/listrefs/listrefs.end.layout | 4 +- .../layout/tablerefs/tablerefs.end.layout | 4 +- .../tablerefsabsbib.end.layout | 4 +- .../org/jabref/cli/ArgumentProcessorTest.java | 52 +++++++++++++--- 19 files changed, 122 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5432ff309fb..bb3cc514b88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,12 +13,14 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added the possibility to find (and add) papers that cite or are cited by a given paper. [#6187](https://github.com/JabRef/jabref/issues/6187) - We added an error-specific message for when a download from a URL fails. [#9826](https://github.com/JabRef/jabref/issues/9826) -- We added support for customizating the citation command (e.g., `[@key1,@key2]`) when [pushing to external applications](https://docs.jabref.org/cite/pushtoapplications). [#10133](https://github.com/JabRef/jabref/issues/10133) +- We added support for customizing the citation command (e.g., `[@key1,@key2]`) when [pushing to external applications](https://docs.jabref.org/cite/pushtoapplications). [#10133](https://github.com/JabRef/jabref/issues/10133) - We added an integrity check for more special characters. [#8712](https://github.com/JabRef/jabref/issues/8712) - We added protected terms described as "Computer science". [#10222](https://github.com/JabRef/jabref/pull/10222) ### Changed +- In the exports listrefs, tablerefs, tablerefsabsbib, use ISO date format in the footer. + ### Fixed - It is possible again to use "current table sort order" for the order of entries when saving. [#9869](https://github.com/JabRef/jabref/issues/9869) @@ -28,6 +30,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where the ISBN fetcher returned the entrytype `misc` for certain ISBN numbers [#10348](https://github.com/JabRef/jabref/issues/10348) - We fixed a bug where an exception was raised when saving less than three export save orders in the preference. [#10157](https://github.com/JabRef/jabref/issues/10157) - We fixed an issue where it was possible to create a group with no name or with a group separator inside the name [#9776](https://github.com/JabRef/jabref/issues/9776) +- JabRef does not hang anymore when exporting via CLI. [#10380](https://github.com/JabRef/jabref/issues/10380) ### Removed diff --git a/buildres/abbrv.jabref.org b/buildres/abbrv.jabref.org index d47628aa8ae..cf71cb48dbd 160000 --- a/buildres/abbrv.jabref.org +++ b/buildres/abbrv.jabref.org @@ -1 +1 @@ -Subproject commit d47628aa8ae2d7d47d5b18a82982d2246996347c +Subproject commit cf71cb48dbd78b2d85856e689e2834f14b91fdbc diff --git a/src/main/java/org/jabref/cli/ArgumentProcessor.java b/src/main/java/org/jabref/cli/ArgumentProcessor.java index 5d5c2c025d6..1f436070319 100644 --- a/src/main/java/org/jabref/cli/ArgumentProcessor.java +++ b/src/main/java/org/jabref/cli/ArgumentProcessor.java @@ -67,13 +67,20 @@ public class ArgumentProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(ArgumentProcessor.class); private final JabRefCLI cli; - private final List parserResults; + + // Written once by processArguments() + private List parserResults = List.of(); + private final Mode startupMode; private final PreferencesService preferencesService; private final FileUpdateMonitor fileUpdateMonitor; private final BibEntryTypesManager entryTypesManager; private boolean noGUINeeded; + /** + * First call the constructor, then call {@link #processArguments()}. + * Afterward, you can access the {@link #getParserResults()} and other getters. + */ public ArgumentProcessor(String[] args, Mode startupMode, PreferencesService preferencesService, @@ -84,8 +91,6 @@ public ArgumentProcessor(String[] args, this.preferencesService = preferencesService; this.fileUpdateMonitor = fileUpdateMonitor; this.entryTypesManager = entryTypesManager; - - this.parserResults = processArguments(); } /** @@ -145,8 +150,8 @@ private Optional importFile(String argument) { Optional importResult = importFile(file, importFormat); importResult.ifPresent(result -> { - OutputPrinter printer = new SystemOutputPrinter(); if (result.hasWarnings()) { + OutputPrinter printer = new SystemOutputPrinter(); printer.showMessage(result.getErrorMessage()); } }); @@ -161,22 +166,21 @@ private Optional importFile(Path file, String importFormat) { fileUpdateMonitor); if (!"*".equals(importFormat)) { - System.out.println(Localization.lang("Importing") + ": " + file); + System.out.println(Localization.lang("Importing %0", file)); ParserResult result = importFormatReader.importFromFile(importFormat, file); return Optional.of(result); } else { // * means "guess the format": - System.out.println(Localization.lang("Importing in unknown format") + ": " + file); + System.out.println(Localization.lang("Importing file %0 as unknown format", file)); ImportFormatReader.UnknownFormatImport importResult = importFormatReader.importUnknownFormat(file, new DummyFileUpdateMonitor()); - System.out.println(Localization.lang("Format used") + ": " + importResult.format()); + System.out.println(Localization.lang("Format used: %0", importResult.format())); return Optional.of(importResult.parserResult()); } } catch (ImportException ex) { - System.err - .println(Localization.lang("Error opening file") + " '" + file + "': " + ex.getLocalizedMessage()); + System.err.println(Localization.lang("Error opening file '%0'", file) + "\n" + ex.getLocalizedMessage()); return Optional.empty(); } } @@ -185,11 +189,7 @@ public List getParserResults() { return parserResults; } - public boolean hasParserResults() { - return !parserResults.isEmpty(); - } - - private List processArguments() { + public void processArguments() { if ((startupMode == Mode.INITIAL_START) && cli.isShowVersion()) { cli.displayVersion(); } @@ -197,7 +197,8 @@ private List processArguments() { if ((startupMode == Mode.INITIAL_START) && cli.isHelp()) { JabRefCLI.printUsage(preferencesService); noGUINeeded = true; - return Collections.emptyList(); + this.parserResults = Collections.emptyList(); + return; } // Check if we should reset all preferences to default values: @@ -220,7 +221,8 @@ private List processArguments() { if (cli.isExportMatches()) { if (!loaded.isEmpty()) { if (!exportMatches(loaded)) { - return Collections.emptyList(); + this.parserResults = Collections.emptyList(); + return; } } else { System.err.println(Localization.lang("The output option depends on a valid input option.")); @@ -275,7 +277,7 @@ private List processArguments() { doAuxImport(loaded); } - return loaded; + this.parserResults = loaded; } private void writeMetadataToPdf(List loaded, @@ -475,15 +477,14 @@ private boolean exportMatches(List loaded) { Globals.entryTypesManager); Optional exporter = exporterFactory.getExporterByName(formatName); if (exporter.isEmpty()) { - System.err.println(Localization.lang("Unknown export format") + ": " + formatName); + System.err.println(Localization.lang("Unknown export format %0", formatName)); } else { // We have an TemplateExporter instance: try { - System.out.println(Localization.lang("Exporting") + ": " + data[1]); + System.out.println(Localization.lang("Exporting %0", data[1])); exporter.get().export(databaseContext, Path.of(data[1]), matches, Collections.emptyList(), Globals.journalAbbreviationRepository); } catch (Exception ex) { - System.err.println(Localization.lang("Could not export file") + " '" + data[1] + "': " - + Throwables.getStackTraceAsString(ex)); + System.err.println(Localization.lang("Could not export file '%0' (reason: %1)", data[1], Throwables.getStackTraceAsString(ex))); } } } @@ -503,7 +504,7 @@ private void doAuxImport(List loaded) { } if (usageMsg) { - System.out.println(Localization.lang("no base-BibTeX-file specified") + "!"); + System.out.println(Localization.lang("no base-BibTeX-file specified!")); System.out.println(Localization.lang("usage") + " :"); System.out.println("jabref --aux infile[.aux],outfile[.bib] base-BibTeX-file"); } @@ -634,32 +635,31 @@ private void exportFile(List loaded, String[] data) { } else if (data.length == 2) { // This signals that the latest import should be stored in the given // format to the given file. - ParserResult pr = loaded.get(loaded.size() - 1); + ParserResult parserResult = loaded.get(loaded.size() - 1); - Path path = pr.getPath().get().toAbsolutePath(); - BibDatabaseContext databaseContext = pr.getDatabaseContext(); + Path path = parserResult.getPath().get().toAbsolutePath(); + BibDatabaseContext databaseContext = parserResult.getDatabaseContext(); databaseContext.setDatabasePath(path); List fileDirForDatabase = databaseContext .getFileDirectories(preferencesService.getFilePreferences()); - System.out.println(Localization.lang("Exporting") + ": " + data[0]); + System.out.println(Localization.lang("Exporting %0", data[0])); ExporterFactory exporterFactory = ExporterFactory.create( preferencesService, Globals.entryTypesManager); Optional exporter = exporterFactory.getExporterByName(data[1]); if (exporter.isEmpty()) { - System.err.println(Localization.lang("Unknown export format") + ": " + data[1]); + System.err.println(Localization.lang("Unknown export format %0", data[1])); } else { // We have an exporter: try { exporter.get().export( - pr.getDatabaseContext(), + parserResult.getDatabaseContext(), Path.of(data[0]), - pr.getDatabaseContext().getDatabase().getEntries(), + parserResult.getDatabaseContext().getDatabase().getEntries(), fileDirForDatabase, Globals.journalAbbreviationRepository); } catch (Exception ex) { - System.err.println(Localization.lang("Could not export file") + " '" + data[0] + "': " - + Throwables.getStackTraceAsString(ex)); + System.err.println(Localization.lang("Could not export file '%0' (reason: %1)", data[0], Throwables.getStackTraceAsString(ex))); } } } diff --git a/src/main/java/org/jabref/cli/Launcher.java b/src/main/java/org/jabref/cli/Launcher.java index caaa9d62744..a49ea92e865 100644 --- a/src/main/java/org/jabref/cli/Launcher.java +++ b/src/main/java/org/jabref/cli/Launcher.java @@ -65,17 +65,18 @@ public static void main(String[] args) { BibEntryTypesManager entryTypesManager = new BibEntryTypesManager(); Globals.entryTypesManager = entryTypesManager; - // Init preferences + // Initialize preferences final JabRefPreferences preferences = JabRefPreferences.getInstance(); - Globals.prefs = preferences; - PreferencesMigrations.runMigrations(preferences, entryTypesManager); // Early exit in case another instance is already running - if (!handleMultipleAppInstances(ARGUMENTS, preferences)) { + if (!handleMultipleAppInstances(ARGUMENTS, preferences.getRemotePreferences())) { return; } - // Init rest of preferences + Globals.prefs = preferences; + PreferencesMigrations.runMigrations(preferences, entryTypesManager); + + // Initialize rest of preferences configureProxy(preferences.getProxyPreferences()); configureSSL(preferences.getSSLPreferences()); initGlobals(preferences); @@ -86,13 +87,17 @@ public static void main(String[] args) { // Process arguments ArgumentProcessor argumentProcessor = new ArgumentProcessor( - ARGUMENTS, ArgumentProcessor.Mode.INITIAL_START, + ARGUMENTS, + ArgumentProcessor.Mode.INITIAL_START, preferences, fileUpdateMonitor, entryTypesManager); + argumentProcessor.processArguments(); if (argumentProcessor.shouldShutDown()) { LOGGER.debug("JabRef shut down after processing command line arguments"); - return; + // A clean shutdown takes 60s time + // We don't need the clean shutdown here + System.exit(0); } MainApplication.main(argumentProcessor.getParserResults(), argumentProcessor.isBlank(), preferences, fileUpdateMonitor, ARGUMENTS); @@ -141,8 +146,7 @@ private static void initializeLogger() { LOGGER = LoggerFactory.getLogger(MainApplication.class); } - private static boolean handleMultipleAppInstances(String[] args, PreferencesService preferences) { - RemotePreferences remotePreferences = preferences.getRemotePreferences(); + private static boolean handleMultipleAppInstances(String[] args, RemotePreferences remotePreferences) { if (remotePreferences.useRemoteServer()) { // Try to contact already running JabRef RemoteClient remoteClient = new RemoteClient(remotePreferences.getPort()); diff --git a/src/main/java/org/jabref/gui/Globals.java b/src/main/java/org/jabref/gui/Globals.java index c78330180b7..04ead50bf63 100644 --- a/src/main/java/org/jabref/gui/Globals.java +++ b/src/main/java/org/jabref/gui/Globals.java @@ -15,7 +15,7 @@ import org.jabref.logic.util.BuildInfo; import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.model.util.FileUpdateMonitor; -import org.jabref.preferences.JabRefPreferences; +import org.jabref.preferences.PreferencesService; import kong.unirest.Unirest; @@ -43,7 +43,7 @@ public class Globals { /** * Each test case initializes this field if required */ - public static JabRefPreferences prefs; + public static PreferencesService prefs; /** * This field is initialized upon startup. diff --git a/src/main/java/org/jabref/gui/JabRefGUI.java b/src/main/java/org/jabref/gui/JabRefGUI.java index 0345881c5b7..9a402703619 100644 --- a/src/main/java/org/jabref/gui/JabRefGUI.java +++ b/src/main/java/org/jabref/gui/JabRefGUI.java @@ -251,7 +251,7 @@ private void openDatabases() { } for (ParserResult pr : failed) { - String message = Localization.lang("Error opening file '%0'.", + String message = Localization.lang("Error opening file '%0'", pr.getPath().map(Path::toString).orElse("(File name unknown)")) + "\n" + pr.getErrorMessage(); diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java index 26ba6b0ac53..4ab31eb094f 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java @@ -217,7 +217,7 @@ public void open() { dialogService.showErrorDialogAndWait(Localization.lang("File not found"), Localization.lang("Could not find file '%0'.", linkedFile.getLink())); } } catch (IOException e) { - dialogService.showErrorDialogAndWait(Localization.lang("Error opening file '%0'.", linkedFile.getLink()), e); + dialogService.showErrorDialogAndWait(Localization.lang("Error opening file '%0'", linkedFile.getLink()), e); } } diff --git a/src/main/java/org/jabref/gui/importer/ImportCommand.java b/src/main/java/org/jabref/gui/importer/ImportCommand.java index 60012994b97..fd74e7a5199 100644 --- a/src/main/java/org/jabref/gui/importer/ImportCommand.java +++ b/src/main/java/org/jabref/gui/importer/ImportCommand.java @@ -146,7 +146,7 @@ private ParserResult doImport(List files, Importer importFormat) throws IO if (FileUtil.isPDFFile(filename) && GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(dialogService, preferencesService.getGrobidPreferences())) { importFormatReader.reset(); } - dialogService.notify(Localization.lang("Importing in unknown format") + "..."); + dialogService.notify(Localization.lang("Importing file %0 as unknown format", filename.getFileName().toString())); }); // This import method never throws an IOException imports.add(importFormatReader.importUnknownFormat(filename, fileUpdateMonitor)); diff --git a/src/main/java/org/jabref/gui/remote/CLIMessageHandler.java b/src/main/java/org/jabref/gui/remote/CLIMessageHandler.java index c24d4298442..272d84fd600 100644 --- a/src/main/java/org/jabref/gui/remote/CLIMessageHandler.java +++ b/src/main/java/org/jabref/gui/remote/CLIMessageHandler.java @@ -38,7 +38,7 @@ public void handleCommandLineArguments(String[] message) { preferencesService, fileUpdateMonitor, entryTypesManager); - + argumentProcessor.processArguments(); List loaded = argumentProcessor.getParserResults(); for (int i = 0; i < loaded.size(); i++) { ParserResult pr = loaded.get(i); diff --git a/src/main/java/org/jabref/logic/bst/BstPreviewLayout.java b/src/main/java/org/jabref/logic/bst/BstPreviewLayout.java index a002d388e60..06a253968b9 100644 --- a/src/main/java/org/jabref/logic/bst/BstPreviewLayout.java +++ b/src/main/java/org/jabref/logic/bst/BstPreviewLayout.java @@ -30,14 +30,14 @@ public BstPreviewLayout(Path path) { name = path.getFileName().toString(); if (!Files.exists(path)) { LOGGER.error("File {} not found", path.toAbsolutePath()); - error = Localization.lang("Error opening file '%0'.", path.toString()); + error = Localization.lang("Error opening file '%0'", path.toString()); return; } try { bstVM = new BstVM(path); } catch (Exception e) { LOGGER.error("Could not read {}.", path.toAbsolutePath(), e); - error = Localization.lang("Error opening file '%0'.", path.toString()); + error = Localization.lang("Error opening file '%0'", path.toString()); } } diff --git a/src/main/java/org/jabref/logic/exporter/TemplateExporter.java b/src/main/java/org/jabref/logic/exporter/TemplateExporter.java index c2e0e7e6e1c..d9d0c33766d 100644 --- a/src/main/java/org/jabref/logic/exporter/TemplateExporter.java +++ b/src/main/java/org/jabref/logic/exporter/TemplateExporter.java @@ -311,7 +311,6 @@ public void export(final BibDatabaseContext databaseContext, missingFormatters.addAll(endLayout.getMissingFormatters()); } - // Clear custom name formatters: layoutPreferences.clearCustomExportNameFormatters(); if (!missingFormatters.isEmpty() && LOGGER.isWarnEnabled()) { diff --git a/src/main/java/org/jabref/logic/layout/Layout.java b/src/main/java/org/jabref/logic/layout/Layout.java index a465d578fd0..65587b1f81b 100644 --- a/src/main/java/org/jabref/logic/layout/Layout.java +++ b/src/main/java/org/jabref/logic/layout/Layout.java @@ -117,7 +117,7 @@ public String doLayout(BibEntry bibtex, BibDatabase database) { /** * Returns the processed text. If the database argument is - * null, no string references will be resolved. Otherwise all valid + * null, no string references will be resolved. Otherwise, all valid * string references will be replaced by the strings' contents. Even * recursive string references are resolved. */ diff --git a/src/main/java/org/jabref/preferences/ExportPreferences.java b/src/main/java/org/jabref/preferences/ExportPreferences.java index 85e1129410e..1e653f45c8a 100644 --- a/src/main/java/org/jabref/preferences/ExportPreferences.java +++ b/src/main/java/org/jabref/preferences/ExportPreferences.java @@ -24,7 +24,6 @@ public ExportPreferences(String lastExportExtension, Path exportWorkingDirectory, SaveOrder exportSaveOrder, List customExporters) { - this.lastExportExtension = new SimpleStringProperty(lastExportExtension); this.exportWorkingDirectory = new SimpleObjectProperty<>(exportWorkingDirectory); this.exportSaveOrder = new SimpleObjectProperty<>(exportSaveOrder); diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index ba88549b5b0..64eb1845322 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -2287,7 +2287,7 @@ private void storeExportSaveOrder(SaveOrder saveOrder) { * For the export configuration, generates the SelfContainedSaveOrder having the reference to TABLE resolved. */ public SelfContainedSaveOrder getSelfContainedTableSaveOrder() { - List sortOrder = mainTableColumnPreferences.getColumnSortOrder(); + List sortOrder = getMainTableColumnPreferences().getColumnSortOrder(); return new SelfContainedSaveOrder( SaveOrder.OrderType.SPECIFIED, sortOrder.stream().flatMap(model -> model.getSortCriteria().stream()).toList()); diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 2e8c314d89b..7abf562ad2c 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -179,8 +179,6 @@ Copy\ to\ clipboard=Copy to clipboard Could\ not\ call\ executable=Could not call executable -Could\ not\ export\ file=Could not export file - Could\ not\ export\ preferences=Could not export preferences Could\ not\ find\ a\ suitable\ import\ format.=Could not find a suitable import format. @@ -315,7 +313,6 @@ Entry\ table\ columns=Entry table columns Entry\ Title\ (Required\ to\ deliver\ recommendations.)=Entry Title (Required to deliver recommendations.) Error=Error Error\ occurred\ when\ parsing\ entry=Error occurred when parsing entry -Error\ opening\ file=Error opening file Error\ during\ persistence\ of\ crawling\ results.=Error during persistence of crawling results. Error\ during\ reading\ of\ study\ definition\ file.=Error during reading of study definition file. '%0'\ exists.\ Overwrite\ file?='%0' exists. Overwrite file? @@ -324,7 +321,15 @@ Export\ preferences=Export preferences Export\ preferences\ to\ file=Export preferences to file Export\ to\ clipboard=Export to clipboard Export\ to\ text\ file.=Export to text file. -Exporting=Exporting + +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 + +Importing\ %0=Importing %0 +Importing\ file\ %0\ as\ unknown\ format=Importing file %0 as unknown format +Format\ used\:\ %0=Format used: %0 + Extension=Extension External\ Changes\ Resolver=External Changes Resolver @@ -376,7 +381,6 @@ Format\:\ Tab\:field;field;...\ (e.g.\ General\:url;pdf;note...)=Format\: Tab\:f Format\ of\ author\ and\ editor\ names=Format of author and editor names Format\ string=Format string -Format\ used=Format used Formatter\ name=Formatter name found\ in\ AUX\ file=found in AUX file @@ -444,10 +448,6 @@ Imported\ entries=Imported entries Importer\ class=Importer class -Importing=Importing - -Importing\ in\ unknown\ format=Importing in unknown format - Include\ subgroups\:\ When\ selected,\ view\ entries\ contained\ in\ this\ group\ or\ its\ subgroups=Include subgroups: When selected, view entries contained in this group or its subgroups Independent\ group\:\ When\ selected,\ view\ only\ this\ group's\ entries=Independent group: When selected, view only this group's entries @@ -573,7 +573,7 @@ New\ group=New group New\ string=New string Next\ entry=Next entry -no\ base-BibTeX-file\ specified=no base-BibTeX-file specified +no\ base-BibTeX-file\ specified!=no base-BibTeX-file specified! no\ library\ generated=no library generated @@ -932,8 +932,6 @@ Unknown\ BibTeX\ entries\:=Unknown BibTeX entries\: unknown\ edit=unknown edit -Unknown\ export\ format=Unknown export format - untitled=untitled Upgrade\ external\ PDF/PS\ links\ to\ use\ the\ '%0'\ field.=Upgrade external PDF/PS links to use the '%0' field. @@ -1021,7 +1019,8 @@ Cannot\ use\ port\ %0\ for\ remote\ operation;\ another\ application\ may\ be\ u Looking\ for\ full\ text\ document...=Looking for full text document... A\ local\ copy\ will\ be\ opened.=A local copy will be opened. -Error\ opening\ file\ '%0'.=Error opening file '%0'. +Error\ opening\ file=Error opening file +Error\ opening\ file\ '%0'=Error opening file '%0' Formatter\ not\ found\:\ %0=Formatter not found: %0 diff --git a/src/main/resources/resource/layout/listrefs/listrefs.end.layout b/src/main/resources/resource/layout/listrefs/listrefs.end.layout index b3b7bb80672..9b01f32434f 100644 --- a/src/main/resources/resource/layout/listrefs/listrefs.end.layout +++ b/src/main/resources/resource/layout/listrefs/listrefs.end.layout @@ -1,8 +1,8 @@
- Created by JabRef on \format[CurrentDate]{dd/MM/yyyy}. + Created by JabRef on \format[CurrentDate]{yyyy/MM/dd}.
- \ No newline at end of file + diff --git a/src/main/resources/resource/layout/tablerefs/tablerefs.end.layout b/src/main/resources/resource/layout/tablerefs/tablerefs.end.layout index f59a0f8bd6c..3b054a36bcf 100644 --- a/src/main/resources/resource/layout/tablerefs/tablerefs.end.layout +++ b/src/main/resources/resource/layout/tablerefs/tablerefs.end.layout @@ -1,10 +1,10 @@
- Created by JabRef on \format[CurrentDate]{dd/MM/yyyy}. + Created by JabRef on \format[CurrentDate]{yyyy/MM/dd}.
- \ No newline at end of file + diff --git a/src/main/resources/resource/layout/tablerefsabsbib/tablerefsabsbib.end.layout b/src/main/resources/resource/layout/tablerefsabsbib/tablerefsabsbib.end.layout index b3b7bb80672..9b01f32434f 100644 --- a/src/main/resources/resource/layout/tablerefsabsbib/tablerefsabsbib.end.layout +++ b/src/main/resources/resource/layout/tablerefsabsbib/tablerefsabsbib.end.layout @@ -1,8 +1,8 @@
- Created by JabRef on \format[CurrentDate]{dd/MM/yyyy}. + Created by JabRef on \format[CurrentDate]{yyyy/MM/dd}.
- \ No newline at end of file + diff --git a/src/test/java/org/jabref/cli/ArgumentProcessorTest.java b/src/test/java/org/jabref/cli/ArgumentProcessorTest.java index 78dcb77f755..55c9520b30a 100644 --- a/src/test/java/org/jabref/cli/ArgumentProcessorTest.java +++ b/src/test/java/org/jabref/cli/ArgumentProcessorTest.java @@ -10,14 +10,19 @@ import org.jabref.cli.ArgumentProcessor.Mode; import org.jabref.logic.bibtex.BibEntryAssert; +import org.jabref.logic.exporter.BibDatabaseWriter; +import org.jabref.logic.exporter.SelfContainedSaveConfiguration; import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.logic.importer.ImporterPreferences; import org.jabref.logic.importer.fileformat.BibtexImporter; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.metadata.SaveOrder; +import org.jabref.model.metadata.SelfContainedSaveOrder; import org.jabref.model.search.rules.SearchRules; import org.jabref.model.util.DummyFileUpdateMonitor; import org.jabref.model.util.FileUpdateMonitor; +import org.jabref.preferences.ExportPreferences; import org.jabref.preferences.PreferencesService; import org.jabref.preferences.SearchPreferences; @@ -32,8 +37,6 @@ class ArgumentProcessorTest { - private ArgumentProcessor processor; - private BibtexImporter bibtexImporter; private final PreferencesService preferencesService = mock(PreferencesService.class, Answers.RETURNS_DEEP_STUBS); private final BibEntryTypesManager entryTypesManager = mock(BibEntryTypesManager.class); private final ImporterPreferences importerPreferences = mock(ImporterPreferences.class, Answers.RETURNS_DEEP_STUBS); @@ -42,16 +45,16 @@ class ArgumentProcessorTest { @BeforeEach() void setup() { when(importerPreferences.getCustomImporters()).thenReturn(FXCollections.emptyObservableSet()); + + when(preferencesService.getImporterPreferences()).thenReturn(importerPreferences); + when(preferencesService.getImportFormatPreferences()).thenReturn(importFormatPreferences); when(preferencesService.getSearchPreferences()).thenReturn( new SearchPreferences(null, EnumSet.noneOf(SearchRules.SearchFlags.class), false) ); - - bibtexImporter = new BibtexImporter(importFormatPreferences, new DummyFileUpdateMonitor()); } @Test void testAuxImport(@TempDir Path tempDir) throws Exception { - String auxFile = Path.of(AuxCommandLineTest.class.getResource("paper.aux").toURI()).toAbsolutePath().toString(); String originBib = Path.of(AuxCommandLineTest.class.getResource("origin.bib").toURI()).toAbsolutePath().toString(); @@ -60,12 +63,13 @@ void testAuxImport(@TempDir Path tempDir) throws Exception { List args = List.of("--nogui", "--debug", "--aux", auxFile + "," + outputBibFile, originBib); - processor = new ArgumentProcessor( + ArgumentProcessor processor = new ArgumentProcessor( args.toArray(String[]::new), Mode.INITIAL_START, preferencesService, mock(FileUpdateMonitor.class), entryTypesManager); + processor.processArguments(); assertTrue(Files.exists(outputBib)); } @@ -79,6 +83,8 @@ void testExportMatches(@TempDir Path tempDir) throws Exception { Objects.requireNonNull(ArgumentProcessorTest.class.getResource("ArgumentProcessorTestExportMatches.bib")) .toURI() ); + + BibtexImporter bibtexImporter = new BibtexImporter(importFormatPreferences, new DummyFileUpdateMonitor()); List expectedEntries = bibtexImporter.importDatabase(expectedBib).getDatabase().getEntries(); Path outputBib = tempDir.resolve("output.bib").toAbsolutePath(); @@ -86,14 +92,46 @@ void testExportMatches(@TempDir Path tempDir) throws Exception { List args = List.of("-n", "--debug", "--exportMatches", "Author=Einstein," + outputBibFile, originBibFile); - processor = new ArgumentProcessor( + ArgumentProcessor processor = new ArgumentProcessor( args.toArray(String[]::new), Mode.INITIAL_START, preferencesService, mock(FileUpdateMonitor.class), entryTypesManager); + processor.processArguments(); assertTrue(Files.exists(outputBib)); BibEntryAssert.assertEquals(expectedEntries, outputBib, bibtexImporter); } + + @Test + void convertBibtexToTablerefsabsbib(@TempDir Path tempDir) throws Exception { + Path originBib = Path.of(Objects.requireNonNull(ArgumentProcessorTest.class.getResource("origin.bib")).toURI()); + String originBibFile = originBib.toAbsolutePath().toString(); + + Path outputHtml = tempDir.resolve("output.html").toAbsolutePath(); + String outputHtmlFile = outputHtml.toAbsolutePath().toString(); + + when(importerPreferences.getCustomImporters()) .thenReturn(FXCollections.emptyObservableSet()); + + SaveOrder saveOrder = new SaveOrder(SaveOrder.OrderType.TABLE, List.of()); + ExportPreferences exportPreferences = new ExportPreferences(".html", tempDir, saveOrder, List.of()); + when(preferencesService.getExportPreferences()).thenReturn(exportPreferences); + + SelfContainedSaveOrder selfContainedSaveOrder = new SelfContainedSaveOrder(SaveOrder.OrderType.ORIGINAL, List.of()); + SelfContainedSaveConfiguration selfContainedSaveConfiguration = new SelfContainedSaveConfiguration(selfContainedSaveOrder, false, BibDatabaseWriter.SaveType.WITH_JABREF_META_DATA, false); + when(preferencesService.getSelfContainedExportConfiguration()).thenReturn(selfContainedSaveConfiguration); + + List args = List.of("-n", "-i", originBibFile + ",bibtex", "-o", outputHtmlFile + ",tablerefsabsbib"); + + ArgumentProcessor processor = new ArgumentProcessor( + args.toArray(String[]::new), + Mode.INITIAL_START, + preferencesService, + mock(FileUpdateMonitor.class), + entryTypesManager); + processor.processArguments(); + + assertTrue(Files.exists(outputHtml)); + } } From 2760070932e73ea46ee459b4dfe22b022d576712 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 16 Sep 2023 13:50:26 +0200 Subject: [PATCH 40/94] [Bot] Update journal abbreviation lists (#10387) * Update journal abbreviation lists * Improve name * Rename job * Try to fix trigger (and allow manual run) * Refine condition * Try other token for approve * Pin repository to JabRef * Constrain even more --------- Co-authored-by: github-merge-queue Co-authored-by: Oliver Kopp --- .github/workflows/automerge.yml | 11 ++++++----- .github/workflows/refresh-csl-subtrees.yml | 2 +- .github/workflows/refresh-journal-lists.yml | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 96b841cadfc..7a96e7b7d96 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -1,20 +1,21 @@ -name: Dependabot auto-merge -on: pull_request +name: Auto Merge +on: [pull_request, workflow_dispatch] permissions: contents: write pull-requests: write jobs: - dependabot: + automerge: runs-on: ubuntu-latest - if: ${{ (github.actor == 'dependabot[bot]') || (github.actor == 'koppor' && startsWith(github.event.pull_request.title, '[Bot]')) }} + # Run only if PR is inside JabRef's main repository and created by dependabot or by an update workflow + if: ${{ (github.repository == 'JabRef/jabref') && ((github.actor == 'dependabot[bot]') || (startsWith(github.event.pull_request.title, '[Bot]'))) }} steps: - name: Approve PR run: gh pr review --approve "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + GITHUB_TOKEN: ${{secrets.GH_TOKEN_JABREF_MACHINE_PR_APPROVE}} - name: Merge PR run: gh pr merge --auto "$PR_URL" env: diff --git a/.github/workflows/refresh-csl-subtrees.yml b/.github/workflows/refresh-csl-subtrees.yml index bf307b65cd2..2ed569dcb07 100644 --- a/.github/workflows/refresh-csl-subtrees.yml +++ b/.github/workflows/refresh-csl-subtrees.yml @@ -15,7 +15,7 @@ jobs: permissions: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request to create a PR - if: (github.repository == 'JabRef/jabref' || github.repository == 'koppor/jabref') + if: (github.repository == 'JabRef/jabref') steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/refresh-journal-lists.yml b/.github/workflows/refresh-journal-lists.yml index 411953fc5f0..340fb4553f3 100644 --- a/.github/workflows/refresh-journal-lists.yml +++ b/.github/workflows/refresh-journal-lists.yml @@ -15,7 +15,7 @@ jobs: permissions: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request to create a PR - if: (github.repository == 'JabRef/jabref' || github.repository == 'koppor/jabref') + if: (github.repository == 'JabRef/jabref') steps: - uses: actions/checkout@v4 with: From 734c645078a10435dc60be4295692a21e66c2cbb Mon Sep 17 00:00:00 2001 From: Siedlerchr Date: Sat, 16 Sep 2023 19:05:41 +0200 Subject: [PATCH 41/94] update javafx to 20.0.2 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ac1a3c81826..5cb28c43ff0 100644 --- a/build.gradle +++ b/build.gradle @@ -105,7 +105,7 @@ dependencyLocking { } javafx { - version = "20" + version = "20.0.2" modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.web', 'javafx.swing' ] } From a7d3398ce146f9dec71bc4cb6e5426f671d92242 Mon Sep 17 00:00:00 2001 From: Christoph Date: Sun, 17 Sep 2023 16:03:26 +0200 Subject: [PATCH 42/94] Try to fix ioob for selected entries (#10389) Co-authored-by: Oliver Kopp --- src/main/java/org/jabref/gui/LibraryTab.java | 20 ++++++++++--------- .../org/jabref/gui/actions/ActionHelper.java | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 0ca5bc7d9cf..fc4880d2562 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -34,6 +34,7 @@ import org.jabref.gui.dialogs.AutosaveUiManager; import org.jabref.gui.entryeditor.EntryEditor; import org.jabref.gui.importer.actions.OpenDatabaseAction; +import org.jabref.gui.maintable.BibEntryTableViewModel; import org.jabref.gui.maintable.MainTable; import org.jabref.gui.maintable.MainTableDataModel; import org.jabref.gui.undo.CountingUndoManager; @@ -344,7 +345,7 @@ public void updateTabTitle(boolean isChanged) { Path databasePath = file.get(); String fileName = databasePath.getFileName().toString(); tabTitle.append(fileName); - toolTipText.append(databasePath.toAbsolutePath().toString()); + toolTipText.append(databasePath.toAbsolutePath()); if (databaseLocation == DatabaseLocation.SHARED) { tabTitle.append(" \u2013 "); @@ -517,15 +518,16 @@ private void createMainTable() { entryTypesManager, taskExecutor, fileUpdateMonitor); - // Add the listener that binds selection to state manager (TODO: should be replaced by proper JavaFX binding as soon as table is implemented in JavaFX) - mainTable.addSelectionListener(listEvent -> stateManager.setSelectedEntries(mainTable.getSelectedEntries())); - - // Update entry editor and preview according to selected entries - mainTable.addSelectionListener(event -> mainTable.getSelectedEntries() - .stream() - .findFirst() - .ifPresent(entryEditor::setEntry)); + // content binding between StateManager#getselectedEntries and mainTable#getSelectedEntries does not work here as it does not trigger the ActionHelper#needsEntriesSelected checker for the menubar + mainTable.addSelectionListener(event -> { + List entries = event.getList().stream().map(BibEntryTableViewModel::getEntry).toList(); + stateManager.setSelectedEntries(entries); + if (!entries.isEmpty()) { + // Update entry editor and preview according to selected entries + entryEditor.setEntry(entries.get(0)); + } + }); } public void setupMainPanel() { diff --git a/src/main/java/org/jabref/gui/actions/ActionHelper.java b/src/main/java/org/jabref/gui/actions/ActionHelper.java index 3a977e1cee6..e82784a4782 100644 --- a/src/main/java/org/jabref/gui/actions/ActionHelper.java +++ b/src/main/java/org/jabref/gui/actions/ActionHelper.java @@ -67,7 +67,7 @@ public static BooleanExpression isFilePresentForSelectedEntry(StateManager state Binding fileIsPresent = EasyBind.valueAt(selectedEntries, 0).mapOpt(entry -> { List files = entry.getFiles(); - if ((entry.getFiles().size() > 0) && stateManager.getActiveDatabase().isPresent()) { + if ((!entry.getFiles().isEmpty()) && stateManager.getActiveDatabase().isPresent()) { if (files.get(0).isOnlineLink()) { return true; } From 9bc5c5d93f564f8b53a4a616cd4fb9befe3d5d02 Mon Sep 17 00:00:00 2001 From: Nitin Suresh Date: Mon, 18 Sep 2023 03:37:43 -0700 Subject: [PATCH 43/94] Add LOBID fetcher (#10135) --- CHANGELOG.md | 1 + .../jabref/logic/importer/WebFetchers.java | 2 + .../logic/importer/fetcher/LOBIDFetcher.java | 203 ++++++++++++++++++ .../transformers/LOBIDQueryTransformer.java | 48 +++++ .../ManageStudyDefinitionViewModelTest.java | 2 + .../importer/fetcher/LOBIDFetcherTest.java | 109 ++++++++++ 6 files changed, 365 insertions(+) create mode 100644 src/main/java/org/jabref/logic/importer/fetcher/LOBIDFetcher.java create mode 100644 src/main/java/org/jabref/logic/importer/fetcher/transformers/LOBIDQueryTransformer.java create mode 100644 src/test/java/org/jabref/logic/importer/fetcher/LOBIDFetcherTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index bb3cc514b88..014929eaf10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added support for customizing the citation command (e.g., `[@key1,@key2]`) when [pushing to external applications](https://docs.jabref.org/cite/pushtoapplications). [#10133](https://github.com/JabRef/jabref/issues/10133) - We added an integrity check for more special characters. [#8712](https://github.com/JabRef/jabref/issues/8712) - We added protected terms described as "Computer science". [#10222](https://github.com/JabRef/jabref/pull/10222) +- We added a fetcher for [LOBID](https://lobid.org/resources/api) resources. [koppor#386](https://github.com/koppor/jabref/issues/386) ### Changed diff --git a/src/main/java/org/jabref/logic/importer/WebFetchers.java b/src/main/java/org/jabref/logic/importer/WebFetchers.java index 918eacd3a13..95b6fce4f9d 100644 --- a/src/main/java/org/jabref/logic/importer/WebFetchers.java +++ b/src/main/java/org/jabref/logic/importer/WebFetchers.java @@ -28,6 +28,7 @@ import org.jabref.logic.importer.fetcher.IEEE; import org.jabref.logic.importer.fetcher.INSPIREFetcher; import org.jabref.logic.importer.fetcher.IacrEprintFetcher; +import org.jabref.logic.importer.fetcher.LOBIDFetcher; import org.jabref.logic.importer.fetcher.LibraryOfCongress; import org.jabref.logic.importer.fetcher.MathSciNet; import org.jabref.logic.importer.fetcher.MedlineFetcher; @@ -119,6 +120,7 @@ public static SortedSet getSearchBasedFetchers(ImportFormatP set.add(new SemanticScholar()); set.add(new ResearchGate(importFormatPreferences)); set.add(new BiodiversityLibrary(importerPreferences)); + set.add(new LOBIDFetcher(importerPreferences)); return set; } diff --git a/src/main/java/org/jabref/logic/importer/fetcher/LOBIDFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/LOBIDFetcher.java new file mode 100644 index 00000000000..87c4045f94b --- /dev/null +++ b/src/main/java/org/jabref/logic/importer/fetcher/LOBIDFetcher.java @@ -0,0 +1,203 @@ +package org.jabref.logic.importer.fetcher; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.jabref.logic.importer.FetcherException; +import org.jabref.logic.importer.ImporterPreferences; +import org.jabref.logic.importer.PagedSearchBasedParserFetcher; +import org.jabref.logic.importer.Parser; +import org.jabref.logic.importer.fetcher.transformers.LOBIDQueryTransformer; +import org.jabref.logic.util.OS; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.types.EntryType; +import org.jabref.model.entry.types.StandardEntryType; + +import kong.unirest.json.JSONArray; +import kong.unirest.json.JSONObject; +import org.apache.http.client.utils.URIBuilder; +import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Fetches data from the LOBID API + * + * @see API documentation for more details + */ +public class LOBIDFetcher implements PagedSearchBasedParserFetcher { + + public static final String FETCHER_NAME = "LOBID"; + + private static final Logger LOGGER = LoggerFactory.getLogger(LOBIDFetcher.class); + + private static final String API_URL = "https://lobid.org/resources/search"; + + private final ImporterPreferences importerPreferences; + + public LOBIDFetcher(ImporterPreferences importerPreferences) { + this.importerPreferences = importerPreferences; + } + + /** + * Gets the query URL + * + * @param luceneQuery the search query + * @param pageNumber the number of the page indexed from 0 + * @return URL + */ + @Override + public URL getURLForQuery(QueryNode luceneQuery, int pageNumber) throws URISyntaxException, MalformedURLException, FetcherException { + URIBuilder uriBuilder = new URIBuilder(API_URL); + uriBuilder.addParameter("q", new LOBIDQueryTransformer().transformLuceneQuery(luceneQuery).orElse("")); // search query + uriBuilder.addParameter("from", String.valueOf(getPageSize() * pageNumber)); // from entry number, starts indexing at 0 + uriBuilder.addParameter("size", String.valueOf(getPageSize())); // page size + uriBuilder.addParameter("format", "json"); // response format + return uriBuilder.build().toURL(); + } + + @Override + public Parser getParser() { + return inputStream -> { + String response = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining(OS.NEWLINE)); + JSONObject jsonObject = new JSONObject(response); + + List entries = new ArrayList<>(); + if (jsonObject.has("member")) { + JSONArray results = jsonObject.getJSONArray("member"); + for (int i = 0; i < results.length(); i++) { + JSONObject jsonEntry = results.getJSONObject(i); + BibEntry entry = parseJSONtoBibtex(jsonEntry); + entries.add(entry); + } + } + + return entries; + }; + } + + private BibEntry parseJSONtoBibtex(JSONObject jsonEntry) { + BibEntry entry = new BibEntry(); + Field nametype = StandardField.JOURNAL; + EntryType entryType = StandardEntryType.InCollection; + + // publication type + JSONArray typeArray = jsonEntry.optJSONArray("type"); + String types = ""; + if (typeArray != null) { + List typeList = IntStream.range(0, typeArray.length()) + .mapToObj(typeArray::optString) + .filter(type -> !type.isEmpty()) + .toList(); + types = String.join(", ", typeList); + entry.setField(StandardField.TYPE, types); + } + + if (types.toLowerCase().contains("book")) { + entryType = StandardEntryType.Book; + nametype = StandardField.BOOKTITLE; + } else if (types.toLowerCase().contains("article")) { + entryType = StandardEntryType.Article; + } + entry.setType(entryType); + + // isbn + String isbn = getFirstArrayElement(jsonEntry, "isbn"); + entry.setField(StandardField.ISBN, isbn); + + // parent resource + String bibliographicCitation = jsonEntry.optString("bibliographicCitation", ""); + String[] bibSplit = bibliographicCitation.split("/"); + String parentResource = ""; + if (bibSplit.length > 0) { + parentResource = bibSplit[0].trim(); + entry.setField(nametype, parentResource); + } + + entry.setField(StandardField.ISSN, getFirstArrayElement(jsonEntry, "issn")); + entry.setField(StandardField.TITLE, jsonEntry.optString("title", "")); + entry.setField(StandardField.ABSTRACT, getFirstArrayElement(jsonEntry, "note")); + entry.setField(StandardField.TITLEADDON, getFirstArrayElement(jsonEntry, "otherTitleInformation")); + entry.setField(StandardField.EDITION, getFirstArrayElement(jsonEntry, "edition")); + + // authors + JSONArray authors = jsonEntry.optJSONArray("contribution"); + if (authors != null) { + List authorNames = getAuthorNames(authors); + if (!authors.isEmpty()) { + entry.setField(StandardField.AUTHOR, String.join(" and ", authorNames)); + } + } + + // publication + Optional.ofNullable(jsonEntry.optJSONArray("publication")) + .map(array -> array.getJSONObject(0)) + .ifPresent(publication -> { + entry.setField(StandardField.PUBLISHER, getFirstArrayElement(publication, "publishedBy")); + entry.setField(StandardField.LOCATION, getFirstArrayElement(publication, "location")); + String date = publication.optString("startDate"); + entry.setField(StandardField.DATE, date); + entry.setField(StandardField.YEAR, date); + }); + + // url + JSONObject describedBy = jsonEntry.optJSONObject("describedBy"); + if (describedBy != null) { + entry.setField(StandardField.URL, describedBy.optString("id")); + } + + // language + JSONArray languageArray = jsonEntry.optJSONArray("language"); + if (languageArray != null) { + List languageList = IntStream.range(0, languageArray.length()) + .mapToObj(languageArray::getJSONObject) + .filter(Objects::nonNull) + .map(language -> language.optString("label")) + .toList(); + entry.setField(StandardField.LANGUAGE, String.join(" and ", languageList)); + } + + // keywords + JSONArray keywordArray = jsonEntry.optJSONArray("subjectslabels"); + if (keywordArray != null) { + List keywordList = IntStream.range(0, keywordArray.length()) + .mapToObj(keywordArray::optString) + .filter(keyword -> !keyword.isEmpty()) + .toList(); + entry.setField(StandardField.KEYWORDS, String.join(", ", keywordList)); + } + + return entry; + } + + private static List getAuthorNames(JSONArray authors) { + return IntStream.range(0, authors.length()) + .mapToObj(authors::getJSONObject) + .map(author -> author.optJSONObject("agent")) + .filter(Objects::nonNull) + .map(agent -> agent.optString("label")) + .toList(); + } + + private static String getFirstArrayElement(JSONObject jsonEntry, String key) { + return Optional.ofNullable(jsonEntry.optJSONArray(key)) + .map(array -> array.getString(0)) + .orElse(""); + } + + @Override + public String getName() { + return FETCHER_NAME; + } +} diff --git a/src/main/java/org/jabref/logic/importer/fetcher/transformers/LOBIDQueryTransformer.java b/src/main/java/org/jabref/logic/importer/fetcher/transformers/LOBIDQueryTransformer.java new file mode 100644 index 00000000000..735b4ab240b --- /dev/null +++ b/src/main/java/org/jabref/logic/importer/fetcher/transformers/LOBIDQueryTransformer.java @@ -0,0 +1,48 @@ +package org.jabref.logic.importer.fetcher.transformers; + +public class LOBIDQueryTransformer extends AbstractQueryTransformer { + + @Override + public String getLogicalAndOperator() { + return " AND "; + } + + @Override + public String getLogicalOrOperator() { + return " OR "; + } + + @Override + protected String getLogicalNotOperator() { + return "-"; + } + + @Override + protected String handleAuthor(String author) { + return createKeyValuePair("contribution.agent.label", author); + } + + @Override + protected String handleTitle(String title) { + return createKeyValuePair("title", title); + } + + @Override + protected String handleJournal(String journalTitle) { + return createKeyValuePair("bibliographicCitation", journalTitle); + } + + @Override + protected String handleYear(String year) { + return "publication.startDate:[" + year + " TO " + year + "]"; + } + + @Override + protected String handleYearRange(String yearRange) { + parseYearRange(yearRange); + if (endYear == Integer.MAX_VALUE) { + return yearRange; + } + return "publication.startDate:[" + startYear + " TO " + endYear + "]"; + } +} diff --git a/src/test/java/org/jabref/gui/slr/ManageStudyDefinitionViewModelTest.java b/src/test/java/org/jabref/gui/slr/ManageStudyDefinitionViewModelTest.java index 2e28e83c7c6..eac16dd0c3f 100644 --- a/src/test/java/org/jabref/gui/slr/ManageStudyDefinitionViewModelTest.java +++ b/src/test/java/org/jabref/gui/slr/ManageStudyDefinitionViewModelTest.java @@ -46,6 +46,7 @@ public void emptyStudyConstructorFillsDatabasesCorrectly() { new StudyCatalogItem("GVK", false), new StudyCatalogItem("IEEEXplore", true), new StudyCatalogItem("INSPIRE", false), + new StudyCatalogItem("LOBID", false), new StudyCatalogItem("MathSciNet", false), new StudyCatalogItem("Medline/PubMed", false), new StudyCatalogItem("ResearchGate", false), @@ -86,6 +87,7 @@ public void studyConstructorFillsDatabasesCorrectly(@TempDir Path tempDir) { new StudyCatalogItem("GVK", false), new StudyCatalogItem("IEEEXplore", false), new StudyCatalogItem("INSPIRE", false), + new StudyCatalogItem("LOBID", false), new StudyCatalogItem("MathSciNet", false), new StudyCatalogItem("Medline/PubMed", false), new StudyCatalogItem("ResearchGate", false), diff --git a/src/test/java/org/jabref/logic/importer/fetcher/LOBIDFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/LOBIDFetcherTest.java new file mode 100644 index 00000000000..978dd23c096 --- /dev/null +++ b/src/test/java/org/jabref/logic/importer/fetcher/LOBIDFetcherTest.java @@ -0,0 +1,109 @@ +package org.jabref.logic.importer.fetcher; + +import java.util.Collections; +import java.util.List; + +import org.jabref.logic.importer.ImporterPreferences; +import org.jabref.logic.importer.PagedSearchBasedFetcher; +import org.jabref.logic.importer.SearchBasedFetcher; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.types.StandardEntryType; +import org.jabref.testutils.category.FetcherTest; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; + +@FetcherTest +class LOBIDFetcherTest implements SearchBasedFetcherCapabilityTest, PagedSearchFetcherTest { + + ImporterPreferences importerPreferences = mock(ImporterPreferences.class); + LOBIDFetcher fetcher; + + @BeforeEach + void setUp() { + fetcher = new LOBIDFetcher(importerPreferences); + } + + @Test + void searchByQueryFindsEntry() throws Exception { + BibEntry firstArticle = new BibEntry(StandardEntryType.Book) + .withField(StandardField.AUTHOR, "Nichols, Cathrine and Blume, Eugen and DruckVerlag Kettler GmbH") + .withField(StandardField.PUBLISHER, "Verlag Kettler") + .withField(StandardField.DATE, "2016") + .withField(StandardField.ISBN, "9783862065752") + .withField(StandardField.KEYWORDS, "(Produktform)Hardback, Cathrine Nichols, Eugen Blume, Staatliche Museen zu Berlin, (Produktgruppe)Ausst: Ausstellungskatalog, (VLB-WN)1580: Hardcover, Softcover / Kunst") + .withField(StandardField.LANGUAGE, "Deutsch") + .withField(StandardField.LOCATION, "Dortmund") + .withField(StandardField.TITLE, "Das Kapital") + .withField(StandardField.TITLEADDON, "Schuld, Territorium, Utopie") + .withField(StandardField.TYPE, "BibliographicResource, Book") + .withField(StandardField.URL, "http://lobid.org/resources/991002500969706485") + .withField(StandardField.YEAR, "2016"); + + BibEntry secondArticle = new BibEntry(StandardEntryType.Article) + .withField(StandardField.AUTHOR, "Nielsen, Cathrin") + .withField(StandardField.TITLE, "\"... und die Substanz ist natürlich allein schon ein seelischer Prozeß\"") + .withField(StandardField.JOURNAL, "Beuys. Die Revolution sind wir") + .withField(StandardField.DATE, "2008") + .withField(StandardField.KEYWORDS, "Beuys, Joseph, Physis") + .withField(StandardField.LANGUAGE, "Deutsch") + .withField(StandardField.TYPE, "BibliographicResource, Article") + .withField(StandardField.URL, "http://lobid.org/resources/990173112890206441") + .withField(StandardField.YEAR, "2008"); + + BibEntry thirdArticle = new BibEntry(StandardEntryType.Book) + .withField(StandardField.AUTHOR, "Nichols, Catherine and Blume, Eugen and Hamburger Bahnhof - Museum für Gegenwart - Berlin and Nationalgalerie (Berlin) and DruckVerlag Kettler GmbH") + .withField(StandardField.ABSTRACT, "Impresum: \"Diese Publikation erscheint anlässlich der Ausstellung Das Kapital. Schuld-Territorium-Utopie. Eine Ausstellung der Nationalgalerie im Hamburger Bahnhof - Museum für Gegenwart - Berlin, 2. Juli-6. November 2016\"") + .withField(StandardField.PUBLISHER, "Verlag Kettler") + .withField(StandardField.DATE, "2016") + .withField(StandardField.EDITION, "1. Auflage") + .withField(StandardField.ISBN, "9783862065752") + .withField(StandardField.KEYWORDS, "Cathrine Nichols, Eugen Blume, Staatliche Museen zu Berlin, Beuys, Joseph: Das Kapital Raum 1970-1977, Kunst, Kapitalismus (Motiv), Geschichte") + .withField(StandardField.LANGUAGE, "Deutsch") + .withField(StandardField.LOCATION, "Dortmund") + .withField(StandardField.TITLE, "Das Kapital") + .withField(StandardField.TITLEADDON, "Schuld - Territorium - Utopie") + .withField(StandardField.TYPE, "BibliographicResource, Book") + .withField(StandardField.URL, "http://lobid.org/resources/990212549810206441") + .withField(StandardField.YEAR, "2016"); + + List fetchedEntries = fetcher.performSearch("Cathrine Nichols"); + assertEquals(List.of(firstArticle, secondArticle, thirdArticle), fetchedEntries); + } + + @Test + void searchByEmptyQueryFindsNothing() throws Exception { + assertEquals(Collections.emptyList(), fetcher.performSearch("")); + } + + @Test + @Override + @Disabled("Results returned contain a few incorrect years. The majority are accurate") + public void supportsYearSearch() { + } + + @Override + public PagedSearchBasedFetcher getPagedFetcher() { + return fetcher; + } + + @Override + public SearchBasedFetcher getFetcher() { + return fetcher; + } + + @Override + public List getTestAuthors() { + return List.of("Nichols, Cathrine", "Blume, Eugen"); + } + + @Override + public String getTestJournal() { + return "Kontinuität und Diskontinuität"; + } +} From e6a6089427db1a9f56eba1ccbd1b69148fb3c525 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:42:14 +0000 Subject: [PATCH 44/94] Bump org.openrewrite.rewrite from 6.3.3 to 6.3.7 Bumps org.openrewrite.rewrite from 6.3.3 to 6.3.7. --- updated-dependencies: - dependency-name: org.openrewrite.rewrite dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5cb28c43ff0..d561a79cd3f 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ plugins { id 'idea' - id 'org.openrewrite.rewrite' version '6.3.3' + id 'org.openrewrite.rewrite' version '6.3.7' } // Enable following for debugging From 37f4bbcc684b1842fbf6c50c8d3472d1e32de884 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:42:17 +0000 Subject: [PATCH 45/94] Bump org.jabref:afterburner.fx from 2.0.0-SNAPSHOT to 2.0.0 Bumps [org.jabref:afterburner.fx](https://github.com/JabRef/afterburner.fx) from 2.0.0-SNAPSHOT to 2.0.0. - [Release notes](https://github.com/JabRef/afterburner.fx/releases) - [Changelog](https://github.com/JabRef/afterburner.fx/blob/main/CHANGELOG.md) - [Commits](https://github.com/JabRef/afterburner.fx/commits) --- updated-dependencies: - dependency-name: org.jabref:afterburner.fx dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5cb28c43ff0..2441e430fa6 100644 --- a/build.gradle +++ b/build.gradle @@ -168,7 +168,7 @@ dependencies { implementation 'jakarta.annotation:jakarta.annotation-api:2.1.1' implementation 'jakarta.inject:jakarta.inject-api:2.0.1' - implementation('org.jabref:afterburner.fx:2.0.0-SNAPSHOT') + implementation('org.jabref:afterburner.fx:2.0.0') implementation 'org.kordamp.ikonli:ikonli-javafx:12.3.1' implementation 'org.kordamp.ikonli:ikonli-materialdesign2-pack:12.3.1' implementation 'com.github.sialcasa.mvvmFX:mvvmfx-validation:f195849ca9' //jitpack From a69879bd3e24b6d36e7127c9c9eb62db379a0c22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:42:28 +0000 Subject: [PATCH 46/94] Bump org.openrewrite.recipe:rewrite-recipe-bom from 2.2.1 to 2.3.0 Bumps [org.openrewrite.recipe:rewrite-recipe-bom](https://github.com/openrewrite/rewrite-recipe-bom) from 2.2.1 to 2.3.0. - [Release notes](https://github.com/openrewrite/rewrite-recipe-bom/releases) - [Commits](https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.2.1...v2.3.0) --- updated-dependencies: - dependency-name: org.openrewrite.recipe:rewrite-recipe-bom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5cb28c43ff0..a4eb6f6e607 100644 --- a/build.gradle +++ b/build.gradle @@ -257,7 +257,7 @@ dependencies { xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2' xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2' - rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.2.1")) + rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.3.0")) rewrite("org.openrewrite.recipe:rewrite-static-analysis") rewrite("org.openrewrite.recipe:rewrite-logging-frameworks") rewrite("org.openrewrite.recipe:rewrite-testing-frameworks") From 6bc25a80d45814759e2f9568a67884fa4ad24523 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:42:47 +0000 Subject: [PATCH 47/94] Bump org.libreoffice:libreoffice from 7.6.0 to 7.6.1 Bumps org.libreoffice:libreoffice from 7.6.0 to 7.6.1. --- updated-dependencies: - dependency-name: org.libreoffice:libreoffice dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 5cb28c43ff0..1498adee9fa 100644 --- a/build.gradle +++ b/build.gradle @@ -136,8 +136,8 @@ dependencies { implementation 'commons-cli:commons-cli:1.5.0' - implementation 'org.libreoffice:unoloader:7.6.0' - implementation 'org.libreoffice:libreoffice:7.6.0' + implementation 'org.libreoffice:unoloader:7.6.1' + implementation 'org.libreoffice:libreoffice:7.6.1' implementation 'io.github.java-diff-utils:java-diff-utils:4.12' implementation 'info.debatty:java-string-similarity:2.0.0' From 7bd715c902c0fc96a47e5f0e5a50bc9802b3afea Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 18 Sep 2023 19:48:35 +0200 Subject: [PATCH 48/94] Improve conditions (and add pull_request_target) --- .github/workflows/automerge.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 7a96e7b7d96..e922cde39d4 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -1,5 +1,5 @@ name: Auto Merge -on: [pull_request, workflow_dispatch] +on: [pull_request_target, workflow_dispatch] permissions: contents: write @@ -9,7 +9,10 @@ jobs: automerge: runs-on: ubuntu-latest # Run only if PR is inside JabRef's main repository and created by dependabot or by an update workflow - if: ${{ (github.repository == 'JabRef/jabref') && ((github.actor == 'dependabot[bot]') || (startsWith(github.event.pull_request.title, '[Bot]'))) }} + if: >- + (github.repository == 'JabRef/jabref') && + ((github.actor == 'dependabot[bot]') || + (startsWith(github.event.pull_request.title, '[Bot]') && (github.event.pull_request.head.repo.full_name == 'JabRef/jabref'))) steps: - name: Approve PR run: gh pr review --approve "$PR_URL" From 987c4afc245d4ddf1354dcd8b65da2bc8859ce06 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 18 Sep 2023 19:50:07 +0200 Subject: [PATCH 49/94] Ups --- .github/workflows/automerge.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index e922cde39d4..1acfc4af179 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -12,7 +12,8 @@ jobs: if: >- (github.repository == 'JabRef/jabref') && ((github.actor == 'dependabot[bot]') || - (startsWith(github.event.pull_request.title, '[Bot]') && (github.event.pull_request.head.repo.full_name == 'JabRef/jabref'))) + (startsWith(github.event.pull_request.title, '[Bot]') && + (github.event.pull_request.head.repo.full_name == 'JabRef/jabref'))) steps: - name: Approve PR run: gh pr review --approve "$PR_URL" From 04f9ade94adc6f1f0f5bffae21ae3838a09d06df Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 18 Sep 2023 19:53:08 +0200 Subject: [PATCH 50/94] Fix workflow_dispatch "actor" --- .github/workflows/automerge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 1acfc4af179..fa695d1169c 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -11,7 +11,7 @@ jobs: # Run only if PR is inside JabRef's main repository and created by dependabot or by an update workflow if: >- (github.repository == 'JabRef/jabref') && - ((github.actor == 'dependabot[bot]') || + ((github.event.pull_request.user.login == 'dependabot[bot]') || (startsWith(github.event.pull_request.title, '[Bot]') && (github.event.pull_request.head.repo.full_name == 'JabRef/jabref'))) steps: From 2e50d1651dcf1e08f024b2f1192dc3e47f1d8207 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 18 Sep 2023 19:54:31 +0200 Subject: [PATCH 51/94] Fix --- .github/workflows/automerge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index fa695d1169c..618ecc20cbd 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -11,7 +11,7 @@ jobs: # Run only if PR is inside JabRef's main repository and created by dependabot or by an update workflow if: >- (github.repository == 'JabRef/jabref') && - ((github.event.pull_request.user.login == 'dependabot[bot]') || + ((github.event.pull_request.user.login == 'dependabot') || (startsWith(github.event.pull_request.title, '[Bot]') && (github.event.pull_request.head.repo.full_name == 'JabRef/jabref'))) steps: From 9cb3c8b7abcb19b951865cf335aee996c395c254 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 18 Sep 2023 19:57:36 +0200 Subject: [PATCH 52/94] Try with email --- .github/workflows/automerge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 618ecc20cbd..62dd1c6ed31 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -11,7 +11,7 @@ jobs: # Run only if PR is inside JabRef's main repository and created by dependabot or by an update workflow if: >- (github.repository == 'JabRef/jabref') && - ((github.event.pull_request.user.login == 'dependabot') || + ((github.event.pull_request.user.email == '49699333+dependabot[bot]@users.noreply.github.com') || (startsWith(github.event.pull_request.title, '[Bot]') && (github.event.pull_request.head.repo.full_name == 'JabRef/jabref'))) steps: From d420ed4b8a2d51f080a426ddad61d7431476864b Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 18 Sep 2023 19:59:34 +0200 Subject: [PATCH 53/94] Revert to actor (and relax title checking) --- .github/workflows/automerge.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 62dd1c6ed31..acb3ec0b885 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -11,8 +11,8 @@ jobs: # Run only if PR is inside JabRef's main repository and created by dependabot or by an update workflow if: >- (github.repository == 'JabRef/jabref') && - ((github.event.pull_request.user.email == '49699333+dependabot[bot]@users.noreply.github.com') || - (startsWith(github.event.pull_request.title, '[Bot]') && + ((github.actor == 'dependabot[bot]') || + ((startsWith(github.event.pull_request.title, '[Bot] ') || (startsWith(github.event.pull_request.title, 'Bump '))) && (github.event.pull_request.head.repo.full_name == 'JabRef/jabref'))) steps: - name: Approve PR From 640b4fd6f1f92b17fa6f0ea0841c3f54d3143214 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 18 Sep 2023 20:02:08 +0200 Subject: [PATCH 54/94] Try - --- .github/workflows/automerge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index acb3ec0b885..956dfe3f41a 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -9,7 +9,7 @@ jobs: automerge: runs-on: ubuntu-latest # Run only if PR is inside JabRef's main repository and created by dependabot or by an update workflow - if: >- + if: > (github.repository == 'JabRef/jabref') && ((github.actor == 'dependabot[bot]') || ((startsWith(github.event.pull_request.title, '[Bot] ') || (startsWith(github.event.pull_request.title, 'Bump '))) && From 43a53ccaa2d268fed5e195af77ca642d1f1cc896 Mon Sep 17 00:00:00 2001 From: Luggas <127773292+Luggas4you@users.noreply.github.com> Date: Mon, 18 Sep 2023 19:55:22 +0200 Subject: [PATCH 55/94] Link to online documentation (#10385) * Add PUSH_TO_APPLICATION to HelpFile.java * Add HELP_PUSH_TO_APPLICATION to StandardActions.java * Add Button to ExternalTab.fxml * Add Button to ExternalTab.java * Fix L10 * Replace Global with .getKeyBindingRepository() * Fix checkstyle --- src/main/java/org/jabref/gui/actions/StandardActions.java | 1 + .../org/jabref/gui/preferences/external/ExternalTab.fxml | 4 ++++ .../org/jabref/gui/preferences/external/ExternalTab.java | 8 ++++++++ src/main/java/org/jabref/logic/help/HelpFile.java | 3 ++- src/main/resources/l10n/JabRef_en.properties | 1 + 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/actions/StandardActions.java b/src/main/java/org/jabref/gui/actions/StandardActions.java index 99cfc029201..be26487c333 100644 --- a/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -161,6 +161,7 @@ public enum StandardActions implements Action { HELP_REGEX_SEARCH(Localization.lang("Help on regular expression search"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), HELP_NAME_FORMATTER(Localization.lang("Help on Name Formatting"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), HELP_SPECIAL_FIELDS(Localization.lang("Help on special fields"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), + HELP_PUSH_TO_APPLICATION(Localization.lang("Help on external applications"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), WEB_MENU(Localization.lang("JabRef resources")), OPEN_WEBPAGE(Localization.lang("Website"), Localization.lang("Opens JabRef's website"), IconTheme.JabRefIcons.HOME), OPEN_FACEBOOK("Facebook", Localization.lang("Opens JabRef's Facebook page"), IconTheme.JabRefIcons.FACEBOOK), diff --git a/src/main/java/org/jabref/gui/preferences/external/ExternalTab.fxml b/src/main/java/org/jabref/gui/preferences/external/ExternalTab.fxml index e96004acdcd..803d9bead9a 100644 --- a/src/main/java/org/jabref/gui/preferences/external/ExternalTab.fxml +++ b/src/main/java/org/jabref/gui/preferences/external/ExternalTab.fxml @@ -56,6 +56,10 @@