package loon.core; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.io.Writer; import loon.LSystem; import loon.Files.FileType; public class FileHandle { protected File file; protected FileType type; protected FileHandle() { } public FileHandle(String fileName) { this.file = new File(fileName); this.type = FileType.Absolute; } public FileHandle(File file) { this.file = file; this.type = FileType.Absolute; } protected FileHandle(String fileName, FileType type) { this.type = type; file = new File(fileName); } protected FileHandle(File file, FileType type) { this.file = file; this.type = type; } public String path() { return file.getPath().replace('\\', '/'); } public String name() { return file.getName(); } public String extension() { String name = file.getName(); int dotIndex = name.lastIndexOf('.'); if (dotIndex == -1){ return ""; } return name.substring(dotIndex + 1); } public String nameWithoutExtension() { String name = file.getName(); int dotIndex = name.lastIndexOf('.'); if (dotIndex == -1){ return name; } return name.substring(0, dotIndex); } public String pathWithoutExtension() { String path = file.getPath().replace('\\', '/'); int dotIndex = path.lastIndexOf('.'); if (dotIndex == -1){ return path; } return path.substring(0, dotIndex); } public FileType type() { return type; } public File file() { if (type == FileType.External) { return new File(LSystem.files().getExternalStoragePath(), file.getPath()); } return file; } public InputStream read() { if (type == FileType.Classpath || (type == FileType.Internal && !file().exists()) || (type == FileType.Local && !file().exists())) { InputStream input = FileHandle.class.getResourceAsStream("/" + file.getPath().replace('\\', '/')); if (input == null) throw new RuntimeException("File not found: " + file + " (" + type + ")"); return input; } try { return new FileInputStream(file()); } catch (Exception ex) { if (file().isDirectory()) throw new RuntimeException( "Cannot open a stream to a directory: " + file + " (" + type + ")", ex); throw new RuntimeException("Error reading file: " + file + " (" + type + ")", ex); } } public BufferedInputStream read(int bufferSize) { return new BufferedInputStream(read(), bufferSize); } public Reader reader() { return new InputStreamReader(read()); } public Reader reader(String charset) { InputStream stream = read(); try { return new InputStreamReader(stream, charset); } catch (UnsupportedEncodingException ex) { LSystem.close(stream); throw new RuntimeException("Error reading file: " + this, ex); } } public BufferedReader reader(int bufferSize) { return new BufferedReader(new InputStreamReader(read()), bufferSize); } public BufferedReader reader(int bufferSize, String charset) { try { return new BufferedReader(new InputStreamReader(read(), charset), bufferSize); } catch (UnsupportedEncodingException ex) { throw new RuntimeException("Error reading file: " + this, ex); } } public String readString() { return readString(null); } public String readString(String charset) { StringBuilder output = new StringBuilder(estimateLength()); InputStreamReader reader = null; try { if (charset == null) reader = new InputStreamReader(read()); else reader = new InputStreamReader(read(), charset); char[] buffer = new char[256]; while (true) { int length = reader.read(buffer); if (length == -1) break; output.append(buffer, 0, length); } } catch (IOException ex) { throw new RuntimeException("Error reading layout file: " + this, ex); } finally { LSystem.close(reader); } return output.toString(); } public byte[] readBytes() { InputStream input = read(); try { return LSystem.copyStreamToByteArray(input, estimateLength()); } catch (IOException ex) { throw new RuntimeException("Error reading file: " + this, ex); } finally { LSystem.close(input); } } private int estimateLength() { int length = (int) length(); return length != 0 ? length : 512; } public int readBytes(byte[] bytes, int offset, int size) { InputStream input = read(); int position = 0; try { while (true) { int count = input.read(bytes, offset + position, size - position); if (count <= 0) break; position += count; } } catch (IOException ex) { throw new RuntimeException("Error reading file: " + this, ex); } finally { LSystem.close(input); } return position - offset; } public OutputStream write(boolean append) { if (type == FileType.Classpath) throw new RuntimeException("Cannot write to a classpath file: " + file); if (type == FileType.Internal) throw new RuntimeException("Cannot write to an internal file: " + file); parent().mkdirs(); try { return new FileOutputStream(file(), append); } catch (Exception ex) { if (file().isDirectory()) throw new RuntimeException( "Cannot open a stream to a directory: " + file + " (" + type + ")", ex); throw new RuntimeException("Error writing file: " + file + " (" + type + ")", ex); } } public OutputStream write(boolean append, int bufferSize) { return new BufferedOutputStream(write(append), bufferSize); } public void write(InputStream input, boolean append) { OutputStream output = null; try { output = write(append); LSystem.copyStream(input, output, 4096); } catch (Exception ex) { throw new RuntimeException("Error stream writing to file: " + file + " (" + type + ")", ex); } finally { LSystem.close(input); LSystem.close(output); } } public Writer writer(boolean append) { return writer(append, null); } public Writer writer(boolean append, String charset) { if (type == FileType.Classpath) throw new RuntimeException("Cannot write to a classpath file: " + file); if (type == FileType.Internal) throw new RuntimeException("Cannot write to an internal file: " + file); parent().mkdirs(); try { FileOutputStream output = new FileOutputStream(file(), append); if (charset == null) return new OutputStreamWriter(output); else return new OutputStreamWriter(output, charset); } catch (IOException ex) { if (file().isDirectory()) throw new RuntimeException( "Cannot open a stream to a directory: " + file + " (" + type + ")", ex); throw new RuntimeException("Error writing file: " + file + " (" + type + ")", ex); } } public void writeString(String string, boolean append) { writeString(string, append, null); } public void writeString(String string, boolean append, String charset) { Writer writer = null; try { writer = writer(append, charset); writer.write(string); } catch (Exception ex) { throw new RuntimeException("Error writing file: " + file + " (" + type + ")", ex); } finally { LSystem.close(writer); } } public void writeBytes(byte[] bytes, boolean append) { OutputStream output = write(append); try { output.write(bytes); } catch (IOException ex) { throw new RuntimeException("Error writing file: " + file + " (" + type + ")", ex); } finally { LSystem.close(output); } } public void writeBytes(byte[] bytes, int offset, int length, boolean append) { OutputStream output = write(append); try { output.write(bytes, offset, length); } catch (IOException ex) { throw new RuntimeException("Error writing file: " + file + " (" + type + ")", ex); } finally { LSystem.close(output); } } public FileHandle[] list() { if (type == FileType.Classpath) throw new RuntimeException("Cannot list a classpath directory: " + file); String[] relativePaths = file().list(); if (relativePaths == null) { return new FileHandle[0]; } FileHandle[] handles = new FileHandle[relativePaths.length]; for (int i = 0, n = relativePaths.length; i < n; i++) { handles[i] = child(relativePaths[i]); } return handles; } public FileHandle[] list(FileFilter filter) { if (type == FileType.Classpath) throw new RuntimeException("Cannot list a classpath directory: " + file); File file = file(); String[] relativePaths = file.list(); if (relativePaths == null) { return new FileHandle[0]; } FileHandle[] handles = new FileHandle[relativePaths.length]; int count = 0; for (int i = 0, n = relativePaths.length; i < n; i++) { String path = relativePaths[i]; FileHandle child = child(path); if (!filter.accept(child.file())) { continue; } handles[count] = child; count++; } if (count < relativePaths.length) { FileHandle[] newHandles = new FileHandle[count]; System.arraycopy(handles, 0, newHandles, 0, count); handles = newHandles; } return handles; } public FileHandle[] list(FilenameFilter filter) { if (type == FileType.Classpath) throw new RuntimeException("Cannot list a classpath directory: " + file); File file = file(); String[] relativePaths = file.list(); if (relativePaths == null) return new FileHandle[0]; FileHandle[] handles = new FileHandle[relativePaths.length]; int count = 0; for (int i = 0, n = relativePaths.length; i < n; i++) { String path = relativePaths[i]; if (!filter.accept(file, path)) continue; handles[count] = child(path); count++; } if (count < relativePaths.length) { FileHandle[] newHandles = new FileHandle[count]; System.arraycopy(handles, 0, newHandles, 0, count); handles = newHandles; } return handles; } public FileHandle[] list(String suffix) { if (type == FileType.Classpath) throw new RuntimeException("Cannot list a classpath directory: " + file); String[] relativePaths = file().list(); if (relativePaths == null) return new FileHandle[0]; FileHandle[] handles = new FileHandle[relativePaths.length]; int count = 0; for (int i = 0, n = relativePaths.length; i < n; i++) { String path = relativePaths[i]; if (!path.endsWith(suffix)) continue; handles[count] = child(path); count++; } if (count < relativePaths.length) { FileHandle[] newHandles = new FileHandle[count]; System.arraycopy(handles, 0, newHandles, 0, count); handles = newHandles; } return handles; } public boolean isDirectory() { if (type == FileType.Classpath) return false; return file().isDirectory(); } public FileHandle child(String name) { if (file.getPath().length() == 0) return new FileHandle(new File(name), type); return new FileHandle(new File(file, name), type); } public FileHandle sibling(String name) { if (file.getPath().length() == 0) throw new RuntimeException("Cannot get the sibling of the root."); return new FileHandle(new File(file.getParent(), name), type); } public FileHandle parent() { File parent = file.getParentFile(); if (parent == null) { if (type == FileType.Absolute) parent = new File("/"); else parent = new File(""); } return new FileHandle(parent, type); } public void mkdirs() { if (type == FileType.Classpath) throw new RuntimeException("Cannot mkdirs with a classpath file: " + file); if (type == FileType.Internal) throw new RuntimeException("Cannot mkdirs with an internal file: " + file); file().mkdirs(); } public boolean exists() { switch (type) { case Internal: if (file().exists()) return true; case Classpath: return FileHandle.class.getResource("/" + file.getPath().replace('\\', '/')) != null; default: break; } return file().exists(); } public boolean delete() { if (type == FileType.Classpath) { throw new RuntimeException("Cannot delete a classpath file: " + file); } if (type == FileType.Internal) { throw new RuntimeException("Cannot delete an internal file: " + file); } return file().delete(); } public boolean deleteDirectory() { if (type == FileType.Classpath) { throw new RuntimeException("Cannot delete a classpath file: " + file); } if (type == FileType.Internal) { throw new RuntimeException("Cannot delete an internal file: " + file); } return deleteDirectory(file()); } public void emptyDirectory() { emptyDirectory(false); } public void emptyDirectory(boolean preserveTree) { if (type == FileType.Classpath) throw new RuntimeException("Cannot delete a classpath file: " + file); if (type == FileType.Internal) throw new RuntimeException("Cannot delete an internal file: " + file); emptyDirectory(file(), preserveTree); } public void copyTo(FileHandle dest) { boolean sourceDir = isDirectory(); if (!sourceDir) { if (dest.isDirectory()) { dest = dest.child(name()); } copyFile(this, dest); return; } if (dest.exists()) { if (!dest.isDirectory()) { throw new RuntimeException( "Destination exists but is not a directory: " + dest); } } else { dest.mkdirs(); if (!dest.isDirectory()) { throw new RuntimeException( "Destination directory cannot be created: " + dest); } } if (!sourceDir) { dest = dest.child(name()); } copyDirectory(this, dest); } public void moveTo(FileHandle dest) { if (type == FileType.Classpath) { throw new RuntimeException("Cannot move a classpath file: " + file); } if (type == FileType.Internal) { throw new RuntimeException("Cannot move an internal file: " + file); } copyTo(dest); delete(); if (exists() && isDirectory()) { deleteDirectory(); } } public long length() { if (type == FileType.Classpath || (type == FileType.Internal && !file.exists())) { InputStream input = read(); try { return input.available(); } catch (Exception ignored) { } finally { LSystem.close(input); } return 0; } return file().length(); } public long lastModified() { return file().lastModified(); } @Override public boolean equals(Object obj) { if (!(obj instanceof FileHandle)) { return false; } FileHandle other = (FileHandle) obj; return type == other.type && path().equals(other.path()); } @Override public int hashCode() { int hash = 1; hash = hash * 37 + type.hashCode(); hash = hash * 67 + path().hashCode(); return hash; } public String toString() { return file.getPath().replace('\\', '/'); } static public FileHandle tempFile(String prefix) { try { return new FileHandle(File.createTempFile(prefix, null)); } catch (IOException ex) { throw new RuntimeException("Unable to create temp file.", ex); } } static public FileHandle tempDirectory(String prefix) { try { File file = File.createTempFile(prefix, null); if (!file.delete()) { throw new IOException("Unable to delete temp file: " + file); } if (!file.mkdir()) { throw new IOException("Unable to create temp directory: " + file); } return new FileHandle(file); } catch (IOException ex) { throw new RuntimeException("Unable to create temp file.", ex); } } static private void emptyDirectory(File file, boolean preserveTree) { if (file.exists()) { File[] files = file.listFiles(); if (files != null) { for (int i = 0, n = files.length; i < n; i++) { if (!files[i].isDirectory()) { files[i].delete(); } else if (preserveTree) { emptyDirectory(files[i], true); } else { deleteDirectory(files[i]); } } } } } static private boolean deleteDirectory(File file) { emptyDirectory(file, false); return file.delete(); } static private void copyFile(FileHandle source, FileHandle dest) { try { dest.write(source.read(), false); } catch (Exception ex) { throw new RuntimeException("Error copying source file: " + source.file + " (" + source.type + ")\n" // + "To destination: " + dest.file + " (" + dest.type + ")", ex); } } static private void copyDirectory(FileHandle sourceDir, FileHandle destDir) { destDir.mkdirs(); FileHandle[] files = sourceDir.list(); for (int i = 0, n = files.length; i < n; i++) { FileHandle srcFile = files[i]; FileHandle destFile = destDir.child(srcFile.name()); if (srcFile.isDirectory()) { copyDirectory(srcFile, destFile); } else { copyFile(srcFile, destFile); } } } }