From dafaebace03058db5b23abf4f363f3f1f5a79af1 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Thu, 6 Nov 2025 23:14:55 +0100 Subject: [PATCH 01/25] Simplyfy code --- .../logic/importer/plaincitation/PlainCitationParser.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jablib/src/main/java/org/jabref/logic/importer/plaincitation/PlainCitationParser.java b/jablib/src/main/java/org/jabref/logic/importer/plaincitation/PlainCitationParser.java index f2417ba3d70..b6a5c7a8fbc 100644 --- a/jablib/src/main/java/org/jabref/logic/importer/plaincitation/PlainCitationParser.java +++ b/jablib/src/main/java/org/jabref/logic/importer/plaincitation/PlainCitationParser.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import org.jabref.logic.importer.FetcherException; import org.jabref.model.entry.BibEntry; @@ -19,7 +18,7 @@ default List parseMultiplePlainCitations(String text) throws FetcherEx return CitationSplitter.splitCitations(text) .map(Unchecked.function(this::parsePlainCitation)) .flatMap(Optional::stream) - .collect(Collectors.toList()); + .toList(); } catch (UncheckedException e) { throw (FetcherException) e.getCause(); } From 5e85c5d44479b3cf375d2e44d77b2c6f5ebb0046 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Thu, 6 Nov 2025 23:16:39 +0100 Subject: [PATCH 02/25] Fix step ignore --- .github/workflows/tests-code.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests-code.yml b/.github/workflows/tests-code.yml index 3a8ad10906d..632a20156e6 100644 --- a/.github/workflows/tests-code.yml +++ b/.github/workflows/tests-code.yml @@ -462,6 +462,7 @@ jobs: run: | echo "cache_key=jbang-$(date +%F)" >> $GITHUB_OUTPUT - name: Use cache + if: steps.changed-jablib-files.outputs.any_changed != 'true' uses: actions/cache@v4 with: path: ~/.jbang From 5b1ffaf81720b84ca5d44e5420a879b278ff3015 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Thu, 6 Nov 2025 23:25:47 +0100 Subject: [PATCH 03/25] Add doi_to_bibtex.java --- jablib-examples/doi_to_bibtex.java | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 jablib-examples/doi_to_bibtex.java diff --git a/jablib-examples/doi_to_bibtex.java b/jablib-examples/doi_to_bibtex.java new file mode 100644 index 00000000000..ec82e3551d9 --- /dev/null +++ b/jablib-examples/doi_to_bibtex.java @@ -0,0 +1,34 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? + +import org.jabref.logic.exporter.BibWriter; +import org.jabref.logic.exporter.BibDatabaseWriter; +import org.jabref.logic.importer.fetcher.CrossRef; +import org.jabref.logic.preferences.JabRefCliPreferences; +import org.jabref.model.database.BibDatabase; +import org.jabref.model.database.BibDatabaseContext; + +import org.tinylog.Logger; + +//DESCRIPTION Converts a DOI to BibTeX + +//JAVA 25+ +//RUNTIME_OPTIONS --enable-native-access=ALL-UNNAMED +//FILES tinylog.properties=tinylog.properties + +//DEPS org.jabref:jablib:6.0-SNAPSHOT +//REPOS mavencentral,mavencentralsnapshots=https://central.sonatype.com/repository/maven-snapshots/,s01oss=https://s01.oss.sonatype.org/content/repositories/snapshots/,oss=https://oss.sonatype.org/content/repositories,jitpack=https://jitpack.io,oss2=https://oss.sonatype.org/content/groups/public,ossrh=https://oss.sonatype.org/content/repositories/snapshots,raw=https://raw.githubusercontent.com/JabRef/jabref/refs/heads/main/jablib/lib/ + +void main() throws Exception { + var preferences = JabRefCliPreferences.getInstance(); + + // All `IdParserFetcher` can do. In JabRef, there is currently only one implemented + + var fetcher = new CrossRef(); + var entry = fetcher.performSearchById("10.47397/tb/44-3/tb138kopp-jabref").get(); // will throw an exception if not found + + try (var writer = new OutputStreamWriter(System.out, StandardCharsets.UTF_8)) { + var context = new BibDatabaseContext(new BibDatabase(List.of(entry))); + var bibWriter = new BibDatabaseWriter(writer, context, preferences); + bibWriter.writeDatabase(context); + } +} From 3f668952beed092a59657cef41f61e457729b8d8 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Thu, 6 Nov 2025 23:52:11 +0100 Subject: [PATCH 04/25] Add doi-to-bibtex to JabKit --- jabkit/src/main/java/org/jabref/JabKit.java | 4 +- .../org/jabref/cli/ArgumentProcessor.java | 1 + .../main/java/org/jabref/cli/DoiToBibtex.java | 68 +++++++++++++++++++ .../logic/exporter/BibDatabaseWriter.java | 4 +- 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java diff --git a/jabkit/src/main/java/org/jabref/JabKit.java b/jabkit/src/main/java/org/jabref/JabKit.java index 04f917af1d5..38b659ffd52 100644 --- a/jabkit/src/main/java/org/jabref/JabKit.java +++ b/jabkit/src/main/java/org/jabref/JabKit.java @@ -32,6 +32,7 @@ import org.jabref.logic.util.Directories; import org.jabref.logic.util.strings.StringUtil; import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.entry.identifier.DOI; import org.jabref.model.util.DummyFileUpdateMonitor; import org.jabref.model.util.FileUpdateMonitor; @@ -72,7 +73,8 @@ public static void main(String[] args) { Injector.setModelOrService(BibEntryTypesManager.class, entryTypesManager); ArgumentProcessor argumentProcessor = new ArgumentProcessor(preferences, entryTypesManager); - CommandLine commandLine = new CommandLine(argumentProcessor); + CommandLine commandLine = new CommandLine(argumentProcessor) + .registerConverter(DOI.class, DOI::new); String usageHeader = BuildInfo.JABREF_BANNER.formatted(buildInfo.version) + "\n" + JABKIT_BRAND; commandLine.getCommandSpec().usageMessage().header(usageHeader); applyUsageFooters(commandLine, diff --git a/jabkit/src/main/java/org/jabref/cli/ArgumentProcessor.java b/jabkit/src/main/java/org/jabref/cli/ArgumentProcessor.java index 6c2f00754c6..e14dc409f92 100644 --- a/jabkit/src/main/java/org/jabref/cli/ArgumentProcessor.java +++ b/jabkit/src/main/java/org/jabref/cli/ArgumentProcessor.java @@ -43,6 +43,7 @@ CheckConsistency.class, CheckIntegrity.class, Convert.class, + DoiToBibtex.class, Fetch.class, GenerateBibFromAux.class, GenerateCitationKeys.class, diff --git a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java new file mode 100644 index 00000000000..bad819a445e --- /dev/null +++ b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java @@ -0,0 +1,68 @@ +package org.jabref.cli; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.Callable; + +import org.jabref.logic.exporter.BibDatabaseWriter; +import org.jabref.logic.importer.FetcherException; +import org.jabref.logic.importer.fetcher.CrossRef; +import org.jabref.model.database.BibDatabase; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.identifier.DOI; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Parameters; + +@Command(name = "doi-to-bibtex", description = "Converts a DOI to BibTeX") +public class DoiToBibtex implements Callable { + + private static final Logger LOGGER = LoggerFactory.getLogger(CheckIntegrity.class); + + @CommandLine.ParentCommand + private ArgumentProcessor argumentProcessor; + + @Parameters(paramLabel = "DOI", description = "one or more DOIs to fetch", arity = "1..*") + private DOI[] dois; + + @Override + public Integer call() { + var fetcher = new CrossRef(); + List entries = new ArrayList<>(dois.length); + + for (DOI doi: dois) { + Optional entry; + try { + entry = fetcher.performSearchById(doi.asString()); + } catch (FetcherException e) { + LOGGER.error("Could not fetch DOI from BibTeX", e); + continue; + } + + if (entry.isEmpty()) { + LOGGER.error("Could not fetch DOI from BibTeX"); + continue; + } + + entries.add(entry.get()); + } + + try (var writer = new OutputStreamWriter(System.out, StandardCharsets.UTF_8)) { + var context = new BibDatabaseContext(new BibDatabase(entries)); + var bibWriter = new BibDatabaseWriter(writer, context, argumentProcessor.cliPreferences); + bibWriter.writeDatabase(context); + } catch (IOException e) { + LOGGER.error("Could not write BibTeX", e); + return 1; + } + return 0; + } +} diff --git a/jablib/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java b/jablib/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java index ae18dfb1089..61449bb5562 100644 --- a/jablib/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java +++ b/jablib/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java @@ -32,7 +32,7 @@ import org.jabref.logic.cleanup.FieldFormatterCleanups; import org.jabref.logic.cleanup.NormalizeWhitespacesCleanup; import org.jabref.logic.formatter.bibtexfields.TrimWhitespaceFormatter; -import org.jabref.logic.preferences.JabRefCliPreferences; +import org.jabref.logic.preferences.CliPreferences; import org.jabref.logic.util.strings.StringUtil; import org.jabref.model.FieldChange; import org.jabref.model.database.BibDatabase; @@ -95,7 +95,7 @@ public BibDatabaseWriter(@NonNull BibWriter bibWriter, /// @param preferences - used to read all the preferences public BibDatabaseWriter(@NonNull Writer writer, @NonNull BibDatabaseContext bibDatabaseContext, - @NonNull JabRefCliPreferences preferences) { + @NonNull CliPreferences preferences) { this(new BibWriter(writer, bibDatabaseContext.getDatabase().getNewLineSeparator()), preferences.getSelfContainedExportConfiguration(), preferences.getFieldPreferences(), From 242436de30bb22d911b62c49eef7372bf12cc16e Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Thu, 6 Nov 2025 23:54:33 +0100 Subject: [PATCH 05/25] Add CHANGELOG.md entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca81a305638..548342c82b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added "IEEE" as another option for parsing plain text citations. [#14233](github.com/JabRef/jabref/pull/14233) - We added automatic date-based groups that create year/month/day subgroups from an entry’s date fields. [#10822](https://github.com/JabRef/jabref/issues/10822) +- We added `doi-to-bibtex` to `JabKit`. [#14244](https://github.com/JabRef/jabref/pull/14244) ### Changed From 5ed94352ff421f9b4b2fe92affd2c9e7efabcfe7 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 7 Nov 2025 00:02:41 +0100 Subject: [PATCH 06/25] Add some debug code (again) --- .github/workflows/tests-code.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/tests-code.yml b/.github/workflows/tests-code.yml index 632a20156e6..39c98f1bf1d 100644 --- a/.github/workflows/tests-code.yml +++ b/.github/workflows/tests-code.yml @@ -520,6 +520,18 @@ jobs: jablib-examples/**/*.java files_ignore: | jablib/src/main/java/**/*-*.java + base_sha: ${{ github.event.pull_request.base.sha }} + write_output_files: true + - run: | + for f in .github/outputs/*.txt; do + echo "=== $f ===" + cat "$f" + echo + done + - run: | + cat < Date: Fri, 7 Nov 2025 00:03:06 +0100 Subject: [PATCH 07/25] Fix formatting --- jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java index bad819a445e..270d8ca38f4 100644 --- a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java +++ b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java @@ -38,7 +38,7 @@ public Integer call() { var fetcher = new CrossRef(); List entries = new ArrayList<>(dois.length); - for (DOI doi: dois) { + for (DOI doi : dois) { Optional entry; try { entry = fetcher.performSearchById(doi.asString()); From c803beb2e41813b3c0ba6983d1026615bf9d3a39 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 7 Nov 2025 00:08:38 +0100 Subject: [PATCH 08/25] Fix logger --- jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java index 270d8ca38f4..8f15b10d514 100644 --- a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java +++ b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java @@ -25,7 +25,7 @@ @Command(name = "doi-to-bibtex", description = "Converts a DOI to BibTeX") public class DoiToBibtex implements Callable { - private static final Logger LOGGER = LoggerFactory.getLogger(CheckIntegrity.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DoiToBibtex.class); @CommandLine.ParentCommand private ArgumentProcessor argumentProcessor; From df625ab162d46d714c619200cbca8a8d512c7982 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 7 Nov 2025 00:13:03 +0100 Subject: [PATCH 09/25] Also deal with jabkit --- .github/workflows/tests-code.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests-code.yml b/.github/workflows/tests-code.yml index 39c98f1bf1d..a6ddb88a6a7 100644 --- a/.github/workflows/tests-code.yml +++ b/.github/workflows/tests-code.yml @@ -516,6 +516,7 @@ jobs: with: files: | .jbang/*.java + jabkit/src/main/java/**/*.java jablib/src/main/java/**/*.java jablib-examples/**/*.java files_ignore: | @@ -559,7 +560,16 @@ jobs: # We modify the JBang scripts directly to avoid issues with relative paths for f in ${{ steps.changed-jablib-files.outputs.all_changed_files }}; do case "$f" in - jablib-examples/*) continue ;; # skip scripts + jablib-examples/*) + # skip scripts + continue + ;; + JabKit/*) + # only JabKit needs its modified sources + if [ "${{ matrix.script }}" != "JabKitLauncher" ]; then + continue + fi + ;; esac echo "//SOURCES ../$f" >> "${{ matrix.script }}" done From d3dcc6b67c85b5c6882c90631f28acd80e7b8628 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 7 Nov 2025 00:13:11 +0100 Subject: [PATCH 10/25] Remove debug --- .github/workflows/tests-code.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.github/workflows/tests-code.yml b/.github/workflows/tests-code.yml index a6ddb88a6a7..4fdf84043a4 100644 --- a/.github/workflows/tests-code.yml +++ b/.github/workflows/tests-code.yml @@ -521,18 +521,6 @@ jobs: jablib-examples/**/*.java files_ignore: | jablib/src/main/java/**/*-*.java - base_sha: ${{ github.event.pull_request.base.sha }} - write_output_files: true - - run: | - for f in .github/outputs/*.txt; do - echo "=== $f ===" - cat "$f" - echo - done - - run: | - cat < Date: Fri, 7 Nov 2025 00:15:09 +0100 Subject: [PATCH 11/25] Fix casing --- .github/workflows/tests-code.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests-code.yml b/.github/workflows/tests-code.yml index 4fdf84043a4..992351c32f2 100644 --- a/.github/workflows/tests-code.yml +++ b/.github/workflows/tests-code.yml @@ -552,7 +552,7 @@ jobs: # skip scripts continue ;; - JabKit/*) + jabkit/*) # only JabKit needs its modified sources if [ "${{ matrix.script }}" != "JabKitLauncher" ]; then continue From 7ea1a65cd0119ba9ea1f38b5e2fd82c42738b612 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 7 Nov 2025 00:17:26 +0100 Subject: [PATCH 12/25] Fix condition --- .github/workflows/tests-code.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests-code.yml b/.github/workflows/tests-code.yml index 992351c32f2..1d8133e0fb7 100644 --- a/.github/workflows/tests-code.yml +++ b/.github/workflows/tests-code.yml @@ -554,7 +554,7 @@ jobs: ;; jabkit/*) # only JabKit needs its modified sources - if [ "${{ matrix.script }}" != "JabKitLauncher" ]; then + if [ "${{ matrix.script }}" != ".jbang/JabKitLauncher.java" ]; then continue fi ;; From d2aca739fc428fd5ee650fa8ca95fa00993e02cb Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 9 Nov 2025 16:04:15 +0100 Subject: [PATCH 13/25] Continue on wrong DOI --- jabkit/src/main/java/org/jabref/JabKit.java | 3 +-- jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java | 11 ++++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/jabkit/src/main/java/org/jabref/JabKit.java b/jabkit/src/main/java/org/jabref/JabKit.java index 38b659ffd52..202472e48e8 100644 --- a/jabkit/src/main/java/org/jabref/JabKit.java +++ b/jabkit/src/main/java/org/jabref/JabKit.java @@ -73,8 +73,7 @@ public static void main(String[] args) { Injector.setModelOrService(BibEntryTypesManager.class, entryTypesManager); ArgumentProcessor argumentProcessor = new ArgumentProcessor(preferences, entryTypesManager); - CommandLine commandLine = new CommandLine(argumentProcessor) - .registerConverter(DOI.class, DOI::new); + CommandLine commandLine = new CommandLine(argumentProcessor); String usageHeader = BuildInfo.JABREF_BANNER.formatted(buildInfo.version) + "\n" + JABKIT_BRAND; commandLine.getCommandSpec().usageMessage().header(usageHeader); applyUsageFooters(commandLine, diff --git a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java index 8f15b10d514..ba0016480f8 100644 --- a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java +++ b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java @@ -31,17 +31,22 @@ public class DoiToBibtex implements Callable { private ArgumentProcessor argumentProcessor; @Parameters(paramLabel = "DOI", description = "one or more DOIs to fetch", arity = "1..*") - private DOI[] dois; + private String[] dois; @Override public Integer call() { var fetcher = new CrossRef(); List entries = new ArrayList<>(dois.length); - for (DOI doi : dois) { + for (String doiString : dois) { + Optional doiParsed = DOI.parse(doiString); + if (doiParsed.isEmpty()) { + LOGGER.warn("Skipped DOI {}, because it is not a valid DOI string", doiString); + continue; + } Optional entry; try { - entry = fetcher.performSearchById(doi.asString()); + entry = fetcher.performSearchById(doiParsed.get().asString()); } catch (FetcherException e) { LOGGER.error("Could not fetch DOI from BibTeX", e); continue; From b68c6e0d66405e125c6feacbf5d32c892be7ada0 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 9 Nov 2025 16:09:25 +0100 Subject: [PATCH 14/25] Apply suggestions from code review Co-authored-by: Subhramit Basu --- jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java index ba0016480f8..485c72e29a8 100644 --- a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java +++ b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java @@ -35,7 +35,7 @@ public class DoiToBibtex implements Callable { @Override public Integer call() { - var fetcher = new CrossRef(); + CrossRef fetcher = new CrossRef(); List entries = new ArrayList<>(dois.length); for (String doiString : dois) { @@ -60,9 +60,9 @@ public Integer call() { entries.add(entry.get()); } - try (var writer = new OutputStreamWriter(System.out, StandardCharsets.UTF_8)) { - var context = new BibDatabaseContext(new BibDatabase(entries)); - var bibWriter = new BibDatabaseWriter(writer, context, argumentProcessor.cliPreferences); + try (OutputStreamWriter writer = new OutputStreamWriter(System.out, StandardCharsets.UTF_8)) { + BibDatabaseContext context = new BibDatabaseContext(new BibDatabase(entries)); + BibDatabaseWriter bibWriter = new BibDatabaseWriter(writer, context, argumentProcessor.cliPreferences); bibWriter.writeDatabase(context); } catch (IOException e) { LOGGER.error("Could not write BibTeX", e); From 05c4a8b3466bb7a7aac5e1e218d9a8d2d2215803 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 9 Nov 2025 16:09:49 +0100 Subject: [PATCH 15/25] Fix checkstyle --- jabkit/src/main/java/org/jabref/JabKit.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jabkit/src/main/java/org/jabref/JabKit.java b/jabkit/src/main/java/org/jabref/JabKit.java index 202472e48e8..04f917af1d5 100644 --- a/jabkit/src/main/java/org/jabref/JabKit.java +++ b/jabkit/src/main/java/org/jabref/JabKit.java @@ -32,7 +32,6 @@ import org.jabref.logic.util.Directories; import org.jabref.logic.util.strings.StringUtil; import org.jabref.model.entry.BibEntryTypesManager; -import org.jabref.model.entry.identifier.DOI; import org.jabref.model.util.DummyFileUpdateMonitor; import org.jabref.model.util.FileUpdateMonitor; From 1f88fb35f99045d792369c2a1298218fc124562d Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 9 Nov 2025 16:15:51 +0100 Subject: [PATCH 16/25] Fix space --- jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java index 485c72e29a8..beee606e50b 100644 --- a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java +++ b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java @@ -60,7 +60,7 @@ public Integer call() { entries.add(entry.get()); } - try (OutputStreamWriter writer = new OutputStreamWriter(System.out, StandardCharsets.UTF_8)) { + try (OutputStreamWriter writer = new OutputStreamWriter(System.out, StandardCharsets.UTF_8)) { BibDatabaseContext context = new BibDatabaseContext(new BibDatabase(entries)); BibDatabaseWriter bibWriter = new BibDatabaseWriter(writer, context, argumentProcessor.cliPreferences); bibWriter.writeDatabase(context); From 19e0ded40cb1495e6c778571e5465c84742717be Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 9 Nov 2025 16:38:01 +0100 Subject: [PATCH 17/25] Disable JUL output at JabKit --- jabkit/build.gradle.kts | 3 +++ jabkit/src/main/java/org/jabref/JabKit.java | 2 ++ 2 files changed, 5 insertions(+) diff --git a/jabkit/build.gradle.kts b/jabkit/build.gradle.kts index 7c5c1d679ec..c97012295d4 100644 --- a/jabkit/build.gradle.kts +++ b/jabkit/build.gradle.kts @@ -68,6 +68,9 @@ application { // Also passed to launcher by java-module-packaging plugin applicationDefaultJvmArgs = listOf( + // JEP 158: Disable all java util logging + "-Xlog:disable", + // Enable JEP 450: Compact Object Headers "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCompactObjectHeaders", diff --git a/jabkit/src/main/java/org/jabref/JabKit.java b/jabkit/src/main/java/org/jabref/JabKit.java index 04f917af1d5..39d945f7ddc 100644 --- a/jabkit/src/main/java/org/jabref/JabKit.java +++ b/jabkit/src/main/java/org/jabref/JabKit.java @@ -58,6 +58,8 @@ public class JabKit { private static final String JABKIT_BRAND = "JabKit - command line toolkit for JabRef"; + /// Note: To test with gradle, use jabkit -> Tasks -> application -> run + /// Use `--args="..."` as parameters to "Run" public static void main(String[] args) { initLogging(args); From 8b144084efe798b8a63feae529dc2b54b8c97b78 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 9 Nov 2025 19:28:22 +0100 Subject: [PATCH 18/25] --porcelain does not output any logs to the console any more --- CHANGELOG.md | 1 + jabkit/src/main/java/org/jabref/JabKit.java | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c5e9b20624..619dd6557e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Changed - Changed: Ctrl + Shift + L now opens the terminal in the active library directory (#14130) +- `JabKit`: `--porcelain` does not output any logs to the console any more. [#14244](https://github.com/JabRef/jabref/pull/14244) ### Fixed diff --git a/jabkit/src/main/java/org/jabref/JabKit.java b/jabkit/src/main/java/org/jabref/JabKit.java index 39d945f7ddc..6c9f65a392f 100644 --- a/jabkit/src/main/java/org/jabref/JabKit.java +++ b/jabkit/src/main/java/org/jabref/JabKit.java @@ -6,6 +6,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -145,11 +146,10 @@ public static void initLogging(String[] args) { // We must configure logging as soon as possible, which is why we cannot wait for the usual // argument parsing workflow to parse logging options e.g. --debug or --porcelain + boolean isPorcelain = Arrays.stream(args).anyMatch("--porcelain"::equalsIgnoreCase); Level logLevel; if (Arrays.stream(args).anyMatch("--debug"::equalsIgnoreCase)) { logLevel = Level.DEBUG; - } else if (Arrays.stream(args).anyMatch("--porcelain"::equalsIgnoreCase)) { - logLevel = Level.ERROR; } else { logLevel = Level.INFO; } @@ -167,7 +167,7 @@ public static void initLogging(String[] args) { // The "Shared File Writer" is explained at // https://tinylog.org/v2/configuration/#shared-file-writer - Map configuration = Map.of( + Map configuration = new HashMap(Map.of( "level", logLevel.name().toLowerCase(), "writerFile", "rolling file", "writerFile.logLevel", logLevel == Level.DEBUG ? "debug" : "info", @@ -175,8 +175,11 @@ public static void initLogging(String[] args) { "writerFile.file", directory + File.separator + "log_{date:yyyy-MM-dd_HH-mm-ss}.txt", "writerFile.charset", "UTF-8", "writerFile.policies", "startup", - "writerFile.backups", "30"); + "writerFile.backups", "30")); configuration.forEach(Configuration::set); + if (isPorcelain) { + configuration.put("writer", "none"); + } LOGGER = LoggerFactory.getLogger(JabKit.class); } From fd0b7ef051c9e66f01cf6049a0d888ffdbc327aa Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 9 Nov 2025 19:50:38 +0100 Subject: [PATCH 19/25] Fix logger config --- jabkit/src/main/java/org/jabref/JabKit.java | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/jabkit/src/main/java/org/jabref/JabKit.java b/jabkit/src/main/java/org/jabref/JabKit.java index 6c9f65a392f..2265bb17fc2 100644 --- a/jabkit/src/main/java/org/jabref/JabKit.java +++ b/jabkit/src/main/java/org/jabref/JabKit.java @@ -6,9 +6,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -165,22 +163,24 @@ public static void initLogging(String[] args) { return; } - // The "Shared File Writer" is explained at - // https://tinylog.org/v2/configuration/#shared-file-writer - Map configuration = new HashMap(Map.of( - "level", logLevel.name().toLowerCase(), - "writerFile", "rolling file", - "writerFile.logLevel", logLevel == Level.DEBUG ? "debug" : "info", - // We need to manually join the path, because ".resolve" does not work on Windows, because ":" is not allowed in file names on Windows - "writerFile.file", directory + File.separator + "log_{date:yyyy-MM-dd_HH-mm-ss}.txt", - "writerFile.charset", "UTF-8", - "writerFile.policies", "startup", - "writerFile.backups", "30")); - configuration.forEach(Configuration::set); + String fileWriterName; if (isPorcelain) { - configuration.put("writer", "none"); + fileWriterName = "writer"; + } else { + fileWriterName = "writerFile"; } + // The "Shared File Writer" is explained at + // https://tinylog.org/v2/configuration/#shared-file-writer + Configuration.set("level", logLevel.name().toLowerCase()); + Configuration.set(fileWriterName, "rolling file"); + Configuration.set("%s.logLevel".formatted(fileWriterName), logLevel == Level.DEBUG ? "debug" : "info"); + // We need to manually join the path, because ".resolve" does not work on Windows, because ":" is not allowed in file names on Windows + Configuration.set("%s.file".formatted(fileWriterName), directory + File.separator + "log_{date:yyyy-MM-dd_HH-mm-ss}.txt"); + Configuration.set("%s.charset".formatted(fileWriterName), "UTF-8"); + Configuration.set("%s.policies".formatted(fileWriterName), "startup"); + Configuration.set("%s.backups".formatted(fileWriterName), "30"); + LOGGER = LoggerFactory.getLogger(JabKit.class); } From 067af4f8a8240eb06df761b24c49a67fe0a544f8 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 9 Nov 2025 19:59:34 +0100 Subject: [PATCH 20/25] Have --porcelain working --- jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java | 12 ++++++++++-- jablib/src/main/resources/l10n/JabRef_en.properties | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java index beee606e50b..8e7af7ccf42 100644 --- a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java +++ b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java @@ -11,6 +11,7 @@ import org.jabref.logic.exporter.BibDatabaseWriter; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.fetcher.CrossRef; +import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; @@ -30,6 +31,9 @@ public class DoiToBibtex implements Callable { @CommandLine.ParentCommand private ArgumentProcessor argumentProcessor; + @CommandLine.Mixin + private ArgumentProcessor.SharedOptions sharedOptions = new ArgumentProcessor.SharedOptions(); + @Parameters(paramLabel = "DOI", description = "one or more DOIs to fetch", arity = "1..*") private String[] dois; @@ -42,18 +46,21 @@ public Integer call() { Optional doiParsed = DOI.parse(doiString); if (doiParsed.isEmpty()) { LOGGER.warn("Skipped DOI {}, because it is not a valid DOI string", doiString); + System.out.println(Localization.lang("DOI %0 is invalid", doiString)); continue; } Optional entry; try { entry = fetcher.performSearchById(doiParsed.get().asString()); } catch (FetcherException e) { - LOGGER.error("Could not fetch DOI from BibTeX", e); + LOGGER.error("Could not fetch BibTeX based on DOI", e); + System.err.println(Localization.lang("Error")); continue; } if (entry.isEmpty()) { - LOGGER.error("Could not fetch DOI from BibTeX"); + LOGGER.error("Could not fetch BibTeX based on DOI"); + System.err.println(Localization.lang("Error")); continue; } @@ -66,6 +73,7 @@ public Integer call() { bibWriter.writeDatabase(context); } catch (IOException e) { LOGGER.error("Could not write BibTeX", e); + System.err.println(Localization.lang("Unable to write to %0.", "stdout")); return 1; } return 0; diff --git a/jablib/src/main/resources/l10n/JabRef_en.properties b/jablib/src/main/resources/l10n/JabRef_en.properties index 14d987aad0c..6a63f88c2b7 100644 --- a/jablib/src/main/resources/l10n/JabRef_en.properties +++ b/jablib/src/main/resources/l10n/JabRef_en.properties @@ -1450,6 +1450,7 @@ No\ problems\ found.=No problems found. Print\ entry\ preview=Print entry preview Invalid\ DOI\:\ '%0'.=Invalid DOI: '%0'. +DOI\ %0\ is\ invalid=DOI %0 is invalid Same\ DOI\ used\ in\ multiple\ entries=Same DOI used in multiple entries should\ start\ with\ a\ name=should start with a name should\ end\ with\ a\ name=should end with a name @@ -1974,7 +1975,6 @@ Update\ with\ bibliographic\ information\ from\ the\ web=Update with bibliograph Could\ not\ find\ any\ bibliographic\ information.=Could not find any bibliographic information. Citation\ key\ deviates\ from\ generated\ key=Citation key deviates from generated key -DOI\ %0\ is\ invalid=DOI %0 is invalid Select\ all\ customized\ types\ to\ be\ stored\ in\ local\ preferences\:=Select all customized types to be stored in local preferences\: Different\ customization,\ current\ settings\ will\ be\ overwritten=Different customization, current settings will be overwritten From 43b4b87e52cd590abaa61d48aa3a5861665c372c Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 9 Nov 2025 20:52:56 +0100 Subject: [PATCH 21/25] Improve strings --- jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java index 8e7af7ccf42..29305ca77c6 100644 --- a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java +++ b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java @@ -47,6 +47,7 @@ public Integer call() { if (doiParsed.isEmpty()) { LOGGER.warn("Skipped DOI {}, because it is not a valid DOI string", doiString); System.out.println(Localization.lang("DOI %0 is invalid", doiString)); + System.err.println(); continue; } Optional entry; @@ -54,13 +55,16 @@ public Integer call() { entry = fetcher.performSearchById(doiParsed.get().asString()); } catch (FetcherException e) { LOGGER.error("Could not fetch BibTeX based on DOI", e); - System.err.println(Localization.lang("Error")); + System.err.println(Localization.lang("No data was found for the identifier" + " - " + doiString)); + System.err.println(e.getLocalizedMessage()); + System.err.println(); continue; } if (entry.isEmpty()) { LOGGER.error("Could not fetch BibTeX based on DOI"); - System.err.println(Localization.lang("Error")); + System.err.println(Localization.lang("No data was found for the identifier" + " - " + doiString)); + System.err.println(); continue; } From b439fdfa86c582e939d812e9076757e4d2ed90de Mon Sep 17 00:00:00 2001 From: Subhramit Basu Date: Mon, 10 Nov 2025 01:25:26 +0530 Subject: [PATCH 22/25] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 619dd6557e3..38d77082db5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Changed - Changed: Ctrl + Shift + L now opens the terminal in the active library directory (#14130) -- `JabKit`: `--porcelain` does not output any logs to the console any more. [#14244](https://github.com/JabRef/jabref/pull/14244) +- `JabKit`: `--porcelain` does not output any logs to the console anymore. [#14244](https://github.com/JabRef/jabref/pull/14244) ### Fixed From bea7fc503d393cf8ebc6871d353d06eb7ee18ed2 Mon Sep 17 00:00:00 2001 From: Subhramit Basu Date: Mon, 10 Nov 2025 01:25:33 +0530 Subject: [PATCH 23/25] Update jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java --- jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java index 29305ca77c6..757fc6b9b9e 100644 --- a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java +++ b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java @@ -62,7 +62,7 @@ public Integer call() { } if (entry.isEmpty()) { - LOGGER.error("Could not fetch BibTeX based on DOI"); + LOGGER.error("Could not fetch BibTeX based on DOI - entry is empty"); System.err.println(Localization.lang("No data was found for the identifier" + " - " + doiString)); System.err.println(); continue; From 06b5c481a091e538f3ca2fc9233ab7d390621b54 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 9 Nov 2025 21:31:33 +0100 Subject: [PATCH 24/25] Workaround for tests --- jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java index 757fc6b9b9e..d3101d22776 100644 --- a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java +++ b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java @@ -55,7 +55,8 @@ public Integer call() { entry = fetcher.performSearchById(doiParsed.get().asString()); } catch (FetcherException e) { LOGGER.error("Could not fetch BibTeX based on DOI", e); - System.err.println(Localization.lang("No data was found for the identifier" + " - " + doiString)); + System.err.print(Localization.lang("No data was found for the identifier"); + System.err.println(" - " + doiString)); System.err.println(e.getLocalizedMessage()); System.err.println(); continue; @@ -63,7 +64,8 @@ public Integer call() { if (entry.isEmpty()) { LOGGER.error("Could not fetch BibTeX based on DOI - entry is empty"); - System.err.println(Localization.lang("No data was found for the identifier" + " - " + doiString)); + System.err.print(Localization.lang("No data was found for the identifier"); + System.err.println(" - " + doiString)); System.err.println(); continue; } From 1644f8b4bf6de057c27bf8ad567cd59c16b32ba6 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 9 Nov 2025 21:54:03 +0100 Subject: [PATCH 25/25] Compilefix --- jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java index d3101d22776..8340e5d7232 100644 --- a/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java +++ b/jabkit/src/main/java/org/jabref/cli/DoiToBibtex.java @@ -55,8 +55,8 @@ public Integer call() { entry = fetcher.performSearchById(doiParsed.get().asString()); } catch (FetcherException e) { LOGGER.error("Could not fetch BibTeX based on DOI", e); - System.err.print(Localization.lang("No data was found for the identifier"); - System.err.println(" - " + doiString)); + System.err.print(Localization.lang("No data was found for the identifier")); + System.err.println(" - " + doiString); System.err.println(e.getLocalizedMessage()); System.err.println(); continue; @@ -64,8 +64,8 @@ public Integer call() { if (entry.isEmpty()) { LOGGER.error("Could not fetch BibTeX based on DOI - entry is empty"); - System.err.print(Localization.lang("No data was found for the identifier"); - System.err.println(" - " + doiString)); + System.err.print(Localization.lang("No data was found for the identifier")); + System.err.println(" - " + doiString); System.err.println(); continue; }