Skip to content

Fix/copy linked files on entry transfer #13535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 47 commits into
base: main
Choose a base branch
from

Conversation

UmutAkbayin
Copy link

@UmutAkbayin UmutAkbayin commented Jul 13, 2025

Closes #12267

Adds a requirements specification file files.md documenting the linked file transfer logic between BibTeX entries in JabRef.
The document captures three key scenarios for when linked files are reachable or not in the target context and the expected behavior regarding path adjustment and file copying.

This improves traceability by formally specifying requirements that are linked to implementation and tests via OpenFastTrace.

Steps to test

  • Verify that the new requirements specification file docs/requirements/files.md is present, properly formatted, and follows JabRef conventions.

  • Run all existing and new unit tests in LinkedFileTransferHelperTest to ensure the linked file transfer logic behaves correctly across all covered scenarios.

  • Manually verify the business logic in JabRef, especially for the scenario where the linked file is not reachable and a nested directory structure must be created. Reviewers should confirm that the path adjustments and file copying behavior conform to the requirements.

  • Provide feedback if the handling of the last case (unreachable file with differing paths) aligns with project expectations.

Mandatory checks

  • I own the copyright of the code submitted and I license it under the MIT license
  • [/] Change in CHANGELOG.md described in a way that is understandable for the average user (if change is visible to the user)
  • Tests created for changes (if applicable)
  • Manually tested changed features in running JabRef (always required)
  • [/] Screenshots added in PR description (if change is visible to the user)
  • [/] Checked developer's documentation: Is the information available and up to date? If not, I outlined it in this pull request.
  • [/] Checked documentation: Is the information available and up to date? If not, I created an issue at https://github.com/JabRef/user-documentation/issues or, even better, I submitted a pull request to the documentation repository.

FilePreferences filePreferences
) {

Set<BibEntry> modifiedEntries = new HashSet<>();
Copy link

Choose a reason for hiding this comment

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

Using constructor for HashSet instead of modern Java collection factory method Set.of(). Modern Java practices recommend using factory methods for cleaner and more maintainable code.

Copy link
Author

Choose a reason for hiding this comment

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

could not find a quick solution. would have to refactor the logic then.


for (BibEntry entry : targetContext.getEntries()) {
boolean entryChanged = false;
List<LinkedFile> linkedFiles = new ArrayList<>();
Copy link

Choose a reason for hiding this comment

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

Using ArrayList constructor instead of modern Java collection factory methods. Should use List.of() for empty list initialization following JabRef's conventions.

Copy link
Author

Choose a reason for hiding this comment

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

could not find a quick solution. would have to refactor the logic then.

Copy link
Member

Choose a reason for hiding this comment

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

TracBot is wrong here 😅

@koppor
Copy link
Member

koppor commented Jul 23, 2025

@UmutAkbayin Please read the bot comment - one week old

Fix the tests

image

@koppor koppor marked this pull request as draft July 23, 2025 19:18
@koppor
Copy link
Member

koppor commented Jul 23, 2025

I converted the PR to draft, because a) merge conflicts b) tests failing.

@koppor
Copy link
Member

koppor commented Jul 23, 2025

"Steps to test" seems to be AI generated. It is not a step-by-step guide. Please refine.

@UmutAkbayin
Copy link
Author

I noticed that one unit test is affected by the changes in LibraryTab#pasteEntry. I’ll look into it tomorrow or over the weekend. I’ll also review the other things like code style.

@UmutAkbayin UmutAkbayin force-pushed the fix/copy-linked-files-on-entry-transfer branch from 3f155a7 to 3ee3b08 Compare August 1, 2025 19:38
Allows tracking the source BibDatabaseContext for later use in pasteEntry()
…er and setter

Enables tracking the source database context for use in pasteEntry()
… CopyTo#copyEntriesWithFeedback and LibraryTab#pasteEntry
Add filePreferences parameter to CopyTo.java to enable
LinkedFile#findIn functionality
…sferHelperTest#isReachableFromPrimaryDirectory method
Cloning the BibEntry in importEntryWithDuplicateCheck to ensure that
any file path adjustments during the copy/paste process do not affect
the original entry in the source database.

This avoids side effects when imported entries are modified (e.g. file links),
especially when the same BibEntry instance is shared between databases.
…ls in LibraryTab and CopyTo to align with new method signature
@UmutAkbayin
Copy link
Author

There seem to be no issues anymore.

@UmutAkbayin UmutAkbayin marked this pull request as ready for review August 1, 2025 22:12

