The File class

Bruce Eckel’s Thinking in Java Contents | Prev | Next

A
directory lister

Suppose
you’d like to see a directory listing. The
File
object can be listed in two ways. If you call
list( )
with no arguments, you’ll get the full list that the
File
object contains. However, if you want a restricted list, for example, all of
the files with an extension of
.java,
then you use a “directory filter,” which is a class that tells how
to select the
File
objects for display.

Here’s
the code for the example: (See page
97
if you have trouble executing this program.)

//: DirList.java
// Displays directory listing
package c10;
import java.io.*;
 
public class DirList {
  public static void main(String[] args) {
    try {
      File path = new File(".");
      String[] list;
      if(args.length == 0)
        list = path.list();
      else
        list = path.list(new DirFilter(args[0]));
      for(int i = 0; i < list.length; i++)
        System.out.println(list[i]);
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
}
 
class DirFilter implements FilenameFilter {
  String afn;
  DirFilter(String afn) { this.afn = afn; }
  public boolean accept(File dir, String name) {
    // Strip path information:
    String f = new File(name).getName();
    return f.indexOf(afn) != -1;
  }
} ///:~ 

The
DirFilter
class “implements” the
interface
FilenameFilter.
(Interfaces were covered in Chapter 7.) It’s useful to see how simple the
FilenameFilter
interface
is:

public interface FilenameFilter {
  boolean accept(File dir, String name);
}

It
says that all that this type of object does is provide a method called
accept( ).
The whole reason behind the creation of this class is to provide the
accept( )
method to the
list( )
method so that
list( )
can
call
back

accept( )
to determine which file names should be included in the list. Thus, this
technique is often referred to as a callback
or sometimes a
functor
(that is,
DirFilter
is a functor because its only job is to hold a method). Because
list( )
takes a
FilenameFilter
object as its argument, it means that you can pass an object of any class that
implements
FilenameFilter
to choose (even at run-time) how the
list( )
method will behave. The purpose of a callback is to provide flexibility in the
behavior of code.

DirFilter
shows that just because an
interface
contains only a set of methods, you’re not restricted to writing only
those methods. (You must at least provide definitions for all the methods in an
interface, however.) In this case, the
DirFilter
constructor is also created.

The
accept( )
method must accept a
File
object representing the directory that a particular file is found in, and a
String
containing the name of that file. You might choose to use or ignore either of
these arguments, but you will probably at least use the file name. Remember
that the
list( )
method is calling
accept( )
for each of the file names in the directory object to see which one should be
included – this is indicated by the
boolean
result returned by
accept( ).

The
list( )
method returns an array. You can query this array for its length and then move
through it selecting the array elements. This ability to easily pass an array
in and out of a method is a tremendous improvement over the behavior of C and
C++.


Anonymous
inner classes

//: DirList2.java
// Uses Java 1.1 anonymous inner classes
import java.io.*;
 
public class DirList2 {
  public static FilenameFilter
  filter(final String afn) {
    // Creation of anonymous inner class:
    return new FilenameFilter() {
      String fn = afn;
      public boolean accept(File dir, String n) {
        // Strip path information:
        String f = new File(n).getName();
        return f.indexOf(fn) != -1;
      }
    }; // End of anonymous inner class
  }
  public static void main(String[] args) {
    try {
      File path = new File(".");
      String[] list;
      if(args.length == 0)
        list = path.list();
      else
        list = path.list(filter(args[0]));
      for(int i = 0; i < list.length; i++)
        System.out.println(list[i]);
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
} ///:~ 

This
design is an improvement because the
FilenameFilter
class is now tightly bound to
DirList2.
However, you can take this approach one step further and define the anonymous
inner class as an argument to
list( ),
in which case it’s even smaller:

//: DirList3.java
// Building the anonymous inner class "in-place"
import java.io.*;
 
public class DirList3 {
  public static void main(final String[] args) {
    try {
      File path = new File(".");
      String[] list;
      if(args.length == 0)
        list = path.list();
      else
        list = path.list(
          new FilenameFilter() {
            public boolean
            accept(File dir, String n) {
              String f = new File(n).getName();
              return f.indexOf(args[0]) != -1;
            }
          });
      for(int i = 0; i < list.length; i++)
        System.out.println(list[i]);
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
} ///:~ 

The
argument to
main( )
is now
final,
since the anonymous inner class uses
args[0]
directly.

This
shows you how anonymous inner classes allow the creation of quick-and-dirty
classes to solve problems. Since everything in Java revolves around classes,
this can be a useful coding technique. One benefit is that it keeps the code
that solves a particular problem isolated together in one spot. On the other
hand, it is not always as easy to read, so you must use it judiciously.


A
sorted directory listing

//: SortedDirList.java
// Displays sorted directory listing
import java.io.*;
import c08.*;
 
public class SortedDirList {
  private File path;
  private String[] list;
  public SortedDirList(final String afn) {
    path = new File(".");
    if(afn == null)
      list = path.list();
    else
      list = path.list(
          new FilenameFilter() {
            public boolean
            accept(File dir, String n) {
              String f = new File(n).getName();
              return f.indexOf(afn) != -1;
            }
          });
    sort();
  }
  void print() {
    for(int i = 0; i < list.length; i++)
      System.out.println(list[i]);
  }
  private void sort() {
    StrSortVector sv = new StrSortVector();
    for(int i = 0; i < list.length; i++)
      sv.addElement(list[i]);
    // The first time an element is pulled from
    // the StrSortVector the list is sorted:
    for(int i = 0; i < list.length; i++)
      list[i] = sv.elementAt(i);
  }
  // Test it:
  public static void main(String[] args) {
    SortedDirList sd;
    if(args.length == 0)
      sd = new SortedDirList(null);
    else
      sd = new SortedDirList(args[0]);
    sd.print();
  }
} ///:~ 

A
few other improvements have been made. Instead of creating
path
and
list
as local variables to
main( ),
they are members of the class so their values can be accessible for the
lifetime of the object. In fact,
main( )
is now just a way to test the class. You can see that the constructor of the
class automatically sorts the list once that list has been created.

The
sort is case-insensitive so you don’t end up with a list of all the words
starting with capital letters, followed by the rest of the words starting with
all the lowercase letters. However, you’ll notice that within a group of
file names that begin with the same letter the capitalized words are listed
first, which is still not quite the desired behavior for the sort. This problem
will be fixed in Java 1.2.

Checking
for and creating directories

//: MakeDirectories.java
// Demonstrates the use of the File class to
// create directories and manipulate files.
import java.io.*;
 
public class MakeDirectories {
  private final static String usage =
    "Usage:MakeDirectories path1 ...n" +
    "Creates each pathn" +
    "Usage:MakeDirectories -d path1 ...n" +
    "Deletes each pathn" +
    "Usage:MakeDirectories -r path1 path2n" +
    "Renames from path1 to path2n";
  private static void usage() {
    System.err.println(usage);
    System.exit(1);
  }
  private static void fileData(File f) {
    System.out.println(
      "Absolute path: " + f.getAbsolutePath() +
      "n Can read: " + f.canRead() +
      "n Can write: " + f.canWrite() +
      "n getName: " + f.getName() +
      "n getParent: " + f.getParent() +
      "n getPath: " + f.getPath() +
      "n length: " + f.length() +
      "n lastModified: " + f.lastModified());
    if(f.isFile())
      System.out.println("it's a file");
    else if(f.isDirectory())
      System.out.println("it's a directory");
  }
  public static void main(String[] args) {
    if(args.length < 1) usage();
    if(args[0].equals("-r")) {
      if(args.length != 3) usage();
      File
        old = new File(args[1]),
        rname = new File(args[2]);
      old.renameTo(rname);
      fileData(old);
      fileData(rname);
      return; // Exit main
    }
    int count = 0;
    boolean del = false;
    if(args[0].equals("-d")) {
      count++;
      del = true;
    }
    for( ; count < args.length; count++) {
      File f = new File(args[count]);
      if(f.exists()) {
        System.out.println(f + " exists");
        if(del) {
          System.out.println("deleting..." + f);
          f.delete();
        }
      }
      else { // Doesn't exist
        if(!del) {
          f.mkdirs();
          System.out.println("created " + f);
        }
      }
      fileData(f);
    }
  }
} ///:~ 

In
fileData( )
you can see the various file investigation methods put to use to display
information about the file or directory path.

More by Author

Must Read