Skip to content

Allow drag and drop of Windows shortcut (.lnk) files #15053

Open
faneeshh wants to merge 8 commits intoJabRef:mainfrom
faneeshh:fix-for-issue-15036
Open

Allow drag and drop of Windows shortcut (.lnk) files #15053
faneeshh wants to merge 8 commits intoJabRef:mainfrom
faneeshh:fix-for-issue-15036

Conversation

@faneeshh
Copy link
Contributor

@faneeshh faneeshh commented Feb 8, 2026

Related issues and pull requests

Closes #15036

PR Description

Implemented support for dragging and dropping Windows shortcut (.lnk) files into JabRef to open libraries. By resolving these shortcuts using the mslinks library, users can now open BibTeX databases directly via shortcuts, preventing file resolution errors. The implementation ensures that only shortcuts pointing to valid .bib files are accepted and processed.

Steps to test

  1. Create a .bib file on your Windows desktop (e.g., test.bib).
  2. Right click the file and select "Create shortcut".
  3. Open JabRef and drag the newly created .lnk file onto the main window.
  4. Observation: The library should open successfully.
  5. Repeat with a shortcut pointing to a .pdf or directory; it should be gracefully ignored.

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
Copy link
Contributor

Review Summary by Qodo

Support drag and drop of Windows shortcut (.lnk) files

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add support for dragging and dropping Windows shortcut (.lnk) files
• Resolve .lnk shortcuts to target .bib files using PowerShell
• Update FrameDndHandler and MainTable to handle shortcut resolution
• Add utility methods in FileUtil for shortcut detection and resolution
Diagram
flowchart LR
  A["User drags .lnk file"] --> B["FrameDndHandler/MainTable receives file"]
  B --> C["FileUtil.isShortcutFile checks extension"]
  C --> D["FileUtil.resolveWindowsShortcut uses PowerShell"]
  D --> E["Target .bib file path returned"]
  E --> F["Library opens with entries"]
Loading

Grey Divider

File Changes

1. jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java ✨ Enhancement +19/-1

Add shortcut resolution to drag-drop handler

• Modified getBibFiles() method to handle shortcut file resolution
• Added flatMap logic to resolve .lnk files to their targets
• Added isAcceptedFile() helper method to check for .bib or .lnk files
• Imported Stream class for stream operations

jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java


2. jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java ✨ Enhancement +3/-2

Integrate shortcut resolution in MainTable drag-drop

• Updated handleOnDragDropped() to resolve shortcuts before processing
• Updated handleOnDragDroppedTableView() to resolve shortcuts before import
• Added import for FileUtil class
• Both methods now call FileUtil.resolveWindowsShortcuts() on dropped files

jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java


3. jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java ✨ Enhancement +74/-0

Add Windows shortcut detection and resolution utilities

• Added isShortcutFile() method to detect .lnk files by extension
• Added resolveWindowsShortcut() method using PowerShell to resolve shortcuts
• Added resolveWindowsShortcuts() utility method to batch resolve multiple paths
• PowerShell implementation handles Windows-only execution with proper error handling

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java


View more (1)
4. CHANGELOG.md 📝 Documentation +1/-0

Document shortcut drag-drop feature

• Added entry documenting support for drag and drop of Windows shortcut files
• References issue #15036 with proper link formatting

CHANGELOG.md


Grey Divider

Qodo Logo

@github-actions github-actions bot added the good first issue An issue intended for project-newcomers. Varies in difficulty. label Feb 8, 2026
@qodo-free-for-open-source-projects
Copy link
Contributor

qodo-free-for-open-source-projects bot commented Feb 8, 2026

Code Review by Qodo

🐞 Bugs (7) 📘 Rule violations (5) 📎 Requirement gaps (0)

Grey Divider


Action required

1. .lnk accepted on non-Windows platforms 🐞 Bug ✓ Correctness ⭐ New
Description
isAcceptedFile() calls FileUtil.isShortcutFile() with no OS guard. On macOS/Linux a dropped
.lnk file passes the filter, but resolveIfShortcut() is a no-op there and returns the original
.lnk path, which is then fed to the BibTeX parser producing confusing parse errors.
Code

jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[R242-244]

+    private boolean isAcceptedFile(Path path) {
+        return FileUtil.isBibFile(path) || FileUtil.isShortcutFile(path);
+    }
Evidence
isShortcutFile() only checks the file extension with no OS check, so it returns true on any
platform. resolveIfShortcut() immediately returns the original path when !OS.WINDOWS, so the
.lnk binary is never resolved. openTheFile() only guards on Files.exists(), so the binary
.lnk content reaches the BibTeX parser.

jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[242-244]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[530-532]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[540-543]
jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[219-222]

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

## Issue description
`isAcceptedFile()` in `FrameDndHandler` accepts `.lnk` files on all operating systems because `FileUtil.isShortcutFile()` only checks the file extension. On non-Windows systems `resolveIfShortcut()` is a no-op and returns the original `.lnk` binary path, which is then parsed as BibTeX.

## Issue Context
- `FileUtil.isShortcutFile()` has no OS check (extension-only test).
- `FileUtil.resolveIfShortcut()` guards with `!OS.WINDOWS` and returns the original path immediately on non-Windows.
- `openTheFile()` only checks `Files.exists()`, so the `.lnk` binary reaches the parser.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[242-244]

Change the method to:
```java
private boolean isAcceptedFile(Path path) {
   return FileUtil.isBibFile(path) || (OS.WINDOWS && FileUtil.isShortcutFile(path));
}
```

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


2. No .bib validation after shortcut resolution 🐞 Bug ✓ Correctness ⭐ New
Description
After resolving a .lnk shortcut, the code never checks whether the resolved target is a .bib
file. A shortcut pointing to a .pdf, .docx, or any other file type is silently forwarded to
openTheFile() and parsed as a BibTeX database, producing confusing errors with no explanation.
Code

jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[R168-171]

+        // Resolve any shortcuts to their targets
+        List<Path> resolvedFiles = filesToOpen.stream()
+                                              .map(FileUtil::resolveIfShortcut)
+                                              .collect(Collectors.toList());
Evidence
Before this PR, getBibFiles() applied filter(FileUtil::isBibFile), guaranteeing only .bib
files reached openFiles(). The new code replaces that filter with isAcceptedFile() (which admits
all .lnk files), and openFiles() maps resolveIfShortcut over the list but never re-validates
the resolved extension before calling openTheFile(). openTheFile() only checks Files.exists(),
so any non-.bib resolved target is parsed as BibTeX.

jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[168-201]
jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[242-244]
jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[219-222]

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

## Issue description
After `resolveIfShortcut()` maps each path to its target, there is no check that the resolved target is actually a `.bib` file. A `.lnk` pointing to a `.pdf` or any other file type will be passed to `openTheFile()` and parsed as BibTeX.

## Issue Context
- The old code used `filter(FileUtil::isBibFile)` in `getBibFiles()` to guarantee only `.bib` files reached `openFiles()`.
- The new code removes that guarantee: `isAcceptedFile()` admits all `.lnk` files, and `openFiles()` does not re-validate after resolution.
- `openTheFile()` only checks `Files.exists()` (line 220), not the file extension.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[168-171]

Add a `.bib` filter after resolution:
```java
List&lt;Path&gt; resolvedFiles = filesToOpen.stream()
   .map(FileUtil::resolveIfShortcut)
   .filter(FileUtil::isBibFile)   // ← ensure resolved target is a .bib file
   .collect(Collectors.toList());
```

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


3. Failed resolution falls back to parsing .lnk binary 🐞 Bug ⛯ Reliability ⭐ New
Description
When resolveIfShortcut() fails (exception thrown, or the shortcut target does not exist on disk),
it returns the original .lnk path. That path passes openTheFile()'s Files.exists() guard (the
.lnk file itself exists), so the binary .lnk content is fed to the BibTeX parser instead of
surfacing a clear "shortcut target not found" error. Broken/orphaned shortcuts are common in
real-world Windows usage.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R556-561]

+        } catch (IOException | ShellLinkException e) {
+            LOGGER.warn("Could not resolve shortcut {}", path, e);
+        }
+
+        return path;
+    }
Evidence
resolveIfShortcut() returns the original .lnk path in two failure cases: (1) an IOException or
ShellLinkException is caught, and (2) the resolved target path does not exist
(!Files.exists(resolvedPath)). In both cases the returned .lnk path is a real file on disk, so
openTheFile()'s Files.exists() guard is bypassed and the binary .lnk data is parsed as BibTeX.

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[549-560]
jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[219-222]

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

## Issue description
When `resolveIfShortcut()` cannot resolve a shortcut (broken link, missing target, or parse exception), it returns the original `.lnk` path. This path passes `openTheFile()`&#x27;s `Files.exists()` check and the binary `.lnk` content is parsed as BibTeX, producing confusing errors instead of a clear failure message.

## Issue Context
- `resolveIfShortcut()` returns the original path on exception (line 556-560) and when the resolved target does not exist (line 552-554).
- The `.lnk` file itself exists on disk, so `openTheFile()`&#x27;s `Files.exists()` guard (line 220) does not filter it out.
- Broken shortcuts (target deleted or moved) are common in real-world Windows usage.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[549-561]
- jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[168-171]

Preferred fix — add `filter(FileUtil::isBibFile)` after resolution in `openFiles()` (covers both this issue and Finding 2):
```java
List&lt;Path&gt; resolvedFiles = filesToOpen.stream()
   .map(FileUtil::resolveIfShortcut)
   .filter(FileUtil::isBibFile)
   .collect(Collectors.toList());
```
Alternatively, change `resolveIfShortcut()` to return `Optional&lt;Path&gt;` so callers can explicitly handle resolution failure.

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


View more (4)
4. resolveWindowsShortcut catches Exception 📘 Rule violation ⛯ Reliability
Description
resolveWindowsShortcut wraps the whole PowerShell invocation in a broad catch (Exception) and
degrades by returning Optional.empty(), which can silently drop .lnk files during drag-and-drop
without a clear recovery path. • The method blocks on process.waitFor() with no timeout and does
not restore the interrupt flag on interruption, which risks hangs and makes failures harder to
diagnose reliably. • This violates the requirement for specific, non-disruptive exception handling
and robust edge-case management around external process execution.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R548-584]

