improvement:

* BackgroundFileTransferablePolicy uses ThreadLocal<Worker> now
* support exception handling in BackgroundFileTransferablePolicy
changes:
* ChecksumComputationTask will only calculate one HashType
* added ChecksumRow.dispose()
fix:
* honor convertValueToString() in SelectDialog
This commit is contained in:
Reinhard Pointner 2009-02-10 19:37:32 +00:00
parent 5674173417
commit dac55956f6
14 changed files with 237 additions and 171 deletions

View File

@ -44,10 +44,7 @@ public class SelectDialog<T> extends JDialog {
list = new JList(new ArrayListModel(options));
list.setSelectedIndex(0);
DefaultFancyListCellRenderer cellRenderer = new DefaultFancyListCellRenderer(4);
cellRenderer.setHighlightingEnabled(false);
list.setCellRenderer(cellRenderer);
list.setCellRenderer(new SelectListCellRenderer());
list.addMouseListener(mouseListener);
JComponent c = (JComponent) getContentPane();
@ -121,4 +118,20 @@ public class SelectDialog<T> extends JDialog {
}
}
};
protected class SelectListCellRenderer extends DefaultFancyListCellRenderer {
public SelectListCellRenderer() {
super(4);
setHighlightingEnabled(false);
}
@Override
public void configureListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.configureListCellRendererComponent(list, convertValueToString(value), index, isSelected, cellHasFocus);
}
};
}

View File

@ -4,11 +4,13 @@ package net.sourceforge.filebot.ui.panel.analyze;
import java.io.File;
import java.util.List;
import java.util.logging.Logger;
import net.sourceforge.filebot.ui.panel.analyze.FileTree.AbstractTreeNode;
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FileNode;
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FolderNode;
import net.sourceforge.filebot.ui.transfer.BackgroundFileTransferablePolicy;
import net.sourceforge.tuned.ExceptionUtilities;
import net.sourceforge.tuned.FileUtilities;
@ -46,6 +48,12 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<Abstra
}
@Override
protected void process(Exception e) {
Logger.getLogger("ui").warning(ExceptionUtilities.getRootCause(e).getMessage());
}
@Override
protected void load(List<File> files) {
try {

View File

@ -10,7 +10,6 @@ import java.awt.event.ActionEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
@ -215,7 +214,7 @@ public class RenamePanel extends FileBotPanel {
model.names().addAll(names);
model.files().addAll(files);
} catch (Exception e) {
Logger.getLogger("ui").log(Level.WARNING, ExceptionUtilities.getRootCause(e).getMessage(), e);
Logger.getLogger("ui").warning(ExceptionUtilities.getRootCause(e).getMessage());
}
}
};

View File

@ -49,7 +49,7 @@ public class ChecksumCell {
public void propertyChange(PropertyChangeEvent evt) {
super.propertyChange(evt);
propertyChangeSupport.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
pcs.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
}
@ -95,17 +95,6 @@ public class ChecksumCell {
}
public void dispose() {
if (task != null) {
task.cancel(true);
}
hashes = null;
error = null;
task = null;
}
public State getState() {
if (hashes != null)
return State.READY;
@ -121,21 +110,38 @@ public class ChecksumCell {
}
public void dispose() {
// clear property change support first
for (PropertyChangeListener listener : pcs.getPropertyChangeListeners()) {
pcs.removePropertyChangeListener(listener);
}
if (task != null) {
task.cancel(true);
}
hashes = null;
error = null;
task = null;
pcs = null;
}
@Override
public String toString() {
return String.format("%s %s", name, hashes);
}
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
pcs.removePropertyChangeListener(listener);
}
}

View File

