Skip to content

Add identifier-based fetched sources in PDF merge dialog#15932

Closed
JiahaoHe123 wants to merge 8 commits into
JabRef:mainfrom
JiahaoHe123:feature-doi-in-merge-dialog
Closed

Add identifier-based fetched sources in PDF merge dialog#15932
JiahaoHe123 wants to merge 8 commits into
JabRef:mainfrom
JiahaoHe123:feature-doi-in-merge-dialog

Conversation

@JiahaoHe123

@JiahaoHe123 JiahaoHe123 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Enrich PDF merge dialog with identifier-based fetched sources
✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

Walkthroughs

User Description

Related issues and pull requests

Closes #15415

PR Description

This PR improves PDF drag-and-drop metadata merge by adding identifier-based enrichment as a follow-up step to existing PDF parsing. After metadata is parsed from the PDF, JabRef now looks for supported identifiers (with DOI prioritized), normalizes DOI values such as info:doi/..., fetches bibliographic data from the corresponding service, and adds successful results as extra merge sources (for example, Entry from DOI). This improves merged-entry completeness while avoiding noisy failed-source columns when an identifier is missing or a fetch does not return data.

Steps to test

1.Open Jabref and Drag this example pdf file to the main table
Screenshot 2026-06-08 at 10 24 21 PM

2.Resize the dialog if needed so all source columns are visible, including the new "Entry from DOI" column.

  • Before
    Screenshot 2026-06-07 at 5 11 20 PM

  • After
    Screenshot 2026-06-07 at 5 12 41 AM

AI usage

Copilot(GPT-5.3-Codex)
ChatGPT-5.5

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • If AI tools were used, I disclosed them in the "AI usage" section and reviewed, understood, and take full ownership of all AI-generated code
  • I manually tested my changes in running JabRef (always required)
  • I added JUnit tests for changes (if applicable)
  • I added screenshots in the PR description (if change is visible to the user)
  • [/] I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
  • I described the change in CHANGELOG.md in a way that can be understood by the average user (if change is visible to the user)
  • [/] I checked the user documentation for up to dateness and submitted a pull request to our user documentation repository
AI Description
• Add a follow-up enrichment step that fetches entries by identifiers found in parsed PDF metadata.
• Normalize and prioritize identifiers (e.g., DOI cleanup) and only add merge columns on successful
  fetch.
• Memoize PDF parsing suppliers to avoid repeated importer work and add focused unit tests.
Diagram
graph TD
  U["Drag PDF"] --> D["PdfMergeDialog"] --> V["MultiMergeEntriesView"]
  D --> S["CachedSupplier"] --> P["PDF parsers"] --> C["Parsed BibEntries"] --> F["IdentifierBasedEntryFetcher"] --> W["WebFetchers (id-based)"] --> R["Fetched BibEntry"] --> V
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Reuse existing per-field UI fetch buttons only (e.g., existing DOI lookup)
  • ➕ Zero additional background enrichment at dialog creation time
  • ➕ Keeps network calls explicitly user-triggered
  • ➖ Lower discoverability; users may not click lookup buttons
  • ➖ Does not help when DOI is present but other fields are incomplete until user action
  • ➖ Does not generalize across multiple identifier types without more UI affordances
2. Move identifier enrichment into the PDF importer pipeline (logic layer)
  • ➕ Keeps UI layer thinner; enrichment becomes part of import result
  • ➕ Potentially reusable in non-GUI import flows
  • ➖ Mixes network I/O into parsing/import responsibilities
  • ➖ Harder to control when/if network calls happen (privacy/offline expectations)
  • ➖ Risk of slowing down imports if not carefully made async/cancellable

Recommendation: Current approach (post-parse, async enrichment in PdfMergeDialog, adding sources only on successful fetch) is a good balance: it improves merge completeness without polluting the UI with failed-source columns. The memoization via CachedSupplier is appropriate to avoid repeated parsing work. Consider (later) adding explicit cancellation/timeout semantics for the background fetch to better handle slow networks, but the overall strategy is sound.

Grey Divider

File Changes

Enhancement (2)
PdfMergeDialog.java Add async identifier-based fetched sources to PDF merge dialog +61/-8

Add async identifier-based fetched sources to PDF merge dialog

• Refactors source creation to memoize PDF importer suppliers, collects non-null parsed entries, and runs an asynchronous enrichment step that fetches entries by supported identifier fields. Successfully fetched entries are added as additional merge sources titled by identifier type (e.g., “Entry from DOI”).