+        try {
+            // Use PowerShell to resolve the shortcut target
+            ProcessBuilder pb = new ProcessBuilder(
+                    "powershell", "-NoProfile", "-Command",
+                    "$ws = New-Object -ComObject WScript.Shell; " +
+                            "$shortcut = $ws.CreateShortcut('" + shortcutPath.toAbsolutePath().toString().replace("'", "''") + "'); " +
+                            "Write-Output $shortcut.TargetPath"
+            );
+
+            pb.redirectErrorStream(true);
+            Process process = pb.start();
+
+            String targetPath;
+            try (java.io.BufferedReader reader = new java.io.BufferedReader(
+                    new java.io.InputStreamReader(process.getInputStream()))) {
+                targetPath = reader.readLine();
+            }
+
+            int exitCode = process.waitFor();
+
+            if (exitCode == 0 && targetPath != null && !targetPath.trim().isEmpty()) {
+                Path resolvedPath = Path.of(targetPath.trim());
+                if (Files.exists(resolvedPath)) {
+                    LOGGER.debug("Resolved shortcut {} to {}", shortcutPath, resolvedPath);
+                    return Optional.of(resolvedPath);
+                } else {
+                    LOGGER.warn("Shortcut target does not exist: {}", resolvedPath);
+                    return Optional.empty();
+                }
+            } else {
+                LOGGER.warn("Failed to resolve shortcut: {} (exit code: {})", shortcutPath, exitCode);
+                return Optional.empty();
+            }
+        } catch (Exception e) {
+            LOGGER.warn("Exception while resolving shortcut: {}", shortcutPath, e);
+            return Optional.empty();
+        }
Evidence
The compliance checklist requires specific exception handling and robust handling of failure points.
The new code uses a broad catch (Exception) and performs an unbounded waitFor(), returning empty
results on failures, which can lead to silent failures and potential hangs.

Rule 3: Generic: Robust Error Handling and Edge Case Management
AGENTS.md
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[566-566]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[581-583]

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

## Issue description
`FileUtil.resolveWindowsShortcut` catches a broad `Exception` and blocks on `process.waitFor()` without a timeout. This can lead to silent failures (returning `Optional.empty()`), and it risks hanging the application thread.
## Issue Context
The shortcut resolution executes an external process (PowerShell) which is a high-failure-risk dependency (missing executable, permission issues, COM errors, long-running/hung process). Exception handling should be specific and interruptions should preserve the thread interrupt status.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[548-584]

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


5. Missing tests for .lnk 📘 Rule violation ✓ Correctness
Description
• New behavior was introduced for resolving Windows shortcuts via
FileUtil.resolveWindowsShortcut/resolveWindowsShortcuts and used in drag-and-drop flows, but no
corresponding JUnit tests were added/updated to cover this behavior. • The existing FileUtilTest
covers file extension handling, yet does not include .lnk cases, leaving key edge cases
(non-Windows behavior, failure modes) unverified.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R524-596]

+    /// Test if the file is a shortcut file by simply checking the extension to be ".lnk"
+    ///
+    /// @param file The file to check
+    /// @return True if file extension is ".lnk", false otherwise
+    public static boolean isShortcutFile(Path file) {
+        return getFileExtension(file).filter("lnk"::equals).isPresent();
+    }
+
+    /// Resolves a Windows shortcut (.lnk) file to its target path.
+    /// Only works on Windows systems. On other systems or if resolution fails, returns empty Optional.
+    ///
+    /// @param shortcutPath The path to the .lnk file
+    /// @return Optional containing the target path, or empty if resolution fails
+    public static Optional<Path> resolveWindowsShortcut(Path shortcutPath) {
+        if (!isShortcutFile(shortcutPath)) {
+            return Optional.empty();
+        }
+
+        // Only attempt on Windows
+        if (!OS.WINDOWS) {
+            LOGGER.debug("Shortcut resolution only supported on Windows");
+            return Optional.empty();
+        }
+
+        try {
+            // Use PowerShell to resolve the shortcut target
+            ProcessBuilder pb = new ProcessBuilder(
+                    "powershell", "-NoProfile", "-Command",
+                    "$ws = New-Object -ComObject WScript.Shell; " +
+                            "$shortcut = $ws.CreateShortcut('" + shortcutPath.toAbsolutePath().toString().replace("'", "''") + "'); " +
+                            "Write-Output $shortcut.TargetPath"
+            );
+
+            pb.redirectErrorStream(true);
+            Process process = pb.start();
+
+            String targetPath;
+            try (java.io.BufferedReader reader = new java.io.BufferedReader(
+                    new java.io.InputStreamReader(process.getInputStream()))) {
+                targetPath = reader.readLine();
+            }
+
+            int exitCode = process.waitFor();
+
+            if (exitCode == 0 && targetPath != null && !targetPath.trim().isEmpty()) {
+                Path resolvedPath = Path.of(targetPath.trim());
+                if (Files.exists(resolvedPath)) {
+                    LOGGER.debug("Resolved shortcut {} to {}", shortcutPath, resolvedPath);
+                    return Optional.of(resolvedPath);
+                } else {
+                    LOGGER.warn("Shortcut target does not exist: {}", resolvedPath);
+                    return Optional.empty();
+                }
+            } else {
+                LOGGER.warn("Failed to resolve shortcut: {} (exit code: {})", shortcutPath, exitCode);
+                return Optional.empty();
+            }
+        } catch (Exception e) {
+            LOGGER.warn("Exception while resolving shortcut: {}", shortcutPath, e);
+            return Optional.empty();
+        }
+    }
+
+    public static List<Path> resolveWindowsShortcuts(List<Path> paths) {
+        return paths.stream()
+                    .flatMap(path -> {
+                        if (isShortcutFile(path)) {
+                            return resolveWindowsShortcut(path).stream();
+                        }
+                        return Stream.of(path);
+                    })
+                    .toList();
+    }
Evidence
The checklist requires appropriate tests for behavior changes. The PR adds new shortcut-resolution
behavior in FileUtil, but existing FileUtilTest extension cases do not include .lnk,
indicating the new functionality is not covered by tests.

AGENTS.md
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-596]
jablib/src/test/java/org/jabref/logic/util/io/FileUtilTest.java[179-190]

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 Windows shortcut (`.lnk`) drag-and-drop support introduces new `FileUtil` behavior without corresponding test coverage.
## Issue Context
At minimum, tests should cover:
- `isShortcutFile` correctly detecting `.lnk` (case handling if intended)
- `resolveWindowsShortcuts` returning the original paths for non-shortcut inputs
- non-Windows behavior: `resolveWindowsShortcut` should return `Optional.empty()` without throwing
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-596]
- jablib/src/test/java/org/jabref/logic/util/io/FileUtilTest.java[179-210]

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


6. Case-sensitive .lnk check 🐞 Bug ✓ Correctness
Description
isShortcutFile uses exact string match ("lnk"::equals) but getFileExtension does not
actually lowercase the extension, despite its documentation. • As a result, shortcuts like
FILE.LNK may not be recognized, breaking the new drag-and-drop behavior on Windows.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R524-530]

+    /// Test if the file is a shortcut file by simply checking the extension to be ".lnk"
+    ///
+    /// @param file The file to check
+    /// @return True if file extension is ".lnk", false otherwise
+    public static boolean isShortcutFile(Path file) {
+        return getFileExtension(file).filter("lnk"::equals).isPresent();
+    }
Evidence
The extension helper claims to return lowercase but returns the raw extension from FilenameUtils.
The shortcut predicate compares with a lowercase literal using a case-sensitive equals.

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[74-94]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-530]

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

## Issue description
`.lnk` detection is effectively case-sensitive because `getFileExtension` returns the raw extension and `isShortcutFile` compares using `&amp;quot;lnk&amp;quot;::equals`.
### Issue Context
Windows file extensions are case-insensitive and `.LNK` is common. The current implementation can fail to detect valid shortcuts.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[74-94]
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-530]
### Suggested approach
- Update `getFileExtension` to return `extension.toLowerCase(Locale.ROOT)` (and adjust imports/tests accordingly), aligning with its Javadoc.
- Alternatively, change `isShortcutFile` to `filter(ext -&amp;gt; ext.equalsIgnoreCase(&amp;quot;lnk&amp;quot;))` (but prefer central normalization in `getFileExtension`).

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


7. Shortcut returns non-file 🐞 Bug ✓ Correctness
Description
resolveWindowsShortcut returns any existing target path, including directories, because it only
checks Files.exists. • Downstream import/link logic in ImportHandler treats unknown paths by
creating an empty entry with a file link, which can create incorrect entries if a shortcut points to
a directory.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R568-573]