@ -5,9 +5,8 @@ package net.sourceforge.filebot.ui.panel.sfv;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.EnumMap;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.SwingWorker;
@ -16,21 +15,18 @@ class ChecksumComputationTask extends SwingWorker<Map<HashType, String>, Void> {
private static final int BUFFER_SIZE = 32 * 1024;
private final File file;
private final HashType type;
public ChecksumComputationTask(File file) {
public ChecksumComputationTask(File file, HashType type) {
this.file = file;
this.type = type;
}
@Override
protected Map<HashType, String> doInBackground() throws Exception {
Map<HashType, Hash> hashes = new EnumMap<HashType, Hash>(HashType.class);
for (HashType type : HashType.values()) {
hashes.put(type, type.newInstance());
}
Hash hash = type.newInstance();
long length = file.length();
if (length > 0) {
@ -38,22 +34,20 @@ class ChecksumComputationTask extends SwingWorker<Map<HashType, String>, Void> {
try {
byte[] buffer = new byte[BUFFER_SIZE];
long position = 0;
int len = 0;
while ((len = in.read(buffer)) >= 0) {
position += len;
for (Hash hash : hashes.values()) {
hash.update(buffer, 0, len);
}
hash.update(buffer, 0, len);
// update progress
setProgress((int) ((position * 100) / length));
// check abort status
if (isCancelled() || Thread.interrupted()) {
if (isCancelled()) {
break;
}
}
@ -62,18 +56,7 @@ class ChecksumComputationTask extends SwingWorker<Map<HashType, String>, Void> {
}
}
return digest(hashes);
}
private Map<HashType, String> digest(Map<HashType, Hash> hashes) {
Map<HashType, String> results = new EnumMap<HashType, String>(HashType.class);
for (Entry<HashType, Hash> entry : hashes.entrySet()) {
results.put(entry.getKey(), entry.getValue().digest());
}
return results;
return Collections.singletonMap(type, hash.digest());
}
}

View File

@ -2,6 +2,8 @@
package net.sourceforge.filebot.ui.panel.sfv;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
@ -56,30 +58,43 @@ public class ChecksumRow {
}
public Collection<ChecksumCell> values() {
return Collections.unmodifiableCollection(hashes.values());
}
public void add(ChecksumCell entry) {
hashes.put(entry.getRoot(), entry);
public void put(ChecksumCell cell) {
ChecksumCell old = hashes.put(cell.getRoot(), cell);
// dispose of old map entry
if (old != null) {
old.dispose();
}
// update state immediately
updateState();
// keep state up-to-date
cell.addPropertyChangeListener(updateStateListener);
}
public void updateState() {
// update state
state = getState(hashes.values());
}
protected State getState(Collection<ChecksumCell> entries) {
public void dispose() {
for (ChecksumCell cell : hashes.values()) {
cell.dispose();
}
hashes.clear();
}
protected State getState(Collection<ChecksumCell> cells) {
// check states before we bother comparing the hash values
for (ChecksumCell entry : entries) {
if (entry.getState() == ChecksumCell.State.ERROR) {
for (ChecksumCell cell : cells) {
if (cell.getState() == ChecksumCell.State.ERROR) {
// one error cell -> error state
return State.ERROR;
} else if (entry.getState() != ChecksumCell.State.READY) {
} else if (cell.getState() != ChecksumCell.State.READY) {
// one cell that is not ready yet -> unknown state
return State.UNKNOWN;
}
@ -92,8 +107,8 @@ public class ChecksumRow {
for (HashType type : HashType.values()) {
checksumSet.clear();
for (ChecksumCell entry : entries) {
String checksum = entry.getChecksum(type);
for (ChecksumCell cell : cells) {
String checksum = cell.getChecksum(type);
if (checksum != null) {
checksumSet.add(checksum);
@ -134,4 +149,12 @@ public class ChecksumRow {
public String toString() {
return String.format("%s %s", name, hashes);
}
private final PropertyChangeListener updateStateListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
updateState();
}
};
}

View File

@ -24,19 +24,19 @@ public class ChecksumTableExportHandler extends TextFileExportHandler {
@Override
public boolean canExport() {
return model.getRowCount() > 0 && model.getChecksumList().size() > 0;
return model.getRowCount() > 0 && model.getChecksumColumns().size() > 0;
}
@Override
public void export(Formatter out) {
export(out, model.getChecksumList().get(0));
export(out, model.getChecksumColumns().get(0));
}
@Override
public String getDefaultFileName() {
return getDefaultFileName(model.getChecksumList().get(0));
return getDefaultFileName(model.getChecksumColumns().get(0));
}

View File

@ -75,7 +75,7 @@ class ChecksumTableModel extends AbstractTableModel implements Iterable<Checksum
}
public List<File> getChecksumList() {
public List<File> getChecksumColumns() {
return Collections.unmodifiableList(columns);
}
@ -118,7 +118,7 @@ class ChecksumTableModel extends AbstractTableModel implements Iterable<Checksum
rows.add(row);
}
row.add(entry);
row.put(entry);
// listen to changes (progress, state)
entry.addPropertyChangeListener(progressListener);
@ -139,10 +139,7 @@ class ChecksumTableModel extends AbstractTableModel implements Iterable<Checksum
public void remove(int... index) {
for (int i : index) {
for (ChecksumCell entry : rows.get(i).values()) {
entry.removePropertyChangeListener(progressListener);
entry.dispose();
}
rows.get(i).dispose();
}
// remove rows

View File

@ -5,19 +5,24 @@ package net.sourceforge.filebot.ui.panel.sfv;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;
import net.miginfocom.swing.MigLayout;
import net.sourceforge.filebot.ResourceManager;
import net.sourceforge.filebot.ui.FileBotPanel;
import net.sourceforge.filebot.ui.FileTransferableMessageHandler;
import net.sourceforge.filebot.ui.SelectDialog;
import net.sourceforge.filebot.ui.transfer.LoadAction;
import net.sourceforge.filebot.ui.transfer.SaveAction;
import net.sourceforge.tuned.FileUtilities;
import net.sourceforge.tuned.MessageHandler;
import net.sourceforge.tuned.ui.TunedUtilities;

View File

@ -19,6 +19,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sourceforge.filebot.ui.transfer.BackgroundFileTransferablePolicy;
import net.sourceforge.tuned.ExceptionUtilities;
class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumCell> {
@ -52,6 +53,12 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumCel
}
@Override
protected void process(Exception e) {
Logger.getLogger("ui").warning(ExceptionUtilities.getRootCause(e).getMessage());
}
protected void loadSfvFile(File sfvFile, Executor executor) {
try {
// don't use new Scanner(File) because of BUG 6368019 (http://bugs.sun.com/view_bug.do?bug_id=6368019)
@ -60,6 +67,9 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumCel
try {
Pattern pattern = Pattern.compile("(.+)\\s+(\\p{XDigit}{8})");
// root for relative file paths in sfv file
File root = sfvFile.getParentFile();
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
@ -71,21 +81,13 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumCel
if (!matcher.matches())
continue;
String filename = matcher.group(1);
String name = matcher.group(1);
String checksum = matcher.group(2);
publish(new ChecksumCell(filename, sfvFile, Collections.singletonMap(HashType.CRC32, checksum)));
ChecksumCell correct = new ChecksumCell(name, sfvFile, Collections.singletonMap(HashType.CRC32, checksum));
ChecksumCell current = createChecksumCell(name, root, new File(root, name), executor);
File column = sfvFile.getParentFile();
File file = new File(column, filename);
if (file.exists()) {
ChecksumComputationTask task = new ChecksumComputationTask(file);
publish(new ChecksumCell(filename, column, task));
executor.execute(task);
}
publish(correct, current);
if (Thread.interrupted()) {
break;
@ -150,11 +152,17 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumCel
load(f, root, newPrefix, executor);
}
} else if (file.isFile()) {
ChecksumComputationTask task = new ChecksumComputationTask(file);
publish(new ChecksumCell(prefix + file.getName(), root, task));
executor.execute(task);
publish(createChecksumCell(prefix + file.getName(), root, file, executor));
}
}
protected ChecksumCell createChecksumCell(String name, File root, File file, Executor executor) {
ChecksumCell cell = new ChecksumCell(name, root, new ChecksumComputationTask(new File(root, name), HashType.CRC32));
// start computation task
executor.execute(cell.getTask());
return cell;
}
}

View File

@ -7,100 +7,94 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferablePolicy {
public static final String LOADING_PROPERTY = "loading";
private BackgroundWorker worker = null;
@Override
public boolean accept(Transferable tr) {
if (isActive())
return false;
private final ThreadLocal<BackgroundWorker> threadLocalWorker = new ThreadLocal<BackgroundWorker>() {
return super.accept(tr);
}
@Override
protected BackgroundWorker initialValue() {
// fail if a non-background-worker thread is trying to access the thread-local worker object
throw new IllegalThreadStateException("Illegal access thread");
}
};
private final List<BackgroundWorker> workers = new ArrayList<BackgroundWorker>(2);
@Override
public synchronized void handleTransferable(Transferable tr, TransferAction action) {
public void handleTransferable(Transferable tr, TransferAction action) {
List<File> files = getFilesFromTransferable(tr);
if (action != TransferAction.ADD)
clear();
worker = new BackgroundWorker(files);
worker.addPropertyChangeListener(backgroundWorkerListener);
worker.execute();
// create and start worker
new BackgroundWorker(files).execute();
}
public synchronized boolean isActive() {
return worker != null && !worker.isDone();
}
public synchronized void reset() {
if (isActive()) {
worker.cancel(true);
public void reset() {
synchronized (workers) {
for (BackgroundWorker worker : workers) {
worker.cancel(true);
}
workers.clear();
}
}
/**
* Receives data chunks from the publish method asynchronously on the Event Dispatch
* Thread.
*
* @param chunks
*/
protected abstract void process(List<V> chunks);
/**
* Sends data chunks to the process method.
*
* @param chunks
*/
protected synchronized void publish(V... chunks) {
if (worker != null) {
worker.publishChunks(chunks);
}
protected abstract void process(Exception e);
protected final void publish(V... chunks) {
threadLocalWorker.get().offer(chunks);
}
private class BackgroundWorker extends SwingWorker<Void, V> {
protected class BackgroundWorker extends SwingWorker<Object, V> {
private final List<File> files;
public BackgroundWorker(List<File> files) {
this.files = files;
// register this worker
synchronized (workers) {
if (workers.add(this) && workers.size() == 1) {
swingPropertyChangeSupport.firePropertyChange(LOADING_PROPERTY, false, true);
}
}
}
@Override
protected Void doInBackground() {
protected Object doInBackground() {
// associate this worker with the current (background) thread
threadLocalWorker.set(this);
try {
load(files);
} catch (Exception e) {
Logger.getLogger("global").log(Level.WARNING, e.getMessage(), e);
} finally {
threadLocalWorker.remove();
}
return null;
}
public void publishChunks(V... chunks) {
public void offer(V... chunks) {
if (!isCancelled()) {
publish(chunks);
}
@ -113,31 +107,54 @@ public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferab
BackgroundFileTransferablePolicy.this.process(chunks);
}
}
}
private final PropertyChangeListener backgroundWorkerListener = new SwingWorkerPropertyChangeAdapter() {
@Override
public void started(PropertyChangeEvent evt) {
propertyChangeSupport.firePropertyChange(LOADING_PROPERTY, false, true);
}
@Override
public void done(PropertyChangeEvent evt) {
propertyChangeSupport.firePropertyChange(LOADING_PROPERTY, true, false);
protected void done() {
// unregister worker
synchronized (workers) {
if (workers.remove(this) && workers.isEmpty()) {
swingPropertyChangeSupport.firePropertyChange(LOADING_PROPERTY, true, false);
}
}
if (!isCancelled()) {
try {
// check for exception
get();
} catch (Exception e) {
BackgroundFileTransferablePolicy.this.process(e);
}
}
}
};
}
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
protected final PropertyChangeSupport swingPropertyChangeSupport = new PropertyChangeSupport(this) {
@Override
public void firePropertyChange(final PropertyChangeEvent evt) {
if (SwingUtilities.isEventDispatchThread()) {
super.firePropertyChange(evt);
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
swingPropertyChangeSupport.firePropertyChange(evt);
}
});
}
}
};
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
swingPropertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
swingPropertyChangeSupport.removePropertyChangeListener(listener);
}
}

View File

@ -2,7 +2,6 @@
package net.sourceforge.tuned.ui;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.ListModel;
@ -11,23 +10,23 @@ import javax.swing.event.ListDataListener;
public class ArrayListModel implements ListModel {
private final ArrayList<Object> data;
private final Object[] data;
public ArrayListModel(Collection<? extends Object> data) {
this.data = new ArrayList<Object>(data);
this.data = data.toArray();
}
@Override
public Object getElementAt(int index) {
return data.get(index);
return data[index];
}
@Override
public int getSize() {
return data.size();
return data.length;
}

View File

@ -32,7 +32,7 @@ public class LoadingOverlayPane extends JComponent {
animationComponent = new ProgressIndicator();
animationComponent.setVisible(false);
add(animationComponent, String.format("pos n %s 100%%-%s n", offsetY != null ? offsetY : "8px", offsetX != null ? offsetX : "18px"));
add(animationComponent, String.format("pos n %s 100%%-%s n", offsetY != null ? offsetY : "8px", offsetX != null ? offsetX : "20px"));
add(component, "grow");
if (propertyChangeSource != null) {

View File

@ -37,22 +37,9 @@ public class ProgressIndicator extends JComponent {
private final Dimension baseSize = new Dimension(32, 32);
private double alpha = 0;
private double speed = 1.2;
private double speed = 24;
private final Timer updateTimer = new Timer(20, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Timer timer = (Timer) e.getSource();
alpha += (timer.getDelay() * speed) / 1000;
if (alpha >= 1)
alpha -= Math.floor(alpha);
repaint();
}
});
private Timer updateTimer;
public ProgressIndicator() {
@ -74,6 +61,13 @@ public class ProgressIndicator extends JComponent {
}
public void animateOnce() {
if ((alpha += (speed / 1000)) >= 1) {
alpha -= Math.floor(alpha);
}
}
@Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
@ -123,12 +117,26 @@ public class ProgressIndicator extends JComponent {
public void startAnimation() {
updateTimer.restart();
if (updateTimer == null) {
updateTimer = new Timer(20, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
animateOnce();
repaint();
}
});
updateTimer.start();
}
}
public void stopAnimation() {
updateTimer.stop();
if (updateTimer != null) {
updateTimer.stop();
updateTimer = null;
}
}