Skip to content

Conversation

@paudelritij
Copy link
Contributor

@paudelritij paudelritij commented Sep 9, 2025

Closes #9798

This PR relinks files, if it has been moved by user.

Steps to test

  • Open `test-support\src\manual-tests\issue-9798\issue-9798.bib in JabRef
  • Start "Lookup" -> "Find unlinked files"
  • Start search
  • File will be relinked - and following shown in the message coloumn (under import tab): File relinked to entry minimal.pdf
D9481E13-FE08-4D7F-9A7D-CDFE9106959C

Mandatory checks

@jabref-machine
Copy link
Collaborator

Note that your PR will not be reviewed/accepted until you have gone through the mandatory checks in the description and marked each of them them exactly in the format of [x] (done), [ ] (not done yet) or [/] (not applicable).

@paudelritij
Copy link
Contributor Author

  1. I starting using existence code from AutoSetFileLinksUtil.java but found it could increase time complexity by using findAssociatedNotLinkedFiles() since it makes unnecessary call (in context of this feature) to while adding items in result (by calling findAssociatedFiles & findByBrokenLinkName), so I decided to create a separate method; and easy for testing as well.
  2. org.jabref.gui.externalfiles.AutoSetFileLinksUtil#findAssociatedNotLinkedFiles was adding same link two times so I suggest to make use of set instead of list.
  3. I had a file (a.pdf) in two places (dir/a.pdf & dir/sub-dir/a.pdf) however org.jabref.gui.externalfiles.AutoSetFileLinksUtil#findByBrokenLinkName was showing me file from sub-dir/a.pdf only so I suggest to show all related files having wantedbasename(forEach()). (one more point is that if b.txt link has been broken and findByBrokenLinkName() found file with wantedbasename to be as of b.pdf and then using code for findfirst() will add only b.pdf not b.txt which is the required one)

@paudelritij paudelritij marked this pull request as ready for review September 24, 2025 18:15
List<LinkedFile> filesToAdd = new ArrayList<>();

for (LinkedFile brokenLink : currentFiles) {
if (brokenLink.findIn(bibDatabase, preferences.getFilePreferences()).isPresent()) {
Copy link
Member

Choose a reason for hiding this comment

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

Isn't this just duplicated? You add all those to entriesWithBrokenLInks that are not present so why checking them again here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not all.. only entries having at least one broken link... though it could be improved thnks for letting know

@Siedlerchr Siedlerchr added the status: changes-required Pull requests that are not yet complete label Sep 24, 2025
@Test
void fixBrokenLinksInLargeDatabase(@TempDir Path tempDir) throws IOException {
Path testRoot = tempDir.resolve("test");
List<BibEntry> entries = new ArrayList<>();
Copy link

Choose a reason for hiding this comment

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

Use modern Java data structures. Instead of using new ArrayList<>(), use List.of() for creating an empty list.

@jabref-machine
Copy link
Collaborator

Your pull request needs to link an issue correctly.

To ease organizational workflows, please link this pull-request to the issue with syntax as described in https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue:

Linking a pull request to an issue using a keyword

You can link a pull request to an issue by using a supported keyword in the pull request's description or in a commit message.

Examples

  • Fixes #xyz links pull-request to issue. Merging the PR will close the issue.
  • Fixes https://github.com/JabRef/jabref/issues/xyz links pull-request to issue. Merging the PR will close the issue.
  • Fixes https://github.com/Koppor/jabref/issues/xyz links pull-request to issue. Merging the PR will close the issue.
  • Fixes [#xyz](https://github.com/JabRef/jabref/issues/xyz) links pull-request to issue. Merging the PR will NOT close the issue.

@trag-bot
Copy link

trag-bot bot commented Sep 25, 2025

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

@calixtus
Copy link
Member

calixtus commented Oct 9, 2025

Is this PR ready for review?

@paudelritij
Copy link
Contributor Author

Is this PR ready for review?

I did changes could anyone please review it? Thanks.

@calixtus
Copy link
Member

Great, we will look into it.

@calixtus calixtus added the status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers label Oct 13, 2025
@koppor koppor removed the status: changes-required Pull requests that are not yet complete label Nov 11, 2025
@jabref-machine
Copy link
Collaborator

Your code currently does not meet JabRef's code guidelines. We use OpenRewrite to ensure "modern" Java coding practices. You can see which checks are failing by locating the box "Some checks were not successful" on the pull request page. To see the test output, locate "Source Code Tests / OpenRewrite (pull_request)" and click on it.

The issues found can be automatically fixed. Please execute the gradle task rewriteRun from the rewrite group of the Gradle Tool window in IntelliJ, then check the results, commit, and push.

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.

Thank you for working on this - and your patience.

Attached some comments 😅

Comment on lines +460 to +470
// Verify specific file links were updated to correct relative paths
assertEquals(Optional.of(newPdf1Location.toString()), entry1.getFiles().stream().filter(file -> file.getLink().equals(newPdf1Location.toString())).findFirst().map(LinkedFile::getLink));
assertEquals(Optional.of(newDocLocation.toString()), entry1.getFiles().stream().filter(file -> file.getLink().equals(newDocLocation.toString())).findFirst().map(LinkedFile::getLink));
assertEquals(Optional.of(newPdf2Location.toString()), entry2.getFiles().stream().filter(file -> file.getLink().equals(newPdf2Location.toString())).findFirst().map(LinkedFile::getLink));
assertEquals(Optional.of(newTxtLocation.toString()), entry2.getFiles().stream().filter(file -> file.getLink().equals(newTxtLocation.toString())).findFirst().map(LinkedFile::getLink));
assertEquals(Optional.of(newPptLocation.toString()), entry3.getFiles().stream().filter(file -> file.getLink().equals(newPptLocation.toString())).findFirst().map(LinkedFile::getLink));

// Verify that files with similar names but different extensions were not incorrectly matched
assertEquals(Optional.empty(), entry1.getFiles().stream().filter(file -> file.getLink().contains("research-paper.txt")).findFirst().map(LinkedFile::getLink));

assertEquals(Optional.empty(), entry2.getFiles().stream().filter(file -> file.getLink().contains("thesis.docx")).findFirst().map(LinkedFile::getLink));
Copy link
Member

Choose a reason for hiding this comment

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

Cant the whole BibDatabase be compared? - If not, please add a code comment why there are multiple assert statements.

}

@Test
void fixBrokenLinksInLargeDatabase(@TempDir Path tempDir) throws IOException {
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 not what the test does. The test checks the size of the result list -- Please adapt the test name

Suggested change
void fixBrokenLinksInLargeDatabase(@TempDir Path tempDir) throws IOException {
void correctCountOfUnlinkedFiles(@TempDir Path tempDir) throws IOException {

Comment on lines +153 to +155
BibEntry entry = new BibEntry(StandardEntryType.Misc);
LinkedFile brokenLink = new LinkedFile("", oldPath.toString(), "PDF");
entry.addFile(brokenLink);
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice if .withFiles could be used. - also in the other tests. -- Use it with List.of(...) and do the new LinkedFile inside.

Comment on lines +375 to +376
// For testing purposes
public int resultListSize() {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// For testing purposes
public int resultListSize() {
// @VisibleForTesting
int resultListSize() {

entry.setFiles(currentFiles);
}
} catch (IOException e) {
LOGGER.error("Error finding associated files for entry", e);
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 if the data structures are consistent in all error cases. Shouldn't the try/catch be more close to the points where the exception can occur and an appropriate handling take place?

Are results consistent in all cases? Maybe yes, but that deserves a code comment.

Comment on lines +192 to +193
resultList.clear();
resultList.addAll(results);
Copy link
Member

Choose a reason for hiding this comment

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

This needs a Java comment - why this is done.

Comment on lines -136 to +138
return linkedFiles;
return linkedFiles.stream().toList();
Copy link
Member

Choose a reason for hiding this comment

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

This feels not OK - promising a list in the method contract, but internally a set with a random order.

Please convert the result type to Set and also adapt the callers. Then, you will see if there is a list assumption anywhere -- and if there should be an ordering done. If an ordering needs to be done, either do it here (Maybe, use SortedSet). Or do it at the place where sorting is needed.

treeRootProperty.setValue(Optional.empty());
}

public void findAndFixBrokenLinks(Path searchDirectory) {
Copy link
Member

Choose a reason for hiding this comment

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

This method needs a JavaDoc comment

Maybe also the name needs to be adjusted --> fixBrokenLinksAndReturnStillBroken

Is it possible NOT to use a global variable resultList?

Finally, this should go to org.jabref.logic.util.io.FileUtil, because, it is NOT a UI thing, but a logic (which could be used by JabKit, too)

@github-actions github-actions bot added status: changes-required Pull requests that are not yet complete and removed status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers labels Nov 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: external-files status: changes-required Pull requests that are not yet complete

Projects

None yet

Development

Successfully merging this pull request may close these issues.

If user moved file, it should simply be relinked - [Find Unlinked Files part]

5 participants