+            if (exitCode == 0 && targetPath != null && !targetPath.trim().isEmpty()) {
+                Path resolvedPath = Path.of(targetPath.trim());
+                if (Files.exists(resolvedPath)) {
+                    LOGGER.debug("Resolved shortcut {} to {}", shortcutPath, resolvedPath);
+                    return Optional.of(resolvedPath);
+                } else {
Evidence
The resolver returns paths that exist without verifying they are regular files. ImportHandler
creates an entry for non-PDF/non-BIB paths, which would include directories returned by the
resolver.

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[568-573]
jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java[183-199]

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

## Issue description
Shortcut resolution currently returns any existing path, including directories. This can leak directory targets into file-import/link flows, creating empty entries with folder links.
### Issue Context
`ImportHandler` treats unknown paths as generic files and creates an empty entry with a link, which is incorrect when the path is a folder.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[568-576]
- jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java[183-199]
### Suggested approach
- Replace `Files.exists(resolvedPath)` with `Files.isRegularFile(resolvedPath)` (or `Files.isRegularFile(resolvedPath, LinkOption.NOFOLLOW_LINKS)` if appropriate).
- Consider logging a clear warning when the shortcut target is not a regular file.
- Optionally: for callers that can accept directories, add an explicit parameter/alternate method rather than returning directories by default.

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



Remediation recommended

8. Logs absolute file paths 📘 Rule violation ⛨ Security
Description
• The new shortcut-resolution flow logs shortcutPath and resolvedPath, which may include user
profile directories/usernames and thus constitutes potentially sensitive information in logs. • This
increases risk of leaking personal data via logs when users drag and drop files from home
directories.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R571-582]

+                    LOGGER.debug("Resolved shortcut {} to {}", shortcutPath, resolvedPath);
+                    return Optional.of(resolvedPath);
+                } else {
+                    LOGGER.warn("Shortcut target does not exist: {}", resolvedPath);
+                    return Optional.empty();
+                }
+            } else {
+                LOGGER.warn("Failed to resolve shortcut: {} (exit code: {})", shortcutPath, exitCode);
+                return Optional.empty();
+            }
+        } catch (Exception e) {
+            LOGGER.warn("Exception while resolving shortcut: {}", shortcutPath, e);
Evidence
The secure logging rule requires avoiding sensitive data in logs. Logging full paths can expose
personal information (e.g., usernames) and local directory structure.

Rule 5: Generic: Secure Logging Practices
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[571-579]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[581-582]

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 shortcut-resolution logs include full filesystem paths (`shortcutPath`, `resolvedPath`) which may contain sensitive user information (e.g., usernames).
## Issue Context
Drag-and-drop file paths commonly point into a user&amp;#x27;s home directory. Logging them at `warn` (and even `debug`) can leak PII into log files.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[571-582]

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


9. CHANGELOG link has space 📘 Rule violation ✓ Correctness
Description
• The added CHANGELOG entry formats the issue link as [#15036] (https://...) which is malformed
Markdown and will not render as a proper hyperlink. • This leaves written project artifacts in a
malformed state, contrary to the requirement to correct typos/malformed text.
Code

CHANGELOG.md[43]

+- Allowed drag and drop of Windows shortcut (.lnk) files to open libraries [#15036] (https://github.com/JabRef/jabref/issues/15036)
Evidence
The compliance rule requires fixing typos and malformed text in changelog/documentation. The new
entry includes an incorrectly formatted Markdown link.

CHANGELOG.md[43-43]
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
The CHANGELOG entry contains malformed Markdown for the issue link due to an extra space between the link text and URL.
## Issue Context
Broken links in CHANGELOG reduce readability and professionalism.
## Fix Focus Areas
- CHANGELOG.md[43-43]

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


10. UI thread blocked 🐞 Bug ⛯ Reliability
Description
resolveWindowsShortcut spawns PowerShell and calls process.waitFor() with no timeout; if
PowerShell startup hangs/slow, drag-and-drop can freeze the JavaFX UI. • The shortcut resolution is
invoked directly inside drag-and-drop handlers (before background tasks), so this blocking behavior
is user-facing.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R548-567]

+        try {
+            // Use PowerShell to resolve the shortcut target
+            ProcessBuilder pb = new ProcessBuilder(
+                    "powershell", "-NoProfile", "-Command",
+                    "$ws = New-Object -ComObject WScript.Shell; " +
+                            "$shortcut = $ws.CreateShortcut('" + shortcutPath.toAbsolutePath().toString().replace("'", "''") + "'); " +
+                            "Write-Output $shortcut.TargetPath"
+            );
+
+            pb.redirectErrorStream(true);
+            Process process = pb.start();
+
+            String targetPath;
+            try (java.io.BufferedReader reader = new java.io.BufferedReader(
+                    new java.io.InputStreamReader(process.getInputStream()))) {
+                targetPath = reader.readLine();
+            }
+
+            int exitCode = process.waitFor();
+
Evidence
The shortcut resolution uses an external process and waits synchronously. The DnD handlers call
resolution inline, which runs on the JavaFX event thread, making UI freezes plausible.

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[548-567]
jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[538-559]
jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[182-188]
jablib/src/main/java/org/jabref/logic/os/OS.java[81-96]

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

## Issue description
`FileUtil.resolveWindowsShortcut` executes PowerShell and blocks on `process.waitFor()` without a timeout. This is called inline from JavaFX drag-and-drop handlers, which can freeze the UI if PowerShell is slow/hangs.
### Issue Context
The codebase already depends on `mslinks.ShellLink` (see `OS.detectProgramPath`) to resolve `.lnk` targets in-process, which avoids external process overhead/hangs.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[548-567]
- jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[538-559]
- jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[182-188]
- jablib/src/main/java/org/jabref/logic/os/OS.java[81-96]
### Suggested approach
- Replace the PowerShell-based resolution with `mslinks.ShellLink` resolution (Windows-only), similar to `OS.detectProgramPath`.
- If PowerShell must remain:
- run resolution on a background executor (not the FX thread)
- add a timeout (e.g., `process.waitFor(timeout, unit)` and `process.destroyForcibly()`)
- drain full output to avoid blocking on filled buffers.

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



Advisory comments

11. Trivial .lnk comments added 📘 Rule violation ✓ Correctness
Description
• The new documentation comment for isShortcutFile restates what the code obviously does (checks
the extension) instead of explaining intent, limitations, or rationale. • This adds noise without
improving maintainability, contrary to the requirement that comments explain the “why”.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R524-530]

+    /// Test if the file is a shortcut file by simply checking the extension to be ".lnk"
+    ///
+    /// @param file The file to check
+    /// @return True if file extension is ".lnk", false otherwise
+    public static boolean isShortcutFile(Path file) {
+        return getFileExtension(file).filter("lnk"::equals).isPresent();
+    }
Evidence
The compliance rule requires comments to explain rationale rather than narrate code. The added
comment states it checks the .lnk extension, which is already evident from the implementation.

AGENTS.md
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-530]

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

## Issue description
Comments for `isShortcutFile` narrate the implementation rather than documenting intent/limitations.
## Issue Context
Per codebase conventions, comments should focus on non-obvious rationale (e.g., why extension-check is sufficient, platform constraints, or security considerations).
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-530]

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


12. Inconsistent DnD shortcut support 🐞 Bug ✓ Correctness
Description
• Main table drag-and-drop resolves .lnk files before importing, but group tree drag-and-drop
import still forwards raw paths. • Users dropping the same shortcut onto different UI areas will see
different behavior; this is likely an unintended inconsistency.
Code

jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[R542-543]

+            List<Path> files = FileUtil.resolveWindowsShortcuts(event.getDragboard().getFiles().stream().map(File::toPath).collect(Collectors.toList()));
Evidence
MainTable explicitly resolves shortcuts, while GroupTreeView constructs the file list without
resolution before invoking the same import handler.

jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[541-576]
jabgui/src/main/java/org/jabref/gui/groups/GroupTreeView.java[408-422]

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

## Issue description
`.lnk` resolution is applied in some drag-and-drop entry points (MainTable) but not others (GroupTreeView), leading to inconsistent behavior.
### Issue Context
Both paths eventually call `ImportHandler.importFilesInBackground`, so normalizing the file list upstream improves consistency.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[538-576]
- jabgui/src/main/java/org/jabref/gui/groups/GroupTreeView.java[408-422]
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[587-596]
### Suggested approach
- Use `FileUtil.resolveWindowsShortcuts(...)` in GroupTreeView as well.
- Or extract a shared helper for `dragboard.getFiles() -&amp;gt; List&amp;lt;Path&amp;gt;` normalization used by all DnD handlers.

ⓘ 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 548 to 584
try {
// Use PowerShell to resolve the shortcut target
ProcessBuilder pb = new ProcessBuilder(
"powershell", "-NoProfile", "-Command",
"$ws = New-Object -ComObject WScript.Shell; " +
"$shortcut = $ws.CreateShortcut('" + shortcutPath.toAbsolutePath().toString().replace("'", "''") + "'); " +
"Write-Output $shortcut.TargetPath"
);

pb.redirectErrorStream(true);
Process process = pb.start();

String targetPath;
try (java.io.BufferedReader reader = new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream()))) {
targetPath = reader.readLine();
}

int exitCode = process.waitFor();

if (exitCode == 0 && targetPath != null && !targetPath.trim().isEmpty()) {
Path resolvedPath = Path.of(targetPath.trim());
if (Files.exists(resolvedPath)) {
LOGGER.debug("Resolved shortcut {} to {}", shortcutPath, resolvedPath);
return Optional.of(resolvedPath);
} else {
LOGGER.warn("Shortcut target does not exist: {}", resolvedPath);
return Optional.empty();
}
} else {
LOGGER.warn("Failed to resolve shortcut: {} (exit code: {})", shortcutPath, exitCode);
return Optional.empty();
}
} catch (Exception e) {
LOGGER.warn("Exception while resolving shortcut: {}", shortcutPath, e);
return Optional.empty();
}

Choose a reason for hiding this comment

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

Action required

1. resolvewindowsshortcut catches exception 📘 Rule violation ⛯ Reliability

resolveWindowsShortcut wraps the whole PowerShell invocation in a broad catch (Exception) and
  degrades by returning Optional.empty(), which can silently drop .lnk files during drag-and-drop
  without a clear recovery path.
• The method blocks on process.waitFor() with no timeout and does not restore the interrupt flag
  on interruption, which risks hangs and makes failures harder to diagnose reliably.
• This violates the requirement for specific, non-disruptive exception handling and robust edge-case
  management around external process execution.
Agent Prompt
## Issue description
`FileUtil.resolveWindowsShortcut` catches a broad `Exception` and blocks on `process.waitFor()` without a timeout. This can lead to silent failures (returning `Optional.empty()`), and it risks hanging the application thread.

## Issue Context
The shortcut resolution executes an external process (PowerShell) which is a high-failure-risk dependency (missing executable, permission issues, COM errors, long-running/hung process). Exception handling should be specific and interruptions should preserve the thread interrupt status.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[548-584]

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

Comment on lines 524 to 596
/// Test if the file is a shortcut file by simply checking the extension to be ".lnk"
///
/// @param file The file to check
/// @return True if file extension is ".lnk", false otherwise
public static boolean isShortcutFile(Path file) {
return getFileExtension(file).filter("lnk"::equals).isPresent();
}

/// Resolves a Windows shortcut (.lnk) file to its target path.
/// Only works on Windows systems. On other systems or if resolution fails, returns empty Optional.
///
/// @param shortcutPath The path to the .lnk file
/// @return Optional containing the target path, or empty if resolution fails
public static Optional<Path> resolveWindowsShortcut(Path shortcutPath) {
if (!isShortcutFile(shortcutPath)) {
return Optional.empty();
}

// Only attempt on Windows
if (!OS.WINDOWS) {
LOGGER.debug("Shortcut resolution only supported on Windows");
return Optional.empty();
}

try {
// Use PowerShell to resolve the shortcut target
ProcessBuilder pb = new ProcessBuilder(
"powershell", "-NoProfile", "-Command",
"$ws = New-Object -ComObject WScript.Shell; " +
"$shortcut = $ws.CreateShortcut('" + shortcutPath.toAbsolutePath().toString().replace("'", "''") + "'); " +
"Write-Output $shortcut.TargetPath"
);

pb.redirectErrorStream(true);
Process process = pb.start();

String targetPath;
try (java.io.BufferedReader reader = new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream()))) {
targetPath = reader.readLine();
}

int exitCode = process.waitFor();

if (exitCode == 0 && targetPath != null && !targetPath.trim().isEmpty()) {
Path resolvedPath = Path.of(targetPath.trim());
if (Files.exists(resolvedPath)) {
LOGGER.debug("Resolved shortcut {} to {}", shortcutPath, resolvedPath);
return Optional.of(resolvedPath);
} else {
LOGGER.warn("Shortcut target does not exist: {}", resolvedPath);
return Optional.empty();
}
} else {
LOGGER.warn("Failed to resolve shortcut: {} (exit code: {})", shortcutPath, exitCode);
return Optional.empty();
}
} catch (Exception e) {
LOGGER.warn("Exception while resolving shortcut: {}", shortcutPath, e);
return Optional.empty();
}
}

