Skip to content
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

Rework AutoSetFileLinks #3368

Merged
merged 14 commits into from
Oct 31, 2017
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.jabref.gui.externalfiles;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.jabref.gui.externalfiletype.ExternalFileType;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.externalfiletype.UnknownExternalFileType;
import org.jabref.logic.util.io.AutoLinkPreferences;
import org.jabref.logic.util.io.FileFinder;
import org.jabref.logic.util.io.FileFinders;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.LinkedFile;
import org.jabref.model.metadata.FileDirectoryPreferences;
import org.jabref.model.util.FileHelper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class AutoSetFileLinksUtil {

private static final Log LOGGER = LogFactory.getLog(AutoSetLinks.class);

public List<LinkedFile> findassociatedNotLinkedFiles(BibEntry entry, BibDatabaseContext databaseContext, FileDirectoryPreferences fileDirPrefs, AutoLinkPreferences autoLinkPrefs, ExternalFileTypes externalFileTypes) {
List<LinkedFile> linkedFiles = new ArrayList<>();

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

// Run the search operation:
FileFinder fileFinder = FileFinders.constructFromConfiguration(autoLinkPrefs);
List<Path> result = fileFinder.findAssociatedFiles(entry, dirs, extensions);

// Iterate over the entries:

for (Path foundFile : result) {
boolean existingSameFile = entry.getFiles().stream()
.map(file -> file.findIn(dirs))
.anyMatch(file -> {
try {
return file.isPresent() && Files.isSameFile(file.get(), foundFile);
} catch (IOException e) {
LOGGER.error("Problem with isSameFile", e);
}
return false;
});
if (!existingSameFile) {

Optional<ExternalFileType> type = FileHelper.getFileExtension(foundFile)
.map(externalFileTypes::getExternalFileTypeByExt)
.orElse(Optional.of(new UnknownExternalFileType("")));

String strType = type.isPresent() ? type.get().getName() : "";

LinkedFile linkedFile = new LinkedFile("", foundFile.toString(), strType);
linkedFiles.add(linkedFile);
}
}

return linkedFiles;
}
}
153 changes: 49 additions & 104 deletions src/main/java/org/jabref/gui/externalfiles/AutoSetLinks.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,10 @@
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import javax.swing.BorderFactory;
import javax.swing.JDialog;
Expand All @@ -24,19 +18,15 @@
import org.jabref.Globals;
import org.jabref.gui.externalfiletype.ExternalFileType;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.externalfiletype.UnknownExternalFileType;
import org.jabref.gui.filelist.FileListEntry;
import org.jabref.gui.filelist.FileListTableModel;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableFieldChange;
import org.jabref.gui.util.DefaultTaskExecutor;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.io.FileFinder;
import org.jabref.logic.util.io.FileFinders;
import org.jabref.logic.util.io.FileUtil;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.FieldName;
import org.jabref.model.util.FileHelper;
import org.jabref.model.entry.FileFieldWriter;
import org.jabref.model.entry.LinkedFile;

public class AutoSetLinks {

Expand All @@ -50,7 +40,7 @@ private AutoSetLinks() {
* @param databaseContext the database for which links are set
*/
public static void autoSetLinks(List<BibEntry> entries, BibDatabaseContext databaseContext) {
autoSetLinks(entries, null, null, null, databaseContext, null, null);
autoSetLinks(entries, null, null, databaseContext, null, null);
}

/**
Expand All @@ -64,12 +54,7 @@ public static void autoSetLinks(List<BibEntry> entries, BibDatabaseContext datab
* @param entries A collection of BibEntry objects to find links for.
* @param ce A NamedCompound to add UndoEdit elements to.
* @param changedEntries MODIFIED, optional. A Set of BibEntry objects to which all modified entries is added.
* This is used for status output and debugging
* @param singleTableModel UGLY HACK. The table model to insert links into. Already existing links are not
* duplicated or removed. This parameter has to be null if entries.count() != 1. The hack has been
* introduced as a bibtexentry does not (yet) support the function getListTableModel() and the
* FileListEntryEditor editor holds an instance of that table model and does not reconstruct it after the
* search has succeeded.

* @param databaseContext The database providing the relevant file directory, if any.
* @param callback An ActionListener that is notified (on the event dispatch thread) when the search is finished.
* The ActionEvent has id=0 if no new links were added, and id=1 if one or more links were added. This
Expand All @@ -79,7 +64,7 @@ public static void autoSetLinks(List<BibEntry> entries, BibDatabaseContext datab
* @return the thread performing the automatically setting
*/
public static Runnable autoSetLinks(final List<BibEntry> entries, final NamedCompound ce,
final Set<BibEntry> changedEntries, final FileListTableModel singleTableModel,
final Set<BibEntry> changedEntries,
final BibDatabaseContext databaseContext, final ActionListener callback, final JDialog diag) {
final Collection<ExternalFileType> types = ExternalFileTypes.getInstance().getExternalFileTypeSelection();
if (diag != null) {
Expand All @@ -95,97 +80,58 @@ public static Runnable autoSetLinks(final List<BibEntry> entries, final NamedCom
diag.setLocationRelativeTo(diag.getParent());
}

Runnable r = new Runnable() {

@Override
public void run() {
// determine directories to search in
final List<Path> dirs = databaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences());

// determine extensions
final List<String> extensions = types.stream().map(ExternalFileType::getExtension).collect(Collectors.toList());

// Run the search operation:
FileFinder fileFinder = FileFinders.constructFromConfiguration(Globals.prefs.getAutoLinkPreferences());
Map<BibEntry, List<Path>> result = fileFinder.findAssociatedFiles(entries, dirs, extensions);

boolean foundAny = false;
// Iterate over the entries:
for (Entry<BibEntry, List<Path>> entryFilePair : result.entrySet()) {
FileListTableModel tableModel;
Optional<String> oldVal = entryFilePair.getKey().getField(FieldName.FILE);
if (singleTableModel == null) {
tableModel = new FileListTableModel();
oldVal.ifPresent(tableModel::setContent);
} else {
assert entries.size() == 1;
tableModel = singleTableModel;
Runnable r = () -> {
boolean foundAny = false;
AutoSetFileLinksUtil util = new AutoSetFileLinksUtil();

for (BibEntry entry : entries) {

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

if (ce != null) {
for (LinkedFile linkedFile : linkedFiles) {
// store undo information
String newVal = FileFieldWriter.getStringRepresentation(linkedFile);

String oldVal = entry.getField(FieldName.FILE).orElse(null);

UndoableFieldChange fieldChange = new UndoableFieldChange(entry, FieldName.FILE, oldVal, newVal);
ce.addEdit(fieldChange);

DefaultTaskExecutor.runInJavaFXThread(() -> {
entry.addFile(linkedFile);
});
foundAny = true;
}
List<Path> files = entryFilePair.getValue();
for (Path file : files) {
file = FileUtil.shortenFileName(file, dirs);
boolean alreadyHas = false;

for (int j = 0; j < tableModel.getRowCount(); j++) {
FileListEntry existingEntry = tableModel.getEntry(j);
if (Paths.get(existingEntry.getLink()).equals(file)) {
alreadyHas = true;
foundAny = true;
break;
}
}
if (!alreadyHas) {
foundAny = true;
Optional<ExternalFileType> type = FileHelper.getFileExtension(file)
.map(extension -> ExternalFileTypes.getInstance().getExternalFileTypeByExt(extension))
.orElse(Optional.of(new UnknownExternalFileType("")));
FileListEntry flEntry = new FileListEntry("", file.toString(), type);
tableModel.addEntry(tableModel.getRowCount(), flEntry);

String newVal = tableModel.getStringRepresentation();
if (newVal.isEmpty()) {
newVal = null;
}
if (ce != null) {
// store undo information
UndoableFieldChange change = new UndoableFieldChange(entryFilePair.getKey(),
FieldName.FILE, oldVal.orElse(null), newVal);
ce.addEdit(change);
}
// hack: if table model is given, do NOT modify entry
if (singleTableModel == null) {
entryFilePair.getKey().setField(FieldName.FILE, newVal);
}
if (changedEntries != null) {
changedEntries.add(entryFilePair.getKey());
}
}

if (changedEntries != null) {
changedEntries.add(entry);
}
}

// handle callbacks and dialog
// FIXME: The ID signals if action was successful :/
final int id = foundAny ? 1 : 0;
SwingUtilities.invokeLater(new Runnable() {

@Override
public void run() {
if (diag != null) {
diag.dispose();
}
if (callback != null) {
callback.actionPerformed(new ActionEvent(this, id, ""));
}
}
});
}

final int id = foundAny ? 1 : 0;
SwingUtilities.invokeLater(() -> {

if (diag != null) {
diag.dispose();
}
if (callback != null) {
callback.actionPerformed(new ActionEvent(AutoSetLinks.class, id, ""));
}

});

};

SwingUtilities.invokeLater(() -> {
// show dialog which will be hidden when the task is done
if (diag != null) {
diag.setVisible(true);
}
});

return r;
}

Expand All @@ -194,8 +140,7 @@ public void run() {
* of external file types. The entry itself is not modified. The entry's bibtex key must have been set.
*
* @param entry The BibEntry to find links for.
* @param singleTableModel The table model to insert links into. Already existing links are not duplicated or
* removed.

* @param databaseContext The database providing the relevant file directory, if any.
* @param callback An ActionListener that is notified (on the event dispatch thread) when the search is finished.
* The ActionEvent has id=0 if no new links were added, and id=1 if one or more links were added. This
Expand All @@ -206,9 +151,9 @@ public void run() {
* parameter can be null, which means that no progress update will be shown.
* @return the runnable able to perform the automatically setting
*/
public static Runnable autoSetLinks(final BibEntry entry, final FileListTableModel singleTableModel,
public static Runnable autoSetLinks(final BibEntry entry,
final BibDatabaseContext databaseContext, final ActionListener callback, final JDialog diag) {
return autoSetLinks(Collections.singletonList(entry), null, null, singleTableModel, databaseContext, callback,
return autoSetLinks(Collections.singletonList(entry), null, null, databaseContext, callback,
diag);
}

Expand Down
25 changes: 15 additions & 10 deletions src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -14,6 +15,7 @@
import org.jabref.Globals;
import org.jabref.gui.BasePanel;
import org.jabref.gui.undo.UndoableFieldChange;
import org.jabref.gui.util.DefaultTaskExecutor;
import org.jabref.gui.worker.AbstractWorker;
import org.jabref.logic.importer.FulltextFetchers;
import org.jabref.logic.l10n.Localization;
Expand Down Expand Up @@ -81,9 +83,9 @@ public void update() {
BibEntry entry = download.getValue();
Optional<URL> result = download.getKey();
if (result.isPresent()) {
List<String> dirs = basePanel.getBibDatabaseContext()
.getFileDirectories(Globals.prefs.getFileDirectoryPreferences());
if (dirs.isEmpty()) {
Optional<Path> dir = basePanel.getBibDatabaseContext().getFirstExistingFileDir(Globals.prefs.getFileDirectoryPreferences());

if (!dir.isPresent()) {
JOptionPane.showMessageDialog(basePanel.frame(),
Localization.lang("Main file directory not set!") + " " + Localization.lang("Preferences")
+ " -> " + Localization.lang("File"),
Expand All @@ -94,13 +96,16 @@ public void update() {
basePanel.getBibDatabaseContext(), entry);
try {
def.download(result.get(), file -> {
Optional<FieldChange> fieldChange = entry.addFile(file);
if (fieldChange.isPresent()) {
UndoableFieldChange edit = new UndoableFieldChange(entry, FieldName.FILE,
entry.getField(FieldName.FILE).orElse(null), fieldChange.get().getNewValue());
basePanel.getUndoManager().addEdit(edit);
basePanel.markBaseChanged();
}
DefaultTaskExecutor.runInJavaFXThread(() -> {
Optional<FieldChange> fieldChange = entry.addFile(file);
if (fieldChange.isPresent()) {
UndoableFieldChange edit = new UndoableFieldChange(entry, FieldName.FILE,
entry.getField(FieldName.FILE).orElse(null), fieldChange.get().getNewValue());
basePanel.getUndoManager().addEdit(edit);
basePanel.markBaseChanged();
}
});

});
} catch (IOException e) {
LOGGER.warn("Problem downloading file", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public void run() {
List<BibEntry> entries = new ArrayList<>(sel);

// Start the automatically setting process:
Runnable r = AutoSetLinks.autoSetLinks(entries, ce, changedEntries, null, panel.getBibDatabaseContext(), null, null);
Runnable r = AutoSetLinks.autoSetLinks(entries, ce, changedEntries, panel.getBibDatabaseContext(), null, null);
JabRefExecutorService.INSTANCE.executeAndWait(r);
}
progress += sel.size() * weightAutoSet;
Expand Down
Loading