In Java, java.io.File is one of the more frequently used low-level API objects. It also happens to be lacking in some basic functionality that we’ve all needed at some point, doesn’t provide different representations/API for files and directories and doesn’t throw fine-grained exceptions to differentiate between different types of error conditions (i.e., file already exists, directory not empty, invalid path, etc.)
At Carfey, we’ve been using our own File and Directory classes for years and have been enhancing their utility over time by adding new functionality as the need arises – File.moveToFile(File newFile), File.writeFromStream(InputStream is), Directory.listFilesRecursively(), Directory.emptyDirectory, etc.
Probably the only real pain you’d encounter using an alternate representation of java.io.File would be in interacting with other libraries that would need access to java.io.File objects. That is easily accommodated by exposing a getJavaFile() method on each class.
Free for Use
Attached you can find our classes and test code available under the MIT licence. It does depend on our open-source Date library which we spoke about here. If you’d rather skip the extra library, you can drop the getLastModifiedDate method from the File class.
Here are some of the highlights.
First, we use different classes to represent File and Directory. Rather than having to invoke isFile() and isDirectory(), we have the type to guide us. Our Directory class has some public constants, such as PATH_SEPARATOR which is equivalent to java.io.File.pathSeparator but uses standard constant naming convention. Also TEMP_DIR, which provides easy access to the temporary directory loaded using the System property java.io.tmpdir. We get typed Exceptions such as DirectoryNotCreatedException, InvalidDirectoryException, DirectoryNotDeletedException with descriptive messages when attempting to construct a Directory reference or physically create/delete a Directory but encountering problems. We can listFiles() or even listFilesRecursively() on a Directory. Want to completely empty a directory of files and subdirectories? Use emptyDirectory().
Our File class similarly gives us typed Exceptions (InvalidFileException, FileIOException, FileNotDeletedException, FileNotDeletedException) with descriptive messages on construction, create or delete of physical Files but encountering problems. We have very convenient move and write methods: moveToFile(File moveFile), writeFromStream(InputStream is), write(String contents). More convenience methods for getting a FileInputStream or FileOutputStream for the given File. Even a getDirectory() that will return one of our Directory objects representing the containing Directory.
Also in the bundle is our own IOUtil class that provides some necessary functions for our File and Directory classes. You happen to get some of its own helpful functionality including
public static final byte getBytes(InputStream is) public static long copyStream(InputStream src, OutputStream dest, int bufferSize, boolean flushEachRead, StreamListener... listeners) public static InputStream streamFromReader(Reader reader) public static String streamAsString(InputStream is, String encoding) public static <T extends Serializable> T cloneThroughSerialize(T t) public static Object deserialize(InputStream is) public static void serializeToOutputStream(Serializable ser, OutputStream os)
If you’re an Eclipse user, this brings up an interesting issue with resolving imports. If you add Carfey File and Directory to your project, you might want to avoid the annoyance of having to select the type of File you want to use. Eclipse allows you to customize how imports are done. If you’ve never done this before, check it out. You can use with other common names such as List, Util, StringUtil, etc.
Now when you “Organize Imports” with Ctrl+Shift+O, you won’t be prompted to choose which File you want.
Happy coding and if you find any bugs or have questions, leave a comment here.