public static List<Path> resolveWindowsShortcuts(List<Path> paths) {
return paths.stream()
.flatMap(path -> {
if (isShortcutFile(path)) {
return resolveWindowsShortcut(path).stream();
}
return Stream.of(path);
})
.toList();
}

Choose a reason for hiding this comment

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

Action required

2. Missing tests for .lnk 📘 Rule violation ✓ Correctness

• New behavior was introduced for resolving Windows shortcuts via
  FileUtil.resolveWindowsShortcut/resolveWindowsShortcuts and used in drag-and-drop flows, but no
  corresponding JUnit tests were added/updated to cover this behavior.
• The existing FileUtilTest covers file extension handling, yet does not include .lnk cases,
  leaving key edge cases (non-Windows behavior, failure modes) unverified.
Agent Prompt
## Issue description
New Windows shortcut (`.lnk`) drag-and-drop support introduces new `FileUtil` behavior without corresponding test coverage.

## Issue Context
At minimum, tests should cover:
- `isShortcutFile` correctly detecting `.lnk` (case handling if intended)
- `resolveWindowsShortcuts` returning the original paths for non-shortcut inputs
- non-Windows behavior: `resolveWindowsShortcut` should return `Optional.empty()` without throwing

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-596]
- jablib/src/test/java/org/jabref/logic/util/io/FileUtilTest.java[179-210]

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

Comment on lines 524 to 530
/// Test if the file is a shortcut file by simply checking the extension to be ".lnk"
///
/// @param file The file to check
/// @return True if file extension is ".lnk", false otherwise
public static boolean isShortcutFile(Path file) {
return getFileExtension(file).filter("lnk"::equals).isPresent();
}

Choose a reason for hiding this comment

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

Action required

3. Case-sensitive .lnk check 🐞 Bug ✓ Correctness

isShortcutFile uses exact string match ("lnk"::equals) but getFileExtension does not
  actually lowercase the extension, despite its documentation.
• As a result, shortcuts like FILE.LNK may not be recognized, breaking the new drag-and-drop
  behavior on Windows.
Agent Prompt
### Issue description
`.lnk` detection is effectively case-sensitive because `getFileExtension` returns the raw extension and `isShortcutFile` compares using `"lnk"::equals`.

### Issue Context
Windows file extensions are case-insensitive and `.LNK` is common. The current implementation can fail to detect valid shortcuts.

### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[74-94]
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-530]

### Suggested approach
- Update `getFileExtension` to return `extension.toLowerCase(Locale.ROOT)` (and adjust imports/tests accordingly), aligning with its Javadoc.
- Alternatively, change `isShortcutFile` to `filter(ext -> ext.equalsIgnoreCase("lnk"))` (but prefer central normalization in `getFileExtension`).

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

Comment on lines 568 to 573
if (exitCode == 0 && targetPath != null && !targetPath.trim().isEmpty()) {
Path resolvedPath = Path.of(targetPath.trim());
if (Files.exists(resolvedPath)) {
LOGGER.debug("Resolved shortcut {} to {}", shortcutPath, resolvedPath);
return Optional.of(resolvedPath);
} else {

Choose a reason for hiding this comment

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

Action required

4. Shortcut returns non-file 🐞 Bug ✓ Correctness

resolveWindowsShortcut returns any existing target path, including directories, because it only
  checks Files.exists.
• Downstream import/link logic in ImportHandler treats unknown paths by creating an empty entry
  with a file link, which can create incorrect entries if a shortcut points to a directory.
Agent Prompt
### Issue description
Shortcut resolution currently returns any existing path, including directories. This can leak directory targets into file-import/link flows, creating empty entries with folder links.

### Issue Context
`ImportHandler` treats unknown paths as generic files and creates an empty entry with a link, which is incorrect when the path is a folder.

### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[568-576]
- jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java[183-199]

### Suggested approach
- Replace `Files.exists(resolvedPath)` with `Files.isRegularFile(resolvedPath)` (or `Files.isRegularFile(resolvedPath, LinkOption.NOFOLLOW_LINKS)` if appropriate).
- Consider logging a clear warning when the shortcut target is not a regular file.
- Optionally: for callers that can accept directories, add an explicit parameter/alternate method rather than returning directories by default.

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

@github-actions github-actions bot added the status: changes-required Pull requests that are not yet complete label Feb 8, 2026
@testlens-app

This comment has been minimized.

@Siedlerchr
Copy link
Member

There is another java library which apparently can parse this stuf https://github.com/DmitriiShamrikov/mslinks
But they don't have an up to date version published version on maven central argh

@koppor
Copy link
Member

koppor commented Feb 8, 2026

Note that resolving works at JabRef (!)

If I double click a .lnk file - JabRef opens. Thus "only" the drag and drop disallowance is wrong currently.

@koppor
Copy link
Member

koppor commented Feb 8, 2026

There is another java library which apparently can parse this stuf DmitriiShamrikov/mslinks But they don't have an up to date version published version on maven central argh

Who works on DmitriiShamrikov/mslinks#29 ?

@koppor
Copy link
Member

koppor commented Feb 8, 2026

We need the newver version, because of JDK11 compatibility - https://github.com/DmitriiShamrikov/mslinks/releases/tag/1.0.8

@koppor koppor marked this pull request as draft February 8, 2026 16:16
@faneeshh
Copy link
Contributor Author

faneeshh commented Feb 9, 2026

Note that resolving works at JabRef (!)

If I double click a .lnk file - JabRef opens. Thus "only" the drag and drop disallowance is wrong currently.

I tested it by only allowing the .lnk extension. While the file is accepted by the UI, it currently results in an empty table because the Drag and Drop path doesn't seem to trigger the same resolution logic as double-clicking or the file picker. I am currently looking into how CommandLineProcessor or the main entry point handles .lnk files to see if I can reuse that existing resolution logic here without adding new dependencies like mslinks.

@faneeshh
Copy link
Contributor Author

faneeshh commented Feb 9, 2026

@koppor I was able to resolve it by synchronizing the two seperate drag and drop code paths by integrating Windows shortcut resolution into the MainTable handlers. Previously dropping a .lnk file directly onto the table was bypassing the resolution logic in OpenDatabaseAction which resulted in "misc" entries. I think applying FileUtil : : resolveIfShortcut within both handleOnDragDroppedTable view and handleOnDragDropped in MainTable.java, and refining the resolution logic in FileUtil.java using mslinks.ShellLink now correctly resolves shortcuts. Let me know what you think!

@koppor
Copy link
Member

koppor commented Feb 11, 2026

recent mslink library available at org.jabref:mslinks:1.1.0-SNAPSHOT. See DmitriiShamrikov/mslinks#29 (comment) for details.

}

private boolean isAcceptedFile(Path path) {
return FileUtil.isBibFile(path) || FileUtil.isShortcutFile(path);
Copy link
Member

Choose a reason for hiding this comment

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

Condition wrong - i think, resolving should take place

Suggested change
return FileUtil.isBibFile(path) || FileUtil.isShortcutFile(path);
return FileUtil.isBibFile(FileUtils.resolveIfShortcut(path));

// Resolve any shortcuts to their targets
List<Path> resolvedFiles = filesToOpen.stream()
.map(FileUtil::resolveIfShortcut)
.collect(Collectors.toList());
Copy link
Member

Choose a reason for hiding this comment

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

Add comment that list needs to be modifiable.

CHANGELOG.md Outdated

### Fixed

- Allowed drag and drop of Windows shortcut (.lnk) files to open libraries [#15036] (https://github.com/JabRef/jabref/issues/15036)
Copy link
Member

Choose a reason for hiding this comment

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

Space too much

import org.jabref.model.entry.LinkedFile;
import org.jabref.model.entry.field.StandardField;

import mslinks.ShellLink;
Copy link
Member

Choose a reason for hiding this comment

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

@Siedlerchr Some dependency seems to already include mslinks :)

@github-actions
Copy link
Contributor

Your pull request conflicts with the target branch.

Please merge with your code. For a step-by-step guide to resolve merge conflicts, see https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-using-the-command-line.

@koppor koppor marked this pull request as ready for review February 18, 2026 13:03
@github-actions
Copy link
Contributor

Do not mark a PR as ready-for-review while the CI is running. CI checks need to be completed and passed.

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

Review Summary by Qodo

Support drag and drop of Windows shortcut (.lnk) files

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Added support for dragging and dropping Windows shortcut (.lnk) files
• Implemented shortcut resolution using mslinks library for target path detection
• Updated file acceptance logic in drag-and-drop handlers across UI components
• Resolves shortcuts before opening libraries to prevent file errors
Diagram
flowchart LR
  A["User drags .lnk file"] --> B["FrameDndHandler accepts .lnk files"]
  B --> C["OpenDatabaseAction resolves shortcut"]
  C --> D["FileUtil.resolveIfShortcut uses mslinks"]
  D --> E["Target .bib file opens in JabRef"]
  F["MainTable drag-drop also resolves shortcuts"] --> D
Loading

Grey Divider

File Changes

1. jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java ✨ Enhancement +5/-1

Accept shortcut files in drag-and-drop handler

• Modified getBibFiles() to accept both .bib and .lnk files via new isAcceptedFile() method
• Added isAcceptedFile() helper method that checks for both bib files and shortcut files

jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java


2. jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java ✨ Enhancement +11/-4

Resolve shortcuts before opening database files

• Added import for Collectors and FileUtil
• Implemented shortcut resolution in openFiles() method before processing files
• Resolves all shortcuts to their target paths using FileUtil.resolveIfShortcut()
• Updated file iteration and processing to use resolved file list

jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java


3. jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java ✨ Enhancement +9/-2

Resolve shortcuts in MainTable drag-drop operations

• Added import for FileUtil
• Updated handleOnDragDropped() to resolve shortcuts when processing dropped files
• Updated handleOnDragDroppedTableView() to resolve shortcuts for import operations
• Applied FileUtil.resolveIfShortcut() in both drag-drop handlers

jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java


View more (2)
4. jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java ✨ Enhancement +39/-0

Add shortcut detection and resolution utilities

• Added imports for mslinks.ShellLink and mslinks.ShellLinkException
• Implemented isShortcutFile() method to detect .lnk files by extension
• Implemented resolveIfShortcut() method that resolves Windows shortcuts to target paths
• Resolution only occurs on Windows systems and gracefully falls back to original path on failure
• Includes error handling and logging for shortcut resolution failures

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java


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

Document shortcut file drag-and-drop support

• Added entry documenting support for drag and drop of Windows shortcut files
• References issue #15036 in the Fixed section

CHANGELOG.md


Grey Divider

Qodo Logo

@github-actions
Copy link
Contributor

Do not mark a PR as ready-for-review if changes are required.
Address the changes first.

@koppor koppor marked this pull request as draft February 18, 2026 13:03
@qodo-free-for-open-source-projects
Copy link
Contributor

Persistent review updated to latest commit 510012a

Comment on lines 242 to 244
private boolean isAcceptedFile(Path path) {
return FileUtil.isBibFile(path) || FileUtil.isShortcutFile(path);
}

Choose a reason for hiding this comment

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

Action required

1. .lnk accepted on non-windows platforms 🐞 Bug ✓ Correctness

isAcceptedFile() calls FileUtil.isShortcutFile() with no OS guard. On macOS/Linux a dropped
.lnk file passes the filter, but resolveIfShortcut() is a no-op there and returns the original
.lnk path, which is then fed to the BibTeX parser producing confusing parse errors.
Agent Prompt
## Issue description
`isAcceptedFile()` in `FrameDndHandler` accepts `.lnk` files on all operating systems because `FileUtil.isShortcutFile()` only checks the file extension. On non-Windows systems `resolveIfShortcut()` is a no-op and returns the original `.lnk` binary path, which is then parsed as BibTeX.

## Issue Context
- `FileUtil.isShortcutFile()` has no OS check (extension-only test).
- `FileUtil.resolveIfShortcut()` guards with `!OS.WINDOWS` and returns the original path immediately on non-Windows.
- `openTheFile()` only checks `Files.exists()`, so the `.lnk` binary reaches the parser.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[242-244]

Change the method to:
```java
private boolean isAcceptedFile(Path path) {
    return FileUtil.isBibFile(path) || (OS.WINDOWS && FileUtil.isShortcutFile(path));
}
```

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

Comment on lines 168 to 171
// Resolve any shortcuts to their targets
List<Path> resolvedFiles = filesToOpen.stream()
.map(FileUtil::resolveIfShortcut)
.collect(Collectors.toList());

Choose a reason for hiding this comment

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

Action required

2. No .bib validation after shortcut resolution 🐞 Bug ✓ Correctness

After resolving a .lnk shortcut, the code never checks whether the resolved target is a .bib
file. A shortcut pointing to a .pdf, .docx, or any other file type is silently forwarded to
openTheFile() and parsed as a BibTeX database, producing confusing errors with no explanation.
Agent Prompt
## Issue description
After `resolveIfShortcut()` maps each path to its target, there is no check that the resolved target is actually a `.bib` file. A `.lnk` pointing to a `.pdf` or any other file type will be passed to `openTheFile()` and parsed as BibTeX.

## Issue Context
- The old code used `filter(FileUtil::isBibFile)` in `getBibFiles()` to guarantee only `.bib` files reached `openFiles()`.
- The new code removes that guarantee: `isAcceptedFile()` admits all `.lnk` files, and `openFiles()` does not re-validate after resolution.
- `openTheFile()` only checks `Files.exists()` (line 220), not the file extension.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[168-171]

Add a `.bib` filter after resolution:
```java
List<Path> resolvedFiles = filesToOpen.stream()
    .map(FileUtil::resolveIfShortcut)
    .filter(FileUtil::isBibFile)   // ← ensure resolved target is a .bib file
    .collect(Collectors.toList());
```

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

Comment on lines 556 to 561
} catch (IOException | ShellLinkException e) {
LOGGER.warn("Could not resolve shortcut {}", path, e);
}

return path;
}

