Skip to content

Fix PushToPreferences reset and import#15395

Merged
calixtus merged 13 commits into
mainfrom
cli_prefs_pushtopreferences
Mar 27, 2026
Merged

Fix PushToPreferences reset and import#15395
calixtus merged 13 commits into
mainfrom
cli_prefs_pushtopreferences

Conversation

@calixtus

@calixtus calixtus commented Mar 23, 2026

Copy link
Copy Markdown
Member

Related issues and pull requests

Follow-up to #14410

PR Description

Qodo:
• Removed redundant defaultFontSize property from WorkspacePreferences
• Refactored PushToApplicationPreferences to use factory pattern with getDefault() and setAll() methods
• Consolidated push application path constants into a single Map for maintainability
• Fixed preference import/export to properly restore PushToApplicationPreferences state
• Moved ImportFormatPreferences and LayoutFormatterPreferences to interface defaults
• Removed duplicate VSCode application entry in push applications list

Steps to test

  • Open File > Preferences
  • Clear or import preference

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
  • [.] 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

This comment was marked as outdated.

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

This comment was marked as outdated.

@calixtus calixtus added dev: code-quality Issues related to code or architecture decisions component: preferences status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers labels Mar 23, 2026
Comment thread jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java Outdated
Comment thread jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java Outdated
Comment thread jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java Outdated
Comment thread jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java Outdated
@testlens-app

This comment has been minimized.

@calixtus calixtus changed the title Cli prefs pushtopreferences Fix PushToPreferences reset and import Mar 23, 2026
@calixtus calixtus marked this pull request as draft March 23, 2026 19:48
@calixtus calixtus removed the status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers label Mar 23, 2026
@calixtus

calixtus commented Mar 23, 2026

Copy link
Copy Markdown
Member Author

Checkstyle fails because of checkstyle/checkstyle#19342 / checkstyle/checkstyle#17052

@testlens-app

This comment has been minimized.

@testlens-app

This comment has been minimized.

@testlens-app

This comment has been minimized.

@calixtus calixtus added the status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers label Mar 26, 2026
@calixtus calixtus marked this pull request as ready for review March 26, 2026 21:50
@calixtus calixtus enabled auto-merge March 26, 2026 21:50
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Refactor preferences handling and fix PushToApplicationPreferences reset/import

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Removed redundant defaultFontSize property from WorkspacePreferences
• Refactored PushToApplicationPreferences to use factory pattern with getDefault() and setAll()
  methods
• Consolidated push application path constants into a single Map for maintainability
• Fixed preference import/export to properly restore PushToApplicationPreferences state
• Moved ImportFormatPreferences and LayoutFormatterPreferences to interface defaults
• Removed duplicate VSCode application entry in push applications list
Diagram
flowchart LR
  A["PushToApplicationPreferences"] -->|"getDefault()"| B["Default Instance"]
  A -->|"setAll()"| C["Copy State"]
  D["JabRefCliPreferences"] -->|"getPushToApplicationPreferencesFromBackingStore()"| E["Load from Storage"]
  F["Import/Export"] -->|"restore"| A
  G["WorkspacePreferences"] -->|"removed defaultFontSize"| H["Simplified"]
Loading

Grey Divider

File Changes

1. jabgui/src/main/java/org/jabref/gui/WorkspacePreferences.java 🐞 Bug fix +0/-9

Removed redundant defaultFontSize property

jabgui/src/main/java/org/jabref/gui/WorkspacePreferences.java


2. jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java 🐞 Bug fix +0/-1

Removed defaultFontSize from workspace preferences loading

jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java


3. jabgui/src/main/java/org/jabref/gui/preferences/external/ExternalTabViewModel.java ✨ Enhancement +12/-14

Simplified PushToApplicationPreferences initialization with factory pattern

jabgui/src/main/java/org/jabref/gui/preferences/external/ExternalTabViewModel.java


