package nbtool.nio; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.LinkedList; import java.util.List; import nbtool.data.log.Log; import nbtool.data.log.LogReference; import nbtool.util.Debug; public class FileIO { private static final Debug.DebugSettings debug = Debug.createSettings(Debug.WARN); public static Path[] getContentsOf(Path directory) { List<Path> pathes = new LinkedList<>(); try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory)) { for (Path path : directoryStream) { pathes.add(path); } } catch (IOException ex) { Debug.error("could not list directory: ", directory); ex.printStackTrace(); return null; } return pathes.toArray(new Path[0]); } public static Path[] getContentsOf(Path directory, int limit) { assert(Files.isDirectory(directory)); List<Path> pathes = new LinkedList<>(); try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory)) { for (Path path : directoryStream) { pathes.add(path); if (pathes.size() == limit) { debug.info("traversal of dir:{%s} stopping at %d files!", directory, limit); break; } } } catch (IOException ex) { Debug.error("could not list directory: ", directory); ex.printStackTrace(); return null; } return pathes.toArray(new Path[0]); } public static Path getPath(String first, String ... parts) { return FileSystems.getDefault().getPath(first, parts); } public static void sizeCheck(Path containsLog) throws IOException { long fsize = Files.size(containsLog); if (fsize < Log.MINIMUM_LOG_SIZE) { Debug.error("asked to read Log file below minimum size: %d < %d @ %s", fsize, Log.MINIMUM_LOG_SIZE, containsLog.toString()); throw new RuntimeException("Log below minimum size threshold"); } } public static BufferedInputStream inputStreamFrom(Path path) throws IOException { return new BufferedInputStream(Files.newInputStream(path, StandardOpenOption.READ)); } public static BufferedOutputStream outputStreamTo(Path path) throws IOException { return new BufferedOutputStream(Files.newOutputStream(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)); } public static Log readLogFromPath(Path path) throws IOException { sizeCheck(path); BufferedInputStream is = inputStreamFrom(path); Log ret = Log.parseFromStream(is); is.close(); return ret; } public static LogReference readRefFromPath(Path path) throws IOException { sizeCheck(path); return LogReference.referenceFromFile(path); } public static void writeLogToPath(Path path, Log log) throws IOException { BufferedOutputStream os = outputStreamTo(path); log.writeTo(os); os.close(); } public static Log[] readAllLogsFromPath(Path path) throws IOException { if (!isValidLogFolder(path)) { Debug.error("cannot read multiple logs from invalid path: ", path.toString()); return null; } LinkedList<Log> found = new LinkedList<>(); DirectoryStream<Path> dirStream = Files.newDirectoryStream(path, "*.{nblog}"); for (Path file : dirStream) { found.add(readLogFromPath(file)); } return found.toArray(new Log[0]); } public static LogReference[] readAllRefsFromPath(Path path, boolean quick) throws IOException { if (!isValidLogFolder(path)) { Debug.error("cannot read multiple refs from invalid path: ", path.toString()); return null; } LinkedList<LogReference> found = new LinkedList<>(); DirectoryStream<Path> dirStream = Files.newDirectoryStream(path, "*.{nblog}"); for (Path file : dirStream) { debug.info("reading %s", file); sizeCheck(file); if (quick) { found.add(LogReference.quickReferenceFromFile(file)); } else { found.add(LogReference.referenceFromFile(file)); } } return found.toArray(new LogReference[0]); } public static String logDescriptionAt(Path path) throws IOException { sizeCheck(path); DataInputStream dis = new DataInputStream(inputStreamFrom(path)); int descSize = dis.readInt(); byte[] desc = new byte[descSize]; dis.readFully(desc); dis.close(); String ret = new String(desc); ret = ret.replace("\0", ""); return ret; } public static boolean isValidLogFolder(Path path) { if(path == null || path.getNameCount() < 1) { Debug.error( "path string null or empty!\n"); return false; } if (!Files.exists(path)) { Debug.error( "file does not exist! %s\n", path.toString()); return false; } if (!Files.isDirectory(path)) { Debug.error( "file is not a directory! %s\n", path.toString()); return false; } if (!Files.isReadable(path) || !Files.isWritable(path)) { Debug.error( "permissions errors for file %s!\n", path.toString()); return false; } return true; } /* asynchronous file writing thread */ public static void queueWriteTask(Path path, byte[] data) { assert(path != null && data != null); assert(!Files.exists(path) || Files.isRegularFile(path)); addTaskToQueue(new Task(path, data)); } public static void addTaskToQueue(Task t) { synchronized(writer.queue) { writer.queue.addLast(t); writer.queue.notify(); } } public static class Task { protected byte[] data; protected Path path; protected Task(Path p, byte[] d) { this.path = p; this.data = d; } public void executeWrite() throws IOException { Files.write(path, data, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } } public static void _NBL_REQUIRED_START_() { Debug.warn("starting FileWriter thread..."); fileWriteThread.start(); } private static final FileWriter writer = new FileWriter(); private static final Thread fileWriteThread = new Thread(writer); private static class FileWriter implements Runnable { protected final LinkedList<Task> queue = new LinkedList<>(); private void loop() { for (;;) { Task task = null; synchronized(queue) { for(;;) { if (queue.isEmpty()) { try { queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { task = queue.removeFirst(); break; } } } try { task.executeWrite(); } catch (IOException e) { Debug.error("error writing file to path %s", task.path.toString()); e.printStackTrace(); } } } @Override public void run() { Debug.warn("FileWriter thread running."); try { loop(); } catch (Exception e) { Debug.error("FileWriter thread ending..."); e.printStackTrace(); throw e; } } } }