Skip to content

Commit

Permalink
Convert find unlinked files dialog to JavaFX (#4375)
Browse files Browse the repository at this point in the history
* Convert find unlinked files dialog to JavaFX

Fixes #4357.

* Fix tests

* Fix checkstyle

* Fix checkstyle

* Remove obsolete language keys
  • Loading branch information
tobiasdiez committed Oct 17, 2018
1 parent e7780ed commit 4893461
Show file tree
Hide file tree
Showing 16 changed files with 724 additions and 1,429 deletions.
1,226 changes: 0 additions & 1,226 deletions src/main/java/org/jabref/gui/FindUnlinkedFilesDialog.java

This file was deleted.

2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/JabRefFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
import org.jabref.gui.actions.DatabasePropertiesAction;
import org.jabref.gui.actions.EditExternalFileTypesAction;
import org.jabref.gui.actions.ErrorConsoleAction;
import org.jabref.gui.actions.FindUnlinkedFilesAction;
import org.jabref.gui.actions.IntegrityCheckAction;
import org.jabref.gui.actions.LookupIdentifierAction;
import org.jabref.gui.actions.ManageCustomExportsAction;
Expand All @@ -89,6 +88,7 @@
import org.jabref.gui.exporter.ExportToClipboardAction;
import org.jabref.gui.exporter.SaveAllAction;
import org.jabref.gui.exporter.SaveDatabaseAction;
import org.jabref.gui.externalfiles.FindUnlinkedFilesAction;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.help.AboutAction;
import org.jabref.gui.help.HelpAction;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.jabref.gui.actions;
package org.jabref.gui.externalfiles;

import org.jabref.gui.FindUnlinkedFilesDialog;
import org.jabref.gui.JabRefFrame;
import org.jabref.gui.actions.SimpleCommand;

public class FindUnlinkedFilesAction extends SimpleCommand {

Expand All @@ -14,7 +14,7 @@ public FindUnlinkedFilesAction(JabRefFrame jabRefFrame) {
@Override
public void execute() {
FindUnlinkedFilesDialog dlg = new FindUnlinkedFilesDialog(jabRefFrame);
dlg.setVisible(true);
dlg.showAndWait();
}

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

import java.io.File;
import java.io.FileFilter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.undo.CompoundEdit;

import org.jabref.gui.BasePanel;
Expand Down Expand Up @@ -67,22 +66,21 @@ private boolean hasSpecialisedCreatorForExternalFileType(
}

/**
* Returns a EntryFromFileCreator object that is capable of creating a
* BibEntry for the given File.
* Returns a {@link EntryFromFileCreator} object that is capable of creating a entry for the given file.
*
* @param file the pdf file
* @return null if there is no EntryFromFileCreator for this File.
* @param file the external file
* @return an empty optional if there is no EntryFromFileCreator for this file.
*/
public EntryFromFileCreator getEntryCreator(File file) {
if ((file == null) || !file.exists()) {
return null;
public Optional<EntryFromFileCreator> getEntryCreator(Path file) {
if (!Files.exists(file)) {
return Optional.empty();
}
for (EntryFromFileCreator creator : entryCreators) {
if (creator.accept(file)) {
return creator;
if (creator.accept(file.toFile())) {
return Optional.of(creator);
}
}
return null;
return Optional.empty();
}

/**
Expand All @@ -93,43 +91,32 @@ public EntryFromFileCreator getEntryCreator(File file) {
* @param entryType
* @return List of unexpected import event messages including failures.
*/
public List<String> addEntrysFromFiles(List<File> files,
BibDatabase database, EntryType entryType,
boolean generateKeywordsFromPathToFile) {
List<String> importGUIMessages = new LinkedList<>();
addEntriesFromFiles(files, database, null, entryType,
generateKeywordsFromPathToFile, null, importGUIMessages);
return importGUIMessages;
public List<String> addEntrysFromFiles(List<Path> files,
BibDatabase database, EntryType entryType,
boolean generateKeywordsFromPathToFile) {
return addEntriesFromFiles(files, database, null, entryType, generateKeywordsFromPathToFile);
}

/**
* Tries to add a entry for each file in the List.
*
* @param files
* @param database
* @param panel
* @param entryType
* @param generateKeywordsFromPathToFile
* @param changeListener
* @param importGUIMessages list of unexpected import event - Messages including
* failures
* @return Returns The number of entries added
* @return Returns a list of unexpected failures while importing
*/
public int addEntriesFromFiles(List<File> files,
BibDatabase database, BasePanel panel, EntryType entryType,
boolean generateKeywordsFromPathToFile,
ChangeListener changeListener, List<String> importGUIMessages) {
public List<String> addEntriesFromFiles(List<Path> files,
BibDatabase database, BasePanel panel, EntryType entryType,
boolean generateKeywordsFromPathToFile) {

List<String> importGUIMessages = new ArrayList<>();
int count = 0;
CompoundEdit ce = new CompoundEdit();
for (File f : files) {
EntryFromFileCreator creator = getEntryCreator(f);
if (creator == null) {
importGUIMessages.add("Problem importing " + f.getPath() + ": Unknown filetype.");
for (Path f : files) {
Optional<EntryFromFileCreator> creator = getEntryCreator(f);
if (!creator.isPresent()) {
importGUIMessages.add("Problem importing " + f.toAbsolutePath() + ": Unknown filetype.");
} else {
Optional<BibEntry> entry = creator.createEntry(f, generateKeywordsFromPathToFile);
Optional<BibEntry> entry = creator.get().createEntry(f.toFile(), generateKeywordsFromPathToFile);
if (!entry.isPresent()) {
importGUIMessages.add("Problem importing " + f.getPath() + ": Entry could not be created.");
importGUIMessages.add("Problem importing " + f.toAbsolutePath() + ": Entry could not be created.");
continue;
}
if (entryType != null) {
Expand All @@ -147,7 +134,7 @@ public int addEntriesFromFiles(List<File> files,
// Work around SIDE EFFECT of creator.createEntry. The EntryFromPDFCreator also creates the entry in the table
// Therefore, we only insert the entry if it is not already present
if (database.insertEntry(entry.get())) {
importGUIMessages.add("Problem importing " + f.getPath() + ": Insert into BibDatabase failed.");
importGUIMessages.add("Problem importing " + f.toAbsolutePath() + ": Insert into BibDatabase failed.");
} else {
count++;
if (panel != null) {
Expand All @@ -156,17 +143,13 @@ public int addEntriesFromFiles(List<File> files,
}
}
}

if (changeListener != null) {
changeListener.stateChanged(new ChangeEvent(this));
}
}

if ((count > 0) && (panel != null)) {
ce.end();
panel.getUndoManager().addEdit(ce);
}
return count;
return importGUIMessages;

}

Expand Down Expand Up @@ -211,12 +194,9 @@ public String toString() {
* @return A List of all known possible file filters.
*/
public List<FileFilter> getFileFilterList() {

List<FileFilter> filters = new ArrayList<>();
filters.add(getFileFilter());
for (FileFilter creator : entryCreators) {
filters.add(creator);
}
filters.addAll(entryCreators);
return filters;
}
}
68 changes: 36 additions & 32 deletions src/main/java/org/jabref/gui/importer/UnlinkedFilesCrawler.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,39 @@

import java.io.File;
import java.io.FileFilter;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javafx.scene.control.CheckBoxTreeItem;

import org.jabref.gui.FindUnlinkedFilesDialog.CheckableTreeNode;
import org.jabref.gui.FindUnlinkedFilesDialog.FileNodeWrapper;
import org.jabref.gui.externalfiles.FindUnlinkedFilesDialog.FileNodeWrapper;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;

/**
* Util class for searching files on the file system which are not linked to a provided {@link BibDatabase}.
*/
public class UnlinkedFilesCrawler {
/**
* File filter, that accepts directories only.
*/
private static final FileFilter DIRECTORY_FILTER = pathname -> (pathname != null) && pathname.isDirectory();
public class UnlinkedFilesCrawler extends BackgroundTask<CheckBoxTreeItem<FileNodeWrapper>> {

private final Path directory;
private final FileFilter fileFilter;
private int counter;
private final BibDatabaseContext databaseContext;


public UnlinkedFilesCrawler(BibDatabaseContext databaseContext) {
public UnlinkedFilesCrawler(Path directory, FileFilter fileFilter, BibDatabaseContext databaseContext) {
this.directory = directory;
this.fileFilter = fileFilter;
this.databaseContext = databaseContext;
}

public CheckableTreeNode searchDirectory(File directory, FileFilter filter) {
UnlinkedPDFFileFilter ff = new UnlinkedPDFFileFilter(filter, databaseContext);
return searchDirectory(directory, ff, new AtomicBoolean(true), null);
@Override
protected CheckBoxTreeItem<FileNodeWrapper> call() throws Exception {
UnlinkedPDFFileFilter unlinkedPDFFileFilter = new UnlinkedPDFFileFilter(fileFilter, databaseContext);
return searchDirectory(directory.toFile(), unlinkedPDFFileFilter);
}

/**
Expand All @@ -43,7 +44,7 @@ public CheckableTreeNode searchDirectory(File directory, FileFilter filter) {
* {@link EntryFromFileCreatorManager}, are taken into the resulting tree. <br>
* <br>
* The result will be a tree structure of nodes of the type
* {@link CheckableTreeNode}. <br>
* {@link CheckBoxTreeItem}. <br>
* <br>
* The user objects that are attached to the nodes is the
* {@link FileNodeWrapper}, which wraps the {@link File}-Object. <br>
Expand All @@ -53,11 +54,7 @@ public CheckableTreeNode searchDirectory(File directory, FileFilter filter) {
* the recursion running. When the states value changes, the method will
* resolve its recursion and return what it has saved so far.
*/
public CheckableTreeNode searchDirectory(File directory, UnlinkedPDFFileFilter ff, AtomicBoolean state, ChangeListener changeListener) {
/* Cancellation of the search from outside! */
if ((state == null) || !state.get()) {
return null;
}
private CheckBoxTreeItem<FileNodeWrapper> searchDirectory(File directory, UnlinkedPDFFileFilter ff) {
// Return null if the directory is not valid.
if ((directory == null) || !directory.exists() || !directory.isDirectory()) {
return null;
Expand All @@ -70,35 +67,42 @@ public CheckableTreeNode searchDirectory(File directory, UnlinkedPDFFileFilter f
} else {
files = Arrays.asList(filesArray);
}
CheckableTreeNode root = new CheckableTreeNode(null);
CheckBoxTreeItem<FileNodeWrapper> root = new CheckBoxTreeItem<>(new FileNodeWrapper(directory.toPath(), 0));

int filesCount = 0;

filesArray = directory.listFiles(DIRECTORY_FILTER);
filesArray = directory.listFiles(pathname -> (pathname != null) && pathname.isDirectory());
List<File> subDirectories;
if (filesArray == null) {
subDirectories = Collections.emptyList();
} else {
subDirectories = Arrays.asList(filesArray);
}
for (File subDirectory : subDirectories) {
CheckableTreeNode subRoot = searchDirectory(subDirectory, ff, state, changeListener);
if ((subRoot != null) && (subRoot.getChildCount() > 0)) {
filesCount += ((FileNodeWrapper) subRoot.getUserObject()).fileCount;
root.add(subRoot);
if (isCanceled()) {
return root;
}

CheckBoxTreeItem<FileNodeWrapper> subRoot = searchDirectory(subDirectory, ff);
if ((subRoot != null) && (!subRoot.getChildren().isEmpty())) {
filesCount += subRoot.getValue().fileCount;
root.getChildren().add(subRoot);
}
}

root.setUserObject(new FileNodeWrapper(directory, files.size() + filesCount));
root.setValue(new FileNodeWrapper(directory.toPath(), files.size() + filesCount));

for (File file : files) {
root.add(new CheckableTreeNode(new FileNodeWrapper(file)));
if (changeListener != null) {
changeListener.stateChanged(new ChangeEvent(this));
root.getChildren().add(new CheckBoxTreeItem<>(new FileNodeWrapper(file.toPath())));

counter++;
if (counter == 1) {
updateMessage(Localization.lang("One file found"));
} else {
updateMessage(Localization.lang("%0 files found", Integer.toString(counter)));
}
}

return root;
}

}
Loading

0 comments on commit 4893461

Please sign in to comment.