From 2651e7115f24062500f4242855f4adab2bf1f51a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:01:55 +0000 Subject: [PATCH 01/20] Bump gittools/actions from 3.0.2 to 3.0.3 (#12224) Bumps [gittools/actions](https://github.com/gittools/actions) from 3.0.2 to 3.0.3. - [Release notes](https://github.com/gittools/actions/releases) - [Commits](https://github.com/gittools/actions/compare/v3.0.2...v3.0.3) --- updated-dependencies: - dependency-name: gittools/actions 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> --- .github/workflows/deployment-arm64.yml | 4 ++-- .github/workflows/deployment-jdk-ea.yml | 4 ++-- .github/workflows/deployment.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index 1239e0eac4b..8b26386157d 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -69,12 +69,12 @@ jobs: submodules: 'true' show-progress: 'false' - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v3.0.2 + uses: gittools/actions/gitversion/setup@v3.0.3 with: versionSpec: "5.x" - name: Run GitVersion id: gitversion - uses: gittools/actions/gitversion/execute@v3.0.2 + uses: gittools/actions/gitversion/execute@v3.0.3 - name: Setup JDK uses: actions/setup-java@v4 with: diff --git a/.github/workflows/deployment-jdk-ea.yml b/.github/workflows/deployment-jdk-ea.yml index cad3cf04ac5..799b6c569c7 100644 --- a/.github/workflows/deployment-jdk-ea.yml +++ b/.github/workflows/deployment-jdk-ea.yml @@ -85,12 +85,12 @@ jobs: packages: pigz version: 1.0 - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v3.0.2 + uses: gittools/actions/gitversion/setup@v3.0.3 with: versionSpec: "5.x" - name: Run GitVersion id: gitversion - uses: gittools/actions/gitversion/execute@v3.0.2 + uses: gittools/actions/gitversion/execute@v3.0.3 # JDK - name: 'Set up JDK ${{ matrix.jdk }}' diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 43d3d412fd6..b0411f93902 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -81,12 +81,12 @@ jobs: packages: pigz version: 1.0 - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v3.0.2 + uses: gittools/actions/gitversion/setup@v3.0.3 with: versionSpec: "5.x" - name: Run GitVersion id: gitversion - uses: gittools/actions/gitversion/execute@v3.0.2 + uses: gittools/actions/gitversion/execute@v3.0.3 - name: Setup JDK uses: actions/setup-java@v4 with: From e7a75f342178e4324bd4147236cdd6aac21c97ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:19:50 +0000 Subject: [PATCH 02/20] Bump io.zonky.test:embedded-postgres from 2.0.7 to 2.1.0 (#12225) Bumps [io.zonky.test:embedded-postgres](https://github.com/zonkyio/embedded-postgres) from 2.0.7 to 2.1.0. - [Release notes](https://github.com/zonkyio/embedded-postgres/releases) - [Commits](https://github.com/zonkyio/embedded-postgres/compare/v2.0.7...v2.1.0) --- updated-dependencies: - dependency-name: io.zonky.test:embedded-postgres dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index fe352ec74f4..560ddef1712 100644 --- a/build.gradle +++ b/build.gradle @@ -375,7 +375,7 @@ dependencies { // Even if "compileOnly" is used, IntelliJ always adds to module-info.java. To avoid issues during committing, we use "implementation" instead of "compileOnly" implementation 'io.github.adr:e-adr:2.0.0-SNAPSHOT' - implementation 'io.zonky.test:embedded-postgres:2.0.7' + implementation 'io.zonky.test:embedded-postgres:2.1.0' implementation enforcedPlatform('io.zonky.test.postgres:embedded-postgres-binaries-bom:17.0.0') testImplementation 'io.github.classgraph:classgraph:4.8.177' From 57d2a6eedce239cab8b6980efb3e09a1fd1cd67b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:22:26 +0000 Subject: [PATCH 03/20] Bump org.junit.platform:junit-platform-launcher from 1.10.3 to 1.11.3 (#12227) Bumps [org.junit.platform:junit-platform-launcher](https://github.com/junit-team/junit5) from 1.10.3 to 1.11.3. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/commits) --- updated-dependencies: - dependency-name: org.junit.platform:junit-platform-launcher dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 560ddef1712..0f7b8cbf6d9 100644 --- a/build.gradle +++ b/build.gradle @@ -380,7 +380,7 @@ dependencies { testImplementation 'io.github.classgraph:classgraph:4.8.177' testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' - testImplementation 'org.junit.platform:junit-platform-launcher:1.10.3' + testImplementation 'org.junit.platform:junit-platform-launcher:1.11.3' testImplementation 'org.mockito:mockito-core:5.14.2' testImplementation 'org.xmlunit:xmlunit-core:2.10.0' From d41d2748acb69ef7e22a57b67479fd0906a0b87e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:25:05 +0000 Subject: [PATCH 04/20] Bump commons-io:commons-io from 2.17.0 to 2.18.0 (#12226) Bumps commons-io:commons-io from 2.17.0 to 2.18.0. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0f7b8cbf6d9..599a13e9ab7 100644 --- a/build.gradle +++ b/build.gradle @@ -370,7 +370,7 @@ dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21' // endregion - implementation 'commons-io:commons-io:2.17.0' + implementation 'commons-io:commons-io:2.18.0' // Even if "compileOnly" is used, IntelliJ always adds to module-info.java. To avoid issues during committing, we use "implementation" instead of "compileOnly" implementation 'io.github.adr:e-adr:2.0.0-SNAPSHOT' From 86d3a1ae69689949caad5edd16be0c719f2fa579 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:28:28 +0000 Subject: [PATCH 05/20] Bump dev.langchain4j:langchain4j-mistral-ai from 0.36.0 to 0.36.2 (#12228) Bumps [dev.langchain4j:langchain4j-mistral-ai](https://github.com/langchain4j/langchain4j) from 0.36.0 to 0.36.2. - [Release notes](https://github.com/langchain4j/langchain4j/releases) - [Commits](https://github.com/langchain4j/langchain4j/compare/0.36.0...0.36.2) --- updated-dependencies: - dependency-name: dev.langchain4j:langchain4j-mistral-ai dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 599a13e9ab7..397d67c70ba 100644 --- a/build.gradle +++ b/build.gradle @@ -341,7 +341,7 @@ dependencies { exclude group: 'com.squareup.retrofit2', module: 'retrofit' exclude group: 'org.jetbrains.kotlin' } - implementation('dev.langchain4j:langchain4j-mistral-ai:0.36.0') { + implementation('dev.langchain4j:langchain4j-mistral-ai:0.36.2') { exclude group: 'com.squareup.okhttp3' exclude group: 'com.squareup.retrofit2', module: 'retrofit' exclude group: 'org.jetbrains.kotlin' From 3a2c71aae5561b51232e054748eb70a7194a6f97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:47:17 +0000 Subject: [PATCH 06/20] Bump src/main/resources/csl-locales from `4753e3a` to `96d704d` (#12230) Bumps [src/main/resources/csl-locales](https://github.com/citation-style-language/locales) from `4753e3a` to `96d704d`. - [Release notes](https://github.com/citation-style-language/locales/releases) - [Commits](https://github.com/citation-style-language/locales/compare/4753e3a9aca4b806ac0e3036ed727d47bf8f678e...96d704de2fc7b930ae4a0ec4686a7143bb4a0d33) --- updated-dependencies: - dependency-name: src/main/resources/csl-locales dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/main/resources/csl-locales | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/csl-locales b/src/main/resources/csl-locales index 4753e3a9aca..96d704de2fc 160000 --- a/src/main/resources/csl-locales +++ b/src/main/resources/csl-locales @@ -1 +1 @@ -Subproject commit 4753e3a9aca4b806ac0e3036ed727d47bf8f678e +Subproject commit 96d704de2fc7b930ae4a0ec4686a7143bb4a0d33 From 6b59c55d530759d2a0947de262fa0b82c0093560 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 18:30:47 +0100 Subject: [PATCH 07/20] Bump org.apache.logging.log4j:log4j-to-slf4j from 2.24.1 to 2.24.2 (#12229) Bumps org.apache.logging.log4j:log4j-to-slf4j from 2.24.1 to 2.24.2. --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-to-slf4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 397d67c70ba..03dbafbff48 100644 --- a/build.gradle +++ b/build.gradle @@ -278,7 +278,7 @@ dependencies { // route all requests to java.util.logging to SLF4J (which in turn routes to tinylog) implementation 'org.slf4j:jul-to-slf4j:2.0.16' // route all requests to log4j to SLF4J - implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.24.1' + implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.24.2' implementation('de.undercouch:citeproc-java:3.1.0') { exclude group: 'org.antlr' From 23c2ad6fcf9bcae7e69b9a1667825ae9613c4451 Mon Sep 17 00:00:00 2001 From: Christoph Date: Mon, 25 Nov 2024 20:44:42 +0100 Subject: [PATCH 08/20] Open specified folder in library properties (#12223) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Open specified folder in library properties * add changelöpg * add dot * fix l10n and checkstyle * Update src/main/java/org/jabref/gui/libraryproperties/general/GeneralPropertiesViewModel.java Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> * getBrowseDirectory --------- Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> --- CHANGELOG.md | 1 + .../general/GeneralPropertiesViewModel.java | 28 ++++++++++++++++--- .../model/database/BibDatabaseContext.java | 4 +-- src/main/resources/l10n/JabRef_en.properties | 2 ++ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdf7e65a39e..e33e6ebd9c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We changed the defualt [unwanted charachters](https://docs.jabref.org/setup/citationkeypatterns#removing-unwanted-characters) in the citation key generator and allow a dash (`-`) and colon (`:`) being part of a citation key. [#12144](https://github.com/JabRef/jabref/pull/12144) - The CitationKey column is now a default shown column for the entry table. [#10510](https://github.com/JabRef/jabref/issues/10510) - We disabled the actions "Open Terminal here" and "Reveal in file explorer" for unsaved libraries. [#11920](https://github.com/JabRef/jabref/issues/11920) +- JabRef now opens the corresponding directory in the library properties when "Browse" is clicked. [#12223](https://github.com/JabRef/jabref/pull/12223) ### Fixed diff --git a/src/main/java/org/jabref/gui/libraryproperties/general/GeneralPropertiesViewModel.java b/src/main/java/org/jabref/gui/libraryproperties/general/GeneralPropertiesViewModel.java index 2e28d8021fd..93edf244bd5 100644 --- a/src/main/java/org/jabref/gui/libraryproperties/general/GeneralPropertiesViewModel.java +++ b/src/main/java/org/jabref/gui/libraryproperties/general/GeneralPropertiesViewModel.java @@ -2,7 +2,9 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.Optional; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ListProperty; @@ -18,6 +20,7 @@ import org.jabref.gui.libraryproperties.PropertiesTabViewModel; import org.jabref.gui.util.DirectoryDialogConfiguration; import org.jabref.logic.l10n.Encodings; +import org.jabref.logic.l10n.Localization; import org.jabref.logic.preferences.CliPreferences; import org.jabref.logic.shared.DatabaseLocation; import org.jabref.model.database.BibDatabaseContext; @@ -40,16 +43,12 @@ public class GeneralPropertiesViewModel implements PropertiesTabViewModel { private final BibDatabaseContext databaseContext; private final MetaData metaData; - private final DirectoryDialogConfiguration directoryDialogConfiguration; GeneralPropertiesViewModel(BibDatabaseContext databaseContext, DialogService dialogService, CliPreferences preferences) { this.dialogService = dialogService; this.preferences = preferences; this.databaseContext = databaseContext; this.metaData = databaseContext.getMetaData(); - - this.directoryDialogConfiguration = new DirectoryDialogConfiguration.Builder() - .withInitialDirectory(preferences.getFilePreferences().getWorkingDirectory()).build(); } @Override @@ -96,16 +95,22 @@ public void storeSettings() { } public void browseLibrarySpecificDir() { + DirectoryDialogConfiguration directoryDialogConfiguration = new DirectoryDialogConfiguration.Builder() + .withInitialDirectory(getBrowseDirectory(librarySpecificDirectoryProperty.getValue())).build(); dialogService.showDirectorySelectionDialog(directoryDialogConfiguration) .ifPresent(dir -> librarySpecificDirectoryProperty.setValue(dir.toAbsolutePath().toString())); } public void browseUserDir() { + DirectoryDialogConfiguration directoryDialogConfiguration = new DirectoryDialogConfiguration.Builder() + .withInitialDirectory(getBrowseDirectory(userSpecificFileDirectoryProperty.getValue())).build(); dialogService.showDirectorySelectionDialog(directoryDialogConfiguration) .ifPresent(dir -> userSpecificFileDirectoryProperty.setValue(dir.toAbsolutePath().toString())); } public void browseLatexDir() { + DirectoryDialogConfiguration directoryDialogConfiguration = new DirectoryDialogConfiguration.Builder() + .withInitialDirectory(getBrowseDirectory(laTexFileDirectoryProperty.getValue())).build(); dialogService.showDirectorySelectionDialog(directoryDialogConfiguration) .ifPresent(dir -> laTexFileDirectoryProperty.setValue(dir.toAbsolutePath().toString())); } @@ -141,4 +146,19 @@ public StringProperty userSpecificFileDirectoryProperty() { public StringProperty laTexFileDirectoryProperty() { return this.laTexFileDirectoryProperty; } + + private Path getBrowseDirectory(String configuredDir) { + if (configuredDir.isEmpty()) { + return preferences.getFilePreferences().getWorkingDirectory(); + } + Optional foundPath = this.databaseContext.getFileDirectories(preferences.getFilePreferences()).stream() + .filter(path -> path.toString().endsWith(configuredDir)) + .filter(Files::exists).findFirst(); + + if (foundPath.isEmpty()) { + dialogService.notify(Localization.lang("Path %0 could not be resolved. Using working dir.", configuredDir)); + return preferences.getFilePreferences().getWorkingDirectory(); + } + return foundPath.get(); + } } diff --git a/src/main/java/org/jabref/model/database/BibDatabaseContext.java b/src/main/java/org/jabref/model/database/BibDatabaseContext.java index bf4aa5154d8..f90a7c64fbd 100644 --- a/src/main/java/org/jabref/model/database/BibDatabaseContext.java +++ b/src/main/java/org/jabref/model/database/BibDatabaseContext.java @@ -162,10 +162,10 @@ public List getFileDirectories(FilePreferences preferences) { // Paths are a) ordered and b) should be contained only once in the result LinkedHashSet fileDirs = new LinkedHashSet<>(3); - Optional userFileDirectory = metaData.getUserFileDirectory(preferences.getUserAndHost()).map(dir -> getFileDirectoryPath(dir)); + Optional userFileDirectory = metaData.getUserFileDirectory(preferences.getUserAndHost()).map(this::getFileDirectoryPath); userFileDirectory.ifPresent(fileDirs::add); - Optional librarySpecificFileDirectory = metaData.getLibrarySpecificFileDirectory().map(dir -> getFileDirectoryPath(dir)); + Optional librarySpecificFileDirectory = metaData.getLibrarySpecificFileDirectory().map(this::getFileDirectoryPath); librarySpecificFileDirectory.ifPresent(fileDirs::add); // fileDirs.isEmpty() is true after these two if there are no directories set in the BIB file itself: diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 2a4ab3bef81..cdd2cad328a 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1059,6 +1059,8 @@ Expected\ syntax\ for\ --fetch\='\:'=Expected syntax f Library-specific\ file\ directory=Library-specific file directory User-specific\ file\ directory=User-specific file directory LaTeX\ file\ directory=LaTeX file directory +Path\ %0\ could\ not\ be\ resolved.\ Using\ working\ dir.=Path %0 could not be resolved. Using working dir. + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=You must enter an integer value in the interval 1025-65535 Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Autocomplete names in 'Firstname Lastname' format only From dc9c49f34df88789e2633dc128237903ec11c581 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 20:50:37 +0000 Subject: [PATCH 09/20] Bump src/main/resources/csl-styles from `568b636` to `b90b81a` (#12231) Bumps [src/main/resources/csl-styles](https://github.com/citation-style-language/styles) from `568b636` to `b90b81a`. - [Release notes](https://github.com/citation-style-language/styles/releases) - [Commits](https://github.com/citation-style-language/styles/compare/568b63625d72c25297b498cf3f658e5691655a41...b90b81a58b1a260423608f3868a6613cc6efe431) --- updated-dependencies: - dependency-name: src/main/resources/csl-styles dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Christoph --- src/main/resources/csl-styles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles index 568b63625d7..b90b81a58b1 160000 --- a/src/main/resources/csl-styles +++ b/src/main/resources/csl-styles @@ -1 +1 @@ -Subproject commit 568b63625d72c25297b498cf3f658e5691655a41 +Subproject commit b90b81a58b1a260423608f3868a6613cc6efe431 From b45398549428a3b51f5dbc05002ea32ca21dd3ff Mon Sep 17 00:00:00 2001 From: Linus Dietz Date: Mon, 25 Nov 2024 21:10:50 +0000 Subject: [PATCH 10/20] Markdown Export Formatter (#12220) * Add Markdown export formatter * updated the changelog * fix checkstyle and l10n * fix import order * Update CHANGELOG.md Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> * Add full stop (changelog) * remove explicit save order - just for test * checkstyle * temp dir --------- Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> Co-authored-by: Subhramit Basu Bhowmick Co-authored-by: Siedlerchr --- CHANGELOG.md | 1 + .../logic/exporter/ExporterFactory.java | 1 + src/main/resources/l10n/JabRef_en.properties | 2 + .../title-markdown/title-md.article.layout | 1 + .../title-markdown/title-md.book.layout | 1 + .../title-md.incollection.layout | 1 + .../title-md.inproceedings.layout | 1 + .../layout/title-markdown/title-md.layout | 1 + .../exporter/MarkdownTitleExporterTest.java | 192 ++++++++++++++++++ 9 files changed, 201 insertions(+) create mode 100644 src/main/resources/resource/layout/title-markdown/title-md.article.layout create mode 100644 src/main/resources/resource/layout/title-markdown/title-md.book.layout create mode 100644 src/main/resources/resource/layout/title-markdown/title-md.incollection.layout create mode 100644 src/main/resources/resource/layout/title-markdown/title-md.inproceedings.layout create mode 100644 src/main/resources/resource/layout/title-markdown/title-md.layout create mode 100644 src/test/java/org/jabref/logic/exporter/MarkdownTitleExporterTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index e33e6ebd9c5..b15f90e68d6 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 a Markdown export layout. [#12220](https://github.com/JabRef/jabref/pull/12220) - We added a "view as BibTeX" option before importing an entry from the citation relation tab. [#11826](https://github.com/JabRef/jabref/issues/11826) - We added support finding LaTeX-encoded special characters based on plain Unicode and vice versa. [#11542](https://github.com/JabRef/jabref/pull/11542) - When a search hits a file, the file icon of that entry is changed accordingly. [#11542](https://github.com/JabRef/jabref/pull/11542) diff --git a/src/main/java/org/jabref/logic/exporter/ExporterFactory.java b/src/main/java/org/jabref/logic/exporter/ExporterFactory.java index 28a89ebfe7c..bcd6de20bef 100644 --- a/src/main/java/org/jabref/logic/exporter/ExporterFactory.java +++ b/src/main/java/org/jabref/logic/exporter/ExporterFactory.java @@ -43,6 +43,7 @@ public static ExporterFactory create(CliPreferences preferences) { exporters.add(new TemplateExporter(Localization.lang("HTML table"), "tablerefs", "tablerefs", "tablerefs", StandardFileType.HTML, layoutPreferences, saveOrder)); exporters.add(new TemplateExporter(Localization.lang("HTML list"), "listrefs", "listrefs", "listrefs", StandardFileType.HTML, layoutPreferences, saveOrder)); exporters.add(new TemplateExporter(Localization.lang("HTML table (with Abstract & BibTeX)"), "tablerefsabsbib", "tablerefsabsbib", "tablerefsabsbib", StandardFileType.HTML, layoutPreferences, saveOrder)); + exporters.add(new TemplateExporter(Localization.lang("Markdown titles"), "title-md", "title-md", "title-markdown", StandardFileType.MARKDOWN, layoutPreferences, saveOrder)); exporters.add(new TemplateExporter("Harvard RTF", "harvard", "harvard", "harvard", StandardFileType.RTF, layoutPreferences, saveOrder)); exporters.add(new TemplateExporter("ISO 690 RTF", "iso690rtf", "iso690RTF", "iso690rtf", StandardFileType.RTF, layoutPreferences, saveOrder)); exporters.add(new TemplateExporter("ISO 690", "iso690txt", "iso690", "iso690txt", StandardFileType.TXT, layoutPreferences, saveOrder)); diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index cdd2cad328a..cadce1ba583 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -466,6 +466,8 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text!=The marked area does HTML\ table=HTML table HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTML table (with Abstract & BibTeX) +Markdown\ titles=Markdown titles + Icon=Icon Ignore=Ignore diff --git a/src/main/resources/resource/layout/title-markdown/title-md.article.layout b/src/main/resources/resource/layout/title-markdown/title-md.article.layout new file mode 100644 index 00000000000..bd491638613 --- /dev/null +++ b/src/main/resources/resource/layout/title-markdown/title-md.article.layout @@ -0,0 +1 @@ +* \format[RemoveLatexCommands,HTMLChars]{\title}. \begin{journal}\format[RemoveLatexCommands,HTMLChars]{\journal}\end{journal}\begin{year} \format{\year}\end{year} diff --git a/src/main/resources/resource/layout/title-markdown/title-md.book.layout b/src/main/resources/resource/layout/title-markdown/title-md.book.layout new file mode 100644 index 00000000000..c136fa5fa3d --- /dev/null +++ b/src/main/resources/resource/layout/title-markdown/title-md.book.layout @@ -0,0 +1 @@ +* \format[RemoveLatexCommands,HTMLChars]{\title}.\begin{publisher} \format[RemoveLatexCommands,HTMLChars]{\publisher}\end{publisher} \format{\year}\end{year} diff --git a/src/main/resources/resource/layout/title-markdown/title-md.incollection.layout b/src/main/resources/resource/layout/title-markdown/title-md.incollection.layout new file mode 100644 index 00000000000..66a1528b0bc --- /dev/null +++ b/src/main/resources/resource/layout/title-markdown/title-md.incollection.layout @@ -0,0 +1 @@ +* \format[RemoveLatexCommands,HTMLChars]{\title}. \begin{booktitle}\format[RemoveLatexCommands,HTMLChars]{\booktitle}\end{booktitle}\begin{publisher}, \format[RemoveLatexCommands,HTMLChars]{\publisher}\end{publisher} \format{\year} diff --git a/src/main/resources/resource/layout/title-markdown/title-md.inproceedings.layout b/src/main/resources/resource/layout/title-markdown/title-md.inproceedings.layout new file mode 100644 index 00000000000..1d16d1eb94f --- /dev/null +++ b/src/main/resources/resource/layout/title-markdown/title-md.inproceedings.layout @@ -0,0 +1 @@ +* \format[RemoveLatexCommands,HTMLChars]{\title}. \begin{publisher}\format[RemoveLatexCommands,HTMLChars]{\publisher} \end{publisher}\begin{series}\format[RemoveLatexCommands,HTMLChars]{\series}\end{series}\begin{!series}\format[RemoveLatexCommands,HTMLChars]{\booktitle} \format{\year}\end{!series} diff --git a/src/main/resources/resource/layout/title-markdown/title-md.layout b/src/main/resources/resource/layout/title-markdown/title-md.layout new file mode 100644 index 00000000000..4fa6ad3e417 --- /dev/null +++ b/src/main/resources/resource/layout/title-markdown/title-md.layout @@ -0,0 +1 @@ +* \format[RemoveLatexCommands,HTMLChars]{\title}.\begin{year} \year\end{year} diff --git a/src/test/java/org/jabref/logic/exporter/MarkdownTitleExporterTest.java b/src/test/java/org/jabref/logic/exporter/MarkdownTitleExporterTest.java new file mode 100644 index 00000000000..19ae753ef15 --- /dev/null +++ b/src/test/java/org/jabref/logic/exporter/MarkdownTitleExporterTest.java @@ -0,0 +1,192 @@ +package org.jabref.logic.exporter; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + +import org.jabref.logic.layout.LayoutFormatterPreferences; +import org.jabref.logic.util.StandardFileType; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.types.StandardEntryType; +import org.jabref.model.metadata.SaveOrder; +import org.jabref.model.metadata.SelfContainedSaveOrder; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Answers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; + +class MarkdownTitleExporterTest { + + private static Exporter htmlWebsiteExporter; + private static BibDatabaseContext databaseContext; + private static final SelfContainedSaveOrder SAVE_MOST_RECENT_FIRST_SAVE_ORDER = new SelfContainedSaveOrder(SaveOrder.OrderType.SPECIFIED, List.of(new SaveOrder.SortCriterion(StandardField.YEAR, true))); + + @BeforeAll + static void setUp() { + htmlWebsiteExporter = new TemplateExporter( + "Title-Markdown", + "title-md", + "title-md", + "title-markdown", + StandardFileType.MARKDOWN, + mock(LayoutFormatterPreferences.class, Answers.RETURNS_DEEP_STUBS), + SAVE_MOST_RECENT_FIRST_SAVE_ORDER, + BlankLineBehaviour.DELETE_BLANKS); + + databaseContext = new BibDatabaseContext(); + } + + @Test + final void exportForNoEntriesWritesNothing(@TempDir Path tempDir) throws Exception { + Path file = tempDir.resolve("ThisIsARandomlyNamedFile"); + Files.createFile(file); + htmlWebsiteExporter.export(databaseContext, tempDir, Collections.emptyList()); + assertEquals(Collections.emptyList(), Files.readAllLines(file)); + } + + @Test + final void exportsCorrectContentArticle(@TempDir Path tempDir) throws Exception { + BibEntry entry = new BibEntry(StandardEntryType.Article) + .withCitationKey("test") + .withField(StandardField.AUTHOR, "Test Author") + .withField(StandardField.TITLE, "Test Title") + .withField(StandardField.JOURNAL, "Journal of this \\& that") + .withField(StandardField.PUBLISHER, "THE PRESS") + .withField(StandardField.YEAR, "2020"); + + Path file = tempDir.resolve("RandomFileName"); + Files.createFile(file); + htmlWebsiteExporter.export(databaseContext, file, Collections.singletonList(entry)); + + List expected = List.of( + "* Test Title. Journal of this & that 2020"); + + assertEquals(expected, Files.readAllLines(file)); + } + + @Test + final void exportsCorrectContentInCollection(@TempDir Path tempDir) throws Exception { + BibEntry entry = new BibEntry(StandardEntryType.InCollection) + .withCitationKey("test") + .withField(StandardField.AUTHOR, "Test Author") + .withField(StandardField.TITLE, "Test Title") + .withField(StandardField.BOOKTITLE, "Test book") + .withField(StandardField.PUBLISHER, "PRESS") + .withField(StandardField.YEAR, "2020"); + + Path file = tempDir.resolve("RandomFileName"); + Files.createFile(file); + htmlWebsiteExporter.export(databaseContext, file, Collections.singletonList(entry)); + + List expected = List.of( + "* Test Title. Test book, PRESS 2020"); + + assertEquals(expected, Files.readAllLines(file)); + } + + @Test + final void exportsCorrectContentBook(@TempDir Path tempDir) throws Exception { + BibEntry entry = new BibEntry(StandardEntryType.Book) + .withCitationKey("test") + .withField(StandardField.AUTHOR, "Test Author") + .withField(StandardField.TITLE, "Test Title") + .withField(StandardField.BOOKTITLE, "Test book") + .withField(StandardField.PUBLISHER, "PRESS") + .withField(StandardField.YEAR, "2020"); + + Path file = tempDir.resolve("RandomFileName"); + Files.createFile(file); + htmlWebsiteExporter.export(databaseContext, file, Collections.singletonList(entry)); + + List expected = List.of( + "* Test Title. PRESS 2020"); + + assertEquals(expected, Files.readAllLines(file)); + } + + @Test + final void exportsCorrectContentInProceeedingsPublisher(@TempDir Path tempDir) throws Exception { + BibEntry entry = new BibEntry(StandardEntryType.InProceedings) + .withCitationKey("test") + .withField(StandardField.AUTHOR, "Test Author") + .withField(StandardField.TITLE, "Test Title") + .withField(StandardField.BOOKTITLE, "Test Conference") + .withField(StandardField.PUBLISHER, "ACM") + .withField(StandardField.SERIES, "CONF'20") + .withField(StandardField.YEAR, "2020"); + + Path file = tempDir.resolve("RandomFileName"); + Files.createFile(file); + htmlWebsiteExporter.export(databaseContext, file, Collections.singletonList(entry)); + + List expected = List.of( + "* Test Title. ACM CONF'20"); + + assertEquals(expected, Files.readAllLines(file)); + } + + @Test + final void exportsCorrectContentInProceeedingsNoPublisher(@TempDir Path tempDir) throws Exception { + BibEntry entry = new BibEntry(StandardEntryType.InProceedings) + .withCitationKey("test") + .withField(StandardField.AUTHOR, "Test Author") + .withField(StandardField.TITLE, "Test Title") + .withField(StandardField.BOOKTITLE, "Test Conference") + .withField(StandardField.SERIES, "CONF'20") + .withField(StandardField.YEAR, "2020"); + + Path file = tempDir.resolve("RandomFileName"); + Files.createFile(file); + htmlWebsiteExporter.export(databaseContext, file, Collections.singletonList(entry)); + + List expected = List.of( + "* Test Title. CONF'20"); + + assertEquals(expected, Files.readAllLines(file)); + } + + @Test + final void exportsCorrectContentInProceeedingsNoSeries(@TempDir Path tempDir) throws Exception { + BibEntry entry = new BibEntry(StandardEntryType.InProceedings) + .withCitationKey("test") + .withField(StandardField.AUTHOR, "Test Author") + .withField(StandardField.TITLE, "Test Title") + .withField(StandardField.BOOKTITLE, "Test Conference") + .withField(StandardField.YEAR, "2020"); + + Path file = tempDir.resolve("RandomFileName"); + Files.createFile(file); + htmlWebsiteExporter.export(databaseContext, file, Collections.singletonList(entry)); + + List expected = List.of( + "* Test Title. Test Conference 2020"); + + assertEquals(expected, Files.readAllLines(file)); + } + + @Test + final void exportsCorrectContentBracketsInTitle(@TempDir Path tempDir) throws Exception { + BibEntry entry = new BibEntry(StandardEntryType.Article) + .withCitationKey("test") + .withField(StandardField.AUTHOR, "Test Author") + .withField(StandardField.TITLE, "This is {JabRef}") + .withField(StandardField.JOURNAL, "Journal of this \\& that") + .withField(StandardField.YEAR, "2020"); + + Path file = tempDir.resolve("RandomFileName"); + Files.createFile(file); + htmlWebsiteExporter.export(databaseContext, file, Collections.singletonList(entry)); + + List expected = List.of( + "* This is JabRef. Journal of this & that 2020"); + + assertEquals(expected, Files.readAllLines(file)); + } +} From 861a6ce7062e70a6d16e3530e3f9b2523cf549ff Mon Sep 17 00:00:00 2001 From: Hitalo Siriano Date: Mon, 25 Nov 2024 18:23:42 -0300 Subject: [PATCH 11/20] Sanitize URLs in file fields to handle invalid pipe characters ('|') (#12156) * Sanitize URLs in file fields to handle invalid pipe characters ('|') Closes #11876. - Introduced URLUtil.createUri() and URLUtil.create() to handle URL sanitization. - Replaced direct calls to URI.create() and URI.create().toURL() with the new utility methods. - URLs containing the pipe character ('|') are now properly encoded as '%7C' to prevent parsing errors. - Added test cases to URLUtilTest to verify correct sanitization and URL creation. * Sanitize URLs in file fields to handle invalid pipe characters ('|') Closes #11876. - Introduced URLUtil.createUri() and URLUtil.create() to handle URL sanitization. - Replaced direct calls to URI.create() and URI.create().toURL() with the new utility methods. - URLs containing the pipe character ('|') are now properly encoded as '%7C' to prevent parsing errors. - Added test cases to URLUtilTest to verify correct sanitization and URL creation. - Added @ArchTest to ensure that the URI.create() method is not directly called in the codebase. * Discard changes to src/main/java/module-info.java * Sanitize URLs in file fields to handle invalid pipe characters ('|') Closes #11876. - Introduced URLUtil.createUri() and URLUtil.create() to handle URL sanitization. - Replaced direct calls to URI.create() and URI.create().toURL() with the new utility methods. - URLs containing the pipe character ('|') are now properly encoded as '%7C' to prevent parsing errors. - Added test cases to URLUtilTest to verify correct sanitization and URL creation. - Added @ArchTest to ensure that the URI.create() method is not directly called in the codebase. * Sanitize URLs in file fields to handle invalid pipe characters ('|') Closes #11876. - Introduced URLUtil.createUri() and URLUtil.create() to handle URL sanitization. - Replaced direct calls to URI.create() and URI.create().toURL() with the new utility methods. - URLs containing the pipe character ('|') are now properly encoded as '%7C' to prevent parsing errors. - Added test cases to URLUtilTest to verify correct sanitization and URL creation. - Added @ArchTest to ensure that the URI.create() method is not directly called in the codebase. * Refine comment * Fix checkstyle * Fix import * Update Openrewrite * Move URLUtil to logic (where possible) * Fix architecture test * Fix FQN * Compilefix * Add Arch exception * Fix imports * Add CHANGELOG.md entry --------- Co-authored-by: Oliver Kopp --- CHANGELOG.md | 1 + build.gradle | 4 +- .../SemanticScholarFetcher.java | 6 +- .../LinkedFilesEditorViewModel.java | 4 +- .../org/jabref/gui/fieldeditors/URLUtil.java | 79 +----------- .../gui/fieldeditors/UrlEditorViewModel.java | 1 + .../linkedfile/AttachFileFromURLAction.java | 4 +- .../LinkedFileEditDialogViewModel.java | 4 +- .../newmergedialog/cell/FieldValueCell.java | 2 +- .../org/jabref/gui/theme/StyleSheetFile.java | 5 +- .../logic/ai/chatting/model/Gpt4AllModel.java | 4 +- .../jabref/logic/importer/fetcher/ACS.java | 4 +- .../logic/importer/fetcher/ApsFetcher.java | 6 +- .../logic/importer/fetcher/ArXivFetcher.java | 4 +- .../importer/fetcher/BibsonomyScraper.java | 4 +- .../logic/importer/fetcher/CiteSeer.java | 6 +- .../logic/importer/fetcher/DoiFetcher.java | 6 +- .../logic/importer/fetcher/DoiResolution.java | 11 +- .../logic/importer/fetcher/GoogleScholar.java | 4 +- .../jabref/logic/importer/fetcher/IEEE.java | 4 +- .../importer/fetcher/IacrEprintFetcher.java | 12 +- .../logic/importer/fetcher/JstorFetcher.java | 8 +- .../jabref/logic/importer/fetcher/Medra.java | 4 +- .../logic/importer/fetcher/OpenAccessDoi.java | 4 +- .../logic/importer/fetcher/ResearchGate.java | 6 +- .../logic/importer/fetcher/ScienceDirect.java | 8 +- .../importer/fetcher/SemanticScholar.java | 4 +- .../importer/fetcher/SpringerFetcher.java | 4 +- .../importer/fileformat/MarcXmlParser.java | 4 +- .../logic/importer/util/FileFieldParser.java | 6 +- .../jabref/logic/layout/format/DOICheck.java | 3 +- .../org/jabref/logic/net/URLDownload.java | 6 +- .../java/org/jabref/logic/util/URLUtil.java | 115 ++++++++++++++++++ .../java/org/jabref/logic/util/Version.java | 3 +- .../jabref/model/entry/identifier/ARK.java | 6 +- .../jabref/model/entry/identifier/DOI.java | 7 +- .../model/entry/identifier/IacrEprint.java | 5 +- .../jabref/model/entry/identifier/SSRN.java | 6 +- .../architecture/MainArchitectureTest.java | 11 +- .../fieldeditors/LinkedFileViewModelTest.java | 6 +- .../DownloadLinkedFileActionTest.java | 10 +- .../org/jabref/logic/help/HelpFileTest.java | 4 +- .../logic/importer/FulltextFetchersTest.java | 12 +- .../logic/importer/fetcher/ACSTest.java | 4 +- .../importer/fetcher/ApsFetcherTest.java | 6 +- .../importer/fetcher/ArXivFetcherTest.java | 22 ++-- .../logic/importer/fetcher/CiteSeerTest.java | 6 +- .../importer/fetcher/DoiResolutionTest.java | 10 +- .../importer/fetcher/GoogleScholarTest.java | 4 +- .../logic/importer/fetcher/IEEETest.java | 12 +- .../importer/fetcher/JstorFetcherTest.java | 4 +- .../importer/fetcher/OpenAccessDoiTest.java | 4 +- .../importer/fetcher/ResearchGateTest.java | 4 +- .../importer/fetcher/ScienceDirectTest.java | 8 +- .../importer/fetcher/SpringerLinkTest.java | 4 +- .../importer/util/FileFieldParserTest.java | 12 +- .../org/jabref/logic/net/URLDownloadTest.java | 32 ++--- .../org/jabref/logic/net/URLUtilTest.java | 11 +- .../logic/util/ExternalLinkCreatorTest.java | 6 +- .../org/jabref/model/entry/BibEntryTest.java | 4 +- .../model/entry/identifier/SSRNTest.java | 4 +- 61 files changed, 328 insertions(+), 246 deletions(-) create mode 100644 src/main/java/org/jabref/logic/util/URLUtil.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b15f90e68d6..a8227aece62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where the "Do not ask again" checkbox was not working, when asking for permission to use Grobid [koppor#556](https://github.com/koppor/jabref/issues/566). - We fixed an issue where we display warning message for moving attached open files. [#10121](https://github.com/JabRef/jabref/issues/10121) - We fixed an issue where it was not possible to select selecting content of other user's comments.[#11106](https://github.com/JabRef/jabref/issues/11106) +- We fixed an issue when handling URLs containing a pipe (`|`) character. [#11876](https://github.com/JabRef/jabref/issues/11876) - We fixed an issue where web search preferences "Custom API key" table modifications not discarded. [#11925](https://github.com/JabRef/jabref/issues/11925) - We fixed an issue when opening attached files in [extra file columns](https://docs.jabref.org/finding-sorting-and-cleaning-entries/filelinks#adding-additional-columns-to-entry-table-for-file-types). [#12005](https://github.com/JabRef/jabref/issues/12005) - We fixed an issue where trying to open a library from a failed mounted directory on Mac would cause an error. [#10548](https://github.com/JabRef/jabref/issues/10548) diff --git a/build.gradle b/build.gradle index 03dbafbff48..6d798bb3bf3 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ plugins { id 'idea' - id 'org.openrewrite.rewrite' version '6.27.0' + id 'org.openrewrite.rewrite' version '6.27.2' id "org.itsallcode.openfasttrace" version "3.0.1" } @@ -400,7 +400,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.19.0")) + rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.22.0")) rewrite("org.openrewrite.recipe:rewrite-static-analysis") rewrite("org.openrewrite.recipe:rewrite-logging-frameworks") rewrite("org.openrewrite.recipe:rewrite-testing-frameworks") diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java index 3267ed44f31..c085fbc50bd 100644 --- a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java @@ -1,7 +1,6 @@ package org.jabref.gui.entryeditor.citationrelationtab.semanticscholar; import java.net.MalformedURLException; -import java.net.URI; import java.net.URL; import java.util.List; @@ -9,6 +8,7 @@ import org.jabref.logic.importer.ImporterPreferences; import org.jabref.logic.importer.fetcher.CustomizableKeyFetcher; import org.jabref.logic.net.URLDownload; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import com.google.gson.Gson; @@ -38,7 +38,7 @@ public List searchCitedBy(BibEntry entry) throws FetcherException { URL citationsUrl; try { - citationsUrl = URI.create(getAPIUrl("citations", entry)).toURL(); + citationsUrl = URLUtil.create(getAPIUrl("citations", entry)); } catch (MalformedURLException e) { throw new FetcherException("Malformed URL", e); } @@ -62,7 +62,7 @@ public List searchCiting(BibEntry entry) throws FetcherException { URL referencesUrl; try { - referencesUrl = URI.create(getAPIUrl("references", entry)).toURL(); + referencesUrl = URLUtil.create(getAPIUrl("references", entry)); } catch (MalformedURLException e) { throw new FetcherException("Malformed URL", e); } diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java index 4cd947b2287..b5c488750ad 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; import java.net.URL; import java.nio.file.Path; import java.util.ArrayList; @@ -38,6 +37,7 @@ import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.BackgroundTask; import org.jabref.logic.util.TaskExecutor; +import org.jabref.logic.util.URLUtil; import org.jabref.logic.util.io.FileUtil; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; @@ -186,7 +186,7 @@ private List findAssociatedNotLinkedFiles(BibEntry entry) { public boolean downloadFile(String urlText) { try { - URL url = URI.create(urlText).toURL(); + URL url = URLUtil.create(urlText); addFromURLAndDownload(url); return true; } catch (MalformedURLException exception) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java b/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java index c1a54103ac6..0278036032e 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java +++ b/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java @@ -1,89 +1,22 @@ package org.jabref.gui.fieldeditors; import java.net.MalformedURLException; -import java.net.URI; import java.net.URL; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.Objects; import java.util.Optional; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.frame.ExternalApplicationsPreferences; +/** + * URL utilities for URLs in the JabRef GUI. + *

+ * For logic-oriented URL utilities see {@link org.jabref.logic.util.URLUtil}. + */ public class URLUtil { - private static final String URL_EXP = "^(https?|ftp)://.+"; - - // Detect Google search URL - private static final String GOOGLE_SEARCH_EXP = "^https?://(?:www\\.)?google\\.[\\.a-z]+?/url.*"; private URLUtil() { } - /** - * Cleans URLs returned by Google search. - * - * - * If you copy links from search results from Google, all links will be enriched with search meta data, e.g. - * https://www.google.de/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&&url=http%3A%2F%2Fwww.inrg.csie.ntu.edu.tw%2Falgorithm2014%2Fhomework%2FWagner-74.pdf&ei=DifeVYHkDYWqU5W0j6gD&usg=AFQjCNFl638rl5KVta1jIMWLyb4CPSZidg&sig2=0hSSMw9XZXL3HJWwEcJtOg - * - * - * @param url the Google search URL string - * @return the cleaned Google URL or @code{url} if no search URL was detected - */ - public static String cleanGoogleSearchURL(String url) { - Objects.requireNonNull(url); - - if (!url.matches(GOOGLE_SEARCH_EXP)) { - return url; - } - // Extract destination URL - try { - URL searchURL = URI.create(url).toURL(); - // URL parameters - String query = searchURL.getQuery(); - // no parameters - if (query == null) { - return url; - } - // extract url parameter - String[] pairs = query.split("&"); - - for (String pair : pairs) { - // "clean" url is decoded value of "url" parameter - if (pair.startsWith("url=")) { - String value = pair.substring(pair.indexOf('=') + 1); - - String decode = URLDecoder.decode(value, StandardCharsets.UTF_8); - // url? - if (decode.matches(URL_EXP)) { - return decode; - } - } - } - return url; - } catch (MalformedURLException e) { - return url; - } - } - - /** - * Checks whether the given String is a URL. - *

- * Currently only checks for a protocol String. - * - * @param url the String to check for a URL - * @return true if url contains a valid URL - */ - public static boolean isURL(String url) { - try { - URI.create(url).toURL(); - return true; - } catch (MalformedURLException | IllegalArgumentException e) { - return false; - } - } - /** * Look for the last '.' in the link, and return the following characters. *

@@ -96,7 +29,7 @@ public static Optional getSuffix(final String link, ExternalApplications String strippedLink = link; try { // Try to strip the query string, if any, to get the correct suffix: - URL url = URI.create(link).toURL(); + URL url = org.jabref.logic.util.URLUtil.create(link); if ((url.getQuery() != null) && (url.getQuery().length() < (link.length() - 1))) { strippedLink = link.substring(0, link.length() - url.getQuery().length() - 1); } diff --git a/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java index dceea6a5265..85f05a5c164 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java @@ -13,6 +13,7 @@ import org.jabref.gui.preferences.GuiPreferences; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.logic.l10n.Localization; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.field.Field; import org.jabref.model.strings.StringUtil; diff --git a/src/main/java/org/jabref/gui/linkedfile/AttachFileFromURLAction.java b/src/main/java/org/jabref/gui/linkedfile/AttachFileFromURLAction.java index 61ec0f65ed4..8ac056f8119 100644 --- a/src/main/java/org/jabref/gui/linkedfile/AttachFileFromURLAction.java +++ b/src/main/java/org/jabref/gui/linkedfile/AttachFileFromURLAction.java @@ -1,7 +1,6 @@ package org.jabref.gui.linkedfile; import java.net.MalformedURLException; -import java.net.URI; import java.net.URL; import java.util.Optional; @@ -14,6 +13,7 @@ import org.jabref.gui.preferences.GuiPreferences; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.TaskExecutor; +import org.jabref.logic.util.URLUtil; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; @@ -61,7 +61,7 @@ public void execute() { } try { - URL url = URI.create(urlforDownload.get()).toURL(); + URL url = URLUtil.create(urlforDownload.get()); LinkedFileViewModel onlineFile = new LinkedFileViewModel( new LinkedFile(url, ""), entry, diff --git a/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogViewModel.java b/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogViewModel.java index 82863bc6c5b..954c53a84b3 100644 --- a/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogViewModel.java +++ b/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogViewModel.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.util.Optional; @@ -25,6 +24,7 @@ import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.logic.FilePreferences; import org.jabref.logic.l10n.Localization; +import org.jabref.logic.util.URLUtil; import org.jabref.logic.util.io.FileNameCleaner; import org.jabref.logic.util.io.FileUtil; import org.jabref.model.database.BibDatabaseContext; @@ -166,7 +166,7 @@ public LinkedFile getNewLinkedFile() { if (LinkedFile.isOnlineLink(link.getValue())) { try { - return new LinkedFile(description.getValue(), URI.create(link.getValue()).toURL(), fileType, sourceUrl.getValue()); + return new LinkedFile(description.getValue(), URLUtil.create(link.getValue()), fileType, sourceUrl.getValue()); } catch (MalformedURLException e) { return new LinkedFile(description.getValue(), link.getValue(), fileType, sourceUrl.getValue()); } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index d93b6d4f45f..68d90e0e2ea 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -20,10 +20,10 @@ import javafx.scene.paint.Color; import org.jabref.gui.actions.ActionFactory; -import org.jabref.gui.fieldeditors.URLUtil; import org.jabref.gui.icon.IconTheme; import org.jabref.gui.preferences.GuiPreferences; import org.jabref.logic.l10n.Localization; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.identifier.DOI; import org.jabref.model.strings.StringUtil; diff --git a/src/main/java/org/jabref/gui/theme/StyleSheetFile.java b/src/main/java/org/jabref/gui/theme/StyleSheetFile.java index 9401e51b18b..515f37854ed 100644 --- a/src/main/java/org/jabref/gui/theme/StyleSheetFile.java +++ b/src/main/java/org/jabref/gui/theme/StyleSheetFile.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.io.InputStream; -import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.nio.file.Files; @@ -11,6 +10,8 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; +import org.jabref.logic.util.URLUtil; + import com.google.common.base.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,7 +55,7 @@ final class StyleSheetFile extends StyleSheet { StyleSheetFile(URL url) { this.url = url; - this.path = Path.of(URI.create(url.toExternalForm())); + this.path = Path.of(URLUtil.createUri(url.toExternalForm())); reload(); } diff --git a/src/main/java/org/jabref/logic/ai/chatting/model/Gpt4AllModel.java b/src/main/java/org/jabref/logic/ai/chatting/model/Gpt4AllModel.java index 20c26951f69..d83a31210a5 100644 --- a/src/main/java/org/jabref/logic/ai/chatting/model/Gpt4AllModel.java +++ b/src/main/java/org/jabref/logic/ai/chatting/model/Gpt4AllModel.java @@ -1,6 +1,5 @@ package org.jabref.logic.ai.chatting.model; -import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; @@ -9,6 +8,7 @@ import java.util.stream.Collectors; import org.jabref.logic.ai.AiPreferences; +import org.jabref.logic.util.URLUtil; import com.google.gson.Gson; import dev.langchain4j.data.message.AiMessage; @@ -62,7 +62,7 @@ public Response generate(List list) { String baseUrl = aiPreferences.getSelectedApiBaseUrl(); String fullUrl = baseUrl.endsWith("/") ? baseUrl + "chat/completions" : baseUrl + "/chat/completions"; HttpRequest httpRequest = HttpRequest.newBuilder() - .uri(URI.create(fullUrl)) + .uri(URLUtil.createUri(fullUrl)) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(requestBody)) .timeout(Duration.ofSeconds(60)) diff --git a/src/main/java/org/jabref/logic/importer/fetcher/ACS.java b/src/main/java/org/jabref/logic/importer/fetcher/ACS.java index 964ad8f38f3..e9f446578b6 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/ACS.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/ACS.java @@ -1,12 +1,12 @@ package org.jabref.logic.importer.fetcher; import java.io.IOException; -import java.net.URI; import java.net.URL; import java.util.Objects; import java.util.Optional; import org.jabref.logic.importer.FulltextFetcher; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.DOI; @@ -45,7 +45,7 @@ public Optional findFullText(BibEntry entry) throws IOException { if (link != null) { LOGGER.info("Fulltext PDF found @ ACS."); - return Optional.of(URI.create(source.replaceFirst("/abs/", "/pdf/")).toURL()); + return Optional.of(URLUtil.create(source.replaceFirst("/abs/", "/pdf/"))); } return Optional.empty(); } diff --git a/src/main/java/org/jabref/logic/importer/fetcher/ApsFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/ApsFetcher.java index 64ca0c70c1b..527dac0bd16 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/ApsFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/ApsFetcher.java @@ -2,13 +2,13 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.util.Objects; import java.util.Optional; import org.jabref.logic.importer.FulltextFetcher; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.DOI; @@ -48,7 +48,7 @@ public Optional findFullText(BibEntry entry) throws IOException { if (code == 200) { LOGGER.info("Fulltext PDF found @ APS."); try { - return Optional.of(URI.create(pdfRequestUrl).toURL()); + return Optional.of(URLUtil.create(pdfRequestUrl)); } catch (MalformedURLException e) { LOGGER.warn("APS returned malformed URL, cannot find PDF."); } @@ -77,7 +77,7 @@ private Optional getId(String doi) { URLConnection con; try { - con = URI.create(doiRequest).toURL().openConnection(); + con = URLUtil.create(doiRequest).openConnection(); con.connect(); con.getInputStream(); String[] urlParts = con.getURL().toString().split("abstract/"); diff --git a/src/main/java/org/jabref/logic/importer/fetcher/ArXivFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/ArXivFetcher.java index e7b72137944..8ab2a590915 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/ArXivFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/ArXivFetcher.java @@ -3,7 +3,6 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; @@ -34,6 +33,7 @@ import org.jabref.logic.importer.PagedSearchBasedFetcher; import org.jabref.logic.importer.fetcher.transformers.ArXivQueryTransformer; import org.jabref.logic.integrity.BracesCorrector; +import org.jabref.logic.util.URLUtil; import org.jabref.logic.util.io.XMLUtil; import org.jabref.logic.util.strings.StringSimilarity; import org.jabref.model.entry.BibEntry; @@ -710,7 +710,7 @@ public ArXivEntry(Node item) { if (linkTitle.equals(Optional.of("pdf"))) { pdfUrlParsed = XMLUtil.getAttributeContent(linkNode, "href").map(url -> { try { - return URI.create(url).toURL(); + return URLUtil.create(url); } catch (MalformedURLException e) { return null; } diff --git a/src/main/java/org/jabref/logic/importer/fetcher/BibsonomyScraper.java b/src/main/java/org/jabref/logic/importer/fetcher/BibsonomyScraper.java index 6f057c34480..3661ea22f80 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/BibsonomyScraper.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/BibsonomyScraper.java @@ -1,7 +1,6 @@ package org.jabref.logic.importer.fetcher; import java.io.IOException; -import java.net.URI; import java.net.URL; import java.util.Optional; @@ -10,6 +9,7 @@ import org.jabref.logic.importer.ParseException; import org.jabref.logic.importer.fileformat.BibtexParser; import org.jabref.logic.net.URLDownload; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.slf4j.Logger; @@ -37,7 +37,7 @@ public static Optional getEntry(String entryUrl, ImportFormatPreferenc String cleanURL = entryUrl.replace("%", "%25").replace(":", "%3A").replace("/", "%2F").replace("?", "%3F") .replace("&", "%26").replace("=", "%3D"); - URL url = URI.create(BibsonomyScraper.BIBSONOMY_SCRAPER + cleanURL + BibsonomyScraper.BIBSONOMY_SCRAPER_POST).toURL(); + URL url = URLUtil.create(BibsonomyScraper.BIBSONOMY_SCRAPER + cleanURL + BibsonomyScraper.BIBSONOMY_SCRAPER_POST); String bibtex = new URLDownload(url).asString(); return BibtexParser.singleFromString(bibtex, importFormatPreferences); } catch (IOException | FetcherException ex) { diff --git a/src/main/java/org/jabref/logic/importer/fetcher/CiteSeer.java b/src/main/java/org/jabref/logic/importer/fetcher/CiteSeer.java index c18794653b7..a7e066cc1f8 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/CiteSeer.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/CiteSeer.java @@ -1,7 +1,6 @@ package org.jabref.logic.importer.fetcher; import java.io.IOException; -import java.net.URI; import java.net.URL; import java.util.List; import java.util.Objects; @@ -15,6 +14,7 @@ import org.jabref.logic.importer.SearchBasedFetcher; import org.jabref.logic.importer.fetcher.transformers.CiteSeerQueryTransformer; import org.jabref.logic.importer.fileformat.CiteSeerParser; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; @@ -103,13 +103,13 @@ public Optional findFullText(BibEntry entry) throws IOException, FetcherExc Optional id = entry.getField(StandardField.DOI); if (id.isPresent()) { String source = PDF_URL.formatted(id.get()); - return Optional.of(URI.create(source).toURL()); + return Optional.of(URLUtil.create(source)); } // if using id fails, we can try the source URL Optional urlString = entry.getField(StandardField.URL); if (urlString.isPresent()) { - return Optional.of(URI.create(urlString.get()).toURL()); + return Optional.of(URLUtil.create(urlString.get())); } return Optional.empty(); diff --git a/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java index 19f01d025a0..2aa9305a869 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java @@ -3,7 +3,6 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; -import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.util.Collections; @@ -27,6 +26,7 @@ import org.jabref.logic.importer.util.MediaTypes; import org.jabref.logic.l10n.Localization; import org.jabref.logic.net.URLDownload; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.DOI; @@ -122,7 +122,7 @@ public Optional performSearchById(String identifier) throws FetcherExc URL doiURL; try { - doiURL = URI.create(doi.get().getURIAsASCIIString()).toURL(); + doiURL = URLUtil.create(doi.get().getURIAsASCIIString()); } catch (MalformedURLException e) { throw new FetcherException("Malformed URL", e); } @@ -219,7 +219,7 @@ public List performSearch(BibEntry entry) throws FetcherException { public Optional getAgency(DOI doi) throws FetcherException, MalformedURLException { Optional agency = Optional.empty(); try { - URLDownload download = getUrlDownload(URI.create(DOI.AGENCY_RESOLVER + "/" + doi.asString()).toURL()); + URLDownload download = getUrlDownload(URLUtil.create(DOI.AGENCY_RESOLVER + "/" + doi.asString())); JSONObject response = new JSONArray(download.asString()).getJSONObject(0); if (response != null) { agency = Optional.ofNullable(response.optString("RA")); diff --git a/src/main/java/org/jabref/logic/importer/fetcher/DoiResolution.java b/src/main/java/org/jabref/logic/importer/fetcher/DoiResolution.java index 81402a3e499..b66b44ca119 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/DoiResolution.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/DoiResolution.java @@ -18,6 +18,7 @@ import org.jabref.logic.importer.WebFetchers; import org.jabref.logic.net.URLDownload; import org.jabref.logic.preferences.DOIPreferences; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.DOI; @@ -61,7 +62,7 @@ public Optional findFullText(BibEntry entry) throws IOException { String doiLink; if (doiPreferences.isUseCustom()) { - base = URI.create(doiPreferences.getDefaultBaseURI()).toURL(); + base = URLUtil.create(doiPreferences.getDefaultBaseURI()); doiLink = doi.get() .getExternalURIWithCustomBase(base.toString()) .map(URI::toASCIIString) @@ -110,11 +111,11 @@ public Optional findFullText(BibEntry entry) throws IOException { // See https://github.com/lehner/LocalCopy for more scrape ideas // link with "PDF" in title tag if (element.attr("title").toLowerCase(Locale.ENGLISH).contains("pdf") && new URLDownload(href).isPdf()) { - return Optional.of(URI.create(href).toURL()); + return Optional.of(URLUtil.create(href)); } if (href.contains("pdf") || hrefText.contains("pdf") && new URLDownload(href).isPdf()) { - links.add(URI.create(href).toURL()); + links.add(URLUtil.create(href)); } } @@ -128,7 +129,7 @@ public Optional findFullText(BibEntry entry) throws IOException { } catch (UnsupportedMimeTypeException type) { // this might be the PDF already as we follow redirects if (type.getMimeType().startsWith("application/pdf")) { - return Optional.of(URI.create(type.getUrl()).toURL()); + return Optional.of(URLUtil.create(type.getUrl())); } LOGGER.warn("DoiResolution fetcher failed: ", type); } catch (IOException e) { @@ -148,7 +149,7 @@ private Optional citationMetaTag(Document html) { if (citationPdfUrl.isPresent()) { try { - return Optional.of(URI.create(citationPdfUrl.get()).toURL()); + return Optional.of(URLUtil.create(citationPdfUrl.get())); } catch (MalformedURLException e) { return Optional.empty(); } diff --git a/src/main/java/org/jabref/logic/importer/fetcher/GoogleScholar.java b/src/main/java/org/jabref/logic/importer/fetcher/GoogleScholar.java index c021916480d..4fbfba362e2 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/GoogleScholar.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/GoogleScholar.java @@ -4,7 +4,6 @@ import java.io.StringReader; import java.net.HttpCookie; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; @@ -25,6 +24,7 @@ import org.jabref.logic.importer.fileformat.BibtexParser; import org.jabref.logic.l10n.Localization; import org.jabref.logic.net.URLDownload; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.paging.Page; @@ -108,7 +108,7 @@ private Optional search(String url) throws IOException { // TODO: check title inside pdf + length? // TODO: report error function needed?! query -> result LOGGER.info("Fulltext PDF found @ Google: {}", target); - pdfLink = Optional.of(URI.create(target).toURL()); + pdfLink = Optional.of(URLUtil.create(target)); break; } } diff --git a/src/main/java/org/jabref/logic/importer/fetcher/IEEE.java b/src/main/java/org/jabref/logic/importer/fetcher/IEEE.java index cfbf973162a..2a5e36b3a33 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/IEEE.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/IEEE.java @@ -3,7 +3,6 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; @@ -24,6 +23,7 @@ import org.jabref.logic.importer.fetcher.transformers.IEEEQueryTransformer; import org.jabref.logic.net.URLDownload; import org.jabref.logic.os.OS; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; import org.jabref.model.entry.field.StandardField; @@ -202,7 +202,7 @@ public Optional findFullText(BibEntry entry) throws FetcherException { LOGGER.debug("Full text document found on IEEE Xplore"); URL value; try { - value = URI.create(matcher.group(1)).toURL(); + value = URLUtil.create(matcher.group(1)); } catch (MalformedURLException e) { throw new FetcherException("Malformed URL", e); } diff --git a/src/main/java/org/jabref/logic/importer/fetcher/IacrEprintFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/IacrEprintFetcher.java index 65899f49237..7e605166836 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/IacrEprintFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/IacrEprintFetcher.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; import java.net.URL; import java.util.Objects; import java.util.Optional; @@ -17,6 +16,7 @@ import org.jabref.logic.importer.fileformat.BibtexParser; import org.jabref.logic.l10n.Localization; import org.jabref.logic.net.URLDownload; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.strings.StringUtil; @@ -63,7 +63,7 @@ public Optional performSearchById(String identifier) throws FetcherExc private Optional createEntryFromIacrCitation(String validIdentifier) throws FetcherException { URL url; try { - url = URI.create(CITATION_URL_PREFIX + validIdentifier).toURL(); + url = URLUtil.create(CITATION_URL_PREFIX + validIdentifier); } catch (MalformedURLException e) { throw new FetcherException("Invalid URL", e); } @@ -83,7 +83,7 @@ private Optional createEntryFromIacrCitation(String validIdentifier) t private void setAdditionalFields(BibEntry entry, String identifier) throws FetcherException { URL entryUrl; try { - entryUrl = URI.create(DESCRIPTION_URL_PREFIX + identifier).toURL(); + entryUrl = URLUtil.create(DESCRIPTION_URL_PREFIX + identifier); } catch (MalformedURLException e) { throw new FetcherException("Invalid URL", e); } @@ -96,7 +96,7 @@ private void setAdditionalFields(BibEntry entry, String identifier) throws Fetch // Version information for entries after year 2000 if (isFromOrAfterYear2000(entry)) { try { - entryUrl = URI.create(VERSION_URL_PREFIX + identifier).toURL(); + entryUrl = URLUtil.create(VERSION_URL_PREFIX + identifier); } catch (MalformedURLException e) { throw new FetcherException("Invalid URL", e); } @@ -160,7 +160,7 @@ public Optional findFullText(BibEntry entry) throws IOException, FetcherExc if (urlField.isPresent()) { URL url; try { - url = URI.create(urlField.get()).toURL(); + url = URLUtil.create(urlField.get()); } catch (MalformedURLException e) { LOGGER.warn("Invalid URL {}", urlField.get(), e); return Optional.empty(); @@ -173,7 +173,7 @@ public Optional findFullText(BibEntry entry) throws IOException, FetcherExc // getRequiredValueBetween refuses to match across the line break. fulltextLinkAsInHtml = fulltextLinkAsInHtml.replaceFirst(".*href=\"/", "").trim(); String fulltextLink = FULLTEXT_URL_PREFIX + fulltextLinkAsInHtml + ".pdf"; - return Optional.of(URI.create(fulltextLink).toURL()); + return Optional.of(URLUtil.create(fulltextLink)); } return Optional.empty(); } diff --git a/src/main/java/org/jabref/logic/importer/fetcher/JstorFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/JstorFetcher.java index 3eb00e2acae..dbbe5fa2c24 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/JstorFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/JstorFetcher.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -23,6 +22,7 @@ import org.jabref.logic.importer.fetcher.transformers.JstorQueryTransformer; import org.jabref.logic.importer.fileformat.BibtexParser; import org.jabref.logic.net.URLDownload; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; @@ -66,10 +66,10 @@ public URL getUrlForIdentifier(String identifier) throws MalformedURLException { if (identifier.contains("/")) { // if identifier links to a entry with a valid doi - return URI.create(start + identifier).toURL(); + return URLUtil.create(start + identifier); } // else use default doi start. - return URI.create(start + "10.2307/" + identifier).toURL(); + return URLUtil.create(start + "10.2307/" + identifier); } @Override @@ -126,7 +126,7 @@ public Optional findFullText(BibEntry entry) throws FetcherException, IOExc } String url = elements.getFirst().attr("href"); - return Optional.of(URI.create(url).toURL()); + return Optional.of(URLUtil.create(url)); } @Override diff --git a/src/main/java/org/jabref/logic/importer/fetcher/Medra.java b/src/main/java/org/jabref/logic/importer/fetcher/Medra.java index 7566f50f4d8..0d1f01a2b6f 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/Medra.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/Medra.java @@ -1,7 +1,6 @@ package org.jabref.logic.importer.fetcher; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; @@ -15,6 +14,7 @@ import org.jabref.logic.importer.util.JsonReader; import org.jabref.logic.importer.util.MediaTypes; import org.jabref.logic.net.URLDownload; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.Author; import org.jabref.model.entry.AuthorList; import org.jabref.model.entry.BibEntry; @@ -104,7 +104,7 @@ public URLDownload getUrlDownload(URL url) { @Override public URL getUrlForIdentifier(String identifier) throws URISyntaxException, MalformedURLException { - return URI.create(API_URL + "/" + identifier).toURL(); + return URLUtil.create(API_URL + "/" + identifier); } @Override diff --git a/src/main/java/org/jabref/logic/importer/fetcher/OpenAccessDoi.java b/src/main/java/org/jabref/logic/importer/fetcher/OpenAccessDoi.java index 05d247b6032..8083b22d5a9 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/OpenAccessDoi.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/OpenAccessDoi.java @@ -2,12 +2,12 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; import java.net.URL; import java.util.Objects; import java.util.Optional; import org.jabref.logic.importer.FulltextFetcher; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.DOI; @@ -67,7 +67,7 @@ public Optional findFullText(DOI doi) throws UnirestException { .map(location -> location.optString("url")) .flatMap(url -> { try { - return Optional.of(URI.create(url).toURL()); + return Optional.of(URLUtil.create(url)); } catch (MalformedURLException e) { LOGGER.debug("Could not determine URL to fetch full text from", e); return Optional.empty(); diff --git a/src/main/java/org/jabref/logic/importer/fetcher/ResearchGate.java b/src/main/java/org/jabref/logic/importer/fetcher/ResearchGate.java index 1691bff0ebe..dff0b43a0fb 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/ResearchGate.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/ResearchGate.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; @@ -24,6 +23,7 @@ import org.jabref.logic.layout.format.RTFChars; import org.jabref.logic.net.URLDownload; import org.jabref.logic.os.OS; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.DOI; @@ -77,7 +77,7 @@ public Optional findFullText(BibEntry entry) throws IOException, FetcherExc LOGGER.debug("PDF link: {}", link); if (link.contains("researchgate.net")) { - return Optional.of(URI.create(link).toURL()); + return Optional.of(URLUtil.create(link)); } return Optional.empty(); } @@ -254,7 +254,7 @@ public List performSearch(QueryNode luceneQuery) throws FetcherExcepti private BufferedReader getInputStream(String urlString) { try { - URL url = URI.create(urlString).toURL(); + URL url = URLUtil.create(urlString); return new BufferedReader(new InputStreamReader(url.openStream())); } catch (IOException e) { LOGGER.debug("Wrong URL", e); diff --git a/src/main/java/org/jabref/logic/importer/fetcher/ScienceDirect.java b/src/main/java/org/jabref/logic/importer/fetcher/ScienceDirect.java index f2559d2f0ec..43fba83c175 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/ScienceDirect.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/ScienceDirect.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; import java.net.URL; import java.util.Objects; import java.util.Optional; @@ -11,6 +10,7 @@ import org.jabref.logic.importer.FulltextFetcher; import org.jabref.logic.importer.ImporterPreferences; import org.jabref.logic.net.URLDownload; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.DOI; @@ -71,7 +71,7 @@ public Optional findFullText(BibEntry entry) throws IOException { Elements metaLinks = html.getElementsByAttributeValue("name", "citation_pdf_url"); if (!metaLinks.isEmpty()) { String link = metaLinks.first().attr("content"); - return Optional.of(URI.create(link).toURL()); + return Optional.of(URLUtil.create(link)); } // We use the ScienceDirect web page which contains the article (presented using HTML). @@ -102,7 +102,7 @@ public Optional findFullText(BibEntry entry) throws IOException { String fullLinkToPdf; if (pdfDownload.has("linkToPdf")) { String linkToPdf = pdfDownload.getString("linkToPdf"); - URL url = URI.create(urlFromDoi).toURL(); + URL url = URLUtil.create(urlFromDoi); fullLinkToPdf = "%s://%s%s".formatted(url.getProtocol(), url.getAuthority(), linkToPdf); } else if (pdfDownload.has("urlMetadata")) { JSONObject urlMetadata = pdfDownload.getJSONObject("urlMetadata"); @@ -122,7 +122,7 @@ public Optional findFullText(BibEntry entry) throws IOException { LOGGER.info("Fulltext PDF found at ScienceDirect at {}.", fullLinkToPdf); try { - return Optional.of(URI.create(fullLinkToPdf).toURL()); + return Optional.of(URLUtil.create(fullLinkToPdf)); } catch (MalformedURLException e) { LOGGER.error("malformed URL", e); return Optional.empty(); diff --git a/src/main/java/org/jabref/logic/importer/fetcher/SemanticScholar.java b/src/main/java/org/jabref/logic/importer/fetcher/SemanticScholar.java index 45aa343a333..c46772da100 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/SemanticScholar.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/SemanticScholar.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; @@ -23,6 +22,7 @@ import org.jabref.logic.importer.fetcher.transformers.DefaultQueryTransformer; import org.jabref.logic.importer.util.JsonReader; import org.jabref.logic.net.URLDownload; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.ArXivIdentifier; @@ -115,7 +115,7 @@ public Optional findFullText(BibEntry entry) throws IOException, FetcherExc return Optional.empty(); } LOGGER.info("Fulltext PDF found @ SemanticScholar. Link: {}", link); - return Optional.of(URI.create(link).toURL()); + return Optional.of(URLUtil.create(link)); } @Override diff --git a/src/main/java/org/jabref/logic/importer/fetcher/SpringerFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/SpringerFetcher.java index aa21089d253..71cce64e126 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/SpringerFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/SpringerFetcher.java @@ -3,7 +3,6 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; @@ -17,6 +16,7 @@ import org.jabref.logic.importer.Parser; import org.jabref.logic.importer.fetcher.transformers.SpringerQueryTransformer; import org.jabref.logic.os.OS; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; import org.jabref.model.entry.Month; @@ -130,7 +130,7 @@ public static BibEntry parseSpringerJSONtoBibtex(JSONObject springerJsonEntry) { JSONObject url = (JSONObject) data; if ("pdf".equalsIgnoreCase(url.optString("format"))) { try { - entry.addFile(new LinkedFile(URI.create(url.optString("value")).toURL(), "PDF")); + entry.addFile(new LinkedFile(URLUtil.create(url.optString("value")), "PDF")); } catch (MalformedURLException e) { LOGGER.info("Malformed URL: {}", url.optString("value")); } diff --git a/src/main/java/org/jabref/logic/importer/fileformat/MarcXmlParser.java b/src/main/java/org/jabref/logic/importer/fileformat/MarcXmlParser.java index 1e918099990..ed4ccda242b 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/MarcXmlParser.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/MarcXmlParser.java @@ -3,7 +3,6 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; -import java.net.URI; import java.time.DateTimeException; import java.util.Arrays; import java.util.LinkedList; @@ -19,6 +18,7 @@ import org.jabref.logic.importer.ParseException; import org.jabref.logic.importer.Parser; import org.jabref.logic.util.StandardFileType; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.AuthorList; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.Date; @@ -401,7 +401,7 @@ private void putElectronicLocation(BibEntry bibEntry, Element datafield) { private static void handleVolltext(BibEntry bibEntry, String fieldName, String resource, Field fallBackField) { if ("Volltext".equals(fieldName) && StringUtil.isNotBlank(resource)) { try { - LinkedFile linkedFile = new LinkedFile("", URI.create(resource).toURL(), StandardFileType.PDF.getName()); + LinkedFile linkedFile = new LinkedFile("", URLUtil.create(resource), StandardFileType.PDF.getName()); bibEntry.setFiles(List.of(linkedFile)); } catch (MalformedURLException | IllegalArgumentException e) { LOGGER.info("Malformed URL: {}", resource); diff --git a/src/main/java/org/jabref/logic/importer/util/FileFieldParser.java b/src/main/java/org/jabref/logic/importer/util/FileFieldParser.java index 695c0a48cbe..59527031e4c 100644 --- a/src/main/java/org/jabref/logic/importer/util/FileFieldParser.java +++ b/src/main/java/org/jabref/logic/importer/util/FileFieldParser.java @@ -1,12 +1,12 @@ package org.jabref.logic.importer.util; import java.net.MalformedURLException; -import java.net.URI; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.LinkedFile; import org.slf4j.Logger; @@ -59,7 +59,7 @@ public List parse() { if (LinkedFile.isOnlineLink(value.trim())) { // needs to be modifiable try { - return List.of(new LinkedFile(URI.create(value).toURL(), "")); + return List.of(new LinkedFile(URLUtil.create(value), "")); } catch (MalformedURLException e) { LOGGER.error("invalid url", e); return files; @@ -157,7 +157,7 @@ static LinkedFile convert(List entry) { LinkedFile field = null; if (LinkedFile.isOnlineLink(entry.get(1))) { try { - field = new LinkedFile(entry.getFirst(), URI.create(entry.get(1)).toURL(), entry.get(2)); + field = new LinkedFile(entry.getFirst(), URLUtil.create(entry.get(1)), entry.get(2)); } catch (MalformedURLException e) { // in case the URL is malformed, store it nevertheless field = new LinkedFile(entry.getFirst(), entry.get(1), entry.get(2)); diff --git a/src/main/java/org/jabref/logic/layout/format/DOICheck.java b/src/main/java/org/jabref/logic/layout/format/DOICheck.java index ed9f2a6fd7b..6f0606291e4 100644 --- a/src/main/java/org/jabref/logic/layout/format/DOICheck.java +++ b/src/main/java/org/jabref/logic/layout/format/DOICheck.java @@ -4,6 +4,7 @@ import org.jabref.logic.layout.LayoutFormatter; import org.jabref.logic.preferences.DOIPreferences; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.identifier.DOI; /** @@ -31,7 +32,7 @@ public String format(String fieldText) { } if (doiPreferences.isUseCustom()) { - var base = URI.create(doiPreferences.getDefaultBaseURI()); + var base = URLUtil.createUri(doiPreferences.getDefaultBaseURI()); return DOI.parse(result).flatMap(doi -> doi.getExternalURIFromBase(base)) .map(URI::toASCIIString) .orElse(result); diff --git a/src/main/java/org/jabref/logic/net/URLDownload.java b/src/main/java/org/jabref/logic/net/URLDownload.java index 6fb9db4cabe..32993af4eff 100644 --- a/src/main/java/org/jabref/logic/net/URLDownload.java +++ b/src/main/java/org/jabref/logic/net/URLDownload.java @@ -15,7 +15,6 @@ import java.net.HttpCookie; import java.net.HttpURLConnection; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; @@ -42,6 +41,7 @@ import org.jabref.logic.importer.FetcherClientException; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.FetcherServerException; +import org.jabref.logic.util.URLUtil; import org.jabref.logic.util.io.FileUtil; import org.jabref.model.strings.StringUtil; @@ -90,7 +90,7 @@ public class URLDownload { * @throws MalformedURLException if no protocol is specified in the source, or an unknown protocol is found */ public URLDownload(String source) throws MalformedURLException { - this(URI.create(source).toURL()); + this(URLUtil.create(source)); } /** @@ -153,7 +153,7 @@ public Optional getMimeType() { // Try to resolve local URIs try { - URLConnection connection = URI.create(source.toString()).toURL().openConnection(); + URLConnection connection = URLUtil.create(source.toString()).openConnection(); contentType = connection.getContentType(); if (!StringUtil.isNullOrEmpty(contentType)) { return Optional.of(contentType); diff --git a/src/main/java/org/jabref/logic/util/URLUtil.java b/src/main/java/org/jabref/logic/util/URLUtil.java new file mode 100644 index 00000000000..fd6317d9d23 --- /dev/null +++ b/src/main/java/org/jabref/logic/util/URLUtil.java @@ -0,0 +1,115 @@ +package org.jabref.logic.util; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * URL utilities for URLs in the JabRef logic. + *

+ * For GUI-oriented URL utilities see {@link org.jabref.gui.fieldeditors.URLUtil}. + */ +public class URLUtil { + private static final String URL_EXP = "^(https?|ftp)://.+"; + // Detect Google search URL + private static final String GOOGLE_SEARCH_EXP = "^https?://(?:www\\.)?google\\.[\\.a-z]+?/url.*"; + + /** + * Cleans URLs returned by Google search. + * + * If you copy links from search results from Google, all links will be enriched with search meta data, e.g. + * https://www.google.de/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&&url=http%3A%2F%2Fwww.inrg.csie.ntu.edu.tw%2Falgorithm2014%2Fhomework%2FWagner-74.pdf&ei=DifeVYHkDYWqU5W0j6gD&usg=AFQjCNFl638rl5KVta1jIMWLyb4CPSZidg&sig2=0hSSMw9XZXL3HJWwEcJtOg + * + * + * @param url the Google search URL string + * @return the cleaned Google URL or @code{url} if no search URL was detected + */ + public static String cleanGoogleSearchURL(String url) { + Objects.requireNonNull(url); + + if (!url.matches(GOOGLE_SEARCH_EXP)) { + return url; + } + // Extract destination URL + try { + URL searchURL = create(url); + // URL parameters + String query = searchURL.getQuery(); + // no parameters + if (query == null) { + return url; + } + // extract url parameter + String[] pairs = query.split("&"); + + for (String pair : pairs) { + // "clean" url is decoded value of "url" parameter + if (pair.startsWith("url=")) { + String value = pair.substring(pair.indexOf('=') + 1); + + String decode = URLDecoder.decode(value, StandardCharsets.UTF_8); + // url? + if (decode.matches(URL_EXP)) { + return decode; + } + } + } + return url; + } catch (MalformedURLException e) { + return url; + } + } + + /** + * Checks whether the given String is a URL. + *

+ * Currently only checks for a protocol String. + * + * @param url the String to check for a URL + * @return true if url contains a valid URL + */ + public static boolean isURL(String url) { + try { + create(url); + return true; + } catch (MalformedURLException | IllegalArgumentException e) { + return false; + } + } + + /** + * Creates a {@link URL} object from the given string URL. + * + * @param url the URL string to be converted into a {@link URL}. + * @return the {@link URL} object created from the string URL. + * @throws MalformedURLException if the URL is malformed and cannot be converted to a {@link URL}. + */ + public static URL create(String url) throws MalformedURLException { + return createUri(url).toURL(); + } + + /** + * Creates a {@link URI} object from the given string URL. + * This method attempts to convert the given URL string into a {@link URI} object. + * The pipe character ('|') is replaced with its percent-encoded equivalent ("%7C") because the pipe character + * is only a valid character according to RFC3986. However, JDK's URI implementation is implementing RFC2396 and RFC2732, but not RFC3986. + * + * @param url the URL string to be converted into a {@link URI}. + * @return the {@link URI} object created from the string URL. + * @throws IllegalArgumentException if the string URL is not a valid URI or if the URI format is incorrect. + * @throws URISyntaxException if the string URL has an invalid syntax and cannot be converted into a {@link URI}. + */ + public static URI createUri(String url) { + try { + // Replace '|' character with its percent-encoded representation '%7C'. + String urlFormat = url.replace("|", "%7C"); + return new URI(urlFormat); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } +} diff --git a/src/main/java/org/jabref/logic/util/Version.java b/src/main/java/org/jabref/logic/util/Version.java index 5e10be4390b..1380c90e109 100644 --- a/src/main/java/org/jabref/logic/util/Version.java +++ b/src/main/java/org/jabref/logic/util/Version.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; -import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -106,7 +105,7 @@ public static Version parse(String version) { * Grabs all the available releases from the GitHub repository */ public static List getAllAvailableVersions() throws IOException { - HttpURLConnection connection = (HttpURLConnection) URI.create(JABREF_GITHUB_RELEASES).toURL().openConnection(); + HttpURLConnection connection = (HttpURLConnection) URLUtil.create(JABREF_GITHUB_RELEASES).openConnection(); connection.setRequestProperty("Accept-Charset", "UTF-8"); try (BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { JSONArray objects = new JSONArray(rd.readLine()); diff --git a/src/main/java/org/jabref/model/entry/identifier/ARK.java b/src/main/java/org/jabref/model/entry/identifier/ARK.java index 709b9eb1f7c..0ab41741fd1 100644 --- a/src/main/java/org/jabref/model/entry/identifier/ARK.java +++ b/src/main/java/org/jabref/model/entry/identifier/ARK.java @@ -3,6 +3,9 @@ import java.net.URI; import java.util.Optional; +import org.jabref.architecture.AllowedToUseLogic; +import org.jabref.logic.util.URLUtil; + /** * Archival Resource Key (ARK) identifiers are URLs that support long-term access to information. They are similar to DOIs * only that we don't know of any service that can extract bibliography information from ARKs. For this reason, if an ARK @@ -15,6 +18,7 @@ * and to provide them with ability to copy/paste the ark as is, we support arks with or without the prefix. *

*/ +@AllowedToUseLogic("Because URL utility is needed") public class ARK extends EprintIdentifier { private final String ark; @@ -35,6 +39,6 @@ public String asString() { @Override public Optional getExternalURI() { - return Optional.of(URI.create("https://n2t.net/ark:/" + asString())); + return Optional.of(URLUtil.createUri("https://n2t.net/ark:/" + asString())); } } diff --git a/src/main/java/org/jabref/model/entry/identifier/DOI.java b/src/main/java/org/jabref/model/entry/identifier/DOI.java index 58e5c59c03b..0b05104f50d 100644 --- a/src/main/java/org/jabref/model/entry/identifier/DOI.java +++ b/src/main/java/org/jabref/model/entry/identifier/DOI.java @@ -12,6 +12,7 @@ import org.jabref.architecture.AllowedToUseLogic; import org.jabref.logic.layout.format.LatexToUnicodeFormatter; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.StandardField; @@ -24,8 +25,8 @@ @AllowedToUseLogic("because we want to have this class 'smart' an be able to parse obscure DOIs, too. For this, we need the LatexToUnicodeformatter.") public class DOI implements Identifier { - public static final URI AGENCY_RESOLVER = URI.create("https://doi.org/doiRA"); - public static final URI RESOLVER = URI.create("https://doi.org/"); + public static final URI AGENCY_RESOLVER = URLUtil.createUri("https://doi.org/doiRA"); + public static final URI RESOLVER = URLUtil.createUri("https://doi.org/"); private static final Logger LOGGER = LoggerFactory.getLogger(DOI.class); @@ -261,7 +262,7 @@ public Optional getExternalURI() { } public Optional getExternalURIWithCustomBase(String customBase) { - return getExternalURIFromBase(URI.create(customBase)); + return getExternalURIFromBase(URLUtil.createUri(customBase)); } public Optional getExternalURIFromBase(URI base) { diff --git a/src/main/java/org/jabref/model/entry/identifier/IacrEprint.java b/src/main/java/org/jabref/model/entry/identifier/IacrEprint.java index 77a88371d9b..953ef33a35b 100644 --- a/src/main/java/org/jabref/model/entry/identifier/IacrEprint.java +++ b/src/main/java/org/jabref/model/entry/identifier/IacrEprint.java @@ -7,14 +7,17 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jabref.architecture.AllowedToUseLogic; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.StandardField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@AllowedToUseLogic("Because URL utility is needed") public class IacrEprint implements Identifier { - public static final URI RESOLVER = URI.create("https://ia.cr"); + public static final URI RESOLVER = URLUtil.createUri("https://ia.cr"); private static final Logger LOGGER = LoggerFactory.getLogger(IacrEprint.class); private static final String IACR_EPRINT_EXP = "\\d{4}\\/\\d{3,5}"; diff --git a/src/main/java/org/jabref/model/entry/identifier/SSRN.java b/src/main/java/org/jabref/model/entry/identifier/SSRN.java index fad2eeae069..0a644b6dba1 100644 --- a/src/main/java/org/jabref/model/entry/identifier/SSRN.java +++ b/src/main/java/org/jabref/model/entry/identifier/SSRN.java @@ -6,9 +6,13 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jabref.architecture.AllowedToUseLogic; +import org.jabref.logic.util.URLUtil; + /** * Represents an SSRN article, identified by its abstract ID. */ +@AllowedToUseLogic("Because URL utility is needed") public class SSRN extends EprintIdentifier { private static final String SSRN_URL_REGEX = "(https?://)?(papers\\.)?ssrn\\.com/(sol3/papers.cfm\\?)?abstract(_id)?=(?\\d+)"; @@ -63,7 +67,7 @@ public String asString() { @Override public Optional getExternalURI() { - URI uri = URI.create("https://ssrn.com/abstract=" + abstractId); + URI uri = URLUtil.createUri("https://ssrn.com/abstract=" + abstractId); return Optional.of(uri); } diff --git a/src/test/java/org/jabref/architecture/MainArchitectureTest.java b/src/test/java/org/jabref/architecture/MainArchitectureTest.java index 1c5fd478d18..476fc088ab8 100644 --- a/src/test/java/org/jabref/architecture/MainArchitectureTest.java +++ b/src/test/java/org/jabref/architecture/MainArchitectureTest.java @@ -1,5 +1,6 @@ package org.jabref.architecture; +import java.net.URI; import java.nio.file.Paths; import org.jabref.logic.importer.fileformat.ImporterTestEngine; @@ -15,7 +16,6 @@ /** * This class checks JabRef's shipped classes for architecture quality. - * * Does not analyze test classes. Hint from StackOverflow */ @AnalyzeClasses(packages = "org.jabref", importOptions = ImportOption.DoNotIncludeTests.class) @@ -150,4 +150,13 @@ public void restrictStandardStreams(JavaClasses classes) { .because("logging framework should be used instead or the class be marked explicitly as @AllowedToUseStandardStreams") .check(classes); } + + @ArchTest + public void shouldNotCallUriCreateMethod(JavaClasses classes) { + noClasses() + .that() + .resideInAPackage("org.jabref..") + .should().callMethod(URI.class, "create", String.class) + .check(classes); + } } diff --git a/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java b/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java index 04295b6cb4c..765042e5dc5 100644 --- a/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java +++ b/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java @@ -5,7 +5,6 @@ import java.net.CookieManager; import java.net.CookiePolicy; import java.net.MalformedURLException; -import java.net.URI; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; @@ -27,6 +26,7 @@ import org.jabref.logic.externalfiles.LinkedFileHandler; import org.jabref.logic.util.CurrentThreadTaskExecutor; import org.jabref.logic.util.TaskExecutor; +import org.jabref.logic.util.URLUtil; import org.jabref.logic.xmp.XmpPreferences; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; @@ -198,7 +198,7 @@ void downloadHtmlFileCausesWarningDisplay(Boolean keepHtmlLink, String warningTe when(filePreferences.getFileDirectoryPattern()).thenReturn("[entrytype]"); databaseContext.setDatabasePath(tempFile); - URL url = URI.create("https://www.google.com/").toURL(); + URL url = URLUtil.create("https://www.google.com/"); String fileType = StandardExternalFileType.URL.getName(); linkedFile = new LinkedFile(url, fileType); @@ -275,7 +275,7 @@ void mimeTypeStringWithParameterIsReturnedAsWithoutParameter() { @ParameterizedTest @ValueSource(booleans = {true, false}) void downloadPdfFileWhenLinkedFilePointsToPdfUrl(boolean keepHtml) throws MalformedURLException { - linkedFile = new LinkedFile(URI.create("http://arxiv.org/pdf/1207.0408v1").toURL(), "pdf"); + linkedFile = new LinkedFile(URLUtil.create("http://arxiv.org/pdf/1207.0408v1"), "pdf"); // Needed Mockito stubbing methods to run test when(filePreferences.shouldStoreFilesRelativeToBibFile()).thenReturn(true); when(filePreferences.getFileNamePattern()).thenReturn("[citationkey]"); diff --git a/src/test/java/org/jabref/gui/linkedfile/DownloadLinkedFileActionTest.java b/src/test/java/org/jabref/gui/linkedfile/DownloadLinkedFileActionTest.java index b7ab54475ee..17e4aa28af1 100644 --- a/src/test/java/org/jabref/gui/linkedfile/DownloadLinkedFileActionTest.java +++ b/src/test/java/org/jabref/gui/linkedfile/DownloadLinkedFileActionTest.java @@ -3,7 +3,6 @@ import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; -import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -18,6 +17,7 @@ import org.jabref.gui.preferences.GuiPreferences; import org.jabref.logic.FilePreferences; import org.jabref.logic.util.CurrentThreadTaskExecutor; +import org.jabref.logic.util.URLUtil; import org.jabref.logic.xmp.XmpPreferences; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; @@ -93,7 +93,7 @@ void tearDown() { void replacesLinkedFiles(@TempDir Path tempFolder) throws Exception { String url = "http://arxiv.org/pdf/1207.0408v1"; - LinkedFile linkedFile = new LinkedFile(URI.create(url).toURL(), ""); + LinkedFile linkedFile = new LinkedFile(URLUtil.create(url), ""); when(databaseContext.getFirstExistingFileDir(any())).thenReturn(Optional.of(tempFolder)); when(filePreferences.getFileNamePattern()).thenReturn("[citationkey]"); when(filePreferences.getFileDirectoryPattern()).thenReturn(""); @@ -118,7 +118,7 @@ void replacesLinkedFiles(@TempDir Path tempFolder) throws Exception { void doesntReplaceSourceURL(boolean keepHtml) throws Exception { String url = "http://arxiv.org/pdf/1207.0408v1"; - LinkedFile linkedFile = new LinkedFile(URI.create(url).toURL(), ""); + LinkedFile linkedFile = new LinkedFile(URLUtil.create(url), ""); when(databaseContext.getFirstExistingFileDir(any())).thenReturn(Optional.of(tempFolder)); when(filePreferences.getFileNamePattern()).thenReturn("[citationkey]"); when(filePreferences.getFileDirectoryPattern()).thenReturn(""); @@ -173,7 +173,7 @@ void keepsHtmlFileLink(@TempDir Path tempFolder) throws Exception { .withStatus(200) .withHeader("Content-Type", "text/html; charset=utf-8"))); - LinkedFile linkedFile = new LinkedFile(URI.create("http://localhost:2331/html").toURL(), ""); + LinkedFile linkedFile = new LinkedFile(URLUtil.create("http://localhost:2331/html"), ""); when(databaseContext.getFirstExistingFileDir(any())).thenReturn(Optional.of(tempFolder)); when(filePreferences.getFileNamePattern()).thenReturn("[citationkey]"); when(filePreferences.getFileDirectoryPattern()).thenReturn(""); @@ -209,7 +209,7 @@ void removesHtmlFileLink(@TempDir Path tempFolder) throws Exception { .withStatus(200) .withHeader("Content-Type", "text/html; charset=utf-8"))); - LinkedFile linkedFile = new LinkedFile(URI.create("http://localhost:2331/html").toURL(), ""); + LinkedFile linkedFile = new LinkedFile(URLUtil.create("http://localhost:2331/html"), ""); when(databaseContext.getFirstExistingFileDir(any())).thenReturn(Optional.of(tempFolder)); when(filePreferences.getFileNamePattern()).thenReturn("[citationkey]"); when(filePreferences.getFileDirectoryPattern()).thenReturn(""); diff --git a/src/test/java/org/jabref/logic/help/HelpFileTest.java b/src/test/java/org/jabref/logic/help/HelpFileTest.java index 63be3fb2316..5c8abcae5d0 100644 --- a/src/test/java/org/jabref/logic/help/HelpFileTest.java +++ b/src/test/java/org/jabref/logic/help/HelpFileTest.java @@ -2,12 +2,12 @@ import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; import java.net.URL; import java.util.Arrays; import java.util.stream.Stream; import org.jabref.logic.net.URLDownload; +import org.jabref.logic.util.URLUtil; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -25,7 +25,7 @@ static Stream getAllHelpFiles() { @ParameterizedTest @MethodSource("getAllHelpFiles") void referToValidPage(HelpFile help) throws IOException { - URL url = URI.create(jabrefHelp + help.getPageName()).toURL(); + URL url = URLUtil.create(jabrefHelp + help.getPageName()); HttpURLConnection http = (HttpURLConnection) url.openConnection(); http.setRequestProperty("User-Agent", URLDownload.USER_AGENT); assertEquals(200, http.getResponseCode(), "Wrong URL: " + url.toString()); diff --git a/src/test/java/org/jabref/logic/importer/FulltextFetchersTest.java b/src/test/java/org/jabref/logic/importer/FulltextFetchersTest.java index 7a9ecf1d031..275ccc59a56 100644 --- a/src/test/java/org/jabref/logic/importer/FulltextFetchersTest.java +++ b/src/test/java/org/jabref/logic/importer/FulltextFetchersTest.java @@ -2,12 +2,12 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; import java.net.URL; import java.util.Optional; import java.util.Set; import org.jabref.logic.importer.fetcher.TrustLevel; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.testutils.category.FetcherTest; @@ -33,7 +33,7 @@ default TrustLevel getTrustLevel() { @Test void acceptPdfUrls() throws MalformedURLException { - URL pdfUrl = URI.create("http://docs.oasis-open.org/wsbpel/2.0/OS/wsbpel-v2.0-OS.pdf").toURL(); + URL pdfUrl = URLUtil.create("http://docs.oasis-open.org/wsbpel/2.0/OS/wsbpel-v2.0-OS.pdf"); FulltextFetcherWithTrustLevel finder = e -> Optional.of(pdfUrl); FulltextFetchers fetcher = new FulltextFetchers(Set.of(finder)); assertEquals(Optional.of(pdfUrl), fetcher.findFullTextPDF(new BibEntry())); @@ -41,7 +41,7 @@ void acceptPdfUrls() throws MalformedURLException { @Test void rejectNonPdfUrls() throws MalformedURLException { - URL pdfUrl = URI.create("https://github.com/JabRef/jabref/blob/master/README.md").toURL(); + URL pdfUrl = URLUtil.create("https://github.com/JabRef/jabref/blob/master/README.md"); FulltextFetcherWithTrustLevel finder = e -> Optional.of(pdfUrl); FulltextFetchers fetcher = new FulltextFetchers(Set.of(finder)); @@ -50,7 +50,7 @@ void rejectNonPdfUrls() throws MalformedURLException { @Test void noTrustLevel() throws MalformedURLException { - URL pdfUrl = URI.create("http://docs.oasis-open.org/wsbpel/2.0/OS/wsbpel-v2.0-OS.pdf").toURL(); + URL pdfUrl = URLUtil.create("http://docs.oasis-open.org/wsbpel/2.0/OS/wsbpel-v2.0-OS.pdf"); FulltextFetcherWithTrustLevel finder = e -> Optional.of(pdfUrl); FulltextFetchers fetcher = new FulltextFetchers(Set.of(finder)); @@ -64,12 +64,12 @@ void higherTrustLevelWins() throws IOException, FetcherException { FulltextFetcher finderHigh = mock(FulltextFetcher.class); when(finderHigh.getTrustLevel()).thenReturn(TrustLevel.SOURCE); - final URL highUrl = URI.create("http://docs.oasis-open.org/wsbpel/2.0/OS/wsbpel-v2.0-OS.pdf").toURL(); + final URL highUrl = URLUtil.create("http://docs.oasis-open.org/wsbpel/2.0/OS/wsbpel-v2.0-OS.pdf"); when(finderHigh.findFullText(entry)).thenReturn(Optional.of(highUrl)); FulltextFetcher finderLow = mock(FulltextFetcher.class); when(finderLow.getTrustLevel()).thenReturn(TrustLevel.UNKNOWN); - final URL lowUrl = URI.create("http://docs.oasis-open.org/opencsa/sca-bpel/sca-bpel-1.1-spec-cd-01.pdf").toURL(); + final URL lowUrl = URLUtil.create("http://docs.oasis-open.org/opencsa/sca-bpel/sca-bpel-1.1-spec-cd-01.pdf"); when(finderLow.findFullText(entry)).thenReturn(Optional.of(lowUrl)); FulltextFetchers fetchers = new FulltextFetchers(Set.of(finderLow, finderHigh)); diff --git a/src/test/java/org/jabref/logic/importer/fetcher/ACSTest.java b/src/test/java/org/jabref/logic/importer/fetcher/ACSTest.java index 71344372b5f..e3d230759bb 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/ACSTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/ACSTest.java @@ -1,9 +1,9 @@ package org.jabref.logic.importer.fetcher; -import java.net.URI; import java.util.Optional; import org.jabref.logic.importer.FulltextFetcher; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.support.DisabledOnCIServer; @@ -23,7 +23,7 @@ void findByDOI() throws Exception { // DOI randomly chosen from https://pubs.acs.org/toc/acscii/0/0 BibEntry entry = new BibEntry().withField(StandardField.DOI, "10.1021/acscentsci.4c00971"); assertEquals( - Optional.of(URI.create("https://pubs.acs.org/doi/pdf/10.1021/acscentsci.4c00971").toURL()), + Optional.of(URLUtil.create("https://pubs.acs.org/doi/pdf/10.1021/acscentsci.4c00971")), fetcher.findFullText(entry) ); } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/ApsFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/ApsFetcherTest.java index a18cd2f7ed2..33d48b36583 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/ApsFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/ApsFetcherTest.java @@ -1,8 +1,8 @@ package org.jabref.logic.importer.fetcher; -import java.net.URI; import java.util.Optional; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.testutils.category.FetcherTest; @@ -25,13 +25,13 @@ void setUp() { @Test void findFullTextFromDoi() throws Exception { BibEntry entry = new BibEntry().withField(StandardField.DOI, "10.1103/PhysRevLett.116.061102"); - assertEquals(Optional.of(URI.create("https://journals.aps.org/prl/pdf/10.1103/PhysRevLett.116.061102").toURL()), finder.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("https://journals.aps.org/prl/pdf/10.1103/PhysRevLett.116.061102")), finder.findFullText(entry)); } @Test void findFullTextFromLowercaseDoi() throws Exception { BibEntry entry = new BibEntry().withField(StandardField.DOI, "10.1103/physrevlett.124.029002"); - assertEquals(Optional.of(URI.create("https://journals.aps.org/prl/pdf/10.1103/PhysRevLett.124.029002").toURL()), finder.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("https://journals.aps.org/prl/pdf/10.1103/PhysRevLett.124.029002")), finder.findFullText(entry)); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java index 68e6ae9115f..6552de6c279 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/ArXivFetcherTest.java @@ -1,7 +1,6 @@ package org.jabref.logic.importer.fetcher; import java.io.IOException; -import java.net.URI; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -16,6 +15,7 @@ import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.logic.importer.PagedSearchBasedFetcher; import org.jabref.logic.importer.SearchBasedFetcher; +import org.jabref.logic.util.URLUtil; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.InternalField; @@ -198,19 +198,19 @@ void findFullTextByDOI() throws IOException { entry.setField(StandardField.DOI, "10.1529/biophysj.104.047340"); entry.setField(StandardField.TITLE, "Pause Point Spectra in DNA Constant-Force Unzipping"); - assertEquals(Optional.of(URI.create("http://arxiv.org/pdf/cond-mat/0406246v1").toURL()), fetcher.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("http://arxiv.org/pdf/cond-mat/0406246v1")), fetcher.findFullText(entry)); } @Test void findFullTextByEprint() throws IOException { entry.setField(StandardField.EPRINT, "1603.06570"); - assertEquals(Optional.of(URI.create("http://arxiv.org/pdf/1603.06570v1").toURL()), fetcher.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("http://arxiv.org/pdf/1603.06570v1")), fetcher.findFullText(entry)); } @Test void findFullTextByEprintWithPrefix() throws IOException { entry.setField(StandardField.EPRINT, "arXiv:1603.06570"); - assertEquals(Optional.of(URI.create("http://arxiv.org/pdf/1603.06570v1").toURL()), fetcher.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("http://arxiv.org/pdf/1603.06570v1")), fetcher.findFullText(entry)); } @Test @@ -218,21 +218,21 @@ void findFullTextByEprintWithUnknownDOI() throws IOException { entry.setField(StandardField.DOI, "10.1529/unknown"); entry.setField(StandardField.EPRINT, "1603.06570"); - assertEquals(Optional.of(URI.create("http://arxiv.org/pdf/1603.06570v1").toURL()), fetcher.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("http://arxiv.org/pdf/1603.06570v1")), fetcher.findFullText(entry)); } @Test void findFullTextByTitle() throws IOException { entry.setField(StandardField.TITLE, "Pause Point Spectra in DNA Constant-Force Unzipping"); - assertEquals(Optional.of(URI.create("http://arxiv.org/pdf/cond-mat/0406246v1").toURL()), fetcher.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("http://arxiv.org/pdf/cond-mat/0406246v1")), fetcher.findFullText(entry)); } @Test void findFullTextByTitleWithCurlyBracket() throws IOException { entry.setField(StandardField.TITLE, "Machine versus {Human} {Attention} in {Deep} {Reinforcement} {Learning} {Tasks}"); - assertEquals(Optional.of(URI.create("http://arxiv.org/pdf/2010.15942v3").toURL()), fetcher.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("http://arxiv.org/pdf/2010.15942v3")), fetcher.findFullText(entry)); } @Test @@ -240,7 +240,7 @@ void findFullTextByTitleWithColonAndJournalWithoutEprint() throws IOException { entry.setField(StandardField.TITLE, "Bayes-TrEx: a Bayesian Sampling Approach to Model Transparency by Example"); entry.setField(StandardField.JOURNAL, "arXiv:2002.10248v4 [cs]"); - assertEquals(Optional.of(URI.create("http://arxiv.org/pdf/2002.10248v4").toURL()), fetcher.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("http://arxiv.org/pdf/2002.10248v4")), fetcher.findFullText(entry)); } @Test @@ -248,7 +248,7 @@ void findFullTextByTitleWithColonAndUrlWithoutEprint() throws IOException { entry.setField(StandardField.TITLE, "Bayes-TrEx: a Bayesian Sampling Approach to Model Transparency by Example"); entry.setField(StandardField.URL, "http://arxiv.org/abs/2002.10248v4"); - assertEquals(Optional.of(URI.create("http://arxiv.org/pdf/2002.10248v4").toURL()), fetcher.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("http://arxiv.org/pdf/2002.10248v4")), fetcher.findFullText(entry)); } @Test @@ -256,7 +256,7 @@ void findFullTextByTitleAndPartOfAuthor() throws IOException { entry.setField(StandardField.TITLE, "Pause Point Spectra in DNA Constant-Force Unzipping"); entry.setField(StandardField.AUTHOR, "Weeks and Lucks"); - assertEquals(Optional.of(URI.create("http://arxiv.org/pdf/cond-mat/0406246v1").toURL()), fetcher.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("http://arxiv.org/pdf/cond-mat/0406246v1")), fetcher.findFullText(entry)); } @Test @@ -264,7 +264,7 @@ void findFullTextByTitleWithCurlyBracketAndPartOfAuthor() throws IOException { entry.setField(StandardField.TITLE, "Machine versus {Human} {Attention} in {Deep} {Reinforcement} {Learning} {Tasks}"); entry.setField(StandardField.AUTHOR, "Zhang, Ruohan and Guo"); - assertEquals(Optional.of(URI.create("http://arxiv.org/pdf/2010.15942v3").toURL()), fetcher.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("http://arxiv.org/pdf/2010.15942v3")), fetcher.findFullText(entry)); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java b/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java index 9ecb239e06f..22b929ab63e 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java @@ -1,7 +1,6 @@ package org.jabref.logic.importer.fetcher; import java.io.IOException; -import java.net.URI; import java.net.URL; import java.util.Collections; import java.util.Iterator; @@ -9,6 +8,7 @@ import java.util.Optional; import org.jabref.logic.importer.FetcherException; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.StandardEntryType; @@ -101,7 +101,7 @@ void searchWithSortingByYearAndYearRange() throws FetcherException { void findByIdAsDOI() throws FetcherException, IOException { BibEntry entry = new BibEntry(StandardEntryType.Misc) .withField(StandardField.DOI, "c16e0888b17cb2c689e5dfa4e2be4fdffb23869e"); - Optional expected = Optional.of(URI.create("https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=c16e0888b17cb2c689e5dfa4e2be4fdffb23869e").toURL()); + Optional expected = Optional.of(URLUtil.create("https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=c16e0888b17cb2c689e5dfa4e2be4fdffb23869e")); assertEquals(expected, fetcher.findFullText(entry)); } @@ -110,7 +110,7 @@ void findBySourceURL() throws FetcherException, IOException { BibEntry entry = new BibEntry(StandardEntryType.Misc) .withField(StandardField.DOI, "") .withField(StandardField.URL, "http://intl.psychosomaticmedicine.org/content/55/3/234.full.pdf"); - Optional expected = Optional.of(URI.create("http://intl.psychosomaticmedicine.org/content/55/3/234.full.pdf").toURL()); + Optional expected = Optional.of(URLUtil.create("http://intl.psychosomaticmedicine.org/content/55/3/234.full.pdf")); assertEquals(expected, fetcher.findFullText(entry)); } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/DoiResolutionTest.java b/src/test/java/org/jabref/logic/importer/fetcher/DoiResolutionTest.java index d66799df0bf..2feb88edb9d 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/DoiResolutionTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/DoiResolutionTest.java @@ -1,10 +1,10 @@ package org.jabref.logic.importer.fetcher; import java.io.IOException; -import java.net.URI; import java.util.Optional; import org.jabref.logic.preferences.DOIPreferences; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.testutils.category.FetcherTest; @@ -35,7 +35,7 @@ void setup() { void linkWithPdfInTitleTag() throws IOException { entry.setField(StandardField.DOI, "10.1051/0004-6361/201527330"); assertEquals( - Optional.of(URI.create("https://www.aanda.org/articles/aa/pdf/2016/01/aa27330-15.pdf").toURL()), + Optional.of(URLUtil.create("https://www.aanda.org/articles/aa/pdf/2016/01/aa27330-15.pdf")), finder.findFullText(entry) ); } @@ -44,13 +44,13 @@ void linkWithPdfInTitleTag() throws IOException { @Test void linkWithPdfStringLeadsToFulltext() throws IOException { entry.setField(StandardField.DOI, "10.1002/acr2.11101"); - assertEquals(Optional.of(URI.create("https://onlinelibrary.wiley.com/doi/pdf/10.1002/acr2.11101").toURL()), finder.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("https://onlinelibrary.wiley.com/doi/pdf/10.1002/acr2.11101")), finder.findFullText(entry)); } @Test void citationMetaTagLeadsToFulltext() throws IOException { entry.setField(StandardField.DOI, "10.1007/978-3-319-89963-3_28"); - assertEquals(Optional.of(URI.create("https://link.springer.com/content/pdf/10.1007/978-3-319-89963-3_28.pdf").toURL()), finder.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("https://link.springer.com/content/pdf/10.1007/978-3-319-89963-3_28.pdf")), finder.findFullText(entry)); } @Test @@ -65,7 +65,7 @@ void returnAnythingWhenBehindSpringerPayWall() throws IOException { // even if the user does not have access // We cannot easily handle this case, because other publisher return the wrong media type. entry.setField(StandardField.DOI, "10.1007/978-3-319-62594-2_12"); - assertEquals(Optional.of(URI.create("https://link.springer.com/content/pdf/10.1007/978-3-319-62594-2_12.pdf").toURL()), finder.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("https://link.springer.com/content/pdf/10.1007/978-3-319-62594-2_12.pdf")), finder.findFullText(entry)); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fetcher/GoogleScholarTest.java b/src/test/java/org/jabref/logic/importer/fetcher/GoogleScholarTest.java index b8a26327f79..2c33a317d02 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/GoogleScholarTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/GoogleScholarTest.java @@ -1,7 +1,6 @@ package org.jabref.logic.importer.fetcher; import java.io.IOException; -import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -10,6 +9,7 @@ import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.logic.importer.PagedSearchBasedFetcher; import org.jabref.logic.importer.SearchBasedFetcher; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.StandardEntryType; @@ -42,7 +42,7 @@ void linkFound() throws Exception { entry.setField(StandardField.TITLE, "Towards Application Portability in Platform as a Service"); assertEquals( - Optional.of(URI.create("https://www.uni-bamberg.de/fileadmin/uni/fakultaeten/wiai_lehrstuehle/praktische_informatik/Dateien/Publikationen/sose14-towards-application-portability-in-paas.pdf").toURL()), + Optional.of(URLUtil.create("https://www.uni-bamberg.de/fileadmin/uni/fakultaeten/wiai_lehrstuehle/praktische_informatik/Dateien/Publikationen/sose14-towards-application-portability-in-paas.pdf")), finder.findFullText(entry) ); } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/IEEETest.java b/src/test/java/org/jabref/logic/importer/fetcher/IEEETest.java index 60ae6d11a44..4d12cac558b 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/IEEETest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/IEEETest.java @@ -1,6 +1,5 @@ package org.jabref.logic.importer.fetcher; -import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -11,6 +10,7 @@ import org.jabref.logic.importer.ImporterPreferences; import org.jabref.logic.importer.PagedSearchBasedFetcher; import org.jabref.logic.importer.SearchBasedFetcher; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.StandardEntryType; @@ -75,7 +75,7 @@ void setUp() { @Disabled("IEEE seems to block us") void findByDOI() throws Exception { BibEntry entry = new BibEntry().withField(StandardField.DOI, "10.1109/ACCESS.2016.2535486"); - assertEquals(Optional.of(URI.create("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=").toURL()), + assertEquals(Optional.of(URLUtil.create("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=")), fetcher.findFullText(entry)); } @@ -83,7 +83,7 @@ void findByDOI() throws Exception { @Disabled("IEEE seems to block us") void findByDocumentUrl() throws Exception { BibEntry entry = new BibEntry().withField(StandardField.URL, "https://ieeexplore.ieee.org/document/7421926/"); - assertEquals(Optional.of(URI.create("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=").toURL()), + assertEquals(Optional.of(URLUtil.create("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=")), fetcher.findFullText(entry)); } @@ -91,7 +91,7 @@ void findByDocumentUrl() throws Exception { @Disabled("IEEE seems to block us") void findByURL() throws Exception { BibEntry entry = new BibEntry().withField(StandardField.URL, "https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=7421926&ref="); - assertEquals(Optional.of(URI.create("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=").toURL()), + assertEquals(Optional.of(URLUtil.create("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=")), fetcher.findFullText(entry)); } @@ -99,7 +99,7 @@ void findByURL() throws Exception { @Disabled("IEEE blocks us - works in browser") void findByOldURL() throws Exception { BibEntry entry = new BibEntry().withField(StandardField.URL, "https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=7421926"); - assertEquals(Optional.of(URI.create("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=").toURL()), + assertEquals(Optional.of(URLUtil.create("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=")), fetcher.findFullText(entry)); } @@ -109,7 +109,7 @@ void findByDOIButNotURL() throws Exception { BibEntry entry = new BibEntry() .withField(StandardField.DOI, "10.1109/ACCESS.2016.2535486") .withField(StandardField.URL, "http://dx.doi.org/10.1109/ACCESS.2016.2535486"); - assertEquals(Optional.of(URI.create("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=").toURL()), + assertEquals(Optional.of(URLUtil.create("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=")), fetcher.findFullText(entry)); } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/JstorFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/JstorFetcherTest.java index 283dc2cdf41..340f07f187a 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/JstorFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/JstorFetcherTest.java @@ -1,6 +1,5 @@ package org.jabref.logic.importer.fetcher; -import java.net.URI; import java.net.URL; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -10,6 +9,7 @@ import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.logic.importer.SearchBasedFetcher; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.StandardEntryType; @@ -80,7 +80,7 @@ void searchByUrlUsingId() throws Exception { @Test void fetchPDF() throws Exception { Optional url = fetcher.findFullText(bibEntry); - assertEquals(Optional.of(URI.create("https://www.jstor.org/stable/pdf/90002164.pdf").toURL()), url); + assertEquals(Optional.of(URLUtil.create("https://www.jstor.org/stable/pdf/90002164.pdf")), url); } @Override diff --git a/src/test/java/org/jabref/logic/importer/fetcher/OpenAccessDoiTest.java b/src/test/java/org/jabref/logic/importer/fetcher/OpenAccessDoiTest.java index dfaf7a99ffe..befec3a4c65 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/OpenAccessDoiTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/OpenAccessDoiTest.java @@ -1,9 +1,9 @@ package org.jabref.logic.importer.fetcher; import java.io.IOException; -import java.net.URI; import java.util.Optional; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.testutils.category.FetcherTest; @@ -28,7 +28,7 @@ void setUp() { @Test void findByDOI() throws IOException { entry.setField(StandardField.DOI, "10.1186/s12993-024-00248-9"); - assertEquals(Optional.of(URI.create("https://behavioralandbrainfunctions.biomedcentral.com/counter/pdf/10.1186/s12993-024-00248-9").toURL()), finder.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create("https://behavioralandbrainfunctions.biomedcentral.com/counter/pdf/10.1186/s12993-024-00248-9")), finder.findFullText(entry)); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fetcher/ResearchGateTest.java b/src/test/java/org/jabref/logic/importer/fetcher/ResearchGateTest.java index 46db0d3b191..265dd46ea61 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/ResearchGateTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/ResearchGateTest.java @@ -1,12 +1,12 @@ package org.jabref.logic.importer.fetcher; import java.io.IOException; -import java.net.URI; import java.util.List; import java.util.Optional; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.ImportFormatPreferences; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.StandardEntryType; @@ -45,7 +45,7 @@ void setUp() { @Test @DisabledOnCIServer("CI server is unreliable") void fullTextFoundByDOI() throws IOException, FetcherException { - assertEquals(Optional.of(URI.create(URL_PDF).toURL()), fetcher.findFullText(entry)); + assertEquals(Optional.of(URLUtil.create(URL_PDF)), fetcher.findFullText(entry)); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fetcher/ScienceDirectTest.java b/src/test/java/org/jabref/logic/importer/fetcher/ScienceDirectTest.java index f516a5d458d..2568fd27204 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/ScienceDirectTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/ScienceDirectTest.java @@ -1,12 +1,12 @@ package org.jabref.logic.importer.fetcher; import java.io.IOException; -import java.net.URI; import java.util.Optional; import javafx.collections.FXCollections; import org.jabref.logic.importer.ImporterPreferences; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.support.DisabledOnCIServer; @@ -39,7 +39,7 @@ void findByDoiOldPage() throws IOException { entry.setField(StandardField.DOI, "10.1016/j.jrmge.2015.08.004"); assertEquals( - Optional.of(URI.create("https://www.sciencedirect.com/science/article/pii/S1674775515001079/pdfft?md5=2b19b19a387cffbae237ca6a987279df&pid=1-s2.0-S1674775515001079-main.pdf").toURL()), + Optional.of(URLUtil.create("https://www.sciencedirect.com/science/article/pii/S1674775515001079/pdfft?md5=2b19b19a387cffbae237ca6a987279df&pid=1-s2.0-S1674775515001079-main.pdf")), finder.findFullText(entry) ); } @@ -50,7 +50,7 @@ void findByDoiNewPage() throws IOException { entry.setField(StandardField.DOI, "10.1016/j.aasri.2014.09.002"); assertEquals( - Optional.of(URI.create("https://www.sciencedirect.com/science/article/pii/S2212671614001024/pdf?md5=4e2e9a369b4d5b3db5100aba599bef8b&pid=1-s2.0-S2212671614001024-main.pdf").toURL()), + Optional.of(URLUtil.create("https://www.sciencedirect.com/science/article/pii/S2212671614001024/pdf?md5=4e2e9a369b4d5b3db5100aba599bef8b&pid=1-s2.0-S2212671614001024-main.pdf")), finder.findFullText(entry) ); } @@ -62,7 +62,7 @@ void findByDoiWorksForBoneArticle() throws IOException { entry.setField(StandardField.DOI, "https://doi.org/10.1016/j.bone.2020.115226"); assertEquals( - Optional.of(URI.create("https://www.sciencedirect.com/science/article/pii/S8756328220300065/pdfft?md5=0ad75ff155637dec358e5c9fb8b90afd&pid=1-s2.0-S8756328220300065-main.pdf").toURL()), + Optional.of(URLUtil.create("https://www.sciencedirect.com/science/article/pii/S8756328220300065/pdfft?md5=0ad75ff155637dec358e5c9fb8b90afd&pid=1-s2.0-S8756328220300065-main.pdf")), finder.findFullText(entry) ); } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/SpringerLinkTest.java b/src/test/java/org/jabref/logic/importer/fetcher/SpringerLinkTest.java index 116afd1791d..f5958ea8d95 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/SpringerLinkTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/SpringerLinkTest.java @@ -1,12 +1,12 @@ package org.jabref.logic.importer.fetcher; import java.io.IOException; -import java.net.URI; import java.util.Optional; import javafx.collections.FXCollections; import org.jabref.logic.importer.ImporterPreferences; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.support.DisabledOnCIServer; @@ -49,7 +49,7 @@ void doiNotPresent() throws IOException { void findByDOI() throws IOException { entry.setField(StandardField.DOI, "10.1186/s13677-015-0042-8"); assertEquals( - Optional.of(URI.create("http://link.springer.com/content/pdf/10.1186/s13677-015-0042-8.pdf").toURL()), + Optional.of(URLUtil.create("http://link.springer.com/content/pdf/10.1186/s13677-015-0042-8.pdf")), finder.findFullText(entry)); } diff --git a/src/test/java/org/jabref/logic/importer/util/FileFieldParserTest.java b/src/test/java/org/jabref/logic/importer/util/FileFieldParserTest.java index 7de789227fe..badf10ac168 100644 --- a/src/test/java/org/jabref/logic/importer/util/FileFieldParserTest.java +++ b/src/test/java/org/jabref/logic/importer/util/FileFieldParserTest.java @@ -1,12 +1,12 @@ package org.jabref.logic.importer.util; -import java.net.URI; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Stream; +import org.jabref.logic.util.URLUtil; import org.jabref.model.entry.LinkedFile; import org.junit.jupiter.params.ParameterizedTest; @@ -65,7 +65,7 @@ private static Stream stringsToParseTest() throws Exception { // parseCorrectOnlineInput Arguments.of( - Collections.singletonList(new LinkedFile(URI.create("http://arxiv.org/pdf/2010.08497v1").toURL(), "PDF")), + Collections.singletonList(new LinkedFile(URLUtil.create("http://arxiv.org/pdf/2010.08497v1"), "PDF")), ":http\\://arxiv.org/pdf/2010.08497v1:PDF" ), @@ -164,24 +164,24 @@ private static Stream stringsToParseTest() throws Exception { // url Arguments.of( - Collections.singletonList(new LinkedFile(URI.create("https://books.google.de/").toURL(), "")), + Collections.singletonList(new LinkedFile(URLUtil.create("https://books.google.de/"), "")), "https://books.google.de/" ), // url with www Arguments.of( - Collections.singletonList(new LinkedFile(URI.create("https://www.google.de/").toURL(), "")), + Collections.singletonList(new LinkedFile(URLUtil.create("https://www.google.de/"), "")), "https://www.google.de/" ), // url as file Arguments.of( - Collections.singletonList(new LinkedFile("", URI.create("http://ceur-ws.org/Vol-438").toURL(), "URL")), + Collections.singletonList(new LinkedFile("", URLUtil.create("http://ceur-ws.org/Vol-438"), "URL")), ":http\\://ceur-ws.org/Vol-438:URL" ), // url as file with desc Arguments.of( - Collections.singletonList(new LinkedFile("desc", URI.create("http://ceur-ws.org/Vol-438").toURL(), "URL")), + Collections.singletonList(new LinkedFile("desc", URLUtil.create("http://ceur-ws.org/Vol-438"), "URL")), "desc:http\\://ceur-ws.org/Vol-438:URL" ), // link with source url diff --git a/src/test/java/org/jabref/logic/net/URLDownloadTest.java b/src/test/java/org/jabref/logic/net/URLDownloadTest.java index ac5af134e71..734820a94fe 100644 --- a/src/test/java/org/jabref/logic/net/URLDownloadTest.java +++ b/src/test/java/org/jabref/logic/net/URLDownloadTest.java @@ -2,13 +2,13 @@ import java.io.File; import java.net.MalformedURLException; -import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import org.jabref.logic.importer.FetcherClientException; import org.jabref.logic.importer.FetcherServerException; +import org.jabref.logic.util.URLUtil; import org.jabref.support.DisabledOnCIServer; import org.jabref.testutils.category.FetcherTest; @@ -36,14 +36,14 @@ class URLDownloadTest { @Test void stringDownloadWithSetEncoding() throws Exception { - URLDownload dl = new URLDownload(URI.create("http://www.google.com").toURL()); + URLDownload dl = new URLDownload(URLUtil.create("http://www.google.com")); assertTrue(dl.asString().contains("Google"), "google.com should contain google"); } @Test void stringDownload() throws Exception { - URLDownload dl = new URLDownload(URI.create("http://www.google.com").toURL()); + URLDownload dl = new URLDownload(URLUtil.create("http://www.google.com")); assertTrue(dl.asString(StandardCharsets.UTF_8).contains("Google"), "google.com should contain google"); } @@ -52,7 +52,7 @@ void stringDownload() throws Exception { void fileDownload() throws Exception { File destination = File.createTempFile("jabref-test", ".html"); try { - URLDownload dl = new URLDownload(URI.create("http://www.google.com").toURL()); + URLDownload dl = new URLDownload(URLUtil.create("http://www.google.com")); dl.toFile(destination.toPath()); assertTrue(destination.exists(), "file must exist"); } finally { @@ -65,14 +65,14 @@ void fileDownload() throws Exception { @Test void determineMimeType() throws Exception { - URLDownload dl = new URLDownload(URI.create("http://www.google.com").toURL()); + URLDownload dl = new URLDownload(URLUtil.create("http://www.google.com")); assertTrue(dl.getMimeType().get().startsWith("text/html")); } @Test void downloadToTemporaryFilePathWithoutFileSavesAsTmpFile() throws Exception { - URLDownload google = new URLDownload(URI.create("http://www.google.com").toURL()); + URLDownload google = new URLDownload(URLUtil.create("http://www.google.com")); String path = google.toTemporaryFile().toString(); assertTrue(path.endsWith(".tmp"), path); @@ -80,7 +80,7 @@ void downloadToTemporaryFilePathWithoutFileSavesAsTmpFile() throws Exception { @Test void downloadToTemporaryFileKeepsName() throws Exception { - URLDownload google = new URLDownload(URI.create("https://github.com/JabRef/jabref/blob/main/LICENSE").toURL()); + URLDownload google = new URLDownload(URLUtil.create("https://github.com/JabRef/jabref/blob/main/LICENSE")); String path = google.toTemporaryFile().toString(); assertTrue(path.contains("LICENSE"), path); @@ -89,7 +89,7 @@ void downloadToTemporaryFileKeepsName() throws Exception { @Test @DisabledOnCIServer("CI Server is apparently blocked") void downloadOfFTPSucceeds() throws Exception { - URLDownload ftp = new URLDownload(URI.create("ftp://ftp.informatik.uni-stuttgart.de/pub/library/ncstrl.ustuttgart_fi/INPROC-2016-15/INPROC-2016-15.pdf").toURL()); + URLDownload ftp = new URLDownload(URLUtil.create("ftp://ftp.informatik.uni-stuttgart.de/pub/library/ncstrl.ustuttgart_fi/INPROC-2016-15/INPROC-2016-15.pdf")); Path path = ftp.toTemporaryFile(); assertNotNull(path); @@ -97,7 +97,7 @@ void downloadOfFTPSucceeds() throws Exception { @Test void downloadOfHttpSucceeds() throws Exception { - URLDownload ftp = new URLDownload(URI.create("http://www.jabref.org").toURL()); + URLDownload ftp = new URLDownload(URLUtil.create("http://www.jabref.org")); Path path = ftp.toTemporaryFile(); assertNotNull(path); @@ -105,7 +105,7 @@ void downloadOfHttpSucceeds() throws Exception { @Test void downloadOfHttpsSucceeds() throws Exception { - URLDownload ftp = new URLDownload(URI.create("https://www.jabref.org").toURL()); + URLDownload ftp = new URLDownload(URLUtil.create("https://www.jabref.org")); Path path = ftp.toTemporaryFile(); assertNotNull(path); @@ -113,21 +113,21 @@ void downloadOfHttpsSucceeds() throws Exception { @Test void checkConnectionSuccess() throws MalformedURLException { - URLDownload google = new URLDownload(URI.create("http://www.google.com").toURL()); + URLDownload google = new URLDownload(URLUtil.create("http://www.google.com")); assertTrue(google.canBeReached()); } @Test void checkConnectionFail() throws MalformedURLException { - URLDownload nonsense = new URLDownload(URI.create("http://nonsenseadddress").toURL()); + URLDownload nonsense = new URLDownload(URLUtil.create("http://nonsenseadddress")); assertThrows(UnirestException.class, nonsense::canBeReached); } @Test void connectTimeoutIsNeverNull() throws MalformedURLException { - URLDownload urlDownload = new URLDownload(URI.create("http://www.example.com").toURL()); + URLDownload urlDownload = new URLDownload(URLUtil.create("http://www.example.com")); assertNotNull(urlDownload.getConnectTimeout(), "there's a non-null default by the constructor"); urlDownload.setConnectTimeout(null); @@ -136,13 +136,13 @@ void connectTimeoutIsNeverNull() throws MalformedURLException { @Test void test503ErrorThrowsFetcherServerException() throws Exception { - URLDownload urlDownload = new URLDownload(URI.create("http://httpstat.us/503").toURL()); + URLDownload urlDownload = new URLDownload(URLUtil.create("http://httpstat.us/503")); assertThrows(FetcherServerException.class, urlDownload::asString); } @Test void test429ErrorThrowsFetcherClientException() throws Exception { - URLDownload urlDownload = new URLDownload(URI.create("http://httpstat.us/429").toURL()); + URLDownload urlDownload = new URLDownload(URLUtil.create("http://httpstat.us/429")); assertThrows(FetcherClientException.class, urlDownload::asString); } @@ -162,7 +162,7 @@ void redirectWorks(@TempDir Path tempDir) throws Exception { .withHeader("Content-Type", "application/pdf") .withBody(pdfContent))); - URLDownload urlDownload = new URLDownload(URI.create("http://localhost:2222/redirect").toURL()); + URLDownload urlDownload = new URLDownload(URLUtil.create("http://localhost:2222/redirect")); Path downloadedFile = tempDir.resolve("download.pdf"); urlDownload.toFile(downloadedFile); byte[] actual = Files.readAllBytes(downloadedFile); diff --git a/src/test/java/org/jabref/logic/net/URLUtilTest.java b/src/test/java/org/jabref/logic/net/URLUtilTest.java index d69d7a1df7e..7de7b641fc5 100644 --- a/src/test/java/org/jabref/logic/net/URLUtilTest.java +++ b/src/test/java/org/jabref/logic/net/URLUtilTest.java @@ -1,6 +1,8 @@ package org.jabref.logic.net; -import org.jabref.gui.fieldeditors.URLUtil; +import java.net.URI; + +import org.jabref.logic.util.URLUtil; import org.junit.jupiter.api.Test; @@ -78,4 +80,11 @@ void isURLshouldRejectInvalidURL() { void isURLshouldRejectEmbeddedURL() { assertFalse(URLUtil.isURL("dblp computer science bibliography, http://dblp.org")); } + + @Test + void createUriShouldHandlePipeCharacter() { + String input = "http://example.com/test|file"; + URI uri = URLUtil.createUri(input); + assertEquals("http://example.com/test%7Cfile", uri.toString()); + } } diff --git a/src/test/java/org/jabref/logic/util/ExternalLinkCreatorTest.java b/src/test/java/org/jabref/logic/util/ExternalLinkCreatorTest.java index 28f1337b3e7..907dbe1504b 100644 --- a/src/test/java/org/jabref/logic/util/ExternalLinkCreatorTest.java +++ b/src/test/java/org/jabref/logic/util/ExternalLinkCreatorTest.java @@ -1,8 +1,6 @@ package org.jabref.logic.util; import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; import java.util.Optional; import org.jabref.model.entry.BibEntry; @@ -22,9 +20,9 @@ class ExternalLinkCreatorTest { private boolean urlIsValid(String url) { try { // This will throw on non-compliance to RFC2396. - URI.create(url).toURL().toURI(); + URLUtil.create(url); return true; - } catch (MalformedURLException | URISyntaxException e) { + } catch (MalformedURLException e) { return false; } } diff --git a/src/test/java/org/jabref/model/entry/BibEntryTest.java b/src/test/java/org/jabref/model/entry/BibEntryTest.java index 3d87ba15c48..a9910037379 100644 --- a/src/test/java/org/jabref/model/entry/BibEntryTest.java +++ b/src/test/java/org/jabref/model/entry/BibEntryTest.java @@ -1,6 +1,5 @@ package org.jabref.model.entry; -import java.net.URI; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -11,6 +10,7 @@ import java.util.Set; import java.util.stream.Stream; +import org.jabref.logic.util.URLUtil; import org.jabref.model.FieldChange; import org.jabref.model.database.BibDatabase; import org.jabref.model.entry.field.BibField; @@ -259,7 +259,7 @@ void getAndAddToLinkedFileList() { void replaceOfLinkWorks() throws Exception { List files = new ArrayList<>(); String urlAsString = "https://www.example.org/file.pdf"; - files.add(new LinkedFile(URI.create(urlAsString).toURL(), "")); + files.add(new LinkedFile(URLUtil.create(urlAsString), "")); entry.setFiles(files); LinkedFile linkedFile = new LinkedFile("", Path.of("file.pdf", ""), ""); diff --git a/src/test/java/org/jabref/model/entry/identifier/SSRNTest.java b/src/test/java/org/jabref/model/entry/identifier/SSRNTest.java index c9a9d9fad66..62f5e2a5bd6 100644 --- a/src/test/java/org/jabref/model/entry/identifier/SSRNTest.java +++ b/src/test/java/org/jabref/model/entry/identifier/SSRNTest.java @@ -4,6 +4,8 @@ import java.util.Optional; import java.util.stream.Stream; +import org.jabref.logic.util.URLUtil; + import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -61,7 +63,7 @@ public void identifierNormalisation() { @Test public void identifierExternalUrl() { SSRN ssrnIdentifier = new SSRN(123456); - URI uri = URI.create("https://ssrn.com/abstract=123456"); + URI uri = URLUtil.createUri("https://ssrn.com/abstract=123456"); assertEquals(Optional.of(uri), ssrnIdentifier.getExternalURI()); } } From 62749e9e08d1a41cecf40adea0168d6d531fc714 Mon Sep 17 00:00:00 2001 From: Christoph Date: Mon, 25 Nov 2024 22:46:05 +0100 Subject: [PATCH 12/20] New Crowdin updates (#12235) * New translations jabref_en.properties (Portuguese, Brazilian) * New translations jabref_en.properties (Swedish) * New translations jabref_en.properties (French) * New translations jabref_en.properties (Spanish) * New translations jabref_en.properties (Arabic) * New translations jabref_en.properties (Danish) * New translations jabref_en.properties (German) * New translations jabref_en.properties (Greek) * New translations jabref_en.properties (Finnish) * New translations jabref_en.properties (Italian) * New translations jabref_en.properties (Japanese) * New translations jabref_en.properties (Korean) * New translations jabref_en.properties (Dutch) * New translations jabref_en.properties (Norwegian) * New translations jabref_en.properties (Polish) * New translations jabref_en.properties (Portuguese) * New translations jabref_en.properties (Russian) * New translations jabref_en.properties (Turkish) * New translations jabref_en.properties (Ukrainian) * New translations jabref_en.properties (Chinese Simplified) * New translations jabref_en.properties (Chinese Traditional) * New translations jabref_en.properties (Vietnamese) * New translations jabref_en.properties (Indonesian) * New translations jabref_en.properties (Persian) * New translations jabref_en.properties (Tagalog) --- src/main/resources/l10n/JabRef_ar.properties | 2 ++ src/main/resources/l10n/JabRef_da.properties | 2 ++ src/main/resources/l10n/JabRef_de.properties | 2 ++ src/main/resources/l10n/JabRef_el.properties | 2 ++ src/main/resources/l10n/JabRef_es.properties | 2 ++ src/main/resources/l10n/JabRef_fa.properties | 2 ++ src/main/resources/l10n/JabRef_fi.properties | 2 ++ src/main/resources/l10n/JabRef_fr.properties | 3 +++ src/main/resources/l10n/JabRef_id.properties | 2 ++ src/main/resources/l10n/JabRef_it.properties | 5 ++++- src/main/resources/l10n/JabRef_ja.properties | 2 ++ src/main/resources/l10n/JabRef_ko.properties | 2 ++ src/main/resources/l10n/JabRef_nl.properties | 4 +++- src/main/resources/l10n/JabRef_no.properties | 2 ++ src/main/resources/l10n/JabRef_pl.properties | 3 +++ src/main/resources/l10n/JabRef_pt.properties | 2 ++ src/main/resources/l10n/JabRef_pt_BR.properties | 2 ++ src/main/resources/l10n/JabRef_ru.properties | 2 ++ src/main/resources/l10n/JabRef_sv.properties | 2 ++ src/main/resources/l10n/JabRef_tl.properties | 2 ++ src/main/resources/l10n/JabRef_tr.properties | 2 ++ src/main/resources/l10n/JabRef_uk.properties | 2 ++ src/main/resources/l10n/JabRef_vi.properties | 2 ++ src/main/resources/l10n/JabRef_zh_CN.properties | 4 +++- src/main/resources/l10n/JabRef_zh_TW.properties | 2 ++ 25 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/main/resources/l10n/JabRef_ar.properties b/src/main/resources/l10n/JabRef_ar.properties index 111db309d93..67e4d9beb81 100644 --- a/src/main/resources/l10n/JabRef_ar.properties +++ b/src/main/resources/l10n/JabRef_ar.properties @@ -232,6 +232,7 @@ Field\ name=اسم الحقل + Import\ preferences=استيراد الإعدادات @@ -468,6 +469,7 @@ Optional\ fields\ 2=الحقول الاختيارية 2 + Could\ not\ run\ the\ 'vim'\ program.=لا يمكن تشغيل برنامَج 'vim'. diff --git a/src/main/resources/l10n/JabRef_da.properties b/src/main/resources/l10n/JabRef_da.properties index c06c35711b1..b924730a4ec 100644 --- a/src/main/resources/l10n/JabRef_da.properties +++ b/src/main/resources/l10n/JabRef_da.properties @@ -290,6 +290,7 @@ Highlight=Fremhæv HTML\ table=HTML-tabel HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTML-tabell (med Abstract & BibTeX) + Icon=Ikon Ignore=Ignorer @@ -678,6 +679,7 @@ Expected\ syntax\ for\ --fetch\='\:'=Forventet syntaks User-specific\ file\ directory=Brugerspecifikt filbibliotek + Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Autofuldfør kun navne i formatet 'Fornavn Efternavn' Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Autofuldfør kun navne i formatet 'Efternavn, Fornavn' Autocomplete\ names\ in\ both\ formats=Autofuldfør navne i begge formater diff --git a/src/main/resources/l10n/JabRef_de.properties b/src/main/resources/l10n/JabRef_de.properties index 6c35cb304a1..b1c3341ab87 100644 --- a/src/main/resources/l10n/JabRef_de.properties +++ b/src/main/resources/l10n/JabRef_de.properties @@ -466,6 +466,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=Der markierte Bereic HTML\ table=HTML-Tabelle HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTML-Tabelle (mit Abstract & BibTeX) + Icon=Icon Ignore=Ignorieren @@ -1060,6 +1061,7 @@ Library-specific\ file\ directory=Bibliothekseigener Dateipfad User-specific\ file\ directory=Benutzerdefiniertes Dateiverzeichnis LaTeX\ file\ directory=LaTeX-Dateiverzeichnis + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=Sie müssen einen Zahlwert zwischen 1025 und 65535 eintragen Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Automatische Vervollständigung von Namen nur im Format 'Vorname Nachname' Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Automatische Vervollständigung von Namen nur im Format 'Nachname, Vorname' diff --git a/src/main/resources/l10n/JabRef_el.properties b/src/main/resources/l10n/JabRef_el.properties index 8e758879050..a09b8d2943a 100644 --- a/src/main/resources/l10n/JabRef_el.properties +++ b/src/main/resources/l10n/JabRef_el.properties @@ -378,6 +378,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=Η μαρκαρισ HTML\ table=Πίνακας HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Πίνακας HTML (με Περίληψη & BibTeX) + Icon=Εικονίδιο Ignore=Παράβλεψη @@ -897,6 +898,7 @@ Expected\ syntax\ for\ --fetch\='\:'=Αναμενόμ User-specific\ file\ directory=Φάκελος αρχείου καθορισμένος από τον χρήστη LaTeX\ file\ directory=Κατάλογος αρχείου LaTeX + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=Πρέπει να εισάγετε μια ακέραια τιμή στο διάστημα 1025-65535 Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Αυτόματη συμπλήρωση ονομάτων μόνο με τη μορφή 'Όνομα Επώνυμο' Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Αυτόματη συμπλήρωση ονομάτων μόνο με τη μορφή 'Επώνυμο, Όνομα' diff --git a/src/main/resources/l10n/JabRef_es.properties b/src/main/resources/l10n/JabRef_es.properties index 3b7fbba315a..5b24695ceb0 100644 --- a/src/main/resources/l10n/JabRef_es.properties +++ b/src/main/resources/l10n/JabRef_es.properties @@ -412,6 +412,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=¡El área marcada n HTML\ table=Tabla HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Tabla HTML (con Abstract BibTeX) + Icon=Icono Ignore=Ignorar @@ -963,6 +964,7 @@ Expected\ syntax\ for\ --fetch\='\:'=Sintaxis esperada User-specific\ file\ directory=Carpeta de archivos de usuario LaTeX\ file\ directory=Directorio de archivos de LaTeX + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=Debe introducir un valor entero en el intervalo 1025-65535 Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Autocompletar nombres sólo en el formato 'Nombre Apellidos' Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Autocompletar nombres sólo en el formato 'Apellidos, Nombre' diff --git a/src/main/resources/l10n/JabRef_fa.properties b/src/main/resources/l10n/JabRef_fa.properties index cfec353d983..d6614f7ea65 100644 --- a/src/main/resources/l10n/JabRef_fa.properties +++ b/src/main/resources/l10n/JabRef_fa.properties @@ -254,6 +254,7 @@ Will\ write\ metadata\ to\ the\ PDFs\ linked\ from\ selected\ entries.=فراد + Open\ file=بازکردن پرونده @@ -419,6 +420,7 @@ Check\ integrity=بررسی بی‌نقصی + Cleanup\ entries=تمیز کردن ورودی‌ها diff --git a/src/main/resources/l10n/JabRef_fi.properties b/src/main/resources/l10n/JabRef_fi.properties index d79921567de..9459fb77f89 100644 --- a/src/main/resources/l10n/JabRef_fi.properties +++ b/src/main/resources/l10n/JabRef_fi.properties @@ -447,6 +447,8 @@ About\ JabRef=Tietoja JabRef + + diff --git a/src/main/resources/l10n/JabRef_fr.properties b/src/main/resources/l10n/JabRef_fr.properties index 44205cf0320..fca72ee2645 100644 --- a/src/main/resources/l10n/JabRef_fr.properties +++ b/src/main/resources/l10n/JabRef_fr.properties @@ -466,6 +466,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=La zone marquée ne HTML\ table=Tableau HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Tableau HTML (avec Résumé & BibTeX) + Icon=Icône Ignore=Ignorer @@ -1059,6 +1060,8 @@ Expected\ syntax\ for\ --fetch\='\:'=Syntaxe attendue Library-specific\ file\ directory=Répertoire spécifique au fichier User-specific\ file\ directory=Répertoire de fichiers spécifique à l'utilisateur LaTeX\ file\ directory=Répertoire de fichiers LaTeX +Path\ %0\ could\ not\ be\ resolved.\ Using\ working\ dir.=Le chemin %0 n'a pas pu être trouvé. Utilisation du répertoire de travail. + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=Vous devez saisir une valeur entière dans l'intervalle 1025-65535 Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Complétion automatique des noms uniquement dans le format 'Prénom Nom' diff --git a/src/main/resources/l10n/JabRef_id.properties b/src/main/resources/l10n/JabRef_id.properties index 3808f0f5c8e..6993281a9da 100644 --- a/src/main/resources/l10n/JabRef_id.properties +++ b/src/main/resources/l10n/JabRef_id.properties @@ -330,6 +330,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=Area yang ditandai t HTML\ table=Tabel HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Tabel HTML (dengan Abstrak dan BibTeX) + Icon=Ikon Ignore=Abaikan @@ -751,6 +752,7 @@ Expected\ syntax\ for\ --fetch\='\:'=Sintaks yang diha User-specific\ file\ directory=Direktori berkas khusus pengguna + Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Nama isian otomatis hanya untuk format 'Namadepan Namaakhir' Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Nama isian otomatis hanya untuk format 'Namaakhir, Namadepan' Autocomplete\ names\ in\ both\ formats=Nama isian otomatis untuk kedua format diff --git a/src/main/resources/l10n/JabRef_it.properties b/src/main/resources/l10n/JabRef_it.properties index 9151455d45e..8b0b95e8664 100644 --- a/src/main/resources/l10n/JabRef_it.properties +++ b/src/main/resources/l10n/JabRef_it.properties @@ -466,6 +466,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=L'area marcata non c HTML\ table=Tabella HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Tabella HTML (con Sommario e BibTeX) + Icon=Icona Ignore=Ignora @@ -1059,6 +1060,8 @@ Expected\ syntax\ for\ --fetch\='\:'=Sintassi attesa p Library-specific\ file\ directory=Directory file specifica della libreria User-specific\ file\ directory=Cartella dei file specifica dell'utente LaTeX\ file\ directory=Cartella dei file LaTeX +Path\ %0\ could\ not\ be\ resolved.\ Using\ working\ dir.=Il percorso %0 non può essere risolto. Utilizzo della directory di lavoro. + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=È necessario immettere un valore intero nell'intervallo 1025-65535 Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Autocompletamento dei nomi solo nel formato 'Firstname Lastname' @@ -1644,7 +1647,7 @@ Found\ overlapping\ ranges=Trovati intervalli di sovrapposizione Found\ touching\ ranges=Trovati intervalli di contatto Note\:\ Use\ the\ placeholder\ %DIR%\ for\ the\ location\ of\ the\ opened\ library\ file.=Nota\: Usa il segnaposto %DIR% per la posizione del file libreria aperto. -Error\ occurred\ while\ executing\ the\ command\ "%0".=Si è verificato un errore durante l'esecuzione del comando "%0". +Error\ occurred\ while\ executing\ the\ command\ "%0".=Si è verificato un errore durante l'esecuzione del comando \"%0\". Reformat\ ISSN=Riformatta ISSN Computer\ science=Informatica diff --git a/src/main/resources/l10n/JabRef_ja.properties b/src/main/resources/l10n/JabRef_ja.properties index 7071b2d9662..a464ce87eeb 100644 --- a/src/main/resources/l10n/JabRef_ja.properties +++ b/src/main/resources/l10n/JabRef_ja.properties @@ -390,6 +390,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=標識した領域 HTML\ table=HTMLテーブル HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTMLテーブル(Abstract及びBibTeX付き) + Icon=アイコン Ignore=無視 @@ -911,6 +912,7 @@ Expected\ syntax\ for\ --fetch\='\:'=--fetch\='<取得 User-specific\ file\ directory=ユーザーファイルディレクトリ LaTeX\ file\ directory=LaTeXファイルディレクトリ + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=1025〜65535の間の整数値を入力しなくてはなりません Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=「名 姓」形式の名前のみ自動補完 Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=「姓, 名」形式の名前のみ自動補完 diff --git a/src/main/resources/l10n/JabRef_ko.properties b/src/main/resources/l10n/JabRef_ko.properties index 8e395560bf8..80058bd0ff2 100644 --- a/src/main/resources/l10n/JabRef_ko.properties +++ b/src/main/resources/l10n/JabRef_ko.properties @@ -370,6 +370,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=표시된 영역에 HTML\ table=HTML 테이블 HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTML 테이블(Abstract & BibTeX 포함) + Icon=아이콘 Ignore=무시 @@ -863,6 +864,7 @@ Expected\ syntax\ for\ --fetch\='\:'=--fetch\='<페처 User-specific\ file\ directory=사용자별 파일 디렉토리 LaTeX\ file\ directory=LaTeX 파일 디렉토리 + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=1025-65535 간격으로 정수 값을 입력해야 합니다. Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only='이름 성' 형식의 이름만 자동 완성 Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only='성, 이름' 형식의 이름만 자동 완성 diff --git a/src/main/resources/l10n/JabRef_nl.properties b/src/main/resources/l10n/JabRef_nl.properties index 0cf1de57a77..e23fb5e1a45 100644 --- a/src/main/resources/l10n/JabRef_nl.properties +++ b/src/main/resources/l10n/JabRef_nl.properties @@ -397,6 +397,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=Het gemarkeerde gebi HTML\ table=HTML tabel HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTML tabel (met Abstract & BibTeX) + Icon=Icoon Ignore=Negeren @@ -943,6 +944,7 @@ Expected\ syntax\ for\ --fetch\='\:'=Verwachte syntax User-specific\ file\ directory=Gebruiker-specifieke map LaTeX\ file\ directory=LaTeX bestandsmap + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=U moet een integerwaarde invoeren in het interval 1025-65535 Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Vul namen in de vorm 'Voornaam Achternaam' formaat automatisch in Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Vul namen in de vorm 'Achternaam, Voornaam' formaat automatisch in @@ -1515,7 +1517,7 @@ Found\ overlapping\ ranges=Overlappende bereiken gevonden Found\ touching\ ranges=Aangrenzende bereiken gevonden Note\:\ Use\ the\ placeholder\ %DIR%\ for\ the\ location\ of\ the\ opened\ library\ file.=Opmerking\: Gebruik de placeholder %DIR% voor de locatie van het geopende bibliotheekbestand. -Error\ occurred\ while\ executing\ the\ command\ "%0".=Fout opgetreden tijdens het uitvoeren van de opdracht "%0". +Error\ occurred\ while\ executing\ the\ command\ "%0".=Fout opgetreden tijdens het uitvoeren van de opdracht \"%0\". Reformat\ ISSN=ISSN formatteren Computer\ science=Informatica diff --git a/src/main/resources/l10n/JabRef_no.properties b/src/main/resources/l10n/JabRef_no.properties index d7d2f0aea26..861c41de911 100644 --- a/src/main/resources/l10n/JabRef_no.properties +++ b/src/main/resources/l10n/JabRef_no.properties @@ -305,6 +305,7 @@ Empty\ Underline=Tom understreking HTML\ table=HTML-tabell HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTML-tabell (med Abstract & BibTeX) + Icon=Ikon Ignore=Ignorer @@ -717,6 +718,7 @@ Optional\ fields\ 2=Valgfrie felter 2 User-specific\ file\ directory=Brukerdefinert filkatalog + Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Autokompletter navn i 'Fornavn Etternavn'-format Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Autokompletter navn i 'Etternavn, Fornavn'-format Autocomplete\ names\ in\ both\ formats=Autokompletter navn i begge format diff --git a/src/main/resources/l10n/JabRef_pl.properties b/src/main/resources/l10n/JabRef_pl.properties index 11da1bb4b10..1f6e58bd45e 100644 --- a/src/main/resources/l10n/JabRef_pl.properties +++ b/src/main/resources/l10n/JabRef_pl.properties @@ -424,6 +424,7 @@ Empty\ Highlight=Puste podświetlenie HTML\ table=Tabela HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Tabela HTML (z Abstraktem i BibTeX) + Icon=Ikona Ignore=Ignoruj @@ -990,6 +991,8 @@ Find\ and\ remove\ duplicate\ citation\ keys=Znajdź i usuń zduplikowane klucze Library-specific\ file\ directory=Katalog plików związanych z biblioteką User-specific\ file\ directory=Katalog plików użytkownika LaTeX\ file\ directory=Katalog plików LaTeX +Path\ %0\ could\ not\ be\ resolved.\ Using\ working\ dir.=Ścieżka %0 nie może zostać rozwiązana. Korzystanie z dziennika roboczego. + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=Musisz podać liczbę całkowitą z przedziału 1025-65535 Send=Wyślij diff --git a/src/main/resources/l10n/JabRef_pt.properties b/src/main/resources/l10n/JabRef_pt.properties index 9971e8a6a3a..fc15e565056 100644 --- a/src/main/resources/l10n/JabRef_pt.properties +++ b/src/main/resources/l10n/JabRef_pt.properties @@ -377,6 +377,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=A área assinalada n HTML\ table=Tabela HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Tabela HTML ( com resumo (abstract) & BibTeX) + Icon=Ícone Ignore=Ignorar @@ -851,6 +852,7 @@ Expected\ syntax\ for\ --fetch\='\:'=Sintaxe esperada User-specific\ file\ directory=Diretório de arquivo específico do usuário + Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Autocompletar nome em um formato 'Nome, Sobrenome' apenas Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Autocompletar nomes em um formato 'Sobrenome, Nome' apenas Autocomplete\ names\ in\ both\ formats=Autocompletar nomes em ambos os formatos diff --git a/src/main/resources/l10n/JabRef_pt_BR.properties b/src/main/resources/l10n/JabRef_pt_BR.properties index be1385e4085..649ab0f1901 100644 --- a/src/main/resources/l10n/JabRef_pt_BR.properties +++ b/src/main/resources/l10n/JabRef_pt_BR.properties @@ -465,6 +465,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=A área assinalada n HTML\ table=Tabela HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Tabela HTML ( com resumo (abstract) & BibTeX) + Icon=Ícone Ignore=Ignorar @@ -1057,6 +1058,7 @@ Library-specific\ file\ directory=Diretório de arquivos específicos da bibliot User-specific\ file\ directory=Diretório de arquivo específico do usuário LaTeX\ file\ directory=Diretório de arquivos LaTeX + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=Você deve digitar um valor inteiro no intervalo 1025-65535 Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Autocompletar nome em um formato 'Nome, Sobrenome' apenas Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Autocompletar nomes em um formato 'Sobrenome, Nome' apenas diff --git a/src/main/resources/l10n/JabRef_ru.properties b/src/main/resources/l10n/JabRef_ru.properties index c0b44f18daa..bd6d0e3f2ac 100644 --- a/src/main/resources/l10n/JabRef_ru.properties +++ b/src/main/resources/l10n/JabRef_ru.properties @@ -403,6 +403,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=Выделенная HTML\ table=Таблица HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Таблица HTML (с резюме & BibTeX) + Icon=Значок Ignore=Пропуск @@ -929,6 +930,7 @@ Expected\ syntax\ for\ --fetch\='\:'=Стандарт User-specific\ file\ directory=Пользовательский каталог файлов LaTeX\ file\ directory=Папка с файлами LaTeX + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=Необходимо ввести целое число в интервале 1025-65535 Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Автозавершение имен только для формата 'Имя Фамилия' Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Автозавершение имен только для формата 'Фамилия, Имя' diff --git a/src/main/resources/l10n/JabRef_sv.properties b/src/main/resources/l10n/JabRef_sv.properties index bddb5ad12ce..b67a954b8a1 100644 --- a/src/main/resources/l10n/JabRef_sv.properties +++ b/src/main/resources/l10n/JabRef_sv.properties @@ -355,6 +355,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=Det markerade områd HTML\ table=HTML-tabell HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTML-tabell (med Abstrakt och BibTeX-post) + Icon=Ikon Ignore=Ignorera @@ -775,6 +776,7 @@ Find\ and\ remove\ duplicate\ citation\ keys=Hitta och ta bort dubbla referensny User-specific\ file\ directory=Användarspecifik filmapp + Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Komplettera enbart namn i 'Förnamn Efternamn'-format Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Komplettera enbart namn i 'Efternamn, Förnamn'-format Autocomplete\ names\ in\ both\ formats=Komplettera namn automatiskt i bägge formaten diff --git a/src/main/resources/l10n/JabRef_tl.properties b/src/main/resources/l10n/JabRef_tl.properties index 03eb0060e63..0d98b17e7e3 100644 --- a/src/main/resources/l10n/JabRef_tl.properties +++ b/src/main/resources/l10n/JabRef_tl.properties @@ -304,6 +304,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=Ang may marka na lug HTML\ table=Talahanayan ng HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Talahanayan ng HTML (may Abstract at BibTeX) + Icon=Icon Ignore=I-ignore @@ -716,6 +717,7 @@ Expected\ syntax\ for\ --fetch\='\:'=Inaasahang syntax User-specific\ file\ directory=Direktoryo ng file tukoy sa gumagamit + Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=Awtomatikong isali ang mga pangalan sa format ng 'Firstname Lastname' Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=Awtomatikong isali ang mga pangalan sa format ng 'Firstname Lastname' lamang Autocomplete\ names\ in\ both\ formats=Mga pangalan ng autocomplete sa parehong mga format diff --git a/src/main/resources/l10n/JabRef_tr.properties b/src/main/resources/l10n/JabRef_tr.properties index f6241c68cb3..91e6a2b52ab 100644 --- a/src/main/resources/l10n/JabRef_tr.properties +++ b/src/main/resources/l10n/JabRef_tr.properties @@ -412,6 +412,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=İşaretlenmiş alan HTML\ table=HTML tablosu HTML\ table\ (with\ Abstract\ &\ BibTeX)=(Özet & BibTeX ile) HTML tablosu + Icon=Simge Ignore=Yoksay @@ -965,6 +966,7 @@ Expected\ syntax\ for\ --fetch\='\:'=--fetch için bek User-specific\ file\ directory=Kullanıcıya özel dosya dizini LaTeX\ file\ directory=LaTeX dosya dizini + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=1025-65535 aralığında bir tam sayı değeri girmelisiniz Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=İsimleri yalnızca 'Ad Soyad' biçiminde otomatik tamamla Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=İsimleri yalnızca 'Soyad, Ad' biçiminde otomatik tamamla diff --git a/src/main/resources/l10n/JabRef_uk.properties b/src/main/resources/l10n/JabRef_uk.properties index d06e34795db..14dfc677902 100644 --- a/src/main/resources/l10n/JabRef_uk.properties +++ b/src/main/resources/l10n/JabRef_uk.properties @@ -514,6 +514,8 @@ Current\ value\:\ %0=Поточне значення\: %0 + + diff --git a/src/main/resources/l10n/JabRef_vi.properties b/src/main/resources/l10n/JabRef_vi.properties index e2dfb886985..f8e2d0e6a21 100644 --- a/src/main/resources/l10n/JabRef_vi.properties +++ b/src/main/resources/l10n/JabRef_vi.properties @@ -313,6 +313,7 @@ Empty\ Underline=Xóa phần gạch dưới HTML\ table=Bảng HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Bảng HTML (với Tóm tắt & BibTeX) + Icon=Biểu tượng Ignore=Bỏ qua @@ -714,6 +715,7 @@ Expected\ syntax\ for\ --fetch\='\:'=Cú pháp mong đ User-specific\ file\ directory=Thư mục tập tin của người sử dụng cụ thể + Connecting...=Đang kết nối... diff --git a/src/main/resources/l10n/JabRef_zh_CN.properties b/src/main/resources/l10n/JabRef_zh_CN.properties index d9803cbca90..93e44eabe76 100644 --- a/src/main/resources/l10n/JabRef_zh_CN.properties +++ b/src/main/resources/l10n/JabRef_zh_CN.properties @@ -397,6 +397,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=标注区域不包 HTML\ table=HTML 表 HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTML 表(包含摘要和 BibTeX) + Icon=图标 Ignore=忽略 @@ -934,6 +935,7 @@ Expected\ syntax\ for\ --fetch\='\:'=期望的语法 - User-specific\ file\ directory=用户指定的文件目录 LaTeX\ file\ directory=LaTeX 文件目录 + You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=请输入一个 1025-65535 之间的整数值 Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=仅自动补全形如 'Firstname Lastname' 格式的姓名 Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=仅自动补全形如 'Lastname, Firstname' 格式的姓名 @@ -1504,7 +1506,7 @@ Found\ overlapping\ ranges=Found overlapping ranges Found\ touching\ ranges=Found touching ranges Note\:\ Use\ the\ placeholder\ %DIR%\ for\ the\ location\ of\ the\ opened\ library\ file.=Note\: Use the placeholder %DIR% for the location of the opened library file. -Error\ occurred\ while\ executing\ the\ command\ "%0".=执行 "%0" 命令时出错 +Error\ occurred\ while\ executing\ the\ command\ "%0".=执行 \"%0\" 命令时出错 Reformat\ ISSN=重新格式化 ISSN Computer\ science=计算机科学 diff --git a/src/main/resources/l10n/JabRef_zh_TW.properties b/src/main/resources/l10n/JabRef_zh_TW.properties index 706f0e1f4b0..17435bc03a6 100644 --- a/src/main/resources/l10n/JabRef_zh_TW.properties +++ b/src/main/resources/l10n/JabRef_zh_TW.properties @@ -307,6 +307,7 @@ Empty\ Underline=清除底線 HTML\ table=HTML 表格 HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTML 表格(包含摘要和 BibTeX) + Icon=圖示 Ignore=忽略 @@ -628,6 +629,7 @@ Find\ and\ remove\ duplicate\ citation\ keys=查找並移除重複的引用鍵 ( LaTeX\ file\ directory=LaTeX 檔案目錄 + Autocomplete\ names\ in\ 'Firstname\ Lastname'\ format\ only=僅自動補全形如 'Firstname Lastname' 的姓名格式 Autocomplete\ names\ in\ 'Lastname,\ Firstname'\ format\ only=僅自動補全形如 'Lastname, Firstname' 的姓名格式 Autocomplete\ names\ in\ both\ formats=自動補全兩種姓名格式 From c93a547e3e338efe8f8e367256d1f1eaf4d187cc Mon Sep 17 00:00:00 2001 From: Christoph Date: Mon, 25 Nov 2024 23:22:50 +0100 Subject: [PATCH 13/20] Fix file adding (#12237) Fixes https://github.com/JabRef/jabref/issues/12236 Follow up to https://github.com/JabRef/jabref/pull/12219 --- .../jabref/gui/linkedfile/LinkedFileEditDialogViewModel.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogViewModel.java b/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogViewModel.java index 954c53a84b3..c360fe6e90f 100644 --- a/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogViewModel.java +++ b/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogViewModel.java @@ -119,6 +119,10 @@ void checkForBadFileNameAndAdd(Path fileToAdd) { dialogService.showErrorDialogAndWait(ex); } } + } else { + link.set(relativize(fileToAdd)); + filePreferences.setWorkingDirectory(fileToAdd); + setExternalFileTypeByExtension(link.getValueSafe()); } } From b02b6ff09e8f084badf1e6408ee62b4da05945d7 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 26 Nov 2024 00:02:46 +0100 Subject: [PATCH 14/20] Add CHANGELOG.md entry for https://github.com/JabRef/jabref/pull/12067 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8227aece62..2cfe8a65db8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added automatic browser extension install on Windows for Chrome and Edge. [#6076](https://github.com/JabRef/jabref/issues/6076) - We added support to automatically open a `.bib` file in the current/parent folder if no other library is opened. [koppor#377](https://github.com/koppor/jabref/issues/377) - We added a search bar for filtering keyboard shortcuts. [#11686](https://github.com/JabRef/jabref/issues/11686) +- We added new modifiers `camel_case`, `camel_case_n`, `short_title`, and `very_short_title` for the [citation key generator](https://docs.jabref.org/setup/citationkeypatterns). [#11367](https://github.com/JabRef/jabref/issues/11367) - By double clicking on a local citation in the Citation Relations Tab you can now jump the linked entry. [#11955](https://github.com/JabRef/jabref/pull/11955) - We use the menu icon for background tasks as a progress indicator to visualise an import's progress when dragging and dropping several PDF files into the main table. [#12072](https://github.com/JabRef/jabref/pull/12072) From a86adbb9fd2db5c40ce279538f5bb8a297b45b09 Mon Sep 17 00:00:00 2001 From: Mtjpp <58153153+Mtjpp@users.noreply.github.com> Date: Tue, 26 Nov 2024 00:03:35 +0100 Subject: [PATCH 15/20] Implementation of new modifiers for issue #11367 (#12067) * Extended modifiers and added options for: CamelN Camel ShortTitleFormatter VeryShortTitleFormatter * Extended modifiers and added options for: CamelN Camel ShortTitleFormatter VeryShortTitleFormatter * Merge remote-tracking branch 'origin/main' # Conflicts: # src/main/java/org/jabref/logic/formatter/casechanger/CamelFormatter.java # src/main/java/org/jabref/logic/formatter/casechanger/CamelNFormatter.java # src/main/java/org/jabref/logic/formatter/casechanger/ShortTitleFormatter.java # src/main/java/org/jabref/logic/formatter/casechanger/VeryShortTitleFormatter.java * Small fix for get key and get name in camel formatters * Changed tests to have only one location inside BracketedPatternTest.java --- .../jabref/logic/formatter/Formatters.java | 27 +++++++- .../formatter/casechanger/CamelFormatter.java | 42 +++++++++++ .../casechanger/CamelNFormatter.java | 48 +++++++++++++ .../casechanger/ShortTitleFormatter.java | 43 ++++++++++++ .../casechanger/VeryShortTitleFormatter.java | 43 ++++++++++++ src/main/resources/l10n/JabRef_en.properties | 8 +++ .../BracketedPatternTest.java | 69 +++++++++++++++++++ 7 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/jabref/logic/formatter/casechanger/CamelFormatter.java create mode 100644 src/main/java/org/jabref/logic/formatter/casechanger/CamelNFormatter.java create mode 100644 src/main/java/org/jabref/logic/formatter/casechanger/ShortTitleFormatter.java create mode 100644 src/main/java/org/jabref/logic/formatter/casechanger/VeryShortTitleFormatter.java diff --git a/src/main/java/org/jabref/logic/formatter/Formatters.java b/src/main/java/org/jabref/logic/formatter/Formatters.java index fb827f9ab8e..a52c4437848 100644 --- a/src/main/java/org/jabref/logic/formatter/Formatters.java +++ b/src/main/java/org/jabref/logic/formatter/Formatters.java @@ -30,12 +30,16 @@ import org.jabref.logic.formatter.bibtexfields.ShortenDOIFormatter; import org.jabref.logic.formatter.bibtexfields.UnicodeToLatexFormatter; import org.jabref.logic.formatter.bibtexfields.UnitsToLatexFormatter; +import org.jabref.logic.formatter.casechanger.CamelFormatter; +import org.jabref.logic.formatter.casechanger.CamelNFormatter; import org.jabref.logic.formatter.casechanger.CapitalizeFormatter; import org.jabref.logic.formatter.casechanger.LowerCaseFormatter; import org.jabref.logic.formatter.casechanger.SentenceCaseFormatter; +import org.jabref.logic.formatter.casechanger.ShortTitleFormatter; import org.jabref.logic.formatter.casechanger.TitleCaseFormatter; import org.jabref.logic.formatter.casechanger.UnprotectTermsFormatter; import org.jabref.logic.formatter.casechanger.UpperCaseFormatter; +import org.jabref.logic.formatter.casechanger.VeryShortTitleFormatter; import org.jabref.logic.formatter.minifier.MinifyNameListFormatter; import org.jabref.logic.formatter.minifier.TruncateFormatter; import org.jabref.logic.layout.format.LatexToUnicodeFormatter; @@ -96,11 +100,20 @@ public static List getOthers() { ); } + public static List getTitleChangers() { + return Arrays.asList( + new VeryShortTitleFormatter(), + new ShortTitleFormatter(), + new CamelFormatter() + ); + } + public static List getAll() { List all = new ArrayList<>(); all.addAll(getConverters()); all.addAll(getCaseChangers()); all.addAll(getOthers()); + all.addAll(getTitleChangers()); return all; } @@ -123,9 +136,21 @@ public static Optional getFormatterForModifier(String modifier) { return Optional.of(new TitleCaseFormatter()); case "sentencecase": return Optional.of(new SentenceCaseFormatter()); + case "veryshorttitle": + return Optional.of(new VeryShortTitleFormatter()); + case "shorttitle": + return Optional.of(new ShortTitleFormatter()); } - if (modifier.startsWith(RegexFormatter.KEY)) { + if (modifier.contains("camel")) { + modifier = modifier.replace("camel", ""); + if (modifier.isEmpty()) { + return Optional.of(new CamelFormatter()); + } else { + int length = Integer.parseInt(modifier); + return Optional.of(new CamelNFormatter(length)); + } + } else if (modifier.startsWith(RegexFormatter.KEY)) { String regex = modifier.substring(RegexFormatter.KEY.length()); return Optional.of(new RegexFormatter(regex)); } else if (TRUNCATE_PATTERN.matcher(modifier).matches()) { diff --git a/src/main/java/org/jabref/logic/formatter/casechanger/CamelFormatter.java b/src/main/java/org/jabref/logic/formatter/casechanger/CamelFormatter.java new file mode 100644 index 00000000000..2d314fc440e --- /dev/null +++ b/src/main/java/org/jabref/logic/formatter/casechanger/CamelFormatter.java @@ -0,0 +1,42 @@ +package org.jabref.logic.formatter.casechanger; + +import java.util.stream.Collectors; + +import org.jabref.logic.cleanup.Formatter; +import org.jabref.logic.l10n.Localization; + +public class CamelFormatter extends Formatter { + + @Override + public String getName() { + return Localization.lang("Camel case"); + } + + @Override + public String getKey() { + return "camel_case"; + } + + @Override + public String format(String input) { + Title title = new Title(input); + + return title.getWords().stream() + .map(Word -> { + Word.toUpperFirst(); + return Word.toString(); + }) + .collect(Collectors.joining("")); + } + + @Override + public String getDescription() { + return Localization.lang( + "Returns capitalized and concatenated title."); + } + + @Override + public String getExampleInput() { + return "this is example input"; + } +} diff --git a/src/main/java/org/jabref/logic/formatter/casechanger/CamelNFormatter.java b/src/main/java/org/jabref/logic/formatter/casechanger/CamelNFormatter.java new file mode 100644 index 00000000000..d3e2efe86ca --- /dev/null +++ b/src/main/java/org/jabref/logic/formatter/casechanger/CamelNFormatter.java @@ -0,0 +1,48 @@ +package org.jabref.logic.formatter.casechanger; + +import java.util.stream.Collectors; + +import org.jabref.logic.cleanup.Formatter; +import org.jabref.logic.l10n.Localization; + +public class CamelNFormatter extends Formatter { + private final int length; + + public CamelNFormatter(int length) { + this.length = length; + } + + @Override + public String getName() { + return Localization.lang("Camel case - n letters max"); + } + + @Override + public String getKey() { + return "camel_case_n"; + } + + @Override + public String format(String input) { + Title title = new Title(input); + + return title.getWords().stream() + .map(Word -> { + Word.toUpperFirst(); + return Word.toString(); + }) + .limit(length) + .collect(Collectors.joining("")); + } + + @Override + public String getDescription() { + return Localization.lang( + "Returns capitalized and concatenated title to N length."); + } + + @Override + public String getExampleInput() { + return "this is camel formatter"; + } +} diff --git a/src/main/java/org/jabref/logic/formatter/casechanger/ShortTitleFormatter.java b/src/main/java/org/jabref/logic/formatter/casechanger/ShortTitleFormatter.java new file mode 100644 index 00000000000..6e9da157286 --- /dev/null +++ b/src/main/java/org/jabref/logic/formatter/casechanger/ShortTitleFormatter.java @@ -0,0 +1,43 @@ +package org.jabref.logic.formatter.casechanger; + +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.jabref.logic.cleanup.Formatter; +import org.jabref.logic.l10n.Localization; + +public class ShortTitleFormatter extends Formatter { + + @Override + public String getName() { + return Localization.lang("Short title"); + } + + @Override + public String getKey() { + return "short_title"; + } + + @Override + public String format(String input) { + Title title = new Title(input); + + return title.getWords().stream() + .filter(Predicate.not( + Word::isSmallerWord)) + .map(Word::toString) + .limit(3) + .collect(Collectors.joining(" ")); + } + + @Override + public String getDescription() { + return Localization.lang( + "Returns first 3 words of the title ignoring any function words."); + } + + @Override + public String getExampleInput() { + return "This is a short title"; + } +} diff --git a/src/main/java/org/jabref/logic/formatter/casechanger/VeryShortTitleFormatter.java b/src/main/java/org/jabref/logic/formatter/casechanger/VeryShortTitleFormatter.java new file mode 100644 index 00000000000..20332909f13 --- /dev/null +++ b/src/main/java/org/jabref/logic/formatter/casechanger/VeryShortTitleFormatter.java @@ -0,0 +1,43 @@ +package org.jabref.logic.formatter.casechanger; + +import java.util.Optional; +import java.util.function.Predicate; + +import org.jabref.logic.cleanup.Formatter; +import org.jabref.logic.l10n.Localization; + +public class VeryShortTitleFormatter extends Formatter { + + @Override + public String getName() { + return Localization.lang("Very short title"); + } + + @Override + public String getKey() { + return "very_short_title"; + } + + @Override + public String format(String input) { + Title title = new Title(input); + + Optional resultTitle = title.getWords().stream() + .filter(Predicate.not( + Word::isSmallerWord)) + .findFirst(); + + return resultTitle.map(Word::toString).orElse(""); + } + + @Override + public String getDescription() { + return Localization.lang( + "Returns first word of the title ignoring any function words."); + } + + @Override + public String getExampleInput() { + return "A very short title"; + } +} diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index cadce1ba583..f0842f5bab4 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1489,6 +1489,14 @@ HTML\ to\ LaTeX=HTML to LaTeX LaTeX\ cleanup=LaTeX cleanup LaTeX\ to\ Unicode=LaTeX to Unicode lower\ case=lower case +Camel\ case=Camel case +Camel\ case\ -\ n\ letters\ max= Camel case - n letters max +Very\ short\ title=Very short title +Short\ title=Short title +Returns\ first\ word\ of\ the\ title\ ignoring\ any\ function\ words.=Returns first word of the title ignoring any function words. +Returns\ first\ 3\ words\ of\ the\ title\ ignoring\ any\ function\ words.=Returns first 3 words of the title ignoring any function words. +Returns\ capitalized\ and\ concatenated\ title\ to\ N\ length.=Returns capitalized and concatenated title to N length. +Returns\ capitalized\ and\ concatenated\ title.=Returns capitalized and concatenated title. Minify\ list\ of\ person\ names=Minify list of person names Normalize\ date=Normalize date Normalize\ en\ dashes=Normalize en dashes diff --git a/src/test/java/org/jabref/logic/citationkeypattern/BracketedPatternTest.java b/src/test/java/org/jabref/logic/citationkeypattern/BracketedPatternTest.java index e7b9e643db5..111858ea386 100644 --- a/src/test/java/org/jabref/logic/citationkeypattern/BracketedPatternTest.java +++ b/src/test/java/org/jabref/logic/citationkeypattern/BracketedPatternTest.java @@ -584,6 +584,75 @@ void expandBracketsDoesNotTruncateWithoutAnArgumentToTruncateModifier() { BracketedPattern.expandBrackets("[fulltitle:truncate]", ';', dbentry, database)); } + /** + * Test the [:camel] modifier + */ + @ParameterizedTest + @CsvSource({ + "'CamelTitleFormatter', 'Camel Title Formatter'", + "'CamelTitleFormatter', 'CAMEL TITLE FORMATTER'", + "'CamelTitleFormatter', 'camel title formatter'", + "'CamelTitleFormatter', 'cAMEL tITLE fORMATTER'", + "'C', 'c'" + }) + + void expandBracketsCamelTitleModifier(String expectedCitationKey, String title) { + BibEntry bibEntry = new BibEntry() + .withField(StandardField.TITLE, title); + assertEquals(expectedCitationKey, + BracketedPattern.expandBrackets("[title:camel]", ';', bibEntry, null)); + } + + /** + * Test the [:veryshorttitle] modifier + */ + @ParameterizedTest + @CsvSource({ + "'Very', 'A very short title'", + "'V', 'V'", + "'V', 'A v'" + }) + + void expandBracketsVeryShortTitleModifier(String expectedCitationKey, String title) { + BibEntry bibEntry = new BibEntry() + .withField(StandardField.TITLE, title); + assertEquals(expectedCitationKey, + BracketedPattern.expandBrackets("[title:veryshorttitle]", ';', bibEntry, null)); + } + + /** + * Test the [:shorttitle] modifier + */ + @ParameterizedTest + @CsvSource({ + "'Very Short Title', 'A very short title'", + "'Short Title', 'Short title'", + "'Title', 'A title'", + "'Title', 'A Title'" + }) + + void expandBracketsShortTitleModifier(String expectedCitationKey, String title) { + BibEntry bibEntry = new BibEntry() + .withField(StandardField.TITLE, title); + assertEquals(expectedCitationKey, + BracketedPattern.expandBrackets("[title:shorttitle]", ';', bibEntry, null)); + } + + /** + * Test the [:camelN] modifier + */ + @Test + void expandBracketsCamelNModifier() { + BibEntry bibEntry = new BibEntry() + .withField(StandardField.TITLE, "Open Source Software And The Private Collective Innovation Model Issues"); + assertEquals("Open", + BracketedPattern.expandBrackets("[title:camel1]", ';', bibEntry, null)); + assertEquals("OpenSourceSoftwareAnd", + BracketedPattern.expandBrackets("[title:camel4]", ';', bibEntry, null)); + assertEquals("OpenSourceSoftwareAndThePrivateCollectiveInnovationModelIssues", + BracketedPattern.expandBrackets("[title:camel10]", ';', bibEntry, null)); + } + /** * Test the [camelN] title marker. */ From 77907c8d57032f18b540690519fca406b75407fd Mon Sep 17 00:00:00 2001 From: leaf-soba Date: Tue, 26 Nov 2024 08:47:20 +0900 Subject: [PATCH 16/20] find the title from second page (#12157) * find the title from second page find the title from second page * fix method name fix method name * add a unit test file with a test cover page add a unit test file with a test cover page * change unit test cover page file change unit test cover page file * fix the pdf file with consistency cover fix the pdf file with consistency cover --- .../fileformat/PdfContentImporter.java | 6 +++--- .../fileformat/PdfContentImporterTest.java | 1 + .../resources/pdfs/IEEE/ieee-paper-cover.pdf | Bin 0 -> 116769 bytes 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 src/test/resources/pdfs/IEEE/ieee-paper-cover.pdf diff --git a/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java index ca266e8c895..7d43be038a4 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java @@ -218,7 +218,7 @@ public ParserResult importDatabase(Path filePath) { private static String extractTitleFromDocument(PDDocument document) throws IOException { TitleExtractorByFontSize stripper = new TitleExtractorByFontSize(); - return stripper.getTitleFromFirstPage(document); + return stripper.getTitle(document); } private static class TitleExtractorByFontSize extends PDFTextStripper { @@ -230,9 +230,9 @@ public TitleExtractorByFontSize() { this.textPositionsList = new ArrayList<>(); } - public String getTitleFromFirstPage(PDDocument document) throws IOException { + public String getTitle(PDDocument document) throws IOException { this.setStartPage(1); - this.setEndPage(1); + this.setEndPage(2); this.writeText(document, new StringWriter()); return findLargestFontText(textPositionsList); } diff --git a/src/test/java/org/jabref/logic/importer/fileformat/PdfContentImporterTest.java b/src/test/java/org/jabref/logic/importer/fileformat/PdfContentImporterTest.java index de6f276a015..318cb858508 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/PdfContentImporterTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/PdfContentImporterTest.java @@ -140,6 +140,7 @@ void pdfTitleExtraction(String expectedTitle, String filePath) throws Exception private static Stream providePdfData() { return Stream.of( Arguments.of("Fundamentals of Distributed Computing: A Practical Tour of Vector Clock Systems", "/pdfs/PdfContentImporter/Baldoni2002.pdf"), + Arguments.of("JabRef Example for Reference Parsing", "/pdfs/IEEE/ieee-paper-cover.pdf"), Arguments.of("On How We Can Teach – Exploring New Ways in Professional Software Development for Students", "/pdfs/PdfContentImporter/Kriha2018.pdf"), Arguments.of("JabRef Example for Reference Parsing", "/pdfs/IEEE/ieee-paper.pdf"), Arguments.of("Paper Title", "/org/jabref/logic/importer/util/LNCS-minimal.pdf"), diff --git a/src/test/resources/pdfs/IEEE/ieee-paper-cover.pdf b/src/test/resources/pdfs/IEEE/ieee-paper-cover.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4c1ca1ebde2ee883fcafd4abd23480c6dfb9b2a9 GIT binary patch literal 116769 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 Date: Tue, 26 Nov 2024 23:39:13 +0530 Subject: [PATCH 17/20] Add entry (#12239) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cfe8a65db8..342df6c1314 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added new modifiers `camel_case`, `camel_case_n`, `short_title`, and `very_short_title` for the [citation key generator](https://docs.jabref.org/setup/citationkeypatterns). [#11367](https://github.com/JabRef/jabref/issues/11367) - By double clicking on a local citation in the Citation Relations Tab you can now jump the linked entry. [#11955](https://github.com/JabRef/jabref/pull/11955) - We use the menu icon for background tasks as a progress indicator to visualise an import's progress when dragging and dropping several PDF files into the main table. [#12072](https://github.com/JabRef/jabref/pull/12072) +- The PDF content importer now supports importing title from upto the second page of the PDF. [#12139](https://github.com/JabRef/jabref/issues/12139) ### Changed From 2549ab6a9758cda1952fdb3ab86b1df8e4efad44 Mon Sep 17 00:00:00 2001 From: Christoph Date: Tue, 26 Nov 2024 22:43:13 +0100 Subject: [PATCH 18/20] New Crowdin updates (#12242) * New translations jabref_en.properties (Portuguese, Brazilian) * New translations jabref_en.properties (French) * New translations jabref_en.properties (German) * New translations jabref_en.properties (Italian) * New translations jabref_en.properties (Polish) --- src/main/resources/l10n/JabRef_de.properties | 12 +++++++++++- src/main/resources/l10n/JabRef_fr.properties | 9 +++++++++ src/main/resources/l10n/JabRef_it.properties | 9 +++++++++ src/main/resources/l10n/JabRef_pl.properties | 5 +++++ src/main/resources/l10n/JabRef_pt_BR.properties | 10 ++++++++++ 5 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/main/resources/l10n/JabRef_de.properties b/src/main/resources/l10n/JabRef_de.properties index b1c3341ab87..8041c1a4c80 100644 --- a/src/main/resources/l10n/JabRef_de.properties +++ b/src/main/resources/l10n/JabRef_de.properties @@ -103,7 +103,7 @@ Available\ import\ formats=Verfügbare Importformate Show\ BibTeX\ source=BibTeX-Quellcode anzeigen Show/edit\ %0\ source=%0-Quelltext anzeigen/editieren -Background\ tasks=Hintergrund Job +Background\ tasks=Hintergrundprozesse Background\ tasks\ are\ running=Hintergrundprozesse laufen @@ -466,6 +466,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=Der markierte Bereic HTML\ table=HTML-Tabelle HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTML-Tabelle (mit Abstract & BibTeX) +Markdown\ titles=Titel im Markdown-Format Icon=Icon @@ -1060,6 +1061,7 @@ Expected\ syntax\ for\ --fetch\='\:'=Erwartete Syntax Library-specific\ file\ directory=Bibliothekseigener Dateipfad User-specific\ file\ directory=Benutzerdefiniertes Dateiverzeichnis LaTeX\ file\ directory=LaTeX-Dateiverzeichnis +Path\ %0\ could\ not\ be\ resolved.\ Using\ working\ dir.=Pfad %0 konnte nicht gefunden werden. Verwende Arbeitsverzeichnis. You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=Sie müssen einen Zahlwert zwischen 1025 und 65535 eintragen @@ -1487,6 +1489,14 @@ HTML\ to\ LaTeX=HTML zu LaTeX LaTeX\ cleanup=LaTeX Aufräumen LaTeX\ to\ Unicode=LaTeX zu Unicode lower\ case=kleinschreibung +Camel\ case=CamelCase +Camel\ case\ -\ n\ letters\ max= CamelCase - max. n Buchstaben +Very\ short\ title=Sehr kurzer Titel +Short\ title=Kurzer Titel +Returns\ first\ word\ of\ the\ title\ ignoring\ any\ function\ words.=Liefert als Ausgabe das erste Wort des Titels, wobei alle Funktionswörter ignoriert werden. +Returns\ first\ 3\ words\ of\ the\ title\ ignoring\ any\ function\ words.=Liefert als Ausgabe die ersten 3 Wörter des Titels, wobei alle Funktionswörter ignoriert werden. +Returns\ capitalized\ and\ concatenated\ title\ to\ N\ length.=Liefert als Ausgabe den großgeschriebenen und verketteten Titel, gekürzt auf Länge N. +Returns\ capitalized\ and\ concatenated\ title.=Liefert als Ausgabe den großgeschriebenen und verketteten Titel. Minify\ list\ of\ person\ names=Reduziere Liste der Personen Normalize\ date=Normalisiere Datum Normalize\ en\ dashes=Gedankenstriche normalisieren diff --git a/src/main/resources/l10n/JabRef_fr.properties b/src/main/resources/l10n/JabRef_fr.properties index fca72ee2645..5aa3e510fd9 100644 --- a/src/main/resources/l10n/JabRef_fr.properties +++ b/src/main/resources/l10n/JabRef_fr.properties @@ -466,6 +466,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=La zone marquée ne HTML\ table=Tableau HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Tableau HTML (avec Résumé & BibTeX) +Markdown\ titles=Titres Markdown Icon=Icône @@ -1488,6 +1489,14 @@ HTML\ to\ LaTeX=HTML vers LaTeX LaTeX\ cleanup=Nettoyage LaTeX LaTeX\ to\ Unicode=LaTeX vers Unicode lower\ case=minuscules +Camel\ case=Casse chameau +Camel\ case\ -\ n\ letters\ max= Casse chameau - n lettres max +Very\ short\ title=Titre très court +Short\ title=Titre court +Returns\ first\ word\ of\ the\ title\ ignoring\ any\ function\ words.=Renvoie le premier mot du titre en ignorant tous les mots de liaison. +Returns\ first\ 3\ words\ of\ the\ title\ ignoring\ any\ function\ words.=Renvoie les 3 premiers mots du titre en ignorant tous les mots de liaison. +Returns\ capitalized\ and\ concatenated\ title\ to\ N\ length.=Renvoie le titre en majuscule et concaténé à la longueur N. +Returns\ capitalized\ and\ concatenated\ title.=Retourne le titre en majuscule et concaténé. Minify\ list\ of\ person\ names=Raccourcir la liste des noms de personne Normalize\ date=Harmoniser la date Normalize\ en\ dashes=Harmoniser les tirets cadratins diff --git a/src/main/resources/l10n/JabRef_it.properties b/src/main/resources/l10n/JabRef_it.properties index 8b0b95e8664..21107c8ac9d 100644 --- a/src/main/resources/l10n/JabRef_it.properties +++ b/src/main/resources/l10n/JabRef_it.properties @@ -466,6 +466,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=L'area marcata non c HTML\ table=Tabella HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Tabella HTML (con Sommario e BibTeX) +Markdown\ titles=Titoli Markdown Icon=Icona @@ -1488,6 +1489,14 @@ HTML\ to\ LaTeX=Da HTML a LaTeX LaTeX\ cleanup=Pulisci il LaTeX LaTeX\ to\ Unicode=Da LaTeX a Unicode lower\ case=minuscolo +Camel\ case=Notazione a cammello +Camel\ case\ -\ n\ letters\ max= Notazione a cammello\nNotazione a cammello - n massimo di lettere +Very\ short\ title=Titolo molto breve +Short\ title=Titolo breve +Returns\ first\ word\ of\ the\ title\ ignoring\ any\ function\ words.=Restituisce la prima parola del titolo ignorando qualsiasi parola di funzione. +Returns\ first\ 3\ words\ of\ the\ title\ ignoring\ any\ function\ words.=Restituisce le prime 3 parole del titolo ignorando qualsiasi parola di funzione. +Returns\ capitalized\ and\ concatenated\ title\ to\ N\ length.=Restituisce il titolo in maiuscolo e concatenato a lunghezza N. +Returns\ capitalized\ and\ concatenated\ title.=Restituisce il titolo in maiuscolo e concatenato. Minify\ list\ of\ person\ names=Riduci la lista di nomi di persona Normalize\ date=Normalizza la data Normalize\ en\ dashes=Normalizza trattini brevi diff --git a/src/main/resources/l10n/JabRef_pl.properties b/src/main/resources/l10n/JabRef_pl.properties index 1f6e58bd45e..ec569378375 100644 --- a/src/main/resources/l10n/JabRef_pl.properties +++ b/src/main/resources/l10n/JabRef_pl.properties @@ -424,6 +424,7 @@ Empty\ Highlight=Puste podświetlenie HTML\ table=Tabela HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Tabela HTML (z Abstraktem i BibTeX) +Markdown\ titles=Tytuły Markdown Icon=Ikona @@ -1309,6 +1310,10 @@ Converts\ units\ to\ LaTeX\ formatting.=Konwertuje jednostki na formatowanie LaT HTML\ to\ LaTeX=HTML do LaTeX LaTeX\ cleanup=Czyszczenie LaTeX lower\ case=małe litery +Camel\ case=Notacja camelCase +Camel\ case\ -\ n\ letters\ max= Notacja camelCase - maksymalnie n liter +Very\ short\ title=Tytuł bardzo skrócony +Short\ title=Tytuł skrócony Normalize\ date=Normalizuj datę Normalize\ month=Normalizuj miesiąc Remove\ word\ enclosing\ braces=Usuń nawiasy otaczające słowo diff --git a/src/main/resources/l10n/JabRef_pt_BR.properties b/src/main/resources/l10n/JabRef_pt_BR.properties index 649ab0f1901..42f2473b5af 100644 --- a/src/main/resources/l10n/JabRef_pt_BR.properties +++ b/src/main/resources/l10n/JabRef_pt_BR.properties @@ -465,6 +465,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text\!=A área assinalada n HTML\ table=Tabela HTML HTML\ table\ (with\ Abstract\ &\ BibTeX)=Tabela HTML ( com resumo (abstract) & BibTeX) +Markdown\ titles=Títulos Markdown Icon=Ícone @@ -1057,6 +1058,7 @@ Expected\ syntax\ for\ --fetch\='\:'=Sintaxe esperada Library-specific\ file\ directory=Diretório de arquivos específicos da biblioteca User-specific\ file\ directory=Diretório de arquivo específico do usuário LaTeX\ file\ directory=Diretório de arquivos LaTeX +Path\ %0\ could\ not\ be\ resolved.\ Using\ working\ dir.=Caminho %0 não pode ser resolvido. Usando o diretório de trabalho. You\ must\ enter\ an\ integer\ value\ in\ the\ interval\ 1025-65535=Você deve digitar um valor inteiro no intervalo 1025-65535 @@ -1484,6 +1486,14 @@ HTML\ to\ LaTeX=HTML para LaTeX LaTeX\ cleanup=Limpar LaTeX (cleanup) LaTeX\ to\ Unicode=LaTeX para Unicode lower\ case=minúsculas +Camel\ case=Camel case +Camel\ case\ -\ n\ letters\ max= Camel case - máx n letras +Very\ short\ title=Título muito curto +Short\ title=Título curto +Returns\ first\ word\ of\ the\ title\ ignoring\ any\ function\ words.=Retorna a primeira palavra do título ignorando quaisquer palavras de função. +Returns\ first\ 3\ words\ of\ the\ title\ ignoring\ any\ function\ words.=Retorna as primeiras 3 palavras do título ignorando quaisquer palavras de função. +Returns\ capitalized\ and\ concatenated\ title\ to\ N\ length.=Retorna capitalizado e concatenado o título até o comprimento N. +Returns\ capitalized\ and\ concatenated\ title.=Retorna capitalizado e concatenado o título. Minify\ list\ of\ person\ names=Minimizar lista de nomes de pessoas Normalize\ date=Normalizar a data Normalize\ en\ dashes=Normalizar "en dashes" From 04c0df9ebd162cdac0e152eea59f803666c7cc15 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Thu, 28 Nov 2024 21:45:25 +0100 Subject: [PATCH 19/20] Add config for moderne (#12238) * Add config for moderne * Move .gitpod down --- .moderne/moderne.yml | 3 +++ build.gradle | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .moderne/moderne.yml diff --git a/.moderne/moderne.yml b/.moderne/moderne.yml new file mode 100644 index 00000000000..e90c2b39622 --- /dev/null +++ b/.moderne/moderne.yml @@ -0,0 +1,3 @@ +specs: specs.moderne.ai/v1/cli +java: + selectedJdk: '23' diff --git a/build.gradle b/build.gradle index 6d798bb3bf3..7b5d4c9a821 100644 --- a/build.gradle +++ b/build.gradle @@ -54,13 +54,14 @@ java { toolchain { // If this is updated, also update - // - .gitpod.Dockerfile + // - build.gradle -> jacoco -> toolVersion (because JaCoCo does not support newest JDK out of the box. Check versions at https://www.jacoco.org/jacoco/trunk/doc/changes.html) // - .devcontainer/devcontainer.json#L34 and + // - .gitpod.Dockerfile + // - .moderne/moderne.yml // - .github/workflows/deployment*.yml // - .github/workflows/tests*.yml // - .github/workflows/update-gradle-wrapper.yml // - docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md - // - build.gradle -> jacoco -> toolVersion (because JaCoCo does not support newest JDK out of the box. Check versions at https://www.jacoco.org/jacoco/trunk/doc/changes.html) languageVersion = JavaLanguageVersion.of(23) // See https://docs.gradle.org/current/javadoc/org/gradle/jvm/toolchain/JvmVendorSpec.html for a full list // vendor = JvmVendorSpec.AMAZON From 37593bfe04c47547bcb3883185be671b5a2d7834 Mon Sep 17 00:00:00 2001 From: Amir Mullagaliev <138519917+mulla028@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:08:25 -0500 Subject: [PATCH 20/20] Click on entry at "Check integrity" focus bug fixed (#12022) * Two lines added: comment and the getScene().getWindow().requestFocus(); to make sure that focus goes to the field even when dialog window is opened * Focus bug added to the Changelog's Fix field * setFocusToField() fixed focus for JournalTitle Field * Checkstyle Applied * Added line in setFocusToField() to force opening every tab before selecting one --- CHANGELOG.md | 1 + .../jabref/gui/entryeditor/EntryEditor.java | 21 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 342df6c1314..0cc4fbe4094 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -123,6 +123,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where trying to open a library from a failed mounted directory on Mac would cause an error. [#10548](https://github.com/JabRef/jabref/issues/10548) - We fixed an issue when the preview was out of sync. [#9172](https://github.com/JabRef/jabref/issues/9172) - We fixed an issue where identifier paste couldn't work with Unicode REPLACEMENT CHARACTER. [#11986](https://github.com/JabRef/jabref/issues/11986) +- We fixed an issue when click on entry at "Check Integrity" wasn't properly focusing the entry and field. [#11997](https://github.com/JabRef/jabref/issues/11997) ### Removed diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index 3a2bd6f2258..ab1bb640dfe 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -13,6 +13,7 @@ import java.util.SortedSet; import java.util.stream.Collectors; +import javafx.application.Platform; import javafx.fxml.FXML; import javafx.geometry.Side; import javafx.scene.control.Button; @@ -61,6 +62,7 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.entry.EntryConverter; import org.jabref.model.entry.field.Field; import org.jabref.model.util.DirectoryMonitorManager; import org.jabref.model.util.FileUpdateMonitor; @@ -463,11 +465,26 @@ private void fetchAndMerge(EntryBasedFetcher fetcher) { public void setFocusToField(Field field) { UiTaskExecutor.runInJavaFXThread(() -> { + Field actualField = field; + boolean fieldFound = false; for (Tab tab : tabbed.getTabs()) { + tabbed.getSelectionModel().select(tab); if ((tab instanceof FieldsEditorTab fieldsEditorTab) - && fieldsEditorTab.getShownFields().contains(field)) { + && fieldsEditorTab.getShownFields().contains(actualField)) { tabbed.getSelectionModel().select(tab); - fieldsEditorTab.requestFocus(field); + Platform.runLater(() -> { + fieldsEditorTab.requestFocus(actualField); + }); + // This line explicitly brings focus back to the main window containing the Entry Editor. + getScene().getWindow().requestFocus(); + fieldFound = true; + break; + } + } + if (!fieldFound) { + Field aliasField = EntryConverter.FIELD_ALIASES.get(field); + if (aliasField != null) { + setFocusToField(aliasField); } } });