* major refactoring of Checksum* Classes (TableModel, ComputationTask, ComputationService)

* SfvTransferablePolicy create one dedicated ComputationTask executor for each drop
* ComputationTask always computes CRC32, MD5 and SHA-1 

* changed TextFileExportHandler to use Formatter instead of PrintWriter
* renamed *Util to *Utilities
* update to GlazedLists 1.8
This commit is contained in:
Reinhard Pointner 2009-02-09 20:56:20 +00:00
parent 684a7512bc
commit 5674173417
36 changed files with 902 additions and 789 deletions

Binary file not shown.

View File

@ -7,7 +7,7 @@ import java.util.Map;
import java.util.prefs.BackingStoreException; import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import net.sourceforge.tuned.ExceptionUtil; import net.sourceforge.tuned.ExceptionUtilities;
import net.sourceforge.tuned.PreferencesList; import net.sourceforge.tuned.PreferencesList;
import net.sourceforge.tuned.PreferencesMap; import net.sourceforge.tuned.PreferencesMap;
import net.sourceforge.tuned.PreferencesMap.Adapter; import net.sourceforge.tuned.PreferencesMap.Adapter;
@ -96,7 +96,7 @@ public final class Settings {
// remove entries // remove entries
prefs.clear(); prefs.clear();
} catch (BackingStoreException e) { } catch (BackingStoreException e) {
throw ExceptionUtil.asRuntimeException(e); throw ExceptionUtilities.asRuntimeException(e);
} }
} }
} }

View File

@ -30,7 +30,7 @@ import javax.swing.SwingWorker;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.ResourceManager;
import net.sourceforge.filebot.web.SearchResult; import net.sourceforge.filebot.web.SearchResult;
import net.sourceforge.tuned.ExceptionUtil; import net.sourceforge.tuned.ExceptionUtilities;
import net.sourceforge.tuned.ui.LabelProvider; import net.sourceforge.tuned.ui.LabelProvider;
import net.sourceforge.tuned.ui.SelectButtonTextField; import net.sourceforge.tuned.ui.SelectButtonTextField;
import net.sourceforge.tuned.ui.TunedUtilities; import net.sourceforge.tuned.ui.TunedUtilities;
@ -184,7 +184,7 @@ public abstract class AbstractSearchPanel<S, E> extends FileBotPanel {
} catch (Exception e) { } catch (Exception e) {
tab.close(); tab.close();
Logger.getLogger("ui").log(Level.WARNING, ExceptionUtil.getRootCause(e).getMessage(), e); Logger.getLogger("ui").log(Level.WARNING, ExceptionUtilities.getRootCause(e).getMessage(), e);
} }
} }
@ -240,7 +240,7 @@ public abstract class AbstractSearchPanel<S, E> extends FileBotPanel {
} catch (Exception e) { } catch (Exception e) {
tab.close(); tab.close();
Logger.getLogger("ui").log(Level.WARNING, ExceptionUtil.getRootCause(e).getMessage(), e); Logger.getLogger("ui").log(Level.WARNING, ExceptionUtilities.getRootCause(e).getMessage(), e);
} finally { } finally {
tab.setLoading(false); tab.setLoading(false);
} }

View File

@ -2,7 +2,7 @@
package net.sourceforge.filebot.ui; package net.sourceforge.filebot.ui;
import java.io.PrintWriter; import java.util.Formatter;
import net.sourceforge.filebot.ui.transfer.TextFileExportHandler; import net.sourceforge.filebot.ui.transfer.TextFileExportHandler;
@ -24,9 +24,9 @@ public class FileBotListExportHandler extends TextFileExportHandler {
@Override @Override
public void export(PrintWriter out) { public void export(Formatter out) {
for (Object entry : list.getModel()) { for (Object entry : list.getModel()) {
out.println(entry); out.format("%s%n", entry);
} }
} }

View File

@ -16,7 +16,7 @@ import javax.swing.SwingWorker;
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FileNode; import net.sourceforge.filebot.ui.panel.analyze.FileTree.FileNode;
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FolderNode; import net.sourceforge.filebot.ui.panel.analyze.FileTree.FolderNode;
import net.sourceforge.tuned.ExceptionUtil; import net.sourceforge.tuned.ExceptionUtilities;
import net.sourceforge.tuned.FileUtilities; import net.sourceforge.tuned.FileUtilities;
import net.sourceforge.tuned.ui.TunedUtilities; import net.sourceforge.tuned.ui.TunedUtilities;
@ -90,7 +90,7 @@ abstract class Tool<M> extends JComponent {
try { try {
setModel(get()); setModel(get());
} catch (Exception e) { } catch (Exception e) {
if (ExceptionUtil.getRootCause(e) instanceof ConcurrentModificationException) { if (ExceptionUtilities.getRootCause(e) instanceof ConcurrentModificationException) {
// if it happens, it is supposed to // if it happens, it is supposed to
} else { } else {
// should not happen // should not happen

View File

@ -49,14 +49,19 @@ class AutoFetchEpisodeListMatcher extends SwingWorker<List<Match<File, Episode>>
} }
public Collection<File> remainingFiles() { public List<File> remainingFiles() {
return Collections.unmodifiableCollection(files); return Collections.unmodifiableList(files);
} }
protected Collection<String> detectSeriesNames(Collection<File> files) { protected Collection<String> detectSeriesNames(Collection<File> files) {
// detect series name(s) from files // detect series name(s) from files
return new SeriesNameMatcher().matchAll(files.toArray(new File[0])); Collection<String> names = new SeriesNameMatcher().matchAll(files.toArray(new File[0]));
if (names.isEmpty())
throw new IllegalArgumentException("Cannot auto-detect series name.");
return names;
} }
@ -79,9 +84,6 @@ class AutoFetchEpisodeListMatcher extends SwingWorker<List<Match<File, Episode>>
}); });
} }
if (tasks.isEmpty())
throw new IllegalArgumentException("Failed to auto-detect series name.");
// fetch episode lists concurrently // fetch episode lists concurrently
List<Episode> episodes = new ArrayList<Episode>(); List<Episode> episodes = new ArrayList<Episode>();
ExecutorService executor = Executors.newFixedThreadPool(tasks.size()); ExecutorService executor = Executors.newFixedThreadPool(tasks.size());

View File

@ -8,13 +8,12 @@ import java.util.Collection;
import net.sourceforge.filebot.similarity.Match; import net.sourceforge.filebot.similarity.Match;
import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.GlazedLists;
class RenameModel { class RenameModel {
private final EventList<Object> names = GlazedLists.threadSafeList(new BasicEventList<Object>()); private final EventList<Object> names = new BasicEventList<Object>();
private final EventList<FileEntry> files = GlazedLists.threadSafeList(new BasicEventList<FileEntry>()); private final EventList<FileEntry> files = new BasicEventList<FileEntry>();
public EventList<Object> names() { public EventList<Object> names() {

View File

@ -31,7 +31,7 @@ import net.sourceforge.filebot.web.Episode;
import net.sourceforge.filebot.web.EpisodeListClient; import net.sourceforge.filebot.web.EpisodeListClient;
import net.sourceforge.filebot.web.TVRageClient; import net.sourceforge.filebot.web.TVRageClient;
import net.sourceforge.filebot.web.TheTVDBClient; import net.sourceforge.filebot.web.TheTVDBClient;
import net.sourceforge.tuned.ExceptionUtil; import net.sourceforge.tuned.ExceptionUtilities;
import net.sourceforge.tuned.ui.ActionPopup; import net.sourceforge.tuned.ui.ActionPopup;
import net.sourceforge.tuned.ui.LoadingOverlayPane; import net.sourceforge.tuned.ui.LoadingOverlayPane;
import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEvent;
@ -215,7 +215,7 @@ public class RenamePanel extends FileBotPanel {
model.names().addAll(names); model.names().addAll(names);
model.files().addAll(files); model.files().addAll(files);
} catch (Exception e) { } catch (Exception e) {
Logger.getLogger("ui").log(Level.WARNING, ExceptionUtil.getRootCause(e).getMessage(), e); Logger.getLogger("ui").log(Level.WARNING, ExceptionUtilities.getRootCause(e).getMessage(), e);
} }
} }
}; };

View File