jabgui/src/main/java/org/jabref/gui/externalfiles/PdfMergeDialog.java


IdentifierBasedEntryFetcher.java Introduce identifier-based entry fetching with DOI normalization +85/-0

Introduce identifier-based entry fetching with DOI normalization

• Adds a helper that scans parsed BibEntry candidates for supported identifier fields (DOI/EPRINT/ISBN/ISSN), normalizes identifiers (notably stripping info:doi prefixes and canonicalizing DOI), and fetches entries via the appropriate IdBasedFetcher. Returns only successful fetches to avoid downstream ‘failed source’ noise.

jablib/src/main/java/org/jabref/logic/importer/IdentifierBasedEntryFetcher.java


Refactor (1)
CachedSupplier.java Add memoizing supplier utility for one-time computation +29/-0

Add memoizing supplier utility for one-time computation

• Introduces a small utility to memoize Supplier results in a thread-safe way, enabling repeated reads of the same parsed BibEntry without re-running importers.

jablib/src/main/java/org/jabref/logic/util/CachedSupplier.java


Tests (1)
IdentifierBasedEntryFetcherTest.java Add unit tests for identifier discovery and DOI normalization +62/-0

Add unit tests for identifier discovery and DOI normalization

• Covers DOI normalization (info:doi/…), empty-identifier behavior, and multi-field fetching to ensure only available identifiers yield fetched entries.

jablib/src/test/java/org/jabref/logic/importer/IdentifierBasedEntryFetcherTest.java


Documentation (1)
CHANGELOG.md Document identifier-based enrichment for PDF merge sources +1/-0

Document identifier-based enrichment for PDF merge sources

• Adds a changelog entry describing that PDF drag-and-drop merge now enriches sources via identifiers (e.g., DOI) and only shows fetched sources when successful.

CHANGELOG.md


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (2) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Grobid label not localized 📘 Rule violation ⚙ Maintainability
Description
The new/modified source label Grobid is added as a hardcoded UI string instead of using
Localization.lang(...), which breaks localization requirements. This makes the multi-merge dialog
partially untranslated in non-English locales.
Code

jabgui/src/main/java/org/jabref/gui/externalfiles/PdfMergeDialog.java[R80-83]

+            Supplier<BibEntry> grobidSupplier = CachedSupplier.memoize(
+                    wrapImporterToSupplier(new PdfGrobidImporter(preferences.getImportFormatPreferences()), filePath));
+            dialog.addSource("Grobid", grobidSupplier);
+            parsedEntrySuppliers.add(grobidSupplier);
Evidence
PR Compliance ID 12 requires all user-facing UI strings to be localized. The code adds the source
label using a raw string literal "Grobid" rather than Localization.lang(...).

AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly: AGENTS.md: Localization requirements: localize all user-facing text (Java and FXML), keep logs in English, enforce UI text conventions, and maintain JabRef_en.properties correctly
jabgui/src/main/java/org/jabref/gui/externalfiles/PdfMergeDialog.java[80-83]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A user-facing string (`Grobid`) is hardcoded in the multi-merge dialog source label instead of being localized.
## Issue Context
Source labels in the merge dialog are visible UI text and must be localized via `Localization.lang(...)` (and backed by `JabRef_en.properties` if needed).
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/PdfMergeDialog.java[80-83]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. replaceFirst regex not precompiled 📘 Rule violation ➹ Performance
Description
normalizeIdentifier uses String.replaceFirst with a regex, which recompiles the pattern on each
call and violates the preferred pattern-reuse approach. This can create avoidable overhead in
repeated identifier normalization.
Code

jablib/src/main/java/org/jabref/logic/importer/IdentifierBasedEntryFetcher.java[R76-80]