Choose a reason for hiding this comment

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

Action required

3. Failed resolution falls back to parsing .lnk binary 🐞 Bug ⛯ Reliability

When resolveIfShortcut() fails (exception thrown, or the shortcut target does not exist on disk),
it returns the original .lnk path. That path passes openTheFile()'s Files.exists() guard (the
.lnk file itself exists), so the binary .lnk content is fed to the BibTeX parser instead of
surfacing a clear "shortcut target not found" error. Broken/orphaned shortcuts are common in
real-world Windows usage.
Agent Prompt
## Issue description
When `resolveIfShortcut()` cannot resolve a shortcut (broken link, missing target, or parse exception), it returns the original `.lnk` path. This path passes `openTheFile()`'s `Files.exists()` check and the binary `.lnk` content is parsed as BibTeX, producing confusing errors instead of a clear failure message.

## Issue Context
- `resolveIfShortcut()` returns the original path on exception (line 556-560) and when the resolved target does not exist (line 552-554).
- The `.lnk` file itself exists on disk, so `openTheFile()`'s `Files.exists()` guard (line 220) does not filter it out.
- Broken shortcuts (target deleted or moved) are common in real-world Windows usage.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[549-561]
- jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[168-171]

Preferred fix — add `filter(FileUtil::isBibFile)` after resolution in `openFiles()` (covers both this issue and Finding 2):
```java
List<Path> resolvedFiles = filesToOpen.stream()
    .map(FileUtil::resolveIfShortcut)
    .filter(FileUtil::isBibFile)
    .collect(Collectors.toList());
```
Alternatively, change `resolveIfShortcut()` to return `Optional<Path>` so callers can explicitly handle resolution failure.

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

@subhramit
Copy link
Member

Hi @faneeshh, can we get an update on this PR? There seems to be unresolved comments.

@testlens-app

This comment has been minimized.

@faneeshh
Copy link
Contributor Author

Hi @faneeshh, can we get an update on this PR? There seems to be unresolved comments.

Sorry I forgot I had this PR drafted. I've implemented all the changes suggested above and it should be fine now. Let me know if any further changes are needed.

@testlens-app
Copy link

testlens-app bot commented Feb 26, 2026

✅ All tests passed ✅

🏷️ Commit: 138fee6
▶️ Tests: 11244 executed
⚪️ Checks: 75/75 completed


Learn more about TestLens at testlens.app.

@subhramit subhramit marked this pull request as ready for review February 26, 2026 23:17
@qodo-free-for-open-source-projects
Copy link
Contributor

Review Summary by Qodo

Add drag and drop support for Windows shortcut files

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Added support for dragging and dropping Windows shortcut (.lnk) files
• Resolves shortcuts to target .bib files before opening libraries
• Integrated shortcut resolution in file handling and drag-drop operations
• Uses mslinks library for Windows shortcut target resolution
Diagram
flowchart LR
  A["User drags .lnk file"] --> B["FrameDndHandler/MainTable"]
  B --> C["FileUtil.resolveIfShortcut"]
  C --> D["ShellLink resolves target"]
  D --> E["Opens target .bib file"]
Loading

Grey Divider

File Changes

1. jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java ✨ Enhancement +5/-1

Add shortcut resolution to frame drag-drop handler

• Modified getBibFiles() to use new isAcceptedFile() method
• Added isAcceptedFile() method that resolves shortcuts before checking if file is .bib
• Enables shortcut resolution in frame-level drag and drop handling

jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java


2. jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java ✨ Enhancement +13/-4

Resolve shortcuts in file opening action

• Added import for FileUtil and Collectors
• Modified openFiles() to resolve shortcuts and filter .bib files before processing
• Creates modifiable list to support iterator.remove() calls in downstream logic
• Applies shortcut resolution at the entry point for file opening

jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java


3. jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java ✨ Enhancement +9/-2

Add shortcut resolution to table drag-drop handlers

• Added import for FileUtil
• Modified handleOnDragDropped() to resolve shortcuts when processing dragged files
• Modified handleOnDragDroppedTableView() to resolve shortcuts for table-level drops
• Ensures shortcuts are resolved before file import operations

jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java


View more (2)
4. jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java ✨ Enhancement +39/-0

Add Windows shortcut detection and resolution utilities

• Added imports for mslinks.ShellLink and mslinks.ShellLinkException
• Added isShortcutFile() method to detect .lnk files by extension
• Added resolveIfShortcut() method that resolves Windows shortcuts to their targets
• Resolution only works on Windows; returns original path on other systems or if resolution fails
• Includes error handling for IO and ShellLink exceptions

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java


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

Document shortcut drag-drop feature

• Added entry documenting the new drag and drop support for Windows shortcut files
• References issue #15036

CHANGELOG.md


Grey Divider

Qodo Logo

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

qodo-free-for-open-source-projects bot commented Feb 26, 2026

Code Review by Qodo

🐞 Bugs (9) 📘 Rule violations (8) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Path.of(targetPath) may crash 📘 Rule violation ⛯ Reliability ⭐ New
Description
resolveIfShortcut calls Path.of(targetPath) without handling InvalidPathException, which can
crash the app for malformed/unsupported shortcut targets. This violates the requirement to handle
edge cases and missing/invalid external input safely.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R549-555]

+            if (targetPath != null && !targetPath.isEmpty()) {
+                Path resolvedPath = Path.of(targetPath);
+
+                if (Files.exists(resolvedPath) && !Files.isDirectory(resolvedPath)) {
+                    return resolvedPath;
+                }
+            }
Evidence
The compliance checklist requires explicit handling of failure points and edge cases; the new code
constructs a Path from an externally-provided shortcut target string without guarding against
invalid path values, and only catches IOException | ShellLinkException.

Rule 3: Generic: Robust Error Handling and Edge Case Management
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[549-555]
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
`FileUtil.resolveIfShortcut` converts the shortcut target string to a `Path` using `Path.of(targetPath)` without handling `InvalidPathException`. If a `.lnk` points to an invalid/unsupported path string, this can throw an unchecked exception and crash the flow.

## Issue Context
The shortcut target is external/untrusted data coming from the `.lnk` file. The method currently only catches `IOException | ShellLinkException`.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[545-561]

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


2. resolveIfShortcut on UI thread 📘 Rule violation ➹ Performance ⭐ New
Description
Drag-and-drop handlers call FileUtil.resolveIfShortcut synchronously while processing JavaFX drag
events, which may perform filesystem IO and block the UI thread. This risks UI freezes during
drag/drop operations.
Code

jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[R541-545]

        if (event.getDragboard().hasFiles()) {
-            List<Path> files = event.getDragboard().getFiles().stream().map(File::toPath).collect(Collectors.toList());
+            List<Path> files = event.getDragboard().getFiles().stream()
+                                    .map(File::toPath)
+                                    .map(FileUtil::resolveIfShortcut)
+                                    .collect(Collectors.toList());
Evidence
The compliance rule requires avoiding long/blocking work on the UI thread; the GUI layer now invokes
shortcut resolution during drag handling, and the resolver reads/parses the .lnk file.

jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[541-545]
jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[242-244]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[545-548]
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
The UI drag-and-drop handlers synchronously resolve `.lnk` shortcuts, which can involve filesystem IO and block the JavaFX UI thread.

## Issue Context
`FileUtil.resolveIfShortcut` parses `.lnk` files via `mslinks.ShellLink`, which may be slow and should not run during UI event handling.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[541-545]
- jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[238-244]
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[540-561]

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


3. Uppercase .BIB not opened 🐞 Bug ✓ Correctness ⭐ New
Description
OpenDatabaseAction.openFiles now filters files with FileUtil.isBibFile, which is effectively
case-sensitive because getFileExtension returns the raw extension. This can cause valid libraries
like "FILE.BIB" (or other non-lowercase variants) to no longer open or raise an already-open tab.
Code

jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[R168-173]

+        // Resolve any shortcuts to their targets and filter to only .bib files.
+        // The resulting list must remain modifiable for downstream processing (iterator.remove() calls below).
+        List<Path> resolvedFiles = filesToOpen.stream()
+                                              .map(FileUtil::resolveIfShortcut)
+                                              .filter(FileUtil::isBibFile)
+                                              .collect(Collectors.toList());
Evidence
openFiles now filters to only files where FileUtil.isBibFile returns true. isBibFile checks for an
exact "bib" match, while getFileExtension returns the extension without lowercasing, so
mixed/uppercase extensions will be filtered out even though the actual open path (openTheFile) does
not require a .bib extension to proceed.

jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[163-174]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[518-524]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[76-96]
jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[216-224]

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

### Issue description
`OpenDatabaseAction.openFiles` now filters inputs using `FileUtil.isBibFile`. Because `isBibFile` does an exact match against &quot;bib&quot; and `getFileExtension` returns the raw extension (not lowercased), valid libraries like `MYLIB.BIB` can be filtered out and therefore will no longer open.

### Issue Context
Before this PR, `openFiles` would open any existing file path (no extension gate). The new filter is intended to prevent trying to open `.lnk` binaries as libraries, but it introduces a regression for mixed/uppercase extensions.

### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[76-96]
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[518-524]
- jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[168-173]
- jablib/src/test/java/org/jabref/logic/util/io/FileUtilTest.java[423-433]

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


View more (7)
4. .lnk accepted on non-Windows platforms 🐞 Bug ✓ Correctness
Description
isAcceptedFile() calls FileUtil.isShortcutFile() with no OS guard. On macOS/Linux a dropped
.lnk file passes the filter, but resolveIfShortcut() is a no-op there and returns the original
.lnk path, which is then fed to the BibTeX parser producing confusing parse errors.
Code

jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[R242-244]

+    private boolean isAcceptedFile(Path path) {
+        return FileUtil.isBibFile(path) || FileUtil.isShortcutFile(path);
+    }
Evidence
isShortcutFile() only checks the file extension with no OS check, so it returns true on any
platform. resolveIfShortcut() immediately returns the original path when !OS.WINDOWS, so the
.lnk binary is never resolved. openTheFile() only guards on Files.exists(), so the binary
.lnk content reaches the BibTeX parser.

jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[242-244]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[530-532]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[540-543]
jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[219-222]

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

## Issue description
`isAcceptedFile()` in `FrameDndHandler` accepts `.lnk` files on all operating systems because `FileUtil.isShortcutFile()` only checks the file extension. On non-Windows systems `resolveIfShortcut()` is a no-op and returns the original `.lnk` binary path, which is then parsed as BibTeX.
## Issue Context
- `FileUtil.isShortcutFile()` has no OS check (extension-only test).
- `FileUtil.resolveIfShortcut()` guards with `!OS.WINDOWS` and returns the original path immediately on non-Windows.
- `openTheFile()` only checks `Files.exists()`, so the `.lnk` binary reaches the parser.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[242-244]
Change the method to:

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


5. No .bib validation after shortcut resolution 🐞 Bug ✓ Correctness
Description
After resolving a .lnk shortcut, the code never checks whether the resolved target is a .bib
file. A shortcut pointing to a .pdf, .docx, or any other file type is silently forwarded to
openTheFile() and parsed as a BibTeX database, producing confusing errors with no explanation.
Code

jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[R168-171]

+        // Resolve any shortcuts to their targets
+        List<Path> resolvedFiles = filesToOpen.stream()
+                                              .map(FileUtil::resolveIfShortcut)
+                                              .collect(Collectors.toList());
Evidence
Before this PR, getBibFiles() applied filter(FileUtil::isBibFile), guaranteeing only .bib
files reached openFiles(). The new code replaces that filter with isAcceptedFile() (which admits
all .lnk files), and openFiles() maps resolveIfShortcut over the list but never re-validates
the resolved extension before calling openTheFile(). openTheFile() only checks Files.exists(),
so any non-.bib resolved target is parsed as BibTeX.

jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[168-201]
jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[242-244]
jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[219-222]

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

## Issue description
After `resolveIfShortcut()` maps each path to its target, there is no check that the resolved target is actually a `.bib` file. A `.lnk` pointing to a `.pdf` or any other file type will be passed to `openTheFile()` and parsed as BibTeX.
## Issue Context
- The old code used `filter(FileUtil::isBibFile)` in `getBibFiles()` to guarantee only `.bib` files reached `openFiles()`.
- The new code removes that guarantee: `isAcceptedFile()` admits all `.lnk` files, and `openFiles()` does not re-validate after resolution.
- `openTheFile()` only checks `Files.exists()` (line 220), not the file extension.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[168-171]
Add a `.bib` filter after resolution:

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


6. Failed resolution falls back to parsing .lnk binary 🐞 Bug ⛯ Reliability
Description
When resolveIfShortcut() fails (exception thrown, or the shortcut target does not exist on disk),
it returns the original .lnk path. That path passes openTheFile()'s Files.exists() guard (the
.lnk file itself exists), so the binary .lnk content is fed to the BibTeX parser instead of
surfacing a clear "shortcut target not found" error. Broken/orphaned shortcuts are common in
real-world Windows usage.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R556-561]

+        } catch (IOException | ShellLinkException e) {
+            LOGGER.warn("Could not resolve shortcut {}", path, e);
+        }
+
+        return path;
+    }
Evidence
resolveIfShortcut() returns the original .lnk path in two failure cases: (1) an IOException or
ShellLinkException is caught, and (2) the resolved target path does not exist
(!Files.exists(resolvedPath)). In both cases the returned .lnk path is a real file on disk, so
openTheFile()'s Files.exists() guard is bypassed and the binary .lnk data is parsed as BibTeX.

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[549-560]
jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[219-222]

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

## Issue description
When `resolveIfShortcut()` cannot resolve a shortcut (broken link, missing target, or parse exception), it returns the original `.lnk` path. This path passes `openTheFile()`&amp;#x27;s `Files.exists()` check and the binary `.lnk` content is parsed as BibTeX, producing confusing errors instead of a clear failure message.
## Issue Context
- `resolveIfShortcut()` returns the original path on exception (line 556-560) and when the resolved target does not exist (line 552-554).
- The `.lnk` file itself exists on disk, so `openTheFile()`&amp;#x27;s `Files.exists()` guard (line 220) does not filter it out.
- Broken shortcuts (target deleted or moved) are common in real-world Windows usage.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[549-561]
- jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[168-171]
Preferred fix — add `filter(FileUtil::isBibFile)` after resolution in `openFiles()` (covers both this issue and Finding 2):

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


7. resolveWindowsShortcut catches Exception 📘 Rule violation ⛯ Reliability
Description
resolveWindowsShortcut wraps the whole PowerShell invocation in a broad catch (Exception) and
degrades by returning Optional.empty(), which can silently drop .lnk files during drag-and-drop
without a clear recovery path. • The method blocks on process.waitFor() with no timeout and does
not restore the interrupt flag on interruption, which risks hangs and makes failures harder to
diagnose reliably. • This violates the requirement for specific, non-disruptive exception handling
and robust edge-case management around external process execution.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R548-584]

+        try {
+            // Use PowerShell to resolve the shortcut target
+            ProcessBuilder pb = new ProcessBuilder(
+                    "powershell", "-NoProfile", "-Command",
+                    "$ws = New-Object -ComObject WScript.Shell; " +
+                            "$shortcut = $ws.CreateShortcut('" + shortcutPath.toAbsolutePath().toString().replace("'", "''") + "'); " +
+                            "Write-Output $shortcut.TargetPath"
+            );
+
+            pb.redirectErrorStream(true);
+            Process process = pb.start();
+
+            String targetPath;
+            try (java.io.BufferedReader reader = new java.io.BufferedReader(
+                    new java.io.InputStreamReader(process.getInputStream()))) {
+                targetPath = reader.readLine();
+            }
+
+            int exitCode = process.waitFor();
+
+            if (exitCode == 0 && targetPath != null && !targetPath.trim().isEmpty()) {
+                Path resolvedPath = Path.of(targetPath.trim());
+                if (Files.exists(resolvedPath)) {
+                    LOGGER.debug("Resolved shortcut {} to {}", shortcutPath, resolvedPath);
+                    return Optional.of(resolvedPath);
+                } else {
+                    LOGGER.warn("Shortcut target does not exist: {}", resolvedPath);
+                    return Optional.empty();
+                }
+            } else {
+                LOGGER.warn("Failed to resolve shortcut: {} (exit code: {})", shortcutPath, exitCode);
+                return Optional.empty();
+            }
+        } catch (Exception e) {
+            LOGGER.warn("Exception while resolving shortcut: {}", shortcutPath, e);
+            return Optional.empty();
+        }
Evidence
The compliance checklist requires specific exception handling and robust handling of failure points.
The new code uses a broad catch (Exception) and performs an unbounded waitFor(), returning empty
results on failures, which can lead to silent failures and potential hangs.

Rule 3: Generic: Robust Error Handling and Edge Case Management
AGENTS.md
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[566-566]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[581-583]

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

## Issue description
`FileUtil.resolveWindowsShortcut` catches a broad `Exception` and blocks on `process.waitFor()` without a timeout. This can lead to silent failures (returning `Optional.empty()`), and it risks hanging the application thread.
## Issue Context
The shortcut resolution executes an external process (PowerShell) which is a high-failure-risk dependency (missing executable, permission issues, COM errors, long-running/hung process). Exception handling should be specific and interruptions should preserve the thread interrupt status.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[548-584]

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


8. Missing tests for .lnk 📘 Rule violation ✓ Correctness
Description
• New behavior was introduced for resolving Windows shortcuts via
FileUtil.resolveWindowsShortcut/resolveWindowsShortcuts and used in drag-and-drop flows, but no
corresponding JUnit tests were added/updated to cover this behavior. • The existing FileUtilTest
covers file extension handling, yet does not include .lnk cases, leaving key edge cases
(non-Windows behavior, failure modes) unverified.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R524-596]