@ -1,168 +0,0 @@
package net.sourceforge.filebot.ui.panel.sfv;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
public class Checksum {
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
public static final String STATE_PROPERTY = "state";
public static final String PROGRESS_PROPERTY = "progress";
private Long checksum = null;
private State state = State.PENDING;
private ChecksumComputationTask computationTask = null;
private String errorMessage = null;
public static enum State {
PENDING,
INPROGRESS,
READY,
ERROR;
}
public Checksum(long checksum) {
setChecksum(checksum);
setState(State.READY);
}
public Checksum(String checksumString) {
this(Long.parseLong(checksumString, 16));
}
protected Checksum(ChecksumComputationTask computationTask) {
this.computationTask = computationTask;
this.computationTask.addPropertyChangeListener(new ComputationTaskPropertyChangeListener());
}
public String getChecksumString() {
return String.format("%08x", checksum).toUpperCase();
}
public Long getChecksum() {
return checksum;
}
public synchronized void setChecksum(Long checksum) {
this.checksum = checksum;
setState(State.READY);
computationTask = null;
}
public synchronized void setChecksumError(Exception exception) {
// get root cause
Throwable cause = exception;
while (cause.getCause() != null)
cause = cause.getCause();
errorMessage = cause.getMessage();
setState(State.ERROR);
computationTask = null;
}
public State getState() {
return state;
}
private void setState(State state) {
this.state = state;
propertyChangeSupport.firePropertyChange(STATE_PROPERTY, null, state);
}
public synchronized Integer getProgress() {
if (state == State.INPROGRESS)
return computationTask.getProgress();
return null;
}
public String getErrorMessage() {
return errorMessage;
}
public synchronized void cancelComputationTask() {
if (computationTask == null)
return;
computationTask.cancel(false);
}
private class ComputationTaskPropertyChangeListener extends SwingWorkerPropertyChangeAdapter {
@Override
public void progress(PropertyChangeEvent evt) {
propertyChangeSupport.firePropertyChange(PROGRESS_PROPERTY, null, evt.getNewValue());
}
@Override
public void started(PropertyChangeEvent evt) {
setState(State.INPROGRESS);
}
@Override
public void done(PropertyChangeEvent evt) {
try {
ChecksumComputationTask task = (ChecksumComputationTask) evt.getSource();
if (!task.isCancelled()) {
setChecksum(task.get());
}
} catch (Exception e) {
// might happen if file system is corrupt (e.g. CRC errors)
setChecksumError(e);
Logger.getLogger("global").log(Level.WARNING, e.getMessage());
}
}
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
@Override
public String toString() {
if (state == State.READY)
return getChecksumString();
if (state == State.ERROR)
return getErrorMessage();
return state.toString();
}
}

View File

@ -0,0 +1,141 @@
package net.sourceforge.filebot.ui.panel.sfv;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.util.Map;
import net.sourceforge.tuned.ExceptionUtilities;
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
public class ChecksumCell {
private final String name;
private final File root;
private Map<HashType, String> hashes;
private ChecksumComputationTask task;
private Throwable error;
public static enum State {
PENDING,
PROGRESS,
READY,
ERROR
}
public ChecksumCell(String name, File root, Map<HashType, String> hashes) {
this.name = name;
this.root = root;
this.hashes = hashes;
}
public ChecksumCell(String name, File root, ChecksumComputationTask computationTask) {
this.name = name;
this.root = root;
this.task = computationTask;
// forward property change events
task.addPropertyChangeListener(new SwingWorkerPropertyChangeAdapter() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
super.propertyChange(evt);
propertyChangeSupport.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
}
@Override
protected void done(PropertyChangeEvent evt) {
try {
hashes = task.get();
} catch (Exception e) {
error = ExceptionUtilities.getRootCause(e);
} finally {
task = null;
}
}
});
}
public String getName() {
return name;
}
public File getRoot() {
return root;
}
public String getChecksum(HashType type) {
if (hashes != null)
return hashes.get(type);
return null;
}
public ChecksumComputationTask getTask() {
return task;
}
public Throwable getError() {
return error;
}
public void dispose() {
if (task != null) {
task.cancel(true);
}
hashes = null;
error = null;
task = null;
}
public State getState() {
if (hashes != null)
return State.READY;
if (error != null)
return State.ERROR;
switch (task.getState()) {
case PENDING:
return State.PENDING;
default:
return State.PROGRESS;
}
}
@Override
public String toString() {
return String.format("%s %s", name, hashes);
}
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
}

View File

