Moved general purpose stuff into util, renamed utils->util
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
package mightypork.util.files;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.zip.Adler32;
|
||||
import java.util.zip.CheckedInputStream;
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
import mightypork.util.logging.Log;
|
||||
|
||||
|
||||
public class FileTreeDiff {
|
||||
|
||||
private static final byte[] BUFFER = new byte[2048];
|
||||
private final Checksum ck1 = new Adler32();
|
||||
private final Checksum ck2 = new Adler32();
|
||||
|
||||
private boolean logging = true;
|
||||
|
||||
private final List<Tuple<File>> compared = new ArrayList<>();
|
||||
private final Comparator<File> fileFirstSorter = new Comparator<File>() {
|
||||
|
||||
@Override
|
||||
public int compare(File o1, File o2)
|
||||
{
|
||||
if (!o1.isDirectory() && o2.isDirectory()) return -1;
|
||||
if (o1.isDirectory() && !o2.isDirectory()) return 1;
|
||||
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public void enableLogging(boolean state)
|
||||
{
|
||||
logging = state;
|
||||
}
|
||||
|
||||
|
||||
public boolean areEqual(File dir1, File dir2)
|
||||
{
|
||||
if (logging) Log.f3("Comparing directory trees:\n 1. " + dir1 + "\n 2. " + dir2);
|
||||
|
||||
try {
|
||||
compared.clear();
|
||||
buildList(dir1, dir2);
|
||||
|
||||
calcChecksum();
|
||||
|
||||
if (logging) Log.f3("No difference found.");
|
||||
|
||||
return true;
|
||||
|
||||
} catch (final NotEqualException e) {
|
||||
if (logging) Log.f3("Difference found:\n" + e.getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void calcChecksum() throws NotEqualException
|
||||
{
|
||||
|
||||
for (final Tuple<File> pair : compared) {
|
||||
ck1.reset();
|
||||
ck2.reset();
|
||||
|
||||
try (FileInputStream in1 = new FileInputStream(pair.a); FileInputStream in2 = new FileInputStream(pair.b)) {
|
||||
|
||||
try (CheckedInputStream cin1 = new CheckedInputStream(in1, ck1); CheckedInputStream cin2 = new CheckedInputStream(in2, ck2)) {
|
||||
|
||||
while (true) {
|
||||
final int read1 = cin1.read(BUFFER);
|
||||
final int read2 = cin2.read(BUFFER);
|
||||
|
||||
if (read1 != read2 || ck1.getValue() != ck2.getValue()) {
|
||||
throw new NotEqualException("Bytes differ:\n" + pair.a + "\n" + pair.b);
|
||||
}
|
||||
|
||||
if (read1 == -1) break;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (final IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void buildList(File f1, File f2) throws NotEqualException
|
||||
{
|
||||
if (f1.isDirectory() != f2.isDirectory()) throw new NotEqualException("isDirectory differs:\n" + f1 + "\n" + f2);
|
||||
|
||||
if (f1.isFile() && f2.isFile()) {
|
||||
if (f1.length() != f2.length()) throw new NotEqualException("Sizes differ:\n" + f1 + "\n" + f2);
|
||||
}
|
||||
|
||||
if (f1.isDirectory()) {
|
||||
final File[] children1 = f1.listFiles();
|
||||
final File[] children2 = f2.listFiles();
|
||||
|
||||
Arrays.sort(children1, fileFirstSorter);
|
||||
Arrays.sort(children2, fileFirstSorter);
|
||||
|
||||
if (children1.length != children2.length) throw new NotEqualException("Child counts differ:\n" + f1 + "\n" + f2);
|
||||
|
||||
for (int i = 0; i < children1.length; i++) {
|
||||
final File ch1 = children1[i];
|
||||
final File ch2 = children2[i];
|
||||
|
||||
if (!ch1.getName().equals(ch2.getName())) throw new NotEqualException("Filenames differ:\n" + ch1 + "\n" + ch2);
|
||||
|
||||
buildList(ch1, ch2);
|
||||
}
|
||||
|
||||
} else {
|
||||
compared.add(new Tuple<>(f1, f2));
|
||||
}
|
||||
}
|
||||
|
||||
private class NotEqualException extends Exception {
|
||||
|
||||
public NotEqualException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Tuple<T> {
|
||||
|
||||
public T a;
|
||||
public T b;
|
||||
|
||||
|
||||
public Tuple(T a, T b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
package mightypork.util.files;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import mightypork.util.logging.Log;
|
||||
import mightypork.util.string.StringUtils;
|
||||
import mightypork.util.string.validation.StringFilter;
|
||||
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
/**
|
||||
* Copy directory recursively.
|
||||
*
|
||||
* @param source source file
|
||||
* @param target target file
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void copyDirectory(File source, File target) throws IOException
|
||||
{
|
||||
copyDirectory(source, target, null, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy directory recursively - advanced variant.
|
||||
*
|
||||
* @param source source file
|
||||
* @param target target file
|
||||
* @param filter filter accepting only files and dirs to be copied
|
||||
* @param filesCopied list into which all the target files will be added
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void copyDirectory(File source, File target, FileFilter filter, List<File> filesCopied) throws IOException
|
||||
{
|
||||
if (!source.exists()) return;
|
||||
|
||||
if (source.isDirectory()) {
|
||||
if (!target.exists() && !target.mkdir()) {
|
||||
throw new IOException("Could not open destination directory.");
|
||||
}
|
||||
|
||||
final String[] children = source.list();
|
||||
for (final String element : children) {
|
||||
copyDirectory(new File(source, element), new File(target, element), filter, filesCopied);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (filter != null && !filter.accept(source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (filesCopied != null) filesCopied.add(target);
|
||||
copyFile(source, target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List directory recursively
|
||||
*
|
||||
* @param source source file
|
||||
* @param filter filter accepting only files and dirs to be copied (or null)
|
||||
* @param files list of the found files
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void listDirectoryRecursive(File source, StringFilter filter, List<File> files) throws IOException
|
||||
{
|
||||
if (source.isDirectory()) {
|
||||
final String[] children = source.list();
|
||||
for (final String element : children) {
|
||||
listDirectoryRecursive(new File(source, element), filter, files);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (filter != null && !filter.accept(source.getAbsolutePath())) {
|
||||
return;
|
||||
}
|
||||
|
||||
files.add(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy file using streams. Make sure target directory exists!
|
||||
*
|
||||
* @param source source file
|
||||
* @param target target file
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void copyFile(File source, File target) throws IOException
|
||||
{
|
||||
|
||||
try (InputStream in = new FileInputStream(source); OutputStream out = new FileOutputStream(target)) {
|
||||
|
||||
copyStream(in, out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy bytes from input to output stream, leaving out stream open
|
||||
*
|
||||
* @param in input stream
|
||||
* @param out output stream
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void copyStream(InputStream in, OutputStream out) throws IOException
|
||||
{
|
||||
if (in == null) {
|
||||
throw new NullPointerException("Input stream is null");
|
||||
}
|
||||
|
||||
if (out == null) {
|
||||
throw new NullPointerException("Output stream is null");
|
||||
}
|
||||
|
||||
final byte[] buf = new byte[2048];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Improved delete
|
||||
*
|
||||
* @param path deleted path
|
||||
* @param recursive recursive delete
|
||||
* @return success
|
||||
*/
|
||||
public static boolean delete(File path, boolean recursive)
|
||||
{
|
||||
if (!path.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!recursive || !path.isDirectory()) return path.delete();
|
||||
|
||||
final String[] list = path.list();
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
if (!delete(new File(path, list[i]), true)) return false;
|
||||
}
|
||||
|
||||
return path.delete();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read entire file to a string.
|
||||
*
|
||||
* @param file file
|
||||
* @return file contents
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String fileToString(File file) throws IOException
|
||||
{
|
||||
try (FileInputStream fin = new FileInputStream(file)) {
|
||||
|
||||
return streamToString(fin);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get files in a folder (create folder if needed)
|
||||
*
|
||||
* @param dir folder
|
||||
* @return list of files
|
||||
*/
|
||||
public static List<File> listDirectory(File dir)
|
||||
{
|
||||
return FileUtils.listDirectory(dir, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get files in a folder (create folder if needed)
|
||||
*
|
||||
* @param dir folder
|
||||
* @param filter file filter
|
||||
* @return list of files
|
||||
*/
|
||||
public static List<File> listDirectory(File dir, FileFilter filter)
|
||||
{
|
||||
try {
|
||||
dir.mkdir();
|
||||
} catch (final RuntimeException e) {
|
||||
Log.e("Error creating folder " + dir, e);
|
||||
}
|
||||
|
||||
final List<File> list = new ArrayList<>();
|
||||
|
||||
try {
|
||||
for (final File f : dir.listFiles(filter)) {
|
||||
list.add(f);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
Log.e("Error listing folder " + dir, e);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove extension.
|
||||
*
|
||||
* @param file file
|
||||
* @return filename without extension
|
||||
*/
|
||||
public static String[] getFilenameParts(File file)
|
||||
{
|
||||
return getFilenameParts(file.getName());
|
||||
}
|
||||
|
||||
|
||||
public static String getExtension(File file)
|
||||
{
|
||||
return getExtension(file.getName());
|
||||
}
|
||||
|
||||
|
||||
public static String getExtension(String file)
|
||||
{
|
||||
return StringUtils.fromLastChar(file, '.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove extension.
|
||||
*
|
||||
* @param filename
|
||||
* @return filename and extension
|
||||
*/
|
||||
public static String[] getFilenameParts(String filename)
|
||||
{
|
||||
String ext, name;
|
||||
|
||||
try {
|
||||
ext = StringUtils.fromLastDot(filename);
|
||||
} catch (final StringIndexOutOfBoundsException e) {
|
||||
ext = "";
|
||||
}
|
||||
|
||||
try {
|
||||
name = StringUtils.toLastDot(filename);
|
||||
} catch (final StringIndexOutOfBoundsException e) {
|
||||
name = "";
|
||||
Log.w("Error extracting extension from file " + filename);
|
||||
}
|
||||
|
||||
return new String[] { name, ext };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read entire input stream to a string, and close it.
|
||||
*
|
||||
* @param in input stream
|
||||
* @return file contents
|
||||
*/
|
||||
public static String streamToString(InputStream in)
|
||||
{
|
||||
return streamToString(in, -1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read input stream to a string, and close it.
|
||||
*
|
||||
* @param in input stream
|
||||
* @param lines max number of lines (-1 to disable limit)
|
||||
* @return file contents
|
||||
*/
|
||||
public static String streamToString(InputStream in, int lines)
|
||||
{
|
||||
if (in == null) {
|
||||
Log.e(new NullPointerException("Null stream to be converted to String."));
|
||||
return ""; // to avoid NPE's
|
||||
}
|
||||
|
||||
BufferedReader br = null;
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
String line;
|
||||
try {
|
||||
int cnt = 0;
|
||||
br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||
while ((line = br.readLine()) != null && (cnt < lines || lines <= 0)) {
|
||||
sb.append(line + "\n");
|
||||
cnt++;
|
||||
}
|
||||
|
||||
if (cnt == lines && lines > 0) {
|
||||
sb.append("--- end of preview ---\n");
|
||||
}
|
||||
|
||||
} catch (final IOException e) {
|
||||
Log.e(e);
|
||||
} finally {
|
||||
try {
|
||||
if (br != null) br.close();
|
||||
} catch (final IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public static InputStream stringToStream(String text)
|
||||
{
|
||||
if (text == null) return null;
|
||||
|
||||
try {
|
||||
return new ByteArrayInputStream(text.getBytes("UTF-8"));
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
Log.e(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static InputStream getResource(String path)
|
||||
{
|
||||
final InputStream in = FileUtils.class.getResourceAsStream(path);
|
||||
|
||||
if (in != null) return in;
|
||||
|
||||
try {
|
||||
return new FileInputStream(new File(".", path));
|
||||
} catch (final FileNotFoundException e) {
|
||||
// error
|
||||
Log.w("Could not open resource stream: " + path);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static String getResourceAsString(String path)
|
||||
{
|
||||
return streamToString(FileUtils.class.getResourceAsStream(path));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save string to file
|
||||
*
|
||||
* @param file file
|
||||
* @param text string
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void stringToFile(File file, String text) throws IOException
|
||||
{
|
||||
try (PrintStream out = new PrintStream(new FileOutputStream(file), false, "UTF-8")) {
|
||||
|
||||
out.print(text);
|
||||
|
||||
out.flush();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void deleteEmptyDirs(File base) throws IOException
|
||||
{
|
||||
for (final File f : listDirectory(base)) {
|
||||
if (!f.isDirectory()) continue;
|
||||
|
||||
deleteEmptyDirs(f);
|
||||
|
||||
final List<File> children = listDirectory(f);
|
||||
if (children.size() == 0) {
|
||||
if (!f.delete()) throw new IOException("Could not delete a directory: " + f);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace special characters with place holders in a filename.
|
||||
*
|
||||
* @param filestring filename string
|
||||
* @return escaped
|
||||
*/
|
||||
public static String escapeFileString(String filestring)
|
||||
{
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (final char c : filestring.toCharArray()) {
|
||||
switch (c) {
|
||||
case '%':
|
||||
sb.append("%%");
|
||||
break;
|
||||
|
||||
case '.':
|
||||
sb.append("%d");
|
||||
break;
|
||||
|
||||
default:
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unescape filename string obtained by escapeFileString().
|
||||
*
|
||||
* @param filestring escaped string
|
||||
* @return clean string
|
||||
*/
|
||||
public static String unescapeFileString(String filestring)
|
||||
{
|
||||
filestring = filestring.replace("%d", ".");
|
||||
filestring = filestring.replace("%%", "%");
|
||||
|
||||
return filestring;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escape filename, keeping the same extension
|
||||
*
|
||||
* @param filename filename
|
||||
* @return escaped
|
||||
*/
|
||||
public static String escapeFilename(String filename)
|
||||
{
|
||||
final String[] parts = getFilenameParts(filename);
|
||||
|
||||
return escapeFileString(parts[0]) + "." + parts[1];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unescape filename, keeping the same extension
|
||||
*
|
||||
* @param filename escaped filename
|
||||
* @return unesaped
|
||||
*/
|
||||
public static String unescapeFilename(String filename)
|
||||
{
|
||||
final String[] parts = getFilenameParts(filename);
|
||||
|
||||
return unescapeFileString(parts[0]) + "." + parts[1];
|
||||
}
|
||||
|
||||
|
||||
public static String getBasename(String name)
|
||||
{
|
||||
return StringUtils.toLastChar(StringUtils.fromLastChar(name, '/'), '.');
|
||||
}
|
||||
|
||||
|
||||
public static String getFilename(String name)
|
||||
{
|
||||
return StringUtils.fromLastChar(name, '/');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy resource to file
|
||||
*
|
||||
* @param resname resource name
|
||||
* @param file out file
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void resourceToFile(String resname, File file) throws IOException
|
||||
{
|
||||
try (InputStream in = FileUtils.getResource(resname); OutputStream out = new FileOutputStream(file)) {
|
||||
|
||||
FileUtils.copyStream(in, out);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get resource as string, safely closing streams.
|
||||
*
|
||||
* @param resname resource name
|
||||
* @return resource as string, empty string on failure
|
||||
* @throws IOException on fail
|
||||
*/
|
||||
public static String resourceToString(String resname) throws IOException
|
||||
{
|
||||
try (InputStream in = FileUtils.getResource(resname)) {
|
||||
return streamToString(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package mightypork.util.files;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.channels.FileLock;
|
||||
|
||||
|
||||
/**
|
||||
* Instance lock (avoid running twice)
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class InstanceLock {
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public static boolean onFile(final File lockFile)
|
||||
{
|
||||
try {
|
||||
final RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile, "rw");
|
||||
|
||||
final FileLock fileLock = randomAccessFile.getChannel().tryLock();
|
||||
if (fileLock != null) {
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
fileLock.release();
|
||||
randomAccessFile.close();
|
||||
if (!lockFile.delete()) throw new IOException();
|
||||
} catch (final Exception e) {
|
||||
System.err.println("Unable to remove lock file.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (final IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package mightypork.util.files;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
public class OsUtils {
|
||||
|
||||
public static enum EnumOS
|
||||
{
|
||||
linux, macos, solaris, unknown, windows;
|
||||
|
||||
public boolean isLinux()
|
||||
{
|
||||
return this == linux || this == solaris;
|
||||
}
|
||||
|
||||
|
||||
public boolean isMac()
|
||||
{
|
||||
return this == macos;
|
||||
}
|
||||
|
||||
|
||||
public boolean isWindows()
|
||||
{
|
||||
return this == windows;
|
||||
}
|
||||
}
|
||||
|
||||
private static EnumOS cachedOs;
|
||||
|
||||
|
||||
/**
|
||||
* Get App dir, ensure it exists
|
||||
*
|
||||
* @param dirname
|
||||
* @return app dir
|
||||
*/
|
||||
public static File getWorkDir(String dirname)
|
||||
{
|
||||
return getWorkDir(dirname, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get App sub-folder
|
||||
*
|
||||
* @param dirname
|
||||
* @param subfolderName
|
||||
* @param create
|
||||
* @return the folder
|
||||
*/
|
||||
public static File getWorkDir(String dirname, String subfolderName, boolean create)
|
||||
{
|
||||
final File f = new File(getWorkDir(dirname), subfolderName);
|
||||
|
||||
if (!f.exists() && create) {
|
||||
if (!f.mkdirs()) {
|
||||
throw new RuntimeException("Could not create.");
|
||||
}
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get App sub-folder, create
|
||||
*
|
||||
* @param dirname
|
||||
* @param subfolderName
|
||||
* @return the folder
|
||||
*/
|
||||
public static File getWorkDir(String dirname, String subfolderName)
|
||||
{
|
||||
return getWorkDir(dirname, subfolderName, true);
|
||||
}
|
||||
|
||||
|
||||
public static EnumOS getOs()
|
||||
{
|
||||
if (cachedOs != null) return cachedOs;
|
||||
|
||||
final String s = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
if (s.contains("win")) {
|
||||
cachedOs = EnumOS.windows;
|
||||
|
||||
} else if (s.contains("mac")) {
|
||||
cachedOs = EnumOS.macos;
|
||||
|
||||
} else if (s.contains("linux") || s.contains("unix")) {
|
||||
cachedOs = EnumOS.linux;
|
||||
|
||||
} else if (s.contains("solaris") || s.contains("sunos")) {
|
||||
cachedOs = EnumOS.solaris;
|
||||
|
||||
} else {
|
||||
cachedOs = EnumOS.unknown;
|
||||
}
|
||||
|
||||
return cachedOs;
|
||||
}
|
||||
|
||||
|
||||
private static File getWorkDir(String dirname, boolean create)
|
||||
{
|
||||
final String userhome = System.getProperty("user.home", ".");
|
||||
File file;
|
||||
|
||||
switch (getOs()) {
|
||||
case linux:
|
||||
case solaris:
|
||||
file = new File(userhome, "." + dirname + '/');
|
||||
break;
|
||||
|
||||
case windows:
|
||||
final String appdata = System.getenv("APPDATA");
|
||||
|
||||
if (appdata != null) {
|
||||
file = new File(appdata, "." + dirname + '/');
|
||||
} else {
|
||||
file = new File(userhome, "." + dirname + '/');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case macos:
|
||||
file = new File(userhome, "Library/Application Support/" + dirname);
|
||||
break;
|
||||
|
||||
default:
|
||||
file = new File(userhome, dirname + "/");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!file.exists() || !file.isDirectory()) {
|
||||
if (create) {
|
||||
if (!file.mkdirs()) {
|
||||
throw new RuntimeException("Could not create working directory.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package mightypork.util.files.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable Arraylist
|
||||
*
|
||||
* @author MightyPork
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class AbstractIonList<T> extends ArrayList<T> implements Ionizable {
|
||||
|
||||
@Override
|
||||
public void ionRead(InputStream in) throws IonException
|
||||
{
|
||||
try {
|
||||
while (true) {
|
||||
final byte b = BinaryUtils.readByte(in);
|
||||
|
||||
if (b == IonMarks.ENTRY) {
|
||||
final T value = (T) Ion.readObject(in);
|
||||
add(value);
|
||||
} else if (b == IonMarks.END) {
|
||||
break;
|
||||
} else {
|
||||
throw new IonException("Unexpected mark in IonList: " + Integer.toHexString(b));
|
||||
}
|
||||
}
|
||||
ionReadCustomData(in);
|
||||
} catch (final IOException e) {
|
||||
throw new IonException("Error reading ion list", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void ionWrite(OutputStream out) throws IonException
|
||||
{
|
||||
try {
|
||||
for (final T entry : this) {
|
||||
if (entry instanceof IonizableOptional && !((IonizableOptional) entry).ionShouldSave()) continue;
|
||||
BinaryUtils.writeByte(out, IonMarks.ENTRY);
|
||||
Ion.writeObject(out, entry);
|
||||
}
|
||||
BinaryUtils.writeByte(out, IonMarks.END);
|
||||
ionWriteCustomData(out);
|
||||
} catch (final IOException e) {
|
||||
throw new IonException("Error reading ion map", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read custom data of this AbstractIonList implementation
|
||||
*
|
||||
* @param in input stream
|
||||
*/
|
||||
public void ionReadCustomData(InputStream in)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write custom data of this AbstractIonList implementation
|
||||
*
|
||||
* @param out output stream
|
||||
*/
|
||||
public void ionWriteCustomData(OutputStream out)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public abstract byte ionMark();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package mightypork.util.files.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable HashMap
|
||||
*
|
||||
* @author MightyPork
|
||||
* @param <V>
|
||||
*/
|
||||
public abstract class AbstractIonMap<V> extends LinkedHashMap<String, V> implements Ionizable {
|
||||
|
||||
@Override
|
||||
public V get(Object key)
|
||||
{
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public V put(String key, V value)
|
||||
{
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void ionRead(InputStream in) throws IonException
|
||||
{
|
||||
try {
|
||||
while (true) {
|
||||
final byte b = BinaryUtils.readByte(in);
|
||||
if (b == IonMarks.ENTRY) {
|
||||
final String key = BinaryUtils.readString(in);
|
||||
|
||||
final V value = (V) Ion.readObject(in);
|
||||
put(key, value);
|
||||
|
||||
} else if (b == IonMarks.END) {
|
||||
break;
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected mark in IonMap: " + Integer.toHexString(b));
|
||||
}
|
||||
}
|
||||
ionReadCustomData(in);
|
||||
} catch (final IOException e) {
|
||||
throw new IonException("Error reading ion map", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void ionWrite(OutputStream out) throws IonException
|
||||
{
|
||||
try {
|
||||
for (final java.util.Map.Entry<String, V> entry : entrySet()) {
|
||||
BinaryUtils.writeByte(out, IonMarks.ENTRY);
|
||||
BinaryUtils.writeString(out, entry.getKey());
|
||||
Ion.writeObject(out, entry.getValue());
|
||||
}
|
||||
BinaryUtils.writeByte(out, IonMarks.END);
|
||||
ionWriteCustomData(out);
|
||||
} catch (final IOException e) {
|
||||
throw new IonException("Error reading ion map", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read custom data of this AbstractIonMap implementation
|
||||
*
|
||||
* @param in input stream
|
||||
*/
|
||||
public void ionReadCustomData(InputStream in)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write custom data of this AbstractIonMap implementation
|
||||
*
|
||||
* @param out output stream
|
||||
*/
|
||||
public void ionWriteCustomData(OutputStream out)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte ionMark()
|
||||
{
|
||||
return IonMarks.MAP;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package mightypork.util.files.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
||||
/**
|
||||
* Utilities to store and load objects to streams.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class BinaryUtils {
|
||||
|
||||
private static ByteBuffer bi = ByteBuffer.allocate(Integer.SIZE / 8);
|
||||
private static ByteBuffer bd = ByteBuffer.allocate(Double.SIZE / 8);
|
||||
private static ByteBuffer bf = ByteBuffer.allocate(Float.SIZE / 8);
|
||||
private static ByteBuffer bc = ByteBuffer.allocate(Character.SIZE / 8);
|
||||
private static ByteBuffer bl = ByteBuffer.allocate(Long.SIZE / 8);
|
||||
private static ByteBuffer bs = ByteBuffer.allocate(Short.SIZE / 8);
|
||||
|
||||
private static byte[] ai = new byte[Integer.SIZE / 8];
|
||||
private static byte[] ad = new byte[Double.SIZE / 8];
|
||||
private static byte[] af = new byte[Float.SIZE / 8];
|
||||
private static byte[] ac = new byte[Character.SIZE / 8];
|
||||
private static byte[] al = new byte[Long.SIZE / 8];
|
||||
private static byte[] as = new byte[Short.SIZE / 8];
|
||||
|
||||
|
||||
// CONVERSIONS
|
||||
|
||||
public static byte[] getBytesBool(boolean bool)
|
||||
{
|
||||
return new byte[] { (byte) (bool ? 1 : 0) };
|
||||
}
|
||||
|
||||
|
||||
public static byte[] getBytesByte(byte num)
|
||||
{
|
||||
return new byte[] { num };
|
||||
}
|
||||
|
||||
|
||||
public static byte[] getBytesChar(char num)
|
||||
{
|
||||
bc.clear();
|
||||
bc.putChar(num);
|
||||
return bc.array();
|
||||
}
|
||||
|
||||
|
||||
public static byte[] getBytesShort(short num)
|
||||
{
|
||||
bs.clear();
|
||||
bs.putShort(num);
|
||||
return bs.array();
|
||||
}
|
||||
|
||||
|
||||
public static byte[] getBytesInt(int num)
|
||||
{
|
||||
bi.clear();
|
||||
bi.putInt(num);
|
||||
return bi.array();
|
||||
}
|
||||
|
||||
|
||||
public static byte[] getBytesLong(long num)
|
||||
{
|
||||
bl.clear();
|
||||
bl.putLong(num);
|
||||
return bl.array();
|
||||
}
|
||||
|
||||
|
||||
public static byte[] getBytesFloat(float num)
|
||||
{
|
||||
bf.clear();
|
||||
bf.putFloat(num);
|
||||
return bf.array();
|
||||
}
|
||||
|
||||
|
||||
public static byte[] getBytesDouble(double num)
|
||||
{
|
||||
bd.clear();
|
||||
bd.putDouble(num);
|
||||
return bd.array();
|
||||
}
|
||||
|
||||
|
||||
public static byte[] getBytesString(String str)
|
||||
{
|
||||
final char[] chars = str.toCharArray();
|
||||
|
||||
final ByteBuffer bstr = ByteBuffer.allocate((Character.SIZE / 8) * chars.length + (Character.SIZE / 8));
|
||||
for (final char c : chars) {
|
||||
bstr.putChar(c);
|
||||
}
|
||||
|
||||
bstr.putChar((char) 0);
|
||||
|
||||
return bstr.array();
|
||||
}
|
||||
|
||||
|
||||
public static void writeBoolean(OutputStream out, boolean num) throws IOException
|
||||
{
|
||||
out.write(getBytesBool(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeByte(OutputStream out, byte num) throws IOException
|
||||
{
|
||||
out.write(getBytesByte(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeChar(OutputStream out, char num) throws IOException
|
||||
{
|
||||
out.write(getBytesChar(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeShort(OutputStream out, short num) throws IOException
|
||||
{
|
||||
out.write(getBytesShort(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeInt(OutputStream out, int num) throws IOException
|
||||
{
|
||||
out.write(getBytesInt(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeLong(OutputStream out, long num) throws IOException
|
||||
{
|
||||
out.write(getBytesLong(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeFloat(OutputStream out, float num) throws IOException
|
||||
{
|
||||
out.write(getBytesFloat(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeDouble(OutputStream out, double num) throws IOException
|
||||
{
|
||||
out.write(getBytesDouble(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeString(OutputStream out, String str) throws IOException
|
||||
{
|
||||
out.write(getBytesString(str));
|
||||
}
|
||||
|
||||
|
||||
// READING
|
||||
|
||||
public static boolean readBoolean(InputStream in) throws IOException
|
||||
{
|
||||
return in.read() > 0;
|
||||
}
|
||||
|
||||
|
||||
public static byte readByte(InputStream in) throws IOException
|
||||
{
|
||||
return (byte) in.read();
|
||||
}
|
||||
|
||||
|
||||
public static char readChar(InputStream in) throws IOException
|
||||
{
|
||||
if (-1 == in.read(ac, 0, ac.length)) throw new IOException("End of stream.");
|
||||
final ByteBuffer buf = ByteBuffer.wrap(ac);
|
||||
return buf.getChar();
|
||||
}
|
||||
|
||||
|
||||
public static short readShort(InputStream in) throws IOException
|
||||
{
|
||||
if (-1 == in.read(as, 0, as.length)) throw new IOException("End of stream.");
|
||||
final ByteBuffer buf = ByteBuffer.wrap(as);
|
||||
return buf.getShort();
|
||||
}
|
||||
|
||||
|
||||
public static long readLong(InputStream in) throws IOException
|
||||
{
|
||||
if (-1 == in.read(al, 0, al.length)) throw new IOException("End of stream.");
|
||||
|
||||
final ByteBuffer buf = ByteBuffer.wrap(al);
|
||||
return buf.getLong();
|
||||
}
|
||||
|
||||
|
||||
public static int readInt(InputStream in) throws IOException
|
||||
{
|
||||
if (-1 == in.read(ai, 0, ai.length)) throw new IOException("End of stream.");
|
||||
final ByteBuffer buf = ByteBuffer.wrap(ai);
|
||||
return buf.getInt();
|
||||
}
|
||||
|
||||
|
||||
public static float readFloat(InputStream in) throws IOException
|
||||
{
|
||||
if (-1 == in.read(af, 0, af.length)) throw new IOException("End of stream.");
|
||||
final ByteBuffer buf = ByteBuffer.wrap(af);
|
||||
return buf.getFloat();
|
||||
}
|
||||
|
||||
|
||||
public static double readDouble(InputStream in) throws IOException
|
||||
{
|
||||
if (-1 == in.read(ad, 0, ad.length)) throw new IOException("End of stream.");
|
||||
final ByteBuffer buf = ByteBuffer.wrap(ad);
|
||||
return buf.getDouble();
|
||||
}
|
||||
|
||||
|
||||
public static String readString(InputStream in) throws IOException
|
||||
{
|
||||
String s = "";
|
||||
char c;
|
||||
while ((c = readChar(in)) > 0) {
|
||||
s += c;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
package mightypork.util.files.ion;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import mightypork.util.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Universal data storage system
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Ion {
|
||||
|
||||
/** Ionizables<Mark, Class> */
|
||||
private static Map<Byte, Class<?>> customIonizables = new HashMap<>();
|
||||
|
||||
// register default ionizables
|
||||
static {
|
||||
try {
|
||||
registerIonizable(IonMarks.MAP, IonMap.class);
|
||||
registerIonizable(IonMarks.LIST, IonList.class);
|
||||
} catch (final IonException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register new Ionizable for direct reconstructing.
|
||||
*
|
||||
* @param mark byte mark to be used, see {@link IonMarks} for reference.
|
||||
* @param objClass class of the registered Ionizable
|
||||
* @throws IonException
|
||||
*/
|
||||
public static void registerIonizable(byte mark, Class<?> objClass) throws IonException
|
||||
{
|
||||
if (customIonizables.containsKey(mark)) {
|
||||
throw new IonException("IonMark " + mark + " is already used.");
|
||||
}
|
||||
customIonizables.put(mark, objClass);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load Ion object from file.
|
||||
*
|
||||
* @param file file path
|
||||
* @return the loaded object
|
||||
* @throws IonException
|
||||
*/
|
||||
public static Object fromFile(String file) throws IonException
|
||||
{
|
||||
return fromFile(new File(file));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load Ion object from file.
|
||||
*
|
||||
* @param file file
|
||||
* @return the loaded object
|
||||
* @throws IonException on failure
|
||||
*/
|
||||
public static Object fromFile(File file) throws IonException
|
||||
{
|
||||
try (InputStream in = new FileInputStream(file)) {
|
||||
|
||||
final Object obj = fromStream(in);
|
||||
return obj;
|
||||
|
||||
} catch (final IOException e) {
|
||||
throw new IonException("Error loading ION file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load Ion object from stream.
|
||||
*
|
||||
* @param in input stream
|
||||
* @return the loaded object
|
||||
* @throws IonException
|
||||
*/
|
||||
public static Object fromStream(InputStream in) throws IonException
|
||||
{
|
||||
return readObject(in);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store Ion object to file.
|
||||
*
|
||||
* @param path file path
|
||||
* @param obj object to store
|
||||
* @throws IonException
|
||||
*/
|
||||
public static void toFile(String path, Object obj) throws IonException
|
||||
{
|
||||
toFile(new File(path), obj);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store Ion object to file.
|
||||
*
|
||||
* @param path file path
|
||||
* @param obj object to store
|
||||
* @throws IonException
|
||||
*/
|
||||
public static void toFile(File path, Object obj) throws IonException
|
||||
{
|
||||
try (OutputStream out = new FileOutputStream(path)) {
|
||||
final String f = path.toString();
|
||||
final File dir = new File(f.substring(0, f.lastIndexOf(File.separator)));
|
||||
|
||||
if (!dir.mkdirs()) throw new IOException("Could not create file.");
|
||||
|
||||
toStream(out, obj);
|
||||
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (final Exception e) {
|
||||
throw new IonException("Error writing to ION file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store Ion object to output stream.
|
||||
*
|
||||
* @param out output stream *
|
||||
* @param obj object to store
|
||||
* @throws IonException
|
||||
*/
|
||||
public static void toStream(OutputStream out, Object obj) throws IonException
|
||||
{
|
||||
writeObject(out, obj);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read single ionizable or primitive object from input stream
|
||||
*
|
||||
* @param in input stream
|
||||
* @return the loaded object
|
||||
* @throws IonException
|
||||
*/
|
||||
public static Object readObject(InputStream in) throws IonException
|
||||
{
|
||||
try {
|
||||
final int bi = in.read();
|
||||
if (bi == -1) throw new IonException("Unexpected end of stream.");
|
||||
final byte b = (byte) bi;
|
||||
if (customIonizables.containsKey(b)) {
|
||||
Ionizable ion;
|
||||
try {
|
||||
ion = ((Ionizable) customIonizables.get(b).newInstance());
|
||||
} catch (final InstantiationException e) {
|
||||
throw new IonException("Cound not instantiate " + customIonizables.get(b).getSimpleName(), e);
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new IonException("Cound not instantiate " + customIonizables.get(b).getSimpleName(), e);
|
||||
}
|
||||
ion.ionRead(in);
|
||||
return ion;
|
||||
}
|
||||
|
||||
switch (b) {
|
||||
case IonMarks.BOOLEAN:
|
||||
return BinaryUtils.readBoolean(in);
|
||||
case IonMarks.BYTE:
|
||||
return BinaryUtils.readByte(in);
|
||||
case IonMarks.CHAR:
|
||||
return BinaryUtils.readChar(in);
|
||||
case IonMarks.SHORT:
|
||||
return BinaryUtils.readShort(in);
|
||||
case IonMarks.INT:
|
||||
return BinaryUtils.readInt(in);
|
||||
case IonMarks.LONG:
|
||||
return BinaryUtils.readLong(in);
|
||||
case IonMarks.FLOAT:
|
||||
return BinaryUtils.readFloat(in);
|
||||
case IonMarks.DOUBLE:
|
||||
return BinaryUtils.readDouble(in);
|
||||
case IonMarks.STRING:
|
||||
final String s = BinaryUtils.readString(in);
|
||||
return s;
|
||||
default:
|
||||
throw new IonException("Invalid Ion mark " + Integer.toHexString(bi));
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new IonException("Error loading ION file: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write single ionizable or primitive object to output stream
|
||||
*
|
||||
* @param out output stream
|
||||
* @param obj stored object
|
||||
* @throws IonException
|
||||
*/
|
||||
public static void writeObject(OutputStream out, Object obj) throws IonException
|
||||
{
|
||||
try {
|
||||
if (obj instanceof Ionizable) {
|
||||
out.write(((Ionizable) obj).ionMark());
|
||||
((Ionizable) obj).ionWrite(out);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Boolean) {
|
||||
out.write(IonMarks.BOOLEAN);
|
||||
BinaryUtils.writeBoolean(out, (Boolean) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Byte) {
|
||||
out.write(IonMarks.BYTE);
|
||||
BinaryUtils.writeByte(out, (Byte) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Character) {
|
||||
out.write(IonMarks.CHAR);
|
||||
BinaryUtils.writeChar(out, (Character) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Short) {
|
||||
out.write(IonMarks.SHORT);
|
||||
BinaryUtils.writeShort(out, (Short) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Integer) {
|
||||
out.write(IonMarks.INT);
|
||||
BinaryUtils.writeInt(out, (Integer) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Long) {
|
||||
out.write(IonMarks.LONG);
|
||||
BinaryUtils.writeLong(out, (Long) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Float) {
|
||||
out.write(IonMarks.FLOAT);
|
||||
BinaryUtils.writeFloat(out, (Float) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Double) {
|
||||
out.write(IonMarks.DOUBLE);
|
||||
BinaryUtils.writeDouble(out, (Double) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof String) {
|
||||
out.write(IonMarks.STRING);
|
||||
BinaryUtils.writeString(out, (String) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IonException(Calc.cname(obj) + " can't be stored in Ion storage.");
|
||||
|
||||
} catch (final IOException e) {
|
||||
throw new IonException("Could not store " + obj, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package mightypork.util.files.ion;
|
||||
|
||||
|
||||
public class IonException extends Exception {
|
||||
|
||||
public IonException() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
public IonException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
|
||||
public IonException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public IonException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package mightypork.util.files.ion;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable Arraylist
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class IonList extends AbstractIonList<Object> {
|
||||
|
||||
public Ionizable getIonizable(int index) throws IonException
|
||||
{
|
||||
return (Ionizable) getCheckType(index, Ionizable.class);
|
||||
}
|
||||
|
||||
|
||||
public boolean getBoolean(int index) throws IonException
|
||||
{
|
||||
return (Boolean) getCheckType(index, Boolean.class);
|
||||
}
|
||||
|
||||
|
||||
public byte getByte(int index) throws IonException
|
||||
{
|
||||
return (Byte) getCheckType(index, Byte.class);
|
||||
}
|
||||
|
||||
|
||||
public char getChar(int index) throws IonException
|
||||
{
|
||||
return (Character) getCheckType(index, Character.class);
|
||||
}
|
||||
|
||||
|
||||
public short getShort(int index) throws IonException
|
||||
{
|
||||
return (Short) getCheckType(index, Short.class);
|
||||
}
|
||||
|
||||
|
||||
public int getInt(int index) throws IonException
|
||||
{
|
||||
return (Integer) getCheckType(index, Integer.class);
|
||||
}
|
||||
|
||||
|
||||
public long getLong(int index) throws IonException
|
||||
{
|
||||
return (Long) getCheckType(index, Long.class);
|
||||
}
|
||||
|
||||
|
||||
public float getFloat(int index) throws IonException
|
||||
{
|
||||
return (Float) getCheckType(index, Float.class);
|
||||
}
|
||||
|
||||
|
||||
public double getDouble(int index) throws IonException
|
||||
{
|
||||
return (Double) getCheckType(index, Double.class);
|
||||
}
|
||||
|
||||
|
||||
public String getString(int index) throws IonException
|
||||
{
|
||||
return (String) getCheckType(index, String.class);
|
||||
}
|
||||
|
||||
|
||||
public void addIonizable(Ionizable o) throws IonException
|
||||
{
|
||||
assertNotNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addBoolean(boolean o) throws IonException
|
||||
{
|
||||
assertNotNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addByte(byte o) throws IonException
|
||||
{
|
||||
assertNotNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addChar(char o) throws IonException
|
||||
{
|
||||
assertNotNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addShort(short o) throws IonException
|
||||
{
|
||||
assertNotNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addInt(int o) throws IonException
|
||||
{
|
||||
assertNotNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addLong(long o) throws IonException
|
||||
{
|
||||
assertNotNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addFloat(float o) throws IonException
|
||||
{
|
||||
assertNotNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addDouble(double o) throws IonException
|
||||
{
|
||||
assertNotNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addString(String o) throws IonException
|
||||
{
|
||||
assertNotNull(o);
|
||||
}
|
||||
|
||||
|
||||
public Object getCheckType(int index, Class<?> type) throws IonException
|
||||
{
|
||||
try {
|
||||
final Object o = super.get(index);
|
||||
if (o == null || !o.getClass().isAssignableFrom(type)) {
|
||||
throw new IonException("Incorrect object type");
|
||||
}
|
||||
return o;
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new IonException("Out of bounds");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void assertNotNull(Object o) throws IonException
|
||||
{
|
||||
if (o == null) throw new IonException("Cannot store null");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte ionMark()
|
||||
{
|
||||
return IonMarks.LIST;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package mightypork.util.files.ion;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable HashMap
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class IonMap extends AbstractIonMap<Object> {
|
||||
|
||||
public boolean getBoolean(String key)
|
||||
{
|
||||
return (Boolean) get(key);
|
||||
}
|
||||
|
||||
|
||||
public boolean getBool(String key)
|
||||
{
|
||||
return (Boolean) get(key);
|
||||
}
|
||||
|
||||
|
||||
public byte getByte(String key)
|
||||
{
|
||||
return (Byte) get(key);
|
||||
}
|
||||
|
||||
|
||||
public char getChar(String key)
|
||||
{
|
||||
return (Character) get(key);
|
||||
}
|
||||
|
||||
|
||||
public short getShort(String key)
|
||||
{
|
||||
return (Short) get(key);
|
||||
}
|
||||
|
||||
|
||||
public int getInt(String key)
|
||||
{
|
||||
return (Integer) get(key);
|
||||
}
|
||||
|
||||
|
||||
public long getLong(String key)
|
||||
{
|
||||
return (Long) get(key);
|
||||
}
|
||||
|
||||
|
||||
public float getFloat(String key)
|
||||
{
|
||||
return (Float) get(key);
|
||||
}
|
||||
|
||||
|
||||
public double getDouble(String key)
|
||||
{
|
||||
return (Double) get(key);
|
||||
}
|
||||
|
||||
|
||||
public String getString(String key)
|
||||
{
|
||||
return (String) get(key);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object get(Object arg0)
|
||||
{
|
||||
return super.get(arg0);
|
||||
}
|
||||
|
||||
|
||||
public void putBoolean(String key, boolean num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putBool(String key, boolean num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putByte(String key, int num)
|
||||
{
|
||||
put(key, (byte) num);
|
||||
}
|
||||
|
||||
|
||||
public void putChar(String key, char num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putCharacter(String key, char num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putShort(String key, int num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putInt(String key, int num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putInteger(String key, int num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putLong(String key, long num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putFloat(String key, double num)
|
||||
{
|
||||
put(key, (float) num);
|
||||
}
|
||||
|
||||
|
||||
public void putDouble(String key, double num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putString(String key, String num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte ionMark()
|
||||
{
|
||||
return IonMarks.MAP;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package mightypork.util.files.ion;
|
||||
|
||||
|
||||
/**
|
||||
* Byte marks used to structure data in Ion files.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class IonMarks {
|
||||
|
||||
/** Null value */
|
||||
public static final byte NULL = 0;
|
||||
|
||||
/** Boolean value */
|
||||
public static final byte BOOLEAN = 1;
|
||||
|
||||
/** Byte value */
|
||||
public static final byte BYTE = 2;
|
||||
|
||||
/** Character value */
|
||||
public static final byte CHAR = 3;
|
||||
|
||||
/** Short value */
|
||||
public static final byte SHORT = 4;
|
||||
|
||||
/** Integer value */
|
||||
public static final byte INT = 5;
|
||||
|
||||
/** Long value */
|
||||
public static final byte LONG = 6;
|
||||
|
||||
/** Float value */
|
||||
public static final byte FLOAT = 7;
|
||||
|
||||
/** Double value */
|
||||
public static final byte DOUBLE = 8;
|
||||
|
||||
/** String value */
|
||||
public static final byte STRING = 9;
|
||||
|
||||
/** List value (begin) - contains entries, ends with END */
|
||||
public static final byte LIST = 10;
|
||||
|
||||
/** Map value (begin) - contains entries, ends with END */
|
||||
public static final byte MAP = 11;
|
||||
|
||||
/**
|
||||
* List / Map entry<br>
|
||||
* In list directly followed by entry value. In map followed by (string) key
|
||||
* and the entry value.
|
||||
*/
|
||||
public static final byte ENTRY = 12;
|
||||
|
||||
/** End of List / Map */
|
||||
public static final byte END = 13;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package mightypork.util.files.ion;
|
||||
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
|
||||
/**
|
||||
* Object that can be saved to and loaded from Ion file.<br>
|
||||
* All classes implementing Ionizable must be registered to {@link Ion} using
|
||||
* Ion.registerIonizable(obj.class).
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public interface Ionizable {
|
||||
|
||||
/**
|
||||
* Load data from the input stream. Mark has already been read, begin
|
||||
* reading right after it.
|
||||
*
|
||||
* @param in input stream
|
||||
* @throws IonException
|
||||
*/
|
||||
public void ionRead(InputStream in) throws IonException;
|
||||
|
||||
|
||||
/**
|
||||
* Store data to output stream. mark has already been written, begin right
|
||||
* after it.
|
||||
*
|
||||
* @param out Output stream
|
||||
* @throws IonException
|
||||
*/
|
||||
public void ionWrite(OutputStream out) throws IonException;
|
||||
|
||||
|
||||
/**
|
||||
* Get Ion mark byte.
|
||||
*
|
||||
* @return Ion mark byte.
|
||||
*/
|
||||
public byte ionMark();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package mightypork.util.files.ion;
|
||||
|
||||
|
||||
/**
|
||||
* Optional ionizable
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public interface IonizableOptional extends Ionizable {
|
||||
|
||||
/**
|
||||
* Get if this ionizable should be saved to a list
|
||||
*
|
||||
* @return should save
|
||||
*/
|
||||
public boolean ionShouldSave();
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package mightypork.util.files.zip;
|
||||
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import mightypork.util.files.FileUtils;
|
||||
import mightypork.util.logging.Log;
|
||||
|
||||
|
||||
/**
|
||||
* Class for building a zip file
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ZipBuilder {
|
||||
|
||||
private final ZipOutputStream out;
|
||||
private final HashSet<String> included = new HashSet<>();
|
||||
|
||||
|
||||
/**
|
||||
* @param target target zip file
|
||||
* @throws IOException if the file is directory or cannot be created
|
||||
*/
|
||||
public ZipBuilder(File target) throws IOException {
|
||||
|
||||
if (!target.getParentFile().mkdirs()) throw new IOException("Could not create output directory.");
|
||||
|
||||
final FileOutputStream dest = new FileOutputStream(target);
|
||||
out = new ZipOutputStream(new BufferedOutputStream(dest));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add stream to a path
|
||||
*
|
||||
* @param path path
|
||||
* @param in stream
|
||||
* @throws IOException
|
||||
*/
|
||||
public void addStream(String path, InputStream in) throws IOException
|
||||
{
|
||||
path = preparePath(path);
|
||||
if (included.contains(path)) {
|
||||
Log.f3("Zip already contains file " + path + ", skipping.");
|
||||
return; // ignore
|
||||
}
|
||||
included.add(path);
|
||||
|
||||
out.putNextEntry(new ZipEntry(path));
|
||||
|
||||
FileUtils.copyStream(in, out);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add string as a file
|
||||
*
|
||||
* @param path path
|
||||
* @param text text to write
|
||||
* @throws IOException
|
||||
*/
|
||||
public void addString(String path, String text) throws IOException
|
||||
{
|
||||
path = preparePath(path);
|
||||
if (included.contains(path)) return; // ignore
|
||||
included.add(path);
|
||||
|
||||
out.putNextEntry(new ZipEntry(path));
|
||||
|
||||
try (InputStream in = FileUtils.stringToStream(text)) {
|
||||
FileUtils.copyStream(in, out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add resource obtained via FileUtils.getResource()
|
||||
*
|
||||
* @param path path
|
||||
* @param resPath resource path
|
||||
* @throws IOException
|
||||
*/
|
||||
public void addResource(String path, String resPath) throws IOException
|
||||
{
|
||||
path = preparePath(path);
|
||||
if (included.contains(path)) return; // ignore
|
||||
included.add(path);
|
||||
|
||||
out.putNextEntry(new ZipEntry(path));
|
||||
|
||||
try (InputStream in = FileUtils.getResource(resPath)) {
|
||||
FileUtils.copyStream(in, out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Normalize path
|
||||
*
|
||||
* @param path original path
|
||||
* @return normalized path
|
||||
*/
|
||||
private static String preparePath(String path)
|
||||
{
|
||||
path = path.replace("\\", "/");
|
||||
|
||||
if (path.charAt(0) == '/') path = path.substring(1);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Close the zip stream
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package mightypork.util.files.zip;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import mightypork.util.files.FileUtils;
|
||||
import mightypork.util.logging.Log;
|
||||
import mightypork.util.string.validation.StringFilter;
|
||||
|
||||
|
||||
/**
|
||||
* Utilities for manipulating zip files
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ZipUtils {
|
||||
|
||||
private static final int BUFFER_SIZE = 2048;
|
||||
|
||||
|
||||
/**
|
||||
* Extract zip file to target directory
|
||||
*
|
||||
* @param file zip file
|
||||
* @param outputDir target directory
|
||||
* @param filter string filter (will be used to test entry names (paths))
|
||||
* @return list of entries extracted (paths)
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<String> extractZip(File file, File outputDir, StringFilter filter) throws IOException
|
||||
{
|
||||
try (ZipFile zip = new ZipFile(file)) {
|
||||
return extractZip(zip, outputDir, filter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract zip file to target directory
|
||||
*
|
||||
* @param zip open zip file
|
||||
* @param outputDir target directory
|
||||
* @param filter string filter (will be used to test entry names (paths))
|
||||
* @return list of entries extracted (paths)
|
||||
* @throws IOException if the file)s) cannot be created
|
||||
*/
|
||||
public static List<String> extractZip(ZipFile zip, File outputDir, StringFilter filter) throws IOException
|
||||
{
|
||||
final ArrayList<String> files = new ArrayList<>();
|
||||
|
||||
if (!outputDir.mkdirs()) throw new IOException("Could not create output directory.");
|
||||
|
||||
final Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
|
||||
|
||||
// process each entry
|
||||
while (zipFileEntries.hasMoreElements()) {
|
||||
final ZipEntry entry = zipFileEntries.nextElement();
|
||||
|
||||
// parse filename and path
|
||||
final String entryPath = entry.getName();
|
||||
final File destFile = new File(outputDir, entryPath);
|
||||
final File destinationParent = destFile.getParentFile();
|
||||
|
||||
if (entry.isDirectory() || (filter != null && !filter.accept(entryPath))) continue;
|
||||
|
||||
// make sure directories exist
|
||||
if (!destinationParent.mkdirs()) throw new IOException("Could not create directory.");
|
||||
|
||||
if (!entry.isDirectory()) {
|
||||
extractZipEntry(zip, entry, destFile);
|
||||
files.add(entryPath);
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read zip entries and add their paths to a list
|
||||
*
|
||||
* @param zipFile open zip file
|
||||
* @return list of entry names
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static List<String> listZip(File zipFile) throws IOException
|
||||
{
|
||||
try (ZipFile zip = new ZipFile(zipFile)) {
|
||||
return listZip(zip);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read zip entries and add their paths to a list
|
||||
*
|
||||
* @param zip open zip file
|
||||
* @return list of entry names
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static List<String> listZip(ZipFile zip) throws IOException
|
||||
{
|
||||
final ArrayList<String> files = new ArrayList<>();
|
||||
|
||||
final Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
|
||||
|
||||
// process each entry
|
||||
while (zipFileEntries.hasMoreElements()) {
|
||||
final ZipEntry entry = zipFileEntries.nextElement();
|
||||
|
||||
if (!entry.isDirectory()) {
|
||||
files.add(entry.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract one zip entry to target file
|
||||
*
|
||||
* @param zip open zip file
|
||||
* @param entry entry from the zip file
|
||||
* @param destFile destination file ((NOT directory!)
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void extractZipEntry(ZipFile zip, ZipEntry entry, File destFile) throws IOException
|
||||
{
|
||||
if (!destFile.getParentFile().mkdirs()) throw new IOException("Could not create output directory.");
|
||||
|
||||
try (InputStream in = zip.getInputStream(entry); BufferedInputStream is = new BufferedInputStream(in); FileOutputStream fos = new FileOutputStream(destFile); BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) {
|
||||
|
||||
FileUtils.copyStream(is, dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load zip entry to String
|
||||
*
|
||||
* @param zip open zip file
|
||||
* @param entry entry from the zip file
|
||||
* @return loaded string
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static String zipEntryToString(ZipFile zip, ZipEntry entry) throws IOException
|
||||
{
|
||||
BufferedInputStream is = null;
|
||||
try {
|
||||
is = new BufferedInputStream(zip.getInputStream(entry));
|
||||
final String s = FileUtils.streamToString(is);
|
||||
return s;
|
||||
} finally {
|
||||
try {
|
||||
if (is != null) is.close();
|
||||
} catch (final IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean entryExists(File selectedFile, String string)
|
||||
{
|
||||
try (ZipFile zf = new ZipFile(selectedFile)) {
|
||||
return zf.getEntry(string) != null;
|
||||
} catch (final IOException | RuntimeException e) {
|
||||
Log.w("Error reading zip.", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user