View more (8)
4. jabgui/src/main/java/org/jabref/gui/push/GuiPushToApplication.java Formatting +4/-1

Reformatted method signature for readability

jabgui/src/main/java/org/jabref/gui/push/GuiPushToApplication.java


5. jabgui/src/main/java/org/jabref/gui/push/GuiPushToApplicationSettings.java ✨ Enhancement +2/-8

Simplified storeSettings to directly modify command paths map

jabgui/src/main/java/org/jabref/gui/push/GuiPushToApplicationSettings.java


6. jabgui/src/main/java/org/jabref/gui/push/GuiPushToApplications.java 🐞 Bug fix +1/-2

Removed duplicate VSCode application entry

jabgui/src/main/java/org/jabref/gui/push/GuiPushToApplications.java


7. jabgui/src/main/java/org/jabref/gui/theme/ThemeManager.java ✨ Enhancement +3/-2

Updated font size logic to use default factory method

jabgui/src/main/java/org/jabref/gui/theme/ThemeManager.java


8. jablib/src/main/java/org/jabref/logic/preferences/CliPreferences.java ✨ Enhancement +18/-4

Moved ImportFormatPreferences and LayoutFormatterPreferences to defaults

jablib/src/main/java/org/jabref/logic/preferences/CliPreferences.java


9. jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java ✨ Enhancement +90/-137

Refactored PushToApplicationPreferences initialization and storage logic

jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java


10. jablib/src/main/java/org/jabref/logic/push/PushToApplicationPreferences.java ✨ Enhancement +38/-10

Added factory pattern and setAll method for state management

jablib/src/main/java/org/jabref/logic/push/PushToApplicationPreferences.java


11. jabgui/src/main/resources/org/jabref/gui/preferences/external/ExternalTab.fxml Formatting +1/-1

Removed editable attribute from SearchableComboBox

jabgui/src/main/resources/org/jabref/gui/preferences/external/ExternalTab.fxml


Grey Divider

Qodo Logo

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

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

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (2) 📎 Requirement gaps (0) 📐 Spec deviations (0)

Grey Divider


Action required

1. getCommandPaths() mutated directly 📘 Rule violation ⛯ Reliability ⭐ New
Description
GuiPushToApplicationSettings.storeSettings() mutates the map returned by the preferences getter
via put(...) instead of creating a mutable copy and setting it back. This can break if the
returned map is immutable or shared, violating the rule to defensively copy before modifying
preference-returned collections.
Code

jabgui/src/main/java/org/jabref/gui/push/GuiPushToApplicationSettings.java[79]

+        preferences.getCommandPaths().put(application.getDisplayName(), path.getText());
Evidence
PR Compliance ID 30 forbids mutating collections returned by preference getters without copying to a
mutable collection first. The new code writes into preferences.getCommandPaths() directly using
put(...).

jabgui/src/main/java/org/jabref/gui/push/GuiPushToApplicationSettings.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
`GuiPushToApplicationSettings.storeSettings()` directly mutates the map returned by `preferences.getCommandPaths()`. Per compliance, preference-returned collections should be treated as potentially immutable/shared and must be copied before modification.

## Issue Context
The previous implementation created a new `HashMap<>(preferences.getCommandPaths())`, updated it, then stored it back. The new implementation uses `put` on the getter result.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/push/GuiPushToApplicationSettings.java[76-80]

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


2. Optional.get() in switch 📘 Rule violation ⛯ Reliability
Description
New code calls Optional.get() without proving presence, which can throw NoSuchElementException
when the key is unknown/corrupt. This risks crashes during preference persistence and violates the
safe-access requirement.
Code

jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[R1107-1115]