@ -2,194 +2,130 @@
package net.sourceforge.filebot.ui.panel.sfv; package net.sourceforge.filebot.ui.panel.sfv;
import java.beans.PropertyChangeEvent; import static java.lang.Math.log10;
import static java.lang.Math.max;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import net.sourceforge.tuned.DefaultThreadFactory; import net.sourceforge.tuned.DefaultThreadFactory;
public class ChecksumComputationService { public class ChecksumComputationService {
public static final String ACTIVE_PROPERTY = "active"; public static final String TASK_COUNT_PROPERTY = "taskCount";
public static final String REMAINING_TASK_COUNT_PROPERTY = "remainingTaskCount";
private final Map<File, ChecksumComputationExecutor> executors = new HashMap<File, ChecksumComputationExecutor>(); private final List<ThreadPoolExecutor> executors = new ArrayList<ThreadPoolExecutor>();
private final AtomicInteger activeSessionTaskCount = new AtomicInteger(0); private final AtomicInteger completedTaskCount = new AtomicInteger(0);
private final AtomicInteger remainingTaskCount = new AtomicInteger(0); private final AtomicInteger totalTaskCount = new AtomicInteger(0);
private final ThreadFactory threadFactory;
/**
* Property change events will be fired on the event dispatch thread
*/
private final SwingWorkerPropertyChangeSupport propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
public ChecksumComputationService() { public ExecutorService newExecutor() {
this(new DefaultThreadFactory("ChecksumComputationPool", Thread.MIN_PRIORITY)); return new ChecksumComputationExecutor();
}
public ChecksumComputationService(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
public Checksum schedule(File file, File workerQueue) {
ChecksumComputationTask task = new ChecksumComputationTask(file);
Checksum checksum = new Checksum(task);
getExecutor(workerQueue).execute(task);
return checksum;
} }
public void reset() { public void reset() {
deactivate(true); synchronized (executors) {
} for (ExecutorService executor : executors) {
private synchronized void deactivate(boolean shutdownNow) {
for (ChecksumComputationExecutor executor : executors.values()) {
if (shutdownNow)
executor.shutdownNow(); executor.shutdownNow();
else }
executor.shutdown();
totalTaskCount.set(0);
completedTaskCount.set(0);
executors.clear();
} }
executors.clear(); fireTaskCountChanged();
activeSessionTaskCount.set(0);
remainingTaskCount.set(0);
} }
public boolean isActive() { public int getTaskCount() {
return activeSessionTaskCount.get() > 0; return getTotalTaskCount() - getCompletedTaskCount();
} }
public int getRemainingTaskCount() { public int getTotalTaskCount() {
return remainingTaskCount.get(); return totalTaskCount.get();
} }
public int getActiveSessionTaskCount() { public int getCompletedTaskCount() {
return activeSessionTaskCount.get(); return completedTaskCount.get();
} }
public synchronized void purge() { public void purge() {
for (ChecksumComputationExecutor executor : executors.values()) { synchronized (executors) {
executor.purge(); for (ThreadPoolExecutor executor : executors) {
executor.purge();
}
} }
} }
private synchronized ChecksumComputationExecutor getExecutor(File workerQueue) {
ChecksumComputationExecutor executor = executors.get(workerQueue);
if (executor == null) {
executor = new ChecksumComputationExecutor();
executors.put(workerQueue, executor);
}
return executor;
}
private class ChecksumComputationExecutor extends ThreadPoolExecutor { private class ChecksumComputationExecutor extends ThreadPoolExecutor {
private static final int MINIMUM_POOL_SIZE = 1;
public ChecksumComputationExecutor() { public ChecksumComputationExecutor() {
super(MINIMUM_POOL_SIZE, MINIMUM_POOL_SIZE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory); super(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new DefaultThreadFactory("ChecksumComputationPool", Thread.MIN_PRIORITY));
synchronized (executors) {
executors.add(this);
}
prestartAllCoreThreads();
} }
private void adjustPoolSize() { protected int getPreferredPoolSize() {
// for a few files, use one thread // for a few files, use one thread
// for lots of files, use multiple threads // for lots of files, use multiple threads
// e.g 50 files ~ 1 thread, 1000 files ~ 3 threads, 40000 files ~ 5 threads // e.g 50 files ~ 1 thread, 200 files ~ 2 threads, 1000 files ~ 3 threads, 40000 files ~ 5 threads
int preferredPoolSize = (int) Math.max(Math.log10(getQueue().size() / 10), MINIMUM_POOL_SIZE); return max((int) log10(getQueue().size()), 1);
if (getCorePoolSize() != preferredPoolSize) {
setCorePoolSize(preferredPoolSize);
}
} }
@Override @Override
public void execute(Runnable command) { public void execute(Runnable command) {
if (activeSessionTaskCount.getAndIncrement() <= 0) { int preferredPoolSize = getPreferredPoolSize();
setActive(true);
if (getCorePoolSize() < preferredPoolSize) {
setCorePoolSize(preferredPoolSize);
} }
super.execute(command); synchronized (this) {
super.execute(command);
}
adjustPoolSize(); totalTaskCount.incrementAndGet();
fireTaskCountChanged();
remainingTaskCount.incrementAndGet();
fireRemainingTaskCountChange();
} }
@Override @Override
public void purge() { public void purge() {
try { int delta = 0;
List<ChecksumComputationTask> cancelledTasks = new ArrayList<ChecksumComputationTask>();
for (Runnable entry : getQueue()) {
ChecksumComputationTask task = (ChecksumComputationTask) entry;
if (task.isCancelled()) {
cancelledTasks.add(task);
}
}
for (ChecksumComputationTask task : cancelledTasks) {
remove(task);
}
} catch (ConcurrentModificationException e) {
Logger.getLogger("global").log(Level.SEVERE, e.toString(), e);
}
}
@Override
public boolean remove(Runnable task) {
boolean success = super.remove(task);
if (success) { synchronized (this) {
activeSessionTaskCount.decrementAndGet(); delta += getQueue().size();
super.purge();
if (remainingTaskCount.decrementAndGet() <= 0) { delta -= getQueue().size();
setActive(false);
}
fireRemainingTaskCountChange();
} }
return success; if (delta > 0) {
// subtract removed tasks from task count
totalTaskCount.addAndGet(-delta);
fireTaskCountChanged();
}
} }
@ -197,54 +133,35 @@ public class ChecksumComputationService {
protected void afterExecute(Runnable r, Throwable t) { protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t); super.afterExecute(r, t);
if (remainingTaskCount.decrementAndGet() <= 0) { completedTaskCount.incrementAndGet();
deactivate(false); fireTaskCountChanged();
setActive(false);
}
fireRemainingTaskCountChange();
}
}
private void setActive(boolean active) {
propertyChangeSupport.firePropertyChange(ACTIVE_PROPERTY, null, active);
}
private void fireRemainingTaskCountChange() {
propertyChangeSupport.firePropertyChange(REMAINING_TASK_COUNT_PROPERTY, null, getRemainingTaskCount());
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
private static class SwingWorkerPropertyChangeSupport extends PropertyChangeSupport {
public SwingWorkerPropertyChangeSupport(Object sourceBean) {
super(sourceBean);
} }
@Override @Override
public void firePropertyChange(final PropertyChangeEvent evt) { protected void terminated() {
SwingUtilities.invokeLater(new Runnable() { synchronized (executors) {
executors.remove(this);
@Override }
public void run() {
SwingWorkerPropertyChangeSupport.super.firePropertyChange(evt);
}
});
} }
}
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
private void fireTaskCountChanged() {
propertyChangeSupport.firePropertyChange(TASK_COUNT_PROPERTY, null, getTaskCount());
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
} }
} }

View File

@ -4,13 +4,14 @@ package net.sourceforge.filebot.ui.panel.sfv;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.util.zip.CRC32; import java.io.InputStream;
import java.util.zip.CheckedInputStream; import java.util.EnumMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
public class ChecksumComputationTask extends SwingWorker<Long, Void> { class ChecksumComputationTask extends SwingWorker<Map<HashType, String>, Void> {
private static final int BUFFER_SIZE = 32 * 1024; private static final int BUFFER_SIZE = 32 * 1024;
@ -23,37 +24,56 @@ public class ChecksumComputationTask extends SwingWorker<Long, Void> {
@Override @Override
protected Long doInBackground() throws Exception { protected Map<HashType, String> doInBackground() throws Exception {
CheckedInputStream cis = new CheckedInputStream(new FileInputStream(file), new CRC32()); Map<HashType, Hash> hashes = new EnumMap<HashType, Hash>(HashType.class);
for (HashType type : HashType.values()) {
hashes.put(type, type.newInstance());
}
long length = file.length(); long length = file.length();
if (length > 0) { if (length > 0) {
long done = 0; InputStream in = new FileInputStream(file);
byte[] buffer = new byte[BUFFER_SIZE]; try {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = 0;
long position = 0;
while ((bytesRead = cis.read(buffer)) >= 0) { int len = 0;
if (isCancelled() || Thread.currentThread().isInterrupted())
break;
done += bytesRead; while ((len = in.read(buffer)) >= 0) {
position += len;
int progress = (int) ((done * 100) / length);
setProgress(progress); for (Hash hash : hashes.values()) {
hash.update(buffer, 0, len);
}
// update progress
setProgress((int) ((position * 100) / length));
// check abort status
if (isCancelled() || Thread.interrupted()) {
break;
}
}
} finally {
in.close();
} }
} }
cis.close(); return digest(hashes);
return cis.getChecksum().getValue();
} }
@Override private Map<HashType, String> digest(Map<HashType, Hash> hashes) {
public String toString() { Map<HashType, String> results = new EnumMap<HashType, String>(HashType.class);
return String.format("%s (%s)", getClass().getSimpleName(), file.getName());
for (Entry<HashType, Hash> entry : hashes.entrySet()) {
results.put(entry.getKey(), entry.getValue().digest());
}
return results;
} }
} }

View File

@ -0,0 +1,29 @@
package net.sourceforge.filebot.ui.panel.sfv;
import java.util.zip.Checksum;
class ChecksumHash implements Hash {
private final Checksum checksum;
public ChecksumHash(Checksum checksum) {
this.checksum = checksum;
}
@Override
public void update(byte[] bytes, int off, int len) {
checksum.update(bytes, off, len);
}
@Override
public String digest() {
return String.format("%08X", checksum.getValue());
}
}

View File

@ -4,8 +4,12 @@ package net.sourceforge.filebot.ui.panel.sfv;
import java.io.File; import java.io.File;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.sourceforge.filebot.FileBotUtilities; import net.sourceforge.filebot.FileBotUtilities;
@ -14,48 +18,26 @@ public class ChecksumRow {
private String name; private String name;
private HashMap<File, Checksum> checksumMap = new HashMap<File, Checksum>(); private Map<File, ChecksumCell> hashes = new HashMap<File, ChecksumCell>(4);
private State state = State.UNKNOWN;
/** /**
* Checksum that is embedded in the file name (e.g. Test[49A93C5F].txt) * Checksum that is embedded in the file name (e.g. Test[49A93C5F].txt)
*/ */
private final Long embeddedChecksum; private String embeddedChecksum;
public static enum State { public static enum State {
UNKNOWN,
OK, OK,
WARNING, WARNING,
ERROR, ERROR
UNKNOWN;
} }
public ChecksumRow(String name) { public ChecksumRow(String name) {
this.name = name; this.name = name;
this.embeddedChecksum = getEmbeddedChecksum(name); this.embeddedChecksum = FileBotUtilities.getEmbeddedChecksum(name);
}
/**
* Try to parse a CRC32 checksum from the given file name. The checksum is assumed to be in
* brackets.
*
* <pre>
* e.g.
* Test[49A93C5F].txt
* </pre>
*
* @param file name that contains a checksum
* @return the checksum or null, if parameter did not contain a checksum
*/
private static Long getEmbeddedChecksum(String name) {
// look for a checksum pattern like [49A93C5F]
String match = FileBotUtilities.getEmbeddedChecksum(name);
if (match != null)
return Long.parseLong(match, 16);
return null;
} }
@ -65,50 +47,91 @@ public class ChecksumRow {
public State getState() { public State getState() {
HashSet<Long> checksums = new HashSet<Long>(); return state;
}
for (Checksum checksum : getChecksums()) {
if (checksum.getState() == Checksum.State.READY) {
checksums.add(checksum.getChecksum()); public ChecksumCell getChecksum(File root) {
} else if (checksum.getState() == Checksum.State.ERROR) { return hashes.get(root);
}
public Collection<ChecksumCell> values() {
return Collections.unmodifiableCollection(hashes.values());
}
public void add(ChecksumCell entry) {
hashes.put(entry.getRoot(), entry);
updateState();
}
public void updateState() {
// update state
state = getState(hashes.values());
}
protected State getState(Collection<ChecksumCell> entries) {
// check states before we bother comparing the hash values
for (ChecksumCell entry : entries) {
if (entry.getState() == ChecksumCell.State.ERROR) {
// one error cell -> error state
return State.ERROR; return State.ERROR;
} else { } else if (entry.getState() != ChecksumCell.State.READY) {
// one cell that is not ready yet -> unknown state
return State.UNKNOWN; return State.UNKNOWN;
} }
} }
if (checksums.size() > 1) { // compare hash values
// checksums do not match Set<String> checksumSet = new HashSet<String>(2);
Set<State> verdictSet = EnumSet.noneOf(State.class);
for (HashType type : HashType.values()) {
checksumSet.clear();
for (ChecksumCell entry : entries) {
String checksum = entry.getChecksum(type);
if (checksum != null) {
checksumSet.add(checksum);
}
}
verdictSet.add(getVerdict(checksumSet));
}
// ERROR > WARNING > OK > UNKOWN
return Collections.max(verdictSet);
}
protected State getVerdict(Set<String> checksumSet) {
if (checksumSet.size() < 1) {
// no hash values
return State.UNKNOWN;
} else if (checksumSet.size() > 1) {
// hashes don't match, something is wrong
return State.ERROR; return State.ERROR;
} else {
// all hashes match
if (embeddedChecksum != null) {
String checksum = checksumSet.iterator().next();
if (checksum.length() == embeddedChecksum.length() && !checksum.equalsIgnoreCase(embeddedChecksum)) {
return State.WARNING;
}
}
return State.OK;
} }
if (!checksums.isEmpty() && embeddedChecksum != null) {
// check if the embedded checksum matches
if (!checksums.contains(embeddedChecksum))
return State.WARNING;
}
return State.OK;
} }
public Checksum getChecksum(File column) { @Override
return checksumMap.get(column); public String toString() {
return String.format("%s %s", name, hashes);
} }
public Collection<Checksum> getChecksums() {
return checksumMap.values();
}
public void putChecksum(File column, Checksum checksum) {
checksumMap.put(column, checksum);
}
public void removeChecksum(File column) {
checksumMap.remove(column);
}
} }

View File

@ -23,24 +23,25 @@ class ChecksumTableCellRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, null, isSelected, false, row, column); super.getTableCellRendererComponent(table, null, isSelected, false, row, column);
if (value == null) if (value instanceof ChecksumCell) {
return this; ChecksumCell checksum = (ChecksumCell) value;
Checksum checksum = (Checksum) value; switch (checksum.getState()) {
case READY:
switch (checksum.getState()) { setText(checksum.getChecksum(HashType.CRC32));
case READY: break;
setText(checksum.getChecksumString()); case PENDING:
return this; setText("Pending ...");
case PENDING: break;
setText("Pending ..."); case ERROR:
return this; setText(checksum.getError().getMessage());
case ERROR: break;
setText(checksum.getErrorMessage()); default:
return this; return progressBarRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
default: }
return progressBarRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
} }
return this;
} }
@ -61,9 +62,11 @@ class ChecksumTableCellRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Checksum checksum = (Checksum) value; ChecksumComputationTask task = ((ChecksumCell) value).getTask();
progressBar.setValue(checksum.getProgress()); if (task != null) {
progressBar.setValue(task.getProgress());
}
if (isSelected) { if (isSelected) {
this.setBackground(table.getSelectionBackground()); this.setBackground(table.getSelectionBackground());

View File

@ -5,11 +5,9 @@ package net.sourceforge.filebot.ui.panel.sfv;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Formatter;
import java.util.Map.Entry; import net.sourceforge.filebot.Settings;
import net.sourceforge.filebot.ui.transfer.TextFileExportHandler; import net.sourceforge.filebot.ui.transfer.TextFileExportHandler;
import net.sourceforge.tuned.FileUtilities; import net.sourceforge.tuned.FileUtilities;
@ -26,19 +24,19 @@ public class ChecksumTableExportHandler extends TextFileExportHandler {
@Override @Override
public boolean canExport() { public boolean canExport() {
return model.getRowCount() > 0 && model.getChecksumColumnCount() > 0; return model.getRowCount() > 0 && model.getChecksumList().size() > 0;
} }
@Override @Override
public void export(PrintWriter out) { public void export(Formatter out) {
export(out, model.getChecksumColumn(0)); export(out, model.getChecksumList().get(0));
} }
@Override @Override
public String getDefaultFileName() { public String getDefaultFileName() {
return getDefaultFileName(model.getChecksumColumn(0)); return getDefaultFileName(model.getChecksumList().get(0));
} }
@ -46,27 +44,21 @@ public class ChecksumTableExportHandler extends TextFileExportHandler {
PrintWriter out = new PrintWriter(file, "UTF-8"); PrintWriter out = new PrintWriter(file, "UTF-8");
try { try {
export(out, column); export(new Formatter(out), column);
} finally { } finally {
out.close(); out.close();
} }
} }
public void export(PrintWriter out, File column) { public void export(Formatter out, File column) {
out.format("; Generated by %s on %tF at %<tT%n", Settings.getApplicationName(), new Date());
out.format(";%n");
out.format(";%n");
SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd"); for (ChecksumRow row : model) {
SimpleDateFormat time = new SimpleDateFormat("HH:mm:ss"); //TODO select hash type
out.format("%s %s%n", row.getName(), row.getChecksum(column).getChecksum(HashType.CRC32));
Date now = new Date();
out.println("; Generated by FileBot on " + date.format(now) + " at " + time.format(now));
out.println(";");
out.println(";");
Map<String, Checksum> checksumMap = model.getChecksumColumn(column);
for (Entry<String, Checksum> entry : checksumMap.entrySet()) {
out.println(String.format("%s %s", entry.getKey(), entry.getValue()));
} }
} }

View File

@ -2,15 +2,20 @@
package net.sourceforge.filebot.ui.panel.sfv; package net.sourceforge.filebot.ui.panel.sfv;
import static javax.swing.event.TableModelEvent.UPDATE;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.io.File; import java.io.File;
import java.util.AbstractList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
@ -19,92 +24,86 @@ import javax.swing.table.TableModel;
import net.sourceforge.tuned.FileUtilities; import net.sourceforge.tuned.FileUtilities;
class ChecksumTableModel extends AbstractTableModel { class ChecksumTableModel extends AbstractTableModel implements Iterable<ChecksumRow> {
private List<ChecksumRow> rows = new ArrayList<ChecksumRow>(50); private final IndexedMap<String, ChecksumRow> rows = new IndexedMap<String, ChecksumRow>() {
@Override
public String key(ChecksumRow value) {
return value.getName();
}
};
/** private final List<File> columns = new ArrayList<File>();
* Hash map for fast access to the row of a given name
*/
private Map<String, ChecksumRow> rowMap = new HashMap<String, ChecksumRow>(50);
private List<File> columns = new ArrayList<File>();
/**
* Checksum start at column 3
*/
private static final int checksumColumnOffset = 2;
@Override @Override
public String getColumnName(int columnIndex) { public String getColumnName(int columnIndex) {
if (columnIndex == 0) switch (columnIndex) {
return "State"; case 0:
return "State";
if (columnIndex == 1) case 1:
return "Name"; return "Name";
default:
if (columnIndex >= checksumColumnOffset) { // works for files too and simply returns the name unchanged
File column = columns.get(columnIndex - checksumColumnOffset); return FileUtilities.getFolderName(getColumnRoot(columnIndex));
// works for files too and simply returns the name unchanged
return FileUtilities.getFolderName(column);
} }
return null;
} }
@Override @Override
public Class<?> getColumnClass(int columnIndex) { public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 0) switch (columnIndex) {
return ChecksumRow.State.class; case 0:
return ChecksumRow.State.class;
if (columnIndex == 1) case 1:
return String.class; return String.class;
default:
if (columnIndex >= checksumColumnOffset) return ChecksumCell.class;
return Checksum.class; }
return null;
} }
public File getColumnRoot(int columnIndex) {
return columns.get(columnIndex - 2);
}
@Override
public int getColumnCount() { public int getColumnCount() {
return checksumColumnOffset + getChecksumColumnCount(); return columns.size() + 2;
} }
public int getChecksumColumnCount() { public List<File> getChecksumList() {
return columns.size();
}
public List<File> getChecksumColumns() {
return Collections.unmodifiableList(columns); return Collections.unmodifiableList(columns);
} }
@Override
public int getRowCount() { public int getRowCount() {
return rows.size(); return rows.size();
} }
@Override
public Object getValueAt(int rowIndex, int columnIndex) { public Object getValueAt(int rowIndex, int columnIndex) {
ChecksumRow row = rows.get(rowIndex); ChecksumRow row = rows.get(rowIndex);
if (columnIndex == 0) switch (columnIndex) {
return row.getState(); case 0:
return row.getState();
if (columnIndex == 1) case 1:
return row.getName(); return row.getName();
default:
if (columnIndex >= checksumColumnOffset) { return row.getChecksum(getColumnRoot(columnIndex));
File column = columns.get(columnIndex - checksumColumnOffset);
return row.getChecksum(column);
} }
}
return null;
@Override
public Iterator<ChecksumRow> iterator() {
return rows.iterator();
} }
@ -112,7 +111,22 @@ class ChecksumTableModel extends AbstractTableModel {
int firstRow = getRowCount(); int firstRow = getRowCount();
for (ChecksumCell entry : list) { for (ChecksumCell entry : list) {
addChecksum(entry.getName(), entry.getChecksum(), entry.getColumn()); ChecksumRow row = rows.getByKey(entry.getName());
if (row == null) {
row = new ChecksumRow(entry.getName());
rows.add(row);
}
row.add(entry);
// listen to changes (progress, state)
entry.addPropertyChangeListener(progressListener);
if (!columns.contains(entry.getRoot())) {
columns.add(entry.getRoot());
fireTableStructureChanged();
}
} }
int lastRow = getRowCount() - 1; int lastRow = getRowCount() - 1;
@ -123,125 +137,142 @@ class ChecksumTableModel extends AbstractTableModel {
} }
private void addChecksum(String name, Checksum checksum, File column) { public void remove(int... index) {
ChecksumRow row = rowMap.get(name); for (int i : index) {
for (ChecksumCell entry : rows.get(i).values()) {
if (row == null) { entry.removePropertyChangeListener(progressListener);
row = new ChecksumRow(name); entry.dispose();
rows.add(row);
rowMap.put(name, row);
}
row.putChecksum(column, checksum);
checksum.addPropertyChangeListener(checksumListener);
if (!columns.contains(column)) {
columns.add(column);
fireTableStructureChanged();
}
}
public void removeRows(int... rowIndices) {
ArrayList<ChecksumRow> rowsToRemove = new ArrayList<ChecksumRow>(rowIndices.length);
for (int i : rowIndices) {
ChecksumRow row = rows.get(i);
rowsToRemove.add(rows.get(i));
for (Checksum checksum : row.getChecksums()) {
checksum.cancelComputationTask();
} }
rowMap.remove(row.getName());
} }
rows.removeAll(rowsToRemove); // remove rows
fireTableRowsDeleted(rowIndices[0], rowIndices[rowIndices.length - 1]); rows.removeAll(index);
fireTableRowsDeleted(index[0], index[index.length - 1]);
} }
public void clear() { public void clear() {
columns.clear(); columns.clear();
rows.clear(); rows.clear();
rowMap.clear();
fireTableStructureChanged(); fireTableStructureChanged();
fireTableDataChanged();
} }
private final PropertyChangeListener progressListener = new PropertyChangeListener() {
public File getChecksumColumn(int columnIndex) {
return columns.get(columnIndex);
}
public Map<String, Checksum> getChecksumColumn(File column) {
LinkedHashMap<String, Checksum> checksumMap = new LinkedHashMap<String, Checksum>();
for (ChecksumRow row : rows) { private final MutableTableModelEvent mutableUpdateEvent = new MutableTableModelEvent(ChecksumTableModel.this, UPDATE);
Checksum checksum = row.getChecksum(column);
if ((checksum != null) && (checksum.getState() == Checksum.State.READY)) {
checksumMap.put(row.getName(), checksum);
}
}
return checksumMap;
}
private final PropertyChangeListener checksumListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
fireTableChanged(new ChecksumTableModelEvent(ChecksumTableModel.this)); ChecksumCell entry = (ChecksumCell) evt.getSource();
int index = rows.getIndexByKey(entry.getName());
if (index >= 0) {
rows.get(index).updateState();
fireTableChanged(mutableUpdateEvent.setRow(index));
}
} }
}; };
public static class ChecksumCell { protected static class MutableTableModelEvent extends TableModelEvent {
private final String name; public MutableTableModelEvent(TableModel source, int type) {
private final Checksum checksum; super(source, 0, 0, ALL_COLUMNS, type);
private final File column;
public ChecksumCell(String name, Checksum checksum, File column) {
this.name = name;
this.checksum = checksum;
this.column = column;
} }
public String getName() { public MutableTableModelEvent setRow(int row) {
return name; this.firstRow = row;
} this.lastRow = row;
return this;
public Checksum getChecksum() {
return checksum;
}
public File getColumn() {
return column;
}
@Override
public String toString() {
return getName();
} }
} }
public static class ChecksumTableModelEvent extends TableModelEvent { protected static abstract class IndexedMap<K, V> extends AbstractList<V> implements Set<V> {
public static final int CHECKSUM_PROGRESS = 10; private final Map<K, Integer> indexMap = new HashMap<K, Integer>(64);
private final List<V> list = new ArrayList<V>(64);
public ChecksumTableModelEvent(TableModel source) { public abstract K key(V value);
super(source);
type = CHECKSUM_PROGRESS;
@Override
public V get(int index) {
return list.get(index);
}
public V getByKey(K key) {
Integer index = indexMap.get(key);
if (index == null)
return null;
return get(index);
}
public int getIndexByKey(K key) {
Integer index = indexMap.get(key);
if (index == null)
return -1;
return index;
}
@Override
public boolean add(V value) {
K key = key(value);
Integer index = indexMap.get(key);
if (index == null && list.add(value)) {
indexMap.put(key, lastIndexOf(value));
return true;
}
return false;
}
public void removeAll(int... index) {
// sort index array
Arrays.sort(index);
// remove in reverse
for (int i = index.length - 1; i >= 0; i--) {
V value = list.remove(index[i]);
indexMap.remove(key(value));
}
updateIndexMap();
}
private void updateIndexMap() {
for (int i = 0; i < list.size(); i++) {
indexMap.put(key(list.get(i)), i);
}
}
@Override
public int size() {
return list.size();
}
@Override
public void clear() {
list.clear();
indexMap.clear();
} }
} }