@Test
void pathDiffers_ShouldAdjustPath() {
var returnedEntries = LinkedFileTransferHelper.adjustLinkedFilesForTarget(sourceContext, targetContext,
Copy link
Member

Choose a reason for hiding this comment

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

Please put datatype here. No var

Copy link
Author

Choose a reason for hiding this comment

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

Done.

assertEquals("target/sourcefiles/test.pdf",
targetContext.getEntries().getFirst().getFiles().getFirst().getLink());
Path expectedFile = targetDir.resolve("test.pdf");
assertFalse(Files.exists(expectedFile));
Copy link
Member

Choose a reason for hiding this comment

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

Not sure why retunedEntries cannot be assertEquals({sometingExpected}, returnedEntries);

@@ -0,0 +1,21 @@
# File Transfer Between Bib Entries

Copy link
Member

Choose a reason for hiding this comment

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

Maybe, add that in the following "reachable" denotes: reachable using a relative path not climbing up the directory structure?

Also that it respects all configured directories for files

Copy link
Author

Choose a reason for hiding this comment

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

Here’s my update to the description regarding file reachability:

“Reachable” means the linked file can be accessed via a relative path that does not climb up the directory structure (no .. segments beyond the root).
Also, this respects all configured directories for files as per JabRef’s settings (link).

According to this understanding, the implementation is aligned with these cases.

Does this match your understanding?

Copy link
Member

Choose a reason for hiding this comment

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

You should move the "reachable" paragraph before the list of requirements. Reason: One often jumps directly to a requirement - and does NOT read the other requirements --> but the definition is put in another requiement.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

private final FilePreferences filePreferences = mock(FilePreferences.class);

@Nested
class WhenFileIsReachable {
Copy link
Member

Choose a reason for hiding this comment

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

I would expect that all three (four?) directory cases of https://docs.jabref.org/finding-sorting-and-cleaning-entries/filelinks#directories-for-files are checked.

I see only shouldStoreFilesRelativeToBibFile being checked. Please add other test cases

  • Global latex directory
  • BibFile-specific directory
  • User-specific directory

If in one of these, the file is found, it is linked relative. If in mulitple, precedence is as follows:

--> user-specific directory > BibFile-specific directory > Global latex directory / relative to the bib file

Copy link
Author

Choose a reason for hiding this comment

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

Hi, I’ve pushed the updated changes, including consolidated assertions and the clarified definition of “reachable”.

Regarding the extended test cases for the different directory types:
I’d like to take a bit more time to read through the relevant code paths and documentation carefully to fully understand the requirements and write robust, meaningful tests.
I’ll plan to follow up on this in the coming days.

Thanks again for the detailed feedback!

Copy link
Member

@koppor koppor left a comment

Choose a reason for hiding this comment

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

There should be test cases for all four kinds of directories

  • Global latex directory OR stored-relative to bib file (2 cases)
  • BibFile-specific directory
  • User-specific directory

Documentation at https://docs.jabref.org/finding-sorting-and-cleaning-entries/filelinks#directories-for-files and https://docs.jabref.org/setup/databaseproperties#override-default-file-directories

…ries and consolidate assertions

- Replace multiple individual assertions with single assertEquals calls comparing expected and actual Sets of BibEntry
- Improves test clarity and maintainability by bundling assertions into one comprehensive check
Copy link
Member

@koppor koppor left a comment

Choose a reason for hiding this comment

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

Some intermediate feedback. Looking forward to the full solution.

@@ -0,0 +1,21 @@
# File Transfer Between Bib Entries

Copy link
Member

Choose a reason for hiding this comment

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

You should move the "reachable" paragraph before the list of requirements. Reason: One often jumps directly to a requirement - and does NOT read the other requirements --> but the definition is put in another requiement.

@@ -166,6 +177,10 @@ public void setContent(List<BibEntry> entries, BibEntryTypesManager entryTypesMa
setContent(builder.toString());
}

public void setSourceBibDatabaseContext(@NonNull BibDatabaseContext context) {
sourceDatabaseContext = Objects.requireNonNull(context, "context must not be null");
Copy link
Member

Choose a reason for hiding this comment

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

No .requireNonNull. Just copy. @NonNull annotation is enough.

Copy link
Author

Choose a reason for hiding this comment

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

Done.


importHandler.importEntriesWithDuplicateCheck(bibDatabaseContext, finalEntriesToAdd, tracker);

if (clipBoardManager.getSourceBibDatabaseContext().isPresent()) {
Copy link
Member

Choose a reason for hiding this comment

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

Use ifPresent and directly use the available context.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

@@ -117,6 +124,10 @@ public static String getContentsPrimary() {
return getContents();
}

public Optional<BibDatabaseContext> getSourceBibDatabaseContext() {
Copy link
Member

Choose a reason for hiding this comment

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

Please keep setters and getters together.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

ClipBoardManager clipBoardManager,
JournalAbbreviationRepository abbreviationRepository,
TaskExecutor taskExecutor) {
Menu copySpecialMenu = factory.createMenu(StandardActions.COPY_MORE);

clipBoardManager.setSourceBibDatabaseContext(libraryTab.getBibDatabaseContext());
Copy link
Member

Choose a reason for hiding this comment

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

This is toatally wrong.

Every thought of switchting tabs? Ever thought about the order of calls? Ever thoght about that there are key bindings such as Ctrl+C?

--> move this to org.jabref.gui.edit.EditAction#execute at the correct place.

Copy link
Author

Choose a reason for hiding this comment

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

Done. I hope the new approach is okay. At least the automatic and manual tests aren't breaking.

Comment on lines 258 to 256
requires org.jetbrains.annotations;
// endregion
requires org.jetbrains.annotations;
// endregion
Copy link
Member

Choose a reason for hiding this comment

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

Why is the indent changed? Is this some Cursor generated code?

Copy link
Author

Choose a reason for hiding this comment

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

I'm using Intellij Idea. I have made use of auto-import of the IDE. Don't know why this file was touched.

Copy link
Author

Choose a reason for hiding this comment

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

Fixed indentation and removal issues in module-info.java. Not ideal for commit history, but a straightforward fix. If you prefer a different approach, please let me know.

Comment on lines 116 to 124
requires java.base;

requires javafx.base;
requires javafx.base;
requires javafx.graphics; // because of javafx.scene.paint.Color
requires afterburner.fx;
requires com.tobiasdiez.easybind;

// for java.awt.geom.Rectangle2D required by org.jabref.logic.pdf.TextExtractor
requires java.desktop;

// SQL
// SQL
requires java.sql;
Copy link
Member

Choose a reason for hiding this comment

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

?!

Copy link
Author

Choose a reason for hiding this comment

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

?? I dont know why this happened. the file was not touched by me. it is also not in my commit history

Copy link
Author

Choose a reason for hiding this comment

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

maybe during rebase or something? idk

Copy link
Author

Choose a reason for hiding this comment

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

Fixed indentation and removal issues in module-info.java. Not ideal for commit history, but a straightforward fix. If you prefer a different approach, please let me know.

}
}

@Nested
Copy link
Member

Choose a reason for hiding this comment

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

Please use // region ... // endregion to group tests. We don't use @Nested on JabRef.

Moreover, please do not use _ in method names; we just have camel case in JabRef

Copy link
Author

Choose a reason for hiding this comment

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

I'll refactor all the tests.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

@koppor koppor marked this pull request as draft August 3, 2025 13:31
- pulled up the reachable definition before the list of requirements
…ction#execute

- Removed clipBoardManager.setSourceBibDatabaseContext(...) from RightClickMenu
- Set the clipboard context dynamically in EditAction before copy and cut actions
- Improves separation of concerns and ensures correct clipboard context regardless of input method (menu, keyboard shortcut, toolbar)
- Uses proper camelCase instead of underscores
- Replaces nested classes with region/endregion comments
- Adds comprehensive test coverage for different file preference scenarios
LOGGER.debug("Adjusted path for reachable file: {} -> {}", currentLink, newLink);
return true;
}
} catch (Exception e) {
Copy link

Choose a reason for hiding this comment

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

Using broad Exception catch block instead of specific exceptions. This can mask important errors and makes it harder to handle different error cases appropriately.

FilePreferences filePreferences
) {

Set<BibEntry> modifiedEntries = new HashSet<>();
Copy link

Choose a reason for hiding this comment

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

Using constructor for HashSet instead of modern Java collection factory method Set.of(). Modern Java practices recommend using factory methods for cleaner and more maintainable code.


for (BibEntry entry : targetContext.getEntries()) {
boolean entryChanged = false;
List<LinkedFile> linkedFiles = new ArrayList<>();
Copy link

Choose a reason for hiding this comment

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

ArrayList is instantiated using constructor instead of modern Java collection factory methods. Should consider using List.of() or other factory methods when possible.

@UmutAkbayin UmutAkbayin requested a review from koppor August 9, 2025 12:39
Copy link

trag-bot bot commented Aug 9, 2025

@trag-bot didn't find any issues in the code! ✅✨

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

When copying and pasting an entry to another bib file - all attached files should also be copied
2 participants