+        if (field == StandardField.DOI) {
+            String cleanedIdentifier = trimmedIdentifier.replaceFirst("(?i)^info:doi[:/]", "");
+            return DOI.parse(cleanedIdentifier)
+                      .map(DOI::asString)
+                      .orElse(cleanedIdentifier);
Evidence
PR Compliance ID 26 requires avoiding repeated regex compilation by precompiling and reusing
Pattern (or using non-regex operations). The added normalization code calls replaceFirst with a
regex literal inside normalizeIdentifier, which triggers repeated compilation.

jablib/src/main/java/org/jabref/logic/importer/IdentifierBasedEntryFetcher.java[76-80]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`String.replaceFirst("(?i)^info:doi[:/]", "")` uses a regex that is recompiled on each invocation.
## Issue Context
The compliance rules prefer precompiled/reused `Pattern` instances (or non-regex string operations) to avoid repeated regex compilation overhead.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/IdentifierBasedEntryFetcher.java[76-80]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. No fetch fallback ✓ Resolved 🐞 Bug ≡ Correctness
Description
IdentifierBasedEntryFetcher.fetchByField uses only the first candidate entry containing the
identifier field and performs a single fetch attempt; if that fetch returns empty, later candidates
are never tried. This can prevent showing “Entry from DOI/ISBN/…” even when a valid identifier
exists in another parsed source (e.g., Grobid/XMP).
Code

jablib/src/main/java/org/jabref/logic/importer/IdentifierBasedEntryFetcher.java[R47-71]

+    public Optional<BibEntry> fetchByField(List<BibEntry> candidates, Field field) {
+        return findIdentifier(candidates, field)
+                .flatMap(identifier -> fetchByFieldAndIdentifier.apply(field, normalizeIdentifier(field, identifier)));
+    }
+
+    public Map<Field, BibEntry> fetchByFields(List<BibEntry> candidates, List<Field> fields) {
+        Map<Field, BibEntry> fetchedEntries = new LinkedHashMap<>();
+
+        for (Field field : fields) {
+            fetchByField(candidates, field).ifPresent(entry -> fetchedEntries.put(field, entry));
+        }
+
+        return fetchedEntries;
+    }
+
+    private Optional<String> findIdentifier(List<BibEntry> candidates, Field field) {
+        for (BibEntry candidate : candidates) {
+            Optional<String> identifier = candidate.getField(field);
+            if (identifier.isPresent()) {
+                return identifier;
+            }
+        }
+
+        return Optional.empty();
+    }
Evidence
fetchByField delegates to findIdentifier which returns immediately on the first present identifier;
there is no retry/fallback after an empty fetch result. PdfMergeDialog builds the candidate list in
a fixed order and appends Grobid last, so an earlier bad identifier can block a later good one.

jablib/src/main/java/org/jabref/logic/importer/IdentifierBasedEntryFetcher.java[47-71]
jabgui/src/main/java/org/jabref/gui/externalfiles/PdfMergeDialog.java[72-84]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`IdentifierBasedEntryFetcher.fetchByField(...)` selects the first candidate with a present identifier field and performs exactly one fetch. If that identifier is malformed/low-quality and the fetch returns empty, the method never falls back to other candidates, so enrichment can be missed.
### Issue Context
In `PdfMergeDialog`, candidates are collected from multiple PDF importers (verbatim/embedded/XMP/content and optionally Grobid). The current logic stops on the first candidate that has the field, regardless of value quality or fetch success.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/IdentifierBasedEntryFetcher.java[47-71]
- jabgui/src/main/java/org/jabref/gui/externalfiles/PdfMergeDialog.java[72-84]
### Proposed change
- Replace the `findIdentifier(...).flatMap(...)` approach with a loop:
- Iterate candidates in order.
- For each candidate: read the field value; trim; skip blank.
- Normalize the identifier.
- Attempt fetch; if present, return it; otherwise continue.
- (Optional) For DOI: if `DOI.parse(normalized)` fails, treat as invalid and continue to next candidate rather than attempting fetch with an invalid string.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Blank identifier lookup ✓ Resolved 🐞 Bug ☼ Reliability
Description
IdentifierBasedEntryFetcher treats empty/whitespace field values as present and can call id-based
fetchers with an empty identifier. This causes unnecessary fetch attempts and can also mask a valid
identifier stored in a later candidate.
Code

jablib/src/main/java/org/jabref/logic/importer/IdentifierBasedEntryFetcher.java[R62-84]

+    private Optional<String> findIdentifier(List<BibEntry> candidates, Field field) {
+        for (BibEntry candidate : candidates) {
+            Optional<String> identifier = candidate.getField(field);
+            if (identifier.isPresent()) {
+                return identifier;
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    static String normalizeIdentifier(Field field, String identifier) {
+        String trimmedIdentifier = identifier.trim();
+
+        if (field == StandardField.DOI) {
+            String cleanedIdentifier = trimmedIdentifier.replaceFirst("(?i)^info:doi[:/]", "");
+            return DOI.parse(cleanedIdentifier)
+                      .map(DOI::asString)
+                      .orElse(cleanedIdentifier);
+        }
+
+        return trimmedIdentifier;
+    }
Evidence
BibEntry.getField does not filter empty strings, so empty values are considered present.
IdentifierBasedEntryFetcher’s findIdentifier returns the first present Optional without checking
content, and normalizeIdentifier does not drop empty identifiers after trimming.

jablib/src/main/java/org/jabref/model/entry/BibEntry.java[452-455]
jablib/src/main/java/org/jabref/logic/importer/IdentifierBasedEntryFetcher.java[62-84]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Empty identifier values are treated as present because `BibEntry.getField` returns an `Optional` even for empty strings. `IdentifierBasedEntryFetcher` trims but does not guard against `""`, so it may call `performSearchById("")`.
### Issue Context
PDF-imported entries can contain empty-string field values. Current `findIdentifier` checks only `isPresent()` and `normalizeIdentifier` returns the trimmed string even if it becomes empty.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/IdentifierBasedEntryFetcher.java[62-84]
### Proposed change
- In `findIdentifier`, only return an identifier if `identifier.isPresent()` **and** `!identifier.get().isBlank()`.
- Additionally (defense in depth), in `fetchByField` (or `normalizeIdentifier`), after trimming/normalizing, skip fetch when the identifier is blank.
- Add a unit test asserting that blank values do not invoke the fetch function (similar to the existing AtomicReference-based test).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
5. Unhandled fetch task errors ✓ Resolved 🐞 Bug ◔ Observability
Description
PdfMergeDialog’s identifier-enrichment BackgroundTask registers only onSuccess, and UiTaskExecutor
wires Task#setOnFailed only when an onFailure handler exists. As a result, exceptions during
identifier fetching can be effectively unhandled and the extra sources may simply never appear
without diagnosable output.
Code

jabgui/src/main/java/org/jabref/gui/externalfiles/PdfMergeDialog.java[R92-101]

+        BackgroundTask.wrap(() -> identifierBasedEntryFetcher.fetchByFields(parsedEntriesSupplier.get(), IdentifierBasedEntryFetcher.SUPPORTED_FIELDS))
+                      .onSuccess(fetchedEntries -> {
+                          for (Field field : IdentifierBasedEntryFetcher.SUPPORTED_FIELDS) {
+                              BibEntry fetchedEntry = fetchedEntries.get(field);
+                              if (fetchedEntry != null) {
+                                  dialog.addSource(Localization.lang("Entry from %0", FieldTextMapper.getDisplayName(field)), fetchedEntry);
+                              }
+                          }
+                      })
+                      .executeWith(taskExecutor);
Evidence
The new enrichment task in PdfMergeDialog does not specify an onFailure handler. UiTaskExecutor only
calls setOnFailed when BackgroundTask provides a non-null exception consumer, otherwise failures
have no callback path from the executor’s wrapper.

jabgui/src/main/java/org/jabref/gui/externalfiles/PdfMergeDialog.java[89-101]
jabgui/src/main/java/org/jabref/gui/util/UiTaskExecutor.java[196-209]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The background fetch in `PdfMergeDialog` does not set `.onFailure(...)`. With the current `UiTaskExecutor`, a BackgroundTask without `onFailure` does not get an `onFailed` handler attached, so failures can be unreported.
### Issue Context
Identifier fetch involves network calls and parsing, so failures are expected occasionally. Without onFailure, missing “Entry from …” columns are hard to debug.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/PdfMergeDialog.java[89-101]
- jabgui/src/main/java/org/jabref/gui/util/UiTaskExecutor.java[196-209]
### Proposed change
- Add `.onFailure(exception -> LOGGER.warn("Identifier-based enrichment failed for {}", filePath, exception))` (or similar) to the BackgroundTask chain in `PdfMergeDialog`.
- Ensure `PdfMergeDialog` has an `org.slf4j.Logger`.
- (Optional) If you want user-facing feedback, use `showToUser(true)` or a dialog notification pattern consistent with other fetch flows; otherwise at least log at debug/warn.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +80 to +83
Supplier<BibEntry> grobidSupplier = CachedSupplier.memoize(
wrapImporterToSupplier(new PdfGrobidImporter(preferences.getImportFormatPreferences()), filePath));
dialog.addSource("Grobid", grobidSupplier);
parsedEntrySuppliers.add(grobidSupplier);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. grobid label not localized 📘 Rule violation ⚙ Maintainability

The new/modified source label Grobid is added as a hardcoded UI string instead of using
Localization.lang(...), which breaks localization requirements. This makes the multi-merge dialog
partially untranslated in non-English locales.
Agent Prompt
## Issue description
A user-facing string (`Grobid`) is hardcoded in the multi-merge dialog source label instead of being localized.

## Issue Context
Source labels in the merge dialog are visible UI text and must be localized via `Localization.lang(...)` (and backed by `JabRef_en.properties` if needed).

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/PdfMergeDialog.java[80-83]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is okay as Grobid is a tool name

@koppor koppor left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much AI code, not sure if read by contributor.

Comment on lines +34 to +35
assertTrue(result.isPresent());
assertEquals("10.1145/3651640.3651646", fetchedIdentifier.get());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, compare optionals directly. You can use Optional.of(...) as expected

Comment on lines +42 to +44
Optional<BibEntry> result = fetcher.fetchByField(List.of(new BibEntry()), StandardField.DOI);

assertFalse(result.isPresent());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above

Comment on lines +57 to +60
assertEquals(2, fetchedEntries.size());
assertTrue(fetchedEntries.containsKey(StandardField.DOI));
assertTrue(fetchedEntries.containsKey(StandardField.ISBN));
assertFalse(fetchedEntries.containsKey(StandardField.ISSN));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? Why not containing the real values?


import java.util.function.Supplier;

/// Utility methods for creating cached suppliers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't there a library for that? Replace calls with https://apidia.net/mvn/com.google.guava/guava/33.1.0-jre/?pck=com.google.common.base&cls=.Suppliers "memorize"

--

impl. not in line with SO answers

String trimmedIdentifier = identifier.trim();

if (field == StandardField.DOI) {
String cleanedIdentifier = trimmedIdentifier.replaceFirst("(?i)^info:doi[:/]", "");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

????? JabRef has DOI class? why cleanup required? add comment!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The y is missing

@github-actions github-actions Bot added the status: changes-required Pull requests that are not yet complete label Jun 9, 2026
@github-actions

Copy link
Copy Markdown
Contributor

The requested changes were not addressed for 10 days. Please follow-up in the next 10 days or your PR will be automatically closed. You can check the contributing guidelines for hints on the pull request process.

@github-actions github-actions Bot added the status: stale Issues marked by a bot as "stale". All issues need to be investigated manually. label Jun 19, 2026
@calixtus

Copy link
Copy Markdown
Member

@JiahaoHe123 Are you still interested in finishing this PR and addressing the comments or should we close this PR?

@calixtus calixtus added the status: waiting-for-feedback The submitter or other users need to provide more information about the issue label Jun 19, 2026
Comment thread CHANGELOG.md
- We fixed an issue where `Quality-> Cleanup -> Rename PDF` together with `Moved linked files to file directory` would lead to an exception. [#15833](https://github.com/JabRef/jabref/issues/15833)
- We fixed an issue where renaming a linked file with a very long title showed a misleading "file is being used by another process" error instead of renaming successfully. [#14771](https://github.com/JabRef/jabref/issues/14771)
- We fixed an issue where JabRef would trigger `The libray has been changed` while still saving. [#4877](https://github.com/JabRef/jabref/issues/4877)
- We fixed PDF drag-and-drop metadata merge to add identifier-based fetched sources (for example Entry from DOI) only when fetch succeeds, improving merged entry completeness without adding noisy failed sources. [#15415](https://github.com/JabRef/jabref/issues/15415)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI generated

String trimmedIdentifier = identifier.trim();

if (field == StandardField.DOI) {
String cleanedIdentifier = trimmedIdentifier.replaceFirst("(?i)^info:doi[:/]", "");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The y is missing

@koppor koppor closed this Jul 4, 2026
@koppor

koppor commented Jul 4, 2026

Copy link
Copy Markdown
Member

Closing this issue due to inactivity 💤

Please ping us if you intend to resume work on this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: changes-required Pull requests that are not yet complete status: stale Issues marked by a bot as "stale". All issues need to be investigated manually. status: waiting-for-feedback The submitter or other users need to provide more information about the issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DOI data should be added in multli merge dialog

4 participants