* 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 {
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.
*
*
* @param name
* simple name of the resource (without extension)
* @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));
}
@Override
public List<FileInfo> listFiles() throws Exception {
List<FileInfo> paths = new ArrayList<FileInfo>();
for (FileObject it : archive.findFiles(ALL_FILES)) {
@ -46,10 +47,12 @@ public class ApacheVFS implements ArchiveExtractor, Closeable {
return paths;
}
@Override
public void extract(File outputDir) throws Exception {
extract(outputDir, null);
}
@Override
public void extract(File outputDir, FileFilter filter) throws Exception {
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 {
/**
* Cache for opened file streams
*/
private Map<String, RandomAccessFile> openedRandomAccessFileList = new HashMap<String, RandomAccessFile>();
/**
* Name of the last volume returned by {@link #getStream(String)}
*/
private String name;
/**
* This method should at least provide the name of the last
* opened volume (propID=PropID.NAME).
*
*
* @see IArchiveOpenVolumeCallback#getProperty(PropID)
*/
@Override
public Object getProperty(PropID propID) throws SevenZipException {
switch (propID) {
case NAME:
return name;
case NAME:
return name;
}
return null;
}
/**
* 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,
@ -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>
* </ul>
*/
@Override
public IInStream getStream(String filename) throws SevenZipException {
try {
// 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
// in order to emulating new stream
randomAccessFile.seek(0);
// Save current volume name in case getProperty() will be called
name = filename;
return new RandomAccessFileInStream(randomAccessFile);
}
// Nothing useful in cache. Open required volume.
randomAccessFile = new RandomAccessFile(filename, "r");
// Put new stream in the cache
openedRandomAccessFileList.put(filename, randomAccessFile);
// Save current volume name in case getProperty() will be called
name = filename;
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
// exist, so it have to try each volume.
// 2. should be there, but doesn't. This is an error case.
// Since normal and error cases are possible,
// we can't throw an error message
return null; // We return always null in this case
@ -93,25 +95,26 @@ class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveO
throw new RuntimeException(e);
}
}
/**
* Close all opened streams
*/
@Override
public void close() throws IOException {
for (RandomAccessFile file : openedRandomAccessFileList.values()) {
file.close();
}
}
@Override
public void setCompleted(Long files, Long bytes) throws SevenZipException {
}
@Override
public void setTotal(Long files, Long bytes) throws SevenZipException {
}
}

View File

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

View File

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

View File

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

View File

@ -61,6 +61,7 @@ public class SevenZipExecutable implements ArchiveExtractor {
}
}
@Override
public List<FileInfo> listFiles() throws IOException {
List<FileInfo> paths = new ArrayList<FileInfo>();
@ -81,11 +82,13 @@ public class SevenZipExecutable implements ArchiveExtractor {
return paths;
}
@Override
public void extract(File outputDir) throws IOException {
// e.g. 7z x -y -aos archive.7z
execute(get7zCommand(), "x", "-y", "-aos", archive.getPath(), "-o" + outputDir.getCanonicalPath());
}
@Override
public void extract(File outputDir, FileFilter filter) throws IOException {
// 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());

View File

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

View File

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

View File

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

View File

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

View File

@ -57,7 +57,7 @@ public class ScriptShellMethods {
}
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) {
@ -121,7 +121,7 @@ public class ScriptShellMethods {
List<File> files = FileUtilities.listFiles(roots);
if (closure != null) {
files = (List<File>) DefaultGroovyMethods.findAll(files, closure);
files = DefaultGroovyMethods.findAll(files, closure);
}
return FileUtilities.sortByUniquePath(files);
@ -144,7 +144,7 @@ public class ScriptShellMethods {
List<File> folders = FileUtilities.listFolders(roots);
if (closure != null) {
folders = (List<File>) DefaultGroovyMethods.findAll(folders, closure);
folders = DefaultGroovyMethods.findAll(folders, closure);
}
return FileUtilities.sortByUniquePath(folders);

View File

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

View File

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

View File

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

View File

@ -91,10 +91,10 @@ public class MediaBindingBean {
return getMovie().getName();
if (infoObject instanceof AudioTrack)
return getAlbumArtist() != null ? getAlbumArtist() : getArtist();
if (infoObject instanceof File)
return FileUtilities.getName((File) infoObject);
if (infoObject instanceof File)
return FileUtilities.getName((File) infoObject);
return null;
return null;
}
@Define("y")
@ -909,6 +909,7 @@ public class MediaBindingBean {
private AssociativeScriptObject createBindingObject(File file, Object info, Map<File, Object> context) {
MediaBindingBean mediaBindingBean = new MediaBindingBean(info, file, context) {
@Override
@Define(undefined)
public <T> T undefined(String name) {
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 {
private final Object object;
private final AccessControlContext context;
private PrivilegedInvocation(Object object, AccessControlContext context) {
this.object = object;
this.context = context;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
return method.invoke(object, args);
@ -36,23 +36,23 @@ public final class PrivilegedInvocation implements InvocationHandler {
}, context);
} catch (PrivilegedActionException e) {
Throwable cause = e.getException();
// the underlying method may have throw an exception
if (cause instanceof InvocationTargetException) {
// get actual cause
cause = cause.getCause();
}
// forward cause
throw cause;
}
}
public static <I> I newProxy(Class<I> interfaceClass, I object, AccessControlContext context) {
InvocationHandler invocationHandler = new PrivilegedInvocation(object, context);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// create dynamic invocation proxy
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
*/
public class PropertyBindings extends AbstractMap<String, Object> {
private final Object object;
private final Map<String, Object> properties = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
private final Object defaultValue;
public PropertyBindings(Object object, Object defaultValue) {
this.object = object;
this.defaultValue = defaultValue;
// get method bindings
for (Method method : object.getClass().getMethods()) {
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")) {
properties.put(method.getName().substring(3), method);
}
// boolean properties
if (method.getName().length() > 2 && method.getName().substring(0, 3).equalsIgnoreCase("is")) {
properties.put(method.getName().substring(2), method);
@ -40,17 +40,17 @@ public class PropertyBindings extends AbstractMap<String, Object> {
}
}
}
@Override
public Object get(Object key) {
Object value = properties.get(key);
// evaluate method
if (value instanceof Method) {
try {
value = ((Method) value).invoke(object);
if (value == null) {
value = defaultValue;
}
@ -58,74 +58,74 @@ public class PropertyBindings extends AbstractMap<String, Object> {
throw new RuntimeException(e);
}
}
return value;
}
@Override
public Object put(String key, Object value) {
return properties.put(key, value);
}
@Override
public Object remove(Object key) {
return properties.remove(key);
}
@Override
public boolean containsKey(Object key) {
return properties.containsKey(key);
}
@Override
public Set<String> keySet() {
return properties.keySet();
}
@Override
public boolean isEmpty() {
return properties.isEmpty();
}
@Override
public String toString() {
return properties.toString();
}
@Override
public Set<Entry<String, Object>> entrySet() {
Set<Entry<String, Object>> entrySet = new HashSet<Entry<String, Object>>();
for (final String key : keySet()) {
entrySet.add(new Entry<String, Object>() {
@Override
public String getKey() {
return key;
}
@Override
public Object getValue() {
return get(key);
}
@Override
public Object setValue(Object value) {
return put(key, value);
}
});
}
return entrySet;
}
}

View File

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

View File

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

View File

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

View File

@ -9,10 +9,10 @@ import jonelo.jacksum.algorithm.Edonkey;
public class Ed2kHash implements Hash {
private final Edonkey ed2k;
public Ed2kHash() {
try {
this.ed2k = new Edonkey();
@ -20,17 +20,17 @@ public class Ed2kHash implements Hash {
throw new RuntimeException(e);
}
}
@Override
public void update(byte[] bytes, int off, int len) {
ed2k.update(bytes, off, len);
}
@Override
public String digest() {
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 void update(byte[] bytes, int off, int len);
public String digest();
}

View File

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

View File

@ -10,17 +10,17 @@ import java.util.regex.Pattern;
public class SfvFormat extends VerificationFormat {
@Override
public String format(String path, String hash) {
// e.g folder/file.txt 970E4EF1
return String.format("%s %s", path, hash);
}
/**
* Pattern used to parse the lines of a sfv file.
*
*
* <pre>
* Sample:
* folder/file.txt 970E4EF1
@ -28,17 +28,17 @@ public class SfvFormat extends VerificationFormat {
* </pre>
*/
private final Pattern pattern = Pattern.compile("^(.+)\\s+(\\p{XDigit}{8})$");
@Override
public Entry<File, String> parseObject(String line) throws ParseException {
Matcher matcher = pattern.matcher(line);
if (!matcher.matches()) {
throw new ParseException("Illegal input pattern", 0);
}
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 {
private final Scanner scanner;
private final VerificationFormat format;
private Entry<File, String> buffer;
private int lineNumber = 0;
public VerificationFileReader(Readable source, VerificationFormat format) {
this.scanner = new Scanner(source);
this.format = format;
}
@Override
public boolean hasNext() {
@ -37,10 +37,10 @@ public class VerificationFileReader implements Iterator<Entry<File, String>>, Cl
// cache next entry
buffer = nextEntry();
}
return buffer != null;
}
@Override
public Entry<File, String> next() {
@ -48,7 +48,7 @@ public class VerificationFileReader implements Iterator<Entry<File, String>>, Cl
if (!hasNext()) {
throw new NoSuchElementException();
}
try {
return buffer;
} finally {
@ -56,15 +56,15 @@ public class VerificationFileReader implements Iterator<Entry<File, String>>, Cl
buffer = null;
}
}
protected Entry<File, String> nextEntry() {
Entry<File, String> entry = null;
// get next valid entry
while (entry == null && scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
// ignore comments
if (!isComment(line)) {
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));
}
}
lineNumber++;
}
return entry;
}
public int getLineNumber() {
return lineNumber;
}
protected boolean isComment(String line) {
return line.isEmpty() || line.startsWith(";");
}
@Override
public void close() throws IOException {
scanner.close();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}

View File

@ -12,40 +12,40 @@ import net.filebot.Settings;
public class VerificationFileWriter implements Closeable {
protected PrintWriter out;
protected VerificationFormat format;
public VerificationFileWriter(File file, VerificationFormat format, String charset) throws IOException {
this(new PrintWriter(file, charset), format, charset);
}
public VerificationFileWriter(PrintWriter out, VerificationFormat format, String charset) {
this.out = out;
this.format = format;
// start by printing the file header
writeHeader(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("; charset=%s%n", charset);
out.format(";%n");
}
public void write(String path, String hash) {
out.format("%s%n", format.format(path, hash));
}
@Override
public void close() throws IOException {
out.close();
}
}

View File

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

View File

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

View File

@ -1,10 +1,10 @@
/* Copyright (c) 2014 Reinhard Pointner, All Rights Reserved
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* 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,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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>
*
*
*/
interface XAttr extends Library {

View File

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

View File

@ -24,14 +24,14 @@ interface MediaInfoLibrary extends Library {
/**
* Create a new handle.
*
*
* @return handle
*/
Pointer New();
/**
* Open a file and collect information about it (technical information and tags).
*
*
* @param handle
* @param file
* full name of the file to open
@ -41,7 +41,7 @@ interface MediaInfoLibrary extends Library {
/**
* Configure or get information about MediaInfo.
*
*
* @param handle
* @param option
* The name of option
@ -53,7 +53,7 @@ interface MediaInfoLibrary extends Library {
/**
* Get all details about a file.
*
*
* @param handle
* @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).
*
*
* @param handle
* @param streamKind
* 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).
*
*
* @param handle
* @param streamKind
* 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.
*
*
* @param handle
* @param streamKind
* Kind of stream (general, video, audio...)
@ -107,14 +107,14 @@ interface MediaInfoLibrary extends Library {
/**
* Close a file opened before with Open().
*
*
* @param handle
*/
void Close(Pointer handle);
/**
* Dispose of a handle created with New().
*
*
* @param handle
*/
void Delete(Pointer handle);

View File

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

View File

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

View File

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

View File

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

View File

@ -3,20 +3,20 @@ package net.filebot.similarity;
public class MetricMin implements SimilarityMetric {
private final SimilarityMetric metric;
private final float minValue;
public MetricMin(SimilarityMetric metric, float minValue) {
this.metric = metric;
this.minValue = minValue;
}
@Override
public float getSimilarity(Object o1, Object o2) {
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 {
private final AbstractStringMetric metric;
private final Transliterator transliterator;
public NameSimilarityMetric() {
// 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"));
}
public NameSimilarityMetric(AbstractStringMetric metric, Transliterator transliterator) {
this.metric = metric;
this.transliterator = transliterator;
}
@Override
public float getSimilarity(Object o1, Object o2) {
return metric.getSimilarity(normalize(o1), normalize(o2));
}
protected String normalize(Object object) {
// use string representation
String name = object.toString();
// apply transliterator
if (transliterator != null) {
name = transliterator.transform(name);
}
// normalize separators
name = normalizePunctuation(name);
// normalize case and trim
return name.toLowerCase();
}
}

View File

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

View File

@ -154,7 +154,7 @@ public class SeasonEpisodeMatcher {
/**
* Try to get season and episode numbers for the given string.
*
*
* @param name
* 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

View File

@ -110,7 +110,7 @@ public class SeriesNameMatcher {
/**
* Try to match and verify all series names using known season episode patterns.
*
*
* @param names
* episode names
* @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.
*
*
* @param names
* list of episode names
* @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.
*
*
* @param 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
@ -218,7 +218,7 @@ public class SeriesNameMatcher {
/**
* Try to match a series name from the first common word sequence.
*
*
* @param names
* various episode names (at least two)
* @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> {
protected SimilarityMetric metric;
protected Object[] paragon;
public SimilarityComparator(SimilarityMetric metric, Object[] paragon) {
this.metric = metric;
this.paragon = paragon;
}
public SimilarityComparator(Object... paragon) {
this(new NameSimilarityMetric(), paragon);
}
@Override
public int compare(Object o1, Object o2) {
float f1 = getMaxSimilarity(o1);
float f2 = getMaxSimilarity(o2);
if (f1 == f2)
return 0;
return f1 > f2 ? -1 : 1;
}
public float getMaxSimilarity(Object obj) {
float m = 0;
for (Object it : paragon) {

View File

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

View File

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

View File

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

View File

@ -12,21 +12,21 @@ import java.util.TimeZone;
public class SubRipWriter implements Closeable {
private final DateFormat timeFormat;
private final Formatter out;
private int lineNumber = 0;
public SubRipWriter(Appendable out) {
this.out = new Formatter(out, Locale.ROOT);
// 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.setTimeZone(TimeZone.getTimeZone("UTC"));
}
public void write(SubtitleElement element) {
// 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%n%n", element.getText());
}
@Override
public void close() throws IOException {
out.close();
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -88,6 +88,7 @@ public enum SubtitleMetrics implements SimilarityMetric {
return 0;
}
@Override
protected float similarity(String match, String s1, String s2) {
return match.length() > 0 ? 1 : 0;
}
@ -153,10 +154,12 @@ public enum SubtitleMetrics implements SimilarityMetric {
private final String FPS = "FPS";
private final String SECONDS = "SECS";
@Override
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
};
@Override
protected Map<String, Object> getProperties(Object object) {
if (object instanceof OpenSubtitlesSubtitleDescriptor) {
return getSubtitleProperties((OpenSubtitlesSubtitleDescriptor) object);

View File

@ -12,18 +12,18 @@ import java.util.logging.Logger;
public abstract class SubtitleReader implements Iterator<SubtitleElement>, Closeable {
protected final Scanner scanner;
protected SubtitleElement current;
public SubtitleReader(Readable source) {
this.scanner = new Scanner(source);
}
protected abstract SubtitleElement readNext() throws Exception;
@Override
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());
}
}
return current != null;
}
@Override
public SubtitleElement next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
try {
return current;
} finally {
current = null;
}
}
@Override
public void close() throws IOException {
scanner.close();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}

View File

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

View File

@ -1,9 +1,9 @@
/*
* BeDecoder.java
*
*
* Created on May 30, 2003, 2:44 PM
* Copyright (C) 2003, 2004, 2005, 2006 Aelitis, All Rights Reserved.
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* 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
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
* AELITIS, SAS au capital de 46,603.30 euros
* 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
* represented as Long, String as byte[], dictionnaries as Map, and list as List.
*
*
* @author TdC_VgA
*/
class BDecoder {
private static final Charset BINARY_CHARSET = Charset.forName("ISO-8859-1");
public static Map<?, ?> decode(InputStream is) throws IOException {
return (new BDecoder().decodeStream(is));
}
public Map<?, ?> decodeStream(InputStream data) throws IOException {
Object res = decodeInputStream(data, 0);
if (res == null)
throw (new IOException("BDecoder: zero length file"));
else if (!(res instanceof Map))
throw (new IOException("BDecoder: top level isn't a Map"));
return ((Map<?, ?>) res);
}
private Object decodeInputStream(InputStream bais, int nesting) throws IOException {
if (!bais.markSupported())
throw new IOException("InputStream must support the mark() method");
// set a mark
bais.mark(Integer.MAX_VALUE);
// read a byte
int tempByte = bais.read();
// decide what to do
switch (tempByte) {
case 'd':
// create a new dictionary object
Map<String, Object> tempMap = new HashMap<String, Object>();
// get the key
byte[] tempByteArray = null;
while ((tempByteArray = (byte[]) decodeInputStream(bais, nesting + 1)) != null) {
// decode some more
Object value = decodeInputStream(bais, nesting + 1);
// add the value to the map
CharBuffer cb = BINARY_CHARSET.decode(ByteBuffer.wrap(tempByteArray));
String key = new String(cb.array(), 0, cb.limit());
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)));
case 'd':
// create a new dictionary object
Map<String, Object> tempMap = new HashMap<String, Object>();
// get the key
byte[] tempByteArray = null;
while ((tempByteArray = (byte[]) decodeInputStream(bais, nesting + 1)) != null) {
// decode some more
Object value = decodeInputStream(bais, nesting + 1);
// add the value to the map
CharBuffer cb = BINARY_CHARSET.decode(ByteBuffer.wrap(tempByteArray));
String key = new String(cb.array(), 0, cb.limit());
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)));
}
}
}
private long getNumberFromStream(InputStream bais, char parseChar) throws IOException {
int length = 0;
// place a mark
bais.mark(Integer.MAX_VALUE);
int tempByte = bais.read();
while ((tempByte != parseChar) && (tempByte >= 0)) {
tempByte = bais.read();
length++;
}
// are we at the end of the stream?
if (tempByte < 0)
return -1;
// reset the mark
bais.reset();
// get the length
byte[] tempArray = new byte[length];
int count = 0;
int len = 0;
// get the string
while ((count != length) && ((len = bais.read(tempArray, count, length - count)) > 0))
count += len;
// jump ahead in the stream to compensate for the :
bais.skip(1);
// return the value
CharBuffer cb = BINARY_CHARSET.decode(ByteBuffer.wrap(tempArray));
String str_value = new String(cb.array(), 0, cb.limit());
return Long.parseLong(str_value);
}
private byte[] getByteArrayFromStream(InputStream bais) throws IOException {
int length = (int) getNumberFromStream(bais, ':');
if (length < 0)
return null;
// note that torrent hashes can be big (consider a 55GB file with 2MB
// pieces
// this generates a pieces hash of 1/2 meg
if (length > 8 * 1024 * 1024)
throw (new IOException("Byte array length too large (" + length + ")"));
byte[] tempArray = new byte[length];
int count = 0;
int len = 0;
// get the string
while ((count != length) && ((len = bais.read(tempArray, count, length - count)) > 0))
count += len;
if (count != tempArray.length)
throw (new IOException("BDecoder::getByteArrayFromStream: truncated"));
return tempArray;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,45 +24,45 @@ import net.filebot.util.ui.SwingUI;
class HighlightListCellRenderer extends AbstractFancyListCellRenderer {
protected final JTextComponent textComponent = new JTextField();
protected final Pattern pattern;
protected final Highlighter.HighlightPainter highlightPainter;
public HighlightListCellRenderer(Pattern pattern, Highlighter.HighlightPainter highlightPainter, int padding) {
super(new Insets(0, 0, 0, 0));
this.pattern = pattern;
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
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);
this.add(textComponent, BorderLayout.WEST);
textComponent.getDocument().addDocumentListener(new HighlightUpdateListener());
}
@Override
protected void configureListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.configureListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
textComponent.setText(value.toString());
}
protected void updateHighlighter() {
textComponent.getHighlighter().removeAllHighlights();
Matcher matcher = pattern.matcher(textComponent.getText());
while (matcher.find()) {
try {
textComponent.getHighlighter().addHighlight(matcher.start(0), matcher.end(0), highlightPainter);
@ -72,38 +72,38 @@ class HighlightListCellRenderer extends AbstractFancyListCellRenderer {
}
}
}
@Override
public void setForeground(Color fg) {
super.setForeground(fg);
// textComponent is null while in super constructor
if (textComponent != null) {
textComponent.setForeground(fg);
}
}
private class HighlightUpdateListener implements DocumentListener {
@Override
public void changedUpdate(DocumentEvent e) {
updateHighlighter();
}
@Override
public void insertUpdate(DocumentEvent e) {
updateHighlighter();
}
@Override
public void removeUpdate(DocumentEvent e) {
updateHighlighter();
}
}
}

View File

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

View File

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

View File

@ -12,31 +12,31 @@ import net.filebot.web.MoviePart;
class MovieFormatter implements MatchFormatter {
@Override
public boolean canFormat(Match<?, ?> match) {
return match.getValue() instanceof MoviePart;
}
@Override
public String preview(Match<?, ?> match) {
return format(match, null);
}
@Override
public String format(Match<?, ?> match, Map<?, ?> context) {
MoviePart video = (MoviePart) match.getValue();
Formatter name = new Formatter(new StringBuilder());
// format as single-file or multi-part movie
name.format("%s (%d)", video.getName(), video.getYear());
if (video.getPartCount() > 1) {
name.format(".CD%d", video.getPartIndex());
}
// remove path separators if the name contains any / or \
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();
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean 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);
if (value instanceof Language) {
Language it = (Language) value;
Language it = value;
label.setText(it.getName());
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")) {
@Override
public void actionPerformed(ActionEvent e) {
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")) {
@Override
public void actionPerformed(ActionEvent e) {
int index = getListComponent().getSelectedIndex();

View File

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

View File

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

View File

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

View File

@ -22,55 +22,55 @@ import net.filebot.ResourceManager;
public class ChecksumButton extends JToggleButton {
private static final Icon contentArea = ResourceManager.getIcon("button.checksum");
private static final Icon contentAreaSelected = ResourceManager.getIcon("button.checksum.selected");
public ChecksumButton(Action action) {
super(action);
setPreferredSize(new Dimension(max(contentAreaSelected.getIconWidth(), contentArea.getIconWidth()), max(contentAreaSelected.getIconHeight(), contentArea.getIconHeight())));
setMinimumSize(getPreferredSize());
setMaximumSize(getPreferredSize());
setForeground(WHITE);
setFont(new Font(DIALOG, PLAIN, 11));
// as image button
setBorderPainted(false);
setContentAreaFilled(false);
setFocusPainted(false);
setEnabled(true);
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
// set appropriate cursor
setCursor(getPredefinedCursor(enabled ? HAND_CURSOR : DEFAULT_CURSOR));
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHint(KEY_RENDERING, VALUE_RENDER_QUALITY);
// paint background image in the center
if (isSelected()) {
contentAreaSelected.paintIcon(this, g2d, (int) round((getWidth() - contentAreaSelected.getIconWidth()) / (double) 2), (int) round((getHeight() - contentAreaSelected.getIconHeight()) / (double) 2));
} else {
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);
// draw text in the center
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 {
private final String name;
private final File root;
private Map<HashType, String> hashes;
private ChecksumComputationTask task;
private Throwable error;
public static enum State {
PENDING,
PROGRESS,
READY,
ERROR
}
public ChecksumCell(String name, File root, Map<HashType, String> hashes) {
this.name = name;
this.root = root;
this.hashes = hashes;
}
public ChecksumCell(String name, File root, ChecksumComputationTask task) {
this.name = name;
this.root = root;
this.hashes = new EnumMap<HashType, String>(HashType.class);
this.task = task;
// forward property change events
task.addPropertyChangeListener(taskListener);
}
public String getName() {
return name;
}
public File getRoot() {
return root;
}
public String getChecksum(HashType hash) {
return hashes.get(hash);
}
public void putTask(ChecksumComputationTask computationTask) {
if (task != null) {
task.removePropertyChangeListener(taskListener);
task.cancel(true);
}
task = computationTask;
error = null;
// forward property change events
task.addPropertyChangeListener(taskListener);
// state changed to PENDING
pcs.firePropertyChange("state", null, getState());
}
public ChecksumComputationTask getTask() {
return task;
}
public Throwable getError() {
return error;
}
public State getState() {
if (task != null) {
switch (task.getState()) {
case PENDING:
return State.PENDING;
default:
return State.PROGRESS;
case PENDING:
return State.PENDING;
default:
return State.PROGRESS;
}
}
if (error != null) {
return State.ERROR;
}
return State.READY;
}
public void dispose() {
// clear property change support
for (PropertyChangeListener listener : pcs.getPropertyChangeListeners()) {
pcs.removePropertyChangeListener(listener);
}
if (task != null) {
task.removePropertyChangeListener(taskListener);
task.cancel(true);
}
hashes = null;
error = null;
task = null;
pcs = null;
}
@Override
public String toString() {
return String.format("%s %s", name, hashes);
}
private final PropertyChangeListener taskListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
if (evt.getNewValue() == StateValue.DONE)
done(evt);
// cell state changed because worker state changed
pcs.firePropertyChange("state", null, getState());
} else {
@ -150,36 +150,36 @@ class ChecksumCell {
pcs.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
}
}
protected void done(PropertyChangeEvent evt) {
try {
hashes.putAll(task.get());
} catch (Exception e) {
Throwable cause = ExceptionUtilities.getRootCause(e);
// ignore cancellation
if (cause instanceof CancellationException) {
return;
}
error = cause;
} finally {
task = null;
}
}
};
private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this, true);
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
}

View File

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

View File

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

View File

@ -16,46 +16,46 @@ import net.filebot.util.ui.SwingUI.DragDropRowTableUI;
class ChecksumTable extends JTable {
public ChecksumTable() {
setFillsViewportHeight(true);
setAutoCreateRowSorter(true);
setAutoCreateColumnsFromModel(true);
setAutoResizeMode(AUTO_RESIZE_SUBSEQUENT_COLUMNS);
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
setRowHeight(20);
setDragEnabled(true);
setUI(new DragDropRowTableUI());
// force white background (e.g. gtk-laf default table background is gray)
setBackground(Color.WHITE);
// highlight CRC32 patterns in filenames in green and with smaller font-size
setDefaultRenderer(String.class, new HighlightPatternCellRenderer(EMBEDDED_CHECKSUM));
setDefaultRenderer(ChecksumRow.State.class, new StateIconCellRenderer());
setDefaultRenderer(ChecksumCell.class, new ChecksumCellRenderer());
}
@Override
protected ChecksumTableModel createDefaultDataModel() {
return new ChecksumTableModel();
}
@Override
protected JTableHeader createDefaultTableHeader() {
return new JTableHeader(columnModel) {
@Override
public String getToolTipText(MouseEvent evt) {
try {
int columnIndex = columnModel.getColumnIndexAtX(evt.getX());
int modelIndex = columnModel.getColumn(columnIndex).getModelIndex();
// display column root of checksum column
return getModel().getColumnRoot(modelIndex).getPath();
} catch (Exception e) {
@ -65,21 +65,21 @@ class ChecksumTable extends JTable {
};
};
}
@Override
public ChecksumTableModel getModel() {
return (ChecksumTableModel) super.getModel();
}
@Override
public void createDefaultColumnsFromModel() {
super.createDefaultColumnsFromModel();
for (int i = 0; i < getColumnCount(); i++) {
TableColumn column = getColumnModel().getColumn(i);
if (i == 0) {
column.setPreferredWidth(45);
} 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 {
private final ChecksumTableModel model;
public ChecksumTableExportHandler(ChecksumTableModel model) {
this.model = model;
}
@Override
public boolean canExport() {
return model.getRowCount() > 0 && defaultColumn() != null;
}
@Override
public void export(PrintWriter out) {
export(new VerificationFileWriter(out, model.getHashType().getFormat(), "UTF-8"), defaultColumn(), model.getHashType());
}
@Override
public String getDefaultFileName() {
return getDefaultFileName(defaultColumn());
}
protected File defaultColumn() {
// select first column that is not a verification file column
@ -46,46 +46,46 @@ class ChecksumTableExportHandler extends TextFileExportHandler {
if (root.isDirectory())
return root;
}
return null;
}
public void export(File file, File column) throws IOException {
VerificationFileWriter writer = new VerificationFileWriter(file, model.getHashType().getFormat(), "UTF-8");
try {
export(writer, column, model.getHashType());
} finally {
writer.close();
}
}
public void export(VerificationFileWriter out, File column, HashType type) {
for (ChecksumRow row : model.rows()) {
ChecksumCell cell = row.getChecksum(column);
if (cell != null) {
String hash = cell.getChecksum(type);
if (hash != null) {
out.write(cell.getName(), hash);
}
}
}
}
public String getDefaultFileName(File column) {
StringBuilder sb = new StringBuilder();
// append file name
sb.append(column != null ? FileUtilities.getName(column) : "name");
// append file extension
sb.append('.').append(model.getHashType().name().toLowerCase());
return sb.toString();
}
}

View File

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

View File

@ -17,63 +17,63 @@ import net.filebot.ui.sfv.ChecksumRow.State;
* DefaultTableCellRenderer with highlighting of text patterns.
*/
class HighlightPatternCellRenderer extends DefaultTableCellRenderer {
private final Pattern pattern;
public HighlightPatternCellRenderer(Pattern pattern) {
this.pattern = pattern;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
// check for error or warning
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
// 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());
// use no-break, because we really don't want line-wrapping in our table cells
StringBuffer htmlText = new StringBuffer("<html><nobr>");
while (matcher.find()) {
matcher.appendReplacement(htmlText, createReplacement(isSelected ? null : (isError ? "red" : "#009900"), "smaller", isError ? "bold" : null));
}
matcher.appendTail(htmlText);
htmlText.append("</nobr></html>");
setText(htmlText.toString());
return this;
}
protected String createReplacement(String cssColor, String cssFontSize, String cssFontWeight) {
// build replacement string like
// e.g. <span style='font-size: smaller; color: #009900;'>$0</span>
StringBuilder replacement = new StringBuilder(60);
replacement.append("<span style='");
if (cssColor != null) {
replacement.append("color:").append(cssColor).append(';');
}
if (cssFontSize != null) {
replacement.append("font-size:").append(cssFontSize).append(';');
}
if (cssFontWeight != null) {
replacement.append("font-weight:").append(cssFontWeight).append(';');
}
return replacement.append("'>$0</span>").toString();
}
}

View File

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

View File

@ -16,28 +16,28 @@ import net.filebot.ui.sfv.ChecksumRow.State;
class StateIconCellRenderer extends DefaultTableCellRenderer {
private final Map<State, Icon> icons = new EnumMap<State, Icon>(State.class);
public StateIconCellRenderer() {
icons.put(State.UNKNOWN, ResourceManager.getIcon("status.unknown"));
icons.put(State.OK, ResourceManager.getIcon("status.ok"));
icons.put(State.WARNING, ResourceManager.getIcon("status.warning"));
icons.put(State.ERROR, ResourceManager.getIcon("status.error"));
setVerticalAlignment(SwingConstants.CENTER);
setHorizontalAlignment(SwingConstants.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, null, isSelected, false, row, column);
setIcon(icons.get(value));
return this;
}
}

View File

@ -15,31 +15,32 @@ import javax.swing.table.TableCellRenderer;
class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
private final JProgressBar progressBar = new JProgressBar(0, 100);
public SwingWorkerCellRenderer() {
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)
setBorder(new EmptyBorder(2, 2, 2, 2));
progressBar.setStringPainted(true);
add(progressBar, BorderLayout.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
progressBar.setValue(((SwingWorker<?, ?>) value).getProgress());
return this;
}
/**
* Overridden for performance reasons.
@ -47,7 +48,7 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
@Override
public void repaint(long tm, int x, int y, int width, int height) {
}
/**
* Overridden for performance reasons.
@ -55,7 +56,7 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
@Override
public void repaint(Rectangle r) {
}
/**
* Overridden for performance reasons.
@ -63,7 +64,7 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
@Override
public void repaint() {
}
/**
* Overridden for performance reasons.
@ -71,5 +72,5 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
@Override
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
}
}

View File

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

View File

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