* Format Source
This commit is contained in:
parent
38a046bf2d
commit
8299e849aa
|
@ -6,7 +6,7 @@ import java.io.File;
|
||||||
|
|
||||||
|
|
||||||
public interface RenameAction {
|
public interface RenameAction {
|
||||||
|
|
||||||
File rename(File from, File to) throws Exception;
|
File rename(File from, File to) throws Exception;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ public final class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the URL of an image resource in this jar. Image must be located in <code>resources/</code> and the file type is assumed to be png.
|
* Get the URL of an image resource in this jar. Image must be located in <code>resources/</code> and the file type is assumed to be png.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
* simple name of the resource (without extension)
|
* simple name of the resource (without extension)
|
||||||
* @return URL of the resource or null if resource does not exist
|
* @return URL of the resource or null if resource does not exist
|
||||||
|
|
|
@ -35,6 +35,7 @@ public class ApacheVFS implements ArchiveExtractor, Closeable {
|
||||||
this.archive = fsm.createFileSystem(fsm.toFileObject(file));
|
this.archive = fsm.createFileSystem(fsm.toFileObject(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<FileInfo> listFiles() throws Exception {
|
public List<FileInfo> listFiles() throws Exception {
|
||||||
List<FileInfo> paths = new ArrayList<FileInfo>();
|
List<FileInfo> paths = new ArrayList<FileInfo>();
|
||||||
for (FileObject it : archive.findFiles(ALL_FILES)) {
|
for (FileObject it : archive.findFiles(ALL_FILES)) {
|
||||||
|
@ -46,10 +47,12 @@ public class ApacheVFS implements ArchiveExtractor, Closeable {
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void extract(File outputDir) throws Exception {
|
public void extract(File outputDir) throws Exception {
|
||||||
extract(outputDir, null);
|
extract(outputDir, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void extract(File outputDir, FileFilter filter) throws Exception {
|
public void extract(File outputDir, FileFilter filter) throws Exception {
|
||||||
fsm.toFileObject(outputDir).copyFrom(archive, filter == null ? ALL_FILES : new FileFilterSelector(filter));
|
fsm.toFileObject(outputDir).copyFrom(archive, filter == null ? ALL_FILES : new FileFilterSelector(filter));
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,33 +18,34 @@ import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
|
||||||
|
|
||||||
|
|
||||||
class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveOpenCallback, Closeable {
|
class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveOpenCallback, Closeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache for opened file streams
|
* Cache for opened file streams
|
||||||
*/
|
*/
|
||||||
private Map<String, RandomAccessFile> openedRandomAccessFileList = new HashMap<String, RandomAccessFile>();
|
private Map<String, RandomAccessFile> openedRandomAccessFileList = new HashMap<String, RandomAccessFile>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the last volume returned by {@link #getStream(String)}
|
* Name of the last volume returned by {@link #getStream(String)}
|
||||||
*/
|
*/
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method should at least provide the name of the last
|
* This method should at least provide the name of the last
|
||||||
* opened volume (propID=PropID.NAME).
|
* opened volume (propID=PropID.NAME).
|
||||||
*
|
*
|
||||||
* @see IArchiveOpenVolumeCallback#getProperty(PropID)
|
* @see IArchiveOpenVolumeCallback#getProperty(PropID)
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public Object getProperty(PropID propID) throws SevenZipException {
|
public Object getProperty(PropID propID) throws SevenZipException {
|
||||||
switch (propID) {
|
switch (propID) {
|
||||||
case NAME:
|
case NAME:
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the required volume will be calculated out of the
|
* The name of the required volume will be calculated out of the
|
||||||
* name of the first volume and a volume index. In case of RAR file,
|
* name of the first volume and a volume index. In case of RAR file,
|
||||||
|
@ -56,6 +57,7 @@ class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveO
|
||||||
* <li>test.part001.rar - first part of a multi-part archive. "00" indicates, that at least 100 volumes must exist.</li>
|
* <li>test.part001.rar - first part of a multi-part archive. "00" indicates, that at least 100 volumes must exist.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public IInStream getStream(String filename) throws SevenZipException {
|
public IInStream getStream(String filename) throws SevenZipException {
|
||||||
try {
|
try {
|
||||||
// We use caching of opened streams, so check cache first
|
// We use caching of opened streams, so check cache first
|
||||||
|
@ -64,19 +66,19 @@ class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveO
|
||||||
// Move the file pointer back to the beginning
|
// Move the file pointer back to the beginning
|
||||||
// in order to emulating new stream
|
// in order to emulating new stream
|
||||||
randomAccessFile.seek(0);
|
randomAccessFile.seek(0);
|
||||||
|
|
||||||
// Save current volume name in case getProperty() will be called
|
// Save current volume name in case getProperty() will be called
|
||||||
name = filename;
|
name = filename;
|
||||||
|
|
||||||
return new RandomAccessFileInStream(randomAccessFile);
|
return new RandomAccessFileInStream(randomAccessFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing useful in cache. Open required volume.
|
// Nothing useful in cache. Open required volume.
|
||||||
randomAccessFile = new RandomAccessFile(filename, "r");
|
randomAccessFile = new RandomAccessFile(filename, "r");
|
||||||
|
|
||||||
// Put new stream in the cache
|
// Put new stream in the cache
|
||||||
openedRandomAccessFileList.put(filename, randomAccessFile);
|
openedRandomAccessFileList.put(filename, randomAccessFile);
|
||||||
|
|
||||||
// Save current volume name in case getProperty() will be called
|
// Save current volume name in case getProperty() will be called
|
||||||
name = filename;
|
name = filename;
|
||||||
return new RandomAccessFileInStream(randomAccessFile);
|
return new RandomAccessFileInStream(randomAccessFile);
|
||||||
|
@ -85,7 +87,7 @@ class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveO
|
||||||
// 1. never exists. 7-Zip doesn't know how many volumes should
|
// 1. never exists. 7-Zip doesn't know how many volumes should
|
||||||
// exist, so it have to try each volume.
|
// exist, so it have to try each volume.
|
||||||
// 2. should be there, but doesn't. This is an error case.
|
// 2. should be there, but doesn't. This is an error case.
|
||||||
|
|
||||||
// Since normal and error cases are possible,
|
// Since normal and error cases are possible,
|
||||||
// we can't throw an error message
|
// we can't throw an error message
|
||||||
return null; // We return always null in this case
|
return null; // We return always null in this case
|
||||||
|
@ -93,25 +95,26 @@ class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveO
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close all opened streams
|
* Close all opened streams
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
for (RandomAccessFile file : openedRandomAccessFileList.values()) {
|
for (RandomAccessFile file : openedRandomAccessFileList.values()) {
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setCompleted(Long files, Long bytes) throws SevenZipException {
|
public void setCompleted(Long files, Long bytes) throws SevenZipException {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTotal(Long files, Long bytes) throws SevenZipException {
|
public void setTotal(Long files, Long bytes) throws SevenZipException {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,48 +16,51 @@ import net.sf.sevenzipjbinding.SevenZipException;
|
||||||
|
|
||||||
|
|
||||||
class ExtractCallback implements IArchiveExtractCallback {
|
class ExtractCallback implements IArchiveExtractCallback {
|
||||||
|
|
||||||
private ISevenZipInArchive inArchive;
|
private ISevenZipInArchive inArchive;
|
||||||
private ExtractOutProvider extractOut;
|
private ExtractOutProvider extractOut;
|
||||||
|
|
||||||
private ExtractOutStream output = null;
|
private ExtractOutStream output = null;
|
||||||
|
|
||||||
|
|
||||||
public ExtractCallback(ISevenZipInArchive inArchive, ExtractOutProvider extractOut) {
|
public ExtractCallback(ISevenZipInArchive inArchive, ExtractOutProvider extractOut) {
|
||||||
this.inArchive = inArchive;
|
this.inArchive = inArchive;
|
||||||
this.extractOut = extractOut;
|
this.extractOut = extractOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public ISequentialOutStream getStream(int index, ExtractAskMode extractAskMode) throws SevenZipException {
|
public ISequentialOutStream getStream(int index, ExtractAskMode extractAskMode) throws SevenZipException {
|
||||||
if (extractAskMode != ExtractAskMode.EXTRACT) {
|
if (extractAskMode != ExtractAskMode.EXTRACT) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isFolder = (Boolean) inArchive.getProperty(index, PropID.IS_FOLDER);
|
boolean isFolder = (Boolean) inArchive.getProperty(index, PropID.IS_FOLDER);
|
||||||
if (isFolder) {
|
if (isFolder) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String path = (String) inArchive.getProperty(index, PropID.PATH);
|
String path = (String) inArchive.getProperty(index, PropID.PATH);
|
||||||
try {
|
try {
|
||||||
OutputStream target = extractOut.getStream(new File(path));
|
OutputStream target = extractOut.getStream(new File(path));
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
output = new ExtractOutStream(target);
|
output = new ExtractOutStream(target);
|
||||||
return output;
|
return output;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new SevenZipException(e);
|
throw new SevenZipException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public void prepareOperation(ExtractAskMode extractAskMode) throws SevenZipException {
|
public void prepareOperation(ExtractAskMode extractAskMode) throws SevenZipException {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setOperationResult(ExtractOperationResult extractOperationResult) throws SevenZipException {
|
public void setOperationResult(ExtractOperationResult extractOperationResult) throws SevenZipException {
|
||||||
if (output != null) {
|
if (output != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -68,18 +71,20 @@ class ExtractCallback implements IArchiveExtractCallback {
|
||||||
output = null;
|
output = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extractOperationResult != ExtractOperationResult.OK) {
|
if (extractOperationResult != ExtractOperationResult.OK) {
|
||||||
throw new SevenZipException("Extraction Error: " + extractOperationResult);
|
throw new SevenZipException("Extraction Error: " + extractOperationResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setCompleted(long completeValue) throws SevenZipException {
|
public void setCompleted(long completeValue) throws SevenZipException {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setTotal(long total) throws SevenZipException {
|
public void setTotal(long total) throws SevenZipException {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import java.io.OutputStream;
|
||||||
|
|
||||||
|
|
||||||
public interface ExtractOutProvider {
|
public interface ExtractOutProvider {
|
||||||
|
|
||||||
OutputStream getStream(File archivePath) throws IOException;
|
OutputStream getStream(File archivePath) throws IOException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,15 +11,15 @@ import net.sf.sevenzipjbinding.SevenZipException;
|
||||||
|
|
||||||
|
|
||||||
class ExtractOutStream implements ISequentialOutStream, Closeable {
|
class ExtractOutStream implements ISequentialOutStream, Closeable {
|
||||||
|
|
||||||
private OutputStream out;
|
private OutputStream out;
|
||||||
|
|
||||||
|
|
||||||
public ExtractOutStream(OutputStream out) {
|
public ExtractOutStream(OutputStream out) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int write(byte[] data) throws SevenZipException {
|
public int write(byte[] data) throws SevenZipException {
|
||||||
try {
|
try {
|
||||||
|
@ -29,11 +29,11 @@ class ExtractOutStream implements ISequentialOutStream, Closeable {
|
||||||
}
|
}
|
||||||
return data.length; // return amount of proceed data
|
return data.length; // return amount of proceed data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ public class SevenZipExecutable implements ArchiveExtractor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<FileInfo> listFiles() throws IOException {
|
public List<FileInfo> listFiles() throws IOException {
|
||||||
List<FileInfo> paths = new ArrayList<FileInfo>();
|
List<FileInfo> paths = new ArrayList<FileInfo>();
|
||||||
|
|
||||||
|
@ -81,11 +82,13 @@ public class SevenZipExecutable implements ArchiveExtractor {
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void extract(File outputDir) throws IOException {
|
public void extract(File outputDir) throws IOException {
|
||||||
// e.g. 7z x -y -aos archive.7z
|
// e.g. 7z x -y -aos archive.7z
|
||||||
execute(get7zCommand(), "x", "-y", "-aos", archive.getPath(), "-o" + outputDir.getCanonicalPath());
|
execute(get7zCommand(), "x", "-y", "-aos", archive.getPath(), "-o" + outputDir.getCanonicalPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void extract(File outputDir, FileFilter filter) throws IOException {
|
public void extract(File outputDir, FileFilter filter) throws IOException {
|
||||||
// e.g. 7z x -y -aos archive.7z file.txt image.png info.nfo
|
// e.g. 7z x -y -aos archive.7z file.txt image.png info.nfo
|
||||||
Stream<String> command = Stream.of(get7zCommand(), "x", "-y", "-aos", archive.getPath(), "-o" + outputDir.getCanonicalPath());
|
Stream<String> command = Stream.of(get7zCommand(), "x", "-y", "-aos", archive.getPath(), "-o" + outputDir.getCanonicalPath());
|
||||||
|
|
|
@ -14,15 +14,15 @@ import com.sun.jna.Platform;
|
||||||
|
|
||||||
|
|
||||||
public class SevenZipLoader {
|
public class SevenZipLoader {
|
||||||
|
|
||||||
private static boolean nativeLibrariesLoaded = false;
|
private static boolean nativeLibrariesLoaded = false;
|
||||||
|
|
||||||
|
|
||||||
private static synchronized void requireNativeLibraries() throws SevenZipNativeInitializationException {
|
private static synchronized void requireNativeLibraries() throws SevenZipNativeInitializationException {
|
||||||
if (nativeLibrariesLoaded) {
|
if (nativeLibrariesLoaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize 7z-JBinding native libs
|
// initialize 7z-JBinding native libs
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
|
@ -32,7 +32,7 @@ public class SevenZipLoader {
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Logger.getLogger(SevenZipLoader.class.getName()).warning("Failed to preload library: " + e);
|
Logger.getLogger(SevenZipLoader.class.getName()).warning("Failed to preload library: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.loadLibrary("7-Zip-JBinding");
|
System.loadLibrary("7-Zip-JBinding");
|
||||||
SevenZip.initLoadedLibraries(); // NATIVE LIBS MUST BE LOADED WITH SYSTEM CLASSLOADER
|
SevenZip.initLoadedLibraries(); // NATIVE LIBS MUST BE LOADED WITH SYSTEM CLASSLOADER
|
||||||
nativeLibrariesLoaded = true;
|
nativeLibrariesLoaded = true;
|
||||||
|
@ -40,17 +40,17 @@ public class SevenZipLoader {
|
||||||
throw new SevenZipNativeInitializationException("Failed to load 7z-JBinding: " + e.getMessage(), e);
|
throw new SevenZipNativeInitializationException("Failed to load 7z-JBinding: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static ISevenZipInArchive open(IInStream stream, IArchiveOpenCallback callback) throws Exception {
|
public static ISevenZipInArchive open(IInStream stream, IArchiveOpenCallback callback) throws Exception {
|
||||||
// initialize 7-Zip-JBinding
|
// initialize 7-Zip-JBinding
|
||||||
requireNativeLibraries();
|
requireNativeLibraries();
|
||||||
|
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
return SevenZip.openInArchive(null, stream);
|
return SevenZip.openInArchive(null, stream);
|
||||||
} else {
|
} else {
|
||||||
return SevenZip.openInArchive(null, stream, callback);
|
return SevenZip.openInArchive(null, stream, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ public class SevenZipNativeBindings implements ArchiveExtractor, Closeable {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<FileInfo> listFiles() throws SevenZipException {
|
public List<FileInfo> listFiles() throws SevenZipException {
|
||||||
List<FileInfo> paths = new ArrayList<FileInfo>();
|
List<FileInfo> paths = new ArrayList<FileInfo>();
|
||||||
|
|
||||||
|
|
|
@ -13,66 +13,66 @@ import net.sf.sevenzipjbinding.SevenZipException;
|
||||||
|
|
||||||
|
|
||||||
public class VolumedArchiveInStream implements IInStream {
|
public class VolumedArchiveInStream implements IInStream {
|
||||||
|
|
||||||
private static final String FIRST_VOLUME_POSTFIX = ".001";
|
private static final String FIRST_VOLUME_POSTFIX = ".001";
|
||||||
|
|
||||||
private long absoluteOffset;
|
private long absoluteOffset;
|
||||||
private long absoluteLength = -1;
|
private long absoluteLength = -1;
|
||||||
|
|
||||||
private int currentIndex = -1;
|
private int currentIndex = -1;
|
||||||
private IInStream currentInStream;
|
private IInStream currentInStream;
|
||||||
private long currentVolumeOffset;
|
private long currentVolumeOffset;
|
||||||
private long currentVolumeLength;
|
private long currentVolumeLength;
|
||||||
private List<Long> volumePositions = new ArrayList<Long>();
|
private List<Long> volumePositions = new ArrayList<Long>();
|
||||||
|
|
||||||
private final IArchiveOpenVolumeCallback archiveOpenVolumeCallback;
|
private final IArchiveOpenVolumeCallback archiveOpenVolumeCallback;
|
||||||
private String cuttedVolumeFilename;
|
private String cuttedVolumeFilename;
|
||||||
|
|
||||||
|
|
||||||
public VolumedArchiveInStream(IArchiveOpenVolumeCallback archiveOpenVolumeCallback) throws SevenZipException {
|
public VolumedArchiveInStream(IArchiveOpenVolumeCallback archiveOpenVolumeCallback) throws SevenZipException {
|
||||||
this((String) archiveOpenVolumeCallback.getProperty(PropID.NAME), archiveOpenVolumeCallback);
|
this((String) archiveOpenVolumeCallback.getProperty(PropID.NAME), archiveOpenVolumeCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public VolumedArchiveInStream(String firstVolumeFilename, IArchiveOpenVolumeCallback archiveOpenVolumeCallback) throws SevenZipException {
|
public VolumedArchiveInStream(String firstVolumeFilename, IArchiveOpenVolumeCallback archiveOpenVolumeCallback) throws SevenZipException {
|
||||||
this.archiveOpenVolumeCallback = archiveOpenVolumeCallback;
|
this.archiveOpenVolumeCallback = archiveOpenVolumeCallback;
|
||||||
volumePositions.add(Long.valueOf(0));
|
volumePositions.add(Long.valueOf(0));
|
||||||
|
|
||||||
if (!firstVolumeFilename.endsWith(FIRST_VOLUME_POSTFIX)) {
|
if (!firstVolumeFilename.endsWith(FIRST_VOLUME_POSTFIX)) {
|
||||||
throw new SevenZipException("The first volume filename '" + firstVolumeFilename + "' don't ends with the postfix: '" + FIRST_VOLUME_POSTFIX + "'. Can't proceed");
|
throw new SevenZipException("The first volume filename '" + firstVolumeFilename + "' don't ends with the postfix: '" + FIRST_VOLUME_POSTFIX + "'. Can't proceed");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cuttedVolumeFilename = firstVolumeFilename.substring(0, firstVolumeFilename.length() - 3);
|
cuttedVolumeFilename = firstVolumeFilename.substring(0, firstVolumeFilename.length() - 3);
|
||||||
openVolume(1, true);
|
openVolume(1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void openVolume(int index, boolean seekToBegin) throws SevenZipException {
|
private void openVolume(int index, boolean seekToBegin) throws SevenZipException {
|
||||||
if (currentIndex == index) {
|
if (currentIndex == index) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = volumePositions.size(); i < index && absoluteLength == -1; i++) {
|
for (int i = volumePositions.size(); i < index && absoluteLength == -1; i++) {
|
||||||
openVolume(i, false);
|
openVolume(i, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (absoluteLength != -1 && volumePositions.size() <= index) {
|
if (absoluteLength != -1 && volumePositions.size() <= index) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String volumeFilename = cuttedVolumeFilename + MessageFormat.format("{0,number,000}", Integer.valueOf(index));
|
String volumeFilename = cuttedVolumeFilename + MessageFormat.format("{0,number,000}", Integer.valueOf(index));
|
||||||
|
|
||||||
// Get new IInStream
|
// Get new IInStream
|
||||||
IInStream newInStream = archiveOpenVolumeCallback.getStream(volumeFilename);
|
IInStream newInStream = archiveOpenVolumeCallback.getStream(volumeFilename);
|
||||||
|
|
||||||
if (newInStream == null) {
|
if (newInStream == null) {
|
||||||
absoluteLength = volumePositions.get(volumePositions.size() - 1).longValue();
|
absoluteLength = volumePositions.get(volumePositions.size() - 1).longValue();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentInStream = newInStream;
|
currentInStream = newInStream;
|
||||||
|
|
||||||
if (volumePositions.size() == index) {
|
if (volumePositions.size() == index) {
|
||||||
// Determine volume size
|
// Determine volume size
|
||||||
currentVolumeLength = currentInStream.seek(0, SEEK_END);
|
currentVolumeLength = currentInStream.seek(0, SEEK_END);
|
||||||
|
@ -80,23 +80,23 @@ public class VolumedArchiveInStream implements IInStream {
|
||||||
throw new RuntimeException("Volume " + index + " is empty");
|
throw new RuntimeException("Volume " + index + " is empty");
|
||||||
}
|
}
|
||||||
volumePositions.add(Long.valueOf(volumePositions.get(index - 1).longValue() + currentVolumeLength));
|
volumePositions.add(Long.valueOf(volumePositions.get(index - 1).longValue() + currentVolumeLength));
|
||||||
|
|
||||||
if (seekToBegin) {
|
if (seekToBegin) {
|
||||||
currentInStream.seek(0, SEEK_SET);
|
currentInStream.seek(0, SEEK_SET);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentVolumeLength = volumePositions.get(index).longValue() - volumePositions.get(index - 1).longValue();
|
currentVolumeLength = volumePositions.get(index).longValue() - volumePositions.get(index - 1).longValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seekToBegin) {
|
if (seekToBegin) {
|
||||||
currentVolumeOffset = 0;
|
currentVolumeOffset = 0;
|
||||||
absoluteOffset = volumePositions.get(index - 1).longValue();
|
absoluteOffset = volumePositions.get(index - 1).longValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
currentIndex = index;
|
currentIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void openVolumeToAbsoluteOffset() throws SevenZipException {
|
private void openVolumeToAbsoluteOffset() throws SevenZipException {
|
||||||
int index = volumePositions.size() - 1;
|
int index = volumePositions.size() - 1;
|
||||||
if (absoluteLength != -1 && absoluteOffset >= absoluteLength) {
|
if (absoluteLength != -1 && absoluteOffset >= absoluteLength) {
|
||||||
|
@ -105,80 +105,80 @@ public class VolumedArchiveInStream implements IInStream {
|
||||||
while (volumePositions.get(index).longValue() > absoluteOffset) {
|
while (volumePositions.get(index).longValue() > absoluteOffset) {
|
||||||
index--;
|
index--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < volumePositions.size() - 1) {
|
if (index < volumePositions.size() - 1) {
|
||||||
openVolume(index + 1, false);
|
openVolume(index + 1, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
index++;
|
index++;
|
||||||
openVolume(index, false);
|
openVolume(index, false);
|
||||||
} while ((absoluteLength == -1 || absoluteOffset < absoluteLength) && volumePositions.get(index).longValue() <= absoluteOffset);
|
} while ((absoluteLength == -1 || absoluteOffset < absoluteLength) && volumePositions.get(index).longValue() <= absoluteOffset);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long seek(long offset, int seekOrigin) throws SevenZipException {
|
public long seek(long offset, int seekOrigin) throws SevenZipException {
|
||||||
long newOffset;
|
long newOffset;
|
||||||
boolean proceedWithSeek = false;
|
boolean proceedWithSeek = false;
|
||||||
switch (seekOrigin) {
|
switch (seekOrigin) {
|
||||||
case SEEK_SET:
|
case SEEK_SET:
|
||||||
newOffset = offset;
|
newOffset = offset;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SEEK_CUR:
|
case SEEK_CUR:
|
||||||
newOffset = absoluteOffset + offset;
|
newOffset = absoluteOffset + offset;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SEEK_END:
|
case SEEK_END:
|
||||||
if (absoluteLength == -1) {
|
if (absoluteLength == -1) {
|
||||||
openVolume(Integer.MAX_VALUE, false);
|
openVolume(Integer.MAX_VALUE, false);
|
||||||
proceedWithSeek = true;
|
proceedWithSeek = true;
|
||||||
}
|
}
|
||||||
newOffset = absoluteLength + offset;
|
newOffset = absoluteLength + offset;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Seek: unknown origin: " + seekOrigin);
|
throw new RuntimeException("Seek: unknown origin: " + seekOrigin);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newOffset == absoluteOffset && !proceedWithSeek) {
|
if (newOffset == absoluteOffset && !proceedWithSeek) {
|
||||||
return newOffset;
|
return newOffset;
|
||||||
}
|
}
|
||||||
absoluteOffset = newOffset;
|
absoluteOffset = newOffset;
|
||||||
|
|
||||||
openVolumeToAbsoluteOffset();
|
openVolumeToAbsoluteOffset();
|
||||||
|
|
||||||
if (absoluteLength != -1 && absoluteLength <= absoluteOffset) {
|
if (absoluteLength != -1 && absoluteLength <= absoluteOffset) {
|
||||||
absoluteOffset = absoluteLength;
|
absoluteOffset = absoluteLength;
|
||||||
return absoluteLength;
|
return absoluteLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentVolumeOffset = absoluteOffset - volumePositions.get(currentIndex - 1).longValue();
|
currentVolumeOffset = absoluteOffset - volumePositions.get(currentIndex - 1).longValue();
|
||||||
currentInStream.seek(currentVolumeOffset, SEEK_SET);
|
currentInStream.seek(currentVolumeOffset, SEEK_SET);
|
||||||
|
|
||||||
return newOffset;
|
return newOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] data) throws SevenZipException {
|
public int read(byte[] data) throws SevenZipException {
|
||||||
if (absoluteLength != -1 && absoluteOffset >= absoluteLength) {
|
if (absoluteLength != -1 && absoluteOffset >= absoluteLength) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read = currentInStream.read(data);
|
int read = currentInStream.read(data);
|
||||||
|
|
||||||
absoluteOffset += read;
|
absoluteOffset += read;
|
||||||
currentVolumeOffset += read;
|
currentVolumeOffset += read;
|
||||||
|
|
||||||
if (currentVolumeOffset >= currentVolumeLength) {
|
if (currentVolumeOffset >= currentVolumeLength) {
|
||||||
openVolume(currentIndex + 1, true);
|
openVolume(currentIndex + 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,6 +292,7 @@ public class GroovyPad extends JFrame {
|
||||||
|
|
||||||
private class ConsoleOutputStream extends ByteArrayOutputStream {
|
private class ConsoleOutputStream extends ByteArrayOutputStream {
|
||||||
|
|
||||||
|
@Override
|
||||||
public void flush() {
|
public void flush() {
|
||||||
try {
|
try {
|
||||||
String message = this.toString("UTF-8");
|
String message = this.toString("UTF-8");
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class ScriptShellMethods {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<File> listFiles(File self, Closure<?> closure) {
|
public static List<File> listFiles(File self, Closure<?> closure) {
|
||||||
return (List<File>) DefaultGroovyMethods.findAll(FileUtilities.getChildren(self), closure);
|
return DefaultGroovyMethods.findAll(FileUtilities.getChildren(self), closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isVideo(File self) {
|
public static boolean isVideo(File self) {
|
||||||
|
@ -121,7 +121,7 @@ public class ScriptShellMethods {
|
||||||
|
|
||||||
List<File> files = FileUtilities.listFiles(roots);
|
List<File> files = FileUtilities.listFiles(roots);
|
||||||
if (closure != null) {
|
if (closure != null) {
|
||||||
files = (List<File>) DefaultGroovyMethods.findAll(files, closure);
|
files = DefaultGroovyMethods.findAll(files, closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileUtilities.sortByUniquePath(files);
|
return FileUtilities.sortByUniquePath(files);
|
||||||
|
@ -144,7 +144,7 @@ public class ScriptShellMethods {
|
||||||
|
|
||||||
List<File> folders = FileUtilities.listFolders(roots);
|
List<File> folders = FileUtilities.listFolders(roots);
|
||||||
if (closure != null) {
|
if (closure != null) {
|
||||||
folders = (List<File>) DefaultGroovyMethods.findAll(folders, closure);
|
folders = DefaultGroovyMethods.findAll(folders, closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileUtilities.sortByUniquePath(folders);
|
return FileUtilities.sortByUniquePath(folders);
|
||||||
|
|
|
@ -15,18 +15,18 @@ import java.util.TreeSet;
|
||||||
|
|
||||||
|
|
||||||
public class AssociativeScriptObject extends GroovyObjectSupport implements Iterable<Entry<Object, Object>> {
|
public class AssociativeScriptObject extends GroovyObjectSupport implements Iterable<Entry<Object, Object>> {
|
||||||
|
|
||||||
private final Map<Object, Object> properties;
|
private final Map<Object, Object> properties;
|
||||||
|
|
||||||
|
|
||||||
public AssociativeScriptObject(Map<?, ?> properties) {
|
public AssociativeScriptObject(Map<?, ?> properties) {
|
||||||
this.properties = new LenientLookup(properties);
|
this.properties = new LenientLookup(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the property with the given name.
|
* Get the property with the given name.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
* the property name
|
* the property name
|
||||||
* @param start
|
* @param start
|
||||||
|
@ -36,77 +36,77 @@ public class AssociativeScriptObject extends GroovyObjectSupport implements Iter
|
||||||
public Object getProperty(String name) {
|
public Object getProperty(String name) {
|
||||||
return properties.get(name);
|
return properties.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setProperty(String name, Object value) {
|
public void setProperty(String name, Object value) {
|
||||||
// ignore, object is immutable
|
// ignore, object is immutable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Entry<Object, Object>> iterator() {
|
public Iterator<Entry<Object, Object>> iterator() {
|
||||||
return properties.entrySet().iterator();
|
return properties.entrySet().iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
// all the properties in alphabetic order
|
// all the properties in alphabetic order
|
||||||
return new TreeSet<Object>(properties.keySet()).toString();
|
return new TreeSet<Object>(properties.keySet()).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map allowing look-up of values by a fault-tolerant key as specified by the defining key.
|
* Map allowing look-up of values by a fault-tolerant key as specified by the defining key.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private static class LenientLookup extends AbstractMap<Object, Object> {
|
private static class LenientLookup extends AbstractMap<Object, Object> {
|
||||||
|
|
||||||
private final Map<String, Entry<?, ?>> lookup = new HashMap<String, Entry<?, ?>>();
|
private final Map<String, Entry<?, ?>> lookup = new HashMap<String, Entry<?, ?>>();
|
||||||
|
|
||||||
|
|
||||||
public LenientLookup(Map<?, ?> source) {
|
public LenientLookup(Map<?, ?> source) {
|
||||||
// populate lookup map
|
// populate lookup map
|
||||||
for (Entry<?, ?> entry : source.entrySet()) {
|
for (Entry<?, ?> entry : source.entrySet()) {
|
||||||
lookup.put(definingKey(entry.getKey()), entry);
|
lookup.put(definingKey(entry.getKey()), entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String definingKey(Object key) {
|
protected String definingKey(Object key) {
|
||||||
// letters and digits are defining, everything else will be ignored
|
// letters and digits are defining, everything else will be ignored
|
||||||
return key.toString().replaceAll("[^\\p{Alnum}]", "").toLowerCase();
|
return key.toString().replaceAll("[^\\p{Alnum}]", "").toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(Object key) {
|
public boolean containsKey(Object key) {
|
||||||
return lookup.containsKey(definingKey(key));
|
return lookup.containsKey(definingKey(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get(Object key) {
|
public Object get(Object key) {
|
||||||
Entry<?, ?> entry = lookup.get(definingKey(key));
|
Entry<?, ?> entry = lookup.get(definingKey(key));
|
||||||
|
|
||||||
if (entry != null)
|
if (entry != null)
|
||||||
return entry.getValue();
|
return entry.getValue();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Entry<Object, Object>> entrySet() {
|
public Set<Entry<Object, Object>> entrySet() {
|
||||||
return new AbstractSet<Entry<Object, Object>>() {
|
return new AbstractSet<Entry<Object, Object>>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Entry<Object, Object>> iterator() {
|
public Iterator<Entry<Object, Object>> iterator() {
|
||||||
return (Iterator) lookup.values().iterator();
|
return (Iterator) lookup.values().iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return lookup.size();
|
return lookup.size();
|
||||||
|
@ -114,5 +114,5 @@ public class AssociativeScriptObject extends GroovyObjectSupport implements Iter
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@ import java.lang.annotation.Target;
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
@Target(METHOD)
|
@Target(METHOD)
|
||||||
public @interface Define {
|
public @interface Define {
|
||||||
|
|
||||||
String[] value();
|
String[] value();
|
||||||
|
|
||||||
static final String undefined = "";
|
static final String undefined = "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,26 +6,26 @@ import javax.script.ScriptException;
|
||||||
|
|
||||||
|
|
||||||
public class ExpressionException extends ScriptException {
|
public class ExpressionException extends ScriptException {
|
||||||
|
|
||||||
private final String message;
|
private final String message;
|
||||||
|
|
||||||
|
|
||||||
public ExpressionException(String message, Exception cause) {
|
public ExpressionException(String message, Exception cause) {
|
||||||
super(cause);
|
super(cause);
|
||||||
|
|
||||||
// can't set message via super constructor
|
// can't set message via super constructor
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ExpressionException(Exception e) {
|
public ExpressionException(Exception e) {
|
||||||
this(e.getMessage(), e);
|
this(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class ExpressionFormatMethods {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pad strings or numbers with given characters ('0' by default).
|
* Pad strings or numbers with given characters ('0' by default).
|
||||||
*
|
*
|
||||||
* e.g. "1" -> "01"
|
* e.g. "1" -> "01"
|
||||||
*/
|
*/
|
||||||
public static String pad(String self, int length, String padding) {
|
public static String pad(String self, int length, String padding) {
|
||||||
|
@ -97,7 +97,7 @@ public class ExpressionFormatMethods {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace space characters with a given characters.
|
* Replace space characters with a given characters.
|
||||||
*
|
*
|
||||||
* e.g. "Doctor Who" -> "Doctor_Who"
|
* e.g. "Doctor Who" -> "Doctor_Who"
|
||||||
*/
|
*/
|
||||||
public static String space(String self, String replacement) {
|
public static String space(String self, String replacement) {
|
||||||
|
@ -106,7 +106,7 @@ public class ExpressionFormatMethods {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upper-case all initials.
|
* Upper-case all initials.
|
||||||
*
|
*
|
||||||
* e.g. "The Day a new Demon was born" -> "The Day A New Demon Was Born"
|
* e.g. "The Day a new Demon was born" -> "The Day A New Demon Was Born"
|
||||||
*/
|
*/
|
||||||
public static String upperInitial(String self) {
|
public static String upperInitial(String self) {
|
||||||
|
@ -131,7 +131,7 @@ public class ExpressionFormatMethods {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get acronym, i.e. first letter of each word.
|
* Get acronym, i.e. first letter of each word.
|
||||||
*
|
*
|
||||||
* e.g. "Deep Space 9" -> "DS9"
|
* e.g. "Deep Space 9" -> "DS9"
|
||||||
*/
|
*/
|
||||||
public static String acronym(String self) {
|
public static String acronym(String self) {
|
||||||
|
@ -148,7 +148,7 @@ public class ExpressionFormatMethods {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lower-case all letters that are not initials.
|
* Lower-case all letters that are not initials.
|
||||||
*
|
*
|
||||||
* e.g. "Gundam SEED" -> "Gundam Seed"
|
* e.g. "Gundam SEED" -> "Gundam Seed"
|
||||||
*/
|
*/
|
||||||
public static String lowerTrail(String self) {
|
public static String lowerTrail(String self) {
|
||||||
|
@ -220,7 +220,7 @@ public class ExpressionFormatMethods {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace trailing parenthesis including any leading whitespace.
|
* Replace trailing parenthesis including any leading whitespace.
|
||||||
*
|
*
|
||||||
* e.g. "The IT Crowd (UK)" -> "The IT Crowd"
|
* e.g. "The IT Crowd (UK)" -> "The IT Crowd"
|
||||||
*/
|
*/
|
||||||
public static String replaceTrailingBrackets(String self) {
|
public static String replaceTrailingBrackets(String self) {
|
||||||
|
@ -233,7 +233,7 @@ public class ExpressionFormatMethods {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace 'part identifier'.
|
* Replace 'part identifier'.
|
||||||
*
|
*
|
||||||
* e.g. "Today Is the Day: Part 1" -> "Today Is the Day, Part 1" or "Today Is the Day (1)" -> "Today Is the Day, Part 1"
|
* e.g. "Today Is the Day: Part 1" -> "Today Is the Day, Part 1" or "Today Is the Day (1)" -> "Today Is the Day, Part 1"
|
||||||
*/
|
*/
|
||||||
public static String replacePart(String self) {
|
public static String replacePart(String self) {
|
||||||
|
@ -257,7 +257,7 @@ public class ExpressionFormatMethods {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply ICU transliteration
|
* Apply ICU transliteration
|
||||||
*
|
*
|
||||||
* @see http://userguide.icu-project.org/transforms/general
|
* @see http://userguide.icu-project.org/transforms/general
|
||||||
*/
|
*/
|
||||||
public static String transliterate(String self, String transformIdentifier) {
|
public static String transliterate(String self, String transformIdentifier) {
|
||||||
|
@ -266,7 +266,7 @@ public class ExpressionFormatMethods {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert Unicode to ASCII as best as possible. Works with most alphabets/scripts used in the world.
|
* Convert Unicode to ASCII as best as possible. Works with most alphabets/scripts used in the world.
|
||||||
*
|
*
|
||||||
* e.g. "Österreich" -> "Osterreich" "カタカナ" -> "katakana"
|
* e.g. "Österreich" -> "Osterreich" "カタカナ" -> "katakana"
|
||||||
*/
|
*/
|
||||||
public static String ascii(String self) {
|
public static String ascii(String self) {
|
||||||
|
@ -283,7 +283,7 @@ public class ExpressionFormatMethods {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace multiple replacement pairs
|
* Replace multiple replacement pairs
|
||||||
*
|
*
|
||||||
* e.g. replace('ä', 'ae', 'ö', 'oe', 'ü', 'ue')
|
* e.g. replace('ä', 'ae', 'ö', 'oe', 'ü', 'ue')
|
||||||
*/
|
*/
|
||||||
public static String replace(String self, String tr0, String tr1, String... tr) {
|
public static String replace(String self, String tr0, String tr1, String... tr) {
|
||||||
|
|
|
@ -91,10 +91,10 @@ public class MediaBindingBean {
|
||||||
return getMovie().getName();
|
return getMovie().getName();
|
||||||
if (infoObject instanceof AudioTrack)
|
if (infoObject instanceof AudioTrack)
|
||||||
return getAlbumArtist() != null ? getAlbumArtist() : getArtist();
|
return getAlbumArtist() != null ? getAlbumArtist() : getArtist();
|
||||||
if (infoObject instanceof File)
|
if (infoObject instanceof File)
|
||||||
return FileUtilities.getName((File) infoObject);
|
return FileUtilities.getName((File) infoObject);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Define("y")
|
@Define("y")
|
||||||
|
@ -909,6 +909,7 @@ public class MediaBindingBean {
|
||||||
|
|
||||||
private AssociativeScriptObject createBindingObject(File file, Object info, Map<File, Object> context) {
|
private AssociativeScriptObject createBindingObject(File file, Object info, Map<File, Object> context) {
|
||||||
MediaBindingBean mediaBindingBean = new MediaBindingBean(info, file, context) {
|
MediaBindingBean mediaBindingBean = new MediaBindingBean(info, file, context) {
|
||||||
|
@Override
|
||||||
@Define(undefined)
|
@Define(undefined)
|
||||||
public <T> T undefined(String name) {
|
public <T> T undefined(String name) {
|
||||||
return null; // never throw exceptions for empty or null values
|
return null; // never throw exceptions for empty or null values
|
||||||
|
|
|
@ -13,22 +13,22 @@ import java.security.PrivilegedExceptionAction;
|
||||||
|
|
||||||
|
|
||||||
public final class PrivilegedInvocation implements InvocationHandler {
|
public final class PrivilegedInvocation implements InvocationHandler {
|
||||||
|
|
||||||
private final Object object;
|
private final Object object;
|
||||||
private final AccessControlContext context;
|
private final AccessControlContext context;
|
||||||
|
|
||||||
|
|
||||||
private PrivilegedInvocation(Object object, AccessControlContext context) {
|
private PrivilegedInvocation(Object object, AccessControlContext context) {
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
|
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
|
||||||
try {
|
try {
|
||||||
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
|
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object run() throws Exception {
|
public Object run() throws Exception {
|
||||||
return method.invoke(object, args);
|
return method.invoke(object, args);
|
||||||
|
@ -36,23 +36,23 @@ public final class PrivilegedInvocation implements InvocationHandler {
|
||||||
}, context);
|
}, context);
|
||||||
} catch (PrivilegedActionException e) {
|
} catch (PrivilegedActionException e) {
|
||||||
Throwable cause = e.getException();
|
Throwable cause = e.getException();
|
||||||
|
|
||||||
// the underlying method may have throw an exception
|
// the underlying method may have throw an exception
|
||||||
if (cause instanceof InvocationTargetException) {
|
if (cause instanceof InvocationTargetException) {
|
||||||
// get actual cause
|
// get actual cause
|
||||||
cause = cause.getCause();
|
cause = cause.getCause();
|
||||||
}
|
}
|
||||||
|
|
||||||
// forward cause
|
// forward cause
|
||||||
throw cause;
|
throw cause;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static <I> I newProxy(Class<I> interfaceClass, I object, AccessControlContext context) {
|
public static <I> I newProxy(Class<I> interfaceClass, I object, AccessControlContext context) {
|
||||||
InvocationHandler invocationHandler = new PrivilegedInvocation(object, context);
|
InvocationHandler invocationHandler = new PrivilegedInvocation(object, context);
|
||||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
|
||||||
// create dynamic invocation proxy
|
// create dynamic invocation proxy
|
||||||
return interfaceClass.cast(Proxy.newProxyInstance(classLoader, new Class[] { interfaceClass }, invocationHandler));
|
return interfaceClass.cast(Proxy.newProxyInstance(classLoader, new Class[] { interfaceClass }, invocationHandler));
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,17 +14,17 @@ import java.util.TreeMap;
|
||||||
* Used to create a map view of the properties of an Object
|
* Used to create a map view of the properties of an Object
|
||||||
*/
|
*/
|
||||||
public class PropertyBindings extends AbstractMap<String, Object> {
|
public class PropertyBindings extends AbstractMap<String, Object> {
|
||||||
|
|
||||||
private final Object object;
|
private final Object object;
|
||||||
private final Map<String, Object> properties = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
|
private final Map<String, Object> properties = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
private final Object defaultValue;
|
private final Object defaultValue;
|
||||||
|
|
||||||
|
|
||||||
public PropertyBindings(Object object, Object defaultValue) {
|
public PropertyBindings(Object object, Object defaultValue) {
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.defaultValue = defaultValue;
|
this.defaultValue = defaultValue;
|
||||||
|
|
||||||
// get method bindings
|
// get method bindings
|
||||||
for (Method method : object.getClass().getMethods()) {
|
for (Method method : object.getClass().getMethods()) {
|
||||||
if (method.getReturnType() != void.class && method.getParameterTypes().length == 0 && !method.getDeclaringClass().getName().startsWith("java")) {
|
if (method.getReturnType() != void.class && method.getParameterTypes().length == 0 && !method.getDeclaringClass().getName().startsWith("java")) {
|
||||||
|
@ -32,7 +32,7 @@ public class PropertyBindings extends AbstractMap<String, Object> {
|
||||||
if (method.getName().length() > 3 && method.getName().substring(0, 3).equalsIgnoreCase("get")) {
|
if (method.getName().length() > 3 && method.getName().substring(0, 3).equalsIgnoreCase("get")) {
|
||||||
properties.put(method.getName().substring(3), method);
|
properties.put(method.getName().substring(3), method);
|
||||||
}
|
}
|
||||||
|
|
||||||
// boolean properties
|
// boolean properties
|
||||||
if (method.getName().length() > 2 && method.getName().substring(0, 3).equalsIgnoreCase("is")) {
|
if (method.getName().length() > 2 && method.getName().substring(0, 3).equalsIgnoreCase("is")) {
|
||||||
properties.put(method.getName().substring(2), method);
|
properties.put(method.getName().substring(2), method);
|
||||||
|
@ -40,17 +40,17 @@ public class PropertyBindings extends AbstractMap<String, Object> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get(Object key) {
|
public Object get(Object key) {
|
||||||
Object value = properties.get(key);
|
Object value = properties.get(key);
|
||||||
|
|
||||||
// evaluate method
|
// evaluate method
|
||||||
if (value instanceof Method) {
|
if (value instanceof Method) {
|
||||||
try {
|
try {
|
||||||
value = ((Method) value).invoke(object);
|
value = ((Method) value).invoke(object);
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
value = defaultValue;
|
value = defaultValue;
|
||||||
}
|
}
|
||||||
|
@ -58,74 +58,74 @@ public class PropertyBindings extends AbstractMap<String, Object> {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object put(String key, Object value) {
|
public Object put(String key, Object value) {
|
||||||
return properties.put(key, value);
|
return properties.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object remove(Object key) {
|
public Object remove(Object key) {
|
||||||
return properties.remove(key);
|
return properties.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(Object key) {
|
public boolean containsKey(Object key) {
|
||||||
return properties.containsKey(key);
|
return properties.containsKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> keySet() {
|
public Set<String> keySet() {
|
||||||
return properties.keySet();
|
return properties.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return properties.isEmpty();
|
return properties.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return properties.toString();
|
return properties.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Entry<String, Object>> entrySet() {
|
public Set<Entry<String, Object>> entrySet() {
|
||||||
Set<Entry<String, Object>> entrySet = new HashSet<Entry<String, Object>>();
|
Set<Entry<String, Object>> entrySet = new HashSet<Entry<String, Object>>();
|
||||||
|
|
||||||
for (final String key : keySet()) {
|
for (final String key : keySet()) {
|
||||||
entrySet.add(new Entry<String, Object>() {
|
entrySet.add(new Entry<String, Object>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getValue() {
|
public Object getValue() {
|
||||||
return get(key);
|
return get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object setValue(Object value) {
|
public Object setValue(Object value) {
|
||||||
return put(key, value);
|
return put(key, value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return entrySet;
|
return entrySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,22 +7,22 @@ import com.sun.jna.Pointer;
|
||||||
|
|
||||||
|
|
||||||
interface GIOLibrary extends Library {
|
interface GIOLibrary extends Library {
|
||||||
|
|
||||||
void g_type_init();
|
void g_type_init();
|
||||||
|
|
||||||
|
|
||||||
Pointer g_vfs_get_default();
|
Pointer g_vfs_get_default();
|
||||||
|
|
||||||
|
|
||||||
Pointer g_vfs_get_file_for_uri(Pointer gvfs, String uri);
|
Pointer g_vfs_get_file_for_uri(Pointer gvfs, String uri);
|
||||||
|
|
||||||
|
|
||||||
Pointer g_file_get_path(Pointer gfile);
|
Pointer g_file_get_path(Pointer gfile);
|
||||||
|
|
||||||
|
|
||||||
void g_free(Pointer gpointer);
|
void g_free(Pointer gpointer);
|
||||||
|
|
||||||
|
|
||||||
void g_object_unref(Pointer gobject);
|
void g_object_unref(Pointer gobject);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,31 +10,31 @@ import com.sun.jna.Pointer;
|
||||||
|
|
||||||
|
|
||||||
public class GVFS {
|
public class GVFS {
|
||||||
|
|
||||||
private static GIOLibrary lib;
|
private static GIOLibrary lib;
|
||||||
private static Pointer gvfs;
|
private static Pointer gvfs;
|
||||||
|
|
||||||
|
|
||||||
private synchronized static GIOLibrary getLibrary() {
|
private synchronized static GIOLibrary getLibrary() {
|
||||||
if (lib == null) {
|
if (lib == null) {
|
||||||
lib = (GIOLibrary) Native.loadLibrary("gio-2.0", GIOLibrary.class);
|
lib = (GIOLibrary) Native.loadLibrary("gio-2.0", GIOLibrary.class);
|
||||||
}
|
}
|
||||||
return lib;
|
return lib;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public synchronized static Pointer getDefaultVFS() {
|
public synchronized static Pointer getDefaultVFS() {
|
||||||
if (gvfs == null) {
|
if (gvfs == null) {
|
||||||
gvfs = getLibrary().g_vfs_get_default();
|
gvfs = getLibrary().g_vfs_get_default();
|
||||||
}
|
}
|
||||||
return gvfs;
|
return gvfs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static File getPathForURI(URI uri) {
|
public static File getPathForURI(URI uri) {
|
||||||
Pointer gfile = getLibrary().g_vfs_get_file_for_uri(getDefaultVFS(), uri.toString());
|
Pointer gfile = getLibrary().g_vfs_get_file_for_uri(getDefaultVFS(), uri.toString());
|
||||||
Pointer chars = getLibrary().g_file_get_path(gfile);
|
Pointer chars = getLibrary().g_file_get_path(gfile);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (chars != null)
|
if (chars != null)
|
||||||
return new File(chars.getString(0));
|
return new File(chars.getString(0));
|
||||||
|
@ -45,5 +45,5 @@ public class GVFS {
|
||||||
getLibrary().g_free(chars);
|
getLibrary().g_free(chars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,24 +6,24 @@ import java.util.zip.Checksum;
|
||||||
|
|
||||||
|
|
||||||
public class ChecksumHash implements Hash {
|
public class ChecksumHash implements Hash {
|
||||||
|
|
||||||
private final Checksum checksum;
|
private final Checksum checksum;
|
||||||
|
|
||||||
|
|
||||||
public ChecksumHash(Checksum checksum) {
|
public ChecksumHash(Checksum checksum) {
|
||||||
this.checksum = checksum;
|
this.checksum = checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(byte[] bytes, int off, int len) {
|
public void update(byte[] bytes, int off, int len) {
|
||||||
checksum.update(bytes, off, len);
|
checksum.update(bytes, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String digest() {
|
public String digest() {
|
||||||
return String.format("%08X", checksum.getValue());
|
return String.format("%08X", checksum.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,10 @@ import jonelo.jacksum.algorithm.Edonkey;
|
||||||
|
|
||||||
|
|
||||||
public class Ed2kHash implements Hash {
|
public class Ed2kHash implements Hash {
|
||||||
|
|
||||||
private final Edonkey ed2k;
|
private final Edonkey ed2k;
|
||||||
|
|
||||||
|
|
||||||
public Ed2kHash() {
|
public Ed2kHash() {
|
||||||
try {
|
try {
|
||||||
this.ed2k = new Edonkey();
|
this.ed2k = new Edonkey();
|
||||||
|
@ -20,17 +20,17 @@ public class Ed2kHash implements Hash {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(byte[] bytes, int off, int len) {
|
public void update(byte[] bytes, int off, int len) {
|
||||||
ed2k.update(bytes, off, len);
|
ed2k.update(bytes, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String digest() {
|
public String digest() {
|
||||||
return String.format("%0" + (ed2k.getByteArray().length * 2) + "x", new BigInteger(1, ed2k.getByteArray()));
|
return String.format("%0" + (ed2k.getByteArray().length * 2) + "x", new BigInteger(1, ed2k.getByteArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,10 @@ package net.filebot.hash;
|
||||||
|
|
||||||
|
|
||||||
public interface Hash {
|
public interface Hash {
|
||||||
|
|
||||||
public void update(byte[] bytes, int off, int len);
|
public void update(byte[] bytes, int off, int len);
|
||||||
|
|
||||||
|
|
||||||
public String digest();
|
public String digest();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,10 @@ import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
|
||||||
public class MessageDigestHash implements Hash {
|
public class MessageDigestHash implements Hash {
|
||||||
|
|
||||||
private final MessageDigest md;
|
private final MessageDigest md;
|
||||||
|
|
||||||
|
|
||||||
public MessageDigestHash(String algorithm) {
|
public MessageDigestHash(String algorithm) {
|
||||||
try {
|
try {
|
||||||
this.md = MessageDigest.getInstance(algorithm);
|
this.md = MessageDigest.getInstance(algorithm);
|
||||||
|
@ -19,23 +19,23 @@ public class MessageDigestHash implements Hash {
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public MessageDigestHash(MessageDigest md) {
|
public MessageDigestHash(MessageDigest md) {
|
||||||
this.md = md;
|
this.md = md;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(byte[] bytes, int off, int len) {
|
public void update(byte[] bytes, int off, int len) {
|
||||||
md.update(bytes, off, len);
|
md.update(bytes, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String digest() {
|
public String digest() {
|
||||||
// e.g. %032x (format for MD-5)
|
// e.g. %032x (format for MD-5)
|
||||||
return String.format("%0" + (md.getDigestLength() * 2) + "x", new BigInteger(1, md.digest()));
|
return String.format("%0" + (md.getDigestLength() * 2) + "x", new BigInteger(1, md.digest()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,17 +10,17 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
public class SfvFormat extends VerificationFormat {
|
public class SfvFormat extends VerificationFormat {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String format(String path, String hash) {
|
public String format(String path, String hash) {
|
||||||
// e.g folder/file.txt 970E4EF1
|
// e.g folder/file.txt 970E4EF1
|
||||||
return String.format("%s %s", path, hash);
|
return String.format("%s %s", path, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pattern used to parse the lines of a sfv file.
|
* Pattern used to parse the lines of a sfv file.
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* Sample:
|
* Sample:
|
||||||
* folder/file.txt 970E4EF1
|
* folder/file.txt 970E4EF1
|
||||||
|
@ -28,17 +28,17 @@ public class SfvFormat extends VerificationFormat {
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
private final Pattern pattern = Pattern.compile("^(.+)\\s+(\\p{XDigit}{8})$");
|
private final Pattern pattern = Pattern.compile("^(.+)\\s+(\\p{XDigit}{8})$");
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entry<File, String> parseObject(String line) throws ParseException {
|
public Entry<File, String> parseObject(String line) throws ParseException {
|
||||||
Matcher matcher = pattern.matcher(line);
|
Matcher matcher = pattern.matcher(line);
|
||||||
|
|
||||||
if (!matcher.matches()) {
|
if (!matcher.matches()) {
|
||||||
throw new ParseException("Illegal input pattern", 0);
|
throw new ParseException("Illegal input pattern", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entry(matcher.group(1), matcher.group(2));
|
return entry(matcher.group(1), matcher.group(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,21 +15,21 @@ import java.util.logging.Logger;
|
||||||
|
|
||||||
|
|
||||||
public class VerificationFileReader implements Iterator<Entry<File, String>>, Closeable {
|
public class VerificationFileReader implements Iterator<Entry<File, String>>, Closeable {
|
||||||
|
|
||||||
private final Scanner scanner;
|
private final Scanner scanner;
|
||||||
|
|
||||||
private final VerificationFormat format;
|
private final VerificationFormat format;
|
||||||
|
|
||||||
private Entry<File, String> buffer;
|
private Entry<File, String> buffer;
|
||||||
|
|
||||||
private int lineNumber = 0;
|
private int lineNumber = 0;
|
||||||
|
|
||||||
|
|
||||||
public VerificationFileReader(Readable source, VerificationFormat format) {
|
public VerificationFileReader(Readable source, VerificationFormat format) {
|
||||||
this.scanner = new Scanner(source);
|
this.scanner = new Scanner(source);
|
||||||
this.format = format;
|
this.format = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
|
@ -37,10 +37,10 @@ public class VerificationFileReader implements Iterator<Entry<File, String>>, Cl
|
||||||
// cache next entry
|
// cache next entry
|
||||||
buffer = nextEntry();
|
buffer = nextEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer != null;
|
return buffer != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entry<File, String> next() {
|
public Entry<File, String> next() {
|
||||||
|
@ -48,7 +48,7 @@ public class VerificationFileReader implements Iterator<Entry<File, String>>, Cl
|
||||||
if (!hasNext()) {
|
if (!hasNext()) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return buffer;
|
return buffer;
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -56,15 +56,15 @@ public class VerificationFileReader implements Iterator<Entry<File, String>>, Cl
|
||||||
buffer = null;
|
buffer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected Entry<File, String> nextEntry() {
|
protected Entry<File, String> nextEntry() {
|
||||||
Entry<File, String> entry = null;
|
Entry<File, String> entry = null;
|
||||||
|
|
||||||
// get next valid entry
|
// get next valid entry
|
||||||
while (entry == null && scanner.hasNextLine()) {
|
while (entry == null && scanner.hasNextLine()) {
|
||||||
String line = scanner.nextLine().trim();
|
String line = scanner.nextLine().trim();
|
||||||
|
|
||||||
// ignore comments
|
// ignore comments
|
||||||
if (!isComment(line)) {
|
if (!isComment(line)) {
|
||||||
try {
|
try {
|
||||||
|
@ -74,33 +74,33 @@ public class VerificationFileReader implements Iterator<Entry<File, String>>, Cl
|
||||||
Logger.getLogger(getClass().getName()).log(Level.WARNING, String.format("Illegal format on line %d: %s", lineNumber, line));
|
Logger.getLogger(getClass().getName()).log(Level.WARNING, String.format("Illegal format on line %d: %s", lineNumber, line));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lineNumber++;
|
lineNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int getLineNumber() {
|
public int getLineNumber() {
|
||||||
return lineNumber;
|
return lineNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected boolean isComment(String line) {
|
protected boolean isComment(String line) {
|
||||||
return line.isEmpty() || line.startsWith(";");
|
return line.isEmpty() || line.startsWith(";");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
scanner.close();
|
scanner.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove() {
|
public void remove() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,40 +12,40 @@ import net.filebot.Settings;
|
||||||
|
|
||||||
|
|
||||||
public class VerificationFileWriter implements Closeable {
|
public class VerificationFileWriter implements Closeable {
|
||||||
|
|
||||||
protected PrintWriter out;
|
protected PrintWriter out;
|
||||||
protected VerificationFormat format;
|
protected VerificationFormat format;
|
||||||
|
|
||||||
|
|
||||||
public VerificationFileWriter(File file, VerificationFormat format, String charset) throws IOException {
|
public VerificationFileWriter(File file, VerificationFormat format, String charset) throws IOException {
|
||||||
this(new PrintWriter(file, charset), format, charset);
|
this(new PrintWriter(file, charset), format, charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public VerificationFileWriter(PrintWriter out, VerificationFormat format, String charset) {
|
public VerificationFileWriter(PrintWriter out, VerificationFormat format, String charset) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
this.format = format;
|
this.format = format;
|
||||||
|
|
||||||
// start by printing the file header
|
// start by printing the file header
|
||||||
writeHeader(charset);
|
writeHeader(charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void writeHeader(String charset) {
|
protected void writeHeader(String charset) {
|
||||||
out.format("; Generated by %s %s on %tF at %<tT%n", Settings.getApplicationName(), Settings.getApplicationVersion(), new Date());
|
out.format("; Generated by %s %s on %tF at %<tT%n", Settings.getApplicationName(), Settings.getApplicationVersion(), new Date());
|
||||||
out.format("; charset=%s%n", charset);
|
out.format("; charset=%s%n", charset);
|
||||||
out.format(";%n");
|
out.format(";%n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void write(String path, String hash) {
|
public void write(String path, String hash) {
|
||||||
out.format("%s%n", format.format(path, hash));
|
out.format("%s%n", format.format(path, hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,74 +14,74 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
public class VerificationFormat extends Format {
|
public class VerificationFormat extends Format {
|
||||||
|
|
||||||
private final String hashTypeHint;
|
private final String hashTypeHint;
|
||||||
|
|
||||||
|
|
||||||
public VerificationFormat() {
|
public VerificationFormat() {
|
||||||
this.hashTypeHint = "";
|
this.hashTypeHint = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public VerificationFormat(String hashTypeHint) {
|
public VerificationFormat(String hashTypeHint) {
|
||||||
this.hashTypeHint = hashTypeHint.isEmpty() ? "" : '?' + hashTypeHint.toUpperCase();
|
this.hashTypeHint = hashTypeHint.isEmpty() ? "" : '?' + hashTypeHint.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StringBuffer format(Object obj, StringBuffer sb, FieldPosition pos) {
|
public StringBuffer format(Object obj, StringBuffer sb, FieldPosition pos) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Entry<File, String> entry = (Entry<File, String>) obj;
|
Entry<File, String> entry = (Entry<File, String>) obj;
|
||||||
|
|
||||||
String path = entry.getKey().getPath();
|
String path = entry.getKey().getPath();
|
||||||
String hash = entry.getValue();
|
String hash = entry.getValue();
|
||||||
|
|
||||||
return sb.append(format(path, hash));
|
return sb.append(format(path, hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String format(String path, String hash) {
|
public String format(String path, String hash) {
|
||||||
// e.g. 1a02a7c1e9ac91346d08829d5037b240f42ded07 ?SHA1*folder/file.txt
|
// e.g. 1a02a7c1e9ac91346d08829d5037b240f42ded07 ?SHA1*folder/file.txt
|
||||||
return String.format("%s %s*%s", hash, hashTypeHint, path);
|
return String.format("%s %s*%s", hash, hashTypeHint, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pattern used to parse the lines of a md5 or sha1 file.
|
* Pattern used to parse the lines of a md5 or sha1 file.
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* Sample MD5:
|
* Sample MD5:
|
||||||
* 50e85fe18e17e3616774637a82968f4c *folder/file.txt
|
* 50e85fe18e17e3616774637a82968f4c *folder/file.txt
|
||||||
* | Group 1 | Group 2 |
|
* | Group 1 | Group 2 |
|
||||||
*
|
*
|
||||||
* Sample SHA-1:
|
* Sample SHA-1:
|
||||||
* 1a02a7c1e9ac91346d08829d5037b240f42ded07 ?SHA1*folder/file.txt
|
* 1a02a7c1e9ac91346d08829d5037b240f42ded07 ?SHA1*folder/file.txt
|
||||||
* | Group 1 | | Group 2 |
|
* | Group 1 | | Group 2 |
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
private final Pattern pattern = Pattern.compile("^(\\p{XDigit}+)\\s+(?:\\?\\w+)?\\*?(.+)$");
|
private final Pattern pattern = Pattern.compile("^(\\p{XDigit}+)\\s+(?:\\?\\w+)?\\*?(.+)$");
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entry<File, String> parseObject(String line) throws ParseException {
|
public Entry<File, String> parseObject(String line) throws ParseException {
|
||||||
Matcher matcher = pattern.matcher(line);
|
Matcher matcher = pattern.matcher(line);
|
||||||
|
|
||||||
if (!matcher.find()) {
|
if (!matcher.find()) {
|
||||||
throw new ParseException("Illegal input pattern", 0);
|
throw new ParseException("Illegal input pattern", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entry(matcher.group(2), matcher.group(1));
|
return entry(matcher.group(2), matcher.group(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entry<File, String> parseObject(String line, ParsePosition pos) {
|
public Entry<File, String> parseObject(String line, ParsePosition pos) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected Entry<File, String> entry(String path, String hash) {
|
protected Entry<File, String> entry(String path, String hash) {
|
||||||
return new SimpleImmutableEntry<File, String>(new File(path), hash);
|
return new SimpleImmutableEntry<File, String>(new File(path), hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,6 +284,7 @@ public class DropToUnlock extends JList<File> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void handleTransferable(Transferable tr, TransferAction action) throws Exception {
|
public void handleTransferable(Transferable tr, TransferAction action) throws Exception {
|
||||||
List<File> files = FileTransferable.getFilesFromTransferable(tr);
|
List<File> files = FileTransferable.getFilesFromTransferable(tr);
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
|
@ -318,6 +319,7 @@ public class DropToUnlock extends JList<File> {
|
||||||
|
|
||||||
protected static class FileChooserAction extends MouseAdapter {
|
protected static class FileChooserAction extends MouseAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
public void mouseClicked(MouseEvent evt) {
|
public void mouseClicked(MouseEvent evt) {
|
||||||
DropToUnlock list = (DropToUnlock) evt.getSource();
|
DropToUnlock list = (DropToUnlock) evt.getSource();
|
||||||
if (evt.getClickCount() > 0) {
|
if (evt.getClickCount() > 0) {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/* Copyright (c) 2014 Reinhard Pointner, All Rights Reserved
|
/* Copyright (c) 2014 Reinhard Pointner, All Rights Reserved
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
@ -18,7 +18,7 @@ import com.sun.jna.Pointer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JNA wrapper for <sys/xattr.h>
|
* JNA wrapper for <sys/xattr.h>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
interface XAttr extends Library {
|
interface XAttr extends Library {
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ public class SmartSeasonEpisodeMatcher extends SeasonEpisodeMatcher {
|
||||||
return super.match(new File(clean(file.getPath())));
|
return super.match(new File(clean(file.getPath())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String head(String name) {
|
public String head(String name) {
|
||||||
return super.head(clean(name));
|
return super.head(clean(name));
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,14 @@ interface MediaInfoLibrary extends Library {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new handle.
|
* Create a new handle.
|
||||||
*
|
*
|
||||||
* @return handle
|
* @return handle
|
||||||
*/
|
*/
|
||||||
Pointer New();
|
Pointer New();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a file and collect information about it (technical information and tags).
|
* Open a file and collect information about it (technical information and tags).
|
||||||
*
|
*
|
||||||
* @param handle
|
* @param handle
|
||||||
* @param file
|
* @param file
|
||||||
* full name of the file to open
|
* full name of the file to open
|
||||||
|
@ -41,7 +41,7 @@ interface MediaInfoLibrary extends Library {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure or get information about MediaInfo.
|
* Configure or get information about MediaInfo.
|
||||||
*
|
*
|
||||||
* @param handle
|
* @param handle
|
||||||
* @param option
|
* @param option
|
||||||
* The name of option
|
* The name of option
|
||||||
|
@ -53,7 +53,7 @@ interface MediaInfoLibrary extends Library {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all details about a file.
|
* Get all details about a file.
|
||||||
*
|
*
|
||||||
* @param handle
|
* @param handle
|
||||||
* @return All details about a file in one string
|
* @return All details about a file in one string
|
||||||
*/
|
*/
|
||||||
|
@ -61,7 +61,7 @@ interface MediaInfoLibrary extends Library {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a piece of information about a file (parameter is a string).
|
* Get a piece of information about a file (parameter is a string).
|
||||||
*
|
*
|
||||||
* @param handle
|
* @param handle
|
||||||
* @param streamKind
|
* @param streamKind
|
||||||
* Kind of stream (general, video, audio...)
|
* Kind of stream (general, video, audio...)
|
||||||
|
@ -79,7 +79,7 @@ interface MediaInfoLibrary extends Library {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a piece of information about a file (parameter is an integer).
|
* Get a piece of information about a file (parameter is an integer).
|
||||||
*
|
*
|
||||||
* @param handle
|
* @param handle
|
||||||
* @param streamKind
|
* @param streamKind
|
||||||
* Kind of stream (general, video, audio...)
|
* Kind of stream (general, video, audio...)
|
||||||
|
@ -95,7 +95,7 @@ interface MediaInfoLibrary extends Library {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count of streams of a stream kind (StreamNumber not filled), or count of piece of information in this stream.
|
* Count of streams of a stream kind (StreamNumber not filled), or count of piece of information in this stream.
|
||||||
*
|
*
|
||||||
* @param handle
|
* @param handle
|
||||||
* @param streamKind
|
* @param streamKind
|
||||||
* Kind of stream (general, video, audio...)
|
* Kind of stream (general, video, audio...)
|
||||||
|
@ -107,14 +107,14 @@ interface MediaInfoLibrary extends Library {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close a file opened before with Open().
|
* Close a file opened before with Open().
|
||||||
*
|
*
|
||||||
* @param handle
|
* @param handle
|
||||||
*/
|
*/
|
||||||
void Close(Pointer handle);
|
void Close(Pointer handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispose of a handle created with New().
|
* Dispose of a handle created with New().
|
||||||
*
|
*
|
||||||
* @param handle
|
* @param handle
|
||||||
*/
|
*/
|
||||||
void Delete(Pointer handle);
|
void Delete(Pointer handle);
|
||||||
|
|
|
@ -10,37 +10,37 @@ import net.filebot.format.PropertyBindings;
|
||||||
|
|
||||||
|
|
||||||
public class CrossPropertyMetric implements SimilarityMetric {
|
public class CrossPropertyMetric implements SimilarityMetric {
|
||||||
|
|
||||||
private SimilarityMetric metric;
|
private SimilarityMetric metric;
|
||||||
|
|
||||||
|
|
||||||
public CrossPropertyMetric(SimilarityMetric metric) {
|
public CrossPropertyMetric(SimilarityMetric metric) {
|
||||||
this.metric = metric;
|
this.metric = metric;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public CrossPropertyMetric() {
|
public CrossPropertyMetric() {
|
||||||
this.metric = new StringEqualsMetric();
|
this.metric = new StringEqualsMetric();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getSimilarity(Object o1, Object o2) {
|
public float getSimilarity(Object o1, Object o2) {
|
||||||
Map<String, Object> m1 = getProperties(o1);
|
Map<String, Object> m1 = getProperties(o1);
|
||||||
if (m1.isEmpty())
|
if (m1.isEmpty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Map<String, Object> m2 = getProperties(o2);
|
Map<String, Object> m2 = getProperties(o2);
|
||||||
if (m2.isEmpty())
|
if (m2.isEmpty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// works with commons keys
|
// works with commons keys
|
||||||
Set<String> keys = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
|
Set<String> keys = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
|
||||||
keys.addAll(m1.keySet());
|
keys.addAll(m1.keySet());
|
||||||
keys.retainAll(m2.keySet());
|
keys.retainAll(m2.keySet());
|
||||||
if (keys.isEmpty())
|
if (keys.isEmpty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
float feedback = 0;
|
float feedback = 0;
|
||||||
for (String k : keys) {
|
for (String k : keys) {
|
||||||
try {
|
try {
|
||||||
|
@ -49,13 +49,13 @@ public class CrossPropertyMetric implements SimilarityMetric {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return feedback / keys.size();
|
return feedback / keys.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected Map<String, Object> getProperties(Object object) {
|
protected Map<String, Object> getProperties(Object object) {
|
||||||
return new PropertyBindings(object, null);
|
return new PropertyBindings(object, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,28 +8,28 @@ import java.io.File;
|
||||||
|
|
||||||
|
|
||||||
public class FileNameMetric implements SimilarityMetric {
|
public class FileNameMetric implements SimilarityMetric {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getSimilarity(Object o1, Object o2) {
|
public float getSimilarity(Object o1, Object o2) {
|
||||||
String s1 = getFileName(o1);
|
String s1 = getFileName(o1);
|
||||||
if (s1 == null || s1.isEmpty())
|
if (s1 == null || s1.isEmpty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
String s2 = getFileName(o2);
|
String s2 = getFileName(o2);
|
||||||
if (s2 == null || s2.isEmpty())
|
if (s2 == null || s2.isEmpty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return s1.startsWith(s2) || s2.startsWith(s1) ? 1 : 0;
|
return s1.startsWith(s2) || s2.startsWith(s1) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String getFileName(Object object) {
|
protected String getFileName(Object object) {
|
||||||
if (object instanceof File) {
|
if (object instanceof File) {
|
||||||
// name without extension normalized to lower-case
|
// name without extension normalized to lower-case
|
||||||
return getName((File) object).trim().toLowerCase();
|
return getName((File) object).trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,28 +6,28 @@ import java.io.File;
|
||||||
|
|
||||||
|
|
||||||
public class FileSizeMetric implements SimilarityMetric {
|
public class FileSizeMetric implements SimilarityMetric {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getSimilarity(Object o1, Object o2) {
|
public float getSimilarity(Object o1, Object o2) {
|
||||||
long l1 = getLength(o1);
|
long l1 = getLength(o1);
|
||||||
if (l1 < 0)
|
if (l1 < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
long l2 = getLength(o2);
|
long l2 = getLength(o2);
|
||||||
if (l2 < 0)
|
if (l2 < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// objects have the same non-negative length
|
// objects have the same non-negative length
|
||||||
return l1 == l2 ? 1 : -1;
|
return l1 == l2 ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected long getLength(Object object) {
|
protected long getLength(Object object) {
|
||||||
if (object instanceof File) {
|
if (object instanceof File) {
|
||||||
return ((File) object).length();
|
return ((File) object).length();
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,26 +6,26 @@ import java.util.Arrays;
|
||||||
|
|
||||||
|
|
||||||
public class Match<Value, Candidate> {
|
public class Match<Value, Candidate> {
|
||||||
|
|
||||||
private final Value value;
|
private final Value value;
|
||||||
private final Candidate candidate;
|
private final Candidate candidate;
|
||||||
|
|
||||||
|
|
||||||
public Match(Value value, Candidate candidate) {
|
public Match(Value value, Candidate candidate) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.candidate = candidate;
|
this.candidate = candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Value getValue() {
|
public Value getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Candidate getCandidate() {
|
public Candidate getCandidate() {
|
||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
|
@ -33,20 +33,20 @@ public class Match<Value, Candidate> {
|
||||||
Match<?, ?> other = (Match<?, ?>) obj;
|
Match<?, ?> other = (Match<?, ?>) obj;
|
||||||
return value == other.value && candidate == other.candidate;
|
return value == other.value && candidate == other.candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Arrays.hashCode(new Object[] { value, candidate });
|
return Arrays.hashCode(new Object[] { value, candidate });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("[%s, %s]", value, candidate);
|
return String.format("[%s, %s]", value, candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,20 @@ package net.filebot.similarity;
|
||||||
|
|
||||||
|
|
||||||
public class MetricMin implements SimilarityMetric {
|
public class MetricMin implements SimilarityMetric {
|
||||||
|
|
||||||
private final SimilarityMetric metric;
|
private final SimilarityMetric metric;
|
||||||
private final float minValue;
|
private final float minValue;
|
||||||
|
|
||||||
|
|
||||||
public MetricMin(SimilarityMetric metric, float minValue) {
|
public MetricMin(SimilarityMetric metric, float minValue) {
|
||||||
this.metric = metric;
|
this.metric = metric;
|
||||||
this.minValue = minValue;
|
this.minValue = minValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getSimilarity(Object o1, Object o2) {
|
public float getSimilarity(Object o1, Object o2) {
|
||||||
return Math.max(metric.getSimilarity(o1, o2), minValue);
|
return Math.max(metric.getSimilarity(o1, o2), minValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,43 +11,43 @@ import com.ibm.icu.text.Transliterator;
|
||||||
|
|
||||||
|
|
||||||
public class NameSimilarityMetric implements SimilarityMetric {
|
public class NameSimilarityMetric implements SimilarityMetric {
|
||||||
|
|
||||||
private final AbstractStringMetric metric;
|
private final AbstractStringMetric metric;
|
||||||
private final Transliterator transliterator;
|
private final Transliterator transliterator;
|
||||||
|
|
||||||
|
|
||||||
public NameSimilarityMetric() {
|
public NameSimilarityMetric() {
|
||||||
// QGramsDistance with a QGram tokenizer seems to work best for similarity of names
|
// QGramsDistance with a QGram tokenizer seems to work best for similarity of names
|
||||||
this(new QGramsDistance(new TokeniserQGram3()), Transliterator.getInstance("Any-Latin;Latin-ASCII;[:Diacritic:]remove"));
|
this(new QGramsDistance(new TokeniserQGram3()), Transliterator.getInstance("Any-Latin;Latin-ASCII;[:Diacritic:]remove"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public NameSimilarityMetric(AbstractStringMetric metric, Transliterator transliterator) {
|
public NameSimilarityMetric(AbstractStringMetric metric, Transliterator transliterator) {
|
||||||
this.metric = metric;
|
this.metric = metric;
|
||||||
this.transliterator = transliterator;
|
this.transliterator = transliterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getSimilarity(Object o1, Object o2) {
|
public float getSimilarity(Object o1, Object o2) {
|
||||||
return metric.getSimilarity(normalize(o1), normalize(o2));
|
return metric.getSimilarity(normalize(o1), normalize(o2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String normalize(Object object) {
|
protected String normalize(Object object) {
|
||||||
// use string representation
|
// use string representation
|
||||||
String name = object.toString();
|
String name = object.toString();
|
||||||
|
|
||||||
// apply transliterator
|
// apply transliterator
|
||||||
if (transliterator != null) {
|
if (transliterator != null) {
|
||||||
name = transliterator.transform(name);
|
name = transliterator.transform(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalize separators
|
// normalize separators
|
||||||
name = normalizePunctuation(name);
|
name = normalizePunctuation(name);
|
||||||
|
|
||||||
// normalize case and trim
|
// normalize case and trim
|
||||||
return name.toLowerCase();
|
return name.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,81 +15,81 @@ import uk.ac.shef.wit.simmetrics.wordhandlers.InterfaceTermHandler;
|
||||||
|
|
||||||
|
|
||||||
public class NumericSimilarityMetric implements SimilarityMetric {
|
public class NumericSimilarityMetric implements SimilarityMetric {
|
||||||
|
|
||||||
private final AbstractStringMetric metric;
|
private final AbstractStringMetric metric;
|
||||||
|
|
||||||
|
|
||||||
public NumericSimilarityMetric() {
|
public NumericSimilarityMetric() {
|
||||||
// I don't exactly know why, but I get a good matching behavior
|
// I don't exactly know why, but I get a good matching behavior
|
||||||
// when using QGramsDistance or BlockDistance
|
// when using QGramsDistance or BlockDistance
|
||||||
metric = new QGramsDistance(new NumberTokeniser());
|
metric = new QGramsDistance(new NumberTokeniser());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getSimilarity(Object o1, Object o2) {
|
public float getSimilarity(Object o1, Object o2) {
|
||||||
return metric.getSimilarity(normalize(o1), normalize(o2));
|
return metric.getSimilarity(normalize(o1), normalize(o2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String normalize(Object object) {
|
protected String normalize(Object object) {
|
||||||
// no need to do anything special here, because we don't care about anything but number patterns anyway
|
// no need to do anything special here, because we don't care about anything but number patterns anyway
|
||||||
return object.toString();
|
return object.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class NumberTokeniser implements InterfaceTokeniser {
|
private static class NumberTokeniser implements InterfaceTokeniser {
|
||||||
|
|
||||||
private final String delimiter = "\\D+";
|
private final String delimiter = "\\D+";
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ArrayList<String> tokenizeToArrayList(String input) {
|
public ArrayList<String> tokenizeToArrayList(String input) {
|
||||||
ArrayList<String> tokens = new ArrayList<String>();
|
ArrayList<String> tokens = new ArrayList<String>();
|
||||||
|
|
||||||
// scan for number patterns, use non-number pattern as delimiter
|
// scan for number patterns, use non-number pattern as delimiter
|
||||||
Scanner scanner = new Scanner(input).useDelimiter(delimiter);
|
Scanner scanner = new Scanner(input).useDelimiter(delimiter);
|
||||||
|
|
||||||
while (scanner.hasNextInt()) {
|
while (scanner.hasNextInt()) {
|
||||||
// remove leading zeros from number tokens by scanning for Integers
|
// remove leading zeros from number tokens by scanning for Integers
|
||||||
tokens.add(String.valueOf(scanner.nextInt()));
|
tokens.add(String.valueOf(scanner.nextInt()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> tokenizeToSet(String input) {
|
public Set<String> tokenizeToSet(String input) {
|
||||||
return new LinkedHashSet<String>(tokenizeToArrayList(input));
|
return new LinkedHashSet<String>(tokenizeToArrayList(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getShortDescriptionString() {
|
public String getShortDescriptionString() {
|
||||||
return getClass().getSimpleName();
|
return getClass().getSimpleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDelimiters() {
|
public String getDelimiters() {
|
||||||
return delimiter;
|
return delimiter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private InterfaceTermHandler stopWordHandler = new DummyStopTermHandler();
|
private InterfaceTermHandler stopWordHandler = new DummyStopTermHandler();
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InterfaceTermHandler getStopWordHandler() {
|
public InterfaceTermHandler getStopWordHandler() {
|
||||||
return stopWordHandler;
|
return stopWordHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setStopWordHandler(InterfaceTermHandler stopWordHandler) {
|
public void setStopWordHandler(InterfaceTermHandler stopWordHandler) {
|
||||||
this.stopWordHandler = stopWordHandler;
|
this.stopWordHandler = stopWordHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,7 @@ public class SeasonEpisodeMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to get season and episode numbers for the given string.
|
* Try to get season and episode numbers for the given string.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
* match this string against the a set of know patterns
|
* match this string against the a set of know patterns
|
||||||
* @return the matches returned by the first pattern that returns any matches for this string, or null if no pattern returned any matches
|
* @return the matches returned by the first pattern that returns any matches for this string, or null if no pattern returned any matches
|
||||||
|
|
|
@ -110,7 +110,7 @@ public class SeriesNameMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to match and verify all series names using known season episode patterns.
|
* Try to match and verify all series names using known season episode patterns.
|
||||||
*
|
*
|
||||||
* @param names
|
* @param names
|
||||||
* episode names
|
* episode names
|
||||||
* @return series names that have been matched one or multiple times depending on the threshold
|
* @return series names that have been matched one or multiple times depending on the threshold
|
||||||
|
@ -154,7 +154,7 @@ public class SeriesNameMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to match all common word sequences in the given list.
|
* Try to match all common word sequences in the given list.
|
||||||
*
|
*
|
||||||
* @param names
|
* @param names
|
||||||
* list of episode names
|
* list of episode names
|
||||||
* @return all common word sequences that have been found
|
* @return all common word sequences that have been found
|
||||||
|
@ -184,7 +184,7 @@ public class SeriesNameMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to match a series name from the given episode name using known season episode patterns.
|
* Try to match a series name from the given episode name using known season episode patterns.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
* episode name
|
* episode name
|
||||||
* @return a substring of the given name that ends before the first occurrence of a season episode pattern, or null if there is no such pattern
|
* @return a substring of the given name that ends before the first occurrence of a season episode pattern, or null if there is no such pattern
|
||||||
|
@ -218,7 +218,7 @@ public class SeriesNameMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to match a series name from the first common word sequence.
|
* Try to match a series name from the first common word sequence.
|
||||||
*
|
*
|
||||||
* @param names
|
* @param names
|
||||||
* various episode names (at least two)
|
* various episode names (at least two)
|
||||||
* @return a word sequence all episode names have in common, or null
|
* @return a word sequence all episode names have in common, or null
|
||||||
|
|
|
@ -6,34 +6,34 @@ import java.util.Comparator;
|
||||||
|
|
||||||
|
|
||||||
public class SimilarityComparator implements Comparator<Object> {
|
public class SimilarityComparator implements Comparator<Object> {
|
||||||
|
|
||||||
protected SimilarityMetric metric;
|
protected SimilarityMetric metric;
|
||||||
protected Object[] paragon;
|
protected Object[] paragon;
|
||||||
|
|
||||||
|
|
||||||
public SimilarityComparator(SimilarityMetric metric, Object[] paragon) {
|
public SimilarityComparator(SimilarityMetric metric, Object[] paragon) {
|
||||||
this.metric = metric;
|
this.metric = metric;
|
||||||
this.paragon = paragon;
|
this.paragon = paragon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SimilarityComparator(Object... paragon) {
|
public SimilarityComparator(Object... paragon) {
|
||||||
this(new NameSimilarityMetric(), paragon);
|
this(new NameSimilarityMetric(), paragon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(Object o1, Object o2) {
|
public int compare(Object o1, Object o2) {
|
||||||
float f1 = getMaxSimilarity(o1);
|
float f1 = getMaxSimilarity(o1);
|
||||||
float f2 = getMaxSimilarity(o2);
|
float f2 = getMaxSimilarity(o2);
|
||||||
|
|
||||||
if (f1 == f2)
|
if (f1 == f2)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return f1 > f2 ? -1 : 1;
|
return f1 > f2 ? -1 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float getMaxSimilarity(Object obj) {
|
public float getMaxSimilarity(Object obj) {
|
||||||
float m = 0;
|
float m = 0;
|
||||||
for (Object it : paragon) {
|
for (Object it : paragon) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package net.filebot.similarity;
|
||||||
|
|
||||||
|
|
||||||
public interface SimilarityMetric {
|
public interface SimilarityMetric {
|
||||||
|
|
||||||
public float getSimilarity(Object o1, Object o2);
|
public float getSimilarity(Object o1, Object o2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,22 +10,22 @@ import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
|
||||||
|
|
||||||
public class TimeStampMetric implements SimilarityMetric {
|
public class TimeStampMetric implements SimilarityMetric {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getSimilarity(Object o1, Object o2) {
|
public float getSimilarity(Object o1, Object o2) {
|
||||||
long t1 = getTimeStamp(o1);
|
long t1 = getTimeStamp(o1);
|
||||||
long t2 = getTimeStamp(o2);
|
long t2 = getTimeStamp(o2);
|
||||||
|
|
||||||
if (t1 <= 0 || t2 <= 0)
|
if (t1 <= 0 || t2 <= 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
float min = min(t1, t2);
|
float min = min(t1, t2);
|
||||||
float max = max(t1, t2);
|
float max = max(t1, t2);
|
||||||
|
|
||||||
return min / max;
|
return min / max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public long getTimeStamp(Object obj) {
|
public long getTimeStamp(Object obj) {
|
||||||
if (obj instanceof File) {
|
if (obj instanceof File) {
|
||||||
try {
|
try {
|
||||||
|
@ -43,7 +43,7 @@ public class TimeStampMetric implements SimilarityMetric {
|
||||||
} else if (obj instanceof Number) {
|
} else if (obj instanceof Number) {
|
||||||
return ((Number) obj).longValue();
|
return ((Number) obj).longValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,61 +9,61 @@ import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
public class MicroDVDReader extends SubtitleReader {
|
public class MicroDVDReader extends SubtitleReader {
|
||||||
|
|
||||||
private double fps = 23.976;
|
private double fps = 23.976;
|
||||||
|
|
||||||
|
|
||||||
public MicroDVDReader(Readable source) {
|
public MicroDVDReader(Readable source) {
|
||||||
super(source);
|
super(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleElement readNext() throws Exception {
|
public SubtitleElement readNext() throws Exception {
|
||||||
String line = scanner.nextLine();
|
String line = scanner.nextLine();
|
||||||
|
|
||||||
List<String> properties = new ArrayList<String>(2);
|
List<String> properties = new ArrayList<String>(2);
|
||||||
int from = 0;
|
int from = 0;
|
||||||
|
|
||||||
while (from < line.length() && line.charAt(from) == '{') {
|
while (from < line.length() && line.charAt(from) == '{') {
|
||||||
int to = line.indexOf('}', from + 1);
|
int to = line.indexOf('}', from + 1);
|
||||||
|
|
||||||
// no more properties
|
// no more properties
|
||||||
if (to < from)
|
if (to < from)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// extract property
|
// extract property
|
||||||
properties.add(line.substring(from + 1, to));
|
properties.add(line.substring(from + 1, to));
|
||||||
|
|
||||||
// skip property
|
// skip property
|
||||||
from = to + 1;
|
from = to + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.size() < 2) {
|
if (properties.size() < 2) {
|
||||||
// ignore illegal lines
|
// ignore illegal lines
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int startFrame = Integer.parseInt(properties.get(0));
|
int startFrame = Integer.parseInt(properties.get(0));
|
||||||
int endFrame = Integer.parseInt(properties.get(1));
|
int endFrame = Integer.parseInt(properties.get(1));
|
||||||
String text = line.substring(from).trim();
|
String text = line.substring(from).trim();
|
||||||
|
|
||||||
// cancel format markers
|
// cancel format markers
|
||||||
text = text.replaceAll("\\{[^\\}]*\\}", "");
|
text = text.replaceAll("\\{[^\\}]*\\}", "");
|
||||||
|
|
||||||
if (startFrame == 1 && endFrame == 1) {
|
if (startFrame == 1 && endFrame == 1) {
|
||||||
// override fps
|
// override fps
|
||||||
fps = Double.parseDouble(text);
|
fps = Double.parseDouble(text);
|
||||||
|
|
||||||
// ignore line
|
// ignore line
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// translate '|' to new lines
|
// translate '|' to new lines
|
||||||
String[] lines = text.split("[|]");
|
String[] lines = text.split("[|]");
|
||||||
|
|
||||||
// convert frame interval to time interval
|
// convert frame interval to time interval
|
||||||
return new SubtitleElement(Math.round(startFrame * fps), Math.round(endFrame * fps), join(lines, "\n"));
|
return new SubtitleElement(Math.round(startFrame * fps), Math.round(endFrame * fps), join(lines, "\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,21 +12,21 @@ import java.util.TimeZone;
|
||||||
|
|
||||||
|
|
||||||
public class SubRipWriter implements Closeable {
|
public class SubRipWriter implements Closeable {
|
||||||
|
|
||||||
private final DateFormat timeFormat;
|
private final DateFormat timeFormat;
|
||||||
private final Formatter out;
|
private final Formatter out;
|
||||||
|
|
||||||
private int lineNumber = 0;
|
private int lineNumber = 0;
|
||||||
|
|
||||||
|
|
||||||
public SubRipWriter(Appendable out) {
|
public SubRipWriter(Appendable out) {
|
||||||
this.out = new Formatter(out, Locale.ROOT);
|
this.out = new Formatter(out, Locale.ROOT);
|
||||||
|
|
||||||
// format used to create time stamps (e.g. 00:02:26,407 --> 00:02:31,356)
|
// format used to create time stamps (e.g. 00:02:26,407 --> 00:02:31,356)
|
||||||
timeFormat = new SimpleDateFormat("HH:mm:ss,SSS", Locale.ROOT);
|
timeFormat = new SimpleDateFormat("HH:mm:ss,SSS", Locale.ROOT);
|
||||||
timeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
timeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void write(SubtitleElement element) {
|
public void write(SubtitleElement element) {
|
||||||
// write a single subtitle in SubRip format, e.g.
|
// write a single subtitle in SubRip format, e.g.
|
||||||
|
@ -37,11 +37,11 @@ public class SubRipWriter implements Closeable {
|
||||||
out.format("%s --> %s%n", timeFormat.format(element.getStart()), timeFormat.format(element.getEnd()));
|
out.format("%s --> %s%n", timeFormat.format(element.getStart()), timeFormat.format(element.getEnd()));
|
||||||
out.format("%s%n%n", element.getText());
|
out.format("%s%n%n", element.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,87 +10,87 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
public class SubStationAlphaReader extends SubtitleReader {
|
public class SubStationAlphaReader extends SubtitleReader {
|
||||||
|
|
||||||
private final DateFormat timeFormat = new SubtitleTimeFormat();
|
private final DateFormat timeFormat = new SubtitleTimeFormat();
|
||||||
private final Pattern newline = Pattern.compile(Pattern.quote("\\n"), Pattern.CASE_INSENSITIVE);
|
private final Pattern newline = Pattern.compile(Pattern.quote("\\n"), Pattern.CASE_INSENSITIVE);
|
||||||
private final Pattern tag = Pattern.compile("[{]\\\\[^}]+[}]");
|
private final Pattern tag = Pattern.compile("[{]\\\\[^}]+[}]");
|
||||||
|
|
||||||
private String[] format;
|
private String[] format;
|
||||||
private int formatIndexStart;
|
private int formatIndexStart;
|
||||||
private int formatIndexEnd;
|
private int formatIndexEnd;
|
||||||
private int formatIndexText;
|
private int formatIndexText;
|
||||||
|
|
||||||
|
|
||||||
public SubStationAlphaReader(Readable source) {
|
public SubStationAlphaReader(Readable source) {
|
||||||
super(source);
|
super(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void readFormat() throws Exception {
|
private void readFormat() throws Exception {
|
||||||
// read format line (e.g. Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text)
|
// read format line (e.g. Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text)
|
||||||
String[] event = scanner.nextLine().split(":", 2);
|
String[] event = scanner.nextLine().split(":", 2);
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (!event[0].equals("Format"))
|
if (!event[0].equals("Format"))
|
||||||
throw new InputMismatchException("Illegal format header: " + Arrays.toString(event));
|
throw new InputMismatchException("Illegal format header: " + Arrays.toString(event));
|
||||||
|
|
||||||
// read columns
|
// read columns
|
||||||
format = event[1].split(",");
|
format = event[1].split(",");
|
||||||
|
|
||||||
// normalize column names
|
// normalize column names
|
||||||
for (int i = 0; i < format.length; i++) {
|
for (int i = 0; i < format.length; i++) {
|
||||||
format[i] = format[i].trim().toLowerCase();
|
format[i] = format[i].trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> lookup = Arrays.asList(format);
|
List<String> lookup = Arrays.asList(format);
|
||||||
formatIndexStart = lookup.indexOf("start");
|
formatIndexStart = lookup.indexOf("start");
|
||||||
formatIndexEnd = lookup.indexOf("end");
|
formatIndexEnd = lookup.indexOf("end");
|
||||||
formatIndexText = lookup.indexOf("text");
|
formatIndexText = lookup.indexOf("text");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleElement readNext() throws Exception {
|
public SubtitleElement readNext() throws Exception {
|
||||||
if (format == null) {
|
if (format == null) {
|
||||||
// move to [Events] sections
|
// move to [Events] sections
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
|
|
||||||
while (!found && scanner.hasNextLine()) {
|
while (!found && scanner.hasNextLine()) {
|
||||||
found = scanner.nextLine().equals("[Events]");
|
found = scanner.nextLine().equals("[Events]");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
throw new InputMismatchException("Cannot find [Events] section");
|
throw new InputMismatchException("Cannot find [Events] section");
|
||||||
}
|
}
|
||||||
|
|
||||||
// read format header
|
// read format header
|
||||||
readFormat();
|
readFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
// read next dialogue line
|
// read next dialogue line
|
||||||
String[] event = scanner.nextLine().split(":", 2);
|
String[] event = scanner.nextLine().split(":", 2);
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (!event[0].equals("Dialogue"))
|
if (!event[0].equals("Dialogue"))
|
||||||
throw new InputMismatchException("Illegal dialogue event: " + Arrays.toString(event));
|
throw new InputMismatchException("Illegal dialogue event: " + Arrays.toString(event));
|
||||||
|
|
||||||
// extract information
|
// extract information
|
||||||
String[] values = event[1].split(",", format.length);
|
String[] values = event[1].split(",", format.length);
|
||||||
|
|
||||||
long start = timeFormat.parse(values[formatIndexStart].trim()).getTime();
|
long start = timeFormat.parse(values[formatIndexStart].trim()).getTime();
|
||||||
long end = timeFormat.parse(values[formatIndexEnd].trim()).getTime();
|
long end = timeFormat.parse(values[formatIndexEnd].trim()).getTime();
|
||||||
String text = values[formatIndexText].trim();
|
String text = values[formatIndexText].trim();
|
||||||
|
|
||||||
return new SubtitleElement(start, end, resolve(text));
|
return new SubtitleElement(start, end, resolve(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String resolve(String text) {
|
protected String resolve(String text) {
|
||||||
// remove tags
|
// remove tags
|
||||||
text = tag.matcher(text).replaceAll("");
|
text = tag.matcher(text).replaceAll("");
|
||||||
|
|
||||||
// resolve line breaks
|
// resolve line breaks
|
||||||
return newline.matcher(text).replaceAll("\n");
|
return newline.matcher(text).replaceAll("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,33 +12,33 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
public class SubViewerReader extends SubtitleReader {
|
public class SubViewerReader extends SubtitleReader {
|
||||||
|
|
||||||
private final DateFormat timeFormat = new SubtitleTimeFormat();
|
private final DateFormat timeFormat = new SubtitleTimeFormat();
|
||||||
private final Pattern newline = compile(quote("[br]"), CASE_INSENSITIVE);
|
private final Pattern newline = compile(quote("[br]"), CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
|
||||||
public SubViewerReader(Readable source) {
|
public SubViewerReader(Readable source) {
|
||||||
super(source);
|
super(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SubtitleElement readNext() throws Exception {
|
protected SubtitleElement readNext() throws Exception {
|
||||||
// element starts with interval (e.g. 00:42:16.33,00:42:19.39)
|
// element starts with interval (e.g. 00:42:16.33,00:42:19.39)
|
||||||
String[] interval = scanner.nextLine().split(",", 2);
|
String[] interval = scanner.nextLine().split(",", 2);
|
||||||
|
|
||||||
if (interval.length < 2 || interval[0].startsWith("[")) {
|
if (interval.length < 2 || interval[0].startsWith("[")) {
|
||||||
// ignore property lines
|
// ignore property lines
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long t1 = timeFormat.parse(interval[0]).getTime();
|
long t1 = timeFormat.parse(interval[0]).getTime();
|
||||||
long t2 = timeFormat.parse(interval[1]).getTime();
|
long t2 = timeFormat.parse(interval[1]).getTime();
|
||||||
|
|
||||||
// translate [br] to new lines
|
// translate [br] to new lines
|
||||||
String[] lines = newline.split(scanner.nextLine());
|
String[] lines = newline.split(scanner.nextLine());
|
||||||
|
|
||||||
return new SubtitleElement(t1, t2, join(lines, "\n"));
|
return new SubtitleElement(t1, t2, join(lines, "\n"));
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
// can't parse interval, ignore line
|
// can't parse interval, ignore line
|
||||||
|
|
|
@ -3,38 +3,38 @@ package net.filebot.subtitle;
|
||||||
|
|
||||||
|
|
||||||
public class SubtitleElement {
|
public class SubtitleElement {
|
||||||
|
|
||||||
private final long start;
|
private final long start;
|
||||||
private final long end;
|
private final long end;
|
||||||
|
|
||||||
private final String text;
|
private final String text;
|
||||||
|
|
||||||
|
|
||||||
public SubtitleElement(long start, long end, String text) {
|
public SubtitleElement(long start, long end, String text) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
this.text = text;
|
this.text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public long getStart() {
|
public long getStart() {
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public long getEnd() {
|
public long getEnd() {
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getText() {
|
public String getText() {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("[%d, %d] %s", start, end, text);
|
return String.format("[%d, %d] %s", start, end, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,44 +7,44 @@ import net.filebot.util.FileUtilities.ExtensionFileFilter;
|
||||||
|
|
||||||
|
|
||||||
public enum SubtitleFormat {
|
public enum SubtitleFormat {
|
||||||
|
|
||||||
SubRip {
|
SubRip {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleReader newReader(Readable readable) {
|
public SubtitleReader newReader(Readable readable) {
|
||||||
return new SubRipReader(readable);
|
return new SubRipReader(readable);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
MicroDVD {
|
MicroDVD {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleReader newReader(Readable readable) {
|
public SubtitleReader newReader(Readable readable) {
|
||||||
return new MicroDVDReader(readable);
|
return new MicroDVDReader(readable);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
SubViewer {
|
SubViewer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleReader newReader(Readable readable) {
|
public SubtitleReader newReader(Readable readable) {
|
||||||
return new SubViewerReader(readable);
|
return new SubViewerReader(readable);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
SubStationAlpha {
|
SubStationAlpha {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleReader newReader(Readable readable) {
|
public SubtitleReader newReader(Readable readable) {
|
||||||
return new SubStationAlphaReader(readable);
|
return new SubStationAlphaReader(readable);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public abstract SubtitleReader newReader(Readable readable);
|
public abstract SubtitleReader newReader(Readable readable);
|
||||||
|
|
||||||
|
|
||||||
public ExtensionFileFilter getFilter() {
|
public ExtensionFileFilter getFilter() {
|
||||||
return MediaTypes.getDefaultFilter("subtitle/" + this.name());
|
return MediaTypes.getDefaultFilter("subtitle/" + this.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,7 @@ public enum SubtitleMetrics implements SimilarityMetric {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected float similarity(String match, String s1, String s2) {
|
protected float similarity(String match, String s1, String s2) {
|
||||||
return match.length() > 0 ? 1 : 0;
|
return match.length() > 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
@ -153,10 +154,12 @@ public enum SubtitleMetrics implements SimilarityMetric {
|
||||||
private final String FPS = "FPS";
|
private final String FPS = "FPS";
|
||||||
private final String SECONDS = "SECS";
|
private final String SECONDS = "SECS";
|
||||||
|
|
||||||
|
@Override
|
||||||
public float getSimilarity(Object o1, Object o2) {
|
public float getSimilarity(Object o1, Object o2) {
|
||||||
return o1 instanceof SubtitleDescriptor ? super.getSimilarity(o1, o2) : super.getSimilarity(o2, o1); // make sure that SubtitleDescriptor is o1
|
return o1 instanceof SubtitleDescriptor ? super.getSimilarity(o1, o2) : super.getSimilarity(o2, o1); // make sure that SubtitleDescriptor is o1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
protected Map<String, Object> getProperties(Object object) {
|
protected Map<String, Object> getProperties(Object object) {
|
||||||
if (object instanceof OpenSubtitlesSubtitleDescriptor) {
|
if (object instanceof OpenSubtitlesSubtitleDescriptor) {
|
||||||
return getSubtitleProperties((OpenSubtitlesSubtitleDescriptor) object);
|
return getSubtitleProperties((OpenSubtitlesSubtitleDescriptor) object);
|
||||||
|
|
|
@ -12,18 +12,18 @@ import java.util.logging.Logger;
|
||||||
|
|
||||||
|
|
||||||
public abstract class SubtitleReader implements Iterator<SubtitleElement>, Closeable {
|
public abstract class SubtitleReader implements Iterator<SubtitleElement>, Closeable {
|
||||||
|
|
||||||
protected final Scanner scanner;
|
protected final Scanner scanner;
|
||||||
protected SubtitleElement current;
|
protected SubtitleElement current;
|
||||||
|
|
||||||
|
|
||||||
public SubtitleReader(Readable source) {
|
public SubtitleReader(Readable source) {
|
||||||
this.scanner = new Scanner(source);
|
this.scanner = new Scanner(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected abstract SubtitleElement readNext() throws Exception;
|
protected abstract SubtitleElement readNext() throws Exception;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
|
@ -36,34 +36,34 @@ public abstract class SubtitleReader implements Iterator<SubtitleElement>, Close
|
||||||
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Illegal input: " + e.getMessage());
|
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Illegal input: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return current != null;
|
return current != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleElement next() {
|
public SubtitleElement next() {
|
||||||
if (!hasNext()) {
|
if (!hasNext()) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return current;
|
return current;
|
||||||
} finally {
|
} finally {
|
||||||
current = null;
|
current = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
scanner.close();
|
scanner.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove() {
|
public void remove() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,45 +13,45 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
class SubtitleTimeFormat extends DateFormat {
|
class SubtitleTimeFormat extends DateFormat {
|
||||||
|
|
||||||
public SubtitleTimeFormat() {
|
public SubtitleTimeFormat() {
|
||||||
// calendar without any kind of special handling for time zone and daylight saving time
|
// calendar without any kind of special handling for time zone and daylight saving time
|
||||||
calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ROOT);
|
calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ROOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StringBuffer format(Date date, StringBuffer sb, FieldPosition pos) {
|
public StringBuffer format(Date date, StringBuffer sb, FieldPosition pos) {
|
||||||
// e.g. 1:42:52.42
|
// e.g. 1:42:52.42
|
||||||
calendar.setTime(date);
|
calendar.setTime(date);
|
||||||
|
|
||||||
sb.append(String.format("%02d", calendar.get(Calendar.HOUR_OF_DAY)));
|
sb.append(String.format("%02d", calendar.get(Calendar.HOUR_OF_DAY)));
|
||||||
sb.append(':').append(String.format("%02d", calendar.get(Calendar.MINUTE)));
|
sb.append(':').append(String.format("%02d", calendar.get(Calendar.MINUTE)));
|
||||||
sb.append(':').append(String.format("%02d", calendar.get(Calendar.SECOND)));
|
sb.append(':').append(String.format("%02d", calendar.get(Calendar.SECOND)));
|
||||||
|
|
||||||
String millis = String.format("%03d", calendar.get(Calendar.MILLISECOND));
|
String millis = String.format("%03d", calendar.get(Calendar.MILLISECOND));
|
||||||
sb.append('.').append(millis.substring(0, 2));
|
sb.append('.').append(millis.substring(0, 2));
|
||||||
|
|
||||||
return sb;
|
return sb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private final Pattern delimiter = Pattern.compile("[:.]");
|
private final Pattern delimiter = Pattern.compile("[:.]");
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date parse(String source, ParsePosition pos) {
|
public Date parse(String source, ParsePosition pos) {
|
||||||
String[] split = delimiter.split(source, 4);
|
String[] split = delimiter.split(source, 4);
|
||||||
|
|
||||||
// reset state
|
// reset state
|
||||||
calendar.clear();
|
calendar.clear();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// handle hours:minutes:seconds
|
// handle hours:minutes:seconds
|
||||||
calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(split[0]));
|
calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(split[0]));
|
||||||
calendar.set(Calendar.MINUTE, Integer.parseInt(split[1]));
|
calendar.set(Calendar.MINUTE, Integer.parseInt(split[1]));
|
||||||
calendar.set(Calendar.SECOND, Integer.parseInt(split[2]));
|
calendar.set(Calendar.SECOND, Integer.parseInt(split[2]));
|
||||||
|
|
||||||
// handle hundredth seconds
|
// handle hundredth seconds
|
||||||
calendar.set(Calendar.MILLISECOND, Integer.parseInt(split[3]) * 10);
|
calendar.set(Calendar.MILLISECOND, Integer.parseInt(split[3]) * 10);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -59,7 +59,7 @@ class SubtitleTimeFormat extends DateFormat {
|
||||||
pos.setErrorIndex(0);
|
pos.setErrorIndex(0);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update position
|
// update position
|
||||||
pos.setIndex(source.length());
|
pos.setIndex(source.length());
|
||||||
return calendar.getTime();
|
return calendar.getTime();
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/*
|
/*
|
||||||
* BeDecoder.java
|
* BeDecoder.java
|
||||||
*
|
*
|
||||||
* Created on May 30, 2003, 2:44 PM
|
* Created on May 30, 2003, 2:44 PM
|
||||||
* Copyright (C) 2003, 2004, 2005, 2006 Aelitis, All Rights Reserved.
|
* Copyright (C) 2003, 2004, 2005, 2006 Aelitis, All Rights Reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
* as published by the Free Software Foundation; either version 2
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*
|
*
|
||||||
* AELITIS, SAS au capital de 46,603.30 euros
|
* AELITIS, SAS au capital de 46,603.30 euros
|
||||||
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
|
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
|
||||||
*/
|
*/
|
||||||
|
@ -37,190 +37,190 @@ import java.util.Map;
|
||||||
/**
|
/**
|
||||||
* A set of utility methods to decode a bencoded array of byte into a Map. integer are
|
* A set of utility methods to decode a bencoded array of byte into a Map. integer are
|
||||||
* represented as Long, String as byte[], dictionnaries as Map, and list as List.
|
* represented as Long, String as byte[], dictionnaries as Map, and list as List.
|
||||||
*
|
*
|
||||||
* @author TdC_VgA
|
* @author TdC_VgA
|
||||||
*/
|
*/
|
||||||
class BDecoder {
|
class BDecoder {
|
||||||
|
|
||||||
private static final Charset BINARY_CHARSET = Charset.forName("ISO-8859-1");
|
private static final Charset BINARY_CHARSET = Charset.forName("ISO-8859-1");
|
||||||
|
|
||||||
|
|
||||||
public static Map<?, ?> decode(InputStream is) throws IOException {
|
public static Map<?, ?> decode(InputStream is) throws IOException {
|
||||||
return (new BDecoder().decodeStream(is));
|
return (new BDecoder().decodeStream(is));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Map<?, ?> decodeStream(InputStream data) throws IOException {
|
public Map<?, ?> decodeStream(InputStream data) throws IOException {
|
||||||
Object res = decodeInputStream(data, 0);
|
Object res = decodeInputStream(data, 0);
|
||||||
|
|
||||||
if (res == null)
|
if (res == null)
|
||||||
throw (new IOException("BDecoder: zero length file"));
|
throw (new IOException("BDecoder: zero length file"));
|
||||||
else if (!(res instanceof Map))
|
else if (!(res instanceof Map))
|
||||||
throw (new IOException("BDecoder: top level isn't a Map"));
|
throw (new IOException("BDecoder: top level isn't a Map"));
|
||||||
|
|
||||||
return ((Map<?, ?>) res);
|
return ((Map<?, ?>) res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Object decodeInputStream(InputStream bais, int nesting) throws IOException {
|
private Object decodeInputStream(InputStream bais, int nesting) throws IOException {
|
||||||
if (!bais.markSupported())
|
if (!bais.markSupported())
|
||||||
throw new IOException("InputStream must support the mark() method");
|
throw new IOException("InputStream must support the mark() method");
|
||||||
|
|
||||||
// set a mark
|
// set a mark
|
||||||
bais.mark(Integer.MAX_VALUE);
|
bais.mark(Integer.MAX_VALUE);
|
||||||
|
|
||||||
// read a byte
|
// read a byte
|
||||||
int tempByte = bais.read();
|
int tempByte = bais.read();
|
||||||
|
|
||||||
// decide what to do
|
// decide what to do
|
||||||
switch (tempByte) {
|
switch (tempByte) {
|
||||||
case 'd':
|
case 'd':
|
||||||
// create a new dictionary object
|
// create a new dictionary object
|
||||||
Map<String, Object> tempMap = new HashMap<String, Object>();
|
Map<String, Object> tempMap = new HashMap<String, Object>();
|
||||||
|
|
||||||
// get the key
|
// get the key
|
||||||
byte[] tempByteArray = null;
|
byte[] tempByteArray = null;
|
||||||
|
|
||||||
while ((tempByteArray = (byte[]) decodeInputStream(bais, nesting + 1)) != null) {
|
while ((tempByteArray = (byte[]) decodeInputStream(bais, nesting + 1)) != null) {
|
||||||
|
|
||||||
// decode some more
|
// decode some more
|
||||||
|
|
||||||
Object value = decodeInputStream(bais, nesting + 1);
|
Object value = decodeInputStream(bais, nesting + 1);
|
||||||
|
|
||||||
// add the value to the map
|
// add the value to the map
|
||||||
|
|
||||||
CharBuffer cb = BINARY_CHARSET.decode(ByteBuffer.wrap(tempByteArray));
|
CharBuffer cb = BINARY_CHARSET.decode(ByteBuffer.wrap(tempByteArray));
|
||||||
|
|
||||||
String key = new String(cb.array(), 0, cb.limit());
|
String key = new String(cb.array(), 0, cb.limit());
|
||||||
|
|
||||||
tempMap.put(key, value);
|
tempMap.put(key, value);
|
||||||
}
|
|
||||||
|
|
||||||
if (bais.available() < nesting)
|
|
||||||
throw (new IOException("BDecoder: invalid input data, 'e' missing from end of dictionary"));
|
|
||||||
|
|
||||||
// return the map
|
|
||||||
return tempMap;
|
|
||||||
|
|
||||||
case 'l':
|
|
||||||
// create the list
|
|
||||||
List<Object> tempList = new ArrayList<Object>();
|
|
||||||
|
|
||||||
// create the key
|
|
||||||
Object tempElement = null;
|
|
||||||
while ((tempElement = decodeInputStream(bais, nesting + 1)) != null)
|
|
||||||
// add the element
|
|
||||||
tempList.add(tempElement);
|
|
||||||
|
|
||||||
if (bais.available() < nesting)
|
|
||||||
throw (new IOException("BDecoder: invalid input data, 'e' missing from end of list"));
|
|
||||||
|
|
||||||
// return the list
|
|
||||||
return tempList;
|
|
||||||
|
|
||||||
case 'e':
|
|
||||||
case -1:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case 'i':
|
|
||||||
return new Long(getNumberFromStream(bais, 'e'));
|
|
||||||
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
// move back one
|
|
||||||
bais.reset();
|
|
||||||
// get the string
|
|
||||||
return getByteArrayFromStream(bais);
|
|
||||||
|
|
||||||
default: {
|
|
||||||
|
|
||||||
int rem_len = bais.available();
|
|
||||||
|
|
||||||
if (rem_len > 256)
|
|
||||||
rem_len = 256;
|
|
||||||
|
|
||||||
byte[] rem_data = new byte[rem_len];
|
|
||||||
|
|
||||||
bais.read(rem_data);
|
|
||||||
|
|
||||||
throw (new IOException("BDecoder: unknown command '" + tempByte + ", remainder = " + new String(rem_data)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bais.available() < nesting)
|
||||||
|
throw (new IOException("BDecoder: invalid input data, 'e' missing from end of dictionary"));
|
||||||
|
|
||||||
|
// return the map
|
||||||
|
return tempMap;
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
// create the list
|
||||||
|
List<Object> tempList = new ArrayList<Object>();
|
||||||
|
|
||||||
|
// create the key
|
||||||
|
Object tempElement = null;
|
||||||
|
while ((tempElement = decodeInputStream(bais, nesting + 1)) != null)
|
||||||
|
// add the element
|
||||||
|
tempList.add(tempElement);
|
||||||
|
|
||||||
|
if (bais.available() < nesting)
|
||||||
|
throw (new IOException("BDecoder: invalid input data, 'e' missing from end of list"));
|
||||||
|
|
||||||
|
// return the list
|
||||||
|
return tempList;
|
||||||
|
|
||||||
|
case 'e':
|
||||||
|
case -1:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case 'i':
|
||||||
|
return new Long(getNumberFromStream(bais, 'e'));
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
// move back one
|
||||||
|
bais.reset();
|
||||||
|
// get the string
|
||||||
|
return getByteArrayFromStream(bais);
|
||||||
|
|
||||||
|
default: {
|
||||||
|
|
||||||
|
int rem_len = bais.available();
|
||||||
|
|
||||||
|
if (rem_len > 256)
|
||||||
|
rem_len = 256;
|
||||||
|
|
||||||
|
byte[] rem_data = new byte[rem_len];
|
||||||
|
|
||||||
|
bais.read(rem_data);
|
||||||
|
|
||||||
|
throw (new IOException("BDecoder: unknown command '" + tempByte + ", remainder = " + new String(rem_data)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private long getNumberFromStream(InputStream bais, char parseChar) throws IOException {
|
private long getNumberFromStream(InputStream bais, char parseChar) throws IOException {
|
||||||
int length = 0;
|
int length = 0;
|
||||||
|
|
||||||
// place a mark
|
// place a mark
|
||||||
bais.mark(Integer.MAX_VALUE);
|
bais.mark(Integer.MAX_VALUE);
|
||||||
|
|
||||||
int tempByte = bais.read();
|
int tempByte = bais.read();
|
||||||
while ((tempByte != parseChar) && (tempByte >= 0)) {
|
while ((tempByte != parseChar) && (tempByte >= 0)) {
|
||||||
tempByte = bais.read();
|
tempByte = bais.read();
|
||||||
length++;
|
length++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// are we at the end of the stream?
|
// are we at the end of the stream?
|
||||||
if (tempByte < 0)
|
if (tempByte < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// reset the mark
|
// reset the mark
|
||||||
bais.reset();
|
bais.reset();
|
||||||
|
|
||||||
// get the length
|
// get the length
|
||||||
byte[] tempArray = new byte[length];
|
byte[] tempArray = new byte[length];
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
|
|
||||||
// get the string
|
// get the string
|
||||||
while ((count != length) && ((len = bais.read(tempArray, count, length - count)) > 0))
|
while ((count != length) && ((len = bais.read(tempArray, count, length - count)) > 0))
|
||||||
count += len;
|
count += len;
|
||||||
|
|
||||||
// jump ahead in the stream to compensate for the :
|
// jump ahead in the stream to compensate for the :
|
||||||
bais.skip(1);
|
bais.skip(1);
|
||||||
|
|
||||||
// return the value
|
// return the value
|
||||||
CharBuffer cb = BINARY_CHARSET.decode(ByteBuffer.wrap(tempArray));
|
CharBuffer cb = BINARY_CHARSET.decode(ByteBuffer.wrap(tempArray));
|
||||||
|
|
||||||
String str_value = new String(cb.array(), 0, cb.limit());
|
String str_value = new String(cb.array(), 0, cb.limit());
|
||||||
|
|
||||||
return Long.parseLong(str_value);
|
return Long.parseLong(str_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private byte[] getByteArrayFromStream(InputStream bais) throws IOException {
|
private byte[] getByteArrayFromStream(InputStream bais) throws IOException {
|
||||||
int length = (int) getNumberFromStream(bais, ':');
|
int length = (int) getNumberFromStream(bais, ':');
|
||||||
|
|
||||||
if (length < 0)
|
if (length < 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// note that torrent hashes can be big (consider a 55GB file with 2MB
|
// note that torrent hashes can be big (consider a 55GB file with 2MB
|
||||||
// pieces
|
// pieces
|
||||||
// this generates a pieces hash of 1/2 meg
|
// this generates a pieces hash of 1/2 meg
|
||||||
if (length > 8 * 1024 * 1024)
|
if (length > 8 * 1024 * 1024)
|
||||||
throw (new IOException("Byte array length too large (" + length + ")"));
|
throw (new IOException("Byte array length too large (" + length + ")"));
|
||||||
|
|
||||||
byte[] tempArray = new byte[length];
|
byte[] tempArray = new byte[length];
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
|
|
||||||
// get the string
|
// get the string
|
||||||
while ((count != length) && ((len = bais.read(tempArray, count, length - count)) > 0))
|
while ((count != length) && ((len = bais.read(tempArray, count, length - count)) > 0))
|
||||||
count += len;
|
count += len;
|
||||||
|
|
||||||
if (count != tempArray.length)
|
if (count != tempArray.length)
|
||||||
throw (new IOException("BDecoder::getByteArrayFromStream: truncated"));
|
throw (new IOException("BDecoder::getByteArrayFromStream: truncated"));
|
||||||
|
|
||||||
return tempArray;
|
return tempArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
public class Torrent {
|
public class Torrent {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private String encoding;
|
private String encoding;
|
||||||
private String createdBy;
|
private String createdBy;
|
||||||
|
@ -24,188 +24,188 @@ public class Torrent {
|
||||||
private String comment;
|
private String comment;
|
||||||
private Long creationDate;
|
private Long creationDate;
|
||||||
private Long pieceLength;
|
private Long pieceLength;
|
||||||
|
|
||||||
private List<Entry> files;
|
private List<Entry> files;
|
||||||
private boolean singleFileTorrent;
|
private boolean singleFileTorrent;
|
||||||
|
|
||||||
|
|
||||||
protected Torrent() {
|
protected Torrent() {
|
||||||
// used by serializer
|
// used by serializer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Torrent(File torrent) throws IOException {
|
public Torrent(File torrent) throws IOException {
|
||||||
this(decodeTorrent(torrent));
|
this(decodeTorrent(torrent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Torrent(Map<?, ?> torrentMap) {
|
public Torrent(Map<?, ?> torrentMap) {
|
||||||
Charset charset = Charset.forName("UTF-8");
|
Charset charset = Charset.forName("UTF-8");
|
||||||
encoding = decodeString(torrentMap.get("encoding"), charset);
|
encoding = decodeString(torrentMap.get("encoding"), charset);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
charset = Charset.forName(encoding);
|
charset = Charset.forName(encoding);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// invalid encoding, just keep using UTF-8
|
// invalid encoding, just keep using UTF-8
|
||||||
}
|
}
|
||||||
|
|
||||||
createdBy = decodeString(torrentMap.get("created by"), charset);
|
createdBy = decodeString(torrentMap.get("created by"), charset);
|
||||||
announce = decodeString(torrentMap.get("announce"), charset);
|
announce = decodeString(torrentMap.get("announce"), charset);
|
||||||
comment = decodeString(torrentMap.get("comment"), charset);
|
comment = decodeString(torrentMap.get("comment"), charset);
|
||||||
creationDate = decodeLong(torrentMap.get("creation date"));
|
creationDate = decodeLong(torrentMap.get("creation date"));
|
||||||
|
|
||||||
Map<?, ?> infoMap = (Map<?, ?>) torrentMap.get("info");
|
Map<?, ?> infoMap = (Map<?, ?>) torrentMap.get("info");
|
||||||
|
|
||||||
name = decodeString(infoMap.get("name"), charset);
|
name = decodeString(infoMap.get("name"), charset);
|
||||||
pieceLength = (Long) infoMap.get("piece length");
|
pieceLength = (Long) infoMap.get("piece length");
|
||||||
|
|
||||||
if (infoMap.containsKey("files")) {
|
if (infoMap.containsKey("files")) {
|
||||||
// torrent contains multiple entries
|
// torrent contains multiple entries
|
||||||
singleFileTorrent = false;
|
singleFileTorrent = false;
|
||||||
|
|
||||||
List<Entry> entries = new ArrayList<Entry>();
|
List<Entry> entries = new ArrayList<Entry>();
|
||||||
|
|
||||||
for (Object fileMapObject : (List<?>) infoMap.get("files")) {
|
for (Object fileMapObject : (List<?>) infoMap.get("files")) {
|
||||||
Map<?, ?> fileMap = (Map<?, ?>) fileMapObject;
|
Map<?, ?> fileMap = (Map<?, ?>) fileMapObject;
|
||||||
List<?> pathList = (List<?>) fileMap.get("path");
|
List<?> pathList = (List<?>) fileMap.get("path");
|
||||||
|
|
||||||
StringBuilder path = new StringBuilder(80);
|
StringBuilder path = new StringBuilder(80);
|
||||||
|
|
||||||
Iterator<?> iterator = pathList.iterator();
|
Iterator<?> iterator = pathList.iterator();
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
// append path element
|
// append path element
|
||||||
path.append(decodeString(iterator.next(), charset));
|
path.append(decodeString(iterator.next(), charset));
|
||||||
|
|
||||||
// append separator
|
// append separator
|
||||||
if (iterator.hasNext()) {
|
if (iterator.hasNext()) {
|
||||||
path.append("/");
|
path.append("/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Long length = decodeLong(fileMap.get("length"));
|
Long length = decodeLong(fileMap.get("length"));
|
||||||
|
|
||||||
entries.add(new Entry(path.toString(), length));
|
entries.add(new Entry(path.toString(), length));
|
||||||
}
|
}
|
||||||
|
|
||||||
files = Collections.unmodifiableList(entries);
|
files = Collections.unmodifiableList(entries);
|
||||||
} else {
|
} else {
|
||||||
// single file torrent
|
// single file torrent
|
||||||
singleFileTorrent = true;
|
singleFileTorrent = true;
|
||||||
|
|
||||||
Long length = decodeLong(infoMap.get("length"));
|
Long length = decodeLong(infoMap.get("length"));
|
||||||
|
|
||||||
files = Collections.singletonList(new Entry(name, length));
|
files = Collections.singletonList(new Entry(name, length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Map<?, ?> decodeTorrent(File torrent) throws IOException {
|
private static Map<?, ?> decodeTorrent(File torrent) throws IOException {
|
||||||
InputStream in = new BufferedInputStream(new FileInputStream(torrent));
|
InputStream in = new BufferedInputStream(new FileInputStream(torrent));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return BDecoder.decode(in);
|
return BDecoder.decode(in);
|
||||||
} finally {
|
} finally {
|
||||||
in.close();
|
in.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String decodeString(Object byteArray, Charset charset) {
|
private String decodeString(Object byteArray, Charset charset) {
|
||||||
if (byteArray == null)
|
if (byteArray == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return new String((byte[]) byteArray, charset);
|
return new String((byte[]) byteArray, charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Long decodeLong(Object number) {
|
private Long decodeLong(Object number) {
|
||||||
if (number == null)
|
if (number == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return (Long) number;
|
return (Long) number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getAnnounce() {
|
public String getAnnounce() {
|
||||||
return announce;
|
return announce;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getComment() {
|
public String getComment() {
|
||||||
return comment;
|
return comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getCreatedBy() {
|
public String getCreatedBy() {
|
||||||
return createdBy;
|
return createdBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Long getCreationDate() {
|
public Long getCreationDate() {
|
||||||
return creationDate;
|
return creationDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getEncoding() {
|
public String getEncoding() {
|
||||||
return encoding;
|
return encoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<Entry> getFiles() {
|
public List<Entry> getFiles() {
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Long getPieceLength() {
|
public Long getPieceLength() {
|
||||||
return pieceLength;
|
return pieceLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isSingleFileTorrent() {
|
public boolean isSingleFileTorrent() {
|
||||||
return singleFileTorrent;
|
return singleFileTorrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class Entry {
|
public static class Entry {
|
||||||
|
|
||||||
private final String path;
|
private final String path;
|
||||||
|
|
||||||
private final long length;
|
private final long length;
|
||||||
|
|
||||||
|
|
||||||
public Entry(String path, long length) {
|
public Entry(String path, long length) {
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
// the last element in the path is the filename
|
// the last element in the path is the filename
|
||||||
// torrents don't contain directory entries, so there is always a non-empty name
|
// torrents don't contain directory entries, so there is always a non-empty name
|
||||||
return path.substring(path.lastIndexOf("/") + 1);
|
return path.substring(path.lastIndexOf("/") + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public long getLength() {
|
public long getLength() {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getPath();
|
return getPath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,20 +8,20 @@ import net.filebot.ui.transfer.TextFileExportHandler;
|
||||||
|
|
||||||
|
|
||||||
public class FileBotListExportHandler extends TextFileExportHandler {
|
public class FileBotListExportHandler extends TextFileExportHandler {
|
||||||
|
|
||||||
protected final FileBotList<?> list;
|
protected final FileBotList<?> list;
|
||||||
|
|
||||||
|
|
||||||
public FileBotListExportHandler(FileBotList<?> list) {
|
public FileBotListExportHandler(FileBotList<?> list) {
|
||||||
this.list = list;
|
this.list = list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canExport() {
|
public boolean canExport() {
|
||||||
return list.getModel().size() > 0;
|
return list.getModel().size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void export(PrintWriter out) {
|
public void export(PrintWriter out) {
|
||||||
|
@ -29,11 +29,11 @@ public class FileBotListExportHandler extends TextFileExportHandler {
|
||||||
out.println(entry);
|
out.println(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDefaultFileName() {
|
public String getDefaultFileName() {
|
||||||
return list.getTitle() + ".txt";
|
return list.getTitle() + ".txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,91 +13,91 @@ import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
|
||||||
public class FileBotTab<T extends JComponent> extends JComponent {
|
public class FileBotTab<T extends JComponent> extends JComponent {
|
||||||
|
|
||||||
private final FileBotTabComponent tabComponent = new FileBotTabComponent();
|
private final FileBotTabComponent tabComponent = new FileBotTabComponent();
|
||||||
|
|
||||||
private final T component;
|
private final T component;
|
||||||
|
|
||||||
|
|
||||||
public FileBotTab(T component) {
|
public FileBotTab(T component) {
|
||||||
this.component = component;
|
this.component = component;
|
||||||
|
|
||||||
tabComponent.getCloseButton().addActionListener(closeAction);
|
tabComponent.getCloseButton().addActionListener(closeAction);
|
||||||
|
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
add(component, BorderLayout.CENTER);
|
add(component, BorderLayout.CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void addTo(JTabbedPane tabbedPane) {
|
public void addTo(JTabbedPane tabbedPane) {
|
||||||
tabbedPane.addTab(this.getTitle(), this);
|
tabbedPane.addTab(this.getTitle(), this);
|
||||||
tabbedPane.setTabComponentAt(tabbedPane.indexOfComponent(this), tabComponent);
|
tabbedPane.setTabComponentAt(tabbedPane.indexOfComponent(this), tabComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
if (!isClosed()) {
|
if (!isClosed()) {
|
||||||
getTabbedPane().remove(this);
|
getTabbedPane().remove(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isClosed() {
|
public boolean isClosed() {
|
||||||
JTabbedPane tabbedPane = getTabbedPane();
|
JTabbedPane tabbedPane = getTabbedPane();
|
||||||
|
|
||||||
if (tabbedPane == null)
|
if (tabbedPane == null)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return getTabbedPane().indexOfComponent(this) < 0;
|
return getTabbedPane().indexOfComponent(this) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private JTabbedPane getTabbedPane() {
|
private JTabbedPane getTabbedPane() {
|
||||||
return (JTabbedPane) SwingUtilities.getAncestorOfClass(JTabbedPane.class, this);
|
return (JTabbedPane) SwingUtilities.getAncestorOfClass(JTabbedPane.class, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public T getComponent() {
|
public T getComponent() {
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public FileBotTabComponent getTabComponent() {
|
public FileBotTabComponent getTabComponent() {
|
||||||
return tabComponent;
|
return tabComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setTitle(String title) {
|
public void setTitle(String title) {
|
||||||
tabComponent.setText(title);
|
tabComponent.setText(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return tabComponent.getText();
|
return tabComponent.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setIcon(Icon icon) {
|
public void setIcon(Icon icon) {
|
||||||
tabComponent.setIcon(icon);
|
tabComponent.setIcon(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return tabComponent.getIcon();
|
return tabComponent.getIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setLoading(boolean loading) {
|
public void setLoading(boolean loading) {
|
||||||
tabComponent.setLoading(loading);
|
tabComponent.setLoading(loading);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ActionListener closeAction = new ActionListener() {
|
private final ActionListener closeAction = new ActionListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,84 +18,84 @@ import net.miginfocom.swing.MigLayout;
|
||||||
|
|
||||||
|
|
||||||
public class FileBotTabComponent extends JComponent {
|
public class FileBotTabComponent extends JComponent {
|
||||||
|
|
||||||
private ProgressIndicator progressIndicator = new ProgressIndicator();
|
private ProgressIndicator progressIndicator = new ProgressIndicator();
|
||||||
private JLabel textLabel = new JLabel();
|
private JLabel textLabel = new JLabel();
|
||||||
private JLabel iconLabel = new JLabel();
|
private JLabel iconLabel = new JLabel();
|
||||||
private AbstractButton closeButton = createCloseButton();
|
private AbstractButton closeButton = createCloseButton();
|
||||||
|
|
||||||
private boolean loading = false;
|
private boolean loading = false;
|
||||||
|
|
||||||
|
|
||||||
public FileBotTabComponent() {
|
public FileBotTabComponent() {
|
||||||
iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||||
textLabel.setHorizontalAlignment(SwingConstants.LEFT);
|
textLabel.setHorizontalAlignment(SwingConstants.LEFT);
|
||||||
|
|
||||||
progressIndicator.setVisible(loading);
|
progressIndicator.setVisible(loading);
|
||||||
progressIndicator.setMinimumSize(new Dimension(16, 16));
|
progressIndicator.setMinimumSize(new Dimension(16, 16));
|
||||||
|
|
||||||
setLayout(new MigLayout("nogrid, insets 0 0 1 3"));
|
setLayout(new MigLayout("nogrid, insets 0 0 1 3"));
|
||||||
|
|
||||||
add(progressIndicator, "hidemode 3");
|
add(progressIndicator, "hidemode 3");
|
||||||
add(iconLabel, "hidemode 3");
|
add(iconLabel, "hidemode 3");
|
||||||
add(textLabel, "gap rel, align left");
|
add(textLabel, "gap rel, align left");
|
||||||
add(closeButton, "gap unrel:push, hidemode 3, align center 45%");
|
add(closeButton, "gap unrel:push, hidemode 3, align center 45%");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setLoading(boolean loading) {
|
public void setLoading(boolean loading) {
|
||||||
this.loading = loading;
|
this.loading = loading;
|
||||||
progressIndicator.setVisible(loading);
|
progressIndicator.setVisible(loading);
|
||||||
iconLabel.setVisible(!loading);
|
iconLabel.setVisible(!loading);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isLoading() {
|
public boolean isLoading() {
|
||||||
return loading;
|
return loading;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setIcon(Icon icon) {
|
public void setIcon(Icon icon) {
|
||||||
iconLabel.setIcon(icon);
|
iconLabel.setIcon(icon);
|
||||||
progressIndicator.setPreferredSize(icon != null ? SwingUI.getDimension(icon) : progressIndicator.getMinimumSize());
|
progressIndicator.setPreferredSize(icon != null ? SwingUI.getDimension(icon) : progressIndicator.getMinimumSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return iconLabel.getIcon();
|
return iconLabel.getIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setText(String text) {
|
public void setText(String text) {
|
||||||
textLabel.setText(text);
|
textLabel.setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getText() {
|
public String getText() {
|
||||||
return textLabel.getText();
|
return textLabel.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public AbstractButton getCloseButton() {
|
public AbstractButton getCloseButton() {
|
||||||
return closeButton;
|
return closeButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected AbstractButton createCloseButton() {
|
protected AbstractButton createCloseButton() {
|
||||||
Icon icon = ResourceManager.getIcon("tab.close");
|
Icon icon = ResourceManager.getIcon("tab.close");
|
||||||
Icon rolloverIcon = ResourceManager.getIcon("tab.close.hover");
|
Icon rolloverIcon = ResourceManager.getIcon("tab.close.hover");
|
||||||
|
|
||||||
JButton button = new JButton(icon);
|
JButton button = new JButton(icon);
|
||||||
button.setRolloverIcon(rolloverIcon);
|
button.setRolloverIcon(rolloverIcon);
|
||||||
|
|
||||||
button.setPreferredSize(SwingUI.getDimension(rolloverIcon));
|
button.setPreferredSize(SwingUI.getDimension(rolloverIcon));
|
||||||
button.setMaximumSize(button.getPreferredSize());
|
button.setMaximumSize(button.getPreferredSize());
|
||||||
|
|
||||||
button.setContentAreaFilled(false);
|
button.setContentAreaFilled(false);
|
||||||
button.setBorderPainted(false);
|
button.setBorderPainted(false);
|
||||||
button.setFocusable(false);
|
button.setFocusable(false);
|
||||||
button.setRolloverEnabled(true);
|
button.setRolloverEnabled(true);
|
||||||
|
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,11 +97,13 @@ public class LanguageComboBoxModel extends AbstractListModel implements ComboBox
|
||||||
return data.get(index);
|
return data.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean add(Language element) {
|
public boolean add(Language element) {
|
||||||
// add first
|
// add first
|
||||||
return addIfAbsent(0, element);
|
return addIfAbsent(0, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void add(int index, Language element) {
|
public void add(int index, Language element) {
|
||||||
addIfAbsent(index, element);
|
addIfAbsent(index, element);
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,10 +125,12 @@ public class MainFrame extends JFrame {
|
||||||
GroovyPad pad = new GroovyPad();
|
GroovyPad pad = new GroovyPad();
|
||||||
|
|
||||||
pad.addWindowListener(new WindowAdapter() {
|
pad.addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
public void windowOpened(WindowEvent e) {
|
public void windowOpened(WindowEvent e) {
|
||||||
MainFrame.this.setVisible(false);
|
MainFrame.this.setVisible(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
public void windowClosing(WindowEvent e) {
|
public void windowClosing(WindowEvent e) {
|
||||||
MainFrame.this.setVisible(true);
|
MainFrame.this.setVisible(true);
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,52 +23,52 @@ import net.filebot.util.ui.notification.QueueNotificationLayout;
|
||||||
|
|
||||||
|
|
||||||
public class NotificationLogging extends Handler {
|
public class NotificationLogging extends Handler {
|
||||||
|
|
||||||
public static final Logger UILogger = createNotificationLogger("net.filebot.logger.ui");
|
public static final Logger UILogger = createNotificationLogger("net.filebot.logger.ui");
|
||||||
|
|
||||||
|
|
||||||
private static Logger createNotificationLogger(String name) {
|
private static Logger createNotificationLogger(String name) {
|
||||||
Logger log = Logger.getLogger(name);
|
Logger log = Logger.getLogger(name);
|
||||||
|
|
||||||
// don't use parent handlers
|
// don't use parent handlers
|
||||||
log.setUseParentHandlers(false);
|
log.setUseParentHandlers(false);
|
||||||
|
|
||||||
// ui handler
|
// ui handler
|
||||||
log.addHandler(new NotificationLogging());
|
log.addHandler(new NotificationLogging());
|
||||||
|
|
||||||
// console handler (for warnings and errors only)
|
// console handler (for warnings and errors only)
|
||||||
ConsoleHandler console = new ConsoleHandler();
|
ConsoleHandler console = new ConsoleHandler();
|
||||||
console.setLevel(Level.WARNING);
|
console.setLevel(Level.WARNING);
|
||||||
log.addHandler(console);
|
log.addHandler(console);
|
||||||
|
|
||||||
return log;
|
return log;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final NotificationManager notificationManager;
|
public final NotificationManager notificationManager;
|
||||||
public final int timeout = 2500;
|
public final int timeout = 2500;
|
||||||
|
|
||||||
|
|
||||||
public NotificationLogging() {
|
public NotificationLogging() {
|
||||||
this(new NotificationManager(new QueueNotificationLayout(NORTH, SOUTH)));
|
this(new NotificationManager(new QueueNotificationLayout(NORTH, SOUTH)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public NotificationLogging(NotificationManager notificationManager) {
|
public NotificationLogging(NotificationManager notificationManager) {
|
||||||
this.notificationManager = notificationManager;
|
this.notificationManager = notificationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void publish(LogRecord record) {
|
public void publish(LogRecord record) {
|
||||||
// fail gracefully on an headless machine
|
// fail gracefully on an headless machine
|
||||||
if (GraphicsEnvironment.isHeadless())
|
if (GraphicsEnvironment.isHeadless())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
final Level level = record.getLevel();
|
final Level level = record.getLevel();
|
||||||
final String message = getMessage(record);
|
final String message = getMessage(record);
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (level == Level.INFO) {
|
if (level == Level.INFO) {
|
||||||
|
@ -81,34 +81,34 @@ public class NotificationLogging extends Handler {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String getMessage(LogRecord record) {
|
protected String getMessage(LogRecord record) {
|
||||||
String message = record.getMessage();
|
String message = record.getMessage();
|
||||||
|
|
||||||
if ((message == null || message.isEmpty()) && record.getThrown() != null) {
|
if ((message == null || message.isEmpty()) && record.getThrown() != null) {
|
||||||
// if message is empty, display exception string
|
// if message is empty, display exception string
|
||||||
return ExceptionUtilities.getMessage(record.getThrown());
|
return ExceptionUtilities.getMessage(record.getThrown());
|
||||||
}
|
}
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void show(String message, Icon icon, int timeout) {
|
protected void show(String message, Icon icon, int timeout) {
|
||||||
notificationManager.show(new MessageNotification(getApplicationName(), message, icon, timeout));
|
notificationManager.show(new MessageNotification(getApplicationName(), message, icon, timeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws SecurityException {
|
public void close() throws SecurityException {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flush() {
|
public void flush() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,13 @@ import javax.swing.JComponent;
|
||||||
|
|
||||||
|
|
||||||
public interface PanelBuilder {
|
public interface PanelBuilder {
|
||||||
|
|
||||||
public String getName();
|
public String getName();
|
||||||
|
|
||||||
|
|
||||||
public Icon getIcon();
|
public Icon getIcon();
|
||||||
|
|
||||||
|
|
||||||
public JComponent create();
|
public JComponent create();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ public class AnalyzePanel extends JComponent {
|
||||||
|
|
||||||
private final PropertyChangeListener filetreeListener = new PropertyChangeListener() {
|
private final PropertyChangeListener filetreeListener = new PropertyChangeListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
// stopped loading, refresh tools
|
// stopped loading, refresh tools
|
||||||
for (int i = 0; i < toolsPanel.getTabCount(); i++) {
|
for (int i = 0; i < toolsPanel.getTabCount(); i++) {
|
||||||
|
|
|
@ -10,22 +10,22 @@ import net.filebot.ui.PanelBuilder;
|
||||||
|
|
||||||
|
|
||||||
public class AnalyzePanelBuilder implements PanelBuilder {
|
public class AnalyzePanelBuilder implements PanelBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Analyze";
|
return "Analyze";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return ResourceManager.getIcon("panel.analyze");
|
return ResourceManager.getIcon("panel.analyze");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JComponent create() {
|
public JComponent create() {
|
||||||
return new AnalyzePanel();
|
return new AnalyzePanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,7 @@ public class FileTree extends JTree {
|
||||||
putValue("files", files);
|
putValue("files", files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent event) {
|
public void actionPerformed(ActionEvent event) {
|
||||||
UserFiles.revealFiles((Collection<File>) getValue("files"));
|
UserFiles.revealFiles((Collection<File>) getValue("files"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,15 +13,15 @@ import net.filebot.util.ui.GradientStyle;
|
||||||
|
|
||||||
|
|
||||||
public class FileTreeCellRenderer extends FancyTreeCellRenderer {
|
public class FileTreeCellRenderer extends FancyTreeCellRenderer {
|
||||||
|
|
||||||
public FileTreeCellRenderer() {
|
public FileTreeCellRenderer() {
|
||||||
super(GradientStyle.TOP_TO_BOTTOM);
|
super(GradientStyle.TOP_TO_BOTTOM);
|
||||||
|
|
||||||
openIcon = ResourceManager.getIcon("tree.open");
|
openIcon = ResourceManager.getIcon("tree.open");
|
||||||
closedIcon = ResourceManager.getIcon("tree.closed");
|
closedIcon = ResourceManager.getIcon("tree.closed");
|
||||||
leafIcon = ResourceManager.getIcon("tree.leaf");
|
leafIcon = ResourceManager.getIcon("tree.leaf");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
||||||
|
@ -30,17 +30,17 @@ public class FileTreeCellRenderer extends FancyTreeCellRenderer {
|
||||||
expanded = true;
|
expanded = true;
|
||||||
leaf = false;
|
leaf = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
|
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean isFolder(Object value) {
|
private boolean isFolder(Object value) {
|
||||||
if (((TreeNode) value).getAllowsChildren())
|
if (((TreeNode) value).getAllowsChildren())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import static net.filebot.util.FileUtilities.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -15,8 +14,6 @@ import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
|
|
@ -10,22 +10,22 @@ import net.filebot.ui.PanelBuilder;
|
||||||
|
|
||||||
|
|
||||||
public class EpisodeListPanelBuilder implements PanelBuilder {
|
public class EpisodeListPanelBuilder implements PanelBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Episodes";
|
return "Episodes";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return ResourceManager.getIcon("panel.episodelist");
|
return ResourceManager.getIcon("panel.episodelist");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JComponent create() {
|
public JComponent create() {
|
||||||
return new EpisodeListPanel();
|
return new EpisodeListPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,47 +6,47 @@ import javax.swing.SpinnerNumberModel;
|
||||||
|
|
||||||
|
|
||||||
class SeasonSpinnerModel extends SpinnerNumberModel {
|
class SeasonSpinnerModel extends SpinnerNumberModel {
|
||||||
|
|
||||||
public static final int ALL_SEASONS = 0;
|
public static final int ALL_SEASONS = 0;
|
||||||
|
|
||||||
public static final int MAX_VALUE = 99;
|
public static final int MAX_VALUE = 99;
|
||||||
|
|
||||||
private Number valueBeforeLock = null;
|
private Number valueBeforeLock = null;
|
||||||
|
|
||||||
|
|
||||||
public SeasonSpinnerModel() {
|
public SeasonSpinnerModel() {
|
||||||
super(ALL_SEASONS, ALL_SEASONS, MAX_VALUE, 1);
|
super(ALL_SEASONS, ALL_SEASONS, MAX_VALUE, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int getSeason() {
|
public int getSeason() {
|
||||||
return getNumber().intValue();
|
return getNumber().intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getMinimum() {
|
public Integer getMinimum() {
|
||||||
return (Integer) super.getMinimum();
|
return (Integer) super.getMinimum();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getMaximum() {
|
public Integer getMaximum() {
|
||||||
return (Integer) super.getMaximum();
|
return (Integer) super.getMaximum();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void spin(int steps) {
|
public void spin(int steps) {
|
||||||
int next = getSeason() + steps;
|
int next = getSeason() + steps;
|
||||||
|
|
||||||
if (next < getMinimum())
|
if (next < getMinimum())
|
||||||
next = getMinimum();
|
next = getMinimum();
|
||||||
else if (next > getMaximum())
|
else if (next > getMaximum())
|
||||||
next = getMaximum();
|
next = getMaximum();
|
||||||
|
|
||||||
setValue(next);
|
setValue(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void lock(int value) {
|
public void lock(int value) {
|
||||||
valueBeforeLock = getNumber();
|
valueBeforeLock = getNumber();
|
||||||
|
@ -54,12 +54,12 @@ class SeasonSpinnerModel extends SpinnerNumberModel {
|
||||||
setMaximum(value);
|
setMaximum(value);
|
||||||
setValue(value);
|
setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void unlock() {
|
public void unlock() {
|
||||||
setMinimum(ALL_SEASONS);
|
setMinimum(ALL_SEASONS);
|
||||||
setMaximum(MAX_VALUE);
|
setMaximum(MAX_VALUE);
|
||||||
|
|
||||||
if (valueBeforeLock != null) {
|
if (valueBeforeLock != null) {
|
||||||
setValue(valueBeforeLock);
|
setValue(valueBeforeLock);
|
||||||
valueBeforeLock = null;
|
valueBeforeLock = null;
|
||||||
|
|
|
@ -10,22 +10,22 @@ import net.filebot.ui.PanelBuilder;
|
||||||
|
|
||||||
|
|
||||||
public class ListPanelBuilder implements PanelBuilder {
|
public class ListPanelBuilder implements PanelBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "List";
|
return "List";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return ResourceManager.getIcon("panel.list");
|
return ResourceManager.getIcon("panel.list");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JComponent create() {
|
public JComponent create() {
|
||||||
return new ListPanel();
|
return new ListPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,42 +21,42 @@ import net.filebot.util.ui.GradientStyle;
|
||||||
|
|
||||||
|
|
||||||
class CharacterHighlightPainter implements Highlighter.HighlightPainter {
|
class CharacterHighlightPainter implements Highlighter.HighlightPainter {
|
||||||
|
|
||||||
private Color gradientBeginColor;
|
private Color gradientBeginColor;
|
||||||
private Color gradientEndColor;
|
private Color gradientEndColor;
|
||||||
|
|
||||||
|
|
||||||
public CharacterHighlightPainter(Color gradientBeginColor, Color gradientEndColor) {
|
public CharacterHighlightPainter(Color gradientBeginColor, Color gradientEndColor) {
|
||||||
this.gradientBeginColor = gradientBeginColor;
|
this.gradientBeginColor = gradientBeginColor;
|
||||||
this.gradientEndColor = gradientEndColor;
|
this.gradientEndColor = gradientEndColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint(Graphics g, int offset1, int offset2, Shape bounds, JTextComponent c) {
|
public void paint(Graphics g, int offset1, int offset2, Shape bounds, JTextComponent c) {
|
||||||
Graphics2D g2d = (Graphics2D) g;
|
Graphics2D g2d = (Graphics2D) g;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// determine locations
|
// determine locations
|
||||||
TextUI mapper = c.getUI();
|
TextUI mapper = c.getUI();
|
||||||
Rectangle p1 = mapper.modelToView(c, offset1);
|
Rectangle p1 = mapper.modelToView(c, offset1);
|
||||||
Rectangle p2 = mapper.modelToView(c, offset2);
|
Rectangle p2 = mapper.modelToView(c, offset2);
|
||||||
|
|
||||||
Rectangle r = p1.union(p2);
|
Rectangle r = p1.union(p2);
|
||||||
|
|
||||||
float w = r.width + 1;
|
float w = r.width + 1;
|
||||||
float h = r.height;
|
float h = r.height;
|
||||||
|
|
||||||
float x = r.x - 1;
|
float x = r.x - 1;
|
||||||
float y = r.y;
|
float y = r.y;
|
||||||
|
|
||||||
float arch = 5f;
|
float arch = 5f;
|
||||||
|
|
||||||
RoundRectangle2D shape = new RoundRectangle2D.Float(x, y, w, h, arch, arch);
|
RoundRectangle2D shape = new RoundRectangle2D.Float(x, y, w, h, arch, arch);
|
||||||
|
|
||||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
g2d.setPaint(GradientStyle.TOP_TO_BOTTOM.getGradientPaint(shape, gradientBeginColor, gradientEndColor));
|
g2d.setPaint(GradientStyle.TOP_TO_BOTTOM.getGradientPaint(shape, gradientBeginColor, gradientEndColor));
|
||||||
|
|
||||||
g2d.fill(shape);
|
g2d.fill(shape);
|
||||||
} catch (BadLocationException e) {
|
} catch (BadLocationException e) {
|
||||||
//should not happen
|
//should not happen
|
||||||
|
|
|
@ -14,55 +14,55 @@ import net.filebot.similarity.Match;
|
||||||
|
|
||||||
|
|
||||||
class ExpressionFormatter implements MatchFormatter {
|
class ExpressionFormatter implements MatchFormatter {
|
||||||
|
|
||||||
private final String expression;
|
private final String expression;
|
||||||
private ExpressionFormat format;
|
private ExpressionFormat format;
|
||||||
|
|
||||||
private Format preview;
|
private Format preview;
|
||||||
private Class<?> target;
|
private Class<?> target;
|
||||||
|
|
||||||
|
|
||||||
public ExpressionFormatter(String expression, Format preview, Class<?> target) {
|
public ExpressionFormatter(String expression, Format preview, Class<?> target) {
|
||||||
if (expression == null || expression.isEmpty())
|
if (expression == null || expression.isEmpty())
|
||||||
throw new IllegalArgumentException("Expression must not be null or empty");
|
throw new IllegalArgumentException("Expression must not be null or empty");
|
||||||
|
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
this.preview = preview;
|
this.preview = preview;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canFormat(Match<?, ?> match) {
|
public boolean canFormat(Match<?, ?> match) {
|
||||||
// target object is required, file is optional
|
// target object is required, file is optional
|
||||||
return target.isInstance(match.getValue()) && (match.getCandidate() == null || match.getCandidate() instanceof File);
|
return target.isInstance(match.getValue()) && (match.getCandidate() == null || match.getCandidate() instanceof File);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String preview(Match<?, ?> match) {
|
public String preview(Match<?, ?> match) {
|
||||||
return preview != null ? preview.format(match.getValue()) : match.getValue().toString();
|
return preview != null ? preview.format(match.getValue()) : match.getValue().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized String format(Match<?, ?> match, Map<?, ?> context) throws ScriptException {
|
public synchronized String format(Match<?, ?> match, Map<?, ?> context) throws ScriptException {
|
||||||
// lazy initialize script engine
|
// lazy initialize script engine
|
||||||
if (format == null) {
|
if (format == null) {
|
||||||
format = new ExpressionFormat(expression);
|
format = new ExpressionFormat(expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
// evaluate the expression using the given bindings
|
// evaluate the expression using the given bindings
|
||||||
Object bindingBean = new MediaBindingBean(match.getValue(), (File) match.getCandidate(), (Map<File, Object>) context);
|
Object bindingBean = new MediaBindingBean(match.getValue(), (File) match.getCandidate(), (Map<File, Object>) context);
|
||||||
String result = format.format(bindingBean).trim();
|
String result = format.format(bindingBean).trim();
|
||||||
|
|
||||||
// if result is empty, check for script exceptions
|
// if result is empty, check for script exceptions
|
||||||
if (result.isEmpty() && format.caughtScriptException() != null) {
|
if (result.isEmpty() && format.caughtScriptException() != null) {
|
||||||
throw format.caughtScriptException();
|
throw format.caughtScriptException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,47 +11,47 @@ import net.filebot.vfs.FileInfo;
|
||||||
|
|
||||||
|
|
||||||
class FileNameFormatter implements MatchFormatter {
|
class FileNameFormatter implements MatchFormatter {
|
||||||
|
|
||||||
private boolean preserveExtension;
|
private boolean preserveExtension;
|
||||||
|
|
||||||
|
|
||||||
public FileNameFormatter(boolean preserveExtension) {
|
public FileNameFormatter(boolean preserveExtension) {
|
||||||
this.preserveExtension = preserveExtension;
|
this.preserveExtension = preserveExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canFormat(Match<?, ?> match) {
|
public boolean canFormat(Match<?, ?> match) {
|
||||||
return match.getValue() instanceof File || match.getValue() instanceof FileInfo || match.getValue() instanceof String;
|
return match.getValue() instanceof File || match.getValue() instanceof FileInfo || match.getValue() instanceof String;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String preview(Match<?, ?> match) {
|
public String preview(Match<?, ?> match) {
|
||||||
return format(match, null);
|
return format(match, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String format(Match<?, ?> match, Map<?, ?> context) {
|
public String format(Match<?, ?> match, Map<?, ?> context) {
|
||||||
Object value = match.getValue();
|
Object value = match.getValue();
|
||||||
|
|
||||||
if (value instanceof File) {
|
if (value instanceof File) {
|
||||||
File file = (File) value;
|
File file = (File) value;
|
||||||
return preserveExtension ? FileUtilities.getName(file) : file.getName();
|
return preserveExtension ? FileUtilities.getName(file) : file.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value instanceof FileInfo) {
|
if (value instanceof FileInfo) {
|
||||||
FileInfo file = (FileInfo) value;
|
FileInfo file = (FileInfo) value;
|
||||||
return preserveExtension ? file.getName() : file.getPath();
|
return preserveExtension ? file.getName() : file.getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value instanceof String) {
|
if (value instanceof String) {
|
||||||
return preserveExtension ? FileUtilities.getNameWithoutExtension(value.toString()) : value.toString();
|
return preserveExtension ? FileUtilities.getNameWithoutExtension(value.toString()) : value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// cannot format value
|
// cannot format value
|
||||||
throw new IllegalArgumentException("Illegal value: " + value);
|
throw new IllegalArgumentException("Illegal value: " + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,45 +24,45 @@ import net.filebot.util.ui.SwingUI;
|
||||||
|
|
||||||
|
|
||||||
class HighlightListCellRenderer extends AbstractFancyListCellRenderer {
|
class HighlightListCellRenderer extends AbstractFancyListCellRenderer {
|
||||||
|
|
||||||
protected final JTextComponent textComponent = new JTextField();
|
protected final JTextComponent textComponent = new JTextField();
|
||||||
|
|
||||||
protected final Pattern pattern;
|
protected final Pattern pattern;
|
||||||
protected final Highlighter.HighlightPainter highlightPainter;
|
protected final Highlighter.HighlightPainter highlightPainter;
|
||||||
|
|
||||||
|
|
||||||
public HighlightListCellRenderer(Pattern pattern, Highlighter.HighlightPainter highlightPainter, int padding) {
|
public HighlightListCellRenderer(Pattern pattern, Highlighter.HighlightPainter highlightPainter, int padding) {
|
||||||
super(new Insets(0, 0, 0, 0));
|
super(new Insets(0, 0, 0, 0));
|
||||||
|
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
this.highlightPainter = highlightPainter;
|
this.highlightPainter = highlightPainter;
|
||||||
|
|
||||||
// pad the cell from inside the text component,
|
// pad the cell from inside the text component,
|
||||||
// so the HighlightPainter may paint in this space as well
|
// so the HighlightPainter may paint in this space as well
|
||||||
textComponent.setBorder(new EmptyBorder(padding, padding, padding, padding));
|
textComponent.setBorder(new EmptyBorder(padding, padding, padding, padding));
|
||||||
|
|
||||||
// make text component transparent, should work for all LAFs (setOpaque(false) may not, e.g. Nimbus)
|
// make text component transparent, should work for all LAFs (setOpaque(false) may not, e.g. Nimbus)
|
||||||
textComponent.setBackground(SwingUI.TRANSLUCENT);
|
textComponent.setBackground(SwingUI.TRANSLUCENT);
|
||||||
|
|
||||||
this.add(textComponent, BorderLayout.WEST);
|
this.add(textComponent, BorderLayout.WEST);
|
||||||
|
|
||||||
textComponent.getDocument().addDocumentListener(new HighlightUpdateListener());
|
textComponent.getDocument().addDocumentListener(new HighlightUpdateListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configureListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
protected void configureListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||||
super.configureListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
super.configureListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
|
|
||||||
textComponent.setText(value.toString());
|
textComponent.setText(value.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void updateHighlighter() {
|
protected void updateHighlighter() {
|
||||||
textComponent.getHighlighter().removeAllHighlights();
|
textComponent.getHighlighter().removeAllHighlights();
|
||||||
|
|
||||||
Matcher matcher = pattern.matcher(textComponent.getText());
|
Matcher matcher = pattern.matcher(textComponent.getText());
|
||||||
|
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
try {
|
try {
|
||||||
textComponent.getHighlighter().addHighlight(matcher.start(0), matcher.end(0), highlightPainter);
|
textComponent.getHighlighter().addHighlight(matcher.start(0), matcher.end(0), highlightPainter);
|
||||||
|
@ -72,38 +72,38 @@ class HighlightListCellRenderer extends AbstractFancyListCellRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setForeground(Color fg) {
|
public void setForeground(Color fg) {
|
||||||
super.setForeground(fg);
|
super.setForeground(fg);
|
||||||
|
|
||||||
// textComponent is null while in super constructor
|
// textComponent is null while in super constructor
|
||||||
if (textComponent != null) {
|
if (textComponent != null) {
|
||||||
textComponent.setForeground(fg);
|
textComponent.setForeground(fg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class HighlightUpdateListener implements DocumentListener {
|
private class HighlightUpdateListener implements DocumentListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void changedUpdate(DocumentEvent e) {
|
public void changedUpdate(DocumentEvent e) {
|
||||||
updateHighlighter();
|
updateHighlighter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void insertUpdate(DocumentEvent e) {
|
public void insertUpdate(DocumentEvent e) {
|
||||||
updateHighlighter();
|
updateHighlighter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeUpdate(DocumentEvent e) {
|
public void removeUpdate(DocumentEvent e) {
|
||||||
updateHighlighter();
|
updateHighlighter();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ class MatchAction extends AbstractAction {
|
||||||
putValue(SMALL_ICON, ResourceManager.getIcon(strict ? "action.match.strict" : "action.match"));
|
putValue(SMALL_ICON, ResourceManager.getIcon(strict ? "action.match.strict" : "action.match"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent evt) {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
if (model.names().isEmpty() || model.files().isEmpty()) {
|
if (model.names().isEmpty() || model.files().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -8,13 +8,13 @@ import net.filebot.similarity.Match;
|
||||||
|
|
||||||
|
|
||||||
public interface MatchFormatter {
|
public interface MatchFormatter {
|
||||||
|
|
||||||
public boolean canFormat(Match<?, ?> match);
|
public boolean canFormat(Match<?, ?> match);
|
||||||
|
|
||||||
|
|
||||||
public String preview(Match<?, ?> match);
|
public String preview(Match<?, ?> match);
|
||||||
|
|
||||||
|
|
||||||
public String format(Match<?, ?> match, Map<?, ?> context) throws Exception;
|
public String format(Match<?, ?> match, Map<?, ?> context) throws Exception;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,48 +16,48 @@ import ca.odell.glazedlists.event.ListEvent;
|
||||||
|
|
||||||
|
|
||||||
public class MatchModel<Value, Candidate> {
|
public class MatchModel<Value, Candidate> {
|
||||||
|
|
||||||
private final EventList<Match<Value, Candidate>> source = new BasicEventList<Match<Value, Candidate>>();
|
private final EventList<Match<Value, Candidate>> source = new BasicEventList<Match<Value, Candidate>>();
|
||||||
|
|
||||||
private final EventList<Value> values;
|
private final EventList<Value> values;
|
||||||
|
|
||||||
private final EventList<Candidate> candidates;
|
private final EventList<Candidate> candidates;
|
||||||
|
|
||||||
|
|
||||||
public MatchModel() {
|
public MatchModel() {
|
||||||
this.values = new MatchView<Value, Candidate>(source) {
|
this.values = new MatchView<Value, Candidate>(source) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value getElement(Match<Value, Candidate> match) {
|
public Value getElement(Match<Value, Candidate> match) {
|
||||||
return match.getValue();
|
return match.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Candidate getComplement(Match<Value, Candidate> match) {
|
public Candidate getComplement(Match<Value, Candidate> match) {
|
||||||
return match.getCandidate();
|
return match.getCandidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Match<Value, Candidate> createMatch(Value element, Candidate complement) {
|
public Match<Value, Candidate> createMatch(Value element, Candidate complement) {
|
||||||
return new Match<Value, Candidate>(element, complement);
|
return new Match<Value, Candidate>(element, complement);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.candidates = new MatchView<Candidate, Value>(source) {
|
this.candidates = new MatchView<Candidate, Value>(source) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Candidate getElement(Match<Value, Candidate> match) {
|
public Candidate getElement(Match<Value, Candidate> match) {
|
||||||
return match.getCandidate();
|
return match.getCandidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value getComplement(Match<Value, Candidate> match) {
|
public Value getComplement(Match<Value, Candidate> match) {
|
||||||
return match.getValue();
|
return match.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Match<Value, Candidate> createMatch(Candidate element, Value complement) {
|
public Match<Value, Candidate> createMatch(Candidate element, Value complement) {
|
||||||
|
@ -65,154 +65,154 @@ public class MatchModel<Value, Candidate> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
source.clear();
|
source.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return source.size();
|
return source.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Match<Value, Candidate> getMatch(int index) {
|
public Match<Value, Candidate> getMatch(int index) {
|
||||||
return source.get(index);
|
return source.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean hasComplement(int index) {
|
public boolean hasComplement(int index) {
|
||||||
if (index >= 0 && index < size()) {
|
if (index >= 0 && index < size()) {
|
||||||
return source.get(index).getValue() != null && source.get(index).getCandidate() != null;
|
return source.get(index).getValue() != null && source.get(index).getCandidate() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public EventList<Match<Value, Candidate>> matches() {
|
public EventList<Match<Value, Candidate>> matches() {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public EventList<Value> values() {
|
public EventList<Value> values() {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public EventList<Candidate> candidates() {
|
public EventList<Candidate> candidates() {
|
||||||
return candidates;
|
return candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void addAll(Collection<Match<Value, Candidate>> matches) {
|
public void addAll(Collection<Match<Value, Candidate>> matches) {
|
||||||
source.addAll(matches);
|
source.addAll(matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void addAll(Collection<Value> values, Collection<Candidate> candidates) {
|
public void addAll(Collection<Value> values, Collection<Candidate> candidates) {
|
||||||
if (this.values.size() != this.candidates.size())
|
if (this.values.size() != this.candidates.size())
|
||||||
throw new IllegalStateException("Existing matches are not balanced");
|
throw new IllegalStateException("Existing matches are not balanced");
|
||||||
|
|
||||||
Iterator<Value> valueIterator = values.iterator();
|
Iterator<Value> valueIterator = values.iterator();
|
||||||
Iterator<Candidate> candidateIterator = candidates.iterator();
|
Iterator<Candidate> candidateIterator = candidates.iterator();
|
||||||
|
|
||||||
while (valueIterator.hasNext() || candidateIterator.hasNext()) {
|
while (valueIterator.hasNext() || candidateIterator.hasNext()) {
|
||||||
Value value = valueIterator.hasNext() ? valueIterator.next() : null;
|
Value value = valueIterator.hasNext() ? valueIterator.next() : null;
|
||||||
Candidate candidate = candidateIterator.hasNext() ? candidateIterator.next() : null;
|
Candidate candidate = candidateIterator.hasNext() ? candidateIterator.next() : null;
|
||||||
|
|
||||||
source.add(new Match<Value, Candidate>(value, candidate));
|
source.add(new Match<Value, Candidate>(value, candidate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private abstract class MatchView<Element, Complement> extends TransformedList<Match<Value, Candidate>, Element> {
|
private abstract class MatchView<Element, Complement> extends TransformedList<Match<Value, Candidate>, Element> {
|
||||||
|
|
||||||
public MatchView(EventList<Match<Value, Candidate>> source) {
|
public MatchView(EventList<Match<Value, Candidate>> source) {
|
||||||
super(source);
|
super(source);
|
||||||
|
|
||||||
source.addListEventListener(this);
|
source.addListEventListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public abstract Element getElement(Match<Value, Candidate> match);
|
public abstract Element getElement(Match<Value, Candidate> match);
|
||||||
|
|
||||||
|
|
||||||
public abstract Complement getComplement(Match<Value, Candidate> match);
|
public abstract Complement getComplement(Match<Value, Candidate> match);
|
||||||
|
|
||||||
|
|
||||||
public abstract Match<Value, Candidate> createMatch(Element element, Complement complement);
|
public abstract Match<Value, Candidate> createMatch(Element element, Complement complement);
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Element get(int index) {
|
public Element get(int index) {
|
||||||
return getElement(index);
|
return getElement(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Element getElement(int index) {
|
public Element getElement(int index) {
|
||||||
return getElement(source.get(index));
|
return getElement(source.get(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Complement getComplement(int index) {
|
public Complement getComplement(int index) {
|
||||||
return getComplement(source.get(index));
|
return getComplement(source.get(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addAll(Collection<? extends Element> values) {
|
public boolean addAll(Collection<? extends Element> values) {
|
||||||
return put(size(), values);
|
return put(size(), values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean add(Element value) {
|
public boolean add(Element value) {
|
||||||
return put(size(), Collections.singleton(value));
|
return put(size(), Collections.singleton(value));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(int index, Element value) {
|
public void add(int index, Element value) {
|
||||||
List<Element> range = new ArrayList<Element>();
|
List<Element> range = new ArrayList<Element>();
|
||||||
|
|
||||||
range.add(value);
|
range.add(value);
|
||||||
range.addAll(subList(index, size()));
|
range.addAll(subList(index, size()));
|
||||||
|
|
||||||
put(index, range);
|
put(index, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Element remove(int index) {
|
public Element remove(int index) {
|
||||||
Element old = getElement(index);
|
Element old = getElement(index);
|
||||||
|
|
||||||
int lastIndex = size() - 1;
|
int lastIndex = size() - 1;
|
||||||
|
|
||||||
// shift subsequent elements
|
// shift subsequent elements
|
||||||
put(index, new ArrayList<Element>(subList(index + 1, lastIndex + 1)));
|
put(index, new ArrayList<Element>(subList(index + 1, lastIndex + 1)));
|
||||||
|
|
||||||
// remove last element
|
// remove last element
|
||||||
if (getComplement(lastIndex) == null) {
|
if (getComplement(lastIndex) == null) {
|
||||||
source.remove(lastIndex);
|
source.remove(lastIndex);
|
||||||
} else {
|
} else {
|
||||||
set(lastIndex, null);
|
set(lastIndex, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Element set(int index, Element element) {
|
public Element set(int index, Element element) {
|
||||||
Element old = getElement(index);
|
Element old = getElement(index);
|
||||||
|
|
||||||
source.set(index, createMatch(element, getComplement(index)));
|
source.set(index, createMatch(element, getComplement(index)));
|
||||||
|
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
|
@ -220,7 +220,7 @@ public class MatchModel<Value, Candidate> {
|
||||||
// exist at the and of the source model
|
// exist at the and of the source model
|
||||||
for (int i = size() - 1; i >= 0; i--) {
|
for (int i = size() - 1; i >= 0; i--) {
|
||||||
Complement complement = getComplement(i);
|
Complement complement = getComplement(i);
|
||||||
|
|
||||||
if (complement != null) {
|
if (complement != null) {
|
||||||
// replace original match with null match
|
// replace original match with null match
|
||||||
source.set(i, createMatch(null, complement));
|
source.set(i, createMatch(null, complement));
|
||||||
|
@ -230,7 +230,7 @@ public class MatchModel<Value, Candidate> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean put(int index, Collection<? extends Element> elements) {
|
private boolean put(int index, Collection<? extends Element> elements) {
|
||||||
for (Element element : elements) {
|
for (Element element : elements) {
|
||||||
|
@ -239,37 +239,37 @@ public class MatchModel<Value, Candidate> {
|
||||||
} else {
|
} else {
|
||||||
source.add(index, createMatch(element, null));
|
source.add(index, createMatch(element, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isWritable() {
|
protected boolean isWritable() {
|
||||||
// can't write to source directly
|
// can't write to source directly
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int size = 0;
|
private int size = 0;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void listChanged(ListEvent<Match<Value, Candidate>> listChanges) {
|
public void listChanged(ListEvent<Match<Value, Candidate>> listChanges) {
|
||||||
updates.beginEvent(true);
|
updates.beginEvent(true);
|
||||||
|
|
||||||
while (listChanges.next()) {
|
while (listChanges.next()) {
|
||||||
int index = listChanges.getIndex();
|
int index = listChanges.getIndex();
|
||||||
int type = listChanges.getType();
|
int type = listChanges.getType();
|
||||||
|
|
||||||
if (type == ListEvent.INSERT || type == ListEvent.UPDATE) {
|
if (type == ListEvent.INSERT || type == ListEvent.UPDATE) {
|
||||||
if (index < size) {
|
if (index < size) {
|
||||||
if (index == size - 1 && getElement(index) == null) {
|
if (index == size - 1 && getElement(index) == null) {
|
||||||
|
@ -287,9 +287,9 @@ public class MatchModel<Value, Candidate> {
|
||||||
size--;
|
size--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updates.commitEvent();
|
updates.commitEvent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,31 +12,31 @@ import net.filebot.web.MoviePart;
|
||||||
|
|
||||||
|
|
||||||
class MovieFormatter implements MatchFormatter {
|
class MovieFormatter implements MatchFormatter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canFormat(Match<?, ?> match) {
|
public boolean canFormat(Match<?, ?> match) {
|
||||||
return match.getValue() instanceof MoviePart;
|
return match.getValue() instanceof MoviePart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String preview(Match<?, ?> match) {
|
public String preview(Match<?, ?> match) {
|
||||||
return format(match, null);
|
return format(match, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String format(Match<?, ?> match, Map<?, ?> context) {
|
public String format(Match<?, ?> match, Map<?, ?> context) {
|
||||||
MoviePart video = (MoviePart) match.getValue();
|
MoviePart video = (MoviePart) match.getValue();
|
||||||
Formatter name = new Formatter(new StringBuilder());
|
Formatter name = new Formatter(new StringBuilder());
|
||||||
|
|
||||||
// format as single-file or multi-part movie
|
// format as single-file or multi-part movie
|
||||||
name.format("%s (%d)", video.getName(), video.getYear());
|
name.format("%s (%d)", video.getName(), video.getYear());
|
||||||
|
|
||||||
if (video.getPartCount() > 1) {
|
if (video.getPartCount() > 1) {
|
||||||
name.format(".CD%d", video.getPartIndex());
|
name.format(".CD%d", video.getPartIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove path separators if the name contains any / or \
|
// remove path separators if the name contains any / or \
|
||||||
return replacePathSeparators(name.out().toString());
|
return replacePathSeparators(name.out().toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,6 +237,7 @@ public class PresetEditor extends JDialog {
|
||||||
|
|
||||||
private final ListCellRenderer<Object> parent = (ListCellRenderer<Object>) combo.getRenderer();
|
private final ListCellRenderer<Object> parent = (ListCellRenderer<Object>) combo.getRenderer();
|
||||||
|
|
||||||
|
@Override
|
||||||
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||||
JLabel label = (JLabel) parent.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
JLabel label = (JLabel) parent.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
|
|
||||||
|
@ -278,7 +279,7 @@ public class PresetEditor extends JDialog {
|
||||||
JLabel label = (JLabel) parent.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
JLabel label = (JLabel) parent.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
|
|
||||||
if (value instanceof Language) {
|
if (value instanceof Language) {
|
||||||
Language it = (Language) value;
|
Language it = value;
|
||||||
label.setText(it.getName());
|
label.setText(it.getName());
|
||||||
label.setIcon(ResourceManager.getFlagIcon(it.getCode()));
|
label.setIcon(ResourceManager.getFlagIcon(it.getCode()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,7 @@ class RenameList<E> extends FileBotList<E> {
|
||||||
|
|
||||||
private final AbstractAction upAction = new AbstractAction("Align Up", ResourceManager.getIcon("action.up")) {
|
private final AbstractAction upAction = new AbstractAction("Align Up", ResourceManager.getIcon("action.up")) {
|
||||||
|
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
int index = getListComponent().getSelectedIndex();
|
int index = getListComponent().getSelectedIndex();
|
||||||
|
|
||||||
|
@ -138,6 +139,7 @@ class RenameList<E> extends FileBotList<E> {
|
||||||
|
|
||||||
private final AbstractAction downAction = new AbstractAction("Align Down", ResourceManager.getIcon("action.down")) {
|
private final AbstractAction downAction = new AbstractAction("Align Down", ResourceManager.getIcon("action.down")) {
|
||||||
|
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
int index = getListComponent().getSelectedIndex();
|
int index = getListComponent().getSelectedIndex();
|
||||||
|
|
||||||
|
|
|
@ -274,7 +274,7 @@ public class RenamePanel extends JComponent {
|
||||||
try {
|
try {
|
||||||
JList list = (JList) evt.getSource();
|
JList list = (JList) evt.getSource();
|
||||||
if (list.getSelectedIndex() >= 0) {
|
if (list.getSelectedIndex() >= 0) {
|
||||||
UserFiles.revealFiles((List<File>) list.getSelectedValuesList());
|
UserFiles.revealFiles(list.getSelectedValuesList());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.getLogger(RenamePanel.class.getName()).log(Level.WARNING, e.getMessage());
|
Logger.getLogger(RenamePanel.class.getName()).log(Level.WARNING, e.getMessage());
|
||||||
|
@ -702,6 +702,7 @@ public class RenamePanel extends JComponent {
|
||||||
this.preset = preset;
|
this.preset = preset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<File> getFiles(ActionEvent evt) {
|
public List<File> getFiles(ActionEvent evt) {
|
||||||
List<File> input = new ArrayList<File>();
|
List<File> input = new ArrayList<File>();
|
||||||
if (preset.getInputFolder() != null) {
|
if (preset.getInputFolder() != null) {
|
||||||
|
@ -725,10 +726,12 @@ public class RenamePanel extends JComponent {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isStrict(ActionEvent evt) {
|
public boolean isStrict(ActionEvent evt) {
|
||||||
return preset.getMatchMode() != null ? MATCH_MODE_STRICT.equals(preset.getMatchMode()) : super.isStrict(evt);
|
return preset.getMatchMode() != null ? MATCH_MODE_STRICT.equals(preset.getMatchMode()) : super.isStrict(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public SortOrder getSortOrder(ActionEvent evt) {
|
public SortOrder getSortOrder(ActionEvent evt) {
|
||||||
return preset.getSortOrder() != null ? preset.getSortOrder() : super.getSortOrder(evt);
|
return preset.getSortOrder() != null ? preset.getSortOrder() : super.getSortOrder(evt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,22 +10,22 @@ import net.filebot.ui.PanelBuilder;
|
||||||
|
|
||||||
|
|
||||||
public class RenamePanelBuilder implements PanelBuilder {
|
public class RenamePanelBuilder implements PanelBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Rename";
|
return "Rename";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return ResourceManager.getIcon("panel.rename");
|
return ResourceManager.getIcon("panel.rename");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JComponent create() {
|
public JComponent create() {
|
||||||
return new RenamePanel();
|
return new RenamePanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,88 +13,88 @@ import javax.swing.event.ListDataListener;
|
||||||
|
|
||||||
|
|
||||||
class ScrollPaneSynchronizer {
|
class ScrollPaneSynchronizer {
|
||||||
|
|
||||||
private final RenameList[] components;
|
private final RenameList[] components;
|
||||||
|
|
||||||
|
|
||||||
public ScrollPaneSynchronizer(RenameList... components) {
|
public ScrollPaneSynchronizer(RenameList... components) {
|
||||||
this.components = components;
|
this.components = components;
|
||||||
|
|
||||||
// share vertical and horizontal scrollbar model
|
// share vertical and horizontal scrollbar model
|
||||||
BoundedRangeModel horizontalScrollBarModel = components[0].getListScrollPane().getHorizontalScrollBar().getModel();
|
BoundedRangeModel horizontalScrollBarModel = components[0].getListScrollPane().getHorizontalScrollBar().getModel();
|
||||||
BoundedRangeModel verticalScrollBarModel = components[0].getListScrollPane().getVerticalScrollBar().getModel();
|
BoundedRangeModel verticalScrollBarModel = components[0].getListScrollPane().getVerticalScrollBar().getModel();
|
||||||
|
|
||||||
// recalculate common size on change
|
// recalculate common size on change
|
||||||
ListDataListener resizeListener = new ListDataListener() {
|
ListDataListener resizeListener = new ListDataListener() {
|
||||||
|
|
||||||
private final Timer timer = new Timer(50, new ActionListener() {
|
private final Timer timer = new Timer(50, new ActionListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
updatePreferredSize();
|
updatePreferredSize();
|
||||||
|
|
||||||
// fire only once
|
// fire only once
|
||||||
timer.stop();
|
timer.stop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void intervalAdded(ListDataEvent e) {
|
public void intervalAdded(ListDataEvent e) {
|
||||||
timer.restart();
|
timer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void intervalRemoved(ListDataEvent e) {
|
public void intervalRemoved(ListDataEvent e) {
|
||||||
timer.restart();
|
timer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contentsChanged(ListDataEvent e) {
|
public void contentsChanged(ListDataEvent e) {
|
||||||
timer.restart();
|
timer.restart();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// apply to all components
|
// apply to all components
|
||||||
for (RenameList<?> component : components) {
|
for (RenameList<?> component : components) {
|
||||||
component.getListScrollPane().getHorizontalScrollBar().setModel(horizontalScrollBarModel);
|
component.getListScrollPane().getHorizontalScrollBar().setModel(horizontalScrollBarModel);
|
||||||
component.getListScrollPane().getVerticalScrollBar().setModel(verticalScrollBarModel);
|
component.getListScrollPane().getVerticalScrollBar().setModel(verticalScrollBarModel);
|
||||||
|
|
||||||
component.getListComponent().getModel().addListDataListener(resizeListener);
|
component.getListComponent().getModel().addListDataListener(resizeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial sync of component sizes
|
// initial sync of component sizes
|
||||||
updatePreferredSize();
|
updatePreferredSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void updatePreferredSize() {
|
public void updatePreferredSize() {
|
||||||
Dimension max = new Dimension();
|
Dimension max = new Dimension();
|
||||||
|
|
||||||
for (RenameList component : components) {
|
for (RenameList component : components) {
|
||||||
// reset preferred size
|
// reset preferred size
|
||||||
component.getListComponent().setPreferredSize(null);
|
component.getListComponent().setPreferredSize(null);
|
||||||
|
|
||||||
// calculate preferred size based on data and renderer
|
// calculate preferred size based on data and renderer
|
||||||
Dimension preferred = component.getListComponent().getPreferredSize();
|
Dimension preferred = component.getListComponent().getPreferredSize();
|
||||||
|
|
||||||
// update maximum size
|
// update maximum size
|
||||||
if (preferred.width > max.width)
|
if (preferred.width > max.width)
|
||||||
max.width = preferred.width;
|
max.width = preferred.width;
|
||||||
if (preferred.height > max.height)
|
if (preferred.height > max.height)
|
||||||
max.height = preferred.height;
|
max.height = preferred.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (RenameList component : components) {
|
for (RenameList component : components) {
|
||||||
// set fixed preferred size
|
// set fixed preferred size
|
||||||
component.getListComponent().setPreferredSize(max);
|
component.getListComponent().setPreferredSize(max);
|
||||||
|
|
||||||
// update scrollbars
|
// update scrollbars
|
||||||
component.getListComponent().revalidate();
|
component.getListComponent().revalidate();
|
||||||
component.getListScrollPane().revalidate();
|
component.getListScrollPane().revalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,36 +39,36 @@ import net.miginfocom.swing.MigLayout;
|
||||||
|
|
||||||
|
|
||||||
class ValidateDialog extends JDialog {
|
class ValidateDialog extends JDialog {
|
||||||
|
|
||||||
private final JList list;
|
private final JList list;
|
||||||
|
|
||||||
private File[] model;
|
private File[] model;
|
||||||
|
|
||||||
private boolean cancelled = true;
|
private boolean cancelled = true;
|
||||||
|
|
||||||
|
|
||||||
public ValidateDialog(Window owner, Collection<File> source) {
|
public ValidateDialog(Window owner, Collection<File> source) {
|
||||||
super(owner, "Invalid Names", ModalityType.DOCUMENT_MODAL);
|
super(owner, "Invalid Names", ModalityType.DOCUMENT_MODAL);
|
||||||
|
|
||||||
model = source.toArray(new File[0]);
|
model = source.toArray(new File[0]);
|
||||||
|
|
||||||
list = new JList(model);
|
list = new JList(model);
|
||||||
list.setEnabled(false);
|
list.setEnabled(false);
|
||||||
|
|
||||||
list.setCellRenderer(new HighlightListCellRenderer(ILLEGAL_CHARACTERS, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4) {
|
list.setCellRenderer(new HighlightListCellRenderer(ILLEGAL_CHARACTERS, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateHighlighter() {
|
protected void updateHighlighter() {
|
||||||
textComponent.getHighlighter().removeAllHighlights();
|
textComponent.getHighlighter().removeAllHighlights();
|
||||||
|
|
||||||
Matcher matcher = pattern.matcher(textComponent.getText());
|
Matcher matcher = pattern.matcher(textComponent.getText());
|
||||||
File file = new File(textComponent.getText());
|
File file = new File(textComponent.getText());
|
||||||
|
|
||||||
// highlight path components separately to ignore "illegal characters" that are either path separators or part of the drive letter (e.g. ':' in 'E:')
|
// highlight path components separately to ignore "illegal characters" that are either path separators or part of the drive letter (e.g. ':' in 'E:')
|
||||||
for (File element : listPath(file)) {
|
for (File element : listPath(file)) {
|
||||||
int limit = element.getPath().length();
|
int limit = element.getPath().length();
|
||||||
matcher.region(limit - element.getName().length(), limit);
|
matcher.region(limit - element.getName().length(), limit);
|
||||||
|
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
try {
|
try {
|
||||||
textComponent.getHighlighter().addHighlight(matcher.start(0), matcher.end(0), highlightPainter);
|
textComponent.getHighlighter().addHighlight(matcher.start(0), matcher.end(0), highlightPainter);
|
||||||
|
@ -80,47 +80,47 @@ class ValidateDialog extends JDialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
JLabel label = new JLabel("Some names contain invalid characters:");
|
JLabel label = new JLabel("Some names contain invalid characters:");
|
||||||
|
|
||||||
JComponent content = (JComponent) getContentPane();
|
JComponent content = (JComponent) getContentPane();
|
||||||
|
|
||||||
content.setLayout(new MigLayout("insets dialog, nogrid, fill", "", "[pref!][fill][pref!]"));
|
content.setLayout(new MigLayout("insets dialog, nogrid, fill", "", "[pref!][fill][pref!]"));
|
||||||
|
|
||||||
content.add(label, "wrap");
|
content.add(label, "wrap");
|
||||||
content.add(new JScrollPane(list), "grow, wrap 2mm");
|
content.add(new JScrollPane(list), "grow, wrap 2mm");
|
||||||
|
|
||||||
content.add(new JButton(validateAction), "align center");
|
content.add(new JButton(validateAction), "align center");
|
||||||
content.add(new JButton(continueAction), "gap related");
|
content.add(new JButton(continueAction), "gap related");
|
||||||
content.add(new JButton(cancelAction), "gap 12mm");
|
content.add(new JButton(cancelAction), "gap 12mm");
|
||||||
|
|
||||||
installAction(content, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelAction);
|
installAction(content, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelAction);
|
||||||
|
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setMinimumSize(new Dimension(365, 280));
|
setMinimumSize(new Dimension(365, 280));
|
||||||
pack();
|
pack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<File> getModel() {
|
public List<File> getModel() {
|
||||||
return unmodifiableList(Arrays.asList(model));
|
return unmodifiableList(Arrays.asList(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isCancelled() {
|
public boolean isCancelled() {
|
||||||
return cancelled;
|
return cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void finish(boolean cancelled) {
|
private void finish(boolean cancelled) {
|
||||||
this.cancelled = cancelled;
|
this.cancelled = cancelled;
|
||||||
|
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
dispose();
|
dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Action validateAction = new AbstractAction("Validate", ResourceManager.getIcon("dialog.continue")) {
|
private final Action validateAction = new AbstractAction("Validate", ResourceManager.getIcon("dialog.continue")) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
// validate names
|
// validate names
|
||||||
|
@ -128,110 +128,110 @@ class ValidateDialog extends JDialog {
|
||||||
// remove illegal characters
|
// remove illegal characters
|
||||||
model[i] = validateFilePath(model[i]);
|
model[i] = validateFilePath(model[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update view
|
// update view
|
||||||
list.repaint();
|
list.repaint();
|
||||||
|
|
||||||
// switch icon
|
// switch icon
|
||||||
continueAction.putValue(SMALL_ICON, getValue(SMALL_ICON));
|
continueAction.putValue(SMALL_ICON, getValue(SMALL_ICON));
|
||||||
|
|
||||||
// disable this action
|
// disable this action
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final Action continueAction = new AbstractAction("Continue", ResourceManager.getIcon("dialog.continue.invalid")) {
|
private final Action continueAction = new AbstractAction("Continue", ResourceManager.getIcon("dialog.continue.invalid")) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
finish(false);
|
finish(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final Action cancelAction = new AbstractAction("Cancel", ResourceManager.getIcon("dialog.cancel")) {
|
private final Action cancelAction = new AbstractAction("Cancel", ResourceManager.getIcon("dialog.cancel")) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
finish(true);
|
finish(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public static boolean validate(Component parent, List<File> source) {
|
public static boolean validate(Component parent, List<File> source) {
|
||||||
IndexView<File> invalidFilePaths = new IndexView<File>(source);
|
IndexView<File> invalidFilePaths = new IndexView<File>(source);
|
||||||
|
|
||||||
for (int i = 0; i < source.size(); i++) {
|
for (int i = 0; i < source.size(); i++) {
|
||||||
// invalid file names are also invalid file paths
|
// invalid file names are also invalid file paths
|
||||||
if (isInvalidFilePath(source.get(i)) && !isUnixFS()) {
|
if (isInvalidFilePath(source.get(i)) && !isUnixFS()) {
|
||||||
invalidFilePaths.addIndex(i);
|
invalidFilePaths.addIndex(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if there is anything to do in the first place
|
// check if there is anything to do in the first place
|
||||||
if (invalidFilePaths.isEmpty()) {
|
if (invalidFilePaths.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidateDialog dialog = new ValidateDialog(getWindow(parent), invalidFilePaths);
|
ValidateDialog dialog = new ValidateDialog(getWindow(parent), invalidFilePaths);
|
||||||
dialog.setLocation(getOffsetLocation(dialog.getOwner()));
|
dialog.setLocation(getOffsetLocation(dialog.getOwner()));
|
||||||
|
|
||||||
// show and block
|
// show and block
|
||||||
dialog.setVisible(true);
|
dialog.setVisible(true);
|
||||||
|
|
||||||
if (dialog.isCancelled()) {
|
if (dialog.isCancelled()) {
|
||||||
// no output
|
// no output
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<File> validatedFilePaths = dialog.getModel();
|
List<File> validatedFilePaths = dialog.getModel();
|
||||||
|
|
||||||
// validate source list via index view
|
// validate source list via index view
|
||||||
for (int i = 0; i < invalidFilePaths.size(); i++) {
|
for (int i = 0; i < invalidFilePaths.size(); i++) {
|
||||||
invalidFilePaths.set(i, validatedFilePaths.get(i));
|
invalidFilePaths.set(i, validatedFilePaths.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class IndexView<E> extends AbstractList<E> {
|
private static class IndexView<E> extends AbstractList<E> {
|
||||||
|
|
||||||
private final List<Integer> mapping = new ArrayList<Integer>();
|
private final List<Integer> mapping = new ArrayList<Integer>();
|
||||||
|
|
||||||
private final List<E> source;
|
private final List<E> source;
|
||||||
|
|
||||||
|
|
||||||
public IndexView(List<E> source) {
|
public IndexView(List<E> source) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean addIndex(int index) {
|
public boolean addIndex(int index) {
|
||||||
return mapping.add(index);
|
return mapping.add(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public E get(int index) {
|
public E get(int index) {
|
||||||
int sourceIndex = mapping.get(index);
|
int sourceIndex = mapping.get(index);
|
||||||
|
|
||||||
if (sourceIndex >= 0)
|
if (sourceIndex >= 0)
|
||||||
return source.get(sourceIndex);
|
return source.get(sourceIndex);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public E set(int index, E element) {
|
public E set(int index, E element) {
|
||||||
return source.set(mapping.get(index), element);
|
return source.set(mapping.get(index), element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return mapping.size();
|
return mapping.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,55 +22,55 @@ import net.filebot.ResourceManager;
|
||||||
|
|
||||||
|
|
||||||
public class ChecksumButton extends JToggleButton {
|
public class ChecksumButton extends JToggleButton {
|
||||||
|
|
||||||
private static final Icon contentArea = ResourceManager.getIcon("button.checksum");
|
private static final Icon contentArea = ResourceManager.getIcon("button.checksum");
|
||||||
private static final Icon contentAreaSelected = ResourceManager.getIcon("button.checksum.selected");
|
private static final Icon contentAreaSelected = ResourceManager.getIcon("button.checksum.selected");
|
||||||
|
|
||||||
|
|
||||||
public ChecksumButton(Action action) {
|
public ChecksumButton(Action action) {
|
||||||
super(action);
|
super(action);
|
||||||
|
|
||||||
setPreferredSize(new Dimension(max(contentAreaSelected.getIconWidth(), contentArea.getIconWidth()), max(contentAreaSelected.getIconHeight(), contentArea.getIconHeight())));
|
setPreferredSize(new Dimension(max(contentAreaSelected.getIconWidth(), contentArea.getIconWidth()), max(contentAreaSelected.getIconHeight(), contentArea.getIconHeight())));
|
||||||
setMinimumSize(getPreferredSize());
|
setMinimumSize(getPreferredSize());
|
||||||
setMaximumSize(getPreferredSize());
|
setMaximumSize(getPreferredSize());
|
||||||
|
|
||||||
setForeground(WHITE);
|
setForeground(WHITE);
|
||||||
setFont(new Font(DIALOG, PLAIN, 11));
|
setFont(new Font(DIALOG, PLAIN, 11));
|
||||||
|
|
||||||
// as image button
|
// as image button
|
||||||
setBorderPainted(false);
|
setBorderPainted(false);
|
||||||
setContentAreaFilled(false);
|
setContentAreaFilled(false);
|
||||||
setFocusPainted(false);
|
setFocusPainted(false);
|
||||||
|
|
||||||
setEnabled(true);
|
setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setEnabled(boolean enabled) {
|
public void setEnabled(boolean enabled) {
|
||||||
super.setEnabled(enabled);
|
super.setEnabled(enabled);
|
||||||
|
|
||||||
// set appropriate cursor
|
// set appropriate cursor
|
||||||
setCursor(getPredefinedCursor(enabled ? HAND_CURSOR : DEFAULT_CURSOR));
|
setCursor(getPredefinedCursor(enabled ? HAND_CURSOR : DEFAULT_CURSOR));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintComponent(Graphics g) {
|
protected void paintComponent(Graphics g) {
|
||||||
Graphics2D g2d = (Graphics2D) g;
|
Graphics2D g2d = (Graphics2D) g;
|
||||||
|
|
||||||
g2d.setRenderingHint(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON);
|
g2d.setRenderingHint(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON);
|
||||||
g2d.setRenderingHint(KEY_RENDERING, VALUE_RENDER_QUALITY);
|
g2d.setRenderingHint(KEY_RENDERING, VALUE_RENDER_QUALITY);
|
||||||
|
|
||||||
// paint background image in the center
|
// paint background image in the center
|
||||||
if (isSelected()) {
|
if (isSelected()) {
|
||||||
contentAreaSelected.paintIcon(this, g2d, (int) round((getWidth() - contentAreaSelected.getIconWidth()) / (double) 2), (int) round((getHeight() - contentAreaSelected.getIconHeight()) / (double) 2));
|
contentAreaSelected.paintIcon(this, g2d, (int) round((getWidth() - contentAreaSelected.getIconWidth()) / (double) 2), (int) round((getHeight() - contentAreaSelected.getIconHeight()) / (double) 2));
|
||||||
} else {
|
} else {
|
||||||
contentArea.paintIcon(this, g2d, (int) round((getWidth() - contentArea.getIconWidth()) / (double) 2), (int) round((getHeight() - contentArea.getIconHeight()) / (double) 2));
|
contentArea.paintIcon(this, g2d, (int) round((getWidth() - contentArea.getIconWidth()) / (double) 2), (int) round((getHeight() - contentArea.getIconHeight()) / (double) 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle2D textBounds = g2d.getFontMetrics().getStringBounds(getText(), g2d);
|
Rectangle2D textBounds = g2d.getFontMetrics().getStringBounds(getText(), g2d);
|
||||||
|
|
||||||
// draw text in the center
|
// draw text in the center
|
||||||
g2d.drawString(getText(), round((getWidth() - textBounds.getWidth()) / 2) + 1, round(getHeight() / 2 - textBounds.getY() - textBounds.getHeight() / 2));
|
g2d.drawString(getText(), round((getWidth() - textBounds.getWidth()) / 2) + 1, round(getHeight() / 2 - textBounds.getY() - textBounds.getHeight() / 2));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,132 +17,132 @@ import net.filebot.util.ExceptionUtilities;
|
||||||
|
|
||||||
|
|
||||||
class ChecksumCell {
|
class ChecksumCell {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final File root;
|
private final File root;
|
||||||
|
|
||||||
private Map<HashType, String> hashes;
|
private Map<HashType, String> hashes;
|
||||||
private ChecksumComputationTask task;
|
private ChecksumComputationTask task;
|
||||||
private Throwable error;
|
private Throwable error;
|
||||||
|
|
||||||
|
|
||||||
public static enum State {
|
public static enum State {
|
||||||
PENDING,
|
PENDING,
|
||||||
PROGRESS,
|
PROGRESS,
|
||||||
READY,
|
READY,
|
||||||
ERROR
|
ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ChecksumCell(String name, File root, Map<HashType, String> hashes) {
|
public ChecksumCell(String name, File root, Map<HashType, String> hashes) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.hashes = hashes;
|
this.hashes = hashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ChecksumCell(String name, File root, ChecksumComputationTask task) {
|
public ChecksumCell(String name, File root, ChecksumComputationTask task) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.hashes = new EnumMap<HashType, String>(HashType.class);
|
this.hashes = new EnumMap<HashType, String>(HashType.class);
|
||||||
this.task = task;
|
this.task = task;
|
||||||
|
|
||||||
// forward property change events
|
// forward property change events
|
||||||
task.addPropertyChangeListener(taskListener);
|
task.addPropertyChangeListener(taskListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public File getRoot() {
|
public File getRoot() {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getChecksum(HashType hash) {
|
public String getChecksum(HashType hash) {
|
||||||
return hashes.get(hash);
|
return hashes.get(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void putTask(ChecksumComputationTask computationTask) {
|
public void putTask(ChecksumComputationTask computationTask) {
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
task.removePropertyChangeListener(taskListener);
|
task.removePropertyChangeListener(taskListener);
|
||||||
task.cancel(true);
|
task.cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
task = computationTask;
|
task = computationTask;
|
||||||
error = null;
|
error = null;
|
||||||
|
|
||||||
// forward property change events
|
// forward property change events
|
||||||
task.addPropertyChangeListener(taskListener);
|
task.addPropertyChangeListener(taskListener);
|
||||||
|
|
||||||
// state changed to PENDING
|
// state changed to PENDING
|
||||||
pcs.firePropertyChange("state", null, getState());
|
pcs.firePropertyChange("state", null, getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ChecksumComputationTask getTask() {
|
public ChecksumComputationTask getTask() {
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Throwable getError() {
|
public Throwable getError() {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public State getState() {
|
public State getState() {
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
switch (task.getState()) {
|
switch (task.getState()) {
|
||||||
case PENDING:
|
case PENDING:
|
||||||
return State.PENDING;
|
return State.PENDING;
|
||||||
default:
|
default:
|
||||||
return State.PROGRESS;
|
return State.PROGRESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
return State.ERROR;
|
return State.ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return State.READY;
|
return State.READY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
// clear property change support
|
// clear property change support
|
||||||
for (PropertyChangeListener listener : pcs.getPropertyChangeListeners()) {
|
for (PropertyChangeListener listener : pcs.getPropertyChangeListeners()) {
|
||||||
pcs.removePropertyChangeListener(listener);
|
pcs.removePropertyChangeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
task.removePropertyChangeListener(taskListener);
|
task.removePropertyChangeListener(taskListener);
|
||||||
task.cancel(true);
|
task.cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
hashes = null;
|
hashes = null;
|
||||||
error = null;
|
error = null;
|
||||||
task = null;
|
task = null;
|
||||||
pcs = null;
|
pcs = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("%s %s", name, hashes);
|
return String.format("%s %s", name, hashes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final PropertyChangeListener taskListener = new PropertyChangeListener() {
|
private final PropertyChangeListener taskListener = new PropertyChangeListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
if ("state".equals(evt.getPropertyName())) {
|
if ("state".equals(evt.getPropertyName())) {
|
||||||
if (evt.getNewValue() == StateValue.DONE)
|
if (evt.getNewValue() == StateValue.DONE)
|
||||||
done(evt);
|
done(evt);
|
||||||
|
|
||||||
// cell state changed because worker state changed
|
// cell state changed because worker state changed
|
||||||
pcs.firePropertyChange("state", null, getState());
|
pcs.firePropertyChange("state", null, getState());
|
||||||
} else {
|
} else {
|
||||||
|
@ -150,36 +150,36 @@ class ChecksumCell {
|
||||||
pcs.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
|
pcs.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void done(PropertyChangeEvent evt) {
|
protected void done(PropertyChangeEvent evt) {
|
||||||
try {
|
try {
|
||||||
hashes.putAll(task.get());
|
hashes.putAll(task.get());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Throwable cause = ExceptionUtilities.getRootCause(e);
|
Throwable cause = ExceptionUtilities.getRootCause(e);
|
||||||
|
|
||||||
// ignore cancellation
|
// ignore cancellation
|
||||||
if (cause instanceof CancellationException) {
|
if (cause instanceof CancellationException) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = cause;
|
error = cause;
|
||||||
} finally {
|
} finally {
|
||||||
task = null;
|
task = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this, true);
|
private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this, true);
|
||||||
|
|
||||||
|
|
||||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||||
pcs.addPropertyChangeListener(listener);
|
pcs.addPropertyChangeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
||||||
pcs.removePropertyChangeListener(listener);
|
pcs.removePropertyChangeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,37 +18,37 @@ import net.filebot.util.ExceptionUtilities;
|
||||||
|
|
||||||
|
|
||||||
public class ChecksumCellRenderer extends DefaultTableCellRenderer {
|
public class ChecksumCellRenderer extends DefaultTableCellRenderer {
|
||||||
|
|
||||||
private final SwingWorkerCellRenderer progressRenderer = new SwingWorkerCellRenderer();
|
private final SwingWorkerCellRenderer progressRenderer = new SwingWorkerCellRenderer();
|
||||||
|
|
||||||
private final Color verificationForeground = new Color(0x009900);
|
private final Color verificationForeground = new Color(0x009900);
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
boolean pendingWorker = false;
|
boolean pendingWorker = false;
|
||||||
|
|
||||||
if (value instanceof SwingWorker) {
|
if (value instanceof SwingWorker) {
|
||||||
if (((SwingWorker<?, ?>) value).getState() != StateValue.PENDING)
|
if (((SwingWorker<?, ?>) value).getState() != StateValue.PENDING)
|
||||||
return progressRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
return progressRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
|
||||||
pendingWorker = true;
|
pendingWorker = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore focus
|
// ignore focus
|
||||||
super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
|
super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
|
||||||
|
|
||||||
// check row state for ERROR
|
// check row state for ERROR
|
||||||
boolean isError = (table.getValueAt(row, 0) == ChecksumRow.State.ERROR);
|
boolean isError = (table.getValueAt(row, 0) == ChecksumRow.State.ERROR);
|
||||||
|
|
||||||
// if row state is ERROR and if we are not selected use text color RED,
|
// if row state is ERROR and if we are not selected use text color RED,
|
||||||
// else use default table colors
|
// else use default table colors
|
||||||
setForeground(isSelected ? table.getSelectionForeground() : isError ? Color.RED : isVerificationColumn(table, column) ? verificationForeground : table.getForeground());
|
setForeground(isSelected ? table.getSelectionForeground() : isError ? Color.RED : isVerificationColumn(table, column) ? verificationForeground : table.getForeground());
|
||||||
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
|
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
|
||||||
|
|
||||||
// use BOLD font on ERROR
|
// use BOLD font on ERROR
|
||||||
setFont(getFont().deriveFont(isError ? BOLD : PLAIN));
|
setFont(getFont().deriveFont(isError ? BOLD : PLAIN));
|
||||||
|
|
||||||
if (pendingWorker) {
|
if (pendingWorker) {
|
||||||
setText("Pending...");
|
setText("Pending...");
|
||||||
} else if (value == null && !isSelected) {
|
} else if (value == null && !isSelected) {
|
||||||
|
@ -58,16 +58,16 @@ public class ChecksumCellRenderer extends DefaultTableCellRenderer {
|
||||||
} else if (value instanceof Throwable) {
|
} else if (value instanceof Throwable) {
|
||||||
setText(ExceptionUtilities.getRootCauseMessage((Throwable) value));
|
setText(ExceptionUtilities.getRootCauseMessage((Throwable) value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean isVerificationColumn(JTable table, int column) {
|
private boolean isVerificationColumn(JTable table, int column) {
|
||||||
ChecksumTableModel model = (ChecksumTableModel) table.getModel();
|
ChecksumTableModel model = (ChecksumTableModel) table.getModel();
|
||||||
int modelColumn = table.getColumnModel().getColumn(column).getModelIndex();
|
int modelColumn = table.getColumnModel().getColumn(column).getModelIndex();
|
||||||
|
|
||||||
return model.isVerificationColumn(modelColumn);
|
return model.isVerificationColumn(modelColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ class ChecksumComputationService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of active executors that are associated with this {@link ChecksumComputationService}.
|
* Get the number of active executors that are associated with this {@link ChecksumComputationService}.
|
||||||
*
|
*
|
||||||
* @return number of active executors
|
* @return number of active executors
|
||||||
* @see {@link #newExecutor()}
|
* @see {@link #newExecutor()}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -21,17 +21,17 @@ import net.filebot.hash.HashType;
|
||||||
|
|
||||||
|
|
||||||
class ChecksumRow {
|
class ChecksumRow {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private Map<File, ChecksumCell> hashes = new HashMap<File, ChecksumCell>(4);
|
private Map<File, ChecksumCell> hashes = new HashMap<File, ChecksumCell>(4);
|
||||||
private State state = State.UNKNOWN;
|
private State state = State.UNKNOWN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checksum that is embedded in the file name (e.g. Test[49A93C5F].txt)
|
* Checksum that is embedded in the file name (e.g. Test[49A93C5F].txt)
|
||||||
*/
|
*/
|
||||||
private String embeddedChecksum;
|
private String embeddedChecksum;
|
||||||
|
|
||||||
|
|
||||||
public static enum State {
|
public static enum State {
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
|
@ -39,72 +39,72 @@ class ChecksumRow {
|
||||||
WARNING,
|
WARNING,
|
||||||
ERROR
|
ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ChecksumRow(String name) {
|
public ChecksumRow(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.embeddedChecksum = getEmbeddedChecksum(name);
|
this.embeddedChecksum = getEmbeddedChecksum(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public State getState() {
|
public State getState() {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void setState(State newValue) {
|
protected void setState(State newValue) {
|
||||||
State oldValue = this.state;
|
State oldValue = this.state;
|
||||||
this.state = newValue;
|
this.state = newValue;
|
||||||
|
|
||||||
pcs.firePropertyChange("state", oldValue, newValue);
|
pcs.firePropertyChange("state", oldValue, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ChecksumCell getChecksum(File root) {
|
public ChecksumCell getChecksum(File root) {
|
||||||
return hashes.get(root);
|
return hashes.get(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Collection<ChecksumCell> values() {
|
public Collection<ChecksumCell> values() {
|
||||||
return Collections.unmodifiableCollection(hashes.values());
|
return Collections.unmodifiableCollection(hashes.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ChecksumCell put(ChecksumCell cell) {
|
public ChecksumCell put(ChecksumCell cell) {
|
||||||
ChecksumCell old = hashes.put(cell.getRoot(), cell);
|
ChecksumCell old = hashes.put(cell.getRoot(), cell);
|
||||||
|
|
||||||
// update state immediately, don't fire property change
|
// update state immediately, don't fire property change
|
||||||
state = getState(hashes.values());
|
state = getState(hashes.values());
|
||||||
|
|
||||||
// keep state up-to-date
|
// keep state up-to-date
|
||||||
cell.addPropertyChangeListener(updateStateListener);
|
cell.addPropertyChangeListener(updateStateListener);
|
||||||
|
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
// clear property change support
|
// clear property change support
|
||||||
for (PropertyChangeListener listener : pcs.getPropertyChangeListeners()) {
|
for (PropertyChangeListener listener : pcs.getPropertyChangeListeners()) {
|
||||||
pcs.removePropertyChangeListener(listener);
|
pcs.removePropertyChangeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ChecksumCell cell : hashes.values()) {
|
for (ChecksumCell cell : hashes.values()) {
|
||||||
cell.dispose();
|
cell.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
name = null;
|
name = null;
|
||||||
embeddedChecksum = null;
|
embeddedChecksum = null;
|
||||||
hashes = null;
|
hashes = null;
|
||||||
state = null;
|
state = null;
|
||||||
pcs = null;
|
pcs = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected State getState(Collection<ChecksumCell> cells) {
|
protected State getState(Collection<ChecksumCell> cells) {
|
||||||
// check states before we bother comparing the hash values
|
// check states before we bother comparing the hash values
|
||||||
|
@ -117,29 +117,29 @@ class ChecksumRow {
|
||||||
return State.UNKNOWN;
|
return State.UNKNOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare hash values
|
// compare hash values
|
||||||
Set<String> checksumSet = new HashSet<String>(2);
|
Set<String> checksumSet = new HashSet<String>(2);
|
||||||
Set<State> verdictSet = EnumSet.noneOf(State.class);
|
Set<State> verdictSet = EnumSet.noneOf(State.class);
|
||||||
|
|
||||||
for (HashType type : HashType.values()) {
|
for (HashType type : HashType.values()) {
|
||||||
checksumSet.clear();
|
checksumSet.clear();
|
||||||
|
|
||||||
for (ChecksumCell cell : cells) {
|
for (ChecksumCell cell : cells) {
|
||||||
String checksum = cell.getChecksum(type);
|
String checksum = cell.getChecksum(type);
|
||||||
|
|
||||||
if (checksum != null) {
|
if (checksum != null) {
|
||||||
checksumSet.add(checksum.toLowerCase());
|
checksumSet.add(checksum.toLowerCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
verdictSet.add(getVerdict(checksumSet));
|
verdictSet.add(getVerdict(checksumSet));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ERROR > WARNING > OK > UNKOWN
|
// ERROR > WARNING > OK > UNKOWN
|
||||||
return Collections.max(verdictSet);
|
return Collections.max(verdictSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected State getVerdict(Set<String> checksumSet) {
|
protected State getVerdict(Set<String> checksumSet) {
|
||||||
if (checksumSet.size() < 1) {
|
if (checksumSet.size() < 1) {
|
||||||
|
@ -152,42 +152,43 @@ class ChecksumRow {
|
||||||
// all hashes match
|
// all hashes match
|
||||||
if (embeddedChecksum != null) {
|
if (embeddedChecksum != null) {
|
||||||
String checksum = checksumSet.iterator().next();
|
String checksum = checksumSet.iterator().next();
|
||||||
|
|
||||||
if (checksum.length() == embeddedChecksum.length() && !checksum.equalsIgnoreCase(embeddedChecksum)) {
|
if (checksum.length() == embeddedChecksum.length() && !checksum.equalsIgnoreCase(embeddedChecksum)) {
|
||||||
return State.WARNING;
|
return State.WARNING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return State.OK;
|
return State.OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("%s %s %s", state, name, hashes);
|
return String.format("%s %s %s", state, name, hashes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private final PropertyChangeListener updateStateListener = new PropertyChangeListener() {
|
private final PropertyChangeListener updateStateListener = new PropertyChangeListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
if ("state".equals(evt.getPropertyName())) {
|
if ("state".equals(evt.getPropertyName())) {
|
||||||
setState(getState(hashes.values()));
|
setState(getState(hashes.values()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this, true);
|
private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this, true);
|
||||||
|
|
||||||
|
|
||||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||||
pcs.addPropertyChangeListener(listener);
|
pcs.addPropertyChangeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
||||||
pcs.removePropertyChangeListener(listener);
|
pcs.removePropertyChangeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,46 +16,46 @@ import net.filebot.util.ui.SwingUI.DragDropRowTableUI;
|
||||||
|
|
||||||
|
|
||||||
class ChecksumTable extends JTable {
|
class ChecksumTable extends JTable {
|
||||||
|
|
||||||
public ChecksumTable() {
|
public ChecksumTable() {
|
||||||
setFillsViewportHeight(true);
|
setFillsViewportHeight(true);
|
||||||
setAutoCreateRowSorter(true);
|
setAutoCreateRowSorter(true);
|
||||||
setAutoCreateColumnsFromModel(true);
|
setAutoCreateColumnsFromModel(true);
|
||||||
setAutoResizeMode(AUTO_RESIZE_SUBSEQUENT_COLUMNS);
|
setAutoResizeMode(AUTO_RESIZE_SUBSEQUENT_COLUMNS);
|
||||||
|
|
||||||
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||||
|
|
||||||
setRowHeight(20);
|
setRowHeight(20);
|
||||||
|
|
||||||
setDragEnabled(true);
|
setDragEnabled(true);
|
||||||
setUI(new DragDropRowTableUI());
|
setUI(new DragDropRowTableUI());
|
||||||
|
|
||||||
// force white background (e.g. gtk-laf default table background is gray)
|
// force white background (e.g. gtk-laf default table background is gray)
|
||||||
setBackground(Color.WHITE);
|
setBackground(Color.WHITE);
|
||||||
|
|
||||||
// highlight CRC32 patterns in filenames in green and with smaller font-size
|
// highlight CRC32 patterns in filenames in green and with smaller font-size
|
||||||
setDefaultRenderer(String.class, new HighlightPatternCellRenderer(EMBEDDED_CHECKSUM));
|
setDefaultRenderer(String.class, new HighlightPatternCellRenderer(EMBEDDED_CHECKSUM));
|
||||||
setDefaultRenderer(ChecksumRow.State.class, new StateIconCellRenderer());
|
setDefaultRenderer(ChecksumRow.State.class, new StateIconCellRenderer());
|
||||||
setDefaultRenderer(ChecksumCell.class, new ChecksumCellRenderer());
|
setDefaultRenderer(ChecksumCell.class, new ChecksumCellRenderer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ChecksumTableModel createDefaultDataModel() {
|
protected ChecksumTableModel createDefaultDataModel() {
|
||||||
return new ChecksumTableModel();
|
return new ChecksumTableModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JTableHeader createDefaultTableHeader() {
|
protected JTableHeader createDefaultTableHeader() {
|
||||||
return new JTableHeader(columnModel) {
|
return new JTableHeader(columnModel) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getToolTipText(MouseEvent evt) {
|
public String getToolTipText(MouseEvent evt) {
|
||||||
try {
|
try {
|
||||||
int columnIndex = columnModel.getColumnIndexAtX(evt.getX());
|
int columnIndex = columnModel.getColumnIndexAtX(evt.getX());
|
||||||
int modelIndex = columnModel.getColumn(columnIndex).getModelIndex();
|
int modelIndex = columnModel.getColumn(columnIndex).getModelIndex();
|
||||||
|
|
||||||
// display column root of checksum column
|
// display column root of checksum column
|
||||||
return getModel().getColumnRoot(modelIndex).getPath();
|
return getModel().getColumnRoot(modelIndex).getPath();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -65,21 +65,21 @@ class ChecksumTable extends JTable {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChecksumTableModel getModel() {
|
public ChecksumTableModel getModel() {
|
||||||
return (ChecksumTableModel) super.getModel();
|
return (ChecksumTableModel) super.getModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createDefaultColumnsFromModel() {
|
public void createDefaultColumnsFromModel() {
|
||||||
super.createDefaultColumnsFromModel();
|
super.createDefaultColumnsFromModel();
|
||||||
|
|
||||||
for (int i = 0; i < getColumnCount(); i++) {
|
for (int i = 0; i < getColumnCount(); i++) {
|
||||||
TableColumn column = getColumnModel().getColumn(i);
|
TableColumn column = getColumnModel().getColumn(i);
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
column.setPreferredWidth(45);
|
column.setPreferredWidth(45);
|
||||||
} else if (i == 1) {
|
} else if (i == 1) {
|
||||||
|
@ -89,5 +89,5 @@ class ChecksumTable extends JTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,32 +13,32 @@ import net.filebot.util.FileUtilities;
|
||||||
|
|
||||||
|
|
||||||
class ChecksumTableExportHandler extends TextFileExportHandler {
|
class ChecksumTableExportHandler extends TextFileExportHandler {
|
||||||
|
|
||||||
private final ChecksumTableModel model;
|
private final ChecksumTableModel model;
|
||||||
|
|
||||||
|
|
||||||
public ChecksumTableExportHandler(ChecksumTableModel model) {
|
public ChecksumTableExportHandler(ChecksumTableModel model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canExport() {
|
public boolean canExport() {
|
||||||
return model.getRowCount() > 0 && defaultColumn() != null;
|
return model.getRowCount() > 0 && defaultColumn() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void export(PrintWriter out) {
|
public void export(PrintWriter out) {
|
||||||
export(new VerificationFileWriter(out, model.getHashType().getFormat(), "UTF-8"), defaultColumn(), model.getHashType());
|
export(new VerificationFileWriter(out, model.getHashType().getFormat(), "UTF-8"), defaultColumn(), model.getHashType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDefaultFileName() {
|
public String getDefaultFileName() {
|
||||||
return getDefaultFileName(defaultColumn());
|
return getDefaultFileName(defaultColumn());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected File defaultColumn() {
|
protected File defaultColumn() {
|
||||||
// select first column that is not a verification file column
|
// select first column that is not a verification file column
|
||||||
|
@ -46,46 +46,46 @@ class ChecksumTableExportHandler extends TextFileExportHandler {
|
||||||
if (root.isDirectory())
|
if (root.isDirectory())
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void export(File file, File column) throws IOException {
|
public void export(File file, File column) throws IOException {
|
||||||
VerificationFileWriter writer = new VerificationFileWriter(file, model.getHashType().getFormat(), "UTF-8");
|
VerificationFileWriter writer = new VerificationFileWriter(file, model.getHashType().getFormat(), "UTF-8");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
export(writer, column, model.getHashType());
|
export(writer, column, model.getHashType());
|
||||||
} finally {
|
} finally {
|
||||||
writer.close();
|
writer.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void export(VerificationFileWriter out, File column, HashType type) {
|
public void export(VerificationFileWriter out, File column, HashType type) {
|
||||||
for (ChecksumRow row : model.rows()) {
|
for (ChecksumRow row : model.rows()) {
|
||||||
ChecksumCell cell = row.getChecksum(column);
|
ChecksumCell cell = row.getChecksum(column);
|
||||||
|
|
||||||
if (cell != null) {
|
if (cell != null) {
|
||||||
String hash = cell.getChecksum(type);
|
String hash = cell.getChecksum(type);
|
||||||
|
|
||||||
if (hash != null) {
|
if (hash != null) {
|
||||||
out.write(cell.getName(), hash);
|
out.write(cell.getName(), hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getDefaultFileName(File column) {
|
public String getDefaultFileName(File column) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
// append file name
|
// append file name
|
||||||
sb.append(column != null ? FileUtilities.getName(column) : "name");
|
sb.append(column != null ? FileUtilities.getName(column) : "name");
|
||||||
|
|
||||||
// append file extension
|
// append file extension
|
||||||
sb.append('.').append(model.getHashType().name().toLowerCase());
|
sb.append('.').append(model.getHashType().name().toLowerCase());
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,6 +242,7 @@ class ChecksumTableModel extends AbstractTableModel {
|
||||||
|
|
||||||
private final PropertyChangeListener progressListener = new PropertyChangeListener() {
|
private final PropertyChangeListener progressListener = new PropertyChangeListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
ChecksumCell cell = (ChecksumCell) evt.getSource();
|
ChecksumCell cell = (ChecksumCell) evt.getSource();
|
||||||
|
|
||||||
|
|
|
@ -17,63 +17,63 @@ import net.filebot.ui.sfv.ChecksumRow.State;
|
||||||
* DefaultTableCellRenderer with highlighting of text patterns.
|
* DefaultTableCellRenderer with highlighting of text patterns.
|
||||||
*/
|
*/
|
||||||
class HighlightPatternCellRenderer extends DefaultTableCellRenderer {
|
class HighlightPatternCellRenderer extends DefaultTableCellRenderer {
|
||||||
|
|
||||||
private final Pattern pattern;
|
private final Pattern pattern;
|
||||||
|
|
||||||
|
|
||||||
public HighlightPatternCellRenderer(Pattern pattern) {
|
public HighlightPatternCellRenderer(Pattern pattern) {
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
|
super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
|
||||||
|
|
||||||
// check for error or warning
|
// check for error or warning
|
||||||
boolean isError = (EnumSet.of(State.ERROR, State.WARNING).contains(table.getValueAt(row, 0)));
|
boolean isError = (EnumSet.of(State.ERROR, State.WARNING).contains(table.getValueAt(row, 0)));
|
||||||
|
|
||||||
// highlight patterns by using a smaller font-size and changing the font-color to a dark green
|
// highlight patterns by using a smaller font-size and changing the font-color to a dark green
|
||||||
// do not change the font-color if cell is selected, because that would look ugly (imagine green text on blue background ...)
|
// do not change the font-color if cell is selected, because that would look ugly (imagine green text on blue background ...)
|
||||||
Matcher matcher = pattern.matcher(value.toString());
|
Matcher matcher = pattern.matcher(value.toString());
|
||||||
|
|
||||||
// use no-break, because we really don't want line-wrapping in our table cells
|
// use no-break, because we really don't want line-wrapping in our table cells
|
||||||
StringBuffer htmlText = new StringBuffer("<html><nobr>");
|
StringBuffer htmlText = new StringBuffer("<html><nobr>");
|
||||||
|
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
matcher.appendReplacement(htmlText, createReplacement(isSelected ? null : (isError ? "red" : "#009900"), "smaller", isError ? "bold" : null));
|
matcher.appendReplacement(htmlText, createReplacement(isSelected ? null : (isError ? "red" : "#009900"), "smaller", isError ? "bold" : null));
|
||||||
}
|
}
|
||||||
|
|
||||||
matcher.appendTail(htmlText);
|
matcher.appendTail(htmlText);
|
||||||
|
|
||||||
htmlText.append("</nobr></html>");
|
htmlText.append("</nobr></html>");
|
||||||
|
|
||||||
setText(htmlText.toString());
|
setText(htmlText.toString());
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String createReplacement(String cssColor, String cssFontSize, String cssFontWeight) {
|
protected String createReplacement(String cssColor, String cssFontSize, String cssFontWeight) {
|
||||||
// build replacement string like
|
// build replacement string like
|
||||||
// e.g. <span style='font-size: smaller; color: #009900;'>$0</span>
|
// e.g. <span style='font-size: smaller; color: #009900;'>$0</span>
|
||||||
StringBuilder replacement = new StringBuilder(60);
|
StringBuilder replacement = new StringBuilder(60);
|
||||||
|
|
||||||
replacement.append("<span style='");
|
replacement.append("<span style='");
|
||||||
|
|
||||||
if (cssColor != null) {
|
if (cssColor != null) {
|
||||||
replacement.append("color:").append(cssColor).append(';');
|
replacement.append("color:").append(cssColor).append(';');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cssFontSize != null) {
|
if (cssFontSize != null) {
|
||||||
replacement.append("font-size:").append(cssFontSize).append(';');
|
replacement.append("font-size:").append(cssFontSize).append(';');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cssFontWeight != null) {
|
if (cssFontWeight != null) {
|
||||||
replacement.append("font-weight:").append(cssFontWeight).append(';');
|
replacement.append("font-weight:").append(cssFontWeight).append(';');
|
||||||
}
|
}
|
||||||
|
|
||||||
return replacement.append("'>$0</span>").toString();
|
return replacement.append("'>$0</span>").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,22 +10,22 @@ import net.filebot.ui.PanelBuilder;
|
||||||
|
|
||||||
|
|
||||||
public class SfvPanelBuilder implements PanelBuilder {
|
public class SfvPanelBuilder implements PanelBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "SFV";
|
return "SFV";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return ResourceManager.getIcon("panel.sfv");
|
return ResourceManager.getIcon("panel.sfv");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JComponent create() {
|
public JComponent create() {
|
||||||
return new SfvPanel();
|
return new SfvPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,28 +16,28 @@ import net.filebot.ui.sfv.ChecksumRow.State;
|
||||||
|
|
||||||
|
|
||||||
class StateIconCellRenderer extends DefaultTableCellRenderer {
|
class StateIconCellRenderer extends DefaultTableCellRenderer {
|
||||||
|
|
||||||
private final Map<State, Icon> icons = new EnumMap<State, Icon>(State.class);
|
private final Map<State, Icon> icons = new EnumMap<State, Icon>(State.class);
|
||||||
|
|
||||||
|
|
||||||
public StateIconCellRenderer() {
|
public StateIconCellRenderer() {
|
||||||
icons.put(State.UNKNOWN, ResourceManager.getIcon("status.unknown"));
|
icons.put(State.UNKNOWN, ResourceManager.getIcon("status.unknown"));
|
||||||
icons.put(State.OK, ResourceManager.getIcon("status.ok"));
|
icons.put(State.OK, ResourceManager.getIcon("status.ok"));
|
||||||
icons.put(State.WARNING, ResourceManager.getIcon("status.warning"));
|
icons.put(State.WARNING, ResourceManager.getIcon("status.warning"));
|
||||||
icons.put(State.ERROR, ResourceManager.getIcon("status.error"));
|
icons.put(State.ERROR, ResourceManager.getIcon("status.error"));
|
||||||
|
|
||||||
setVerticalAlignment(SwingConstants.CENTER);
|
setVerticalAlignment(SwingConstants.CENTER);
|
||||||
setHorizontalAlignment(SwingConstants.CENTER);
|
setHorizontalAlignment(SwingConstants.CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
super.getTableCellRendererComponent(table, null, isSelected, false, row, column);
|
super.getTableCellRendererComponent(table, null, isSelected, false, row, column);
|
||||||
|
|
||||||
setIcon(icons.get(value));
|
setIcon(icons.get(value));
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,31 +15,32 @@ import javax.swing.table.TableCellRenderer;
|
||||||
|
|
||||||
|
|
||||||
class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
|
class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
|
||||||
|
|
||||||
private final JProgressBar progressBar = new JProgressBar(0, 100);
|
private final JProgressBar progressBar = new JProgressBar(0, 100);
|
||||||
|
|
||||||
|
|
||||||
public SwingWorkerCellRenderer() {
|
public SwingWorkerCellRenderer() {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
|
|
||||||
// set margin for progress bar on parent component,
|
// set margin for progress bar on parent component,
|
||||||
// because setting it on the progress bar itself does not work (border size is not respected in the paint method)
|
// because setting it on the progress bar itself does not work (border size is not respected in the paint method)
|
||||||
setBorder(new EmptyBorder(2, 2, 2, 2));
|
setBorder(new EmptyBorder(2, 2, 2, 2));
|
||||||
|
|
||||||
progressBar.setStringPainted(true);
|
progressBar.setStringPainted(true);
|
||||||
|
|
||||||
add(progressBar, BorderLayout.CENTER);
|
add(progressBar, BorderLayout.CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
|
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
|
||||||
|
|
||||||
progressBar.setValue(((SwingWorker<?, ?>) value).getProgress());
|
progressBar.setValue(((SwingWorker<?, ?>) value).getProgress());
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridden for performance reasons.
|
* Overridden for performance reasons.
|
||||||
|
@ -47,7 +48,7 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
|
||||||
@Override
|
@Override
|
||||||
public void repaint(long tm, int x, int y, int width, int height) {
|
public void repaint(long tm, int x, int y, int width, int height) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridden for performance reasons.
|
* Overridden for performance reasons.
|
||||||
|
@ -55,7 +56,7 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
|
||||||
@Override
|
@Override
|
||||||
public void repaint(Rectangle r) {
|
public void repaint(Rectangle r) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridden for performance reasons.
|
* Overridden for performance reasons.
|
||||||
|
@ -63,7 +64,7 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
|
||||||
@Override
|
@Override
|
||||||
public void repaint() {
|
public void repaint() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridden for performance reasons.
|
* Overridden for performance reasons.
|
||||||
|
@ -71,5 +72,5 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
|
||||||
@Override
|
@Override
|
||||||
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
|
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,105 +17,107 @@ import net.miginfocom.swing.MigLayout;
|
||||||
|
|
||||||
|
|
||||||
class TotalProgressPanel extends JComponent {
|
class TotalProgressPanel extends JComponent {
|
||||||
|
|
||||||
private final JProgressBar progressBar = new JProgressBar(0, 0);
|
private final JProgressBar progressBar = new JProgressBar(0, 0);
|
||||||
|
|
||||||
private final int millisToSetVisible = 200;
|
private final int millisToSetVisible = 200;
|
||||||
|
|
||||||
|
|
||||||
public TotalProgressPanel(ChecksumComputationService computationService) {
|
public TotalProgressPanel(ChecksumComputationService computationService) {
|
||||||
setLayout(new MigLayout("insets 1px"));
|
setLayout(new MigLayout("insets 1px"));
|
||||||
|
|
||||||
setBorder(new TitledBorder("Total Progress"));
|
setBorder(new TitledBorder("Total Progress"));
|
||||||
|
|
||||||
// invisible by default
|
// invisible by default
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
|
|
||||||
progressBar.setStringPainted(true);
|
progressBar.setStringPainted(true);
|
||||||
|
|
||||||
add(progressBar, "growx");
|
add(progressBar, "growx");
|
||||||
|
|
||||||
computationService.addPropertyChangeListener(progressListener);
|
computationService.addPropertyChangeListener(progressListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final PropertyChangeListener progressListener = new PropertyChangeListener() {
|
private final PropertyChangeListener progressListener = new PropertyChangeListener() {
|
||||||
|
|
||||||
private static final String SHOW = "show";
|
private static final String SHOW = "show";
|
||||||
private static final String HIDE = "hide";
|
private static final String HIDE = "hide";
|
||||||
|
|
||||||
private final DelayedToggle delayed = new DelayedToggle();
|
private final DelayedToggle delayed = new DelayedToggle();
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
final int completedTaskCount = getComputationService(evt).getCompletedTaskCount();
|
final int completedTaskCount = getComputationService(evt).getCompletedTaskCount();
|
||||||
final int totalTaskCount = getComputationService(evt).getTotalTaskCount();
|
final int totalTaskCount = getComputationService(evt).getTotalTaskCount();
|
||||||
|
|
||||||
// invoke on EDT
|
// invoke on EDT
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
if (completedTaskCount == totalTaskCount) {
|
if (completedTaskCount == totalTaskCount) {
|
||||||
// delayed hide on reset, immediate hide on finish
|
// delayed hide on reset, immediate hide on finish
|
||||||
delayed.toggle(HIDE, totalTaskCount == 0 ? millisToSetVisible : 0, visibilityActionHandler);
|
delayed.toggle(HIDE, totalTaskCount == 0 ? millisToSetVisible : 0, visibilityActionHandler);
|
||||||
} else if (totalTaskCount != 0) {
|
} else if (totalTaskCount != 0) {
|
||||||
delayed.toggle(SHOW, millisToSetVisible, visibilityActionHandler);
|
delayed.toggle(SHOW, millisToSetVisible, visibilityActionHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalTaskCount != 0) {
|
if (totalTaskCount != 0) {
|
||||||
progressBar.setValue(completedTaskCount);
|
progressBar.setValue(completedTaskCount);
|
||||||
progressBar.setMaximum(totalTaskCount);
|
progressBar.setMaximum(totalTaskCount);
|
||||||
|
|
||||||
progressBar.setString(String.format("%d / %d", completedTaskCount, totalTaskCount));
|
progressBar.setString(String.format("%d / %d", completedTaskCount, totalTaskCount));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ChecksumComputationService getComputationService(PropertyChangeEvent evt) {
|
private ChecksumComputationService getComputationService(PropertyChangeEvent evt) {
|
||||||
return ((ChecksumComputationService) evt.getSource());
|
return ((ChecksumComputationService) evt.getSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ActionListener visibilityActionHandler = new ActionListener() {
|
private final ActionListener visibilityActionHandler = new ActionListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
setVisible(e.getActionCommand() == SHOW);
|
setVisible(e.getActionCommand() == SHOW);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
protected static class DelayedToggle {
|
protected static class DelayedToggle {
|
||||||
|
|
||||||
private Timer timer = null;
|
private Timer timer = null;
|
||||||
|
|
||||||
|
|
||||||
public void toggle(String action, int delay, final ActionListener actionHandler) {
|
public void toggle(String action, int delay, final ActionListener actionHandler) {
|
||||||
if (timer != null) {
|
if (timer != null) {
|
||||||
if (action.equals(timer.getActionCommand())) {
|
if (action.equals(timer.getActionCommand())) {
|
||||||
// action has not changed, don't stop existing timer
|
// action has not changed, don't stop existing timer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
timer.stop();
|
timer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
timer = new Timer(delay, new ActionListener() {
|
timer = new Timer(delay, new ActionListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
actionHandler.actionPerformed(e);
|
actionHandler.actionPerformed(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
timer.setActionCommand(action);
|
timer.setActionCommand(action);
|
||||||
timer.setRepeats(false);
|
timer.setRepeats(false);
|
||||||
timer.start();
|
timer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,29 +21,29 @@ import net.filebot.vfs.MemoryFile;
|
||||||
|
|
||||||
|
|
||||||
class MemoryFileListExportHandler implements TransferableExportHandler, ClipboardHandler {
|
class MemoryFileListExportHandler implements TransferableExportHandler, ClipboardHandler {
|
||||||
|
|
||||||
public boolean canExport(JComponent component) {
|
public boolean canExport(JComponent component) {
|
||||||
JList list = (JList) component;
|
JList list = (JList) component;
|
||||||
|
|
||||||
// can't export anything, if nothing is selected
|
// can't export anything, if nothing is selected
|
||||||
return !list.isSelectionEmpty();
|
return !list.isSelectionEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<MemoryFile> export(JComponent component) {
|
public List<MemoryFile> export(JComponent component) {
|
||||||
JList list = (JList) component;
|
JList list = (JList) component;
|
||||||
|
|
||||||
// get selected values
|
// get selected values
|
||||||
final Object[] selection = list.getSelectedValues();
|
final Object[] selection = list.getSelectedValues();
|
||||||
|
|
||||||
// as file list
|
// as file list
|
||||||
return new AbstractList<MemoryFile>() {
|
return new AbstractList<MemoryFile>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MemoryFile get(int index) {
|
public MemoryFile get(int index) {
|
||||||
return (MemoryFile) selection[index];
|
return (MemoryFile) selection[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
|
@ -51,35 +51,35 @@ class MemoryFileListExportHandler implements TransferableExportHandler, Clipboar
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSourceActions(JComponent component) {
|
public int getSourceActions(JComponent component) {
|
||||||
return canExport(component) ? TransferHandler.COPY_OR_MOVE : TransferHandler.NONE;
|
return canExport(component) ? TransferHandler.COPY_OR_MOVE : TransferHandler.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Transferable createTransferable(JComponent component) {
|
public Transferable createTransferable(JComponent component) {
|
||||||
Map<String, ByteBuffer> vfs = new HashMap<String, ByteBuffer>();
|
Map<String, ByteBuffer> vfs = new HashMap<String, ByteBuffer>();
|
||||||
|
|
||||||
for (MemoryFile file : export(component)) {
|
for (MemoryFile file : export(component)) {
|
||||||
vfs.put(file.getName(), file.getData());
|
vfs.put(file.getName(), file.getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ByteBufferTransferable(vfs);
|
return new ByteBufferTransferable(vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exportToClipboard(JComponent component, Clipboard clip, int action) {
|
public void exportToClipboard(JComponent component, Clipboard clip, int action) {
|
||||||
clip.setContents(createTransferable(component), null);
|
clip.setContents(createTransferable(component), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exportDone(JComponent source, Transferable data, int action) {
|
public void exportDone(JComponent source, Transferable data, int action) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue