001 // Copyright 2006-2008 Regents of the University of California. May be used 002 // under the terms of the revised BSD license. See LICENSING for details. 003 package org.joe_e.file; 004 005 import java.io.File; 006 import java.io.FileInputStream; 007 import java.io.FileNotFoundException; 008 import java.io.FileOutputStream; 009 import java.io.IOException; 010 import java.io.InputStream; 011 import java.io.OutputStream; 012 import java.util.Arrays; 013 import java.util.Comparator; 014 015 import org.joe_e.array.ConstArray; 016 017 /** 018 * {@link File} construction. This provides a capability-safe API for access to 019 * files. A File object grants read and write access to a file or directory. 020 * Due to limitations in Java, all file references are to textual file names, not 021 * file descriptors. Multiple operations on a File may thus apply to different 022 * incarnations of the file. 023 */ 024 public final class Filesystem { 025 026 private Filesystem() {} 027 028 /* 029 * The safety of this API relies on all File constructors being tamed away. 030 */ 031 032 /** 033 * Produce a File capability for a file contained in a folder. The returned 034 * object is just a handle; the underlying file may not yet exist. 035 * @param folder containing folder 036 * @param child a single filename component, not a relative path 037 * @return a capability for the requested file 038 * @throws IllegalArgumentException if <code>folder</code> is null or 039 * the empty path 040 */ 041 static public File file(final File folder, final String child) 042 throws InvalidFilenameException { 043 // With Java's File(File, String) constructor, either of these can give 044 // access to all files on system; null is ambiently available. 045 if (folder == null || folder.getPath().equals("")) { 046 throw new IllegalArgumentException(); 047 } 048 checkName(child); 049 return new File(folder, child); 050 } 051 052 /** 053 * Vets a filename. Checks that the argument would be interpreted as a 054 * file name rather than as a path or relative directory specifier 055 * @param name a single filename component, not a relative path 056 * @throws InvalidFilenameException <code>name</code> is rejected\ 057 */ 058 static public void checkName(final String name) 059 throws InvalidFilenameException { 060 // Check for path operation names. 061 if (name.equals("") || name.equals(".") || name.equals("..")) { 062 throw new InvalidFilenameException(); 063 } 064 065 // Check for path separator char. 066 if (name.indexOf(File.separatorChar) != -1) { 067 throw new InvalidFilenameException(); 068 } 069 // '/' works as a separator on Windows, even though it isn't the 070 // platform separator character 071 if ('/' != File.separatorChar && name.indexOf('/') != -1) { 072 throw new InvalidFilenameException(); 073 } 074 } 075 076 /** 077 * List the contents of a directory. 078 * @param dir directory to list 079 * @return directory entries, sorted alphabetically 080 * @throws IOException <code>dir</code> is not a directory, or an I/O error 081 */ 082 static public ConstArray<File> list(final File dir) throws IOException { 083 final File[] contents = dir.listFiles(); 084 if (contents == null) { 085 throw new IOException(); 086 } 087 Arrays.sort(contents, new Comparator<File>() { 088 public int compare(final File a, final File b) { 089 return a.getName().compareTo(b.getName()); 090 } 091 }); 092 return ConstArray.array(contents); 093 } 094 095 /** 096 * Gets the length of a file 097 * @param file file to stat 098 * @return the length of the file in bytes 099 * @throws FileNotFoundException <code>file</code> not found 100 */ 101 static public long length(final File file) 102 throws FileNotFoundException { 103 if (!file.isFile()) { 104 throw new FileNotFoundException(); 105 } 106 return file.length(); 107 } 108 109 /** 110 * Opens an existing file for reading. 111 * @param file file to open 112 * @return opened input stream 113 * @throws FileNotFoundException <code>file</code> not found 114 */ 115 static public InputStream read(final File file) 116 throws FileNotFoundException { 117 if (!file.isFile()) { 118 throw new FileNotFoundException(); 119 } 120 return new FileInputStream(file); 121 } 122 123 /** 124 * Creates a file for writing. 125 * @param file file to create 126 * @return opened output stream 127 * @throws IOException <code>file</code> could not be created 128 */ 129 static public OutputStream writeNew(final File file) throws IOException { 130 if (!file.createNewFile()) { 131 throw new IOException(); 132 } 133 return new FileOutputStream(file); 134 } 135 }