+    private void storePushToApplicationPath(Map<String, String> commandPair) {
+        commandPair.forEach((key, value) -> {
+            // is only for the preferences and therefore is okay to throw NoSuchElementException
+            switch (PushApplications.getApplicationByDisplayName(key).get()) {
+                case PushApplications.EMACS ->
+                        put(PUSH_EMACS_PATH, value);
+                case PushApplications.LYX ->
+                        put(PUSH_LYXPIPE, value);
+                case PushApplications.TEXMAKER ->
Evidence
PR Compliance ID 32 forbids unsafe Optional access such as Optional.get() without proven presence.
The added code uses PushApplications.getApplicationByDisplayName(key).get() inside
storePushToApplicationPath, which can throw if the Optional is empty.

jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1107-1115]
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
`storePushToApplicationPath` calls `Optional.get()` on the result of `PushApplications.getApplicationByDisplayName(key)` without guaranteeing the Optional is present, which can throw `NoSuchElementException`.
## Issue Context
This method processes preference keys/values and may encounter unexpected/unknown keys (e.g., corrupted prefs, future values, manual edits). Compliance requires safe Optional access.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1107-1131]
Suggested direction:
- Replace `.get()` with `ifPresent(...)` (ignore unknown keys) or `orElseThrow(...)` with a clear, handled exception path (if unknown keys should be treated as errors), or provide a safe default mapping.

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


3. Import refresh no-op 🐞 Bug ✓ Correctness
Description
JabRefCliPreferences#importPreferences copies each cached preference object onto itself
(setAll(getX())), so values imported into the Java Preferences backing store are never loaded into
the in-memory preferences objects.
Code

jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[R1044-1045]

+        getProxyPreferences().setAll(getProxyPreferences());
+        getPushToApplicationPreferences().setAll(getPushToApplicationPreferences());
Evidence
After importing into the backing store, the code performs setAll(getProxyPreferences()) and
setAll(getPushToApplicationPreferences()), which are self-assignments and therefore cannot reflect
newly imported backing-store values. Both getProxyPreferences() and
getPushToApplicationPreferences() cache instances, so without re-reading from the backing store,
imported values won't apply.

jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1037-1046]
jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1337-1343]
jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1080-1086]

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

## Issue description
`importPreferences` imports into the Java Preferences backing store, but then performs self-assignments (`setAll(getX())`), so imported values are never applied.
### Issue Context
Both proxy and push-to-application preferences are cached in fields and only initialized once.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1037-1046]
- jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1080-1105]
- jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1337-1374]
### Implementation notes
After `importPreferencesToBackingStore(path)`, rebuild fresh objects from the backing store and then `setAll` onto the cached instances, using the *current* values as defaults for missing keys. For example:
- `ProxyPreferences merged = getProxyPreferencesFromBackingStore(getProxyPreferences()); getProxyPreferences().setAll(merged);`
- `PushToApplicationPreferences merged = getPushToApplicationPreferencesFromBackingStore(getPushToApplicationPreferences()); getPushToApplicationPreferences().setAll(merged);`
(Ensure the push defaults-merging uses correct default map keys; see the separate finding.)

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


View more (2)
4. Push paths not persisted 🐞 Bug ⛯ Reliability
Description
JabRefCliPreferences listens to command-path changes using a property ChangeListener on the
MapProperty, but PushToApplicationPreferences updates the map via clear/putAll, which does not fire
a property change; thus storePushToApplicationPath is never invoked and changes are not written to
PREFS_NODE.
Code

jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1088]

+        pushToApplicationPreferences.getCommandPaths().addListener((_, _, newValue) -> storePushToApplicationPath(newValue));
Evidence
The listener registered is a 3-arg ChangeListener on the MapProperty, which only triggers when the
property value (map reference) is replaced. However, PushToApplicationPreferences#setCommandPaths
and #setAll mutate the existing observable map via clear()+putAll(), so no property change occurs
and persistence via storePushToApplicationPath is skipped.

jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1085-1092]
jablib/src/main/java/org/jabref/logic/push/PushToApplicationPreferences.java[61-68]
jablib/src/main/java/org/jabref/logic/push/PushToApplicationPreferences.java[82-89]
jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1107-1131]

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

