Skip to content

Fix/citation refresh button#15363

Merged
Siedlerchr merged 3 commits intoJabRef:mainfrom
Syimyk43:fix/citation-refresh-button
Mar 30, 2026
Merged

Fix/citation refresh button#15363
Siedlerchr merged 3 commits intoJabRef:mainfrom
Syimyk43:fix/citation-refresh-button

Conversation

@Syimyk43
Copy link
Copy Markdown
Contributor

Related issues and pull requests

Closes #12247

PR Description

Improved the refresh button behavior in the Citation Relations tab.
Before, clicking refresh always refetched from the remote unconditionally.
Now it checks the cache state first:
if there was an error it retries immediately,
if the data was fetched recently it asks the user for confirmation,
if the data is old enough it refetches normally.

Steps to test

  1. Open JabRef and load a library with an entry that has a DOI
  2. Click the "Citations" tab in the entry editor
  3. Wait for citations to load
  4. Immediately click the refresh button -> a confirmation dialog should appear
  5. Click "No" -> nothing happens
  6. Click refresh again and click "Yes" -> citations are refetched
Screenshot 2026-03-18 at 3 09 11 AM

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • 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
Screenshot 2026-03-18 at 3 15 09 AM
  • 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

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Improve citation refresh button and fix DOI case sensitivity

✨ Enhancement 🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Improved citation refresh button with smart caching logic
  - Retries immediately on error state
  - Requests confirmation if data fetched recently
  - Refetches normally if data is old enough
• Fixed case-insensitive DOI comparison in duplicate detection
• Added public methods to expose cache state checking
• Enhanced test coverage with new duplicate detection scenarios
Diagram
flowchart LR
  A["Refresh Button Clicked"] --> B{"Check Cache State"}
  B -->|Error Found| C["Retry Immediately"]
  B -->|Recently Fetched| D["Ask User Confirmation"]
  D -->|User Confirms| E["Refetch Data"]
  D -->|User Declines| F["Cancel"]
  B -->|Data Old| E
  C --> E
  E --> G["Update Citations"]
  G["Update Citations"] --> H["Display Results"]
  
  I["DOI Comparison"] --> J{"Case Insensitive?"}
  J -->|Yes| K["Match Found"]
  J -->|No| L["No Match"]
Loading

Grey Divider

File Changes

1. jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java ✨ Enhancement +31/-2

Implement smart refresh button with cache awareness

• Replaced direct searchForRelations calls with new handleRefresh method
• Added handleRefresh method implementing smart cache-aware refresh logic
• Checks for error state and retries immediately if found
• Checks if data is updatable and prompts user confirmation if recently fetched
• Proceeds with normal refetch if data is old enough

jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java


2. jablib/src/main/java/org/jabref/logic/citation/SearchCitationsRelationsService.java ✨ Enhancement +8/-0

Expose cache state checking methods

• Added isReferencesUpdatable public method to check if references need updating
• Added isCitationsUpdatable public method to check if citations need updating
• Both methods delegate to relationsRepository for cache state checking

jablib/src/main/java/org/jabref/logic/citation/SearchCitationsRelationsService.java


3. jablib/src/main/java/org/jabref/logic/database/DuplicateCheck.java 🐞 Bug fix +9/-1

Fix case-insensitive DOI duplicate detection

• Modified haveSameIdentifier method to perform case-insensitive DOI comparison
• Added special handling for StandardField.DOI using equalsIgnoreCase
• Other identifier fields continue to use case-sensitive comparison
• Fixes issue where DOIs differing only in letter casing were not recognized as duplicates

jablib/src/main/java/org/jabref/logic/database/DuplicateCheck.java


View more (2)
4. jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java 🧪 Tests +249/-232

Add case-insensitive DOI duplicate detection tests

• Reformatted existing test code with consistent indentation
• Added twoEntriesWithSameDoiButDifferentCasingAreDuplicates test
• Added twoEntriesWithSameDoiButDifferentCasingAndDifferentTypesAreDuplicates test
• New tests verify case-insensitive DOI matching works correctly

jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java


5. CHANGELOG.md 📝 Documentation +2/-0

Document citation refresh and DOI comparison improvements

• Added entry describing improved refresh button behavior in Citation Relations tab
• Added entry documenting fix for case-insensitive DOI comparison in duplicate detection
• Both entries reference their respective GitHub issues

CHANGELOG.md


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented Mar 18, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (3) 📎 Requirement gaps (0)

Grey Divider


Action required

1. setField() used in DOI tests 📘 Rule violation ✓ Correctness
Description
New tests mutate BibEntry using setField, contrary to the preferred withField style. This
reduces consistency with JabRef’s functional/withers pattern.
Code

jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java[R405-416]

+        simpleArticle.setField(StandardField.DOI, "10.1016/j.is.2004.02.002");
+        unrelatedArticle.setField(StandardField.DOI, "10.1016/J.IS.2004.02.002");
+
+        assertTrue(duplicateChecker.isDuplicate(simpleArticle, unrelatedArticle, BibDatabaseMode.BIBTEX));
+    }
+
+    @Test
+    void twoEntriesWithSameDoiButDifferentCasingAndDifferentTypesAreDuplicates() {
+        simpleArticle.setField(StandardField.DOI, "10.1016/j.is.2004.02.002");
+        unrelatedArticle.setField(StandardField.DOI, "10.1016/J.IS.2004.02.002");
+        unrelatedArticle.setType(StandardEntryType.Misc);
+
Evidence
PR Compliance ID 19 requires using withField instead of setField when creating/modifying
BibEntry instances; the newly added tests set DOIs via setField.

AGENTS.md
jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java[405-416]

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

## Issue description
New tests use `setField` to modify `BibEntry` instances, but JabRef prefers `withField`.

## Issue Context
The project encourages immutable/functional patterns and consistent test/code style.

## Fix Focus Areas
- jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java[405-418]

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


2. Localized error retry broken 🐞 Bug ✓ Correctness
Description
CitationRelationsTab.handleRefresh detects an error state by checking whether the placeholder label
text starts with the localized token "Error", but the actual localized error sentences may not start
with that token (e.g., zh_CN), so errors are not recognized and refresh won’t reliably retry
immediately after a failure. Additionally, the DOI lookup failure path sets placeholders with
hardcoded "Error ", which will never match non-English Localization.lang("Error") prefixes.
Code

jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[R999-1005]

+        boolean hasError = citationComponents.listView().getPlaceholder() instanceof Label label
+                && label.getText().startsWith(Localization.lang("Error"));
+
+        if (hasError) {
+            searchForRelations(citationComponents, otherCitationComponents, true);
+            return;
+        }
Evidence
The refresh handler uses a string-prefix heuristic based on Localization.lang("Error"). However, the
fetch failure placeholder is a fully localized sentence ("Error while fetching …"), and in zh_CN
that sentence does not begin with the translation of "Error" ("错误"), so the prefix check returns
false and the code does not take the immediate-retry path. Separately, the DOI lookup failure path
uses a hardcoded English "Error " prefix, which also won’t match Localization.lang("Error") in
non-English locales.

jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[995-1005]
jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[847-858]
jablib/src/main/resources/l10n/JabRef_zh_CN.properties[240-243]
jablib/src/main/resources/l10n/JabRef_zh_CN.properties[1698-1701]
jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[787-811]

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

## Issue description
`CitationRelationsTab.handleRefresh` decides whether to “retry immediately on error” by checking if the list placeholder is a `Label` whose text starts with `Localization.lang("Error")`. This is not reliable across locales (some translations of the full error sentence don’t start with the localized “Error” token) and also fails for the DOI lookup failure path, which sets placeholders to a hardcoded English prefix (`"Error " + ex.getMessage()`).

## Issue Context
The UI already sets error placeholders in `executeSearch(...).onFailure(...)` using fully localized sentences. The refresh logic should not depend on the *rendered* (localized) text to detect error state.

## Fix approach (one of these)
1) **Explicit state**: add a boolean/enum fetch state to `CitationComponents` or maintain a `Map<CitationComponents, FetchState>` in the tab; set it to ERROR on failures and OK on success. `handleRefresh` should consult that state.

