Moved general purpose stuff into util, renamed utils->util

This commit is contained in:
ondra
2014-04-16 14:32:56 +02:00
parent e58156b7ee
commit 6843e746bc
208 changed files with 656 additions and 629 deletions
+149
View File
@@ -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;
}
}
}
+505
View File
@@ -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;
}
}
}
+149
View File
@@ -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;
}
}
+277
View File
@@ -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);
}
}
+157
View File
@@ -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;
}
}
+156
View File
@@ -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();
}
}
+179
View File
@@ -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;
}
}
}