View File

@ -0,0 +1,12 @@
package net.sourceforge.filebot.ui.panel.sfv;
interface Hash {
public void update(byte[] bytes, int off, int len);
public String digest();
}

View File

@ -0,0 +1,34 @@
package net.sourceforge.filebot.ui.panel.sfv;
import java.util.zip.CRC32;
enum HashType {
CRC32 {
@Override
public Hash newInstance() {
return new ChecksumHash(new CRC32());
}
},
MD5 {
@Override
public Hash newInstance() {
return new MessageDigestHash("MD5");
}
},
SHA1 {
@Override
public Hash newInstance() {
return new MessageDigestHash("SHA-1");
}
};
public abstract Hash newInstance();
}

View File

@ -0,0 +1,41 @@
package net.sourceforge.filebot.ui.panel.sfv;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
class MessageDigestHash implements Hash {
private final MessageDigest md;
public MessageDigestHash(String algorithm) {
try {
this.md = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
}
public MessageDigestHash(MessageDigest md) {
this.md = md;
}
@Override
public void update(byte[] bytes, int off, int len) {
md.update(bytes, off, len);
}
@Override
public String digest() {
// e.g. %032x (format for MD-5)
return String.format("%0" + (md.getDigestLength() * 2) + "x", new BigInteger(1, md.digest()));
}
}

View File

@ -5,24 +5,19 @@ package net.sourceforge.filebot.ui.panel.sfv;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder; import javax.swing.border.TitledBorder;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.ResourceManager;
import net.sourceforge.filebot.ui.FileBotPanel; import net.sourceforge.filebot.ui.FileBotPanel;
import net.sourceforge.filebot.ui.FileTransferableMessageHandler; 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.LoadAction;
import net.sourceforge.filebot.ui.transfer.SaveAction; import net.sourceforge.filebot.ui.transfer.SaveAction;
import net.sourceforge.tuned.FileUtilities;
import net.sourceforge.tuned.MessageHandler; import net.sourceforge.tuned.MessageHandler;
import net.sourceforge.tuned.ui.TunedUtilities; import net.sourceforge.tuned.ui.TunedUtilities;
@ -82,7 +77,8 @@ public class SfvPanel extends FileBotPanel {
int row = sfvTable.getSelectionModel().getMinSelectionIndex(); int row = sfvTable.getSelectionModel().getMinSelectionIndex();
sfvTable.removeRows(sfvTable.getSelectedRows()); // remove selected rows
sfvTable.getModel().remove(sfvTable.getSelectedRows());
int maxRow = sfvTable.getRowCount() - 1; int maxRow = sfvTable.getRowCount() - 1;

View File

@ -14,7 +14,6 @@ import javax.swing.table.TableColumn;
import javax.swing.table.TableModel; import javax.swing.table.TableModel;
import net.sourceforge.filebot.FileBotUtilities; import net.sourceforge.filebot.FileBotUtilities;
import net.sourceforge.filebot.ui.panel.sfv.ChecksumTableModel.ChecksumTableModelEvent;
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler; import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
@ -27,7 +26,6 @@ class SfvTable extends JTable {
public SfvTable() { public SfvTable() {
transferablePolicy = new SfvTransferablePolicy(getModel(), checksumComputationService); transferablePolicy = new SfvTransferablePolicy(getModel(), checksumComputationService);
exportHandler = new ChecksumTableExportHandler(getModel()); exportHandler = new ChecksumTableExportHandler(getModel());
@ -48,7 +46,7 @@ class SfvTable extends JTable {
// highlight CRC32 patterns in filenames in green and with smaller font-size // highlight CRC32 patterns in filenames in green and with smaller font-size
setDefaultRenderer(String.class, new HighlightPatternCellRenderer(FileBotUtilities.EMBEDDED_CHECKSUM_PATTERN, "#009900", "smaller")); setDefaultRenderer(String.class, new HighlightPatternCellRenderer(FileBotUtilities.EMBEDDED_CHECKSUM_PATTERN, "#009900", "smaller"));
setDefaultRenderer(ChecksumRow.State.class, new StateIconTableCellRenderer()); setDefaultRenderer(ChecksumRow.State.class, new StateIconTableCellRenderer());
setDefaultRenderer(Checksum.class, new ChecksumTableCellRenderer()); setDefaultRenderer(ChecksumCell.class, new ChecksumTableCellRenderer());
} }
@ -91,6 +89,7 @@ class SfvTable extends JTable {
for (int i = 0; i < getColumnCount(); i++) { for (int i = 0; i < getColumnCount(); i++) {
TableColumn column = getColumnModel().getColumn(i); TableColumn column = getColumnModel().getColumn(i);
if (i == 0) { if (i == 0) {
column.setPreferredWidth(45); column.setPreferredWidth(45);
} else if (i == 1) { } else if (i == 1) {
@ -110,26 +109,15 @@ class SfvTable extends JTable {
} }
public void removeRows(int... rowIndices) {
getModel().removeRows(rowIndices);
}
@Override @Override
public void tableChanged(TableModelEvent e) { public void tableChanged(TableModelEvent e) {
// only request repaint when progress changes. Selection will go haywire if you don't. //TODO CCS in SfvPanel??
if (e.getType() == ChecksumTableModelEvent.CHECKSUM_PROGRESS) {
repaint();
return;
}
if (e.getType() == TableModelEvent.DELETE) { if (e.getType() == TableModelEvent.DELETE) {
// remove cancelled tasks from queue // remove cancelled tasks from queue
checksumComputationService.purge(); checksumComputationService.purge();
} }
super.tableChanged(e); super.tableChanged(e);
} }

View File

@ -5,12 +5,14 @@ package net.sourceforge.filebot.ui.panel.sfv;
import static net.sourceforge.filebot.FileBotUtilities.SFV_FILES; import static net.sourceforge.filebot.FileBotUtilities.SFV_FILES;
import static net.sourceforge.tuned.FileUtilities.containsOnly; import static net.sourceforge.tuned.FileUtilities.containsOnly;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -19,14 +21,14 @@ import java.util.regex.Pattern;
import net.sourceforge.filebot.ui.transfer.BackgroundFileTransferablePolicy; import net.sourceforge.filebot.ui.transfer.BackgroundFileTransferablePolicy;
class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTableModel.ChecksumCell> { class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumCell> {
private final ChecksumTableModel tableModel; private final ChecksumTableModel model;
private final ChecksumComputationService checksumComputationService; private final ChecksumComputationService checksumComputationService;
public SfvTransferablePolicy(ChecksumTableModel tableModel, ChecksumComputationService checksumComputationService) { public SfvTransferablePolicy(ChecksumTableModel model, ChecksumComputationService checksumComputationService) {
this.tableModel = tableModel; this.model = model;
this.checksumComputationService = checksumComputationService; this.checksumComputationService = checksumComputationService;
} }
@ -40,46 +42,58 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
@Override @Override
protected void clear() { protected void clear() {
checksumComputationService.reset(); checksumComputationService.reset();
tableModel.clear(); model.clear();
} }
@Override @Override
protected void process(List<ChecksumTableModel.ChecksumCell> chunks) { protected void process(List<ChecksumCell> chunks) {
tableModel.addAll(chunks); model.addAll(chunks);
} }
protected void loadSfvFile(File sfvFile) { protected void loadSfvFile(File sfvFile, Executor executor) {
try { try {
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(sfvFile), "UTF-8")); // don't use new Scanner(File) because of BUG 6368019 (http://bugs.sun.com/view_bug.do?bug_id=6368019)
Scanner scanner = new Scanner(new FileInputStream(sfvFile), "utf-8");
String line = null; try {
Pattern pattern = Pattern.compile("(.*)\\s+(\\p{XDigit}{8})"); Pattern pattern = Pattern.compile("(.+)\\s+(\\p{XDigit}{8})");
while (((line = in.readLine()) != null) && !Thread.interrupted()) {
if (line.startsWith(";"))
continue;
Matcher matcher = pattern.matcher(line); while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (!matcher.matches())
continue; if (line.startsWith(";"))
continue;
String filename = matcher.group(1);
String checksumString = matcher.group(2); Matcher matcher = pattern.matcher(line);
publish(new ChecksumTableModel.ChecksumCell(filename, new Checksum(checksumString), sfvFile)); if (!matcher.matches())
continue;
File column = sfvFile.getParentFile();
File file = new File(column, filename); String filename = matcher.group(1);
String checksum = matcher.group(2);
if (file.exists()) {
publish(new ChecksumTableModel.ChecksumCell(filename, checksumComputationService.schedule(file, column), column)); publish(new ChecksumCell(filename, sfvFile, Collections.singletonMap(HashType.CRC32, checksum)));
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);
}
if (Thread.interrupted()) {
break;
}
} }
} finally {
scanner.close();
} }
in.close();
} catch (IOException e) { } catch (IOException e) {
// should not happen // should not happen
Logger.getLogger("global").log(Level.SEVERE, e.toString(), e); Logger.getLogger("global").log(Level.SEVERE, e.toString(), e);
@ -95,44 +109,52 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
@Override @Override
protected void load(List<File> files) { protected void load(List<File> files) {
ExecutorService executor = checksumComputationService.newExecutor();
try { try {
if (containsOnly(files, SFV_FILES)) { if (containsOnly(files, SFV_FILES)) {
// one or more sfv files // one or more sfv files
for (File file : files) { for (File file : files) {
loadSfvFile(file); loadSfvFile(file, executor);
} }
} else if ((files.size() == 1) && files.get(0).isDirectory()) { } else if ((files.size() == 1) && files.get(0).isDirectory()) {
// one single folder // one single folder
File file = files.get(0); File file = files.get(0);
for (File f : file.listFiles()) { for (File f : file.listFiles()) {
load(f, file, ""); load(f, file, "", executor);
} }
} else { } else {
// bunch of files // bunch of files
for (File f : files) { for (File f : files) {
load(f, f.getParentFile(), ""); load(f, f.getParentFile(), "", executor);
} }
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
// supposed to happen if background execution was aborted // supposed to happen if background execution was aborted
} finally {
executor.shutdown();
} }
} }
protected void load(File file, File column, String prefix) throws InterruptedException { protected void load(File file, File root, String prefix, Executor executor) throws InterruptedException {
if (Thread.interrupted()) if (Thread.interrupted())
throw new InterruptedException(); throw new InterruptedException();
if (file.isDirectory()) { if (file.isDirectory()) {
// load all files in the file tree // load all files in the file tree
String newPrefix = prefix + file.getName() + "/"; String newPrefix = prefix + file.getName() + "/";
for (File f : file.listFiles()) { for (File f : file.listFiles()) {
load(f, column, newPrefix); load(f, root, newPrefix, executor);
} }
} else if (file.isFile()) { } else if (file.isFile()) {
publish(new ChecksumTableModel.ChecksumCell(prefix + file.getName(), checksumComputationService.schedule(file, column), column)); ChecksumComputationTask task = new ChecksumComputationTask(file);
publish(new ChecksumCell(prefix + file.getName(), root, task));
executor.execute(task);
} }
} }
} }

View File

@ -3,6 +3,8 @@ package net.sourceforge.filebot.ui.panel.sfv;
import java.awt.Component; import java.awt.Component;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JTable; import javax.swing.JTable;
@ -10,17 +12,20 @@ import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableCellRenderer;
import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.ResourceManager;
import net.sourceforge.filebot.ui.panel.sfv.ChecksumRow.State;
class StateIconTableCellRenderer extends DefaultTableCellRenderer { class StateIconTableCellRenderer extends DefaultTableCellRenderer {
private Icon warning = ResourceManager.getIcon("status.warning"); private final Map<State, Icon> icons = new EnumMap<State, Icon>(State.class);
private Icon error = ResourceManager.getIcon("status.error");
private Icon unknown = ResourceManager.getIcon("status.unknown");
private Icon ok = ResourceManager.getIcon("status.ok");
public StateIconTableCellRenderer() { public StateIconTableCellRenderer() {
icons.put(State.UNKNOWN, ResourceManager.getIcon("status.unknown"));
icons.put(State.OK, ResourceManager.getIcon("status.ok"));
icons.put(State.WARNING, ResourceManager.getIcon("status.warning"));
icons.put(State.ERROR, ResourceManager.getIcon("status.error"));
setVerticalAlignment(SwingConstants.CENTER); setVerticalAlignment(SwingConstants.CENTER);
setHorizontalAlignment(SwingConstants.CENTER); setHorizontalAlignment(SwingConstants.CENTER);
} }
@ -30,22 +35,7 @@ class StateIconTableCellRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, null, isSelected, false, row, column); super.getTableCellRendererComponent(table, null, isSelected, false, row, column);
ChecksumRow.State state = (ChecksumRow.State) value; setIcon(icons.get(value));
switch (state) {
case OK:
setIcon(ok);
break;
case ERROR:
setIcon(error);
break;
case WARNING:
setIcon(warning);
break;
case UNKNOWN:
setIcon(unknown);
break;
}
return this; return this;
} }

View File

@ -2,6 +2,8 @@
package net.sourceforge.filebot.ui.panel.sfv; package net.sourceforge.filebot.ui.panel.sfv;
import static net.sourceforge.filebot.ui.panel.sfv.ChecksumComputationService.TASK_COUNT_PROPERTY;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
@ -9,6 +11,8 @@ import javax.swing.BorderFactory;
import javax.swing.Box; import javax.swing.Box;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
import javax.swing.JProgressBar; import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import net.sourceforge.tuned.ui.TunedUtilities; import net.sourceforge.tuned.ui.TunedUtilities;
@ -19,13 +23,13 @@ class TotalProgressPanel extends Box {
private final JProgressBar progressBar = new JProgressBar(0, 0); private final JProgressBar progressBar = new JProgressBar(0, 0);
private final ChecksumComputationService checksumComputationService; private final ChecksumComputationService service;
public TotalProgressPanel(ChecksumComputationService checksumComputationService) { public TotalProgressPanel(ChecksumComputationService checksumComputationService) {
super(BoxLayout.Y_AXIS); super(BoxLayout.Y_AXIS);
this.checksumComputationService = checksumComputationService; this.service = checksumComputationService;
// invisible by default // invisible by default
setVisible(false); setVisible(false);
@ -38,40 +42,49 @@ class TotalProgressPanel extends Box {
add(progressBar); add(progressBar);
checksumComputationService.addPropertyChangeListener(progressListener); checksumComputationService.addPropertyChangeListener(TASK_COUNT_PROPERTY, progressListener);
} }
private PropertyChangeListener progressListener = new PropertyChangeListener() { private final PropertyChangeListener progressListener = new PropertyChangeListener() {
private Timer setVisibleTimer;
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
final int completedTaskCount = service.getCompletedTaskCount();
final int totalTaskCount = service.getTotalTaskCount();
String property = evt.getPropertyName(); // invoke on EDT
SwingUtilities.invokeLater(new Runnable() {
if (property == ChecksumComputationService.ACTIVE_PROPERTY) {
Boolean active = (Boolean) evt.getNewValue();
if (active) { @Override
TunedUtilities.invokeLater(millisToSetVisible, new Runnable() { public void run() {
if (completedTaskCount < totalTaskCount) {
@Override if (setVisibleTimer == null) {
public void run() { setVisibleTimer = TunedUtilities.invokeLater(millisToSetVisible, new Runnable() {
setVisible(checksumComputationService.isActive());
@Override
public void run() {
setVisible(service.getTaskCount() > service.getCompletedTaskCount());
}
});
} }
}); } else {
} else { if (setVisibleTimer != null) {
// hide when not active setVisibleTimer.stop();
setVisible(false); setVisibleTimer = null;
} }
} else if (property == ChecksumComputationService.REMAINING_TASK_COUNT_PROPERTY) {
// hide when not active
int taskCount = checksumComputationService.getActiveSessionTaskCount(); setVisible(false);
int progress = taskCount - checksumComputationService.getRemainingTaskCount(); }
progressBar.setValue(progress); progressBar.setValue(completedTaskCount);
progressBar.setMaximum(taskCount); progressBar.setMaximum(totalTaskCount);
progressBar.setString(progressBar.getValue() + " / " + progressBar.getMaximum()); progressBar.setString(completedTaskCount + " / " + totalTaskCount);
} };
});
} }
}; };

View File

@ -9,7 +9,8 @@ import java.util.EventListener;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.JScrollPane; import net.sourceforge.filebot.web.SubtitleDescriptor;
import net.sourceforge.tuned.DownloadTask;
import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.EventList;
@ -24,7 +25,33 @@ public class SubtitlePackagePanel extends JComponent {
public SubtitlePackagePanel() { public SubtitlePackagePanel() {
setLayout(new BorderLayout()); setLayout(new BorderLayout());
add(new JScrollPane(createList()), BorderLayout.CENTER); add(createList(), BorderLayout.CENTER);
model.add(new SubtitlePackage(new SubtitleDescriptor() {
@Override
public DownloadTask createDownloadTask() {
return null;
}
@Override
public String getArchiveType() {
return ArchiveType.ZIP.getExtension();
}
@Override
public String getLanguageName() {
return "english";
}
@Override
public String getName() {
return "Firefly 1x01 The Train Job.srt";
}
}));
} }
@ -33,11 +60,15 @@ public class SubtitlePackagePanel extends JComponent {
} }
protected JList createList() { protected JComponent createList() {
ObservableElementList<SubtitlePackage> observableList = new ObservableElementList<SubtitlePackage>(model, new SubtitlePackageConnector()); ObservableElementList<SubtitlePackage> observableList = new ObservableElementList<SubtitlePackage>(model, new SubtitlePackageConnector());
JList list = new JList(new EventListModel<SubtitlePackage>(observableList)); JList list = new JList(new EventListModel<SubtitlePackage>(observableList));
list.setCellRenderer(new SubtitleCellRenderer());
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(-1);
return list; return list;
} }
@ -51,9 +82,14 @@ public class SubtitlePackagePanel extends JComponent {
private ObservableElementList<SubtitlePackage> list = null; private ObservableElementList<SubtitlePackage> list = null;
public EventListener installListener(SubtitlePackage element) { public EventListener installListener(final SubtitlePackage element) {
PropertyChangeListener listener = new SubtitlePackageListener(element); PropertyChangeListener listener = new PropertyChangeListener() {
element.getDownloadTask().addPropertyChangeListener(listener);
@Override
public void propertyChange(PropertyChangeEvent evt) {
list.elementChanged(element);
}
};
return listener; return listener;
} }
@ -64,24 +100,10 @@ public class SubtitlePackagePanel extends JComponent {
} }
public void setObservableElementList(ObservableElementList<SubtitlePackage> list) { @SuppressWarnings("unchecked")
this.list = list; @Override
} public void setObservableElementList(ObservableElementList<? extends SubtitlePackage> list) {
this.list = (ObservableElementList<SubtitlePackage>) list;
protected class SubtitlePackageListener implements PropertyChangeListener {
private final SubtitlePackage subtitlePackage;
public SubtitlePackageListener(SubtitlePackage subtitlePackage) {
this.subtitlePackage = subtitlePackage;
}
public void propertyChange(PropertyChangeEvent evt) {
list.elementChanged(subtitlePackage);
}
} }
} }

View File

@ -8,6 +8,8 @@ import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
@ -76,7 +78,7 @@ public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferab
} }
private class BackgroundWorker extends SwingWorker<Object, V> { private class BackgroundWorker extends SwingWorker<Void, V> {
private final List<File> files; private final List<File> files;
@ -87,8 +89,13 @@ public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferab
@Override @Override
protected Object doInBackground() { protected Void doInBackground() {
load(files); try {
load(files);
} catch (Exception e) {
Logger.getLogger("global").log(Level.WARNING, e.getMessage(), e);
}
return null; return null;
} }

View File

@ -23,7 +23,7 @@ public abstract class FileTransferablePolicy extends TransferablePolicy {
/** /**
* Pattern that will match Windows (\r\n), Unix (\n) and Mac (\r) line separators. * Pattern that will match Windows (\r\n), Unix (\n) and Mac (\r) line separators.
*/ */
public static final Pattern LINE_SEPARATOR = Pattern.compile("\r?\n|\r\n?"); public static final Pattern LINE_SEPARATOR = Pattern.compile("\r\n|[\r\n]");
@Override @Override

View File

@ -6,7 +6,7 @@ import java.awt.datatransfer.Transferable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.util.Formatter;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.TransferHandler; import javax.swing.TransferHandler;
@ -17,7 +17,7 @@ public abstract class TextFileExportHandler implements TransferableExportHandler
public abstract boolean canExport(); public abstract boolean canExport();
public abstract void export(PrintWriter out); public abstract void export(Formatter out);
public abstract String getDefaultFileName(); public abstract String getDefaultFileName();
@ -28,7 +28,7 @@ public abstract class TextFileExportHandler implements TransferableExportHandler
PrintWriter out = new PrintWriter(file, "UTF-8"); PrintWriter out = new PrintWriter(file, "UTF-8");
try { try {
export(out); export(new Formatter(out));
} finally { } finally {
out.close(); out.close();
} }
@ -47,8 +47,8 @@ public abstract class TextFileExportHandler implements TransferableExportHandler
@Override @Override
public Transferable createTransferable(JComponent c) { public Transferable createTransferable(JComponent c) {
// get transfer data // get transfer data
StringWriter buffer = new StringWriter(); StringBuilder buffer = new StringBuilder();
export(new PrintWriter(buffer)); export(new Formatter(buffer));
return new LazyTextFileTransferable(buffer.toString(), getDefaultFileName()); return new LazyTextFileTransferable(buffer.toString(), getDefaultFileName());
} }

View File

@ -14,7 +14,6 @@ import java.net.URI;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -135,7 +134,7 @@ public class AnidbClient implements EpisodeListClient {
@Override @Override
public Collection<Episode> getEpisodeList(SearchResult searchResult, int season) throws Exception { public List<Episode> getEpisodeList(SearchResult searchResult, int season) throws Exception {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -3,23 +3,23 @@ package net.sourceforge.filebot.web;
import java.net.URI; import java.net.URI;
import java.util.Collection; import java.util.List;
import javax.swing.Icon; import javax.swing.Icon;
public interface EpisodeListClient { public interface EpisodeListClient {
public Collection<SearchResult> search(String query) throws Exception; public List<SearchResult> search(String query) throws Exception;
public boolean hasSingleSeasonSupport(); public boolean hasSingleSeasonSupport();
public Collection<Episode> getEpisodeList(SearchResult searchResult) throws Exception; public List<Episode> getEpisodeList(SearchResult searchResult) throws Exception;
public Collection<Episode> getEpisodeList(SearchResult searchResult, int season) throws Exception; public List<Episode> getEpisodeList(SearchResult searchResult, int season) throws Exception;
public URI getEpisodeListLink(SearchResult searchResult); public URI getEpisodeListLink(SearchResult searchResult);

View File

@ -3,7 +3,7 @@ package net.sourceforge.filebot.web;
import java.net.URI; import java.net.URI;
import java.util.Collection; import java.util.List;
import java.util.Locale; import java.util.Locale;
import javax.swing.Icon; import javax.swing.Icon;
@ -11,10 +11,10 @@ import javax.swing.Icon;
public interface SubtitleClient { public interface SubtitleClient {
public Collection<SearchResult> search(String query) throws Exception; public List<SearchResult> search(String query) throws Exception;
public Collection<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, Locale language) throws Exception; public List<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, Locale language) throws Exception;
public URI getSubtitleListLink(SearchResult searchResult, Locale language); public URI getSubtitleListLink(SearchResult searchResult, Locale language);

View File

@ -2,7 +2,7 @@
package net.sourceforge.tuned; package net.sourceforge.tuned;
public final class ExceptionUtil { public final class ExceptionUtilities {
public static Throwable getRootCause(Throwable t) { public static Throwable getRootCause(Throwable t) {
while (t.getCause() != null) { while (t.getCause() != null) {
@ -25,7 +25,7 @@ public final class ExceptionUtil {
/** /**
* Dummy constructor to prevent instantiation. * Dummy constructor to prevent instantiation.
*/ */
private ExceptionUtil() { private ExceptionUtilities() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -282,7 +282,7 @@ public class PreferencesMap<T> implements Map<String, T> {
return constructor.newInstance(stringValue); return constructor.newInstance(stringValue);
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
// try to throw the cause directly, e.g. NumberFormatException // try to throw the cause directly, e.g. NumberFormatException
throw ExceptionUtil.asRuntimeException(e.getCause()); throw ExceptionUtilities.asRuntimeException(e.getCause());
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -7,7 +7,7 @@ import java.util.Arrays;
import javax.swing.Icon; import javax.swing.Icon;
import net.sourceforge.tuned.ExceptionUtil; import net.sourceforge.tuned.ExceptionUtilities;
/** /**
@ -73,7 +73,7 @@ public class SimpleLabelProvider<T> implements LabelProvider<T> {
try { try {
return (String) getTextMethod.invoke(value); return (String) getTextMethod.invoke(value);
} catch (Exception e) { } catch (Exception e) {
throw ExceptionUtil.asRuntimeException(e); throw ExceptionUtilities.asRuntimeException(e);
} }
} }
@ -83,7 +83,7 @@ public class SimpleLabelProvider<T> implements LabelProvider<T> {
try { try {
return (Icon) getIconMethod.invoke(value); return (Icon) getIconMethod.invoke(value);
} catch (Exception e) { } catch (Exception e) {
throw ExceptionUtil.asRuntimeException(e); throw ExceptionUtilities.asRuntimeException(e);
} }
} }

View File

@ -24,7 +24,7 @@ import javax.swing.KeyStroke;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.Timer; import javax.swing.Timer;
import net.sourceforge.tuned.ExceptionUtil; import net.sourceforge.tuned.ExceptionUtilities;
public final class TunedUtilities { public final class TunedUtilities {
@ -120,7 +120,7 @@ public final class TunedUtilities {
return listener; return listener;
} catch (Exception e) { } catch (Exception e) {
throw ExceptionUtil.asRuntimeException(e); throw ExceptionUtilities.asRuntimeException(e);
} }
} }
@ -139,7 +139,7 @@ public final class TunedUtilities {
try { try {
firePropertyChange.invoke(target, evt.getPropertyName(), evt.getOldValue(), evt.getNewValue()); firePropertyChange.invoke(target, evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
} catch (Exception e) { } catch (Exception e) {
throw ExceptionUtil.asRuntimeException(e); throw ExceptionUtilities.asRuntimeException(e);
} }
} }
} }