+    /// Test if the file is a shortcut file by simply checking the extension to be ".lnk"
+    ///
+    /// @param file The file to check
+    /// @return True if file extension is ".lnk", false otherwise
+    public static boolean isShortcutFile(Path file) {
+        return getFileExtension(file).filter("lnk"::equals).isPresent();
+    }
+
+    /// Resolves a Windows shortcut (.lnk) file to its target path.
+    /// Only works on Windows systems. On other systems or if resolution fails, returns empty Optional.
+    ///
+    /// @param shortcutPath The path to the .lnk file
+    /// @return Optional containing the target path, or empty if resolution fails
+    public static Optional<Path> resolveWindowsShortcut(Path shortcutPath) {
+        if (!isShortcutFile(shortcutPath)) {
+            return Optional.empty();
+        }
+
+        // Only attempt on Windows
+        if (!OS.WINDOWS) {
+            LOGGER.debug("Shortcut resolution only supported on Windows");
+            return Optional.empty();
+        }
+
+        try {
+            // Use PowerShell to resolve the shortcut target
+            ProcessBuilder pb = new ProcessBuilder(
+                    "powershell", "-NoProfile", "-Command",
+                    "$ws = New-Object -ComObject WScript.Shell; " +
+                            "$shortcut = $ws.CreateShortcut('" + shortcutPath.toAbsolutePath().toString().replace("'", "''") + "'); " +
+                            "Write-Output $shortcut.TargetPath"
+            );
+
+            pb.redirectErrorStream(true);
+            Process process = pb.start();
+
+            String targetPath;
+            try (java.io.BufferedReader reader = new java.io.BufferedReader(
+                    new java.io.InputStreamReader(process.getInputStream()))) {
+                targetPath = reader.readLine();
+            }
+
+            int exitCode = process.waitFor();
+
+            if (exitCode == 0 && targetPath != null && !targetPath.trim().isEmpty()) {
+                Path resolvedPath = Path.of(targetPath.trim());
+                if (Files.exists(resolvedPath)) {
+                    LOGGER.debug("Resolved shortcut {} to {}", shortcutPath, resolvedPath);
+                    return Optional.of(resolvedPath);
+                } else {
+                    LOGGER.warn("Shortcut target does not exist: {}", resolvedPath);
+                    return Optional.empty();
+                }
+            } else {
+                LOGGER.warn("Failed to resolve shortcut: {} (exit code: {})", shortcutPath, exitCode);
+                return Optional.empty();
+            }
+        } catch (Exception e) {
+            LOGGER.warn("Exception while resolving shortcut: {}", shortcutPath, e);
+            return Optional.empty();
+        }
+    }
+
+    public static List<Path> resolveWindowsShortcuts(List<Path> paths) {
+        return paths.stream()
+                    .flatMap(path -> {
+                        if (isShortcutFile(path)) {
+                            return resolveWindowsShortcut(path).stream();
+                        }
+                        return Stream.of(path);
+                    })
+                    .toList();
+    }
Evidence
The checklist requires appropriate tests for behavior changes. The PR adds new shortcut-resolution
behavior in FileUtil, but existing FileUtilTest extension cases do not include .lnk,
indicating the new functionality is not covered by tests.

AGENTS.md
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-596]
jablib/src/test/java/org/jabref/logic/util/io/FileUtilTest.java[179-190]

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 Windows shortcut (`.lnk`) drag-and-drop support introduces new `FileUtil` behavior without corresponding test coverage.
## Issue Context
At minimum, tests should cover:
- `isShortcutFile` correctly detecting `.lnk` (case handling if intended)
- `resolveWindowsShortcuts` returning the original paths for non-shortcut inputs
- non-Windows behavior: `resolveWindowsShortcut` should return `Optional.empty()` without throwing
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-596]
- jablib/src/test/java/org/jabref/logic/util/io/FileUtilTest.java[179-210]

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


9. Case-sensitive .lnk check 🐞 Bug ✓ Correctness
Description
isShortcutFile uses exact string match ("lnk"::equals) but getFileExtension does not
actually lowercase the extension, despite its documentation. • As a result, shortcuts like
FILE.LNK may not be recognized, breaking the new drag-and-drop behavior on Windows.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R524-530]

+    /// Test if the file is a shortcut file by simply checking the extension to be ".lnk"
+    ///
+    /// @param file The file to check
+    /// @return True if file extension is ".lnk", false otherwise
+    public static boolean isShortcutFile(Path file) {
+        return getFileExtension(file).filter("lnk"::equals).isPresent();
+    }
Evidence
The extension helper claims to return lowercase but returns the raw extension from FilenameUtils.
The shortcut predicate compares with a lowercase literal using a case-sensitive equals.

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[74-94]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-530]

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

## Issue description
`.lnk` detection is effectively case-sensitive because `getFileExtension` returns the raw extension and `isShortcutFile` compares using `&amp;amp;quot;lnk&amp;amp;quot;::equals`.
### Issue Context
Windows file extensions are case-insensitive and `.LNK` is common. The current implementation can fail to detect valid shortcuts.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[74-94]
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-530]
### Suggested approach
- Update `getFileExtension` to return `extension.toLowerCase(Locale.ROOT)` (and adjust imports/tests accordingly), aligning with its Javadoc.
- Alternatively, change `isShortcutFile` to `filter(ext -&amp;amp;gt; ext.equalsIgnoreCase(&amp;amp;quot;lnk&amp;amp;quot;))` (but prefer central normalization in `getFileExtension`).

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


10. Shortcut returns non-file 🐞 Bug ✓ Correctness
Description
resolveWindowsShortcut returns any existing target path, including directories, because it only
checks Files.exists. • Downstream import/link logic in ImportHandler treats unknown paths by
creating an empty entry with a file link, which can create incorrect entries if a shortcut points to
a directory.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R568-573]

+            if (exitCode == 0 && targetPath != null && !targetPath.trim().isEmpty()) {
+                Path resolvedPath = Path.of(targetPath.trim());
+                if (Files.exists(resolvedPath)) {
+                    LOGGER.debug("Resolved shortcut {} to {}", shortcutPath, resolvedPath);
+                    return Optional.of(resolvedPath);
+                } else {
Evidence
The resolver returns paths that exist without verifying they are regular files. ImportHandler
creates an entry for non-PDF/non-BIB paths, which would include directories returned by the
resolver.

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[568-573]
jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java[183-199]

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

## Issue description
Shortcut resolution currently returns any existing path, including directories. This can leak directory targets into file-import/link flows, creating empty entries with folder links.
### Issue Context
`ImportHandler` treats unknown paths as generic files and creates an empty entry with a link, which is incorrect when the path is a folder.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[568-576]
- jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java[183-199]
### Suggested approach
- Replace `Files.exists(resolvedPath)` with `Files.isRegularFile(resolvedPath)` (or `Files.isRegularFile(resolvedPath, LinkOption.NOFOLLOW_LINKS)` if appropriate).
- Consider logging a clear warning when the shortcut target is not a regular file.
- Optionally: for callers that can accept directories, add an explicit parameter/alternate method rather than returning directories by default.

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



Remediation recommended

11. Warn log lacks file context 📘 Rule violation ✧ Quality ⭐ New
Description
The new warning log for shortcut resolution failures omits which shortcut path failed, reducing
diagnosability. This does not provide actionable context about what failed and why.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R556-558]

+        } catch (IOException | ShellLinkException e) {
+            LOGGER.warn("Could not resolve shortcut file", e);
+        }
Evidence
The compliance rule requires meaningful context in errors; the added log message is generic and does
not include the path being resolved (or any relevant details), making production debugging harder.

Rule 3: Generic: Robust Error Handling and Edge Case Management
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[556-558]

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 log message emitted when shortcut resolution fails is too generic and doesn&#x27;t identify which `.lnk` file caused the failure.

## Issue Context
This makes troubleshooting user reports difficult when multiple files are dragged/dropped or opened.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[556-558]

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


12. I/O during drag-over 🐞 Bug ➹ Performance ⭐ New
Description
FrameDndHandler now resolves shortcuts (including filesystem existence checks) while handling
drag-over events on the JavaFX UI thread. This can cause noticeable UI stalls when dragging .lnk
files from slow/network locations or when drag-over fires frequently.
Code

jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[R238-244]

+            return dragboard.getFiles().stream().map(File::toPath).filter(this::isAcceptedFile).collect(Collectors.toList());
        }
    }

+    private boolean isAcceptedFile(Path path) {
+        return FileUtil.isBibFile(FileUtil.resolveIfShortcut(path));
+    }
Evidence
Drag-over handlers call hasBibFiles, which calls getBibFiles. After this PR, getBibFiles filters via
isAcceptedFile, which resolves .lnk targets via FileUtil.resolveIfShortcut; that function parses
shortcut files and performs Files.exists checks, i.e., potentially blocking I/O, on the UI event
thread.

jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[164-173]
jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[230-244]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[545-555]

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

### Issue description
`FrameDndHandler` performs shortcut resolution while deciding whether a dragboard &quot;has bib files&quot; during drag-over. This can trigger I/O-heavy work (parsing `.lnk`, `Files.exists`) repeatedly on the JavaFX UI thread, causing UI lag.

### Issue Context
Drag-over events fire frequently; the current `hasBibFiles -&gt; getBibFiles -&gt; isAcceptedFile -&gt; resolveIfShortcut` chain does potentially blocking work.

### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[164-173]
- jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[230-244]
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[540-561]

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


13. Logs absolute file paths 📘 Rule violation ⛨ Security
Description
• The new shortcut-resolution flow logs shortcutPath and resolvedPath, which may include user
profile directories/usernames and thus constitutes potentially sensitive information in logs. • This
increases risk of leaking personal data via logs when users drag and drop files from home
directories.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R571-582]