2) **Stable marker on placeholder node**: when creating the error placeholder `Label`, set a stable marker such as `placeholder.setId("citation-relations-error")` or a style class. Then `handleRefresh` checks for that marker instead of string prefixes.

Also ensure the DOI lookup failure uses `Localization.lang(...)` and the same error marker.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[787-811]
- jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[847-858]
- jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[995-1005]

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



Remediation recommended

3. Formatting-only changes in DuplicateCheckTest 📘 Rule violation ✓ Correctness
Description
Large portions of DuplicateCheckTest were reindented without any behavioral change, increasing
diff noise and review effort. This violates the requirement to avoid unrelated reformatting.
Code

jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java[R30-34]

    private static BibEntry getSimpleArticle() {
        return new BibEntry(StandardEntryType.Article)
-                .withField(StandardField.AUTHOR, "Single Author")
-                .withField(StandardField.TITLE, "A serious paper about something")
-                .withField(StandardField.YEAR, "2017");
+            .withField(StandardField.AUTHOR, "Single Author")
+            .withField(StandardField.TITLE, "A serious paper about something")
+            .withField(StandardField.YEAR, "2017");
Evidence
PR Compliance ID 2 forbids unrelated/formatting-only churn; the shown hunk changes only
indentation/alignment for the same chained calls.

AGENTS.md
jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java[30-34]

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

## Issue description
`DuplicateCheckTest` includes broad indentation-only changes that are unrelated to the behavioral changes in this PR, creating review noise.

## Issue Context
The compliance checklist requires minimal diffs and avoiding unrelated reformatting.

## Fix Focus Areas
- jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java[30-34]
- jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java[103-344]
- jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java[423-452]
- jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java[457-552]
- jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java[559-629]

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


4. handleRefresh() behavior untested 📘 Rule violation ⛯ Reliability
Description
The refresh-button behavior in CitationRelationsTab was changed (error retry, confirmation on
recent fetch, refetch otherwise) without accompanying automated test coverage. This risks
regressions in the new decision logic.
Code

jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[R995-1022]

+    private void handleRefresh(CitationComponents citationComponents, CitationComponents otherCitationComponents) {
+        BibEntry entry = citationComponents.entry();
+        boolean isCites = citationComponents.searchType() == CitationFetcher.SearchType.CITES;
+
+        boolean hasError = citationComponents.listView().getPlaceholder() instanceof Label label
+                && label.getText().startsWith(Localization.lang("Error"));
+
+        if (hasError) {
+            searchForRelations(citationComponents, otherCitationComponents, true);
+            return;
+        }
+
+        boolean isUpdatable = isCites
+                              ? searchCitationsRelationsService.isReferencesUpdatable(entry)
+                              : searchCitationsRelationsService.isCitationsUpdatable(entry);
+
+        if (!isUpdatable) {
+            boolean confirmed = dialogService.showConfirmationDialogAndWait(
+                    Localization.lang("Refetch citations"),
+                    Localization.lang("The citations were fetched recently. Are you sure you want to refetch?")
+            );
+            if (!confirmed) {
+                return;
+            }
+        }
+
+        searchForRelations(citationComponents, otherCitationComponents, true);
+    }
Evidence
PR Compliance ID 15 requires updating/adding tests for behavior changes, and PR Compliance ID 16
requires test updates when org.jabref.logic changes; this PR adds new refresh decision logic and
new logic-service methods without any visible corresponding tests.

AGENTS.md
AGENTS.md
jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[995-1022]
jablib/src/main/java/org/jabref/logic/citation/SearchCitationsRelationsService.java[150-156]

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

## Issue description
Refresh-button behavior was changed, but there are no accompanying automated tests verifying the new branches (retry on error, confirmation on recent fetch, normal refetch otherwise).

## Issue Context
This PR adds new decision logic in the GUI layer and introduces new logic-service methods; compliance requires updating tests for behavior changes.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[470-474]
- jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[995-1022]
- jablib/src/main/java/org/jabref/logic/citation/SearchCitationsRelationsService.java[150-156]

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


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +405 to +416
simpleArticle.setField(StandardField.DOI, "10.1016/j.is.2004.02.002");
unrelatedArticle.setField(StandardField.DOI, "10.1016/J.IS.2004.02.002");

assertTrue(duplicateChecker.isDuplicate(simpleArticle, unrelatedArticle, BibDatabaseMode.BIBTEX));
}

@Test
void twoEntriesWithSameDoiButDifferentCasingAndDifferentTypesAreDuplicates() {
simpleArticle.setField(StandardField.DOI, "10.1016/j.is.2004.02.002");
unrelatedArticle.setField(StandardField.DOI, "10.1016/J.IS.2004.02.002");
unrelatedArticle.setType(StandardEntryType.Misc);

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. setfield() used in doi tests 📘 Rule violation ✓ Correctness

New tests mutate BibEntry using setField, contrary to the preferred withField style. This
reduces consistency with JabRef’s functional/withers pattern.
Agent Prompt
## Issue description
New tests use `setField` to modify `BibEntry` instances, but JabRef prefers `withField`.

## Issue Context
The project encourages immutable/functional patterns and consistent test/code style.

## Fix Focus Areas
- jablib/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java[405-418]

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

Comment on lines +999 to +1005
boolean hasError = citationComponents.listView().getPlaceholder() instanceof Label label
&& label.getText().startsWith(Localization.lang("Error"));

if (hasError) {
searchForRelations(citationComponents, otherCitationComponents, true);
return;
}
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

2. Localized error retry broken 🐞 Bug ✓ Correctness

CitationRelationsTab.handleRefresh detects an error state by checking whether the placeholder label
text starts with the localized token "Error", but the actual localized error sentences may not start
with that token (e.g., zh_CN), so errors are not recognized and refresh won’t reliably retry
immediately after a failure. Additionally, the DOI lookup failure path sets placeholders with
hardcoded "Error ", which will never match non-English Localization.lang("Error") prefixes.
Agent Prompt
## Issue description
`CitationRelationsTab.handleRefresh` decides whether to “retry immediately on error” by checking if the list placeholder is a `Label` whose text starts with `Localization.lang("Error")`. This is not reliable across locales (some translations of the full error sentence don’t start with the localized “Error” token) and also fails for the DOI lookup failure path, which sets placeholders to a hardcoded English prefix (`"Error " + ex.getMessage()`).

## Issue Context
The UI already sets error placeholders in `executeSearch(...).onFailure(...)` using fully localized sentences. The refresh logic should not depend on the *rendered* (localized) text to detect error state.

## Fix approach (one of these)
1) **Explicit state**: add a boolean/enum fetch state to `CitationComponents` or maintain a `Map<CitationComponents, FetchState>` in the tab; set it to ERROR on failures and OK on success. `handleRefresh` should consult that state.

2) **Stable marker on placeholder node**: when creating the error placeholder `Label`, set a stable marker such as `placeholder.setId("citation-relations-error")` or a style class. Then `handleRefresh` checks for that marker instead of string prefixes.

Also ensure the DOI lookup failure uses `Localization.lang(...)` and the same error marker.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[787-811]
- jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[847-858]
- jabgui/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java[995-1005]

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

@Syimyk43 Syimyk43 force-pushed the fix/citation-refresh-button branch from 6ec4b39 to be4c490 Compare March 18, 2026 08:45
- Ask user before refetching if data was fetched recently
- Retry immediately if last fetch resulted in an error
- Refetch normally if data is old enough (TTL expired)
Fixes JabRef#12247
@testlens-app

This comment has been minimized.

Comment thread CHANGELOG.md Outdated
- We enabled drag and drop of Windows shortcut (`.lnk`) files to open libraries. [#15036](https://github.com/JabRef/jabref/issues/15036)
- We refined the "Select files to import" page in "Search for unlinked local files" dialog to give the users the choice of linking the file to a related entry or import it to a new entry. [#13689](https://github.com/JabRef/jabref/issues/13689)
- The "Make/Sync bibliography" button in OO/LO panel now refreshes citations before generating bibliographies. [#14387](https://github.com/JabRef/jabref/issues/14387)
- Improved refresh button in Citation Relations tab: asks for confirmation if data was fetched recently, retries immediately on error, and refetches normally if data is old. [#12247](https://github.com/JabRef/jabref/issues/12247)
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.

Too much information.

Suggested change
- Improved refresh button in Citation Relations tab: asks for confirmation if data was fetched recently, retries immediately on error, and refetches normally if data is old. [#12247](https://github.com/JabRef/jabref/issues/12247)
- Improved responsiveness and user interface of refresh button in Citation Relations tab. [#12247](https://github.com/JabRef/jabref/issues/12247)


private void handleRefresh(CitationComponents citationComponents, CitationComponents otherCitationComponents) {
BibEntry entry = citationComponents.entry();
boolean isCites = citationComponents.searchType() == CitationFetcher.SearchType.CITES;
Copy link
Copy Markdown
Member

@calixtus calixtus Mar 19, 2026

Choose a reason for hiding this comment

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

Wouldnt it be easier to just put this into the arguments of the method?
Image

eg.

refreshCitingButton.setOnMouseClicked(_ -> handleRefresh(citingComponents, citedByComponents, refreshCitedByButton.setOnMouseClicked(_ -> handleRefresh(citedByComponents, citingComponents, CitationFetcher.SearchType.CITED_BY));

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.

Can you expand? The current code reads: if mouse is clicked on the left, a new handler is installed on the right?

Maybe, you mean putting CitationFetcher.SearchType.CITED_BY as parameter and keep the parameter order? However, this is not the style of this component ^^

Copy link
Copy Markdown
Member

@calixtus calixtus Mar 19, 2026

Choose a reason for hiding this comment

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

Maybe i misread the code. Was a quick interpretation by looking at CitationFetcher.SearchTypes enum providing only two alternatives CITES and CITED_BY and the method being called by the mentioned event triggers.

@testlens-app
Copy link
Copy Markdown

testlens-app Bot commented Mar 25, 2026

✅ All tests passed ✅

🏷️ Commit: a0d40d3
▶️ Tests: 10203 executed
⚪️ Checks: 52/52 completed


Learn more about TestLens at testlens.app.

@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels Mar 25, 2026
@Syimyk43
Copy link
Copy Markdown
Contributor Author

@calixtus @koppor I addressed the missing localization keys and shortened the CHANGELOG entry as suggested. All checks are passing. Ready for review.

@Syimyk43
Copy link
Copy Markdown
Contributor Author

@calixtus This is ready for review when you have a moment!

@Siedlerchr Siedlerchr added this pull request to the merge queue Mar 30, 2026
@github-actions github-actions Bot added the status: to-be-merged PRs which are accepted and should go into the merge-queue. label Mar 30, 2026
Merged via the queue into JabRef:main with commit ef82976 Mar 30, 2026
52 checks passed
Ranjeet2702 pushed a commit to Ranjeet2702/jabref that referenced this pull request Apr 14, 2026
* Improve refresh button behavior in Citation Relations tab
- Ask user before refetching if data was fetched recently
- Retry immediately if last fetch resulted in an error
- Refetch normally if data is old enough (TTL expired)
Fixes JabRef#12247

* Update CHANGELOG for JabRef#12247

* Add missing localization keys and shorten CHANGELOG entry
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve "Refresh button" of Citation Relations

4 participants