## Issue description
Push-to-application command path updates are not persisted because the code registers a ChangeListener on the MapProperty instead of a MapChangeListener for map content mutations.
### Issue Context
`PushToApplicationPreferences#setCommandPaths` and `setAll` mutate the existing observable map (clear/putAll) without replacing the MapProperty value.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1087-1092]
- jablib/src/main/java/org/jabref/logic/push/PushToApplicationPreferences.java[61-89]
### Implementation notes
Replace the current listener with a `MapChangeListener` (or `InvalidationListener`) observing map content changes. For example:
- `pushToApplicationPreferences.getCommandPaths().addListener((MapChangeListener<String,String>) change -> storePushToApplicationPath(pushToApplicationPreferences.getCommandPaths()));`
Alternatively, change `setCommandPaths` to replace the property value (set a new observable map) so a ChangeListener would fire—but the MapChangeListener approach is the most direct.

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


5. Push defaults lookup broken 🐞 Bug ✓ Correctness
Description
readPushToApplicationPath uses preference-key constants (e.g., PUSH_EMACS_PATH) to look up defaults
in a map that is keyed by application display names, so it falls back to empty strings and loses
OS-detected defaults.
Code

jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[R1139-1147]

+        commands.put(PushApplications.EMACS.getDisplayName(), get(PUSH_EMACS_PATH, defaults.getOrDefault(PUSH_EMACS_PATH, "")));
+        commands.put(PushApplications.LYX.getDisplayName(), get(PUSH_LYXPIPE, defaults.getOrDefault(PUSH_LYXPIPE, "")));
+        commands.put(PushApplications.TEXMAKER.getDisplayName(), get(PUSH_TEXMAKER_PATH, defaults.getOrDefault(PUSH_TEXMAKER_PATH, "")));
+        commands.put(PushApplications.TEXSTUDIO.getDisplayName(), get(PUSH_TEXSTUDIO_PATH, defaults.getOrDefault(PUSH_TEXSTUDIO_PATH, "")));
+        commands.put(PushApplications.TEXWORKS.getDisplayName(), get(PUSH_TEXWORKS_PATH, defaults.getOrDefault(PUSH_TEXWORKS_PATH, "")));
+        commands.put(PushApplications.VIM.getDisplayName(), get(PUSH_VIM, defaults.getOrDefault(PUSH_VIM, "")));
+        commands.put(PushApplications.WIN_EDT.getDisplayName(), get(PUSH_WINEDT_PATH, defaults.getOrDefault(PUSH_WINEDT_PATH, "")));
+        commands.put(PushApplications.SUBLIME_TEXT.getDisplayName(), get(PUSH_SUBLIME_TEXT_PATH, defaults.getOrDefault(PUSH_SUBLIME_TEXT_PATH, "")));
+        commands.put(PushApplications.VSCODE.getDisplayName(), get(PUSH_VSCODE_PATH, defaults.getOrDefault(PUSH_VSCODE_PATH, "")));
Evidence
PushToApplicationPreferences.getDefault() builds its commandPaths map keyed by
PushApplications.*.getDisplayName() (e.g., "Emacs"), but readPushToApplicationPath tries to read
defaults from that map using keys like "emacsPath" (PUSH_EMACS_PATH). As a result,
defaults.getOrDefault(PUSH_EMACS_PATH, "") always misses and returns "", discarding the intended
OS.detectProgramPath defaults.

jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1136-1148]
jablib/src/main/java/org/jabref/logic/push/PushToApplicationPreferences.java[37-55]
jablib/src/main/java/org/jabref/logic/push/PushApplications.java[7-16]
jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[415-427]

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

