Concepts / Handling Invalid Directories

Handling Invalid Directories

A Java File object represents a file or directory, and creating one with new File(name) asks the program to refer to a path rather than read it.

  • Programming

A Program That Asks for a Folder

Many programs need to work with folders on the disk. A common example is a small tool that asks the user to type a directory name and then shows the files inside it. That tool looks simple, but it has a real problem to solve: the user might type the name of a folder that does not exist, or the name of an ordinary file instead of a folder. A program that blindly tries to list whatever it is given will fail or behave unpredictably. This article follows one complete Java program, called DirectoryList, that handles those inputs safely.

The source describes the program's purpose this way: it lists the files in a directory specified by the user, and if the name entered is not a directory, a message is printed and the program ends. Keep that description in mind as you read. The goal is not only to know what the program prints, but to understand how it decides what to do.

What do you think happens?

Suppose the user types the name of an ordinary file, not a folder, into DirectoryList. Before reading on, decide what the program does: does it print a file listing, or does it print one of its messages? Which if statement controls that decision?

Reveal answer
Answer:

The program prints a message saying the file is not a directory, and it does not print a file listing.

The outer test checks whether the path is not a directory. A regular file is not a directory, so the outer block runs. Inside that block, exists() returns true because something by that name is present, so the program takes the branch for something that exists but is not a directory. The listing branch is only reached when isDirectory() is true.

What new File(name) Actually Does

The File class in Java can represent either a file or a directory. The key idea is that creating a File object refers to a path; it does not read anything from the disk. The line directory = new File(directoryName) takes the text the user typed and wraps it in an object. At that moment the program has a reference to a location, not the folder's contents, and it has not yet confirmed that the location exists.

After the object is created, the program asks it questions. The isDirectory() method checks whether the object represents a directory, and exists() checks whether the path is present at all. Those questions are what separate a missing path, an ordinary file, and a valid folder. The diagram below shows the relationship between the variable, the path string, and the folder on disk.

directoryFile object variablepath on diskmay or may not existdirectoryNametext the user typedpassed to new Filerefers to
After new File(name) runs, the variable holds a reference to a path. The disk is only consulted when the program asks questions such as isDirectory() or exists().

Two Checks: exists() and isDirectory()

The validity decision rests on two questions that answer different things. The exists() method answers whether anything is present at the path. The isDirectory() method answers whether the thing at the path is a directory. A directory is valid when it exists and is a directory, and the two checks together decide which message or listing the program produces. Here is the full program. The message wording is illustrative and was written to match the branch structure described in the source; the structure itself is the point to study.

Code walkthrough
import java.io.File;
import java.util.Scanner;

public class DirectoryList {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.print("Enter a directory name: ");
        String directoryName = in.nextLine();
        File directory = new File(directoryName);
        if (directory.isDirectory() == false) {
            if (directory.exists() == false) {
                System.out.println("There is no such directory.");
            } else {
                System.out.println("The file is not a directory.");
            }
        } else {
            String[] files = directory.list();
            for (int i = 0; i < files.length; i++) {
                System.out.println("    " + files[i]);
            }
        }
    }
}
  1. 1
    Line 1-2 The File and Scanner classes are imported so the program can refer to them.
  2. 2
    Line 6-8 The user's typed text is read and wrapped in a File object named directory. No folder contents are read yet.
  3. 3
    Line 9 The outer test asks whether the object is not a directory. This is the negated test that controls the whole decision.
  4. 4
    Line 10-14 Inside the outer block, exists() decides between no such directory and a file that is not a directory.
  5. 5
    Line 15-19 Only when isDirectory() is true does the else branch run. list() returns the file names, and the loop prints each one with an indent.
  • new File(directoryName) creates an object that refers to the typed path without reading the disk
  • The outer if tests isDirectory() == false, a negated test that runs when the path is not a directory
  • The inner if separates a missing path (exists() == false) from an existing non-directory
  • The else branch runs only when isDirectory() is true, and only then is list() called
Path the user typedexists()isDirectory()Branch the program takes
Nothing by that namefalsefalseOuter block, inner exists() == false: no such directory
Ordinary filetruefalseOuter block, inner else: file is not a directory
Valid foldertruetrueElse branch: list() is called and names are printed

