diff --git a/.github/workflows/tests-code.yml b/.github/workflows/tests-code.yml index 3a8ad10906d..1d8133e0fb7 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 @@ -515,6 +516,7 @@ jobs: with: files: | .jbang/*.java + jabkit/src/main/java/**/*.java jablib/src/main/java/**/*.java jablib-examples/**/*.java files_ignore: | @@ -546,7 +548,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 }}" != ".jbang/JabKitLauncher.java" ]; then + continue + fi + ;; esac echo "//SOURCES ../$f" >> "${{ matrix.script }}" done 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 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..8f15b10d514 --- /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(DoiToBibtex.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-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); + } +} 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(), 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(); }