## Issue description
`readPushToApplicationPath` reads defaults from a `commandPaths` map keyed by application display names, but uses preference-key constants for lookup, so it always falls back to empty strings.
### Issue Context
`PushToApplicationPreferences.getDefault().getCommandPaths()` is keyed by `PushApplications.*.getDisplayName()`.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[1139-1147]
- jablib/src/main/java/org/jabref/logic/push/PushToApplicationPreferences.java[37-55]
### Implementation notes
Change default lookups to use display-name keys, e.g.
- `get(PUSH_EMACS_PATH, defaults.getOrDefault(PushApplications.EMACS.getDisplayName(), ""))`
Repeat for each application entry. This preserves existing preference storage keys (PUSH_* constants) while correctly sourcing default values from the defaults command-path map.

ⓘ 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

@testlens-app

testlens-app Bot commented Mar 26, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: 40b18aa
▶️ Tests: 10203 executed
⚪️ Checks: 94/94 completed


Learn more about TestLens at testlens.app.

<Label text="%Application to push entries to"/>
<SearchableComboBox fx:id="pushToApplicationCombo"
prefWidth="200.0" GridPane.columnIndex="1" editable="false"/>
prefWidth="200.0" GridPane.columnIndex="1"/>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

default behaviour

@calixtus calixtus added this pull request to the merge queue Mar 27, 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 27, 2026
Merged via the queue into main with commit 5772d68 Mar 27, 2026
94 of 95 checks passed
@calixtus calixtus deleted the cli_prefs_pushtopreferences branch March 27, 2026 21:21
Siedlerchr added a commit to geovani-rocha/jabref that referenced this pull request Mar 28, 2026
…o fix-group-icons

* 'fix-group-icons' of github.com:geovani-rocha/jabref: (26 commits)
  chore(deps): update dependency org.apache.logging.log4j:log4j-to-slf4j to v2.25.4 (JabRef#15436)
  chore(deps): update jackson monorepo to v3.1.1 (JabRef#15435)
  Fix PushToPreferences reset and import (JabRef#15395)
  Add fulltext fetcher for Wiley via their TDM API (JabRef#15388)
  Embed in-text nature in reference marks for CSL citations (JabRef#15381)
  Chore(deps): Bump com.gradleup.shadow:shadow-gradle-plugin (JabRef#15430)
  Fix not on fx thread exceptions for cleanup and cite key generator (JabRef#15424)
  Revert "Update gradle to nightly of 2026-03-23 (JabRef#15372)"
  feat: add benchmarks for Lucene fulltext search and linked file indexing, including setup and teardown of the index. (JabRef#15385)
  Chore(deps): Bump org.openrewrite.recipe:rewrite-recipe-bom (JabRef#15418)
  Add claude gitignore (JabRef#15413)
  Fix group filter icon in side pane (JabRef#15408)
  Add new prs_link feature
  Chore(deps): Bump org.glassfish.hk2:hk2-api in /versions (JabRef#15422)
  Chore(deps): Bump org.openrewrite.rewrite from 7.28.2 to 7.29.0 (JabRef#15419)
  Chore(deps): Bump jablib/src/main/resources/csl-styles (JabRef#15417)
  Fix for inconsistent "hide tab bar" behavior (JabRef#15409)
  Update dependency org.glassfish.hk2:hk2-utils to v4 (JabRef#15407)
  Persist file notifications (JabRef#15403)
  Update dependency org.glassfish.hk2:hk2-locator to v4 (JabRef#15405)
  ...
Ranjeet2702 pushed a commit to Ranjeet2702/jabref that referenced this pull request Apr 14, 2026
* Initial cleanups

* Adapt PushToApplicationPreferences

* Fix artifact

* Cleanup in WorkspacePreferences

* Fix import

* Fix and simplify Map of command paths

* Fix duplicate VSCode application

* Stylistic changes

* Fix empty strings in prefs

* Fix resetting of application settings

* Fix checkstyle

---------

Co-authored-by: Carl Christian Snethlage <calixtus@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: preferences dev: code-quality Issues related to code or architecture decisions status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers status: to-be-merged PRs which are accepted and should go into the merge-queue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants