Skip to content

Cancel in duplicate resolver incorrectly keeps the left entry - #16234

Merged
Siedlerchr merged 6 commits into
JabRef:mainfrom
geritwagner:patch-1
Jul 16, 2026
Merged

Cancel in duplicate resolver incorrectly keeps the left entry#16234
Siedlerchr merged 6 commits into
JabRef:mainfrom
geritwagner:patch-1

Conversation

@geritwagner

@geritwagner geritwagner commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

PR Description

Fixes the duplicate resolver so that clicking Cancel returns BREAK instead of KEEP_LEFT. This stops duplicate resolution without removing either entry from the current pair.

Steps to test

AI usage


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

@github-actions

Copy link
Copy Markdown
Contributor

Hey @geritwagner! 👋

Thank you for contributing to JabRef!

We have automated checks in place, based on which you will soon get feedback if any of them are failing. We also use Qodo for review assistance. It will update your pull request description with a review help and offer suggestions to improve the pull request.

After all automated checks pass, a maintainer will also review your contribution. Once that happens, you can go through their comments in the "Files changed" tab and act on them, or reply to the conversation if you have further inputs. You can read about the whole pull request process in our contribution guide.

Please ensure that your pull request is in line with our AI Usage Policy and make necessary disclosures.

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

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Fix Cancel action in duplicate resolver to stop resolution (BREAK)

🐞 Bug fix 🕐 Less than 10 minutes

Grey Divider

AI Description

• Make Duplicate Resolver **Cancel** return BREAK instead of KEEP_LEFT.
• Prevent accidental retention/removal behavior when users abort duplicate resolution.
• Ensure current duplicate pair remains unchanged when cancelling.
Diagram

graph TD
  U(("User")) --> D["DuplicateResolverDialog"] --> R["Result: BREAK"] --> F["Duplicate resolution flow"]

  subgraph Legend
    direction LR
    _actor(("Actor")) ~~~ _ui["UI component"] ~~~ _res["Result enum"] ~~~ _flow["Workflow"]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Bind results via button userData/action handlers
  • ➕ Removes brittle button.equals(...) chain
  • ➕ Makes each button’s outcome explicit and localized
  • ➕ Reduces risk of future mis-mapping when UI changes
  • ➖ Slightly more wiring in dialog initialization
  • ➖ May require minor refactor beyond the minimal fix
2. Add a focused UI/controller regression test for Cancel behavior
  • ➕ Prevents recurrence of Cancel mapping regressions
  • ➕ Documents expected semantics: cancel stops without selecting either entry
  • ➖ May require test harness/mocking for JavaFX dialogs
  • ➖ More effort than the one-line fix

Recommendation: Current one-line fix is appropriate for restoring correct behavior quickly. Consider a follow-up to bind results directly to button metadata/handlers and/or add a small regression test to ensure Cancel always maps to BREAK.

Files changed (1) +1 / -1

Bug fix (1) +1 / -1
DuplicateResolverDialog.javaReturn BREAK when Cancel is pressed +1/-1

Return BREAK when Cancel is pressed

• Changes the Cancel button handling to return 'DuplicateResolverResult.BREAK' instead of 'KEEP_LEFT'. This ensures cancelling stops duplicate resolution without implicitly choosing the left entry.

jabgui/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java

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

qodo-free-for-open-source-projects Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

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

Grey Divider


Remediation recommended

1. Cancel doesn't cancel scan ✓ Resolved 🐞 Bug ➹ Performance
Description
Cancel now returns DuplicateResolverResult.BREAK, but in DuplicateSearch the BREAK handling only
clears the queue and flips libraryAnalyzed without stopping the background
searchPossibleDuplicates task. This can keep the O(n²) duplicate scan running after the user
cancels, wasting CPU and continuing to enqueue duplicates until the scan completes.
Code

jabgui/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java[R150-154]

       } else if (button.equals(removeExact)) {
           return DuplicateResolverResult.AUTOREMOVE_EXACT;
       } else if (button.equals(cancel)) {
-                return DuplicateResolverResult.KEEP_LEFT;
+                return DuplicateResolverResult.BREAK;
       }
Evidence
The dialog now maps Cancel to BREAK, and BREAK is handled in DuplicateSearch by clearing the
consumer queue only. The producer (searchPossibleDuplicates) never checks libraryAnalyzed/a
cancel flag and will keep adding duplicates; it only stops if interrupted, but the scan is launched
via executeInterruptableTask which does not return a Future to cancel.

jabgui/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java[137-156]
jabgui/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java[95-120]
jabgui/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java[183-197]
jablib/src/main/java/org/jabref/logic/util/HeadlessExecutorService.java[99-101]

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

## Issue description
Clicking **Cancel** now returns `DuplicateResolverResult.BREAK`, which stops UI-driven resolution, but the background producer (`searchPossibleDuplicates`) is not cancelled and can continue scanning and enqueueing duplicates.
## Issue Context
- `DuplicateSearch` starts the scan via `HeadlessExecutorService.INSTANCE.executeInterruptableTask(...)`, which currently provides no handle to cancel.
- BREAK handling clears the queue, but the producer loop does not observe any cancel flag (it only checks `Thread.interrupted()`).
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java[150-154]
- jabgui/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java[95-120]
- jabgui/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java[183-197]
- jablib/src/main/java/org/jabref/logic/util/HeadlessExecutorService.java[99-101]
## Suggested approach
- Change duplicate scan execution to return a `Future<?>` (e.g., use `submit(...)`) and store it in `DuplicateSearch`.
- When resolver result is `BREAK`, call `future.cancel(true)` (and/or set an explicit `AtomicBoolean cancelled` checked inside `searchPossibleDuplicates`).
- Update `searchPossibleDuplicates` to exit early when cancellation is requested (in addition to `Thread.interrupted()`).

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


Grey Divider

Qodo Logo

koppor
koppor previously approved these changes Jul 15, 2026

@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.

Think, this looks good

@Siedlerchr

Copy link
Copy Markdown
Member

@geritwagner Looks good to me overall, can you comment on the qodo comment? Is that a valid concern?

Siedlerchr
Siedlerchr previously approved these changes Jul 15, 2026
* upstream/main: (29 commits)
  fix cooldown zero not supported
  Fix PDF title extraction misreading kerns as word boundaries (#16246)
  Set cooldown days to 1 (#16257)
  Chore(deps): Bump com.dlsc.gemsfx:gemsfx in /versions (#16263)
  Chore(deps): Bump org.cyclonedx.bom from 3.2.4 to 3.3.0 (#16262)
  Chore(deps): Bump jablib/src/main/resources/csl-styles (#16261)
  fix: NPE in zbMATH fetcher and mock network requests (#16106)
  Chore(deps): Bump org.jabref:latex-conv from 0.1.0-SNAPSHOT to 0.1.0 in /versions (#16254)
  Move linked-files editor buttons into trailing row (#16243)
  Chore(deps): Bump org.jetbrains.kotlin:kotlin-stdlib-jdk8 in /versions (#16253)
  Chore(deps): Bump org.openrewrite.rewrite from 7.36.0 to 7.37.0 (#16252)
  Chore(deps): Bump org.openrewrite.recipe:rewrite-recipe-bom (#16251)
  Prevent stale search results from outdated queries (#16212)
  Fix DateFormatter to use explicit Locale.US instead of system default (#16225)
  New translations jabref_en.properties (Italian) (#16244)
  Replace latex2unicode with the latex-conv library (#16242)
  Fix Postgres binary packaging (#16156)
  Fix IllegalArgumentException when relativizing paths with different roots (#16200)
  fixes the SpringerNatureWebFetcherTest (#16216)
  New Crowdin updates (#16214)
  ...
@Siedlerchr
Siedlerchr dismissed stale reviews from koppor and themself via 890c626 July 15, 2026 18:53
Siedlerchr
Siedlerchr previously approved these changes Jul 15, 2026
@github-actions github-actions Bot added status: changes-required Pull requests that are not yet complete and removed status: no-bot-comments labels Jul 15, 2026
* upstream/main:
  Fix verbatim BibTeX import from PDFs when text precedes the entry (#16245)
Siedlerchr
Siedlerchr previously approved these changes Jul 15, 2026
@Siedlerchr
Siedlerchr enabled auto-merge July 15, 2026 18:56
@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels Jul 15, 2026
@Siedlerchr
Siedlerchr added this pull request to the merge queue Jul 15, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Jul 15, 2026
@Siedlerchr
Siedlerchr enabled auto-merge July 15, 2026 19:59
@geritwagner

Copy link
Copy Markdown
Contributor Author

@geritwagner Looks good to me overall, can you comment on the qodo comment? Is that a valid concern?

I believe runtime is a valid concern in the deduplication procedure. In my view, fully separating duplicate search from duplicate resolution should be preferred (see #15190). Once this is implemented, the cancel option in duplicate resolution should no longer affect the runtime of duplicate search. More broadly, a practical solution to the O(n²) runtime issue would be to introduce a blocking stage (see 1).

@Siedlerchr
Siedlerchr disabled auto-merge July 16, 2026 08:19
@Siedlerchr

Siedlerchr commented Jul 16, 2026

Copy link
Copy Markdown
Member

@geritwagner That is indeed a point I have thought about as well in trying to optimize the runtime of the duplicate search. I now implemented at least the task cancelling here a well.

I created an internal issue for tackling the runtime problem

@Siedlerchr
Siedlerchr merged commit 9d46866 into JabRef:main Jul 16, 2026
53 of 54 checks passed
@geritwagner

Copy link
Copy Markdown
Contributor Author

The blocking procedures of ASySD and BibDedupe may be a suitable starting point for that.

Siedlerchr added a commit that referenced this pull request Jul 16, 2026
…-textflow

* upstream/main:
  Fix exit of LspClientHandler (#16268)
  Cancel in duplicate resolver incorrectly keeps the left entry (#16234)
  fix: jabkit pdf update not writing XMP metadata to existing PDFs (#16117)
  Fix inspire texkey arxiv import (#16235)
  Fix verbatim BibTeX import from PDFs when text precedes the entry (#16245)
  fix cooldown zero not supported
  Fix PDF title extraction misreading kerns as word boundaries (#16246)
  Set cooldown days to 1 (#16257)
  Chore(deps): Bump com.dlsc.gemsfx:gemsfx in /versions (#16263)
  Chore(deps): Bump org.cyclonedx.bom from 3.2.4 to 3.3.0 (#16262)
  Chore(deps): Bump jablib/src/main/resources/csl-styles (#16261)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants