From 8656af9508693850c73c78071f49bdd31a164c80 Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Wed, 2 Nov 2016 00:07:08 +0800 Subject: [PATCH] Refactor Filter / Tools --- .../net/filebot/ui/filter/AttributeTool.java | 9 ++- source/net/filebot/ui/filter/ExtractTool.java | 56 +++++++---------- source/net/filebot/ui/filter/FileTree.java | 34 +++++++--- .../net/filebot/ui/filter/FileTreePanel.java | 4 +- .../ui/filter/FileTreeTransferablePolicy.java | 36 ++++------- source/net/filebot/ui/filter/FilterPanel.java | 4 +- .../net/filebot/ui/filter/MediaInfoTool.java | 13 ++-- source/net/filebot/ui/filter/SplitTool.java | 33 +++++----- source/net/filebot/ui/filter/Tool.java | 63 +++++++++---------- source/net/filebot/ui/filter/TypeTool.java | 23 +++++-- 10 files changed, 144 insertions(+), 131 deletions(-) diff --git a/source/net/filebot/ui/filter/AttributeTool.java b/source/net/filebot/ui/filter/AttributeTool.java index f96a2b47..8da3c24d 100644 --- a/source/net/filebot/ui/filter/AttributeTool.java +++ b/source/net/filebot/ui/filter/AttributeTool.java @@ -8,6 +8,7 @@ import java.awt.Color; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CancellationException; import javax.swing.BorderFactory; import javax.swing.JScrollPane; @@ -48,10 +49,10 @@ class AttributeTool extends Tool { } @Override - protected TableModel createModelInBackground(File root) { + protected TableModel createModelInBackground(List root) { FileAttributesTableModel model = new FileAttributesTableModel(); - if (root == null) { + if (root.isEmpty()) { return model; } @@ -74,6 +75,10 @@ class AttributeTool extends Tool { model.addRow(String.format("%s::%d", "OMDb", movie.getImdbId()), metaObject, originalName, file); } } + + if (Thread.interrupted()) { + throw new CancellationException(); + } } return model; diff --git a/source/net/filebot/ui/filter/ExtractTool.java b/source/net/filebot/ui/filter/ExtractTool.java index 1472c0b9..063ffdd4 100644 --- a/source/net/filebot/ui/filter/ExtractTool.java +++ b/source/net/filebot/ui/filter/ExtractTool.java @@ -2,11 +2,10 @@ package net.filebot.ui.filter; import static net.filebot.Logging.*; import static net.filebot.UserFiles.*; -import static net.filebot.util.ExceptionUtilities.*; import static net.filebot.util.FileUtilities.*; +import static net.filebot.util.ui.SwingUI.*; import java.awt.Color; -import java.awt.event.ActionEvent; import java.io.File; import java.io.FileFilter; import java.util.ArrayList; @@ -21,7 +20,6 @@ import java.util.function.Consumer; import java.util.function.Supplier; import java.util.logging.Level; -import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; import javax.swing.JScrollPane; @@ -76,8 +74,8 @@ class ExtractTool extends Tool { } @Override - protected TableModel createModelInBackground(File root) throws InterruptedException { - if (root == null) { + protected TableModel createModelInBackground(List root) throws Exception { + if (root.isEmpty()) { return new ArchiveEntryModel(); } @@ -85,46 +83,34 @@ class ExtractTool extends Tool { List files = listFiles(root, Archive.VOLUME_ONE_FILTER, HUMAN_NAME_ORDER); List entries = new ArrayList(); - try { - for (File file : files) { - try (Archive archive = Archive.open(file)) { - for (FileInfo it : archive.listFiles()) { - entries.add(new ArchiveEntry(file, it)); - } + for (File file : files) { + try (Archive archive = Archive.open(file)) { + for (FileInfo it : archive.listFiles()) { + entries.add(new ArchiveEntry(file, it)); } + } - // unwind thread, if we have been cancelled - if (Thread.interrupted()) { - throw new InterruptedException(); - } - } - } catch (Exception e) { // unwind thread, if we have been cancelled - if (findCause(e, InterruptedException.class) != null) { - throw findCause(e, InterruptedException.class); + if (Thread.interrupted()) { + throw new CancellationException(); } - log.log(Level.WARNING, e.getMessage(), e); } return new ArchiveEntryModel(entries); } - private Action extractAction = new AbstractAction("Extract All", ResourceManager.getIcon("package.extract")) { + private Action extractAction = newAction("Extract All", ResourceManager.getIcon("package.extract"), evt -> { + List archives = ((ArchiveEntryModel) table.getModel()).getArchiveList(); + if (archives.isEmpty()) + return; - @Override - public void actionPerformed(ActionEvent evt) { - final List archives = ((ArchiveEntryModel) table.getModel()).getArchiveList(); - if (archives.isEmpty()) - return; + File selectedFile = showOpenDialogSelectFolder(archives.get(0).getParentFile(), "Extract to ...", evt); + if (selectedFile == null) + return; - File selectedFile = showOpenDialogSelectFolder(archives.get(0).getParentFile(), "Extract to ...", evt); - if (selectedFile == null) - return; - - ExtractWorker worker = new ExtractWorker(archives, selectedFile, null, true, ConflictAction.AUTO); - ProgressMonitor.runTask("Extract", "Extracting files...", worker); - } - }; + ExtractWorker worker = new ExtractWorker(archives, selectedFile, null, true, ConflictAction.AUTO); + ProgressMonitor.runTask("Extract", "Extracting files...", worker); + }); private static class ArchiveEntry { @@ -199,7 +185,7 @@ class ExtractTool extends Tool { } - protected static class ExtractWorker implements ProgressWorker { + private static class ExtractWorker implements ProgressWorker { private final File[] archives; private final File outputFolder; diff --git a/source/net/filebot/ui/filter/FileTree.java b/source/net/filebot/ui/filter/FileTree.java index dd375143..57cc6c4d 100644 --- a/source/net/filebot/ui/filter/FileTree.java +++ b/source/net/filebot/ui/filter/FileTree.java @@ -8,7 +8,6 @@ import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; -import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; @@ -54,8 +53,20 @@ public class FileTree extends JTree { return (DefaultTreeModel) super.getModel(); } - public FolderNode getRoot() { - return (FolderNode) getModel().getRoot(); + public List getRoot() { + FolderNode model = (FolderNode) getModel().getRoot(); + + return model.getChildren().stream().map(node -> { + if (node instanceof FolderNode) { + FolderNode folder = (FolderNode) node; + return folder.getFile(); + } + if (node instanceof FileNode) { + FileNode file = (FileNode) node; + return file.getFile(); + } + return null; + }).collect(toList()); } public void clear() { @@ -258,15 +269,16 @@ public class FileTree extends JTree { private final String title; private final List children; - /** - * Creates a root node (no parent, no title, empty list of children) - */ public FolderNode() { - this(null, "", new ArrayList(0)); + this(emptyList()); // empty root node + } + + public FolderNode(List children) { + this(null, "/", children); // root node } public FolderNode(String title, List children) { - this(null, title, children); + this(null, title, children); // virtual node } public FolderNode(File file, String title, List children) { @@ -318,8 +330,9 @@ public class FileTree extends JTree { @Override protected Iterator children(TreeNode node) { - if (node instanceof FolderNode) + if (node instanceof FolderNode) { return ((FolderNode) node).getChildren().iterator(); + } // can't step into non-folder nodes return null; @@ -333,8 +346,9 @@ public class FileTree extends JTree { @Override protected File filter(TreeNode node) { - if (node instanceof FileNode) + if (node instanceof FileNode) { return ((FileNode) node).getFile(); + } // filter out non-file nodes return null; diff --git a/source/net/filebot/ui/filter/FileTreePanel.java b/source/net/filebot/ui/filter/FileTreePanel.java index 757eec37..f5b93234 100644 --- a/source/net/filebot/ui/filter/FileTreePanel.java +++ b/source/net/filebot/ui/filter/FileTreePanel.java @@ -63,8 +63,10 @@ class FileTreePanel extends JComponent { fireFileTreeChange(); }); + public static final String FILE_TREE_PROPERTY = "FILE_TREE"; + private void fireFileTreeChange() { - firePropertyChange("filetree", null, fileTree); + firePropertyChange(FILE_TREE_PROPERTY, null, fileTree); } } diff --git a/source/net/filebot/ui/filter/FileTreeTransferablePolicy.java b/source/net/filebot/ui/filter/FileTreeTransferablePolicy.java index a37f3c15..fbe4dc39 100644 --- a/source/net/filebot/ui/filter/FileTreeTransferablePolicy.java +++ b/source/net/filebot/ui/filter/FileTreeTransferablePolicy.java @@ -2,14 +2,15 @@ package net.filebot.ui.filter; import static net.filebot.Logging.*; import static net.filebot.Settings.*; +import static net.filebot.util.ExceptionUtilities.*; import static net.filebot.util.FileUtilities.*; import static net.filebot.util.ui.SwingUI.*; import java.io.File; import java.util.ArrayList; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.CancellationException; import java.util.logging.Level; import javax.swing.tree.TreeNode; @@ -18,7 +19,6 @@ import net.filebot.mac.MacAppUtilities; import net.filebot.ui.filter.FileTree.FileNode; import net.filebot.ui.filter.FileTree.FolderNode; import net.filebot.ui.transfer.BackgroundFileTransferablePolicy; -import net.filebot.util.ExceptionUtilities; import net.filebot.util.FastFile; class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy { @@ -42,46 +42,36 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy root) { - tree.getModel().setRoot(root.get(0)); + tree.getModel().setRoot(new FolderNode(root)); tree.getModel().reload(); } @Override protected void process(Exception e) { - log.log(Level.WARNING, ExceptionUtilities.getRootCauseMessage(e), e); - } - - @Override - protected void handleInBackground(List files, TransferAction action) { - super.handleInBackground(files, action); + log.log(Level.WARNING, getRootCauseMessage(e), e); } @Override protected void load(List files, TransferAction action) { + // make sure we have access to the parent folder structure, not just the dropped file + if (isMacSandbox()) { + MacAppUtilities.askUnlockFolders(getWindow(tree), files); + } + try { - if (files.size() > 1 || containsOnly(files, FILES)) { - files = Arrays.asList(files.get(0).getParentFile()); - } - - // make sure we have access to the parent folder structure, not just the dropped file - if (isMacSandbox()) { - MacAppUtilities.askUnlockFolders(getWindow(tree), files); - } - // use fast file to minimize system calls like length(), isDirectory(), isFile(), ... - FastFile root = new FastFile(filter(files, FOLDERS).get(0)); + TreeNode[] node = files.stream().map(FastFile::new).map(this::getTreeNode).toArray(TreeNode[]::new); // publish on EDT - TreeNode[] node = { getTreeNode(root) }; publish(node); - } catch (InterruptedException e) { + } catch (CancellationException e) { // supposed to happen if background execution was aborted } } - private TreeNode getTreeNode(File file) throws InterruptedException { + private TreeNode getTreeNode(File file) { if (Thread.interrupted()) { - throw new InterruptedException(); + throw new CancellationException(); } if (file.isDirectory()) { diff --git a/source/net/filebot/ui/filter/FilterPanel.java b/source/net/filebot/ui/filter/FilterPanel.java index 40d0926e..06243c52 100644 --- a/source/net/filebot/ui/filter/FilterPanel.java +++ b/source/net/filebot/ui/filter/FilterPanel.java @@ -21,11 +21,11 @@ public class FilterPanel extends JComponent { add(fileTreePanel, "grow 1, w 300:pref:500"); add(toolsPanel, "grow 2"); - fileTreePanel.addPropertyChangeListener("filetree", evt -> { + fileTreePanel.addPropertyChangeListener(FileTreePanel.FILE_TREE_PROPERTY, evt -> { // stopped loading, refresh tools for (int i = 0; i < toolsPanel.getTabCount(); i++) { Tool tool = (Tool) toolsPanel.getComponentAt(i); - tool.updateRoot(fileTreePanel.getFileTree().getRoot().getFile()); + tool.setRoot(fileTreePanel.getFileTree().getRoot()); } }); } diff --git a/source/net/filebot/ui/filter/MediaInfoTool.java b/source/net/filebot/ui/filter/MediaInfoTool.java index e30cea1d..328b7c7d 100644 --- a/source/net/filebot/ui/filter/MediaInfoTool.java +++ b/source/net/filebot/ui/filter/MediaInfoTool.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.TreeMap; +import java.util.concurrent.CancellationException; import java.util.regex.Pattern; import java.util.stream.IntStream; @@ -53,8 +54,8 @@ class MediaInfoTool extends Tool { } @Override - protected TableModel createModelInBackground(File root) { - if (root == null) { + protected TableModel createModelInBackground(List root) { + if (root.isEmpty()) { return new MediaInfoTableModel(); } @@ -74,9 +75,13 @@ class MediaInfoTool extends Tool { }); }); } catch (IllegalArgumentException e) { - debug.finest(e.getMessage()); + debug.finest(e::toString); } catch (Exception e) { - debug.warning(e.getMessage()); + debug.warning(e::toString); + } + + if (Thread.interrupted()) { + throw new CancellationException(); } }); } diff --git a/source/net/filebot/ui/filter/SplitTool.java b/source/net/filebot/ui/filter/SplitTool.java index cb956ea6..0156b04f 100644 --- a/source/net/filebot/ui/filter/SplitTool.java +++ b/source/net/filebot/ui/filter/SplitTool.java @@ -7,13 +7,12 @@ import java.awt.Color; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CancellationException; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; import javax.swing.tree.TreeNode; @@ -51,14 +50,11 @@ class SplitTool extends Tool { tree.setTransferHandler(new DefaultTransferHandler(null, new FileTreeExportHandler())); tree.setDragEnabled(true); - spinnerModel.addChangeListener(new ChangeListener() { - - @Override - public void stateChanged(ChangeEvent evt) { - // update model in foreground, will be much faster than the initial load because length() is cached now - if (getRoot() != null) { - updateRoot(getRoot()); - } + // update model in foreground, will be much faster than the initial load because length() is cached now + spinnerModel.addChangeListener(evt -> { + List root = getRoot(); + if (root.size() > 0) { + setRoot(root); } }); } @@ -68,8 +64,8 @@ class SplitTool extends Tool { } @Override - protected TreeModel createModelInBackground(File root) throws InterruptedException { - if (root == null) { + protected TreeModel createModelInBackground(List root) { + if (root.isEmpty()) { return new DefaultTreeModel(new FolderNode("Volumes", emptyList())); } @@ -93,7 +89,7 @@ class SplitTool extends Tool { if (totalSize + fileSize > splitSize) { // part is full, add node and start with next one - rootGroup.add(createStatisticsNode(String.format("Disk %d", nextPart++), currentPart)); + rootGroup.add(createStatisticsNode(nextPart++, currentPart)); // reset total size and file list totalSize = 0; @@ -102,11 +98,15 @@ class SplitTool extends Tool { totalSize += fileSize; currentPart.add(f); + + if (Thread.interrupted()) { + throw new CancellationException(); + } } if (!currentPart.isEmpty()) { // add last part - rootGroup.add(createStatisticsNode(String.format("Disk %d", nextPart++), currentPart)); + rootGroup.add(createStatisticsNode(nextPart++, currentPart)); } if (!remainder.isEmpty()) { @@ -121,4 +121,9 @@ class SplitTool extends Tool { tree.setModel(model); } + protected FolderNode createStatisticsNode(int disk, List files) { + System.out.println(files); + return createStatisticsNode(String.format("Disk %,d", disk), files); + } + } diff --git a/source/net/filebot/ui/filter/Tool.java b/source/net/filebot/ui/filter/Tool.java index 2a5882ad..d1eee5c0 100644 --- a/source/net/filebot/ui/filter/Tool.java +++ b/source/net/filebot/ui/filter/Tool.java @@ -1,60 +1,66 @@ package net.filebot.ui.filter; +import static java.util.Collections.*; +import static java.util.stream.Collectors.*; import static net.filebot.Logging.*; +import static net.filebot.util.ExceptionUtilities.*; +import static net.filebot.util.FileUtilities.*; import java.io.File; -import java.util.ArrayList; import java.util.Collection; -import java.util.ConcurrentModificationException; import java.util.List; +import java.util.concurrent.CancellationException; import java.util.logging.Level; import javax.swing.JComponent; import javax.swing.SwingWorker; import javax.swing.tree.TreeNode; -import org.apache.commons.io.FileUtils; - import net.filebot.ui.filter.FileTree.FileNode; import net.filebot.ui.filter.FileTree.FolderNode; -import net.filebot.util.ExceptionUtilities; import net.filebot.util.FileUtilities; import net.filebot.util.ui.LoadingOverlayPane; abstract class Tool extends JComponent { - private UpdateModelTask updateTask = null; - private File root = null; + private List root = emptyList(); + + private UpdateModelTask updateTask; public Tool(String name) { setName(name); } - public File getRoot() { + public List getRoot() { return root; } - public void updateRoot(File root) { + public void setRoot(List root) { this.root = root; if (updateTask != null) { updateTask.cancel(true); } - Tool.this.firePropertyChange(LoadingOverlayPane.LOADING_PROPERTY, false, true); + setLoading(true); + updateTask = new UpdateModelTask(root); updateTask.execute(); } - protected abstract M createModelInBackground(File root) throws InterruptedException; + protected void setLoading(boolean loading) { + firePropertyChange(LoadingOverlayPane.LOADING_PROPERTY, !loading, loading); + } + + protected abstract M createModelInBackground(List root) throws Exception; protected abstract void setModel(M model); private class UpdateModelTask extends SwingWorker { - private final File root; + private final List root; - public UpdateModelTask(File root) { + public UpdateModelTask(List root) { this.root = root; } @@ -66,7 +72,7 @@ abstract class Tool extends JComponent { @Override protected void done() { if (this == updateTask) { - Tool.this.firePropertyChange(LoadingOverlayPane.LOADING_PROPERTY, true, false); + setLoading(false); } // update task will only be cancelled if a newer update task has been started @@ -74,14 +80,12 @@ abstract class Tool extends JComponent { try { setModel(get()); } catch (Exception e) { - Throwable cause = ExceptionUtilities.getRootCause(e); + Throwable cause = getRootCause(e); - if (cause instanceof ConcurrentModificationException || cause instanceof InterruptedException) { - // if it happens, it is supposed to - debug.log(Level.FINEST, e.getMessage(), e); + if (cause instanceof InterruptedException || cause instanceof CancellationException) { + debug.log(Level.FINEST, e, e::toString); // if it happens, it is supposed to } else { - // should not happen - debug.log(Level.WARNING, e.getMessage(), e); + debug.log(Level.WARNING, e, e::toString); // should not happen } } } @@ -89,26 +93,17 @@ abstract class Tool extends JComponent { } protected List createFileNodes(Collection files) { - List nodes = new ArrayList(files.size()); - for (File f : files) { - nodes.add(new FileNode(f)); - } - return nodes; + return files.stream().map(FileNode::new).collect(toList()); } protected FolderNode createStatisticsNode(String name, List files) { - long totalCount = 0; - long totalSize = 0; - - for (File f : files) { - totalCount += FileUtilities.listFiles(f, FileUtilities.FILES).size(); - totalSize += FileUtils.sizeOf(f); - } + List selection = listFiles(files, FILES, null); + long size = selection.stream().mapToLong(File::length).sum(); // set node text (e.g. txt (1 file, 42 Byte)) - String title = String.format("%s (%,d %s, %s)", name, totalCount, totalCount == 1 ? "file" : "files", FileUtilities.formatSize(totalSize)); + String title = String.format("%s (%,d %s, %s)", name, selection.size(), selection.size() == 1 ? "file" : "files", FileUtilities.formatSize(size)); - return new FolderNode(null, title, createFileNodes(files)); + return new FolderNode(title, createFileNodes(files)); } } diff --git a/source/net/filebot/ui/filter/TypeTool.java b/source/net/filebot/ui/filter/TypeTool.java index 28c76b3f..b32fd8ef 100644 --- a/source/net/filebot/ui/filter/TypeTool.java +++ b/source/net/filebot/ui/filter/TypeTool.java @@ -10,6 +10,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.CancellationException; import java.util.SortedMap; import java.util.TreeMap; @@ -44,12 +45,13 @@ class TypeTool extends Tool { } @Override - protected TreeModel createModelInBackground(File root) throws InterruptedException { - if (root == null) { + protected TreeModel createModelInBackground(List root) { + if (root.isEmpty()) { return new DefaultTreeModel(new FolderNode("Types", emptyList())); } List filesAndFolders = listFiles(root, NOT_HIDDEN, HUMAN_NAME_ORDER); + List groups = new ArrayList(); for (Entry it : getMetaTypes().entrySet()) { @@ -57,15 +59,24 @@ class TypeTool extends Tool { if (selection.size() > 0) { groups.add(createStatisticsNode(it.getKey(), selection)); } + + if (Thread.interrupted()) { + throw new CancellationException(); + } } SortedMap extensionGroups = new TreeMap(String.CASE_INSENSITIVE_ORDER); - for (Entry> it : mapByExtension(filter(filesAndFolders, FILES)).entrySet()) { - if (it.getKey() == null) - continue; - extensionGroups.put(it.getKey(), createStatisticsNode(it.getKey(), it.getValue())); + for (Entry> it : mapByExtension(filter(filesAndFolders, FILES)).entrySet()) { + if (it.getKey() != null) { + extensionGroups.put(it.getKey(), createStatisticsNode(it.getKey(), it.getValue())); + } + + if (Thread.interrupted()) { + throw new CancellationException(); + } } + groups.addAll(extensionGroups.values()); // create tree model