Skip to content

Commit f86e24d

Browse files
authored
Merge pull request #3509 from JabRef/fix3472
Fixes #3472: Files starting with BibTeX key of a similar entry are no longer found by mistake
2 parents c2a38de + 9d03922 commit f86e24d

File tree

15 files changed

+119
-126
lines changed

15 files changed

+119
-126
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
5656
- We fixed an issue where the same Java Look and Feel would be listed more than once in the Preferences. [#3391](https://github.com/JabRef/jabref/issues/3391)
5757
- We fixed an issue where errors in citation styles triggered an exception when opening the preferences dialog. [#3389](https://github.com/JabRef/jabref/issues/3389)
5858
- We fixed an issue where entries were displayed twice after insertion into a shared database. [#3164](https://github.com/JabRef/jabref/issues/3164)
59+
- We improved the auto link algorithm so that files starting with a similar key are no longer found (e.g, `Einstein1902` vs `Einstein1902a`). [#3472](https://github.com/JabRef/jabref/issues/3472)
5960
- We fixed an issue where special fields (such as `printed`) could not be cleared when syncing special fields via the keywords. [#3432](https://github.com/JabRef/jabref/issues/3432)
6061
- We fixed an issue where the tooltip of the global search bar showed html tags instead of formatting the text. [#3381](https://github.com/JabRef/jabref/issues/3381)
6162
- We fixed an issue where timestamps were not updated for changed entries. [#2810](https://github.com/JabRef/jabref/issues/2810)

src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import javafx.scene.control.Tab;
44

5+
import org.jabref.gui.util.DefaultTaskExecutor;
56
import org.jabref.model.entry.BibEntry;
67

78
public abstract class EntryEditorTab extends Tab {
@@ -32,7 +33,7 @@ protected void handleFocus() {
3233
public void notifyAboutFocus(BibEntry entry) {
3334
if (!entry.equals(currentEntry)) {
3435
currentEntry = entry;
35-
bindToEntry(entry);
36+
DefaultTaskExecutor.runInJavaFXThread(() -> bindToEntry(entry));
3637
}
3738
handleFocus();
3839
}

src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,33 @@
2727
public class AutoSetFileLinksUtil {
2828

2929
private static final Log LOGGER = LogFactory.getLog(AutoSetLinks.class);
30+
private List<Path> directories;
31+
private AutoLinkPreferences autoLinkPreferences;
32+
private ExternalFileTypes externalFileTypes;
3033

31-
public List<LinkedFile> findassociatedNotLinkedFiles(BibEntry entry, BibDatabaseContext databaseContext, FileDirectoryPreferences fileDirPrefs, AutoLinkPreferences autoLinkPrefs, ExternalFileTypes externalFileTypes) {
34+
public AutoSetFileLinksUtil(BibDatabaseContext databaseContext, FileDirectoryPreferences fileDirectoryPreferences, AutoLinkPreferences autoLinkPreferences, ExternalFileTypes externalFileTypes) {
35+
this(databaseContext.getFileDirectoriesAsPaths(fileDirectoryPreferences), autoLinkPreferences, externalFileTypes);
36+
}
37+
38+
public AutoSetFileLinksUtil(List<Path> directories, AutoLinkPreferences autoLinkPreferences, ExternalFileTypes externalFileTypes) {
39+
this.directories = directories;
40+
this.autoLinkPreferences = autoLinkPreferences;
41+
this.externalFileTypes = externalFileTypes;
42+
}
43+
44+
public List<LinkedFile> findAssociatedNotLinkedFiles(BibEntry entry) {
3245
List<LinkedFile> linkedFiles = new ArrayList<>();
3346

34-
List<Path> dirs = databaseContext.getFileDirectoriesAsPaths(fileDirPrefs);
3547
List<String> extensions = externalFileTypes.getExternalFileTypeSelection().stream().map(ExternalFileType::getExtension).collect(Collectors.toList());
3648

37-
// Run the search operation:
38-
FileFinder fileFinder = FileFinders.constructFromConfiguration(autoLinkPrefs);
39-
List<Path> result = fileFinder.findAssociatedFiles(entry, dirs, extensions);
40-
41-
// Iterate over the entries:
49+
// Run the search operation
50+
FileFinder fileFinder = FileFinders.constructFromConfiguration(autoLinkPreferences);
51+
List<Path> result = fileFinder.findAssociatedFiles(entry, directories, extensions);
4252

53+
// Collect the found files that are not yet linked
4354
for (Path foundFile : result) {
44-
boolean existingSameFile = entry.getFiles().stream()
45-
.map(file -> file.findIn(dirs))
55+
boolean fileAlreadyLinked = entry.getFiles().stream()
56+
.map(file -> file.findIn(directories))
4657
.anyMatch(file -> {
4758
try {
4859
return file.isPresent() && Files.isSameFile(file.get(), foundFile);
@@ -51,15 +62,13 @@ public List<LinkedFile> findassociatedNotLinkedFiles(BibEntry entry, BibDatabase
5162
}
5263
return false;
5364
});
54-
if (!existingSameFile) {
55-
65+
if (!fileAlreadyLinked) {
5666
Optional<ExternalFileType> type = FileHelper.getFileExtension(foundFile)
5767
.map(externalFileTypes::getExternalFileTypeByExt)
5868
.orElse(Optional.of(new UnknownExternalFileType("")));
5969

6070
String strType = type.isPresent() ? type.get().getName() : "";
61-
String relativeFilePath = FileUtil.shortenFileName(foundFile, dirs)
62-
.toString();
71+
String relativeFilePath = FileUtil.shortenFileName(foundFile, directories).toString();
6372
LinkedFile linkedFile = new LinkedFile("", relativeFilePath, strType);
6473
linkedFiles.add(linkedFile);
6574
}

src/main/java/org/jabref/gui/externalfiles/AutoSetLinks.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,11 @@ public static Runnable autoSetLinks(final List<BibEntry> entries, final NamedCom
8282

8383
Runnable r = () -> {
8484
boolean foundAny = false;
85-
AutoSetFileLinksUtil util = new AutoSetFileLinksUtil();
85+
AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(databaseContext, Globals.prefs.getFileDirectoryPreferences(), Globals.prefs.getAutoLinkPreferences(), ExternalFileTypes.getInstance());
8686

8787
for (BibEntry entry : entries) {
8888

89-
List<LinkedFile> linkedFiles = util.findassociatedNotLinkedFiles(entry, databaseContext, Globals.prefs.getFileDirectoryPreferences(), Globals.prefs.getAutoLinkPreferences(), ExternalFileTypes.getInstance());
89+
List<LinkedFile> linkedFiles = util.findAssociatedNotLinkedFiles(entry);
9090

9191
if (ce != null) {
9292
for (LinkedFile linkedFile : linkedFiles) {

src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,8 @@ public void bindToEntry(BibEntry entry) {
158158
private List<LinkedFileViewModel> findAssociatedNotLinkedFiles(BibEntry entry) {
159159
List<LinkedFileViewModel> result = new ArrayList<>();
160160

161-
AutoSetFileLinksUtil util = new AutoSetFileLinksUtil();
162-
List<LinkedFile> linkedFiles = util.findassociatedNotLinkedFiles(entry, databaseContext, Globals.prefs.getFileDirectoryPreferences(), Globals.prefs.getAutoLinkPreferences(), ExternalFileTypes.getInstance());
163-
161+
AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(databaseContext, Globals.prefs.getFileDirectoryPreferences(), Globals.prefs.getAutoLinkPreferences(), ExternalFileTypes.getInstance());
162+
List<LinkedFile> linkedFiles = util.findAssociatedNotLinkedFiles(entry);
164163
for (LinkedFile linkedFile : linkedFiles) {
165164
LinkedFileViewModel newLinkedFile = new LinkedFileViewModel(linkedFile, entry, databaseContext);
166165
newLinkedFile.markAsAutomaticallyFound();

src/main/java/org/jabref/logic/bibtexkeypattern/BibtexKeyPatternUtil.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@
1919
* This is the utility class of the LabelPattern package.
2020
*/
2121
public class BibtexKeyPatternUtil extends BracketedPattern {
22-
private static final Log LOGGER = LogFactory.getLog(BibtexKeyPatternUtil.class);
23-
2422
// All single characters that we can use for extending a key to make it unique:
25-
private static final String CHARS = "abcdefghijklmnopqrstuvwxyz";
23+
public static final String CHARS = "abcdefghijklmnopqrstuvwxyz";
24+
private static final Log LOGGER = LogFactory.getLog(BibtexKeyPatternUtil.class);
2625

2726
private BibtexKeyPatternUtil() {
2827
}

src/main/java/org/jabref/logic/net/URLDownload.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ public Path toTemporaryFile() throws IOException {
260260

261261
// Take everything after the last '/' as name + extension
262262
String fileNameWithExtension = sourcePath.substring(sourcePath.lastIndexOf('/') + 1);
263-
String fileName = FileUtil.getFileName(fileNameWithExtension);
263+
String fileName = FileUtil.getBaseName(fileNameWithExtension);
264264
String extension = "." + FileHelper.getFileExtension(fileNameWithExtension).orElse("tmp");
265265

266266
// Create temporary file and download to it

src/main/java/org/jabref/logic/util/io/CiteKeyBasedFileFinder.java

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@
55
import java.nio.file.Path;
66
import java.nio.file.attribute.BasicFileAttributes;
77
import java.util.ArrayList;
8-
import java.util.HashMap;
8+
import java.util.Collections;
99
import java.util.HashSet;
1010
import java.util.List;
11-
import java.util.Map;
1211
import java.util.Objects;
1312
import java.util.Optional;
1413
import java.util.Set;
1514
import java.util.function.BiPredicate;
1615
import java.util.stream.Collectors;
1716
import java.util.stream.Stream;
1817

18+
import org.jabref.logic.bibtexkeypattern.BibtexKeyPatternUtil;
1919
import org.jabref.model.entry.BibEntry;
20+
import org.jabref.model.strings.StringUtil;
2021
import org.jabref.model.util.FileHelper;
2122

2223
import org.apache.commons.logging.Log;
@@ -32,54 +33,55 @@ class CiteKeyBasedFileFinder implements FileFinder {
3233
}
3334

3435
@Override
35-
public Map<BibEntry, List<Path>> findAssociatedFiles(List<BibEntry> entries, List<Path> directories, List<String> extensions) {
36+
public List<Path> findAssociatedFiles(BibEntry entry, List<Path> directories, List<String> extensions) {
3637
Objects.requireNonNull(directories);
37-
Objects.requireNonNull(entries);
38+
Objects.requireNonNull(entry);
3839

39-
Map<BibEntry, List<Path>> result = new HashMap<>();
40+
Optional<String> citeKeyOptional = entry.getCiteKeyOptional();
41+
if (StringUtil.isBlank(citeKeyOptional)) {
42+
return Collections.emptyList();
43+
}
44+
String citeKey = citeKeyOptional.get();
45+
46+
List<Path> result = new ArrayList<>();
4047

4148
// First scan directories
4249
Set<Path> filesWithExtension = findFilesByExtension(directories, extensions);
4350

44-
// Initialize Result-Set
45-
for (BibEntry entry : entries) {
46-
result.put(entry, new ArrayList<>());
47-
}
48-
4951
// Now look for keys
50-
nextFile:
5152
for (Path file : filesWithExtension) {
5253
String name = file.getFileName().toString();
53-
int dot = name.lastIndexOf('.');
54-
// First, look for exact matches:
55-
for (BibEntry entry : entries) {
56-
Optional<String> citeKey = entry.getCiteKeyOptional();
57-
if ((citeKey.isPresent()) && !citeKey.get().isEmpty() && (dot > 0)
58-
&& name.substring(0, dot).equals(citeKey.get())) {
59-
result.get(entry).add(file);
60-
continue nextFile;
61-
}
54+
String nameWithoutExtension = FileUtil.getBaseName(name);
55+
56+
// First, look for exact matches
57+
if (nameWithoutExtension.equals(citeKey)) {
58+
result.add(file);
59+
continue;
6260
}
63-
// If we get here, we did not find any exact matches. If non-exact
64-
// matches are allowed, try to find one:
65-
if (!exactKeyOnly) {
66-
for (BibEntry entry : entries) {
67-
Optional<String> citeKey = entry.getCiteKeyOptional();
68-
if ((citeKey.isPresent()) && !citeKey.get().isEmpty() && name.startsWith(citeKey.get())) {
69-
result.get(entry).add(file);
70-
continue nextFile;
71-
}
72-
}
61+
// If we get here, we did not find any exact matches. If non-exact matches are allowed, try to find one
62+
if (!exactKeyOnly && matches(name, citeKey)) {
63+
result.add(file);
7364
}
7465
}
7566

76-
return result;
67+
return result.stream().sorted().collect(Collectors.toList());
68+
}
69+
70+
private boolean matches(String filename, String citeKey) {
71+
boolean startsWithKey = filename.startsWith(citeKey);
72+
if (startsWithKey) {
73+
// The file name starts with the key, that's already a good start
74+
// However, we do not want to match "JabRefa" for "JabRef" since this is probably a file belonging to another entry published in the same time / same name
75+
char charAfterKey = filename.charAt(citeKey.length());
76+
return !BibtexKeyPatternUtil.CHARS.contains(Character.toString(charAfterKey));
77+
}
78+
return false;
7779
}
7880

7981
/**
8082
* Returns a list of all files in the given directories which have one of the given extension.
8183
*/
82-
public Set<Path> findFilesByExtension(List<Path> directories, List<String> extensions) {
84+
private Set<Path> findFilesByExtension(List<Path> directories, List<String> extensions) {
8385
Objects.requireNonNull(extensions, "Extensions must not be null!");
8486

8587
BiPredicate<Path, BasicFileAttributes> isFileWithCorrectExtension = (path, attributes) ->
Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package org.jabref.logic.util.io;
22

33
import java.nio.file.Path;
4-
import java.util.Collections;
54
import java.util.List;
6-
import java.util.Map;
75

86
import org.jabref.model.entry.BibEntry;
97

@@ -13,14 +11,9 @@ public interface FileFinder {
1311
* Finds all files in the given directories that are probably associated with the given entries and have one of the
1412
* passed extensions.
1513
*
16-
* @param entries The entries to search for.
14+
* @param entry The entry to search files for.
1715
* @param directories The root directories to search.
1816
* @param extensions The extensions that are acceptable.
1917
*/
20-
Map<BibEntry, List<Path>> findAssociatedFiles(List<BibEntry> entries, List<Path> directories, List<String> extensions);
21-
22-
default List<Path> findAssociatedFiles(BibEntry entry, List<Path> directories, List<String> extensions) {
23-
Map<BibEntry, List<Path>> associatedFiles = findAssociatedFiles(Collections.singletonList(entry), directories, extensions);
24-
return associatedFiles.getOrDefault(entry, Collections.emptyList());
25-
}
18+
List<Path> findAssociatedFiles(BibEntry entry, List<Path> directories, List<String> extensions);
2619
}

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.jabref.model.entry.BibEntry;
2828
import org.jabref.model.util.OptionalUtil;
2929

30+
import org.apache.commons.io.FilenameUtils;
3031
import org.apache.commons.logging.Log;
3132
import org.apache.commons.logging.LogFactory;
3233

@@ -65,13 +66,8 @@ public static Optional<String> getFileExtension(File file) {
6566
/**
6667
* Returns the name part of a file name (i.e., everything in front of last ".").
6768
*/
68-
public static String getFileName(String fileNameWithExtension) {
69-
int dotPosition = fileNameWithExtension.lastIndexOf('.');
70-
if (dotPosition >= 0) {
71-
return fileNameWithExtension.substring(0, dotPosition);
72-
} else {
73-
return fileNameWithExtension;
74-
}
69+
public static String getBaseName(String fileNameWithExtension) {
70+
return FilenameUtils.getBaseName(fileNameWithExtension);
7571
}
7672

7773
/**
@@ -80,7 +76,7 @@ public static String getFileName(String fileNameWithExtension) {
8076
* Currently, only the length is restricted to 255 chars, see MAXIMUM_FILE_NAME_LENGTH.
8177
*/
8278
public static String getValidFileName(String fileName) {
83-
String nameWithoutExtension = getFileName(fileName);
79+
String nameWithoutExtension = getBaseName(fileName);
8480

8581
if (nameWithoutExtension.length() > MAXIMUM_FILE_NAME_LENGTH) {
8682
Optional<String> extension = getFileExtension(fileName);

0 commit comments

Comments
 (0)