+                    LOGGER.debug("Resolved shortcut {} to {}", shortcutPath, resolvedPath);
+                    return Optional.of(resolvedPath);
+                } else {
+                    LOGGER.warn("Shortcut target does not exist: {}", resolvedPath);
+                    return Optional.empty();
+                }
+            } else {
+                LOGGER.warn("Failed to resolve shortcut: {} (exit code: {})", shortcutPath, exitCode);
+                return Optional.empty();
+            }
+        } catch (Exception e) {
+            LOGGER.warn("Exception while resolving shortcut: {}", shortcutPath, e);
Evidence
The secure logging rule requires avoiding sensitive data in logs. Logging full paths can expose
personal information (e.g., usernames) and local directory structure.

Rule 5: Generic: Secure Logging Practices
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[571-579]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[581-582]

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 shortcut-resolution logs include full filesystem paths (`shortcutPath`, `resolvedPath`) which may contain sensitive user information (e.g., usernames).
## Issue Context
Drag-and-drop file paths commonly point into a user&amp;amp;#x27;s home directory. Logging them at `warn` (and even `debug`) can leak PII into log files.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[571-582]

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


View more (2)
14. CHANGELOG link has space 📘 Rule violation ✓ Correctness
Description
• The added CHANGELOG entry formats the issue link as [#15036] (https://...) which is malformed
Markdown and will not render as a proper hyperlink. • This leaves written project artifacts in a
malformed state, contrary to the requirement to correct typos/malformed text.
Code

CHANGELOG.md[43]

+- Allowed drag and drop of Windows shortcut (.lnk) files to open libraries [#15036] (https://github.com/JabRef/jabref/issues/15036)
Evidence
The compliance rule requires fixing typos and malformed text in changelog/documentation. The new
entry includes an incorrectly formatted Markdown link.

CHANGELOG.md[43-43]
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
The CHANGELOG entry contains malformed Markdown for the issue link due to an extra space between the link text and URL.
## Issue Context
Broken links in CHANGELOG reduce readability and professionalism.
## Fix Focus Areas
- CHANGELOG.md[43-43]

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


15. UI thread blocked 🐞 Bug ⛯ Reliability
Description
resolveWindowsShortcut spawns PowerShell and calls process.waitFor() with no timeout; if
PowerShell startup hangs/slow, drag-and-drop can freeze the JavaFX UI. • The shortcut resolution is
invoked directly inside drag-and-drop handlers (before background tasks), so this blocking behavior
is user-facing.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R548-567]

+        try {
+            // Use PowerShell to resolve the shortcut target
+            ProcessBuilder pb = new ProcessBuilder(
+                    "powershell", "-NoProfile", "-Command",
+                    "$ws = New-Object -ComObject WScript.Shell; " +
+                            "$shortcut = $ws.CreateShortcut('" + shortcutPath.toAbsolutePath().toString().replace("'", "''") + "'); " +
+                            "Write-Output $shortcut.TargetPath"
+            );
+
+            pb.redirectErrorStream(true);
+            Process process = pb.start();
+
+            String targetPath;
+            try (java.io.BufferedReader reader = new java.io.BufferedReader(
+                    new java.io.InputStreamReader(process.getInputStream()))) {
+                targetPath = reader.readLine();
+            }
+
+            int exitCode = process.waitFor();
+
Evidence
The shortcut resolution uses an external process and waits synchronously. The DnD handlers call
resolution inline, which runs on the JavaFX event thread, making UI freezes plausible.

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[548-567]
jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[538-559]
jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[182-188]
jablib/src/main/java/org/jabref/logic/os/OS.java[81-96]

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

## Issue description
`FileUtil.resolveWindowsShortcut` executes PowerShell and blocks on `process.waitFor()` without a timeout. This is called inline from JavaFX drag-and-drop handlers, which can freeze the UI if PowerShell is slow/hangs.
### Issue Context
The codebase already depends on `mslinks.ShellLink` (see `OS.detectProgramPath`) to resolve `.lnk` targets in-process, which avoids external process overhead/hangs.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[548-567]
- jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[538-559]
- jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[182-188]
- jablib/src/main/java/org/jabref/logic/os/OS.java[81-96]
### Suggested approach
- Replace the PowerShell-based resolution with `mslinks.ShellLink` resolution (Windows-only), similar to `OS.detectProgramPath`.
- If PowerShell must remain:
- run resolution on a background executor (not the FX thread)
- add a timeout (e.g., `process.waitFor(timeout, unit)` and `process.destroyForcibly()`)
- drain full output to avoid blocking on filled buffers.

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



Advisory comments

16. Trivial .lnk comments added 📘 Rule violation ✓ Correctness
Description
• The new documentation comment for isShortcutFile restates what the code obviously does (checks
the extension) instead of explaining intent, limitations, or rationale. • This adds noise without
improving maintainability, contrary to the requirement that comments explain the “why”.
Code

jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[R524-530]

+    /// Test if the file is a shortcut file by simply checking the extension to be ".lnk"
+    ///
+    /// @param file The file to check
+    /// @return True if file extension is ".lnk", false otherwise
+    public static boolean isShortcutFile(Path file) {
+        return getFileExtension(file).filter("lnk"::equals).isPresent();
+    }
Evidence
The compliance rule requires comments to explain rationale rather than narrate code. The added
comment states it checks the .lnk extension, which is already evident from the implementation.

AGENTS.md
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-530]

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

## Issue description
Comments for `isShortcutFile` narrate the implementation rather than documenting intent/limitations.
## Issue Context
Per codebase conventions, comments should focus on non-obvious rationale (e.g., why extension-check is sufficient, platform constraints, or security considerations).
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[524-530]

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


17. Inconsistent DnD shortcut support 🐞 Bug ✓ Correctness
Description
• Main table drag-and-drop resolves .lnk files before importing, but group tree drag-and-drop
import still forwards raw paths. • Users dropping the same shortcut onto different UI areas will see
different behavior; this is likely an unintended inconsistency.
Code

jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[R542-543]

+            List<Path> files = FileUtil.resolveWindowsShortcuts(event.getDragboard().getFiles().stream().map(File::toPath).collect(Collectors.toList()));
Evidence
MainTable explicitly resolves shortcuts, while GroupTreeView constructs the file list without
resolution before invoking the same import handler.

jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[541-576]
jabgui/src/main/java/org/jabref/gui/groups/GroupTreeView.java[408-422]

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

## Issue description
`.lnk` resolution is applied in some drag-and-drop entry points (MainTable) but not others (GroupTreeView), leading to inconsistent behavior.
### Issue Context
Both paths eventually call `ImportHandler.importFilesInBackground`, so normalizing the file list upstream improves consistency.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[538-576]
- jabgui/src/main/java/org/jabref/gui/groups/GroupTreeView.java[408-422]
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[587-596]
### Suggested approach
- Use `FileUtil.resolveWindowsShortcuts(...)` in GroupTreeView as well.
- Or extract a shared helper for `dragboard.getFiles() -&amp;amp;gt; List&amp;amp;lt;Path&amp;amp;gt;` normalization used by all DnD handlers.

ⓘ 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

@subhramit
Copy link
Member

Please fix the mandatory checks section of the PR. You can copy-paste from another PR or from our PULL_REQUEST_TEMPLATE

Comment on lines +549 to +555
if (targetPath != null && !targetPath.isEmpty()) {
Path resolvedPath = Path.of(targetPath);

if (Files.exists(resolvedPath) && !Files.isDirectory(resolvedPath)) {
return resolvedPath;
}
}

Choose a reason for hiding this comment

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

Action required

1. path.of(targetpath) may crash 📘 Rule violation ⛯ Reliability

resolveIfShortcut calls Path.of(targetPath) without handling InvalidPathException, which can
crash the app for malformed/unsupported shortcut targets. This violates the requirement to handle
edge cases and missing/invalid external input safely.
Agent Prompt
## Issue description
`FileUtil.resolveIfShortcut` converts the shortcut target string to a `Path` using `Path.of(targetPath)` without handling `InvalidPathException`. If a `.lnk` points to an invalid/unsupported path string, this can throw an unchecked exception and crash the flow.

## Issue Context
The shortcut target is external/untrusted data coming from the `.lnk` file. The method currently only catches `IOException | ShellLinkException`.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[545-561]

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

Comment on lines 541 to +545
if (event.getDragboard().hasFiles()) {
List<Path> files = event.getDragboard().getFiles().stream().map(File::toPath).collect(Collectors.toList());
List<Path> files = event.getDragboard().getFiles().stream()
.map(File::toPath)
.map(FileUtil::resolveIfShortcut)
.collect(Collectors.toList());

Choose a reason for hiding this comment

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

Action required

2. resolveifshortcut on ui thread 📘 Rule violation ➹ Performance

Drag-and-drop handlers call FileUtil.resolveIfShortcut synchronously while processing JavaFX drag
events, which may perform filesystem IO and block the UI thread. This risks UI freezes during
drag/drop operations.
Agent Prompt
## Issue description
The UI drag-and-drop handlers synchronously resolve `.lnk` shortcuts, which can involve filesystem IO and block the JavaFX UI thread.

## Issue Context
`FileUtil.resolveIfShortcut` parses `.lnk` files via `mslinks.ShellLink`, which may be slow and should not run during UI event handling.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java[541-545]
- jabgui/src/main/java/org/jabref/gui/frame/FrameDndHandler.java[238-244]
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[540-561]

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

Comment on lines +168 to +173
// Resolve any shortcuts to their targets and filter to only .bib files.
// The resulting list must remain modifiable for downstream processing (iterator.remove() calls below).
List<Path> resolvedFiles = filesToOpen.stream()
.map(FileUtil::resolveIfShortcut)
.filter(FileUtil::isBibFile)
.collect(Collectors.toList());

Choose a reason for hiding this comment

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

Action required

3. Uppercase .bib not opened 🐞 Bug ✓ Correctness

OpenDatabaseAction.openFiles now filters files with FileUtil.isBibFile, which is effectively
case-sensitive because getFileExtension returns the raw extension. This can cause valid libraries
like "FILE.BIB" (or other non-lowercase variants) to no longer open or raise an already-open tab.
Agent Prompt
### Issue description
`OpenDatabaseAction.openFiles` now filters inputs using `FileUtil.isBibFile`. Because `isBibFile` does an exact match against "bib" and `getFileExtension` returns the raw extension (not lowercased), valid libraries like `MYLIB.BIB` can be filtered out and therefore will no longer open.

### Issue Context
Before this PR, `openFiles` would open any existing file path (no extension gate). The new filter is intended to prevent trying to open `.lnk` binaries as libraries, but it introduces a regression for mixed/uppercase extensions.

### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[76-96]
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[518-524]
- jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java[168-173]
- jablib/src/test/java/org/jabref/logic/util/io/FileUtilTest.java[423-433]

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

@faneeshh faneeshh changed the title Allow drag and drop of Windows shortcut (.lnk) files : Fix for issue #15036 Allow drag and drop of Windows shortcut (.lnk) files Feb 27, 2026
@faneeshh
Copy link
Contributor Author

Please fix the mandatory checks section of the PR. You can copy-paste from another PR or from our PULL_REQUEST_TEMPLATE

Should work now

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

Labels

good first issue An issue intended for project-newcomers. Varies in difficulty. status: changes-required Pull requests that are not yet complete

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Windows] Allow drag and drop of symblinks

4 participants