How the two checks combine to choose a branch

Following Three Kinds of Input

The easiest way to build the right mental model is to trace the program for different inputs. The flowchart below shows the order of checks for any typed path. Follow an input from the top, answer each decision, and note which terminal box it reaches.

User types a namenew File(name)refers to the pathisDirectory() ==false?exists() == false?No such directoryprogram endsFile is not adirectoryprogram endslist() calledreturns array of namesPrint each file nameindented linesinputfirst checkyes, not a directoryno, is a directoryyes, nothing thereno, something existsnames in array
The outer decision asks whether the path is not a directory. Only when it is a directory does the program reach the listing step.
Worked example

Tracing a valid folder through the program

The user types the name of a folder that exists and is a directory. Which lines run, and where does the printed output come from?

  1. 1
    Create the object

    new File(directoryName) creates a reference to the typed path. The disk has not been read yet.

  2. 2
    Outer test

    isDirectory() returns true, so the test isDirectory() == false is false. The outer block is skipped.

  3. 3
    Else branch

    Control moves to the else branch, the only branch that calls list().

  4. 4
    Retrieve names

    list() returns an array of file names in the directory and stores it in files.

  5. 5
    Print with the loop

    files.length sets the loop limit, and each entry is printed on its own indented line. The printed output comes from this loop, not from either message.

Result

The program prints an indented list of the names inside the folder and does not print any error message.

What do you think happens?

The user types the name of a path that does not exist anywhere on the disk. Which if statement sends the program to its message, and which message does it print?

Reveal answer
Answer:

The outer test isDirectory() == false is true, then the inner test exists() == false is true, so the program prints the no such directory message.

Nothing is present at the path, so exists() returns false, and the inner if takes its first branch. The listing code never runs.

Mistakes When Reading the Branch Logic

  • Common mistake

    Reading isDirectory() == false as the condition for a valid folder

    What it looks like
    if (directory.isDirectory() == false) { ... list() ... }
    Why it fails

    The outer block runs when the path is not a directory. Calling list() there would ask a regular file for its contents.

    Do this instead

    Treat the outer block as the error path. list() belongs in the else branch, which runs only when isDirectory() is true.

  • Common mistake

    Assuming exists() alone proves the path is a directory

    What it looks like

    A regular file with the typed name makes exists() true, but the program must still reject it.

    Why it fails

    exists() only reports that something is present at the path. It does not say what kind of thing it is.

    Do this instead

    Use isDirectory() to decide whether the path is a directory, and use exists() to separate a missing path from a file.

  • Common mistake

    Thinking new File(name) reads the folder's contents

    Why it fails

    The source states that creating the object does not read anything from the disk by itself. It only creates an object that refers to the path.

    Do this instead

    Expect the disk to be consulted only when the program asks a question such as isDirectory(), exists(), or list().

  • Common mistake

    Forgetting that the messages end the program, so no listing follows an error message

Tracing the Branches Yourself

Practice by tracing rather than running. For each input below, decide which branch the program takes before you check the answer. Use the flowchart and the comparison table as references, and name the if statement that controls the decision.

EASY

Trace DirectoryList for these three inputs: (a) a path that is not present on the disk, (b) an ordinary text file, (c) a folder that exists. For each one, state which test is evaluated first, which branch runs, and whether list() is called.

Hints
  • Start with the outer test isDirectory() == false. Ask whether it is true for this input.
  • Only the else branch calls list(), so ask whether that branch can be reached for a regular file.
  • For a missing path, exists() returns false, so the inner if takes its first branch.
MEDIUM

Explain in one or two sentences why the program can safely call list() without checking the path's contents first, using only the order of the checks in DirectoryList.

Hints
  • Look at which branch contains the list() call.
  • Think about what happens before that branch is reached.

Key Takeaways

  • A File object created with new File(name) refers to a path and does not read the disk by itself; the program then asks it questions.
  • isDirectory() checks whether the path is a directory, and exists() checks whether anything is present, and together they choose the branch.
  • The outer test isDirectory() == false is a negated test; its block handles missing paths and non-directory files, and list() runs only in the else branch.
  • list() returns an array of file names, which the program prints with a loop once the validity check has passed.