* Format Source

This commit is contained in:
Reinhard Pointner 2015-07-25 22:47:19 +00:00
parent 38a046bf2d
commit 8299e849aa
194 changed files with 2700 additions and 2622 deletions

View File

@ -6,7 +6,7 @@ import java.io.File;
public interface RenameAction { public interface RenameAction {
File rename(File from, File to) throws Exception; File rename(File from, File to) throws Exception;
} }

View File

@ -56,7 +56,7 @@ public final class ResourceManager {
/** /**
* Get the URL of an image resource in this jar. Image must be located in <code>resources/</code> and the file type is assumed to be png. * Get the URL of an image resource in this jar. Image must be located in <code>resources/</code> and the file type is assumed to be png.
* *
* @param name * @param name
* simple name of the resource (without extension) * simple name of the resource (without extension)
* @return URL of the resource or null if resource does not exist * @return URL of the resource or null if resource does not exist

View File

@ -35,6 +35,7 @@ public class ApacheVFS implements ArchiveExtractor, Closeable {
this.archive = fsm.createFileSystem(fsm.toFileObject(file)); this.archive = fsm.createFileSystem(fsm.toFileObject(file));
} }
@Override
public List<FileInfo> listFiles() throws Exception { public List<FileInfo> listFiles() throws Exception {
List<FileInfo> paths = new ArrayList<FileInfo>(); List<FileInfo> paths = new ArrayList<FileInfo>();
for (FileObject it : archive.findFiles(ALL_FILES)) { for (FileObject it : archive.findFiles(ALL_FILES)) {
@ -46,10 +47,12 @@ public class ApacheVFS implements ArchiveExtractor, Closeable {
return paths; return paths;
} }
@Override
public void extract(File outputDir) throws Exception { public void extract(File outputDir) throws Exception {
extract(outputDir, null); extract(outputDir, null);
} }
@Override
public void extract(File outputDir, FileFilter filter) throws Exception { public void extract(File outputDir, FileFilter filter) throws Exception {
fsm.toFileObject(outputDir).copyFrom(archive, filter == null ? ALL_FILES : new FileFilterSelector(filter)); fsm.toFileObject(outputDir).copyFrom(archive, filter == null ? ALL_FILES : new FileFilterSelector(filter));
} }

View File

@ -18,33 +18,34 @@ import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveOpenCallback, Closeable { class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveOpenCallback, Closeable {
/** /**
* Cache for opened file streams * Cache for opened file streams
*/ */
private Map<String, RandomAccessFile> openedRandomAccessFileList = new HashMap<String, RandomAccessFile>(); private Map<String, RandomAccessFile> openedRandomAccessFileList = new HashMap<String, RandomAccessFile>();
/** /**
* Name of the last volume returned by {@link #getStream(String)} * Name of the last volume returned by {@link #getStream(String)}
*/ */
private String name; private String name;
/** /**
* This method should at least provide the name of the last * This method should at least provide the name of the last
* opened volume (propID=PropID.NAME). * opened volume (propID=PropID.NAME).
* *
* @see IArchiveOpenVolumeCallback#getProperty(PropID) * @see IArchiveOpenVolumeCallback#getProperty(PropID)
*/ */
@Override
public Object getProperty(PropID propID) throws SevenZipException { public Object getProperty(PropID propID) throws SevenZipException {
switch (propID) { switch (propID) {
case NAME: case NAME:
return name; return name;
} }
return null; return null;
} }
/** /**
* The name of the required volume will be calculated out of the * The name of the required volume will be calculated out of the
* name of the first volume and a volume index. In case of RAR file, * name of the first volume and a volume index. In case of RAR file,
@ -56,6 +57,7 @@ class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveO
* <li>test.part001.rar - first part of a multi-part archive. "00" indicates, that at least 100 volumes must exist.</li> * <li>test.part001.rar - first part of a multi-part archive. "00" indicates, that at least 100 volumes must exist.</li>
* </ul> * </ul>
*/ */
@Override
public IInStream getStream(String filename) throws SevenZipException { public IInStream getStream(String filename) throws SevenZipException {
try { try {
// We use caching of opened streams, so check cache first // We use caching of opened streams, so check cache first
@ -64,19 +66,19 @@ class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveO
// Move the file pointer back to the beginning // Move the file pointer back to the beginning
// in order to emulating new stream // in order to emulating new stream
randomAccessFile.seek(0); randomAccessFile.seek(0);
// Save current volume name in case getProperty() will be called // Save current volume name in case getProperty() will be called
name = filename; name = filename;
return new RandomAccessFileInStream(randomAccessFile); return new RandomAccessFileInStream(randomAccessFile);
} }
// Nothing useful in cache. Open required volume. // Nothing useful in cache. Open required volume.
randomAccessFile = new RandomAccessFile(filename, "r"); randomAccessFile = new RandomAccessFile(filename, "r");
// Put new stream in the cache // Put new stream in the cache
openedRandomAccessFileList.put(filename, randomAccessFile); openedRandomAccessFileList.put(filename, randomAccessFile);
// Save current volume name in case getProperty() will be called // Save current volume name in case getProperty() will be called
name = filename; name = filename;
return new RandomAccessFileInStream(randomAccessFile); return new RandomAccessFileInStream(randomAccessFile);
@ -85,7 +87,7 @@ class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveO
// 1. never exists. 7-Zip doesn't know how many volumes should // 1. never exists. 7-Zip doesn't know how many volumes should
// exist, so it have to try each volume. // exist, so it have to try each volume.
// 2. should be there, but doesn't. This is an error case. // 2. should be there, but doesn't. This is an error case.
// Since normal and error cases are possible, // Since normal and error cases are possible,
// we can't throw an error message // we can't throw an error message
return null; // We return always null in this case return null; // We return always null in this case
@ -93,25 +95,26 @@ class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveO
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
/** /**
* Close all opened streams * Close all opened streams
*/ */
@Override
public void close() throws IOException { public void close() throws IOException {
for (RandomAccessFile file : openedRandomAccessFileList.values()) { for (RandomAccessFile file : openedRandomAccessFileList.values()) {
file.close(); file.close();
} }
} }
@Override @Override
public void setCompleted(Long files, Long bytes) throws SevenZipException { public void setCompleted(Long files, Long bytes) throws SevenZipException {
} }
@Override @Override
public void setTotal(Long files, Long bytes) throws SevenZipException { public void setTotal(Long files, Long bytes) throws SevenZipException {
} }
} }

View File

@ -16,48 +16,51 @@ import net.sf.sevenzipjbinding.SevenZipException;
class ExtractCallback implements IArchiveExtractCallback { class ExtractCallback implements IArchiveExtractCallback {
private ISevenZipInArchive inArchive; private ISevenZipInArchive inArchive;
private ExtractOutProvider extractOut; private ExtractOutProvider extractOut;
private ExtractOutStream output = null; private ExtractOutStream output = null;
public ExtractCallback(ISevenZipInArchive inArchive, ExtractOutProvider extractOut) { public ExtractCallback(ISevenZipInArchive inArchive, ExtractOutProvider extractOut) {
this.inArchive = inArchive; this.inArchive = inArchive;
this.extractOut = extractOut; this.extractOut = extractOut;
} }
@Override
public ISequentialOutStream getStream(int index, ExtractAskMode extractAskMode) throws SevenZipException { public ISequentialOutStream getStream(int index, ExtractAskMode extractAskMode) throws SevenZipException {
if (extractAskMode != ExtractAskMode.EXTRACT) { if (extractAskMode != ExtractAskMode.EXTRACT) {
return null; return null;
} }
boolean isFolder = (Boolean) inArchive.getProperty(index, PropID.IS_FOLDER); boolean isFolder = (Boolean) inArchive.getProperty(index, PropID.IS_FOLDER);
if (isFolder) { if (isFolder) {
return null; return null;
} }
String path = (String) inArchive.getProperty(index, PropID.PATH); String path = (String) inArchive.getProperty(index, PropID.PATH);
try { try {
OutputStream target = extractOut.getStream(new File(path)); OutputStream target = extractOut.getStream(new File(path));
if (target == null) { if (target == null) {
return null; return null;
} }
output = new ExtractOutStream(target); output = new ExtractOutStream(target);
return output; return output;
} catch (IOException e) { } catch (IOException e) {
throw new SevenZipException(e); throw new SevenZipException(e);
} }
} }
@Override
public void prepareOperation(ExtractAskMode extractAskMode) throws SevenZipException { public void prepareOperation(ExtractAskMode extractAskMode) throws SevenZipException {
} }
@Override
public void setOperationResult(ExtractOperationResult extractOperationResult) throws SevenZipException { public void setOperationResult(ExtractOperationResult extractOperationResult) throws SevenZipException {
if (output != null) { if (output != null) {
try { try {
@ -68,18 +71,20 @@ class ExtractCallback implements IArchiveExtractCallback {
output = null; output = null;
} }
} }
if (extractOperationResult != ExtractOperationResult.OK) { if (extractOperationResult != ExtractOperationResult.OK) {
throw new SevenZipException("Extraction Error: " + extractOperationResult); throw new SevenZipException("Extraction Error: " + extractOperationResult);
} }
} }
@Override
public void setCompleted(long completeValue) throws SevenZipException { public void setCompleted(long completeValue) throws SevenZipException {
} }
@Override
public void setTotal(long total) throws SevenZipException { public void setTotal(long total) throws SevenZipException {
} }
} }

View File

@ -8,7 +8,7 @@ import java.io.OutputStream;
public interface ExtractOutProvider { public interface ExtractOutProvider {
OutputStream getStream(File archivePath) throws IOException; OutputStream getStream(File archivePath) throws IOException;
} }

View File

@ -11,15 +11,15 @@ import net.sf.sevenzipjbinding.SevenZipException;
class ExtractOutStream implements ISequentialOutStream, Closeable { class ExtractOutStream implements ISequentialOutStream, Closeable {
private OutputStream out; private OutputStream out;
public ExtractOutStream(OutputStream out) { public ExtractOutStream(OutputStream out) {
this.out = out; this.out = out;
} }
@Override @Override
public int write(byte[] data) throws SevenZipException { public int write(byte[] data) throws SevenZipException {
try { try {
@ -29,11 +29,11 @@ class ExtractOutStream implements ISequentialOutStream, Closeable {
} }
return data.length; // return amount of proceed data return data.length; // return amount of proceed data
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
out.close(); out.close();
} }
} }

View File

@ -61,6 +61,7 @@ public class SevenZipExecutable implements ArchiveExtractor {
} }
} }
@Override
public List<FileInfo> listFiles() throws IOException { public List<FileInfo> listFiles() throws IOException {
List<FileInfo> paths = new ArrayList<FileInfo>(); List<FileInfo> paths = new ArrayList<FileInfo>();
@ -81,11 +82,13 @@ public class SevenZipExecutable implements ArchiveExtractor {
return paths; return paths;
} }
@Override
public void extract(File outputDir) throws IOException { public void extract(File outputDir) throws IOException {
// e.g. 7z x -y -aos archive.7z // e.g. 7z x -y -aos archive.7z
execute(get7zCommand(), "x", "-y", "-aos", archive.getPath(), "-o" + outputDir.getCanonicalPath()); execute(get7zCommand(), "x", "-y", "-aos", archive.getPath(), "-o" + outputDir.getCanonicalPath());
} }
@Override
public void extract(File outputDir, FileFilter filter) throws IOException { public void extract(File outputDir, FileFilter filter) throws IOException {
// e.g. 7z x -y -aos archive.7z file.txt image.png info.nfo // e.g. 7z x -y -aos archive.7z file.txt image.png info.nfo
Stream<String> command = Stream.of(get7zCommand(), "x", "-y", "-aos", archive.getPath(), "-o" + outputDir.getCanonicalPath()); Stream<String> command = Stream.of(get7zCommand(), "x", "-y", "-aos", archive.getPath(), "-o" + outputDir.getCanonicalPath());

View File

@ -14,15 +14,15 @@ import com.sun.jna.Platform;
public class SevenZipLoader { public class SevenZipLoader {
private static boolean nativeLibrariesLoaded = false; private static boolean nativeLibrariesLoaded = false;
private static synchronized void requireNativeLibraries() throws SevenZipNativeInitializationException { private static synchronized void requireNativeLibraries() throws SevenZipNativeInitializationException {
if (nativeLibrariesLoaded) { if (nativeLibrariesLoaded) {
return; return;
} }
// initialize 7z-JBinding native libs // initialize 7z-JBinding native libs
try { try {
try { try {
@ -32,7 +32,7 @@ public class SevenZipLoader {
} catch (Throwable e) { } catch (Throwable e) {
Logger.getLogger(SevenZipLoader.class.getName()).warning("Failed to preload library: " + e); Logger.getLogger(SevenZipLoader.class.getName()).warning("Failed to preload library: " + e);
} }
System.loadLibrary("7-Zip-JBinding"); System.loadLibrary("7-Zip-JBinding");
SevenZip.initLoadedLibraries(); // NATIVE LIBS MUST BE LOADED WITH SYSTEM CLASSLOADER SevenZip.initLoadedLibraries(); // NATIVE LIBS MUST BE LOADED WITH SYSTEM CLASSLOADER
nativeLibrariesLoaded = true; nativeLibrariesLoaded = true;
@ -40,17 +40,17 @@ public class SevenZipLoader {
throw new SevenZipNativeInitializationException("Failed to load 7z-JBinding: " + e.getMessage(), e); throw new SevenZipNativeInitializationException("Failed to load 7z-JBinding: " + e.getMessage(), e);
} }
} }
public static ISevenZipInArchive open(IInStream stream, IArchiveOpenCallback callback) throws Exception { public static ISevenZipInArchive open(IInStream stream, IArchiveOpenCallback callback) throws Exception {
// initialize 7-Zip-JBinding // initialize 7-Zip-JBinding
requireNativeLibraries(); requireNativeLibraries();
if (callback == null) { if (callback == null) {
return SevenZip.openInArchive(null, stream); return SevenZip.openInArchive(null, stream);
} else { } else {
return SevenZip.openInArchive(null, stream, callback); return SevenZip.openInArchive(null, stream, callback);
} }
} }
} }

View File

@ -60,6 +60,7 @@ public class SevenZipNativeBindings implements ArchiveExtractor, Closeable {
return item; return item;
} }
@Override
public List<FileInfo> listFiles() throws SevenZipException { public List<FileInfo> listFiles() throws SevenZipException {
List<FileInfo> paths = new ArrayList<FileInfo>(); List<FileInfo> paths = new ArrayList<FileInfo>();

View File

@ -13,66 +13,66 @@ import net.sf.sevenzipjbinding.SevenZipException;
public class VolumedArchiveInStream implements IInStream { public class VolumedArchiveInStream implements IInStream {
private static final String FIRST_VOLUME_POSTFIX = ".001"; private static final String FIRST_VOLUME_POSTFIX = ".001";
private long absoluteOffset; private long absoluteOffset;
private long absoluteLength = -1; private long absoluteLength = -1;
private int currentIndex = -1; private int currentIndex = -1;
private IInStream currentInStream; private IInStream currentInStream;
private long currentVolumeOffset; private long currentVolumeOffset;
private long currentVolumeLength; private long currentVolumeLength;
private List<Long> volumePositions = new ArrayList<Long>(); private List<Long> volumePositions = new ArrayList<Long>();
private final IArchiveOpenVolumeCallback archiveOpenVolumeCallback; private final IArchiveOpenVolumeCallback archiveOpenVolumeCallback;
private String cuttedVolumeFilename; private String cuttedVolumeFilename;
public VolumedArchiveInStream(IArchiveOpenVolumeCallback archiveOpenVolumeCallback) throws SevenZipException { public VolumedArchiveInStream(IArchiveOpenVolumeCallback archiveOpenVolumeCallback) throws SevenZipException {
this((String) archiveOpenVolumeCallback.getProperty(PropID.NAME), archiveOpenVolumeCallback); this((String) archiveOpenVolumeCallback.getProperty(PropID.NAME), archiveOpenVolumeCallback);
} }
public VolumedArchiveInStream(String firstVolumeFilename, IArchiveOpenVolumeCallback archiveOpenVolumeCallback) throws SevenZipException { public VolumedArchiveInStream(String firstVolumeFilename, IArchiveOpenVolumeCallback archiveOpenVolumeCallback) throws SevenZipException {
this.archiveOpenVolumeCallback = archiveOpenVolumeCallback; this.archiveOpenVolumeCallback = archiveOpenVolumeCallback;
volumePositions.add(Long.valueOf(0)); volumePositions.add(Long.valueOf(0));
if (!firstVolumeFilename.endsWith(FIRST_VOLUME_POSTFIX)) { if (!firstVolumeFilename.endsWith(FIRST_VOLUME_POSTFIX)) {
throw new SevenZipException("The first volume filename '" + firstVolumeFilename + "' don't ends with the postfix: '" + FIRST_VOLUME_POSTFIX + "'. Can't proceed"); throw new SevenZipException("The first volume filename '" + firstVolumeFilename + "' don't ends with the postfix: '" + FIRST_VOLUME_POSTFIX + "'. Can't proceed");
} }
cuttedVolumeFilename = firstVolumeFilename.substring(0, firstVolumeFilename.length() - 3); cuttedVolumeFilename = firstVolumeFilename.substring(0, firstVolumeFilename.length() - 3);
openVolume(1, true); openVolume(1, true);
} }
private void openVolume(int index, boolean seekToBegin) throws SevenZipException { private void openVolume(int index, boolean seekToBegin) throws SevenZipException {
if (currentIndex == index) { if (currentIndex == index) {
return; return;
} }
for (int i = volumePositions.size(); i < index && absoluteLength == -1; i++) { for (int i = volumePositions.size(); i < index && absoluteLength == -1; i++) {
openVolume(i, false); openVolume(i, false);
} }
if (absoluteLength != -1 && volumePositions.size() <= index) { if (absoluteLength != -1 && volumePositions.size() <= index) {
return; return;
} }
String volumeFilename = cuttedVolumeFilename + MessageFormat.format("{0,number,000}", Integer.valueOf(index)); String volumeFilename = cuttedVolumeFilename + MessageFormat.format("{0,number,000}", Integer.valueOf(index));
// Get new IInStream // Get new IInStream
IInStream newInStream = archiveOpenVolumeCallback.getStream(volumeFilename); IInStream newInStream = archiveOpenVolumeCallback.getStream(volumeFilename);
if (newInStream == null) { if (newInStream == null) {
absoluteLength = volumePositions.get(volumePositions.size() - 1).longValue(); absoluteLength = volumePositions.get(volumePositions.size() - 1).longValue();
return; return;
} }
currentInStream = newInStream; currentInStream = newInStream;
if (volumePositions.size() == index) { if (volumePositions.size() == index) {
// Determine volume size // Determine volume size
currentVolumeLength = currentInStream.seek(0, SEEK_END); currentVolumeLength = currentInStream.seek(0, SEEK_END);
@ -80,23 +80,23 @@ public class VolumedArchiveInStream implements IInStream {
throw new RuntimeException("Volume " + index + " is empty"); throw new RuntimeException("Volume " + index + " is empty");
} }
volumePositions.add(Long.valueOf(volumePositions.get(index - 1).longValue() + currentVolumeLength)); volumePositions.add(Long.valueOf(volumePositions.get(index - 1).longValue() + currentVolumeLength));
if (seekToBegin) { if (seekToBegin) {
currentInStream.seek(0, SEEK_SET); currentInStream.seek(0, SEEK_SET);
} }
} else { } else {
currentVolumeLength = volumePositions.get(index).longValue() - volumePositions.get(index - 1).longValue(); currentVolumeLength = volumePositions.get(index).longValue() - volumePositions.get(index - 1).longValue();
} }
if (seekToBegin) { if (seekToBegin) {
currentVolumeOffset = 0; currentVolumeOffset = 0;
absoluteOffset = volumePositions.get(index - 1).longValue(); absoluteOffset = volumePositions.get(index - 1).longValue();
} }
currentIndex = index; currentIndex = index;
} }
private void openVolumeToAbsoluteOffset() throws SevenZipException { private void openVolumeToAbsoluteOffset() throws SevenZipException {
int index = volumePositions.size() - 1; int index = volumePositions.size() - 1;
if (absoluteLength != -1 && absoluteOffset >= absoluteLength) { if (absoluteLength != -1 && absoluteOffset >= absoluteLength) {
@ -105,80 +105,80 @@ public class VolumedArchiveInStream implements IInStream {
while (volumePositions.get(index).longValue() > absoluteOffset) { while (volumePositions.get(index).longValue() > absoluteOffset) {
index--; index--;
} }
if (index < volumePositions.size() - 1) { if (index < volumePositions.size() - 1) {
openVolume(index + 1, false); openVolume(index + 1, false);
return; return;
} }
do { do {
index++; index++;
openVolume(index, false); openVolume(index, false);
} while ((absoluteLength == -1 || absoluteOffset < absoluteLength) && volumePositions.get(index).longValue() <= absoluteOffset); } while ((absoluteLength == -1 || absoluteOffset < absoluteLength) && volumePositions.get(index).longValue() <= absoluteOffset);
} }
@Override @Override
public long seek(long offset, int seekOrigin) throws SevenZipException { public long seek(long offset, int seekOrigin) throws SevenZipException {
long newOffset; long newOffset;
boolean proceedWithSeek = false; boolean proceedWithSeek = false;
switch (seekOrigin) { switch (seekOrigin) {
case SEEK_SET: case SEEK_SET:
newOffset = offset; newOffset = offset;
break; break;
case SEEK_CUR: case SEEK_CUR:
newOffset = absoluteOffset + offset; newOffset = absoluteOffset + offset;
break; break;
case SEEK_END: case SEEK_END:
if (absoluteLength == -1) { if (absoluteLength == -1) {
openVolume(Integer.MAX_VALUE, false); openVolume(Integer.MAX_VALUE, false);
proceedWithSeek = true; proceedWithSeek = true;
} }
newOffset = absoluteLength + offset; newOffset = absoluteLength + offset;
break; break;
default: default:
throw new RuntimeException("Seek: unknown origin: " + seekOrigin); throw new RuntimeException("Seek: unknown origin: " + seekOrigin);
} }
if (newOffset == absoluteOffset && !proceedWithSeek) { if (newOffset == absoluteOffset && !proceedWithSeek) {
return newOffset; return newOffset;
} }
absoluteOffset = newOffset; absoluteOffset = newOffset;
openVolumeToAbsoluteOffset(); openVolumeToAbsoluteOffset();
if (absoluteLength != -1 && absoluteLength <= absoluteOffset) { if (absoluteLength != -1 && absoluteLength <= absoluteOffset) {
absoluteOffset = absoluteLength; absoluteOffset = absoluteLength;
return absoluteLength; return absoluteLength;
} }
currentVolumeOffset = absoluteOffset - volumePositions.get(currentIndex - 1).longValue(); currentVolumeOffset = absoluteOffset - volumePositions.get(currentIndex - 1).longValue();
currentInStream.seek(currentVolumeOffset, SEEK_SET); currentInStream.seek(currentVolumeOffset, SEEK_SET);
return newOffset; return newOffset;
} }
@Override @Override
public int read(byte[] data) throws SevenZipException { public int read(byte[] data) throws SevenZipException {
if (absoluteLength != -1 && absoluteOffset >= absoluteLength) { if (absoluteLength != -1 && absoluteOffset >= absoluteLength) {
return 0; return 0;
} }
int read = currentInStream.read(data); int read = currentInStream.read(data);
absoluteOffset += read; absoluteOffset += read;
currentVolumeOffset += read; currentVolumeOffset += read;
if (currentVolumeOffset >= currentVolumeLength) { if (currentVolumeOffset >= currentVolumeLength) {
openVolume(currentIndex + 1, true); openVolume(currentIndex + 1, true);
} }
return read; return read;
} }
} }

View File

@ -292,6 +292,7 @@ public class GroovyPad extends JFrame {
private class ConsoleOutputStream extends ByteArrayOutputStream { private class ConsoleOutputStream extends ByteArrayOutputStream {
@Override
public void flush() { public void flush() {
try { try {
String message = this.toString("UTF-8"); String message = this.toString("UTF-8");

View File

@ -57,7 +57,7 @@ public class ScriptShellMethods {
} }
public static List<File> listFiles(File self, Closure<?> closure) { public static List<File> listFiles(File self, Closure<?> closure) {
return (List<File>) DefaultGroovyMethods.findAll(FileUtilities.getChildren(self), closure); return DefaultGroovyMethods.findAll(FileUtilities.getChildren(self), closure);
} }
public static boolean isVideo(File self) { public static boolean isVideo(File self) {
@ -121,7 +121,7 @@ public class ScriptShellMethods {
List<File> files = FileUtilities.listFiles(roots); List<File> files = FileUtilities.listFiles(roots);
if (closure != null) { if (closure != null) {
files = (List<File>) DefaultGroovyMethods.findAll(files, closure); files = DefaultGroovyMethods.findAll(files, closure);
} }
return FileUtilities.sortByUniquePath(files); return FileUtilities.sortByUniquePath(files);
@ -144,7 +144,7 @@ public class ScriptShellMethods {
List<File> folders = FileUtilities.listFolders(roots); List<File> folders = FileUtilities.listFolders(roots);
if (closure != null) { if (closure != null) {
folders = (List<File>) DefaultGroovyMethods.findAll(folders, closure); folders = DefaultGroovyMethods.findAll(folders, closure);
} }
return FileUtilities.sortByUniquePath(folders); return FileUtilities.sortByUniquePath(folders);

View File

@ -15,18 +15,18 @@ import java.util.TreeSet;
public class AssociativeScriptObject extends GroovyObjectSupport implements Iterable<Entry<Object, Object>> { public class AssociativeScriptObject extends GroovyObjectSupport implements Iterable<Entry<Object, Object>> {
private final Map<Object, Object> properties; private final Map<Object, Object> properties;
public AssociativeScriptObject(Map<?, ?> properties) { public AssociativeScriptObject(Map<?, ?> properties) {
this.properties = new LenientLookup(properties); this.properties = new LenientLookup(properties);
} }
/** /**
* Get the property with the given name. * Get the property with the given name.
* *
* @param name * @param name
* the property name * the property name
* @param start * @param start
@ -36,77 +36,77 @@ public class AssociativeScriptObject extends GroovyObjectSupport implements Iter
public Object getProperty(String name) { public Object getProperty(String name) {
return properties.get(name); return properties.get(name);
} }
@Override @Override
public void setProperty(String name, Object value) { public void setProperty(String name, Object value) {
// ignore, object is immutable // ignore, object is immutable
} }
@Override @Override
public Iterator<Entry<Object, Object>> iterator() { public Iterator<Entry<Object, Object>> iterator() {
return properties.entrySet().iterator(); return properties.entrySet().iterator();
} }
@Override @Override
public String toString() { public String toString() {
// all the properties in alphabetic order // all the properties in alphabetic order
return new TreeSet<Object>(properties.keySet()).toString(); return new TreeSet<Object>(properties.keySet()).toString();
} }
/** /**
* Map allowing look-up of values by a fault-tolerant key as specified by the defining key. * Map allowing look-up of values by a fault-tolerant key as specified by the defining key.
* *
*/ */
private static class LenientLookup extends AbstractMap<Object, Object> { private static class LenientLookup extends AbstractMap<Object, Object> {
private final Map<String, Entry<?, ?>> lookup = new HashMap<String, Entry<?, ?>>(); private final Map<String, Entry<?, ?>> lookup = new HashMap<String, Entry<?, ?>>();
public LenientLookup(Map<?, ?> source) { public LenientLookup(Map<?, ?> source) {
// populate lookup map // populate lookup map
for (Entry<?, ?> entry : source.entrySet()) { for (Entry<?, ?> entry : source.entrySet()) {
lookup.put(definingKey(entry.getKey()), entry); lookup.put(definingKey(entry.getKey()), entry);
} }
} }
protected String definingKey(Object key) { protected String definingKey(Object key) {
// letters and digits are defining, everything else will be ignored // letters and digits are defining, everything else will be ignored
return key.toString().replaceAll("[^\\p{Alnum}]", "").toLowerCase(); return key.toString().replaceAll("[^\\p{Alnum}]", "").toLowerCase();
} }
@Override @Override
public boolean containsKey(Object key) { public boolean containsKey(Object key) {
return lookup.containsKey(definingKey(key)); return lookup.containsKey(definingKey(key));
} }
@Override @Override
public Object get(Object key) { public Object get(Object key) {
Entry<?, ?> entry = lookup.get(definingKey(key)); Entry<?, ?> entry = lookup.get(definingKey(key));
if (entry != null) if (entry != null)
return entry.getValue(); return entry.getValue();
return null; return null;
} }
@Override @Override
public Set<Entry<Object, Object>> entrySet() { public Set<Entry<Object, Object>> entrySet() {
return new AbstractSet<Entry<Object, Object>>() { return new AbstractSet<Entry<Object, Object>>() {
@Override @Override
public Iterator<Entry<Object, Object>> iterator() { public Iterator<Entry<Object, Object>> iterator() {
return (Iterator) lookup.values().iterator(); return (Iterator) lookup.values().iterator();
} }
@Override @Override
public int size() { public int size() {
return lookup.size(); return lookup.size();
@ -114,5 +114,5 @@ public class AssociativeScriptObject extends GroovyObjectSupport implements Iter
}; };
} }
} }
} }

View File

@ -12,8 +12,8 @@ import java.lang.annotation.Target;
@Retention(RUNTIME) @Retention(RUNTIME)
@Target(METHOD) @Target(METHOD)
public @interface Define { public @interface Define {
String[] value(); String[] value();
static final String undefined = ""; static final String undefined = "";
} }

View File

@ -6,26 +6,26 @@ import javax.script.ScriptException;
public class ExpressionException extends ScriptException { public class ExpressionException extends ScriptException {
private final String message; private final String message;
public ExpressionException(String message, Exception cause) { public ExpressionException(String message, Exception cause) {
super(cause); super(cause);
// can't set message via super constructor // can't set message via super constructor
this.message = message; this.message = message;
} }
public ExpressionException(Exception e) { public ExpressionException(Exception e) {
this(e.getMessage(), e); this(e.getMessage(), e);
} }
@Override @Override
public String getMessage() { public String getMessage() {
return message; return message;
} }
} }

View File

@ -32,7 +32,7 @@ public class ExpressionFormatMethods {
/** /**
* Pad strings or numbers with given characters ('0' by default). * Pad strings or numbers with given characters ('0' by default).
* *
* e.g. "1" -> "01" * e.g. "1" -> "01"
*/ */
public static String pad(String self, int length, String padding) { public static String pad(String self, int length, String padding) {
@ -97,7 +97,7 @@ public class ExpressionFormatMethods {
/** /**
* Replace space characters with a given characters. * Replace space characters with a given characters.
* *
* e.g. "Doctor Who" -> "Doctor_Who" * e.g. "Doctor Who" -> "Doctor_Who"
*/ */
public static String space(String self, String replacement) { public static String space(String self, String replacement) {
@ -106,7 +106,7 @@ public class ExpressionFormatMethods {
/** /**
* Upper-case all initials. * Upper-case all initials.
* *
* e.g. "The Day a new Demon was born" -> "The Day A New Demon Was Born" * e.g. "The Day a new Demon was born" -> "The Day A New Demon Was Born"
*/ */
public static String upperInitial(String self) { public static String upperInitial(String self) {
@ -131,7 +131,7 @@ public class ExpressionFormatMethods {
/** /**
* Get acronym, i.e. first letter of each word. * Get acronym, i.e. first letter of each word.
* *
* e.g. "Deep Space 9" -> "DS9" * e.g. "Deep Space 9" -> "DS9"
*/ */
public static String acronym(String self) { public static String acronym(String self) {
@ -148,7 +148,7 @@ public class ExpressionFormatMethods {
/** /**
* Lower-case all letters that are not initials. * Lower-case all letters that are not initials.
* *
* e.g. "Gundam SEED" -> "Gundam Seed" * e.g. "Gundam SEED" -> "Gundam Seed"
*/ */
public static String lowerTrail(String self) { public static String lowerTrail(String self) {
@ -220,7 +220,7 @@ public class ExpressionFormatMethods {
/** /**
* Replace trailing parenthesis including any leading whitespace. * Replace trailing parenthesis including any leading whitespace.
* *
* e.g. "The IT Crowd (UK)" -> "The IT Crowd" * e.g. "The IT Crowd (UK)" -> "The IT Crowd"
*/ */
public static String replaceTrailingBrackets(String self) { public static String replaceTrailingBrackets(String self) {
@ -233,7 +233,7 @@ public class ExpressionFormatMethods {
/** /**
* Replace 'part identifier'. * Replace 'part identifier'.
* *
* e.g. "Today Is the Day: Part 1" -> "Today Is the Day, Part 1" or "Today Is the Day (1)" -> "Today Is the Day, Part 1" * e.g. "Today Is the Day: Part 1" -> "Today Is the Day, Part 1" or "Today Is the Day (1)" -> "Today Is the Day, Part 1"
*/ */
public static String replacePart(String self) { public static String replacePart(String self) {
@ -257,7 +257,7 @@ public class ExpressionFormatMethods {
/** /**
* Apply ICU transliteration * Apply ICU transliteration
* *
* @see http://userguide.icu-project.org/transforms/general * @see http://userguide.icu-project.org/transforms/general
*/ */
public static String transliterate(String self, String transformIdentifier) { public static String transliterate(String self, String transformIdentifier) {
@ -266,7 +266,7 @@ public class ExpressionFormatMethods {
/** /**
* Convert Unicode to ASCII as best as possible. Works with most alphabets/scripts used in the world. * Convert Unicode to ASCII as best as possible. Works with most alphabets/scripts used in the world.
* *
* e.g. "Österreich" -> "Osterreich" "カタカナ" -> "katakana" * e.g. "Österreich" -> "Osterreich" "カタカナ" -> "katakana"
*/ */
public static String ascii(String self) { public static String ascii(String self) {
@ -283,7 +283,7 @@ public class ExpressionFormatMethods {
/** /**
* Replace multiple replacement pairs * Replace multiple replacement pairs
* *
* e.g. replace('ä', 'ae', 'ö', 'oe', 'ü', 'ue') * e.g. replace('ä', 'ae', 'ö', 'oe', 'ü', 'ue')
*/ */
public static String replace(String self, String tr0, String tr1, String... tr) { public static String replace(String self, String tr0, String tr1, String... tr) {

View File

@ -91,10 +91,10 @@ public class MediaBindingBean {
return getMovie().getName(); return getMovie().getName();
if (infoObject instanceof AudioTrack) if (infoObject instanceof AudioTrack)
return getAlbumArtist() != null ? getAlbumArtist() : getArtist(); return getAlbumArtist() != null ? getAlbumArtist() : getArtist();
if (infoObject instanceof File) if (infoObject instanceof File)
return FileUtilities.getName((File) infoObject); return FileUtilities.getName((File) infoObject);
return null; return null;
} }
@Define("y") @Define("y")
@ -909,6 +909,7 @@ public class MediaBindingBean {
private AssociativeScriptObject createBindingObject(File file, Object info, Map<File, Object> context) { private AssociativeScriptObject createBindingObject(File file, Object info, Map<File, Object> context) {
MediaBindingBean mediaBindingBean = new MediaBindingBean(info, file, context) { MediaBindingBean mediaBindingBean = new MediaBindingBean(info, file, context) {
@Override
@Define(undefined) @Define(undefined)
public <T> T undefined(String name) { public <T> T undefined(String name) {
return null; // never throw exceptions for empty or null values return null; // never throw exceptions for empty or null values

View File

@ -13,22 +13,22 @@ import java.security.PrivilegedExceptionAction;
public final class PrivilegedInvocation implements InvocationHandler { public final class PrivilegedInvocation implements InvocationHandler {
private final Object object; private final Object object;
private final AccessControlContext context; private final AccessControlContext context;
private PrivilegedInvocation(Object object, AccessControlContext context) { private PrivilegedInvocation(Object object, AccessControlContext context) {
this.object = object; this.object = object;
this.context = context; this.context = context;
} }
@Override @Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
try { try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override @Override
public Object run() throws Exception { public Object run() throws Exception {
return method.invoke(object, args); return method.invoke(object, args);
@ -36,23 +36,23 @@ public final class PrivilegedInvocation implements InvocationHandler {
}, context); }, context);
} catch (PrivilegedActionException e) { } catch (PrivilegedActionException e) {
Throwable cause = e.getException(); Throwable cause = e.getException();
// the underlying method may have throw an exception // the underlying method may have throw an exception
if (cause instanceof InvocationTargetException) { if (cause instanceof InvocationTargetException) {
// get actual cause // get actual cause
cause = cause.getCause(); cause = cause.getCause();
} }
// forward cause // forward cause
throw cause; throw cause;
} }
} }
public static <I> I newProxy(Class<I> interfaceClass, I object, AccessControlContext context) { public static <I> I newProxy(Class<I> interfaceClass, I object, AccessControlContext context) {
InvocationHandler invocationHandler = new PrivilegedInvocation(object, context); InvocationHandler invocationHandler = new PrivilegedInvocation(object, context);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// create dynamic invocation proxy // create dynamic invocation proxy
return interfaceClass.cast(Proxy.newProxyInstance(classLoader, new Class[] { interfaceClass }, invocationHandler)); return interfaceClass.cast(Proxy.newProxyInstance(classLoader, new Class[] { interfaceClass }, invocationHandler));
} }

View File

@ -14,17 +14,17 @@ import java.util.TreeMap;
* Used to create a map view of the properties of an Object * Used to create a map view of the properties of an Object
*/ */
public class PropertyBindings extends AbstractMap<String, Object> { public class PropertyBindings extends AbstractMap<String, Object> {
private final Object object; private final Object object;
private final Map<String, Object> properties = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER); private final Map<String, Object> properties = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
private final Object defaultValue; private final Object defaultValue;
public PropertyBindings(Object object, Object defaultValue) { public PropertyBindings(Object object, Object defaultValue) {
this.object = object; this.object = object;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
// get method bindings // get method bindings
for (Method method : object.getClass().getMethods()) { for (Method method : object.getClass().getMethods()) {
if (method.getReturnType() != void.class && method.getParameterTypes().length == 0 && !method.getDeclaringClass().getName().startsWith("java")) { if (method.getReturnType() != void.class && method.getParameterTypes().length == 0 && !method.getDeclaringClass().getName().startsWith("java")) {
@ -32,7 +32,7 @@ public class PropertyBindings extends AbstractMap<String, Object> {
if (method.getName().length() > 3 && method.getName().substring(0, 3).equalsIgnoreCase("get")) { if (method.getName().length() > 3 && method.getName().substring(0, 3).equalsIgnoreCase("get")) {
properties.put(method.getName().substring(3), method); properties.put(method.getName().substring(3), method);
} }
// boolean properties // boolean properties
if (method.getName().length() > 2 && method.getName().substring(0, 3).equalsIgnoreCase("is")) { if (method.getName().length() > 2 && method.getName().substring(0, 3).equalsIgnoreCase("is")) {
properties.put(method.getName().substring(2), method); properties.put(method.getName().substring(2), method);
@ -40,17 +40,17 @@ public class PropertyBindings extends AbstractMap<String, Object> {
} }
} }
} }
@Override @Override
public Object get(Object key) { public Object get(Object key) {
Object value = properties.get(key); Object value = properties.get(key);
// evaluate method // evaluate method
if (value instanceof Method) { if (value instanceof Method) {
try { try {
value = ((Method) value).invoke(object); value = ((Method) value).invoke(object);
if (value == null) { if (value == null) {
value = defaultValue; value = defaultValue;
} }
@ -58,74 +58,74 @@ public class PropertyBindings extends AbstractMap<String, Object> {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
return value; return value;
} }
@Override @Override
public Object put(String key, Object value) { public Object put(String key, Object value) {
return properties.put(key, value); return properties.put(key, value);
} }
@Override @Override
public Object remove(Object key) { public Object remove(Object key) {
return properties.remove(key); return properties.remove(key);
} }
@Override @Override
public boolean containsKey(Object key) { public boolean containsKey(Object key) {
return properties.containsKey(key); return properties.containsKey(key);
} }
@Override @Override
public Set<String> keySet() { public Set<String> keySet() {
return properties.keySet(); return properties.keySet();
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return properties.isEmpty(); return properties.isEmpty();
} }
@Override @Override
public String toString() { public String toString() {
return properties.toString(); return properties.toString();
} }
@Override @Override
public Set<Entry<String, Object>> entrySet() { public Set<Entry<String, Object>> entrySet() {
Set<Entry<String, Object>> entrySet = new HashSet<Entry<String, Object>>(); Set<Entry<String, Object>> entrySet = new HashSet<Entry<String, Object>>();
for (final String key : keySet()) { for (final String key : keySet()) {
entrySet.add(new Entry<String, Object>() { entrySet.add(new Entry<String, Object>() {
@Override @Override
public String getKey() { public String getKey() {
return key; return key;
} }
@Override @Override
public Object getValue() { public Object getValue() {
return get(key); return get(key);
} }
@Override @Override
public Object setValue(Object value) { public Object setValue(Object value) {
return put(key, value); return put(key, value);
} }
}); });
} }
return entrySet; return entrySet;
} }
} }

View File

@ -7,22 +7,22 @@ import com.sun.jna.Pointer;
interface GIOLibrary extends Library { interface GIOLibrary extends Library {
void g_type_init(); void g_type_init();
Pointer g_vfs_get_default(); Pointer g_vfs_get_default();
Pointer g_vfs_get_file_for_uri(Pointer gvfs, String uri); Pointer g_vfs_get_file_for_uri(Pointer gvfs, String uri);
Pointer g_file_get_path(Pointer gfile); Pointer g_file_get_path(Pointer gfile);
void g_free(Pointer gpointer); void g_free(Pointer gpointer);
void g_object_unref(Pointer gobject); void g_object_unref(Pointer gobject);
} }

View File

@ -10,31 +10,31 @@ import com.sun.jna.Pointer;
public class GVFS { public class GVFS {
private static GIOLibrary lib; private static GIOLibrary lib;
private static Pointer gvfs; private static Pointer gvfs;
private synchronized static GIOLibrary getLibrary() { private synchronized static GIOLibrary getLibrary() {
if (lib == null) { if (lib == null) {
lib = (GIOLibrary) Native.loadLibrary("gio-2.0", GIOLibrary.class); lib = (GIOLibrary) Native.loadLibrary("gio-2.0", GIOLibrary.class);
} }
return lib; return lib;
} }
public synchronized static Pointer getDefaultVFS() { public synchronized static Pointer getDefaultVFS() {
if (gvfs == null) { if (gvfs == null) {
gvfs = getLibrary().g_vfs_get_default(); gvfs = getLibrary().g_vfs_get_default();
} }
return gvfs; return gvfs;
} }
public static File getPathForURI(URI uri) { public static File getPathForURI(URI uri) {
Pointer gfile = getLibrary().g_vfs_get_file_for_uri(getDefaultVFS(), uri.toString()); Pointer gfile = getLibrary().g_vfs_get_file_for_uri(getDefaultVFS(), uri.toString());
Pointer chars = getLibrary().g_file_get_path(gfile); Pointer chars = getLibrary().g_file_get_path(gfile);
try { try {
if (chars != null) if (chars != null)
return new File(chars.getString(0)); return new File(chars.getString(0));
@ -45,5 +45,5 @@ public class GVFS {
getLibrary().g_free(chars); getLibrary().g_free(chars);
} }
} }
} }

View File

@ -6,24 +6,24 @@ import java.util.zip.Checksum;
public class ChecksumHash implements Hash { public class ChecksumHash implements Hash {
private final Checksum checksum; private final Checksum checksum;
public ChecksumHash(Checksum checksum) { public ChecksumHash(Checksum checksum) {
this.checksum = checksum; this.checksum = checksum;
} }
@Override @Override
public void update(byte[] bytes, int off, int len) { public void update(byte[] bytes, int off, int len) {
checksum.update(bytes, off, len); checksum.update(bytes, off, len);
} }
@Override @Override
public String digest() { public String digest() {
return String.format("%08X", checksum.getValue()); return String.format("%08X", checksum.getValue());
} }
} }

View File

@ -9,10 +9,10 @@ import jonelo.jacksum.algorithm.Edonkey;
public class Ed2kHash implements Hash { public class Ed2kHash implements Hash {
private final Edonkey ed2k; private final Edonkey ed2k;
public Ed2kHash() { public Ed2kHash() {
try { try {
this.ed2k = new Edonkey(); this.ed2k = new Edonkey();
@ -20,17 +20,17 @@ public class Ed2kHash implements Hash {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override @Override
public void update(byte[] bytes, int off, int len) { public void update(byte[] bytes, int off, int len) {
ed2k.update(bytes, off, len); ed2k.update(bytes, off, len);
} }
@Override @Override
public String digest() { public String digest() {
return String.format("%0" + (ed2k.getByteArray().length * 2) + "x", new BigInteger(1, ed2k.getByteArray())); return String.format("%0" + (ed2k.getByteArray().length * 2) + "x", new BigInteger(1, ed2k.getByteArray()));
} }
} }

View File

@ -3,10 +3,10 @@ package net.filebot.hash;
public interface Hash { public interface Hash {
public void update(byte[] bytes, int off, int len); public void update(byte[] bytes, int off, int len);
public String digest(); public String digest();
} }

View File

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

View File

@ -10,17 +10,17 @@ import java.util.regex.Pattern;
public class SfvFormat extends VerificationFormat { public class SfvFormat extends VerificationFormat {
@Override @Override
public String format(String path, String hash) { public String format(String path, String hash) {
// e.g folder/file.txt 970E4EF1 // e.g folder/file.txt 970E4EF1
return String.format("%s %s", path, hash); return String.format("%s %s", path, hash);
} }
/** /**
* Pattern used to parse the lines of a sfv file. * Pattern used to parse the lines of a sfv file.
* *
* <pre> * <pre>
* Sample: * Sample:
* folder/file.txt 970E4EF1 * folder/file.txt 970E4EF1
@ -28,17 +28,17 @@ public class SfvFormat extends VerificationFormat {
* </pre> * </pre>
*/ */
private final Pattern pattern = Pattern.compile("^(.+)\\s+(\\p{XDigit}{8})$"); private final Pattern pattern = Pattern.compile("^(.+)\\s+(\\p{XDigit}{8})$");
@Override @Override
public Entry<File, String> parseObject(String line) throws ParseException { public Entry<File, String> parseObject(String line) throws ParseException {
Matcher matcher = pattern.matcher(line); Matcher matcher = pattern.matcher(line);
if (!matcher.matches()) { if (!matcher.matches()) {
throw new ParseException("Illegal input pattern", 0); throw new ParseException("Illegal input pattern", 0);
} }
return entry(matcher.group(1), matcher.group(2)); return entry(matcher.group(1), matcher.group(2));
} }
} }

View File

@ -15,21 +15,21 @@ import java.util.logging.Logger;
public class VerificationFileReader implements Iterator<Entry<File, String>>, Closeable { public class VerificationFileReader implements Iterator<Entry<File, String>>, Closeable {
private final Scanner scanner; private final Scanner scanner;
private final VerificationFormat format; private final VerificationFormat format;
private Entry<File, String> buffer; private Entry<File, String> buffer;
private int lineNumber = 0; private int lineNumber = 0;
public VerificationFileReader(Readable source, VerificationFormat format) { public VerificationFileReader(Readable source, VerificationFormat format) {
this.scanner = new Scanner(source); this.scanner = new Scanner(source);
this.format = format; this.format = format;
} }
@Override @Override
public boolean hasNext() { public boolean hasNext() {
@ -37,10 +37,10 @@ public class VerificationFileReader implements Iterator<Entry<File, String>>, Cl
// cache next entry // cache next entry
buffer = nextEntry(); buffer = nextEntry();
} }
return buffer != null; return buffer != null;
} }
@Override @Override
public Entry<File, String> next() { public Entry<File, String> next() {
@ -48,7 +48,7 @@ public class VerificationFileReader implements Iterator<Entry<File, String>>, Cl
if (!hasNext()) { if (!hasNext()) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
try { try {
return buffer; return buffer;
} finally { } finally {
@ -56,15 +56,15 @@ public class VerificationFileReader implements Iterator<Entry<File, String>>, Cl
buffer = null; buffer = null;
} }
} }
protected Entry<File, String> nextEntry() { protected Entry<File, String> nextEntry() {
Entry<File, String> entry = null; Entry<File, String> entry = null;
// get next valid entry // get next valid entry
while (entry == null && scanner.hasNextLine()) { while (entry == null && scanner.hasNextLine()) {
String line = scanner.nextLine().trim(); String line = scanner.nextLine().trim();
// ignore comments // ignore comments
if (!isComment(line)) { if (!isComment(line)) {
try { try {
@ -74,33 +74,33 @@ public class VerificationFileReader implements Iterator<Entry<File, String>>, Cl
Logger.getLogger(getClass().getName()).log(Level.WARNING, String.format("Illegal format on line %d: %s", lineNumber, line)); Logger.getLogger(getClass().getName()).log(Level.WARNING, String.format("Illegal format on line %d: %s", lineNumber, line));
} }
} }
lineNumber++; lineNumber++;
} }
return entry; return entry;
} }
public int getLineNumber() { public int getLineNumber() {
return lineNumber; return lineNumber;
} }
protected boolean isComment(String line) { protected boolean isComment(String line) {
return line.isEmpty() || line.startsWith(";"); return line.isEmpty() || line.startsWith(";");
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
scanner.close(); scanner.close();
} }
@Override @Override
public void remove() { public void remove() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }

View File

@ -12,40 +12,40 @@ import net.filebot.Settings;
public class VerificationFileWriter implements Closeable { public class VerificationFileWriter implements Closeable {
protected PrintWriter out; protected PrintWriter out;
protected VerificationFormat format; protected VerificationFormat format;
public VerificationFileWriter(File file, VerificationFormat format, String charset) throws IOException { public VerificationFileWriter(File file, VerificationFormat format, String charset) throws IOException {
this(new PrintWriter(file, charset), format, charset); this(new PrintWriter(file, charset), format, charset);
} }
public VerificationFileWriter(PrintWriter out, VerificationFormat format, String charset) { public VerificationFileWriter(PrintWriter out, VerificationFormat format, String charset) {
this.out = out; this.out = out;
this.format = format; this.format = format;
// start by printing the file header // start by printing the file header
writeHeader(charset); writeHeader(charset);
} }
protected void writeHeader(String charset) { protected void writeHeader(String charset) {
out.format("; Generated by %s %s on %tF at %<tT%n", Settings.getApplicationName(), Settings.getApplicationVersion(), new Date()); out.format("; Generated by %s %s on %tF at %<tT%n", Settings.getApplicationName(), Settings.getApplicationVersion(), new Date());
out.format("; charset=%s%n", charset); out.format("; charset=%s%n", charset);
out.format(";%n"); out.format(";%n");
} }
public void write(String path, String hash) { public void write(String path, String hash) {
out.format("%s%n", format.format(path, hash)); out.format("%s%n", format.format(path, hash));
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
out.close(); out.close();
} }
} }

View File

@ -14,74 +14,74 @@ import java.util.regex.Pattern;
public class VerificationFormat extends Format { public class VerificationFormat extends Format {
private final String hashTypeHint; private final String hashTypeHint;
public VerificationFormat() { public VerificationFormat() {
this.hashTypeHint = ""; this.hashTypeHint = "";
} }
public VerificationFormat(String hashTypeHint) { public VerificationFormat(String hashTypeHint) {
this.hashTypeHint = hashTypeHint.isEmpty() ? "" : '?' + hashTypeHint.toUpperCase(); this.hashTypeHint = hashTypeHint.isEmpty() ? "" : '?' + hashTypeHint.toUpperCase();
} }
@Override @Override
public StringBuffer format(Object obj, StringBuffer sb, FieldPosition pos) { public StringBuffer format(Object obj, StringBuffer sb, FieldPosition pos) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Entry<File, String> entry = (Entry<File, String>) obj; Entry<File, String> entry = (Entry<File, String>) obj;
String path = entry.getKey().getPath(); String path = entry.getKey().getPath();
String hash = entry.getValue(); String hash = entry.getValue();
return sb.append(format(path, hash)); return sb.append(format(path, hash));
} }
public String format(String path, String hash) { public String format(String path, String hash) {
// e.g. 1a02a7c1e9ac91346d08829d5037b240f42ded07 ?SHA1*folder/file.txt // e.g. 1a02a7c1e9ac91346d08829d5037b240f42ded07 ?SHA1*folder/file.txt
return String.format("%s %s*%s", hash, hashTypeHint, path); return String.format("%s %s*%s", hash, hashTypeHint, path);
} }
/** /**
* Pattern used to parse the lines of a md5 or sha1 file. * Pattern used to parse the lines of a md5 or sha1 file.
* *
* <pre> * <pre>
* Sample MD5: * Sample MD5:
* 50e85fe18e17e3616774637a82968f4c *folder/file.txt * 50e85fe18e17e3616774637a82968f4c *folder/file.txt
* | Group 1 | Group 2 | * | Group 1 | Group 2 |
* *
* Sample SHA-1: * Sample SHA-1:
* 1a02a7c1e9ac91346d08829d5037b240f42ded07 ?SHA1*folder/file.txt * 1a02a7c1e9ac91346d08829d5037b240f42ded07 ?SHA1*folder/file.txt
* | Group 1 | | Group 2 | * | Group 1 | | Group 2 |
* </pre> * </pre>
*/ */
private final Pattern pattern = Pattern.compile("^(\\p{XDigit}+)\\s+(?:\\?\\w+)?\\*?(.+)$"); private final Pattern pattern = Pattern.compile("^(\\p{XDigit}+)\\s+(?:\\?\\w+)?\\*?(.+)$");
@Override @Override
public Entry<File, String> parseObject(String line) throws ParseException { public Entry<File, String> parseObject(String line) throws ParseException {
Matcher matcher = pattern.matcher(line); Matcher matcher = pattern.matcher(line);
if (!matcher.find()) { if (!matcher.find()) {
throw new ParseException("Illegal input pattern", 0); throw new ParseException("Illegal input pattern", 0);
} }
return entry(matcher.group(2), matcher.group(1)); return entry(matcher.group(2), matcher.group(1));
} }
@Override @Override
public Entry<File, String> parseObject(String line, ParsePosition pos) { public Entry<File, String> parseObject(String line, ParsePosition pos) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
protected Entry<File, String> entry(String path, String hash) { protected Entry<File, String> entry(String path, String hash) {
return new SimpleImmutableEntry<File, String>(new File(path), hash); return new SimpleImmutableEntry<File, String>(new File(path), hash);
} }
} }

View File

@ -284,6 +284,7 @@ public class DropToUnlock extends JList<File> {
return true; return true;
} }
@Override
public void handleTransferable(Transferable tr, TransferAction action) throws Exception { public void handleTransferable(Transferable tr, TransferAction action) throws Exception {
List<File> files = FileTransferable.getFilesFromTransferable(tr); List<File> files = FileTransferable.getFilesFromTransferable(tr);
if (files != null) { if (files != null) {
@ -318,6 +319,7 @@ public class DropToUnlock extends JList<File> {
protected static class FileChooserAction extends MouseAdapter { protected static class FileChooserAction extends MouseAdapter {
@Override
public void mouseClicked(MouseEvent evt) { public void mouseClicked(MouseEvent evt) {
DropToUnlock list = (DropToUnlock) evt.getSource(); DropToUnlock list = (DropToUnlock) evt.getSource();
if (evt.getClickCount() > 0) { if (evt.getClickCount() > 0) {

View File

@ -1,10 +1,10 @@
/* Copyright (c) 2014 Reinhard Pointner, All Rights Reserved /* Copyright (c) 2014 Reinhard Pointner, All Rights Reserved
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
@ -18,7 +18,7 @@ import com.sun.jna.Pointer;
/** /**
* JNA wrapper for <sys/xattr.h> * JNA wrapper for <sys/xattr.h>
* *
*/ */
interface XAttr extends Library { interface XAttr extends Library {

View File

@ -33,6 +33,7 @@ public class SmartSeasonEpisodeMatcher extends SeasonEpisodeMatcher {
return super.match(new File(clean(file.getPath()))); return super.match(new File(clean(file.getPath())));
} }
@Override
public String head(String name) { public String head(String name) {
return super.head(clean(name)); return super.head(clean(name));
} }

View File

@ -24,14 +24,14 @@ interface MediaInfoLibrary extends Library {
/** /**
* Create a new handle. * Create a new handle.
* *
* @return handle * @return handle
*/ */
Pointer New(); Pointer New();
/** /**
* Open a file and collect information about it (technical information and tags). * Open a file and collect information about it (technical information and tags).
* *
* @param handle * @param handle
* @param file * @param file
* full name of the file to open * full name of the file to open
@ -41,7 +41,7 @@ interface MediaInfoLibrary extends Library {
/** /**
* Configure or get information about MediaInfo. * Configure or get information about MediaInfo.
* *
* @param handle * @param handle
* @param option * @param option
* The name of option * The name of option
@ -53,7 +53,7 @@ interface MediaInfoLibrary extends Library {
/** /**
* Get all details about a file. * Get all details about a file.
* *
* @param handle * @param handle
* @return All details about a file in one string * @return All details about a file in one string
*/ */
@ -61,7 +61,7 @@ interface MediaInfoLibrary extends Library {
/** /**
* Get a piece of information about a file (parameter is a string). * Get a piece of information about a file (parameter is a string).
* *
* @param handle * @param handle
* @param streamKind * @param streamKind
* Kind of stream (general, video, audio...) * Kind of stream (general, video, audio...)
@ -79,7 +79,7 @@ interface MediaInfoLibrary extends Library {
/** /**
* Get a piece of information about a file (parameter is an integer). * Get a piece of information about a file (parameter is an integer).
* *
* @param handle * @param handle
* @param streamKind * @param streamKind
* Kind of stream (general, video, audio...) * Kind of stream (general, video, audio...)
@ -95,7 +95,7 @@ interface MediaInfoLibrary extends Library {
/** /**
* Count of streams of a stream kind (StreamNumber not filled), or count of piece of information in this stream. * Count of streams of a stream kind (StreamNumber not filled), or count of piece of information in this stream.
* *
* @param handle * @param handle
* @param streamKind * @param streamKind
* Kind of stream (general, video, audio...) * Kind of stream (general, video, audio...)
@ -107,14 +107,14 @@ interface MediaInfoLibrary extends Library {
/** /**
* Close a file opened before with Open(). * Close a file opened before with Open().
* *
* @param handle * @param handle
*/ */
void Close(Pointer handle); void Close(Pointer handle);
/** /**
* Dispose of a handle created with New(). * Dispose of a handle created with New().
* *
* @param handle * @param handle
*/ */
void Delete(Pointer handle); void Delete(Pointer handle);

View File

@ -10,37 +10,37 @@ import net.filebot.format.PropertyBindings;
public class CrossPropertyMetric implements SimilarityMetric { public class CrossPropertyMetric implements SimilarityMetric {
private SimilarityMetric metric; private SimilarityMetric metric;
public CrossPropertyMetric(SimilarityMetric metric) { public CrossPropertyMetric(SimilarityMetric metric) {
this.metric = metric; this.metric = metric;
} }
public CrossPropertyMetric() { public CrossPropertyMetric() {
this.metric = new StringEqualsMetric(); this.metric = new StringEqualsMetric();
} }
@Override @Override
public float getSimilarity(Object o1, Object o2) { public float getSimilarity(Object o1, Object o2) {
Map<String, Object> m1 = getProperties(o1); Map<String, Object> m1 = getProperties(o1);
if (m1.isEmpty()) if (m1.isEmpty())
return 0; return 0;
Map<String, Object> m2 = getProperties(o2); Map<String, Object> m2 = getProperties(o2);
if (m2.isEmpty()) if (m2.isEmpty())
return 0; return 0;
// works with commons keys // works with commons keys
Set<String> keys = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER); Set<String> keys = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
keys.addAll(m1.keySet()); keys.addAll(m1.keySet());
keys.retainAll(m2.keySet()); keys.retainAll(m2.keySet());
if (keys.isEmpty()) if (keys.isEmpty())
return 0; return 0;
float feedback = 0; float feedback = 0;
for (String k : keys) { for (String k : keys) {
try { try {
@ -49,13 +49,13 @@ public class CrossPropertyMetric implements SimilarityMetric {
// ignore // ignore
} }
} }
return feedback / keys.size(); return feedback / keys.size();
} }
protected Map<String, Object> getProperties(Object object) { protected Map<String, Object> getProperties(Object object) {
return new PropertyBindings(object, null); return new PropertyBindings(object, null);
} }
} }

View File

@ -8,28 +8,28 @@ import java.io.File;
public class FileNameMetric implements SimilarityMetric { public class FileNameMetric implements SimilarityMetric {
@Override @Override
public float getSimilarity(Object o1, Object o2) { public float getSimilarity(Object o1, Object o2) {
String s1 = getFileName(o1); String s1 = getFileName(o1);
if (s1 == null || s1.isEmpty()) if (s1 == null || s1.isEmpty())
return 0; return 0;
String s2 = getFileName(o2); String s2 = getFileName(o2);
if (s2 == null || s2.isEmpty()) if (s2 == null || s2.isEmpty())
return 0; return 0;
return s1.startsWith(s2) || s2.startsWith(s1) ? 1 : 0; return s1.startsWith(s2) || s2.startsWith(s1) ? 1 : 0;
} }
protected String getFileName(Object object) { protected String getFileName(Object object) {
if (object instanceof File) { if (object instanceof File) {
// name without extension normalized to lower-case // name without extension normalized to lower-case
return getName((File) object).trim().toLowerCase(); return getName((File) object).trim().toLowerCase();
} }
return null; return null;
} }
} }

View File

@ -6,28 +6,28 @@ import java.io.File;
public class FileSizeMetric implements SimilarityMetric { public class FileSizeMetric implements SimilarityMetric {
@Override @Override
public float getSimilarity(Object o1, Object o2) { public float getSimilarity(Object o1, Object o2) {
long l1 = getLength(o1); long l1 = getLength(o1);
if (l1 < 0) if (l1 < 0)
return 0; return 0;
long l2 = getLength(o2); long l2 = getLength(o2);
if (l2 < 0) if (l2 < 0)
return 0; return 0;
// objects have the same non-negative length // objects have the same non-negative length
return l1 == l2 ? 1 : -1; return l1 == l2 ? 1 : -1;
} }
protected long getLength(Object object) { protected long getLength(Object object) {
if (object instanceof File) { if (object instanceof File) {
return ((File) object).length(); return ((File) object).length();
} }
return -1; return -1;
} }
} }

View File

@ -6,26 +6,26 @@ import java.util.Arrays;
public class Match<Value, Candidate> { public class Match<Value, Candidate> {
private final Value value; private final Value value;
private final Candidate candidate; private final Candidate candidate;
public Match(Value value, Candidate candidate) { public Match(Value value, Candidate candidate) {
this.value = value; this.value = value;
this.candidate = candidate; this.candidate = candidate;
} }
public Value getValue() { public Value getValue() {
return value; return value;
} }
public Candidate getCandidate() { public Candidate getCandidate() {
return candidate; return candidate;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
@ -33,20 +33,20 @@ public class Match<Value, Candidate> {
Match<?, ?> other = (Match<?, ?>) obj; Match<?, ?> other = (Match<?, ?>) obj;
return value == other.value && candidate == other.candidate; return value == other.value && candidate == other.candidate;
} }
return false; return false;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Arrays.hashCode(new Object[] { value, candidate }); return Arrays.hashCode(new Object[] { value, candidate });
} }
@Override @Override
public String toString() { public String toString() {
return String.format("[%s, %s]", value, candidate); return String.format("[%s, %s]", value, candidate);
} }
} }

View File

@ -3,20 +3,20 @@ package net.filebot.similarity;
public class MetricMin implements SimilarityMetric { public class MetricMin implements SimilarityMetric {
private final SimilarityMetric metric; private final SimilarityMetric metric;
private final float minValue; private final float minValue;
public MetricMin(SimilarityMetric metric, float minValue) { public MetricMin(SimilarityMetric metric, float minValue) {
this.metric = metric; this.metric = metric;
this.minValue = minValue; this.minValue = minValue;
} }
@Override @Override
public float getSimilarity(Object o1, Object o2) { public float getSimilarity(Object o1, Object o2) {
return Math.max(metric.getSimilarity(o1, o2), minValue); return Math.max(metric.getSimilarity(o1, o2), minValue);
} }
} }

View File

@ -11,43 +11,43 @@ import com.ibm.icu.text.Transliterator;
public class NameSimilarityMetric implements SimilarityMetric { public class NameSimilarityMetric implements SimilarityMetric {
private final AbstractStringMetric metric; private final AbstractStringMetric metric;
private final Transliterator transliterator; private final Transliterator transliterator;
public NameSimilarityMetric() { public NameSimilarityMetric() {
// QGramsDistance with a QGram tokenizer seems to work best for similarity of names // QGramsDistance with a QGram tokenizer seems to work best for similarity of names
this(new QGramsDistance(new TokeniserQGram3()), Transliterator.getInstance("Any-Latin;Latin-ASCII;[:Diacritic:]remove")); this(new QGramsDistance(new TokeniserQGram3()), Transliterator.getInstance("Any-Latin;Latin-ASCII;[:Diacritic:]remove"));
} }
public NameSimilarityMetric(AbstractStringMetric metric, Transliterator transliterator) { public NameSimilarityMetric(AbstractStringMetric metric, Transliterator transliterator) {
this.metric = metric; this.metric = metric;
this.transliterator = transliterator; this.transliterator = transliterator;
} }
@Override @Override
public float getSimilarity(Object o1, Object o2) { public float getSimilarity(Object o1, Object o2) {
return metric.getSimilarity(normalize(o1), normalize(o2)); return metric.getSimilarity(normalize(o1), normalize(o2));
} }
protected String normalize(Object object) { protected String normalize(Object object) {
// use string representation // use string representation
String name = object.toString(); String name = object.toString();
// apply transliterator // apply transliterator
if (transliterator != null) { if (transliterator != null) {
name = transliterator.transform(name); name = transliterator.transform(name);
} }
// normalize separators // normalize separators
name = normalizePunctuation(name); name = normalizePunctuation(name);
// normalize case and trim // normalize case and trim
return name.toLowerCase(); return name.toLowerCase();
} }
} }

View File

@ -15,81 +15,81 @@ import uk.ac.shef.wit.simmetrics.wordhandlers.InterfaceTermHandler;
public class NumericSimilarityMetric implements SimilarityMetric { public class NumericSimilarityMetric implements SimilarityMetric {
private final AbstractStringMetric metric; private final AbstractStringMetric metric;
public NumericSimilarityMetric() { public NumericSimilarityMetric() {
// I don't exactly know why, but I get a good matching behavior // I don't exactly know why, but I get a good matching behavior
// when using QGramsDistance or BlockDistance // when using QGramsDistance or BlockDistance
metric = new QGramsDistance(new NumberTokeniser()); metric = new QGramsDistance(new NumberTokeniser());
} }
@Override @Override
public float getSimilarity(Object o1, Object o2) { public float getSimilarity(Object o1, Object o2) {
return metric.getSimilarity(normalize(o1), normalize(o2)); return metric.getSimilarity(normalize(o1), normalize(o2));
} }
protected String normalize(Object object) { protected String normalize(Object object) {
// no need to do anything special here, because we don't care about anything but number patterns anyway // no need to do anything special here, because we don't care about anything but number patterns anyway
return object.toString(); return object.toString();
} }
private static class NumberTokeniser implements InterfaceTokeniser { private static class NumberTokeniser implements InterfaceTokeniser {
private final String delimiter = "\\D+"; private final String delimiter = "\\D+";
@Override @Override
public ArrayList<String> tokenizeToArrayList(String input) { public ArrayList<String> tokenizeToArrayList(String input) {
ArrayList<String> tokens = new ArrayList<String>(); ArrayList<String> tokens = new ArrayList<String>();
// scan for number patterns, use non-number pattern as delimiter // scan for number patterns, use non-number pattern as delimiter
Scanner scanner = new Scanner(input).useDelimiter(delimiter); Scanner scanner = new Scanner(input).useDelimiter(delimiter);
while (scanner.hasNextInt()) { while (scanner.hasNextInt()) {
// remove leading zeros from number tokens by scanning for Integers // remove leading zeros from number tokens by scanning for Integers
tokens.add(String.valueOf(scanner.nextInt())); tokens.add(String.valueOf(scanner.nextInt()));
} }
return tokens; return tokens;
} }
@Override @Override
public Set<String> tokenizeToSet(String input) { public Set<String> tokenizeToSet(String input) {
return new LinkedHashSet<String>(tokenizeToArrayList(input)); return new LinkedHashSet<String>(tokenizeToArrayList(input));
} }
@Override @Override
public String getShortDescriptionString() { public String getShortDescriptionString() {
return getClass().getSimpleName(); return getClass().getSimpleName();
} }
@Override @Override
public String getDelimiters() { public String getDelimiters() {
return delimiter; return delimiter;
} }
private InterfaceTermHandler stopWordHandler = new DummyStopTermHandler(); private InterfaceTermHandler stopWordHandler = new DummyStopTermHandler();
@Override @Override
public InterfaceTermHandler getStopWordHandler() { public InterfaceTermHandler getStopWordHandler() {
return stopWordHandler; return stopWordHandler;
} }
@Override @Override
public void setStopWordHandler(InterfaceTermHandler stopWordHandler) { public void setStopWordHandler(InterfaceTermHandler stopWordHandler) {
this.stopWordHandler = stopWordHandler; this.stopWordHandler = stopWordHandler;
} }
} }
} }

View File

@ -154,7 +154,7 @@ public class SeasonEpisodeMatcher {
/** /**
* Try to get season and episode numbers for the given string. * Try to get season and episode numbers for the given string.
* *
* @param name * @param name
* match this string against the a set of know patterns * match this string against the a set of know patterns
* @return the matches returned by the first pattern that returns any matches for this string, or null if no pattern returned any matches * @return the matches returned by the first pattern that returns any matches for this string, or null if no pattern returned any matches

View File

@ -110,7 +110,7 @@ public class SeriesNameMatcher {
/** /**
* Try to match and verify all series names using known season episode patterns. * Try to match and verify all series names using known season episode patterns.
* *
* @param names * @param names
* episode names * episode names
* @return series names that have been matched one or multiple times depending on the threshold * @return series names that have been matched one or multiple times depending on the threshold
@ -154,7 +154,7 @@ public class SeriesNameMatcher {
/** /**
* Try to match all common word sequences in the given list. * Try to match all common word sequences in the given list.
* *
* @param names * @param names
* list of episode names * list of episode names
* @return all common word sequences that have been found * @return all common word sequences that have been found
@ -184,7 +184,7 @@ public class SeriesNameMatcher {
/** /**
* Try to match a series name from the given episode name using known season episode patterns. * Try to match a series name from the given episode name using known season episode patterns.
* *
* @param name * @param name
* episode name * episode name
* @return a substring of the given name that ends before the first occurrence of a season episode pattern, or null if there is no such pattern * @return a substring of the given name that ends before the first occurrence of a season episode pattern, or null if there is no such pattern
@ -218,7 +218,7 @@ public class SeriesNameMatcher {
/** /**
* Try to match a series name from the first common word sequence. * Try to match a series name from the first common word sequence.
* *
* @param names * @param names
* various episode names (at least two) * various episode names (at least two)
* @return a word sequence all episode names have in common, or null * @return a word sequence all episode names have in common, or null

View File

@ -6,34 +6,34 @@ import java.util.Comparator;
public class SimilarityComparator implements Comparator<Object> { public class SimilarityComparator implements Comparator<Object> {
protected SimilarityMetric metric; protected SimilarityMetric metric;
protected Object[] paragon; protected Object[] paragon;
public SimilarityComparator(SimilarityMetric metric, Object[] paragon) { public SimilarityComparator(SimilarityMetric metric, Object[] paragon) {
this.metric = metric; this.metric = metric;
this.paragon = paragon; this.paragon = paragon;
} }
public SimilarityComparator(Object... paragon) { public SimilarityComparator(Object... paragon) {
this(new NameSimilarityMetric(), paragon); this(new NameSimilarityMetric(), paragon);
} }
@Override @Override
public int compare(Object o1, Object o2) { public int compare(Object o1, Object o2) {
float f1 = getMaxSimilarity(o1); float f1 = getMaxSimilarity(o1);
float f2 = getMaxSimilarity(o2); float f2 = getMaxSimilarity(o2);
if (f1 == f2) if (f1 == f2)
return 0; return 0;
return f1 > f2 ? -1 : 1; return f1 > f2 ? -1 : 1;
} }
public float getMaxSimilarity(Object obj) { public float getMaxSimilarity(Object obj) {
float m = 0; float m = 0;
for (Object it : paragon) { for (Object it : paragon) {

View File

@ -3,7 +3,7 @@ package net.filebot.similarity;
public interface SimilarityMetric { public interface SimilarityMetric {
public float getSimilarity(Object o1, Object o2); public float getSimilarity(Object o1, Object o2);
} }

View File

@ -10,22 +10,22 @@ import java.nio.file.attribute.BasicFileAttributes;
public class TimeStampMetric implements SimilarityMetric { public class TimeStampMetric implements SimilarityMetric {
@Override @Override
public float getSimilarity(Object o1, Object o2) { public float getSimilarity(Object o1, Object o2) {
long t1 = getTimeStamp(o1); long t1 = getTimeStamp(o1);
long t2 = getTimeStamp(o2); long t2 = getTimeStamp(o2);
if (t1 <= 0 || t2 <= 0) if (t1 <= 0 || t2 <= 0)
return -1; return -1;
float min = min(t1, t2); float min = min(t1, t2);
float max = max(t1, t2); float max = max(t1, t2);
return min / max; return min / max;
} }
public long getTimeStamp(Object obj) { public long getTimeStamp(Object obj) {
if (obj instanceof File) { if (obj instanceof File) {
try { try {
@ -43,7 +43,7 @@ public class TimeStampMetric implements SimilarityMetric {
} else if (obj instanceof Number) { } else if (obj instanceof Number) {
return ((Number) obj).longValue(); return ((Number) obj).longValue();
} }
return -1; return -1;
} }
} }

View File

@ -9,61 +9,61 @@ import java.util.List;
public class MicroDVDReader extends SubtitleReader { public class MicroDVDReader extends SubtitleReader {
private double fps = 23.976; private double fps = 23.976;
public MicroDVDReader(Readable source) { public MicroDVDReader(Readable source) {
super(source); super(source);
} }
@Override @Override
public SubtitleElement readNext() throws Exception { public SubtitleElement readNext() throws Exception {
String line = scanner.nextLine(); String line = scanner.nextLine();
List<String> properties = new ArrayList<String>(2); List<String> properties = new ArrayList<String>(2);
int from = 0; int from = 0;
while (from < line.length() && line.charAt(from) == '{') { while (from < line.length() && line.charAt(from) == '{') {
int to = line.indexOf('}', from + 1); int to = line.indexOf('}', from + 1);
// no more properties // no more properties
if (to < from) if (to < from)
break; break;
// extract property // extract property
properties.add(line.substring(from + 1, to)); properties.add(line.substring(from + 1, to));
// skip property // skip property
from = to + 1; from = to + 1;
} }
if (properties.size() < 2) { if (properties.size() < 2) {
// ignore illegal lines // ignore illegal lines
return null; return null;
} }
int startFrame = Integer.parseInt(properties.get(0)); int startFrame = Integer.parseInt(properties.get(0));
int endFrame = Integer.parseInt(properties.get(1)); int endFrame = Integer.parseInt(properties.get(1));
String text = line.substring(from).trim(); String text = line.substring(from).trim();
// cancel format markers // cancel format markers
text = text.replaceAll("\\{[^\\}]*\\}", ""); text = text.replaceAll("\\{[^\\}]*\\}", "");
if (startFrame == 1 && endFrame == 1) { if (startFrame == 1 && endFrame == 1) {
// override fps // override fps
fps = Double.parseDouble(text); fps = Double.parseDouble(text);
// ignore line // ignore line
return null; return null;
} }
// translate '|' to new lines // translate '|' to new lines
String[] lines = text.split("[|]"); String[] lines = text.split("[|]");
// convert frame interval to time interval // convert frame interval to time interval
return new SubtitleElement(Math.round(startFrame * fps), Math.round(endFrame * fps), join(lines, "\n")); return new SubtitleElement(Math.round(startFrame * fps), Math.round(endFrame * fps), join(lines, "\n"));
} }
} }

View File

@ -12,21 +12,21 @@ import java.util.TimeZone;
public class SubRipWriter implements Closeable { public class SubRipWriter implements Closeable {
private final DateFormat timeFormat; private final DateFormat timeFormat;
private final Formatter out; private final Formatter out;
private int lineNumber = 0; private int lineNumber = 0;
public SubRipWriter(Appendable out) { public SubRipWriter(Appendable out) {
this.out = new Formatter(out, Locale.ROOT); this.out = new Formatter(out, Locale.ROOT);
// format used to create time stamps (e.g. 00:02:26,407 --> 00:02:31,356) // format used to create time stamps (e.g. 00:02:26,407 --> 00:02:31,356)
timeFormat = new SimpleDateFormat("HH:mm:ss,SSS", Locale.ROOT); timeFormat = new SimpleDateFormat("HH:mm:ss,SSS", Locale.ROOT);
timeFormat.setTimeZone(TimeZone.getTimeZone("UTC")); timeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
} }
public void write(SubtitleElement element) { public void write(SubtitleElement element) {
// write a single subtitle in SubRip format, e.g. // write a single subtitle in SubRip format, e.g.
@ -37,11 +37,11 @@ public class SubRipWriter implements Closeable {
out.format("%s --> %s%n", timeFormat.format(element.getStart()), timeFormat.format(element.getEnd())); out.format("%s --> %s%n", timeFormat.format(element.getStart()), timeFormat.format(element.getEnd()));
out.format("%s%n%n", element.getText()); out.format("%s%n%n", element.getText());
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
out.close(); out.close();
} }
} }

View File

@ -10,87 +10,87 @@ import java.util.regex.Pattern;
public class SubStationAlphaReader extends SubtitleReader { public class SubStationAlphaReader extends SubtitleReader {
private final DateFormat timeFormat = new SubtitleTimeFormat(); private final DateFormat timeFormat = new SubtitleTimeFormat();
private final Pattern newline = Pattern.compile(Pattern.quote("\\n"), Pattern.CASE_INSENSITIVE); private final Pattern newline = Pattern.compile(Pattern.quote("\\n"), Pattern.CASE_INSENSITIVE);
private final Pattern tag = Pattern.compile("[{]\\\\[^}]+[}]"); private final Pattern tag = Pattern.compile("[{]\\\\[^}]+[}]");
private String[] format; private String[] format;
private int formatIndexStart; private int formatIndexStart;
private int formatIndexEnd; private int formatIndexEnd;
private int formatIndexText; private int formatIndexText;
public SubStationAlphaReader(Readable source) { public SubStationAlphaReader(Readable source) {
super(source); super(source);
} }
private void readFormat() throws Exception { private void readFormat() throws Exception {
// read format line (e.g. Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text) // read format line (e.g. Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text)
String[] event = scanner.nextLine().split(":", 2); String[] event = scanner.nextLine().split(":", 2);
// sanity check // sanity check
if (!event[0].equals("Format")) if (!event[0].equals("Format"))
throw new InputMismatchException("Illegal format header: " + Arrays.toString(event)); throw new InputMismatchException("Illegal format header: " + Arrays.toString(event));
// read columns // read columns
format = event[1].split(","); format = event[1].split(",");
// normalize column names // normalize column names
for (int i = 0; i < format.length; i++) { for (int i = 0; i < format.length; i++) {
format[i] = format[i].trim().toLowerCase(); format[i] = format[i].trim().toLowerCase();
} }
List<String> lookup = Arrays.asList(format); List<String> lookup = Arrays.asList(format);
formatIndexStart = lookup.indexOf("start"); formatIndexStart = lookup.indexOf("start");
formatIndexEnd = lookup.indexOf("end"); formatIndexEnd = lookup.indexOf("end");
formatIndexText = lookup.indexOf("text"); formatIndexText = lookup.indexOf("text");
} }
@Override @Override
public SubtitleElement readNext() throws Exception { public SubtitleElement readNext() throws Exception {
if (format == null) { if (format == null) {
// move to [Events] sections // move to [Events] sections
boolean found = false; boolean found = false;
while (!found && scanner.hasNextLine()) { while (!found && scanner.hasNextLine()) {
found = scanner.nextLine().equals("[Events]"); found = scanner.nextLine().equals("[Events]");
} }
if (!found) { if (!found) {
throw new InputMismatchException("Cannot find [Events] section"); throw new InputMismatchException("Cannot find [Events] section");
} }
// read format header // read format header
readFormat(); readFormat();
} }
// read next dialogue line // read next dialogue line
String[] event = scanner.nextLine().split(":", 2); String[] event = scanner.nextLine().split(":", 2);
// sanity check // sanity check
if (!event[0].equals("Dialogue")) if (!event[0].equals("Dialogue"))
throw new InputMismatchException("Illegal dialogue event: " + Arrays.toString(event)); throw new InputMismatchException("Illegal dialogue event: " + Arrays.toString(event));
// extract information // extract information
String[] values = event[1].split(",", format.length); String[] values = event[1].split(",", format.length);
long start = timeFormat.parse(values[formatIndexStart].trim()).getTime(); long start = timeFormat.parse(values[formatIndexStart].trim()).getTime();
long end = timeFormat.parse(values[formatIndexEnd].trim()).getTime(); long end = timeFormat.parse(values[formatIndexEnd].trim()).getTime();
String text = values[formatIndexText].trim(); String text = values[formatIndexText].trim();
return new SubtitleElement(start, end, resolve(text)); return new SubtitleElement(start, end, resolve(text));
} }
protected String resolve(String text) { protected String resolve(String text) {
// remove tags // remove tags
text = tag.matcher(text).replaceAll(""); text = tag.matcher(text).replaceAll("");
// resolve line breaks // resolve line breaks
return newline.matcher(text).replaceAll("\n"); return newline.matcher(text).replaceAll("\n");
} }
} }

View File

@ -12,33 +12,33 @@ import java.util.regex.Pattern;
public class SubViewerReader extends SubtitleReader { public class SubViewerReader extends SubtitleReader {
private final DateFormat timeFormat = new SubtitleTimeFormat(); private final DateFormat timeFormat = new SubtitleTimeFormat();
private final Pattern newline = compile(quote("[br]"), CASE_INSENSITIVE); private final Pattern newline = compile(quote("[br]"), CASE_INSENSITIVE);
public SubViewerReader(Readable source) { public SubViewerReader(Readable source) {
super(source); super(source);
} }
@Override @Override
protected SubtitleElement readNext() throws Exception { protected SubtitleElement readNext() throws Exception {
// element starts with interval (e.g. 00:42:16.33,00:42:19.39) // element starts with interval (e.g. 00:42:16.33,00:42:19.39)
String[] interval = scanner.nextLine().split(",", 2); String[] interval = scanner.nextLine().split(",", 2);
if (interval.length < 2 || interval[0].startsWith("[")) { if (interval.length < 2 || interval[0].startsWith("[")) {
// ignore property lines // ignore property lines
return null; return null;
} }
try { try {
long t1 = timeFormat.parse(interval[0]).getTime(); long t1 = timeFormat.parse(interval[0]).getTime();
long t2 = timeFormat.parse(interval[1]).getTime(); long t2 = timeFormat.parse(interval[1]).getTime();
// translate [br] to new lines // translate [br] to new lines
String[] lines = newline.split(scanner.nextLine()); String[] lines = newline.split(scanner.nextLine());
return new SubtitleElement(t1, t2, join(lines, "\n")); return new SubtitleElement(t1, t2, join(lines, "\n"));
} catch (ParseException e) { } catch (ParseException e) {
// can't parse interval, ignore line // can't parse interval, ignore line

View File

@ -3,38 +3,38 @@ package net.filebot.subtitle;
public class SubtitleElement { public class SubtitleElement {
private final long start; private final long start;
private final long end; private final long end;
private final String text; private final String text;
public SubtitleElement(long start, long end, String text) { public SubtitleElement(long start, long end, String text) {
this.start = start; this.start = start;
this.end = end; this.end = end;
this.text = text; this.text = text;
} }
public long getStart() { public long getStart() {
return start; return start;
} }
public long getEnd() { public long getEnd() {
return end; return end;
} }
public String getText() { public String getText() {
return text; return text;
} }
@Override @Override
public String toString() { public String toString() {
return String.format("[%d, %d] %s", start, end, text); return String.format("[%d, %d] %s", start, end, text);
} }
} }

View File

@ -7,44 +7,44 @@ import net.filebot.util.FileUtilities.ExtensionFileFilter;
public enum SubtitleFormat { public enum SubtitleFormat {
SubRip { SubRip {
@Override @Override
public SubtitleReader newReader(Readable readable) { public SubtitleReader newReader(Readable readable) {
return new SubRipReader(readable); return new SubRipReader(readable);
} }
}, },
MicroDVD { MicroDVD {
@Override @Override
public SubtitleReader newReader(Readable readable) { public SubtitleReader newReader(Readable readable) {
return new MicroDVDReader(readable); return new MicroDVDReader(readable);
} }
}, },
SubViewer { SubViewer {
@Override @Override
public SubtitleReader newReader(Readable readable) { public SubtitleReader newReader(Readable readable) {
return new SubViewerReader(readable); return new SubViewerReader(readable);
} }
}, },
SubStationAlpha { SubStationAlpha {
@Override @Override
public SubtitleReader newReader(Readable readable) { public SubtitleReader newReader(Readable readable) {
return new SubStationAlphaReader(readable); return new SubStationAlphaReader(readable);
} }
}; };
public abstract SubtitleReader newReader(Readable readable); public abstract SubtitleReader newReader(Readable readable);
public ExtensionFileFilter getFilter() { public ExtensionFileFilter getFilter() {
return MediaTypes.getDefaultFilter("subtitle/" + this.name()); return MediaTypes.getDefaultFilter("subtitle/" + this.name());
} }
} }

View File

@ -88,6 +88,7 @@ public enum SubtitleMetrics implements SimilarityMetric {
return 0; return 0;
} }
@Override
protected float similarity(String match, String s1, String s2) { protected float similarity(String match, String s1, String s2) {
return match.length() > 0 ? 1 : 0; return match.length() > 0 ? 1 : 0;
} }
@ -153,10 +154,12 @@ public enum SubtitleMetrics implements SimilarityMetric {
private final String FPS = "FPS"; private final String FPS = "FPS";
private final String SECONDS = "SECS"; private final String SECONDS = "SECS";
@Override
public float getSimilarity(Object o1, Object o2) { public float getSimilarity(Object o1, Object o2) {
return o1 instanceof SubtitleDescriptor ? super.getSimilarity(o1, o2) : super.getSimilarity(o2, o1); // make sure that SubtitleDescriptor is o1 return o1 instanceof SubtitleDescriptor ? super.getSimilarity(o1, o2) : super.getSimilarity(o2, o1); // make sure that SubtitleDescriptor is o1
}; };
@Override
protected Map<String, Object> getProperties(Object object) { protected Map<String, Object> getProperties(Object object) {
if (object instanceof OpenSubtitlesSubtitleDescriptor) { if (object instanceof OpenSubtitlesSubtitleDescriptor) {
return getSubtitleProperties((OpenSubtitlesSubtitleDescriptor) object); return getSubtitleProperties((OpenSubtitlesSubtitleDescriptor) object);

View File

@ -12,18 +12,18 @@ import java.util.logging.Logger;
public abstract class SubtitleReader implements Iterator<SubtitleElement>, Closeable { public abstract class SubtitleReader implements Iterator<SubtitleElement>, Closeable {
protected final Scanner scanner; protected final Scanner scanner;
protected SubtitleElement current; protected SubtitleElement current;
public SubtitleReader(Readable source) { public SubtitleReader(Readable source) {
this.scanner = new Scanner(source); this.scanner = new Scanner(source);
} }
protected abstract SubtitleElement readNext() throws Exception; protected abstract SubtitleElement readNext() throws Exception;
@Override @Override
public boolean hasNext() { public boolean hasNext() {
@ -36,34 +36,34 @@ public abstract class SubtitleReader implements Iterator<SubtitleElement>, Close
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Illegal input: " + e.getMessage()); Logger.getLogger(getClass().getName()).log(Level.WARNING, "Illegal input: " + e.getMessage());
} }
} }
return current != null; return current != null;
} }
@Override @Override
public SubtitleElement next() { public SubtitleElement next() {
if (!hasNext()) { if (!hasNext()) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
try { try {
return current; return current;
} finally { } finally {
current = null; current = null;
} }
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
scanner.close(); scanner.close();
} }
@Override @Override
public void remove() { public void remove() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }

View File

@ -13,45 +13,45 @@ import java.util.regex.Pattern;
class SubtitleTimeFormat extends DateFormat { class SubtitleTimeFormat extends DateFormat {
public SubtitleTimeFormat() { public SubtitleTimeFormat() {
// calendar without any kind of special handling for time zone and daylight saving time // calendar without any kind of special handling for time zone and daylight saving time
calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ROOT); calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ROOT);
} }
@Override @Override
public StringBuffer format(Date date, StringBuffer sb, FieldPosition pos) { public StringBuffer format(Date date, StringBuffer sb, FieldPosition pos) {
// e.g. 1:42:52.42 // e.g. 1:42:52.42
calendar.setTime(date); calendar.setTime(date);
sb.append(String.format("%02d", calendar.get(Calendar.HOUR_OF_DAY))); sb.append(String.format("%02d", calendar.get(Calendar.HOUR_OF_DAY)));
sb.append(':').append(String.format("%02d", calendar.get(Calendar.MINUTE))); sb.append(':').append(String.format("%02d", calendar.get(Calendar.MINUTE)));
sb.append(':').append(String.format("%02d", calendar.get(Calendar.SECOND))); sb.append(':').append(String.format("%02d", calendar.get(Calendar.SECOND)));
String millis = String.format("%03d", calendar.get(Calendar.MILLISECOND)); String millis = String.format("%03d", calendar.get(Calendar.MILLISECOND));
sb.append('.').append(millis.substring(0, 2)); sb.append('.').append(millis.substring(0, 2));
return sb; return sb;
} }
private final Pattern delimiter = Pattern.compile("[:.]"); private final Pattern delimiter = Pattern.compile("[:.]");
@Override @Override
public Date parse(String source, ParsePosition pos) { public Date parse(String source, ParsePosition pos) {
String[] split = delimiter.split(source, 4); String[] split = delimiter.split(source, 4);
// reset state // reset state
calendar.clear(); calendar.clear();
try { try {
// handle hours:minutes:seconds // handle hours:minutes:seconds
calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(split[0])); calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(split[0]));
calendar.set(Calendar.MINUTE, Integer.parseInt(split[1])); calendar.set(Calendar.MINUTE, Integer.parseInt(split[1]));
calendar.set(Calendar.SECOND, Integer.parseInt(split[2])); calendar.set(Calendar.SECOND, Integer.parseInt(split[2]));
// handle hundredth seconds // handle hundredth seconds
calendar.set(Calendar.MILLISECOND, Integer.parseInt(split[3]) * 10); calendar.set(Calendar.MILLISECOND, Integer.parseInt(split[3]) * 10);
} catch (Exception e) { } catch (Exception e) {
@ -59,7 +59,7 @@ class SubtitleTimeFormat extends DateFormat {
pos.setErrorIndex(0); pos.setErrorIndex(0);
return null; return null;
} }
// update position // update position
pos.setIndex(source.length()); pos.setIndex(source.length());
return calendar.getTime(); return calendar.getTime();

View File

@ -1,9 +1,9 @@
/* /*
* BeDecoder.java * BeDecoder.java
* *
* Created on May 30, 2003, 2:44 PM * Created on May 30, 2003, 2:44 PM
* Copyright (C) 2003, 2004, 2005, 2006 Aelitis, All Rights Reserved. * Copyright (C) 2003, 2004, 2005, 2006 Aelitis, All Rights Reserved.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* *
* AELITIS, SAS au capital de 46,603.30 euros * AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*/ */
@ -37,190 +37,190 @@ import java.util.Map;
/** /**
* A set of utility methods to decode a bencoded array of byte into a Map. integer are * A set of utility methods to decode a bencoded array of byte into a Map. integer are
* represented as Long, String as byte[], dictionnaries as Map, and list as List. * represented as Long, String as byte[], dictionnaries as Map, and list as List.
* *
* @author TdC_VgA * @author TdC_VgA
*/ */
class BDecoder { class BDecoder {
private static final Charset BINARY_CHARSET = Charset.forName("ISO-8859-1"); private static final Charset BINARY_CHARSET = Charset.forName("ISO-8859-1");
public static Map<?, ?> decode(InputStream is) throws IOException { public static Map<?, ?> decode(InputStream is) throws IOException {
return (new BDecoder().decodeStream(is)); return (new BDecoder().decodeStream(is));
} }
public Map<?, ?> decodeStream(InputStream data) throws IOException { public Map<?, ?> decodeStream(InputStream data) throws IOException {
Object res = decodeInputStream(data, 0); Object res = decodeInputStream(data, 0);
if (res == null) if (res == null)
throw (new IOException("BDecoder: zero length file")); throw (new IOException("BDecoder: zero length file"));
else if (!(res instanceof Map)) else if (!(res instanceof Map))
throw (new IOException("BDecoder: top level isn't a Map")); throw (new IOException("BDecoder: top level isn't a Map"));
return ((Map<?, ?>) res); return ((Map<?, ?>) res);
} }
private Object decodeInputStream(InputStream bais, int nesting) throws IOException { private Object decodeInputStream(InputStream bais, int nesting) throws IOException {
if (!bais.markSupported()) if (!bais.markSupported())
throw new IOException("InputStream must support the mark() method"); throw new IOException("InputStream must support the mark() method");
// set a mark // set a mark
bais.mark(Integer.MAX_VALUE); bais.mark(Integer.MAX_VALUE);
// read a byte // read a byte
int tempByte = bais.read(); int tempByte = bais.read();
// decide what to do // decide what to do
switch (tempByte) { switch (tempByte) {
case 'd': case 'd':
// create a new dictionary object // create a new dictionary object
Map<String, Object> tempMap = new HashMap<String, Object>(); Map<String, Object> tempMap = new HashMap<String, Object>();
// get the key // get the key
byte[] tempByteArray = null; byte[] tempByteArray = null;
while ((tempByteArray = (byte[]) decodeInputStream(bais, nesting + 1)) != null) { while ((tempByteArray = (byte[]) decodeInputStream(bais, nesting + 1)) != null) {
// decode some more // decode some more
Object value = decodeInputStream(bais, nesting + 1); Object value = decodeInputStream(bais, nesting + 1);
// add the value to the map // add the value to the map
CharBuffer cb = BINARY_CHARSET.decode(ByteBuffer.wrap(tempByteArray)); CharBuffer cb = BINARY_CHARSET.decode(ByteBuffer.wrap(tempByteArray));
String key = new String(cb.array(), 0, cb.limit()); String key = new String(cb.array(), 0, cb.limit());
tempMap.put(key, value); tempMap.put(key, value);
}
if (bais.available() < nesting)
throw (new IOException("BDecoder: invalid input data, 'e' missing from end of dictionary"));
// return the map
return tempMap;
case 'l':
// create the list
List<Object> tempList = new ArrayList<Object>();
// create the key
Object tempElement = null;
while ((tempElement = decodeInputStream(bais, nesting + 1)) != null)
// add the element
tempList.add(tempElement);
if (bais.available() < nesting)
throw (new IOException("BDecoder: invalid input data, 'e' missing from end of list"));
// return the list
return tempList;
case 'e':
case -1:
return null;
case 'i':
return new Long(getNumberFromStream(bais, 'e'));
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// move back one
bais.reset();
// get the string
return getByteArrayFromStream(bais);
default: {
int rem_len = bais.available();
if (rem_len > 256)
rem_len = 256;
byte[] rem_data = new byte[rem_len];
bais.read(rem_data);
throw (new IOException("BDecoder: unknown command '" + tempByte + ", remainder = " + new String(rem_data)));
} }
if (bais.available() < nesting)
throw (new IOException("BDecoder: invalid input data, 'e' missing from end of dictionary"));
// return the map
return tempMap;
case 'l':
// create the list
List<Object> tempList = new ArrayList<Object>();
// create the key
Object tempElement = null;
while ((tempElement = decodeInputStream(bais, nesting + 1)) != null)
// add the element
tempList.add(tempElement);
if (bais.available() < nesting)
throw (new IOException("BDecoder: invalid input data, 'e' missing from end of list"));
// return the list
return tempList;
case 'e':
case -1:
return null;
case 'i':
return new Long(getNumberFromStream(bais, 'e'));
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// move back one
bais.reset();
// get the string
return getByteArrayFromStream(bais);
default: {
int rem_len = bais.available();
if (rem_len > 256)
rem_len = 256;
byte[] rem_data = new byte[rem_len];
bais.read(rem_data);
throw (new IOException("BDecoder: unknown command '" + tempByte + ", remainder = " + new String(rem_data)));
}
} }
} }
private long getNumberFromStream(InputStream bais, char parseChar) throws IOException { private long getNumberFromStream(InputStream bais, char parseChar) throws IOException {
int length = 0; int length = 0;
// place a mark // place a mark
bais.mark(Integer.MAX_VALUE); bais.mark(Integer.MAX_VALUE);
int tempByte = bais.read(); int tempByte = bais.read();
while ((tempByte != parseChar) && (tempByte >= 0)) { while ((tempByte != parseChar) && (tempByte >= 0)) {
tempByte = bais.read(); tempByte = bais.read();
length++; length++;
} }
// are we at the end of the stream? // are we at the end of the stream?
if (tempByte < 0) if (tempByte < 0)
return -1; return -1;
// reset the mark // reset the mark
bais.reset(); bais.reset();
// get the length // get the length
byte[] tempArray = new byte[length]; byte[] tempArray = new byte[length];
int count = 0; int count = 0;
int len = 0; int len = 0;
// get the string // get the string
while ((count != length) && ((len = bais.read(tempArray, count, length - count)) > 0)) while ((count != length) && ((len = bais.read(tempArray, count, length - count)) > 0))
count += len; count += len;
// jump ahead in the stream to compensate for the : // jump ahead in the stream to compensate for the :
bais.skip(1); bais.skip(1);
// return the value // return the value
CharBuffer cb = BINARY_CHARSET.decode(ByteBuffer.wrap(tempArray)); CharBuffer cb = BINARY_CHARSET.decode(ByteBuffer.wrap(tempArray));
String str_value = new String(cb.array(), 0, cb.limit()); String str_value = new String(cb.array(), 0, cb.limit());
return Long.parseLong(str_value); return Long.parseLong(str_value);
} }
private byte[] getByteArrayFromStream(InputStream bais) throws IOException { private byte[] getByteArrayFromStream(InputStream bais) throws IOException {
int length = (int) getNumberFromStream(bais, ':'); int length = (int) getNumberFromStream(bais, ':');
if (length < 0) if (length < 0)
return null; return null;
// note that torrent hashes can be big (consider a 55GB file with 2MB // note that torrent hashes can be big (consider a 55GB file with 2MB
// pieces // pieces
// this generates a pieces hash of 1/2 meg // this generates a pieces hash of 1/2 meg
if (length > 8 * 1024 * 1024) if (length > 8 * 1024 * 1024)
throw (new IOException("Byte array length too large (" + length + ")")); throw (new IOException("Byte array length too large (" + length + ")"));
byte[] tempArray = new byte[length]; byte[] tempArray = new byte[length];
int count = 0; int count = 0;
int len = 0; int len = 0;
// get the string // get the string
while ((count != length) && ((len = bais.read(tempArray, count, length - count)) > 0)) while ((count != length) && ((len = bais.read(tempArray, count, length - count)) > 0))
count += len; count += len;
if (count != tempArray.length) if (count != tempArray.length)
throw (new IOException("BDecoder::getByteArrayFromStream: truncated")); throw (new IOException("BDecoder::getByteArrayFromStream: truncated"));
return tempArray; return tempArray;
} }
} }

View File

@ -16,7 +16,7 @@ import java.util.Map;
public class Torrent { public class Torrent {
private String name; private String name;
private String encoding; private String encoding;
private String createdBy; private String createdBy;
@ -24,188 +24,188 @@ public class Torrent {
private String comment; private String comment;
private Long creationDate; private Long creationDate;
private Long pieceLength; private Long pieceLength;
private List<Entry> files; private List<Entry> files;
private boolean singleFileTorrent; private boolean singleFileTorrent;
protected Torrent() { protected Torrent() {
// used by serializer // used by serializer
} }
public Torrent(File torrent) throws IOException { public Torrent(File torrent) throws IOException {
this(decodeTorrent(torrent)); this(decodeTorrent(torrent));
} }
public Torrent(Map<?, ?> torrentMap) { public Torrent(Map<?, ?> torrentMap) {
Charset charset = Charset.forName("UTF-8"); Charset charset = Charset.forName("UTF-8");
encoding = decodeString(torrentMap.get("encoding"), charset); encoding = decodeString(torrentMap.get("encoding"), charset);
try { try {
charset = Charset.forName(encoding); charset = Charset.forName(encoding);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// invalid encoding, just keep using UTF-8 // invalid encoding, just keep using UTF-8
} }
createdBy = decodeString(torrentMap.get("created by"), charset); createdBy = decodeString(torrentMap.get("created by"), charset);
announce = decodeString(torrentMap.get("announce"), charset); announce = decodeString(torrentMap.get("announce"), charset);
comment = decodeString(torrentMap.get("comment"), charset); comment = decodeString(torrentMap.get("comment"), charset);
creationDate = decodeLong(torrentMap.get("creation date")); creationDate = decodeLong(torrentMap.get("creation date"));
Map<?, ?> infoMap = (Map<?, ?>) torrentMap.get("info"); Map<?, ?> infoMap = (Map<?, ?>) torrentMap.get("info");
name = decodeString(infoMap.get("name"), charset); name = decodeString(infoMap.get("name"), charset);
pieceLength = (Long) infoMap.get("piece length"); pieceLength = (Long) infoMap.get("piece length");
if (infoMap.containsKey("files")) { if (infoMap.containsKey("files")) {
// torrent contains multiple entries // torrent contains multiple entries
singleFileTorrent = false; singleFileTorrent = false;
List<Entry> entries = new ArrayList<Entry>(); List<Entry> entries = new ArrayList<Entry>();
for (Object fileMapObject : (List<?>) infoMap.get("files")) { for (Object fileMapObject : (List<?>) infoMap.get("files")) {
Map<?, ?> fileMap = (Map<?, ?>) fileMapObject; Map<?, ?> fileMap = (Map<?, ?>) fileMapObject;
List<?> pathList = (List<?>) fileMap.get("path"); List<?> pathList = (List<?>) fileMap.get("path");
StringBuilder path = new StringBuilder(80); StringBuilder path = new StringBuilder(80);
Iterator<?> iterator = pathList.iterator(); Iterator<?> iterator = pathList.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
// append path element // append path element
path.append(decodeString(iterator.next(), charset)); path.append(decodeString(iterator.next(), charset));
// append separator // append separator
if (iterator.hasNext()) { if (iterator.hasNext()) {
path.append("/"); path.append("/");
} }
} }
Long length = decodeLong(fileMap.get("length")); Long length = decodeLong(fileMap.get("length"));
entries.add(new Entry(path.toString(), length)); entries.add(new Entry(path.toString(), length));
} }
files = Collections.unmodifiableList(entries); files = Collections.unmodifiableList(entries);
} else { } else {
// single file torrent // single file torrent
singleFileTorrent = true; singleFileTorrent = true;
Long length = decodeLong(infoMap.get("length")); Long length = decodeLong(infoMap.get("length"));
files = Collections.singletonList(new Entry(name, length)); files = Collections.singletonList(new Entry(name, length));
} }
} }
private static Map<?, ?> decodeTorrent(File torrent) throws IOException { private static Map<?, ?> decodeTorrent(File torrent) throws IOException {
InputStream in = new BufferedInputStream(new FileInputStream(torrent)); InputStream in = new BufferedInputStream(new FileInputStream(torrent));
try { try {
return BDecoder.decode(in); return BDecoder.decode(in);
} finally { } finally {
in.close(); in.close();
} }
} }
private String decodeString(Object byteArray, Charset charset) { private String decodeString(Object byteArray, Charset charset) {
if (byteArray == null) if (byteArray == null)
return null; return null;
return new String((byte[]) byteArray, charset); return new String((byte[]) byteArray, charset);
} }
private Long decodeLong(Object number) { private Long decodeLong(Object number) {
if (number == null) if (number == null)
return null; return null;
return (Long) number; return (Long) number;
} }
public String getAnnounce() { public String getAnnounce() {
return announce; return announce;
} }
public String getComment() { public String getComment() {
return comment; return comment;
} }
public String getCreatedBy() { public String getCreatedBy() {
return createdBy; return createdBy;
} }
public Long getCreationDate() { public Long getCreationDate() {
return creationDate; return creationDate;
} }
public String getEncoding() { public String getEncoding() {
return encoding; return encoding;
} }
public List<Entry> getFiles() { public List<Entry> getFiles() {
return files; return files;
} }
public String getName() { public String getName() {
return name; return name;
} }
public Long getPieceLength() { public Long getPieceLength() {
return pieceLength; return pieceLength;
} }
public boolean isSingleFileTorrent() { public boolean isSingleFileTorrent() {
return singleFileTorrent; return singleFileTorrent;
} }
public static class Entry { public static class Entry {
private final String path; private final String path;
private final long length; private final long length;
public Entry(String path, long length) { public Entry(String path, long length) {
this.path = path; this.path = path;
this.length = length; this.length = length;
} }
public String getPath() { public String getPath() {
return path; return path;
} }
public String getName() { public String getName() {
// the last element in the path is the filename // the last element in the path is the filename
// torrents don't contain directory entries, so there is always a non-empty name // torrents don't contain directory entries, so there is always a non-empty name
return path.substring(path.lastIndexOf("/") + 1); return path.substring(path.lastIndexOf("/") + 1);
} }
public long getLength() { public long getLength() {
return length; return length;
} }
@Override @Override
public String toString() { public String toString() {
return getPath(); return getPath();
} }
} }
} }

View File

@ -8,20 +8,20 @@ import net.filebot.ui.transfer.TextFileExportHandler;
public class FileBotListExportHandler extends TextFileExportHandler { public class FileBotListExportHandler extends TextFileExportHandler {
protected final FileBotList<?> list; protected final FileBotList<?> list;
public FileBotListExportHandler(FileBotList<?> list) { public FileBotListExportHandler(FileBotList<?> list) {
this.list = list; this.list = list;
} }
@Override @Override
public boolean canExport() { public boolean canExport() {
return list.getModel().size() > 0; return list.getModel().size() > 0;
} }
@Override @Override
public void export(PrintWriter out) { public void export(PrintWriter out) {
@ -29,11 +29,11 @@ public class FileBotListExportHandler extends TextFileExportHandler {
out.println(entry); out.println(entry);
} }
} }
@Override @Override
public String getDefaultFileName() { public String getDefaultFileName() {
return list.getTitle() + ".txt"; return list.getTitle() + ".txt";
} }
} }

View File

@ -13,91 +13,91 @@ import javax.swing.SwingUtilities;
public class FileBotTab<T extends JComponent> extends JComponent { public class FileBotTab<T extends JComponent> extends JComponent {
private final FileBotTabComponent tabComponent = new FileBotTabComponent(); private final FileBotTabComponent tabComponent = new FileBotTabComponent();
private final T component; private final T component;
public FileBotTab(T component) { public FileBotTab(T component) {
this.component = component; this.component = component;
tabComponent.getCloseButton().addActionListener(closeAction); tabComponent.getCloseButton().addActionListener(closeAction);
setLayout(new BorderLayout()); setLayout(new BorderLayout());
add(component, BorderLayout.CENTER); add(component, BorderLayout.CENTER);
} }
public void addTo(JTabbedPane tabbedPane) { public void addTo(JTabbedPane tabbedPane) {
tabbedPane.addTab(this.getTitle(), this); tabbedPane.addTab(this.getTitle(), this);
tabbedPane.setTabComponentAt(tabbedPane.indexOfComponent(this), tabComponent); tabbedPane.setTabComponentAt(tabbedPane.indexOfComponent(this), tabComponent);
} }
public void close() { public void close() {
if (!isClosed()) { if (!isClosed()) {
getTabbedPane().remove(this); getTabbedPane().remove(this);
} }
} }
public boolean isClosed() { public boolean isClosed() {
JTabbedPane tabbedPane = getTabbedPane(); JTabbedPane tabbedPane = getTabbedPane();
if (tabbedPane == null) if (tabbedPane == null)
return true; return true;
return getTabbedPane().indexOfComponent(this) < 0; return getTabbedPane().indexOfComponent(this) < 0;
} }
private JTabbedPane getTabbedPane() { private JTabbedPane getTabbedPane() {
return (JTabbedPane) SwingUtilities.getAncestorOfClass(JTabbedPane.class, this); return (JTabbedPane) SwingUtilities.getAncestorOfClass(JTabbedPane.class, this);
} }
public T getComponent() { public T getComponent() {
return component; return component;
} }
public FileBotTabComponent getTabComponent() { public FileBotTabComponent getTabComponent() {
return tabComponent; return tabComponent;
} }
public void setTitle(String title) { public void setTitle(String title) {
tabComponent.setText(title); tabComponent.setText(title);
} }
public String getTitle() { public String getTitle() {
return tabComponent.getText(); return tabComponent.getText();
} }
public void setIcon(Icon icon) { public void setIcon(Icon icon) {
tabComponent.setIcon(icon); tabComponent.setIcon(icon);
} }
public Icon getIcon() { public Icon getIcon() {
return tabComponent.getIcon(); return tabComponent.getIcon();
} }
public void setLoading(boolean loading) { public void setLoading(boolean loading) {
tabComponent.setLoading(loading); tabComponent.setLoading(loading);
} }
private final ActionListener closeAction = new ActionListener() { private final ActionListener closeAction = new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
close(); close();
} }
}; };
} }

View File

@ -18,84 +18,84 @@ import net.miginfocom.swing.MigLayout;
public class FileBotTabComponent extends JComponent { public class FileBotTabComponent extends JComponent {
private ProgressIndicator progressIndicator = new ProgressIndicator(); private ProgressIndicator progressIndicator = new ProgressIndicator();
private JLabel textLabel = new JLabel(); private JLabel textLabel = new JLabel();
private JLabel iconLabel = new JLabel(); private JLabel iconLabel = new JLabel();
private AbstractButton closeButton = createCloseButton(); private AbstractButton closeButton = createCloseButton();
private boolean loading = false; private boolean loading = false;
public FileBotTabComponent() { public FileBotTabComponent() {
iconLabel.setHorizontalAlignment(SwingConstants.CENTER); iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
textLabel.setHorizontalAlignment(SwingConstants.LEFT); textLabel.setHorizontalAlignment(SwingConstants.LEFT);
progressIndicator.setVisible(loading); progressIndicator.setVisible(loading);
progressIndicator.setMinimumSize(new Dimension(16, 16)); progressIndicator.setMinimumSize(new Dimension(16, 16));
setLayout(new MigLayout("nogrid, insets 0 0 1 3")); setLayout(new MigLayout("nogrid, insets 0 0 1 3"));
add(progressIndicator, "hidemode 3"); add(progressIndicator, "hidemode 3");
add(iconLabel, "hidemode 3"); add(iconLabel, "hidemode 3");
add(textLabel, "gap rel, align left"); add(textLabel, "gap rel, align left");
add(closeButton, "gap unrel:push, hidemode 3, align center 45%"); add(closeButton, "gap unrel:push, hidemode 3, align center 45%");
} }
public void setLoading(boolean loading) { public void setLoading(boolean loading) {
this.loading = loading; this.loading = loading;
progressIndicator.setVisible(loading); progressIndicator.setVisible(loading);
iconLabel.setVisible(!loading); iconLabel.setVisible(!loading);
} }
public boolean isLoading() { public boolean isLoading() {
return loading; return loading;
} }
public void setIcon(Icon icon) { public void setIcon(Icon icon) {
iconLabel.setIcon(icon); iconLabel.setIcon(icon);
progressIndicator.setPreferredSize(icon != null ? SwingUI.getDimension(icon) : progressIndicator.getMinimumSize()); progressIndicator.setPreferredSize(icon != null ? SwingUI.getDimension(icon) : progressIndicator.getMinimumSize());
} }
public Icon getIcon() { public Icon getIcon() {
return iconLabel.getIcon(); return iconLabel.getIcon();
} }
public void setText(String text) { public void setText(String text) {
textLabel.setText(text); textLabel.setText(text);
} }
public String getText() { public String getText() {
return textLabel.getText(); return textLabel.getText();
} }
public AbstractButton getCloseButton() { public AbstractButton getCloseButton() {
return closeButton; return closeButton;
} }
protected AbstractButton createCloseButton() { protected AbstractButton createCloseButton() {
Icon icon = ResourceManager.getIcon("tab.close"); Icon icon = ResourceManager.getIcon("tab.close");
Icon rolloverIcon = ResourceManager.getIcon("tab.close.hover"); Icon rolloverIcon = ResourceManager.getIcon("tab.close.hover");
JButton button = new JButton(icon); JButton button = new JButton(icon);
button.setRolloverIcon(rolloverIcon); button.setRolloverIcon(rolloverIcon);
button.setPreferredSize(SwingUI.getDimension(rolloverIcon)); button.setPreferredSize(SwingUI.getDimension(rolloverIcon));
button.setMaximumSize(button.getPreferredSize()); button.setMaximumSize(button.getPreferredSize());
button.setContentAreaFilled(false); button.setContentAreaFilled(false);
button.setBorderPainted(false); button.setBorderPainted(false);
button.setFocusable(false); button.setFocusable(false);
button.setRolloverEnabled(true); button.setRolloverEnabled(true);
return button; return button;
} }
} }

View File

@ -97,11 +97,13 @@ public class LanguageComboBoxModel extends AbstractListModel implements ComboBox
return data.get(index); return data.get(index);
} }
@Override
public boolean add(Language element) { public boolean add(Language element) {
// add first // add first
return addIfAbsent(0, element); return addIfAbsent(0, element);
} }
@Override
public void add(int index, Language element) { public void add(int index, Language element) {
addIfAbsent(index, element); addIfAbsent(index, element);
} }

View File

@ -125,10 +125,12 @@ public class MainFrame extends JFrame {
GroovyPad pad = new GroovyPad(); GroovyPad pad = new GroovyPad();
pad.addWindowListener(new WindowAdapter() { pad.addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) { public void windowOpened(WindowEvent e) {
MainFrame.this.setVisible(false); MainFrame.this.setVisible(false);
}; };
@Override
public void windowClosing(WindowEvent e) { public void windowClosing(WindowEvent e) {
MainFrame.this.setVisible(true); MainFrame.this.setVisible(true);
}; };

View File

@ -23,52 +23,52 @@ import net.filebot.util.ui.notification.QueueNotificationLayout;
public class NotificationLogging extends Handler { public class NotificationLogging extends Handler {
public static final Logger UILogger = createNotificationLogger("net.filebot.logger.ui"); public static final Logger UILogger = createNotificationLogger("net.filebot.logger.ui");
private static Logger createNotificationLogger(String name) { private static Logger createNotificationLogger(String name) {
Logger log = Logger.getLogger(name); Logger log = Logger.getLogger(name);
// don't use parent handlers // don't use parent handlers
log.setUseParentHandlers(false); log.setUseParentHandlers(false);
// ui handler // ui handler
log.addHandler(new NotificationLogging()); log.addHandler(new NotificationLogging());
// console handler (for warnings and errors only) // console handler (for warnings and errors only)
ConsoleHandler console = new ConsoleHandler(); ConsoleHandler console = new ConsoleHandler();
console.setLevel(Level.WARNING); console.setLevel(Level.WARNING);
log.addHandler(console); log.addHandler(console);
return log; return log;
} }
public final NotificationManager notificationManager; public final NotificationManager notificationManager;
public final int timeout = 2500; public final int timeout = 2500;
public NotificationLogging() { public NotificationLogging() {
this(new NotificationManager(new QueueNotificationLayout(NORTH, SOUTH))); this(new NotificationManager(new QueueNotificationLayout(NORTH, SOUTH)));
} }
public NotificationLogging(NotificationManager notificationManager) { public NotificationLogging(NotificationManager notificationManager) {
this.notificationManager = notificationManager; this.notificationManager = notificationManager;
} }
@Override @Override
public void publish(LogRecord record) { public void publish(LogRecord record) {
// fail gracefully on an headless machine // fail gracefully on an headless machine
if (GraphicsEnvironment.isHeadless()) if (GraphicsEnvironment.isHeadless())
return; return;
final Level level = record.getLevel(); final Level level = record.getLevel();
final String message = getMessage(record); final String message = getMessage(record);
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@Override @Override
public void run() { public void run() {
if (level == Level.INFO) { if (level == Level.INFO) {
@ -81,34 +81,34 @@ public class NotificationLogging extends Handler {
} }
}); });
} }
protected String getMessage(LogRecord record) { protected String getMessage(LogRecord record) {
String message = record.getMessage(); String message = record.getMessage();
if ((message == null || message.isEmpty()) && record.getThrown() != null) { if ((message == null || message.isEmpty()) && record.getThrown() != null) {
// if message is empty, display exception string // if message is empty, display exception string
return ExceptionUtilities.getMessage(record.getThrown()); return ExceptionUtilities.getMessage(record.getThrown());
} }
return message; return message;
} }
protected void show(String message, Icon icon, int timeout) { protected void show(String message, Icon icon, int timeout) {
notificationManager.show(new MessageNotification(getApplicationName(), message, icon, timeout)); notificationManager.show(new MessageNotification(getApplicationName(), message, icon, timeout));
} }
@Override @Override
public void close() throws SecurityException { public void close() throws SecurityException {
} }
@Override @Override
public void flush() { public void flush() {
} }
} }

View File

@ -7,13 +7,13 @@ import javax.swing.JComponent;
public interface PanelBuilder { public interface PanelBuilder {
public String getName(); public String getName();
public Icon getIcon(); public Icon getIcon();
public JComponent create(); public JComponent create();
} }

View File

@ -37,6 +37,7 @@ public class AnalyzePanel extends JComponent {
private final PropertyChangeListener filetreeListener = new PropertyChangeListener() { private final PropertyChangeListener filetreeListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
// stopped loading, refresh tools // stopped loading, refresh tools
for (int i = 0; i < toolsPanel.getTabCount(); i++) { for (int i = 0; i < toolsPanel.getTabCount(); i++) {

View File

@ -10,22 +10,22 @@ import net.filebot.ui.PanelBuilder;
public class AnalyzePanelBuilder implements PanelBuilder { public class AnalyzePanelBuilder implements PanelBuilder {
@Override @Override
public String getName() { public String getName() {
return "Analyze"; return "Analyze";
} }
@Override @Override
public Icon getIcon() { public Icon getIcon() {
return ResourceManager.getIcon("panel.analyze"); return ResourceManager.getIcon("panel.analyze");
} }
@Override @Override
public JComponent create() { public JComponent create() {
return new AnalyzePanel(); return new AnalyzePanel();
} }
} }

View File

@ -114,6 +114,7 @@ public class FileTree extends JTree {
putValue("files", files); putValue("files", files);
} }
@Override
public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) {
UserFiles.revealFiles((Collection<File>) getValue("files")); UserFiles.revealFiles((Collection<File>) getValue("files"));
} }

View File

@ -13,15 +13,15 @@ import net.filebot.util.ui.GradientStyle;
public class FileTreeCellRenderer extends FancyTreeCellRenderer { public class FileTreeCellRenderer extends FancyTreeCellRenderer {
public FileTreeCellRenderer() { public FileTreeCellRenderer() {
super(GradientStyle.TOP_TO_BOTTOM); super(GradientStyle.TOP_TO_BOTTOM);
openIcon = ResourceManager.getIcon("tree.open"); openIcon = ResourceManager.getIcon("tree.open");
closedIcon = ResourceManager.getIcon("tree.closed"); closedIcon = ResourceManager.getIcon("tree.closed");
leafIcon = ResourceManager.getIcon("tree.leaf"); leafIcon = ResourceManager.getIcon("tree.leaf");
} }
@Override @Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
@ -30,17 +30,17 @@ public class FileTreeCellRenderer extends FancyTreeCellRenderer {
expanded = true; expanded = true;
leaf = false; leaf = false;
} }
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
return this; return this;
} }
private boolean isFolder(Object value) { private boolean isFolder(Object value) {
if (((TreeNode) value).getAllowsChildren()) if (((TreeNode) value).getAllowsChildren())
return true; return true;
return false; return false;
} }
} }

View File

@ -7,7 +7,6 @@ import static net.filebot.util.FileUtilities.*;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@ -15,8 +14,6 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;

View File

@ -10,22 +10,22 @@ import net.filebot.ui.PanelBuilder;
public class EpisodeListPanelBuilder implements PanelBuilder { public class EpisodeListPanelBuilder implements PanelBuilder {
@Override @Override
public String getName() { public String getName() {
return "Episodes"; return "Episodes";
} }
@Override @Override
public Icon getIcon() { public Icon getIcon() {
return ResourceManager.getIcon("panel.episodelist"); return ResourceManager.getIcon("panel.episodelist");
} }
@Override @Override
public JComponent create() { public JComponent create() {
return new EpisodeListPanel(); return new EpisodeListPanel();
} }
} }

View File

@ -6,47 +6,47 @@ import javax.swing.SpinnerNumberModel;
class SeasonSpinnerModel extends SpinnerNumberModel { class SeasonSpinnerModel extends SpinnerNumberModel {
public static final int ALL_SEASONS = 0; public static final int ALL_SEASONS = 0;
public static final int MAX_VALUE = 99; public static final int MAX_VALUE = 99;
private Number valueBeforeLock = null; private Number valueBeforeLock = null;
public SeasonSpinnerModel() { public SeasonSpinnerModel() {
super(ALL_SEASONS, ALL_SEASONS, MAX_VALUE, 1); super(ALL_SEASONS, ALL_SEASONS, MAX_VALUE, 1);
} }
public int getSeason() { public int getSeason() {
return getNumber().intValue(); return getNumber().intValue();
} }
@Override @Override
public Integer getMinimum() { public Integer getMinimum() {
return (Integer) super.getMinimum(); return (Integer) super.getMinimum();
} }
@Override @Override
public Integer getMaximum() { public Integer getMaximum() {
return (Integer) super.getMaximum(); return (Integer) super.getMaximum();
} }
public void spin(int steps) { public void spin(int steps) {
int next = getSeason() + steps; int next = getSeason() + steps;
if (next < getMinimum()) if (next < getMinimum())
next = getMinimum(); next = getMinimum();
else if (next > getMaximum()) else if (next > getMaximum())
next = getMaximum(); next = getMaximum();
setValue(next); setValue(next);
} }
public void lock(int value) { public void lock(int value) {
valueBeforeLock = getNumber(); valueBeforeLock = getNumber();
@ -54,12 +54,12 @@ class SeasonSpinnerModel extends SpinnerNumberModel {
setMaximum(value); setMaximum(value);
setValue(value); setValue(value);
} }
public void unlock() { public void unlock() {
setMinimum(ALL_SEASONS); setMinimum(ALL_SEASONS);
setMaximum(MAX_VALUE); setMaximum(MAX_VALUE);
if (valueBeforeLock != null) { if (valueBeforeLock != null) {
setValue(valueBeforeLock); setValue(valueBeforeLock);
valueBeforeLock = null; valueBeforeLock = null;

View File

@ -10,22 +10,22 @@ import net.filebot.ui.PanelBuilder;
public class ListPanelBuilder implements PanelBuilder { public class ListPanelBuilder implements PanelBuilder {
@Override @Override
public String getName() { public String getName() {
return "List"; return "List";
} }
@Override @Override
public Icon getIcon() { public Icon getIcon() {
return ResourceManager.getIcon("panel.list"); return ResourceManager.getIcon("panel.list");
} }
@Override @Override
public JComponent create() { public JComponent create() {
return new ListPanel(); return new ListPanel();
} }
} }

View File

@ -21,42 +21,42 @@ import net.filebot.util.ui.GradientStyle;
class CharacterHighlightPainter implements Highlighter.HighlightPainter { class CharacterHighlightPainter implements Highlighter.HighlightPainter {
private Color gradientBeginColor; private Color gradientBeginColor;
private Color gradientEndColor; private Color gradientEndColor;
public CharacterHighlightPainter(Color gradientBeginColor, Color gradientEndColor) { public CharacterHighlightPainter(Color gradientBeginColor, Color gradientEndColor) {
this.gradientBeginColor = gradientBeginColor; this.gradientBeginColor = gradientBeginColor;
this.gradientEndColor = gradientEndColor; this.gradientEndColor = gradientEndColor;
} }
@Override @Override
public void paint(Graphics g, int offset1, int offset2, Shape bounds, JTextComponent c) { public void paint(Graphics g, int offset1, int offset2, Shape bounds, JTextComponent c) {
Graphics2D g2d = (Graphics2D) g; Graphics2D g2d = (Graphics2D) g;
try { try {
// determine locations // determine locations
TextUI mapper = c.getUI(); TextUI mapper = c.getUI();
Rectangle p1 = mapper.modelToView(c, offset1); Rectangle p1 = mapper.modelToView(c, offset1);
Rectangle p2 = mapper.modelToView(c, offset2); Rectangle p2 = mapper.modelToView(c, offset2);
Rectangle r = p1.union(p2); Rectangle r = p1.union(p2);
float w = r.width + 1; float w = r.width + 1;
float h = r.height; float h = r.height;
float x = r.x - 1; float x = r.x - 1;
float y = r.y; float y = r.y;
float arch = 5f; float arch = 5f;
RoundRectangle2D shape = new RoundRectangle2D.Float(x, y, w, h, arch, arch); RoundRectangle2D shape = new RoundRectangle2D.Float(x, y, w, h, arch, arch);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(GradientStyle.TOP_TO_BOTTOM.getGradientPaint(shape, gradientBeginColor, gradientEndColor)); g2d.setPaint(GradientStyle.TOP_TO_BOTTOM.getGradientPaint(shape, gradientBeginColor, gradientEndColor));
g2d.fill(shape); g2d.fill(shape);
} catch (BadLocationException e) { } catch (BadLocationException e) {
//should not happen //should not happen

View File

@ -14,55 +14,55 @@ import net.filebot.similarity.Match;
class ExpressionFormatter implements MatchFormatter { class ExpressionFormatter implements MatchFormatter {
private final String expression; private final String expression;
private ExpressionFormat format; private ExpressionFormat format;
private Format preview; private Format preview;
private Class<?> target; private Class<?> target;
public ExpressionFormatter(String expression, Format preview, Class<?> target) { public ExpressionFormatter(String expression, Format preview, Class<?> target) {
if (expression == null || expression.isEmpty()) if (expression == null || expression.isEmpty())
throw new IllegalArgumentException("Expression must not be null or empty"); throw new IllegalArgumentException("Expression must not be null or empty");
this.expression = expression; this.expression = expression;
this.preview = preview; this.preview = preview;
this.target = target; this.target = target;
} }
@Override @Override
public boolean canFormat(Match<?, ?> match) { public boolean canFormat(Match<?, ?> match) {
// target object is required, file is optional // target object is required, file is optional
return target.isInstance(match.getValue()) && (match.getCandidate() == null || match.getCandidate() instanceof File); return target.isInstance(match.getValue()) && (match.getCandidate() == null || match.getCandidate() instanceof File);
} }
@Override @Override
public String preview(Match<?, ?> match) { public String preview(Match<?, ?> match) {
return preview != null ? preview.format(match.getValue()) : match.getValue().toString(); return preview != null ? preview.format(match.getValue()) : match.getValue().toString();
} }
@Override @Override
public synchronized String format(Match<?, ?> match, Map<?, ?> context) throws ScriptException { public synchronized String format(Match<?, ?> match, Map<?, ?> context) throws ScriptException {
// lazy initialize script engine // lazy initialize script engine
if (format == null) { if (format == null) {
format = new ExpressionFormat(expression); format = new ExpressionFormat(expression);
} }
// evaluate the expression using the given bindings // evaluate the expression using the given bindings
Object bindingBean = new MediaBindingBean(match.getValue(), (File) match.getCandidate(), (Map<File, Object>) context); Object bindingBean = new MediaBindingBean(match.getValue(), (File) match.getCandidate(), (Map<File, Object>) context);
String result = format.format(bindingBean).trim(); String result = format.format(bindingBean).trim();
// if result is empty, check for script exceptions // if result is empty, check for script exceptions
if (result.isEmpty() && format.caughtScriptException() != null) { if (result.isEmpty() && format.caughtScriptException() != null) {
throw format.caughtScriptException(); throw format.caughtScriptException();
} }
return result; return result;
} }
} }

View File

@ -11,47 +11,47 @@ import net.filebot.vfs.FileInfo;
class FileNameFormatter implements MatchFormatter { class FileNameFormatter implements MatchFormatter {
private boolean preserveExtension; private boolean preserveExtension;
public FileNameFormatter(boolean preserveExtension) { public FileNameFormatter(boolean preserveExtension) {
this.preserveExtension = preserveExtension; this.preserveExtension = preserveExtension;
} }
@Override @Override
public boolean canFormat(Match<?, ?> match) { public boolean canFormat(Match<?, ?> match) {
return match.getValue() instanceof File || match.getValue() instanceof FileInfo || match.getValue() instanceof String; return match.getValue() instanceof File || match.getValue() instanceof FileInfo || match.getValue() instanceof String;
} }
@Override @Override
public String preview(Match<?, ?> match) { public String preview(Match<?, ?> match) {
return format(match, null); return format(match, null);
} }
@Override @Override
public String format(Match<?, ?> match, Map<?, ?> context) { public String format(Match<?, ?> match, Map<?, ?> context) {
Object value = match.getValue(); Object value = match.getValue();
if (value instanceof File) { if (value instanceof File) {
File file = (File) value; File file = (File) value;
return preserveExtension ? FileUtilities.getName(file) : file.getName(); return preserveExtension ? FileUtilities.getName(file) : file.getName();
} }
if (value instanceof FileInfo) { if (value instanceof FileInfo) {
FileInfo file = (FileInfo) value; FileInfo file = (FileInfo) value;
return preserveExtension ? file.getName() : file.getPath(); return preserveExtension ? file.getName() : file.getPath();
} }
if (value instanceof String) { if (value instanceof String) {
return preserveExtension ? FileUtilities.getNameWithoutExtension(value.toString()) : value.toString(); return preserveExtension ? FileUtilities.getNameWithoutExtension(value.toString()) : value.toString();
} }
// cannot format value // cannot format value
throw new IllegalArgumentException("Illegal value: " + value); throw new IllegalArgumentException("Illegal value: " + value);
} }
} }

View File

@ -24,45 +24,45 @@ import net.filebot.util.ui.SwingUI;
class HighlightListCellRenderer extends AbstractFancyListCellRenderer { class HighlightListCellRenderer extends AbstractFancyListCellRenderer {
protected final JTextComponent textComponent = new JTextField(); protected final JTextComponent textComponent = new JTextField();
protected final Pattern pattern; protected final Pattern pattern;
protected final Highlighter.HighlightPainter highlightPainter; protected final Highlighter.HighlightPainter highlightPainter;
public HighlightListCellRenderer(Pattern pattern, Highlighter.HighlightPainter highlightPainter, int padding) { public HighlightListCellRenderer(Pattern pattern, Highlighter.HighlightPainter highlightPainter, int padding) {
super(new Insets(0, 0, 0, 0)); super(new Insets(0, 0, 0, 0));
this.pattern = pattern; this.pattern = pattern;
this.highlightPainter = highlightPainter; this.highlightPainter = highlightPainter;
// pad the cell from inside the text component, // pad the cell from inside the text component,
// so the HighlightPainter may paint in this space as well // so the HighlightPainter may paint in this space as well
textComponent.setBorder(new EmptyBorder(padding, padding, padding, padding)); textComponent.setBorder(new EmptyBorder(padding, padding, padding, padding));
// make text component transparent, should work for all LAFs (setOpaque(false) may not, e.g. Nimbus) // make text component transparent, should work for all LAFs (setOpaque(false) may not, e.g. Nimbus)
textComponent.setBackground(SwingUI.TRANSLUCENT); textComponent.setBackground(SwingUI.TRANSLUCENT);
this.add(textComponent, BorderLayout.WEST); this.add(textComponent, BorderLayout.WEST);
textComponent.getDocument().addDocumentListener(new HighlightUpdateListener()); textComponent.getDocument().addDocumentListener(new HighlightUpdateListener());
} }
@Override @Override
protected void configureListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { protected void configureListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.configureListCellRendererComponent(list, value, index, isSelected, cellHasFocus); super.configureListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
textComponent.setText(value.toString()); textComponent.setText(value.toString());
} }
protected void updateHighlighter() { protected void updateHighlighter() {
textComponent.getHighlighter().removeAllHighlights(); textComponent.getHighlighter().removeAllHighlights();
Matcher matcher = pattern.matcher(textComponent.getText()); Matcher matcher = pattern.matcher(textComponent.getText());
while (matcher.find()) { while (matcher.find()) {
try { try {
textComponent.getHighlighter().addHighlight(matcher.start(0), matcher.end(0), highlightPainter); textComponent.getHighlighter().addHighlight(matcher.start(0), matcher.end(0), highlightPainter);
@ -72,38 +72,38 @@ class HighlightListCellRenderer extends AbstractFancyListCellRenderer {
} }
} }
} }
@Override @Override
public void setForeground(Color fg) { public void setForeground(Color fg) {
super.setForeground(fg); super.setForeground(fg);
// textComponent is null while in super constructor // textComponent is null while in super constructor
if (textComponent != null) { if (textComponent != null) {
textComponent.setForeground(fg); textComponent.setForeground(fg);
} }
} }
private class HighlightUpdateListener implements DocumentListener { private class HighlightUpdateListener implements DocumentListener {
@Override @Override
public void changedUpdate(DocumentEvent e) { public void changedUpdate(DocumentEvent e) {
updateHighlighter(); updateHighlighter();
} }
@Override @Override
public void insertUpdate(DocumentEvent e) { public void insertUpdate(DocumentEvent e) {
updateHighlighter(); updateHighlighter();
} }
@Override @Override
public void removeUpdate(DocumentEvent e) { public void removeUpdate(DocumentEvent e) {
updateHighlighter(); updateHighlighter();
} }
} }
} }

View File

@ -42,6 +42,7 @@ class MatchAction extends AbstractAction {
putValue(SMALL_ICON, ResourceManager.getIcon(strict ? "action.match.strict" : "action.match")); putValue(SMALL_ICON, ResourceManager.getIcon(strict ? "action.match.strict" : "action.match"));
} }
@Override
public void actionPerformed(ActionEvent evt) { public void actionPerformed(ActionEvent evt) {
if (model.names().isEmpty() || model.files().isEmpty()) { if (model.names().isEmpty() || model.files().isEmpty()) {
return; return;

View File

@ -8,13 +8,13 @@ import net.filebot.similarity.Match;
public interface MatchFormatter { public interface MatchFormatter {
public boolean canFormat(Match<?, ?> match); public boolean canFormat(Match<?, ?> match);
public String preview(Match<?, ?> match); public String preview(Match<?, ?> match);
public String format(Match<?, ?> match, Map<?, ?> context) throws Exception; public String format(Match<?, ?> match, Map<?, ?> context) throws Exception;
} }

View File

@ -16,48 +16,48 @@ import ca.odell.glazedlists.event.ListEvent;
public class MatchModel<Value, Candidate> { public class MatchModel<Value, Candidate> {
private final EventList<Match<Value, Candidate>> source = new BasicEventList<Match<Value, Candidate>>(); private final EventList<Match<Value, Candidate>> source = new BasicEventList<Match<Value, Candidate>>();
private final EventList<Value> values; private final EventList<Value> values;
private final EventList<Candidate> candidates; private final EventList<Candidate> candidates;
public MatchModel() { public MatchModel() {
this.values = new MatchView<Value, Candidate>(source) { this.values = new MatchView<Value, Candidate>(source) {
@Override @Override
public Value getElement(Match<Value, Candidate> match) { public Value getElement(Match<Value, Candidate> match) {
return match.getValue(); return match.getValue();
} }
@Override @Override
public Candidate getComplement(Match<Value, Candidate> match) { public Candidate getComplement(Match<Value, Candidate> match) {
return match.getCandidate(); return match.getCandidate();
} }
@Override @Override
public Match<Value, Candidate> createMatch(Value element, Candidate complement) { public Match<Value, Candidate> createMatch(Value element, Candidate complement) {
return new Match<Value, Candidate>(element, complement); return new Match<Value, Candidate>(element, complement);
} }
}; };
this.candidates = new MatchView<Candidate, Value>(source) { this.candidates = new MatchView<Candidate, Value>(source) {
@Override @Override
public Candidate getElement(Match<Value, Candidate> match) { public Candidate getElement(Match<Value, Candidate> match) {
return match.getCandidate(); return match.getCandidate();
} }
@Override @Override
public Value getComplement(Match<Value, Candidate> match) { public Value getComplement(Match<Value, Candidate> match) {
return match.getValue(); return match.getValue();
} }
@Override @Override
public Match<Value, Candidate> createMatch(Candidate element, Value complement) { public Match<Value, Candidate> createMatch(Candidate element, Value complement) {
@ -65,154 +65,154 @@ public class MatchModel<Value, Candidate> {
} }
}; };
} }
public void clear() { public void clear() {
source.clear(); source.clear();
} }
public int size() { public int size() {
return source.size(); return source.size();
} }
public Match<Value, Candidate> getMatch(int index) { public Match<Value, Candidate> getMatch(int index) {
return source.get(index); return source.get(index);
} }
public boolean hasComplement(int index) { public boolean hasComplement(int index) {
if (index >= 0 && index < size()) { if (index >= 0 && index < size()) {
return source.get(index).getValue() != null && source.get(index).getCandidate() != null; return source.get(index).getValue() != null && source.get(index).getCandidate() != null;
} }
return false; return false;
} }
public EventList<Match<Value, Candidate>> matches() { public EventList<Match<Value, Candidate>> matches() {
return source; return source;
} }
public EventList<Value> values() { public EventList<Value> values() {
return values; return values;
} }
public EventList<Candidate> candidates() { public EventList<Candidate> candidates() {
return candidates; return candidates;
} }
public void addAll(Collection<Match<Value, Candidate>> matches) { public void addAll(Collection<Match<Value, Candidate>> matches) {
source.addAll(matches); source.addAll(matches);
} }
public void addAll(Collection<Value> values, Collection<Candidate> candidates) { public void addAll(Collection<Value> values, Collection<Candidate> candidates) {
if (this.values.size() != this.candidates.size()) if (this.values.size() != this.candidates.size())
throw new IllegalStateException("Existing matches are not balanced"); throw new IllegalStateException("Existing matches are not balanced");
Iterator<Value> valueIterator = values.iterator(); Iterator<Value> valueIterator = values.iterator();
Iterator<Candidate> candidateIterator = candidates.iterator(); Iterator<Candidate> candidateIterator = candidates.iterator();
while (valueIterator.hasNext() || candidateIterator.hasNext()) { while (valueIterator.hasNext() || candidateIterator.hasNext()) {
Value value = valueIterator.hasNext() ? valueIterator.next() : null; Value value = valueIterator.hasNext() ? valueIterator.next() : null;
Candidate candidate = candidateIterator.hasNext() ? candidateIterator.next() : null; Candidate candidate = candidateIterator.hasNext() ? candidateIterator.next() : null;
source.add(new Match<Value, Candidate>(value, candidate)); source.add(new Match<Value, Candidate>(value, candidate));
} }
} }
private abstract class MatchView<Element, Complement> extends TransformedList<Match<Value, Candidate>, Element> { private abstract class MatchView<Element, Complement> extends TransformedList<Match<Value, Candidate>, Element> {
public MatchView(EventList<Match<Value, Candidate>> source) { public MatchView(EventList<Match<Value, Candidate>> source) {
super(source); super(source);
source.addListEventListener(this); source.addListEventListener(this);
} }
public abstract Element getElement(Match<Value, Candidate> match); public abstract Element getElement(Match<Value, Candidate> match);
public abstract Complement getComplement(Match<Value, Candidate> match); public abstract Complement getComplement(Match<Value, Candidate> match);
public abstract Match<Value, Candidate> createMatch(Element element, Complement complement); public abstract Match<Value, Candidate> createMatch(Element element, Complement complement);
@Override @Override
public Element get(int index) { public Element get(int index) {
return getElement(index); return getElement(index);
} }
public Element getElement(int index) { public Element getElement(int index) {
return getElement(source.get(index)); return getElement(source.get(index));
} }
public Complement getComplement(int index) { public Complement getComplement(int index) {
return getComplement(source.get(index)); return getComplement(source.get(index));
} }
@Override @Override
public boolean addAll(Collection<? extends Element> values) { public boolean addAll(Collection<? extends Element> values) {
return put(size(), values); return put(size(), values);
} }
@Override @Override
public boolean add(Element value) { public boolean add(Element value) {
return put(size(), Collections.singleton(value)); return put(size(), Collections.singleton(value));
}; };
@Override @Override
public void add(int index, Element value) { public void add(int index, Element value) {
List<Element> range = new ArrayList<Element>(); List<Element> range = new ArrayList<Element>();
range.add(value); range.add(value);
range.addAll(subList(index, size())); range.addAll(subList(index, size()));
put(index, range); put(index, range);
} }
@Override @Override
public Element remove(int index) { public Element remove(int index) {
Element old = getElement(index); Element old = getElement(index);
int lastIndex = size() - 1; int lastIndex = size() - 1;
// shift subsequent elements // shift subsequent elements
put(index, new ArrayList<Element>(subList(index + 1, lastIndex + 1))); put(index, new ArrayList<Element>(subList(index + 1, lastIndex + 1)));
// remove last element // remove last element
if (getComplement(lastIndex) == null) { if (getComplement(lastIndex) == null) {
source.remove(lastIndex); source.remove(lastIndex);
} else { } else {
set(lastIndex, null); set(lastIndex, null);
} }
return old; return old;
} }
@Override @Override
public Element set(int index, Element element) { public Element set(int index, Element element) {
Element old = getElement(index); Element old = getElement(index);
source.set(index, createMatch(element, getComplement(index))); source.set(index, createMatch(element, getComplement(index)));
return old; return old;
} }
@Override @Override
public void clear() { public void clear() {
@ -220,7 +220,7 @@ public class MatchModel<Value, Candidate> {
// exist at the and of the source model // exist at the and of the source model
for (int i = size() - 1; i >= 0; i--) { for (int i = size() - 1; i >= 0; i--) {
Complement complement = getComplement(i); Complement complement = getComplement(i);
if (complement != null) { if (complement != null) {
// replace original match with null match // replace original match with null match
source.set(i, createMatch(null, complement)); source.set(i, createMatch(null, complement));
@ -230,7 +230,7 @@ public class MatchModel<Value, Candidate> {
} }
} }
} }
private boolean put(int index, Collection<? extends Element> elements) { private boolean put(int index, Collection<? extends Element> elements) {
for (Element element : elements) { for (Element element : elements) {
@ -239,37 +239,37 @@ public class MatchModel<Value, Candidate> {
} else { } else {
source.add(index, createMatch(element, null)); source.add(index, createMatch(element, null));
} }
index++; index++;
} }
return true; return true;
} }
@Override @Override
protected boolean isWritable() { protected boolean isWritable() {
// can't write to source directly // can't write to source directly
return false; return false;
} }
private int size = 0; private int size = 0;
@Override @Override
public int size() { public int size() {
return size; return size;
} }
@Override @Override
public void listChanged(ListEvent<Match<Value, Candidate>> listChanges) { public void listChanged(ListEvent<Match<Value, Candidate>> listChanges) {
updates.beginEvent(true); updates.beginEvent(true);
while (listChanges.next()) { while (listChanges.next()) {
int index = listChanges.getIndex(); int index = listChanges.getIndex();
int type = listChanges.getType(); int type = listChanges.getType();
if (type == ListEvent.INSERT || type == ListEvent.UPDATE) { if (type == ListEvent.INSERT || type == ListEvent.UPDATE) {
if (index < size) { if (index < size) {
if (index == size - 1 && getElement(index) == null) { if (index == size - 1 && getElement(index) == null) {
@ -287,9 +287,9 @@ public class MatchModel<Value, Candidate> {
size--; size--;
} }
} }
updates.commitEvent(); updates.commitEvent();
} }
} }
} }

View File

@ -12,31 +12,31 @@ import net.filebot.web.MoviePart;
class MovieFormatter implements MatchFormatter { class MovieFormatter implements MatchFormatter {
@Override @Override
public boolean canFormat(Match<?, ?> match) { public boolean canFormat(Match<?, ?> match) {
return match.getValue() instanceof MoviePart; return match.getValue() instanceof MoviePart;
} }
@Override @Override
public String preview(Match<?, ?> match) { public String preview(Match<?, ?> match) {
return format(match, null); return format(match, null);
} }
@Override @Override
public String format(Match<?, ?> match, Map<?, ?> context) { public String format(Match<?, ?> match, Map<?, ?> context) {
MoviePart video = (MoviePart) match.getValue(); MoviePart video = (MoviePart) match.getValue();
Formatter name = new Formatter(new StringBuilder()); Formatter name = new Formatter(new StringBuilder());
// format as single-file or multi-part movie // format as single-file or multi-part movie
name.format("%s (%d)", video.getName(), video.getYear()); name.format("%s (%d)", video.getName(), video.getYear());
if (video.getPartCount() > 1) { if (video.getPartCount() > 1) {
name.format(".CD%d", video.getPartIndex()); name.format(".CD%d", video.getPartIndex());
} }
// remove path separators if the name contains any / or \ // remove path separators if the name contains any / or \
return replacePathSeparators(name.out().toString()); return replacePathSeparators(name.out().toString());
} }

View File

@ -237,6 +237,7 @@ public class PresetEditor extends JDialog {
private final ListCellRenderer<Object> parent = (ListCellRenderer<Object>) combo.getRenderer(); private final ListCellRenderer<Object> parent = (ListCellRenderer<Object>) combo.getRenderer();
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel) parent.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); JLabel label = (JLabel) parent.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
@ -278,7 +279,7 @@ public class PresetEditor extends JDialog {
JLabel label = (JLabel) parent.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); JLabel label = (JLabel) parent.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof Language) { if (value instanceof Language) {
Language it = (Language) value; Language it = value;
label.setText(it.getName()); label.setText(it.getName());
label.setIcon(ResourceManager.getFlagIcon(it.getCode())); label.setIcon(ResourceManager.getFlagIcon(it.getCode()));
} }

View File

@ -126,6 +126,7 @@ class RenameList<E> extends FileBotList<E> {
private final AbstractAction upAction = new AbstractAction("Align Up", ResourceManager.getIcon("action.up")) { private final AbstractAction upAction = new AbstractAction("Align Up", ResourceManager.getIcon("action.up")) {
@Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
int index = getListComponent().getSelectedIndex(); int index = getListComponent().getSelectedIndex();
@ -138,6 +139,7 @@ class RenameList<E> extends FileBotList<E> {
private final AbstractAction downAction = new AbstractAction("Align Down", ResourceManager.getIcon("action.down")) { private final AbstractAction downAction = new AbstractAction("Align Down", ResourceManager.getIcon("action.down")) {
@Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
int index = getListComponent().getSelectedIndex(); int index = getListComponent().getSelectedIndex();

View File

@ -274,7 +274,7 @@ public class RenamePanel extends JComponent {
try { try {
JList list = (JList) evt.getSource(); JList list = (JList) evt.getSource();
if (list.getSelectedIndex() >= 0) { if (list.getSelectedIndex() >= 0) {
UserFiles.revealFiles((List<File>) list.getSelectedValuesList()); UserFiles.revealFiles(list.getSelectedValuesList());
} }
} catch (Exception e) { } catch (Exception e) {
Logger.getLogger(RenamePanel.class.getName()).log(Level.WARNING, e.getMessage()); Logger.getLogger(RenamePanel.class.getName()).log(Level.WARNING, e.getMessage());
@ -702,6 +702,7 @@ public class RenamePanel extends JComponent {
this.preset = preset; this.preset = preset;
} }
@Override
public List<File> getFiles(ActionEvent evt) { public List<File> getFiles(ActionEvent evt) {
List<File> input = new ArrayList<File>(); List<File> input = new ArrayList<File>();
if (preset.getInputFolder() != null) { if (preset.getInputFolder() != null) {
@ -725,10 +726,12 @@ public class RenamePanel extends JComponent {
return input; return input;
} }
@Override
public boolean isStrict(ActionEvent evt) { public boolean isStrict(ActionEvent evt) {
return preset.getMatchMode() != null ? MATCH_MODE_STRICT.equals(preset.getMatchMode()) : super.isStrict(evt); return preset.getMatchMode() != null ? MATCH_MODE_STRICT.equals(preset.getMatchMode()) : super.isStrict(evt);
} }
@Override
public SortOrder getSortOrder(ActionEvent evt) { public SortOrder getSortOrder(ActionEvent evt) {
return preset.getSortOrder() != null ? preset.getSortOrder() : super.getSortOrder(evt); return preset.getSortOrder() != null ? preset.getSortOrder() : super.getSortOrder(evt);
} }

View File

@ -10,22 +10,22 @@ import net.filebot.ui.PanelBuilder;
public class RenamePanelBuilder implements PanelBuilder { public class RenamePanelBuilder implements PanelBuilder {
@Override @Override
public String getName() { public String getName() {
return "Rename"; return "Rename";
} }
@Override @Override
public Icon getIcon() { public Icon getIcon() {
return ResourceManager.getIcon("panel.rename"); return ResourceManager.getIcon("panel.rename");
} }
@Override @Override
public JComponent create() { public JComponent create() {
return new RenamePanel(); return new RenamePanel();
} }
} }

View File

@ -13,88 +13,88 @@ import javax.swing.event.ListDataListener;
class ScrollPaneSynchronizer { class ScrollPaneSynchronizer {
private final RenameList[] components; private final RenameList[] components;
public ScrollPaneSynchronizer(RenameList... components) { public ScrollPaneSynchronizer(RenameList... components) {
this.components = components; this.components = components;
// share vertical and horizontal scrollbar model // share vertical and horizontal scrollbar model
BoundedRangeModel horizontalScrollBarModel = components[0].getListScrollPane().getHorizontalScrollBar().getModel(); BoundedRangeModel horizontalScrollBarModel = components[0].getListScrollPane().getHorizontalScrollBar().getModel();
BoundedRangeModel verticalScrollBarModel = components[0].getListScrollPane().getVerticalScrollBar().getModel(); BoundedRangeModel verticalScrollBarModel = components[0].getListScrollPane().getVerticalScrollBar().getModel();
// recalculate common size on change // recalculate common size on change
ListDataListener resizeListener = new ListDataListener() { ListDataListener resizeListener = new ListDataListener() {
private final Timer timer = new Timer(50, new ActionListener() { private final Timer timer = new Timer(50, new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
updatePreferredSize(); updatePreferredSize();
// fire only once // fire only once
timer.stop(); timer.stop();
} }
}); });
@Override @Override
public void intervalAdded(ListDataEvent e) { public void intervalAdded(ListDataEvent e) {
timer.restart(); timer.restart();
} }
@Override @Override
public void intervalRemoved(ListDataEvent e) { public void intervalRemoved(ListDataEvent e) {
timer.restart(); timer.restart();
} }
@Override @Override
public void contentsChanged(ListDataEvent e) { public void contentsChanged(ListDataEvent e) {
timer.restart(); timer.restart();
} }
}; };
// apply to all components // apply to all components
for (RenameList<?> component : components) { for (RenameList<?> component : components) {
component.getListScrollPane().getHorizontalScrollBar().setModel(horizontalScrollBarModel); component.getListScrollPane().getHorizontalScrollBar().setModel(horizontalScrollBarModel);
component.getListScrollPane().getVerticalScrollBar().setModel(verticalScrollBarModel); component.getListScrollPane().getVerticalScrollBar().setModel(verticalScrollBarModel);
component.getListComponent().getModel().addListDataListener(resizeListener); component.getListComponent().getModel().addListDataListener(resizeListener);
} }
// initial sync of component sizes // initial sync of component sizes
updatePreferredSize(); updatePreferredSize();
} }
public void updatePreferredSize() { public void updatePreferredSize() {
Dimension max = new Dimension(); Dimension max = new Dimension();
for (RenameList component : components) { for (RenameList component : components) {
// reset preferred size // reset preferred size
component.getListComponent().setPreferredSize(null); component.getListComponent().setPreferredSize(null);
// calculate preferred size based on data and renderer // calculate preferred size based on data and renderer
Dimension preferred = component.getListComponent().getPreferredSize(); Dimension preferred = component.getListComponent().getPreferredSize();
// update maximum size // update maximum size
if (preferred.width > max.width) if (preferred.width > max.width)
max.width = preferred.width; max.width = preferred.width;
if (preferred.height > max.height) if (preferred.height > max.height)
max.height = preferred.height; max.height = preferred.height;
} }
for (RenameList component : components) { for (RenameList component : components) {
// set fixed preferred size // set fixed preferred size
component.getListComponent().setPreferredSize(max); component.getListComponent().setPreferredSize(max);
// update scrollbars // update scrollbars
component.getListComponent().revalidate(); component.getListComponent().revalidate();
component.getListScrollPane().revalidate(); component.getListScrollPane().revalidate();
} }
} }
} }

View File

@ -39,36 +39,36 @@ import net.miginfocom.swing.MigLayout;
class ValidateDialog extends JDialog { class ValidateDialog extends JDialog {
private final JList list; private final JList list;
private File[] model; private File[] model;
private boolean cancelled = true; private boolean cancelled = true;
public ValidateDialog(Window owner, Collection<File> source) { public ValidateDialog(Window owner, Collection<File> source) {
super(owner, "Invalid Names", ModalityType.DOCUMENT_MODAL); super(owner, "Invalid Names", ModalityType.DOCUMENT_MODAL);
model = source.toArray(new File[0]); model = source.toArray(new File[0]);
list = new JList(model); list = new JList(model);
list.setEnabled(false); list.setEnabled(false);
list.setCellRenderer(new HighlightListCellRenderer(ILLEGAL_CHARACTERS, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4) { list.setCellRenderer(new HighlightListCellRenderer(ILLEGAL_CHARACTERS, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4) {
@Override @Override
protected void updateHighlighter() { protected void updateHighlighter() {
textComponent.getHighlighter().removeAllHighlights(); textComponent.getHighlighter().removeAllHighlights();
Matcher matcher = pattern.matcher(textComponent.getText()); Matcher matcher = pattern.matcher(textComponent.getText());
File file = new File(textComponent.getText()); File file = new File(textComponent.getText());
// highlight path components separately to ignore "illegal characters" that are either path separators or part of the drive letter (e.g. ':' in 'E:') // highlight path components separately to ignore "illegal characters" that are either path separators or part of the drive letter (e.g. ':' in 'E:')
for (File element : listPath(file)) { for (File element : listPath(file)) {
int limit = element.getPath().length(); int limit = element.getPath().length();
matcher.region(limit - element.getName().length(), limit); matcher.region(limit - element.getName().length(), limit);
while (matcher.find()) { while (matcher.find()) {
try { try {
textComponent.getHighlighter().addHighlight(matcher.start(0), matcher.end(0), highlightPainter); textComponent.getHighlighter().addHighlight(matcher.start(0), matcher.end(0), highlightPainter);
@ -80,47 +80,47 @@ class ValidateDialog extends JDialog {
} }
} }
}); });
JLabel label = new JLabel("Some names contain invalid characters:"); JLabel label = new JLabel("Some names contain invalid characters:");
JComponent content = (JComponent) getContentPane(); JComponent content = (JComponent) getContentPane();
content.setLayout(new MigLayout("insets dialog, nogrid, fill", "", "[pref!][fill][pref!]")); content.setLayout(new MigLayout("insets dialog, nogrid, fill", "", "[pref!][fill][pref!]"));
content.add(label, "wrap"); content.add(label, "wrap");
content.add(new JScrollPane(list), "grow, wrap 2mm"); content.add(new JScrollPane(list), "grow, wrap 2mm");
content.add(new JButton(validateAction), "align center"); content.add(new JButton(validateAction), "align center");
content.add(new JButton(continueAction), "gap related"); content.add(new JButton(continueAction), "gap related");
content.add(new JButton(cancelAction), "gap 12mm"); content.add(new JButton(cancelAction), "gap 12mm");
installAction(content, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelAction); installAction(content, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelAction);
setDefaultCloseOperation(DISPOSE_ON_CLOSE); setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setMinimumSize(new Dimension(365, 280)); setMinimumSize(new Dimension(365, 280));
pack(); pack();
} }
public List<File> getModel() { public List<File> getModel() {
return unmodifiableList(Arrays.asList(model)); return unmodifiableList(Arrays.asList(model));
} }
public boolean isCancelled() { public boolean isCancelled() {
return cancelled; return cancelled;
} }
private void finish(boolean cancelled) { private void finish(boolean cancelled) {
this.cancelled = cancelled; this.cancelled = cancelled;
setVisible(false); setVisible(false);
dispose(); dispose();
} }
private final Action validateAction = new AbstractAction("Validate", ResourceManager.getIcon("dialog.continue")) { private final Action validateAction = new AbstractAction("Validate", ResourceManager.getIcon("dialog.continue")) {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
// validate names // validate names
@ -128,110 +128,110 @@ class ValidateDialog extends JDialog {
// remove illegal characters // remove illegal characters
model[i] = validateFilePath(model[i]); model[i] = validateFilePath(model[i]);
} }
// update view // update view
list.repaint(); list.repaint();
// switch icon // switch icon
continueAction.putValue(SMALL_ICON, getValue(SMALL_ICON)); continueAction.putValue(SMALL_ICON, getValue(SMALL_ICON));
// disable this action // disable this action
setEnabled(false); setEnabled(false);
} }
}; };
private final Action continueAction = new AbstractAction("Continue", ResourceManager.getIcon("dialog.continue.invalid")) { private final Action continueAction = new AbstractAction("Continue", ResourceManager.getIcon("dialog.continue.invalid")) {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
finish(false); finish(false);
} }
}; };
private final Action cancelAction = new AbstractAction("Cancel", ResourceManager.getIcon("dialog.cancel")) { private final Action cancelAction = new AbstractAction("Cancel", ResourceManager.getIcon("dialog.cancel")) {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
finish(true); finish(true);
} }
}; };
public static boolean validate(Component parent, List<File> source) { public static boolean validate(Component parent, List<File> source) {
IndexView<File> invalidFilePaths = new IndexView<File>(source); IndexView<File> invalidFilePaths = new IndexView<File>(source);
for (int i = 0; i < source.size(); i++) { for (int i = 0; i < source.size(); i++) {
// invalid file names are also invalid file paths // invalid file names are also invalid file paths
if (isInvalidFilePath(source.get(i)) && !isUnixFS()) { if (isInvalidFilePath(source.get(i)) && !isUnixFS()) {
invalidFilePaths.addIndex(i); invalidFilePaths.addIndex(i);
} }
} }
// check if there is anything to do in the first place // check if there is anything to do in the first place
if (invalidFilePaths.isEmpty()) { if (invalidFilePaths.isEmpty()) {
return true; return true;
} }
ValidateDialog dialog = new ValidateDialog(getWindow(parent), invalidFilePaths); ValidateDialog dialog = new ValidateDialog(getWindow(parent), invalidFilePaths);
dialog.setLocation(getOffsetLocation(dialog.getOwner())); dialog.setLocation(getOffsetLocation(dialog.getOwner()));
// show and block // show and block
dialog.setVisible(true); dialog.setVisible(true);
if (dialog.isCancelled()) { if (dialog.isCancelled()) {
// no output // no output
return false; return false;
} }
List<File> validatedFilePaths = dialog.getModel(); List<File> validatedFilePaths = dialog.getModel();
// validate source list via index view // validate source list via index view
for (int i = 0; i < invalidFilePaths.size(); i++) { for (int i = 0; i < invalidFilePaths.size(); i++) {
invalidFilePaths.set(i, validatedFilePaths.get(i)); invalidFilePaths.set(i, validatedFilePaths.get(i));
} }
return true; return true;
} }
private static class IndexView<E> extends AbstractList<E> { private static class IndexView<E> extends AbstractList<E> {
private final List<Integer> mapping = new ArrayList<Integer>(); private final List<Integer> mapping = new ArrayList<Integer>();
private final List<E> source; private final List<E> source;
public IndexView(List<E> source) { public IndexView(List<E> source) {
this.source = source; this.source = source;
} }
public boolean addIndex(int index) { public boolean addIndex(int index) {
return mapping.add(index); return mapping.add(index);
} }
@Override @Override
public E get(int index) { public E get(int index) {
int sourceIndex = mapping.get(index); int sourceIndex = mapping.get(index);
if (sourceIndex >= 0) if (sourceIndex >= 0)
return source.get(sourceIndex); return source.get(sourceIndex);
return null; return null;
} }
@Override @Override
public E set(int index, E element) { public E set(int index, E element) {
return source.set(mapping.get(index), element); return source.set(mapping.get(index), element);
} }
@Override @Override
public int size() { public int size() {
return mapping.size(); return mapping.size();
} }
} }
} }

View File

@ -22,55 +22,55 @@ import net.filebot.ResourceManager;
public class ChecksumButton extends JToggleButton { public class ChecksumButton extends JToggleButton {
private static final Icon contentArea = ResourceManager.getIcon("button.checksum"); private static final Icon contentArea = ResourceManager.getIcon("button.checksum");
private static final Icon contentAreaSelected = ResourceManager.getIcon("button.checksum.selected"); private static final Icon contentAreaSelected = ResourceManager.getIcon("button.checksum.selected");
public ChecksumButton(Action action) { public ChecksumButton(Action action) {
super(action); super(action);
setPreferredSize(new Dimension(max(contentAreaSelected.getIconWidth(), contentArea.getIconWidth()), max(contentAreaSelected.getIconHeight(), contentArea.getIconHeight()))); setPreferredSize(new Dimension(max(contentAreaSelected.getIconWidth(), contentArea.getIconWidth()), max(contentAreaSelected.getIconHeight(), contentArea.getIconHeight())));
setMinimumSize(getPreferredSize()); setMinimumSize(getPreferredSize());
setMaximumSize(getPreferredSize()); setMaximumSize(getPreferredSize());
setForeground(WHITE); setForeground(WHITE);
setFont(new Font(DIALOG, PLAIN, 11)); setFont(new Font(DIALOG, PLAIN, 11));
// as image button // as image button
setBorderPainted(false); setBorderPainted(false);
setContentAreaFilled(false); setContentAreaFilled(false);
setFocusPainted(false); setFocusPainted(false);
setEnabled(true); setEnabled(true);
} }
@Override @Override
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
super.setEnabled(enabled); super.setEnabled(enabled);
// set appropriate cursor // set appropriate cursor
setCursor(getPredefinedCursor(enabled ? HAND_CURSOR : DEFAULT_CURSOR)); setCursor(getPredefinedCursor(enabled ? HAND_CURSOR : DEFAULT_CURSOR));
} }
@Override @Override
protected void paintComponent(Graphics g) { protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g; Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON); g2d.setRenderingHint(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHint(KEY_RENDERING, VALUE_RENDER_QUALITY); g2d.setRenderingHint(KEY_RENDERING, VALUE_RENDER_QUALITY);
// paint background image in the center // paint background image in the center
if (isSelected()) { if (isSelected()) {
contentAreaSelected.paintIcon(this, g2d, (int) round((getWidth() - contentAreaSelected.getIconWidth()) / (double) 2), (int) round((getHeight() - contentAreaSelected.getIconHeight()) / (double) 2)); contentAreaSelected.paintIcon(this, g2d, (int) round((getWidth() - contentAreaSelected.getIconWidth()) / (double) 2), (int) round((getHeight() - contentAreaSelected.getIconHeight()) / (double) 2));
} else { } else {
contentArea.paintIcon(this, g2d, (int) round((getWidth() - contentArea.getIconWidth()) / (double) 2), (int) round((getHeight() - contentArea.getIconHeight()) / (double) 2)); contentArea.paintIcon(this, g2d, (int) round((getWidth() - contentArea.getIconWidth()) / (double) 2), (int) round((getHeight() - contentArea.getIconHeight()) / (double) 2));
} }
Rectangle2D textBounds = g2d.getFontMetrics().getStringBounds(getText(), g2d); Rectangle2D textBounds = g2d.getFontMetrics().getStringBounds(getText(), g2d);
// draw text in the center // draw text in the center
g2d.drawString(getText(), round((getWidth() - textBounds.getWidth()) / 2) + 1, round(getHeight() / 2 - textBounds.getY() - textBounds.getHeight() / 2)); g2d.drawString(getText(), round((getWidth() - textBounds.getWidth()) / 2) + 1, round(getHeight() / 2 - textBounds.getY() - textBounds.getHeight() / 2));
} }

View File

@ -17,132 +17,132 @@ import net.filebot.util.ExceptionUtilities;
class ChecksumCell { class ChecksumCell {
private final String name; private final String name;
private final File root; private final File root;
private Map<HashType, String> hashes; private Map<HashType, String> hashes;
private ChecksumComputationTask task; private ChecksumComputationTask task;
private Throwable error; private Throwable error;
public static enum State { public static enum State {
PENDING, PENDING,
PROGRESS, PROGRESS,
READY, READY,
ERROR ERROR
} }
public ChecksumCell(String name, File root, Map<HashType, String> hashes) { public ChecksumCell(String name, File root, Map<HashType, String> hashes) {
this.name = name; this.name = name;
this.root = root; this.root = root;
this.hashes = hashes; this.hashes = hashes;
} }
public ChecksumCell(String name, File root, ChecksumComputationTask task) { public ChecksumCell(String name, File root, ChecksumComputationTask task) {
this.name = name; this.name = name;
this.root = root; this.root = root;
this.hashes = new EnumMap<HashType, String>(HashType.class); this.hashes = new EnumMap<HashType, String>(HashType.class);
this.task = task; this.task = task;
// forward property change events // forward property change events
task.addPropertyChangeListener(taskListener); task.addPropertyChangeListener(taskListener);
} }
public String getName() { public String getName() {
return name; return name;
} }
public File getRoot() { public File getRoot() {
return root; return root;
} }
public String getChecksum(HashType hash) { public String getChecksum(HashType hash) {
return hashes.get(hash); return hashes.get(hash);
} }
public void putTask(ChecksumComputationTask computationTask) { public void putTask(ChecksumComputationTask computationTask) {
if (task != null) { if (task != null) {
task.removePropertyChangeListener(taskListener); task.removePropertyChangeListener(taskListener);
task.cancel(true); task.cancel(true);
} }
task = computationTask; task = computationTask;
error = null; error = null;
// forward property change events // forward property change events
task.addPropertyChangeListener(taskListener); task.addPropertyChangeListener(taskListener);
// state changed to PENDING // state changed to PENDING
pcs.firePropertyChange("state", null, getState()); pcs.firePropertyChange("state", null, getState());
} }
public ChecksumComputationTask getTask() { public ChecksumComputationTask getTask() {
return task; return task;
} }
public Throwable getError() { public Throwable getError() {
return error; return error;
} }
public State getState() { public State getState() {
if (task != null) { if (task != null) {
switch (task.getState()) { switch (task.getState()) {
case PENDING: case PENDING:
return State.PENDING; return State.PENDING;
default: default:
return State.PROGRESS; return State.PROGRESS;
} }
} }
if (error != null) { if (error != null) {
return State.ERROR; return State.ERROR;
} }
return State.READY; return State.READY;
} }
public void dispose() { public void dispose() {
// clear property change support // clear property change support
for (PropertyChangeListener listener : pcs.getPropertyChangeListeners()) { for (PropertyChangeListener listener : pcs.getPropertyChangeListeners()) {
pcs.removePropertyChangeListener(listener); pcs.removePropertyChangeListener(listener);
} }
if (task != null) { if (task != null) {
task.removePropertyChangeListener(taskListener); task.removePropertyChangeListener(taskListener);
task.cancel(true); task.cancel(true);
} }
hashes = null; hashes = null;
error = null; error = null;
task = null; task = null;
pcs = null; pcs = null;
} }
@Override @Override
public String toString() { public String toString() {
return String.format("%s %s", name, hashes); return String.format("%s %s", name, hashes);
} }
private final PropertyChangeListener taskListener = new PropertyChangeListener() { private final PropertyChangeListener taskListener = new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) { if ("state".equals(evt.getPropertyName())) {
if (evt.getNewValue() == StateValue.DONE) if (evt.getNewValue() == StateValue.DONE)
done(evt); done(evt);
// cell state changed because worker state changed // cell state changed because worker state changed
pcs.firePropertyChange("state", null, getState()); pcs.firePropertyChange("state", null, getState());
} else { } else {
@ -150,36 +150,36 @@ class ChecksumCell {
pcs.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue()); pcs.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
} }
} }
protected void done(PropertyChangeEvent evt) { protected void done(PropertyChangeEvent evt) {
try { try {
hashes.putAll(task.get()); hashes.putAll(task.get());
} catch (Exception e) { } catch (Exception e) {
Throwable cause = ExceptionUtilities.getRootCause(e); Throwable cause = ExceptionUtilities.getRootCause(e);
// ignore cancellation // ignore cancellation
if (cause instanceof CancellationException) { if (cause instanceof CancellationException) {
return; return;
} }
error = cause; error = cause;
} finally { } finally {
task = null; task = null;
} }
} }
}; };
private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this, true); private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this, true);
public void addPropertyChangeListener(PropertyChangeListener listener) { public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener); pcs.addPropertyChangeListener(listener);
} }
public void removePropertyChangeListener(PropertyChangeListener listener) { public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener); pcs.removePropertyChangeListener(listener);
} }
} }

View File

@ -18,37 +18,37 @@ import net.filebot.util.ExceptionUtilities;
public class ChecksumCellRenderer extends DefaultTableCellRenderer { public class ChecksumCellRenderer extends DefaultTableCellRenderer {
private final SwingWorkerCellRenderer progressRenderer = new SwingWorkerCellRenderer(); private final SwingWorkerCellRenderer progressRenderer = new SwingWorkerCellRenderer();
private final Color verificationForeground = new Color(0x009900); private final Color verificationForeground = new Color(0x009900);
@Override @Override
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) {
boolean pendingWorker = false; boolean pendingWorker = false;
if (value instanceof SwingWorker) { if (value instanceof SwingWorker) {
if (((SwingWorker<?, ?>) value).getState() != StateValue.PENDING) if (((SwingWorker<?, ?>) value).getState() != StateValue.PENDING)
return progressRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); return progressRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
pendingWorker = true; pendingWorker = true;
} }
// ignore focus // ignore focus
super.getTableCellRendererComponent(table, value, isSelected, false, row, column); super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
// check row state for ERROR // check row state for ERROR
boolean isError = (table.getValueAt(row, 0) == ChecksumRow.State.ERROR); boolean isError = (table.getValueAt(row, 0) == ChecksumRow.State.ERROR);
// if row state is ERROR and if we are not selected use text color RED, // if row state is ERROR and if we are not selected use text color RED,
// else use default table colors // else use default table colors
setForeground(isSelected ? table.getSelectionForeground() : isError ? Color.RED : isVerificationColumn(table, column) ? verificationForeground : table.getForeground()); setForeground(isSelected ? table.getSelectionForeground() : isError ? Color.RED : isVerificationColumn(table, column) ? verificationForeground : table.getForeground());
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground()); setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
// use BOLD font on ERROR // use BOLD font on ERROR
setFont(getFont().deriveFont(isError ? BOLD : PLAIN)); setFont(getFont().deriveFont(isError ? BOLD : PLAIN));
if (pendingWorker) { if (pendingWorker) {
setText("Pending..."); setText("Pending...");
} else if (value == null && !isSelected) { } else if (value == null && !isSelected) {
@ -58,16 +58,16 @@ public class ChecksumCellRenderer extends DefaultTableCellRenderer {
} else if (value instanceof Throwable) { } else if (value instanceof Throwable) {
setText(ExceptionUtilities.getRootCauseMessage((Throwable) value)); setText(ExceptionUtilities.getRootCauseMessage((Throwable) value));
} }
return this; return this;
} }
private boolean isVerificationColumn(JTable table, int column) { private boolean isVerificationColumn(JTable table, int column) {
ChecksumTableModel model = (ChecksumTableModel) table.getModel(); ChecksumTableModel model = (ChecksumTableModel) table.getModel();
int modelColumn = table.getColumnModel().getColumn(column).getModelIndex(); int modelColumn = table.getColumnModel().getColumn(column).getModelIndex();
return model.isVerificationColumn(modelColumn); return model.isVerificationColumn(modelColumn);
} }
} }

View File

@ -53,7 +53,7 @@ class ChecksumComputationService {
/** /**
* Get the number of active executors that are associated with this {@link ChecksumComputationService}. * Get the number of active executors that are associated with this {@link ChecksumComputationService}.
* *
* @return number of active executors * @return number of active executors
* @see {@link #newExecutor()} * @see {@link #newExecutor()}
*/ */

View File

@ -21,17 +21,17 @@ import net.filebot.hash.HashType;
class ChecksumRow { class ChecksumRow {
private String name; private String name;
private Map<File, ChecksumCell> hashes = new HashMap<File, ChecksumCell>(4); private Map<File, ChecksumCell> hashes = new HashMap<File, ChecksumCell>(4);
private State state = State.UNKNOWN; 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 String embeddedChecksum; private String embeddedChecksum;
public static enum State { public static enum State {
UNKNOWN, UNKNOWN,
@ -39,72 +39,72 @@ class ChecksumRow {
WARNING, WARNING,
ERROR ERROR
} }
public ChecksumRow(String name) { public ChecksumRow(String name) {
this.name = name; this.name = name;
this.embeddedChecksum = getEmbeddedChecksum(name); this.embeddedChecksum = getEmbeddedChecksum(name);
} }
public String getName() { public String getName() {
return name; return name;
} }
public State getState() { public State getState() {
return state; return state;
} }
protected void setState(State newValue) { protected void setState(State newValue) {
State oldValue = this.state; State oldValue = this.state;
this.state = newValue; this.state = newValue;
pcs.firePropertyChange("state", oldValue, newValue); pcs.firePropertyChange("state", oldValue, newValue);
} }
public ChecksumCell getChecksum(File root) { public ChecksumCell getChecksum(File root) {
return hashes.get(root); return hashes.get(root);
} }
public Collection<ChecksumCell> values() { public Collection<ChecksumCell> values() {
return Collections.unmodifiableCollection(hashes.values()); return Collections.unmodifiableCollection(hashes.values());
} }
public ChecksumCell put(ChecksumCell cell) { public ChecksumCell put(ChecksumCell cell) {
ChecksumCell old = hashes.put(cell.getRoot(), cell); ChecksumCell old = hashes.put(cell.getRoot(), cell);
// update state immediately, don't fire property change // update state immediately, don't fire property change
state = getState(hashes.values()); state = getState(hashes.values());
// keep state up-to-date // keep state up-to-date
cell.addPropertyChangeListener(updateStateListener); cell.addPropertyChangeListener(updateStateListener);
return old; return old;
} }
public void dispose() { public void dispose() {
// clear property change support // clear property change support
for (PropertyChangeListener listener : pcs.getPropertyChangeListeners()) { for (PropertyChangeListener listener : pcs.getPropertyChangeListeners()) {
pcs.removePropertyChangeListener(listener); pcs.removePropertyChangeListener(listener);
} }
for (ChecksumCell cell : hashes.values()) { for (ChecksumCell cell : hashes.values()) {
cell.dispose(); cell.dispose();
} }
name = null; name = null;
embeddedChecksum = null; embeddedChecksum = null;
hashes = null; hashes = null;
state = null; state = null;
pcs = null; pcs = null;
} }
protected State getState(Collection<ChecksumCell> cells) { protected State getState(Collection<ChecksumCell> cells) {
// check states before we bother comparing the hash values // check states before we bother comparing the hash values
@ -117,29 +117,29 @@ class ChecksumRow {
return State.UNKNOWN; return State.UNKNOWN;
} }
} }
// compare hash values // compare hash values
Set<String> checksumSet = new HashSet<String>(2); Set<String> checksumSet = new HashSet<String>(2);
Set<State> verdictSet = EnumSet.noneOf(State.class); Set<State> verdictSet = EnumSet.noneOf(State.class);
for (HashType type : HashType.values()) { for (HashType type : HashType.values()) {
checksumSet.clear(); checksumSet.clear();
for (ChecksumCell cell : cells) { for (ChecksumCell cell : cells) {
String checksum = cell.getChecksum(type); String checksum = cell.getChecksum(type);
if (checksum != null) { if (checksum != null) {
checksumSet.add(checksum.toLowerCase()); checksumSet.add(checksum.toLowerCase());
} }
} }
verdictSet.add(getVerdict(checksumSet)); verdictSet.add(getVerdict(checksumSet));
} }
// ERROR > WARNING > OK > UNKOWN // ERROR > WARNING > OK > UNKOWN
return Collections.max(verdictSet); return Collections.max(verdictSet);
} }
protected State getVerdict(Set<String> checksumSet) { protected State getVerdict(Set<String> checksumSet) {
if (checksumSet.size() < 1) { if (checksumSet.size() < 1) {
@ -152,42 +152,43 @@ class ChecksumRow {
// all hashes match // all hashes match
if (embeddedChecksum != null) { if (embeddedChecksum != null) {
String checksum = checksumSet.iterator().next(); String checksum = checksumSet.iterator().next();
if (checksum.length() == embeddedChecksum.length() && !checksum.equalsIgnoreCase(embeddedChecksum)) { if (checksum.length() == embeddedChecksum.length() && !checksum.equalsIgnoreCase(embeddedChecksum)) {
return State.WARNING; return State.WARNING;
} }
} }
return State.OK; return State.OK;
} }
} }
@Override @Override
public String toString() { public String toString() {
return String.format("%s %s %s", state, name, hashes); return String.format("%s %s %s", state, name, hashes);
} }
private final PropertyChangeListener updateStateListener = new PropertyChangeListener() { private final PropertyChangeListener updateStateListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) { if ("state".equals(evt.getPropertyName())) {
setState(getState(hashes.values())); setState(getState(hashes.values()));
} }
} }
}; };
private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this, true); private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this, true);
public void addPropertyChangeListener(PropertyChangeListener listener) { public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener); pcs.addPropertyChangeListener(listener);
} }
public void removePropertyChangeListener(PropertyChangeListener listener) { public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener); pcs.removePropertyChangeListener(listener);
} }
} }

View File

@ -16,46 +16,46 @@ import net.filebot.util.ui.SwingUI.DragDropRowTableUI;
class ChecksumTable extends JTable { class ChecksumTable extends JTable {
public ChecksumTable() { public ChecksumTable() {
setFillsViewportHeight(true); setFillsViewportHeight(true);
setAutoCreateRowSorter(true); setAutoCreateRowSorter(true);
setAutoCreateColumnsFromModel(true); setAutoCreateColumnsFromModel(true);
setAutoResizeMode(AUTO_RESIZE_SUBSEQUENT_COLUMNS); setAutoResizeMode(AUTO_RESIZE_SUBSEQUENT_COLUMNS);
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
setRowHeight(20); setRowHeight(20);
setDragEnabled(true); setDragEnabled(true);
setUI(new DragDropRowTableUI()); setUI(new DragDropRowTableUI());
// force white background (e.g. gtk-laf default table background is gray) // force white background (e.g. gtk-laf default table background is gray)
setBackground(Color.WHITE); setBackground(Color.WHITE);
// 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(EMBEDDED_CHECKSUM)); setDefaultRenderer(String.class, new HighlightPatternCellRenderer(EMBEDDED_CHECKSUM));
setDefaultRenderer(ChecksumRow.State.class, new StateIconCellRenderer()); setDefaultRenderer(ChecksumRow.State.class, new StateIconCellRenderer());
setDefaultRenderer(ChecksumCell.class, new ChecksumCellRenderer()); setDefaultRenderer(ChecksumCell.class, new ChecksumCellRenderer());
} }
@Override @Override
protected ChecksumTableModel createDefaultDataModel() { protected ChecksumTableModel createDefaultDataModel() {
return new ChecksumTableModel(); return new ChecksumTableModel();
} }
@Override @Override
protected JTableHeader createDefaultTableHeader() { protected JTableHeader createDefaultTableHeader() {
return new JTableHeader(columnModel) { return new JTableHeader(columnModel) {
@Override @Override
public String getToolTipText(MouseEvent evt) { public String getToolTipText(MouseEvent evt) {
try { try {
int columnIndex = columnModel.getColumnIndexAtX(evt.getX()); int columnIndex = columnModel.getColumnIndexAtX(evt.getX());
int modelIndex = columnModel.getColumn(columnIndex).getModelIndex(); int modelIndex = columnModel.getColumn(columnIndex).getModelIndex();
// display column root of checksum column // display column root of checksum column
return getModel().getColumnRoot(modelIndex).getPath(); return getModel().getColumnRoot(modelIndex).getPath();
} catch (Exception e) { } catch (Exception e) {
@ -65,21 +65,21 @@ class ChecksumTable extends JTable {
}; };
}; };
} }
@Override @Override
public ChecksumTableModel getModel() { public ChecksumTableModel getModel() {
return (ChecksumTableModel) super.getModel(); return (ChecksumTableModel) super.getModel();
} }
@Override @Override
public void createDefaultColumnsFromModel() { public void createDefaultColumnsFromModel() {
super.createDefaultColumnsFromModel(); super.createDefaultColumnsFromModel();
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) {
@ -89,5 +89,5 @@ class ChecksumTable extends JTable {
} }
} }
} }
} }

View File

@ -13,32 +13,32 @@ import net.filebot.util.FileUtilities;
class ChecksumTableExportHandler extends TextFileExportHandler { class ChecksumTableExportHandler extends TextFileExportHandler {
private final ChecksumTableModel model; private final ChecksumTableModel model;
public ChecksumTableExportHandler(ChecksumTableModel model) { public ChecksumTableExportHandler(ChecksumTableModel model) {
this.model = model; this.model = model;
} }
@Override @Override
public boolean canExport() { public boolean canExport() {
return model.getRowCount() > 0 && defaultColumn() != null; return model.getRowCount() > 0 && defaultColumn() != null;
} }
@Override @Override
public void export(PrintWriter out) { public void export(PrintWriter out) {
export(new VerificationFileWriter(out, model.getHashType().getFormat(), "UTF-8"), defaultColumn(), model.getHashType()); export(new VerificationFileWriter(out, model.getHashType().getFormat(), "UTF-8"), defaultColumn(), model.getHashType());
} }
@Override @Override
public String getDefaultFileName() { public String getDefaultFileName() {
return getDefaultFileName(defaultColumn()); return getDefaultFileName(defaultColumn());
} }
protected File defaultColumn() { protected File defaultColumn() {
// select first column that is not a verification file column // select first column that is not a verification file column
@ -46,46 +46,46 @@ class ChecksumTableExportHandler extends TextFileExportHandler {
if (root.isDirectory()) if (root.isDirectory())
return root; return root;
} }
return null; return null;
} }
public void export(File file, File column) throws IOException { public void export(File file, File column) throws IOException {
VerificationFileWriter writer = new VerificationFileWriter(file, model.getHashType().getFormat(), "UTF-8"); VerificationFileWriter writer = new VerificationFileWriter(file, model.getHashType().getFormat(), "UTF-8");
try { try {
export(writer, column, model.getHashType()); export(writer, column, model.getHashType());
} finally { } finally {
writer.close(); writer.close();
} }
} }
public void export(VerificationFileWriter out, File column, HashType type) { public void export(VerificationFileWriter out, File column, HashType type) {
for (ChecksumRow row : model.rows()) { for (ChecksumRow row : model.rows()) {
ChecksumCell cell = row.getChecksum(column); ChecksumCell cell = row.getChecksum(column);
if (cell != null) { if (cell != null) {
String hash = cell.getChecksum(type); String hash = cell.getChecksum(type);
if (hash != null) { if (hash != null) {
out.write(cell.getName(), hash); out.write(cell.getName(), hash);
} }
} }
} }
} }
public String getDefaultFileName(File column) { public String getDefaultFileName(File column) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
// append file name // append file name
sb.append(column != null ? FileUtilities.getName(column) : "name"); sb.append(column != null ? FileUtilities.getName(column) : "name");
// append file extension // append file extension
sb.append('.').append(model.getHashType().name().toLowerCase()); sb.append('.').append(model.getHashType().name().toLowerCase());
return sb.toString(); return sb.toString();
} }
} }

View File

@ -242,6 +242,7 @@ class ChecksumTableModel extends AbstractTableModel {
private final PropertyChangeListener progressListener = new PropertyChangeListener() { private final PropertyChangeListener progressListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
ChecksumCell cell = (ChecksumCell) evt.getSource(); ChecksumCell cell = (ChecksumCell) evt.getSource();

View File

@ -17,63 +17,63 @@ import net.filebot.ui.sfv.ChecksumRow.State;
* DefaultTableCellRenderer with highlighting of text patterns. * DefaultTableCellRenderer with highlighting of text patterns.
*/ */
class HighlightPatternCellRenderer extends DefaultTableCellRenderer { class HighlightPatternCellRenderer extends DefaultTableCellRenderer {
private final Pattern pattern; private final Pattern pattern;
public HighlightPatternCellRenderer(Pattern pattern) { public HighlightPatternCellRenderer(Pattern pattern) {
this.pattern = pattern; this.pattern = pattern;
} }
@Override @Override
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, value, isSelected, false, row, column); super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
// check for error or warning // check for error or warning
boolean isError = (EnumSet.of(State.ERROR, State.WARNING).contains(table.getValueAt(row, 0))); boolean isError = (EnumSet.of(State.ERROR, State.WARNING).contains(table.getValueAt(row, 0)));
// highlight patterns by using a smaller font-size and changing the font-color to a dark green // highlight patterns by using a smaller font-size and changing the font-color to a dark green
// do not change the font-color if cell is selected, because that would look ugly (imagine green text on blue background ...) // do not change the font-color if cell is selected, because that would look ugly (imagine green text on blue background ...)
Matcher matcher = pattern.matcher(value.toString()); Matcher matcher = pattern.matcher(value.toString());
// use no-break, because we really don't want line-wrapping in our table cells // use no-break, because we really don't want line-wrapping in our table cells
StringBuffer htmlText = new StringBuffer("<html><nobr>"); StringBuffer htmlText = new StringBuffer("<html><nobr>");
while (matcher.find()) { while (matcher.find()) {
matcher.appendReplacement(htmlText, createReplacement(isSelected ? null : (isError ? "red" : "#009900"), "smaller", isError ? "bold" : null)); matcher.appendReplacement(htmlText, createReplacement(isSelected ? null : (isError ? "red" : "#009900"), "smaller", isError ? "bold" : null));
} }
matcher.appendTail(htmlText); matcher.appendTail(htmlText);
htmlText.append("</nobr></html>"); htmlText.append("</nobr></html>");
setText(htmlText.toString()); setText(htmlText.toString());
return this; return this;
} }
protected String createReplacement(String cssColor, String cssFontSize, String cssFontWeight) { protected String createReplacement(String cssColor, String cssFontSize, String cssFontWeight) {
// build replacement string like // build replacement string like
// e.g. <span style='font-size: smaller; color: #009900;'>$0</span> // e.g. <span style='font-size: smaller; color: #009900;'>$0</span>
StringBuilder replacement = new StringBuilder(60); StringBuilder replacement = new StringBuilder(60);
replacement.append("<span style='"); replacement.append("<span style='");
if (cssColor != null) { if (cssColor != null) {
replacement.append("color:").append(cssColor).append(';'); replacement.append("color:").append(cssColor).append(';');
} }
if (cssFontSize != null) { if (cssFontSize != null) {
replacement.append("font-size:").append(cssFontSize).append(';'); replacement.append("font-size:").append(cssFontSize).append(';');
} }
if (cssFontWeight != null) { if (cssFontWeight != null) {
replacement.append("font-weight:").append(cssFontWeight).append(';'); replacement.append("font-weight:").append(cssFontWeight).append(';');
} }
return replacement.append("'>$0</span>").toString(); return replacement.append("'>$0</span>").toString();
} }
} }

View File

@ -10,22 +10,22 @@ import net.filebot.ui.PanelBuilder;
public class SfvPanelBuilder implements PanelBuilder { public class SfvPanelBuilder implements PanelBuilder {
@Override @Override
public String getName() { public String getName() {
return "SFV"; return "SFV";
} }
@Override @Override
public Icon getIcon() { public Icon getIcon() {
return ResourceManager.getIcon("panel.sfv"); return ResourceManager.getIcon("panel.sfv");
} }
@Override @Override
public JComponent create() { public JComponent create() {
return new SfvPanel(); return new SfvPanel();
} }
} }

View File

@ -16,28 +16,28 @@ import net.filebot.ui.sfv.ChecksumRow.State;
class StateIconCellRenderer extends DefaultTableCellRenderer { class StateIconCellRenderer extends DefaultTableCellRenderer {
private final Map<State, Icon> icons = new EnumMap<State, Icon>(State.class); private final Map<State, Icon> icons = new EnumMap<State, Icon>(State.class);
public StateIconCellRenderer() { public StateIconCellRenderer() {
icons.put(State.UNKNOWN, ResourceManager.getIcon("status.unknown")); icons.put(State.UNKNOWN, ResourceManager.getIcon("status.unknown"));
icons.put(State.OK, ResourceManager.getIcon("status.ok")); icons.put(State.OK, ResourceManager.getIcon("status.ok"));
icons.put(State.WARNING, ResourceManager.getIcon("status.warning")); icons.put(State.WARNING, ResourceManager.getIcon("status.warning"));
icons.put(State.ERROR, ResourceManager.getIcon("status.error")); icons.put(State.ERROR, ResourceManager.getIcon("status.error"));
setVerticalAlignment(SwingConstants.CENTER); setVerticalAlignment(SwingConstants.CENTER);
setHorizontalAlignment(SwingConstants.CENTER); setHorizontalAlignment(SwingConstants.CENTER);
} }
@Override @Override
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);
setIcon(icons.get(value)); setIcon(icons.get(value));
return this; return this;
} }
} }

View File

@ -15,31 +15,32 @@ import javax.swing.table.TableCellRenderer;
class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer { class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
private final JProgressBar progressBar = new JProgressBar(0, 100); private final JProgressBar progressBar = new JProgressBar(0, 100);
public SwingWorkerCellRenderer() { public SwingWorkerCellRenderer() {
super(new BorderLayout()); super(new BorderLayout());
// set margin for progress bar on parent component, // set margin for progress bar on parent component,
// because setting it on the progress bar itself does not work (border size is not respected in the paint method) // because setting it on the progress bar itself does not work (border size is not respected in the paint method)
setBorder(new EmptyBorder(2, 2, 2, 2)); setBorder(new EmptyBorder(2, 2, 2, 2));
progressBar.setStringPainted(true); progressBar.setStringPainted(true);
add(progressBar, BorderLayout.CENTER); add(progressBar, BorderLayout.CENTER);
} }
@Override
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) {
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground()); setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
progressBar.setValue(((SwingWorker<?, ?>) value).getProgress()); progressBar.setValue(((SwingWorker<?, ?>) value).getProgress());
return this; return this;
} }
/** /**
* Overridden for performance reasons. * Overridden for performance reasons.
@ -47,7 +48,7 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
@Override @Override
public void repaint(long tm, int x, int y, int width, int height) { public void repaint(long tm, int x, int y, int width, int height) {
} }
/** /**
* Overridden for performance reasons. * Overridden for performance reasons.
@ -55,7 +56,7 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
@Override @Override
public void repaint(Rectangle r) { public void repaint(Rectangle r) {
} }
/** /**
* Overridden for performance reasons. * Overridden for performance reasons.
@ -63,7 +64,7 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
@Override @Override
public void repaint() { public void repaint() {
} }
/** /**
* Overridden for performance reasons. * Overridden for performance reasons.
@ -71,5 +72,5 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
@Override @Override
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
} }
} }

View File

@ -17,105 +17,107 @@ import net.miginfocom.swing.MigLayout;
class TotalProgressPanel extends JComponent { class TotalProgressPanel extends JComponent {
private final JProgressBar progressBar = new JProgressBar(0, 0); private final JProgressBar progressBar = new JProgressBar(0, 0);
private final int millisToSetVisible = 200; private final int millisToSetVisible = 200;
public TotalProgressPanel(ChecksumComputationService computationService) { public TotalProgressPanel(ChecksumComputationService computationService) {
setLayout(new MigLayout("insets 1px")); setLayout(new MigLayout("insets 1px"));
setBorder(new TitledBorder("Total Progress")); setBorder(new TitledBorder("Total Progress"));
// invisible by default // invisible by default
setVisible(false); setVisible(false);
progressBar.setStringPainted(true); progressBar.setStringPainted(true);
add(progressBar, "growx"); add(progressBar, "growx");
computationService.addPropertyChangeListener(progressListener); computationService.addPropertyChangeListener(progressListener);
} }
private final PropertyChangeListener progressListener = new PropertyChangeListener() { private final PropertyChangeListener progressListener = new PropertyChangeListener() {
private static final String SHOW = "show"; private static final String SHOW = "show";
private static final String HIDE = "hide"; private static final String HIDE = "hide";
private final DelayedToggle delayed = new DelayedToggle(); private final DelayedToggle delayed = new DelayedToggle();
@Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
final int completedTaskCount = getComputationService(evt).getCompletedTaskCount(); final int completedTaskCount = getComputationService(evt).getCompletedTaskCount();
final int totalTaskCount = getComputationService(evt).getTotalTaskCount(); final int totalTaskCount = getComputationService(evt).getTotalTaskCount();
// invoke on EDT // invoke on EDT
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@Override @Override
public void run() { public void run() {
if (completedTaskCount == totalTaskCount) { if (completedTaskCount == totalTaskCount) {
// delayed hide on reset, immediate hide on finish // delayed hide on reset, immediate hide on finish
delayed.toggle(HIDE, totalTaskCount == 0 ? millisToSetVisible : 0, visibilityActionHandler); delayed.toggle(HIDE, totalTaskCount == 0 ? millisToSetVisible : 0, visibilityActionHandler);
} else if (totalTaskCount != 0) { } else if (totalTaskCount != 0) {
delayed.toggle(SHOW, millisToSetVisible, visibilityActionHandler); delayed.toggle(SHOW, millisToSetVisible, visibilityActionHandler);
} }
if (totalTaskCount != 0) { if (totalTaskCount != 0) {
progressBar.setValue(completedTaskCount); progressBar.setValue(completedTaskCount);
progressBar.setMaximum(totalTaskCount); progressBar.setMaximum(totalTaskCount);
progressBar.setString(String.format("%d / %d", completedTaskCount, totalTaskCount)); progressBar.setString(String.format("%d / %d", completedTaskCount, totalTaskCount));
} }
}; };
}); });
} }
private ChecksumComputationService getComputationService(PropertyChangeEvent evt) { private ChecksumComputationService getComputationService(PropertyChangeEvent evt) {
return ((ChecksumComputationService) evt.getSource()); return ((ChecksumComputationService) evt.getSource());
} }
private final ActionListener visibilityActionHandler = new ActionListener() { private final ActionListener visibilityActionHandler = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
setVisible(e.getActionCommand() == SHOW); setVisible(e.getActionCommand() == SHOW);
} }
}; };
}; };
protected static class DelayedToggle { protected static class DelayedToggle {
private Timer timer = null; private Timer timer = null;
public void toggle(String action, int delay, final ActionListener actionHandler) { public void toggle(String action, int delay, final ActionListener actionHandler) {
if (timer != null) { if (timer != null) {
if (action.equals(timer.getActionCommand())) { if (action.equals(timer.getActionCommand())) {
// action has not changed, don't stop existing timer // action has not changed, don't stop existing timer
return; return;
} }
timer.stop(); timer.stop();
} }
timer = new Timer(delay, new ActionListener() { timer = new Timer(delay, new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
actionHandler.actionPerformed(e); actionHandler.actionPerformed(e);
} }
}); });
timer.setActionCommand(action); timer.setActionCommand(action);
timer.setRepeats(false); timer.setRepeats(false);
timer.start(); timer.start();
} }
} }
} }

View File

@ -21,29 +21,29 @@ import net.filebot.vfs.MemoryFile;
class MemoryFileListExportHandler implements TransferableExportHandler, ClipboardHandler { class MemoryFileListExportHandler implements TransferableExportHandler, ClipboardHandler {
public boolean canExport(JComponent component) { public boolean canExport(JComponent component) {
JList list = (JList) component; JList list = (JList) component;
// can't export anything, if nothing is selected // can't export anything, if nothing is selected
return !list.isSelectionEmpty(); return !list.isSelectionEmpty();
} }
public List<MemoryFile> export(JComponent component) { public List<MemoryFile> export(JComponent component) {
JList list = (JList) component; JList list = (JList) component;
// get selected values // get selected values
final Object[] selection = list.getSelectedValues(); final Object[] selection = list.getSelectedValues();
// as file list // as file list
return new AbstractList<MemoryFile>() { return new AbstractList<MemoryFile>() {
@Override @Override
public MemoryFile get(int index) { public MemoryFile get(int index) {
return (MemoryFile) selection[index]; return (MemoryFile) selection[index];
} }
@Override @Override
public int size() { public int size() {
@ -51,35 +51,35 @@ class MemoryFileListExportHandler implements TransferableExportHandler, Clipboar
} }
}; };
} }
@Override @Override
public int getSourceActions(JComponent component) { public int getSourceActions(JComponent component) {
return canExport(component) ? TransferHandler.COPY_OR_MOVE : TransferHandler.NONE; return canExport(component) ? TransferHandler.COPY_OR_MOVE : TransferHandler.NONE;
} }
@Override @Override
public Transferable createTransferable(JComponent component) { public Transferable createTransferable(JComponent component) {
Map<String, ByteBuffer> vfs = new HashMap<String, ByteBuffer>(); Map<String, ByteBuffer> vfs = new HashMap<String, ByteBuffer>();
for (MemoryFile file : export(component)) { for (MemoryFile file : export(component)) {
vfs.put(file.getName(), file.getData()); vfs.put(file.getName(), file.getData());
} }
return new ByteBufferTransferable(vfs); return new ByteBufferTransferable(vfs);
} }
@Override @Override
public void exportToClipboard(JComponent component, Clipboard clip, int action) { public void exportToClipboard(JComponent component, Clipboard clip, int action) {
clip.setContents(createTransferable(component), null); clip.setContents(createTransferable(component), null);
} }
@Override @Override
public void exportDone(JComponent source, Transferable data, int action) { public void exportDone(JComponent source, Transferable data, int action) {
} }
} }

Some files were not shown because too many files have changed in this diff Show More