/* * The MIT License (MIT) * * Copyright (c) 2014-2017 Sri Harsha Chilakapati * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.shc.silenceengine.io; import com.shc.silenceengine.core.SilenceEngine; import com.shc.silenceengine.core.SilenceException; import com.shc.silenceengine.utils.functional.Promise; import java.util.List; /** * <p> A FilePath is handle to a file that can be both an external file besides the JAR, or has an absolute path from * the root of the filesystem or is a resource packed in some JAR in the classpath. This class uses some tricks to find * the other properties of the resources like is that a directory or a file, it's size and also whether it exists. To * construct a FilePath, use either {@link #getExternalFile(String)} or {@link #getResourceFile(String)} methods.</p> * * <pre> * FilePath resource = FilePath.getResourceFile("resources/test.png"); * FilePath external = FilePath.getExternalFile("C:/Windows/explorer.exe"); * </pre> * * <p> Once you create a FilePath, you can derive new files if that is a directory, or you can get the parent path of a * path. You can also get a list of files in a directory denoted by a FilePath instance.</p> * * @author Sri Harsha Chilakapati */ public abstract class FilePath { /** * The UNIX style path separator ({@code /}) character. All the path strings are converted to this character when * creating an instance with the constructor. */ public static final char SEPARATOR = '/'; // The path of the resource, and the type protected String path; protected Type type; /** * Constructs an instance of FilePath by taking a path string, and a type. * * @param path The path string of the path * @param type The type of the file, one of {@link Type#EXTERNAL} or {@link Type#RESOURCE}. */ protected FilePath(String path, Type type) { if (path.startsWith("http") || path.startsWith("ftp")) this.path = path; else this.path = path.replaceAll("\\\\", "" + SEPARATOR).replaceAll("/+", "" + SEPARATOR).trim(); this.type = type; if (type == Type.RESOURCE && this.path.startsWith("/")) this.path = this.path.replaceFirst("/", ""); } /** * This method creates a {@link FilePath} instance that handles a path to an external file or directory. The path * can be either an absolute path, or a relative path. In case of the relative path, the files are expected to be in * project root folder when running through an IDE, and besides the JAR file when running from an executable JAR. * * @param path The path string that specifies the location of the file or directory. * * @return The FilePath instance that can be used to handle an external file. */ public static FilePath getExternalFile(String path) { return SilenceEngine.io.createExternalFilePath(path); } /** * This method creates a {@link FilePath} instance that handles a path to an internal file or directory, which is * present in the classpath. This path is always absolute, and starts with the root of classpath, which will be the * source directory in case of running from the IDE, or from the root of the JAR file when running from executable * JAR file. * * @param path The path string that specifies the location of the file or directory. * * @return The FilePath instance that can be used to handle a resource file, which will be packed into the JAR. */ public static FilePath getResourceFile(String path) { return SilenceEngine.io.createResourceFilePath(path); } /** * Checks if the file resolved to from this FilePath instance actually exists, or whether it is a hypothetical one. * * @return A promise that returns true if the file exists, or else False. */ public abstract Promise<Boolean> exists(); /** * Checks if this FilePath is a directory, or a file. * * @return A promise that returns true if a directory, else false. */ public abstract Promise<Boolean> isDirectory(); /** * Checks if this FilePath is a file, or a directory. * * @return A promise that returns true if a file, else false. */ public abstract Promise<Boolean> isFile(); /** * Copies the contents of this FilePath into another FilePath replacing the destination contents. * * @param path The destination FilePath where to copy the contents of this FilePath. * * @return A promise that can be used to know when the process completed * * @throws SilenceException If either this or the destination are directories, or if this path doesn't exist. */ public abstract Promise<Void> copyTo(FilePath path); /** * Moves this path to another path, overwriting the destination if something exists there already. * * @param path The destination path to move the contents of this path into. * * @return A promise that can be used to know when the process completed * * @throws SilenceException If either the source or the destinations are resources. */ public abstract Promise<Void> moveTo(FilePath path); public abstract Promise<Void> mkdirs(); public abstract Promise<Void> createFile(); /** * Gets a new FilePath that represents the parent directory of this FilePath. * * @return The parent directory of this FilePath. */ public FilePath getParent() { String[] parts = path.split("" + SEPARATOR); String path = parts[0]; for (int i = 1; i < parts.length - 1; i++) path += SEPARATOR + parts[i] + SEPARATOR; return type == Type.RESOURCE ? SilenceEngine.io.createResourceFilePath(path + SEPARATOR) : SilenceEngine.io.createExternalFilePath(path + SEPARATOR); } /** * Gets the path string of this FilePath instance. This path will be the same as the one that you used to create * this object, except that it will be changed to use the separator defined by {@link #SEPARATOR} which works in all * the platforms. * * @return The path string of this FilePath instance. */ public String getPath() { if (path.trim().endsWith("" + SEPARATOR)) return path.trim().substring(0, path.lastIndexOf(SEPARATOR)); return path; } /** * Gets a new FilePath that represents a file that is a child of this path. * * @param path The path of the child, relative to this path. * * @return The FilePath instance of the child. * * @throws SilenceException If this path is not a directory, or if it does not exist. */ public FilePath getChild(String path) { return type == Type.RESOURCE ? SilenceEngine.io.createResourceFilePath(this.path + SEPARATOR + path) : SilenceEngine.io.createExternalFilePath(this.path + SEPARATOR + path); } /** * Gets the absolute path string of this FilePath instance. The root of the path will be the root of the filesystem * if this path is an external file, or the root of the classpath if this is a JAR file resource. * * @return The absolute path string of this FilePath instance. */ public String getAbsolutePath() { return path; } /** * Deletes the file resolved by this FilePath instance. * * @return A promise returning true if the attempt is successful or false otherwise. * * @throws SilenceException If this file is a resource, or if this doesn't exist. */ public abstract Promise<Boolean> delete(); /** * Marks this FilePath to be deleted on JVM exit. * * @throws SilenceException If this path is a resource. */ public abstract void deleteOnExit(); /** * Returns the extension of the file represented by this FilePath. * * @return The extension of the file without the leading dot. */ public String getExtension() { String[] parts = getPath().split("\\.(?=[^\\.]+$)"); return parts.length > 1 ? parts[1] : ""; } /** * Returns the file name of the file represented by this FilePath. * * @return The filename of this FilePath, along with the extension. */ public String getName() { String path = this.path; if (path.endsWith("" + SEPARATOR)) path = path.substring(0, path.length() - 1); return path.substring(path.lastIndexOf(SEPARATOR) + 1); } /** * Returns the file name of this file represented by this FilePath without it's extension. * * @return The filename of this FilePath, without the extension. */ public String getNameWithoutExtension() { return getName().replaceAll("\\." + getExtension(), ""); } /** * Returns the file size of this path in number of bytes. In case of a directory, the size will be the sum of the * sizes of all it's children. If this file path does not exist, a value of {@code -1} is returned. * * @return A promise returning the size of the file in bytes. */ public abstract Promise<Long> sizeInBytes(); /** * Returns a list of FilePaths for the children of this directory. * * @return A promise returning an un-modifiable list of FilePaths for all the children of this directory. * * @throws SilenceException If this is not a directory, of if this doesn't exist. */ public abstract Promise<List<FilePath>> listFiles(); @Override public int hashCode() { int result = getPath().hashCode(); result = 31 * result + getType().hashCode(); return result; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FilePath filePath = (FilePath) o; return getPath().equals(filePath.getPath()) && getType() == filePath.getType(); } @Override public String toString() { return "FilePath{" + "path='" + getPath() + '\'' + ", name='" + getName() + "'" + ", extension='" + getExtension() + "'" + ", type=" + type + ", isDirectory=" + isDirectory() + ", exists=" + exists() + ", size=" + sizeInBytes() + '}'; } /** * Gets the type of file this FilePath instance resolves to. * * @return One of {@link Type#EXTERNAL} or {@link Type#RESOURCE}. */ public Type getType() { return type; } public enum Type { RESOURCE, EXTERNAL } }