This program deletes all files and folders beneath a given node.

*EDIT*

I have the following "test" directory structure on drive K:

Folder K:\garbage\
f_00000b (file)
f_00000f ( " )
f_0000b3 ( " )
dir1    [FOLDER]

Folder K:\garbage\dir1\
abc.pdf (file)
b12.pdf (file)
b36.pdf (file)
dir2   [FOLDER]

Folder K:\garbage\dir1\dir2\
A.pdf   (file)
a1.pdf  (file)
A2.pdf  (file)

*END EDIT*

Program works because I stumbled onto "try-with-resources", the line surrounded with all the ///////////////////////// being "the resource".

import java.io.IOError;
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import javax.swing.JOptionPane;

public class CreatingDirectories {

    public static void main(String[] args) {

      deleteEverythingBelowThisNode(Paths.get("K:/garbage/dir1/dir2"));
    }

    static void deleteAllFilesIn(String ps){

      Path p = Paths.get(ps);

      try ////////////////////////////////////////////////////////////////
         (DirectoryStream<Path> paths = Files.newDirectoryStream(p)) /////
      { //////////////////////////////////////////////////////////////////

        for(Path q: paths)
          Files.delete(q);

      }catch(NotDirectoryException e){
        System.out.println("Not directory: " + p.toString() + "--" + e);
      }catch(DirectoryNotEmptyException e){
        System.out.println("Not empty: " + p.toString() + "--" + e);
      }
      catch(IOException e){
        System.out.println("IO: " + p.toString() + "--" + e);
      }
      catch(IOError e){
        System.out.println("IO ERROR: " + e);
      }
    }

    static void deleteEverythingBelowThisNode(Path p){

      String            sep        = p.getFileSystem().getSeparator();
      ArrayList<String> pathPieces = new ArrayList<>() ;
      String []         paths      = new String[p.getNameCount()];

      for(int i = 0 ; i < p.getNameCount() ; i++){
        pathPieces.add(p.getName(i).toString());
        paths[i] = p.getRoot().toString();
      }

      for(int i = 0; i < p.getNameCount() ; i++)
        for(int k = 0; k <= i; k++)
          paths[i] += pathPieces.get(k) + sep;

      for(int k = p.getNameCount() - 1; k >= 0; k--)
        deleteAllFilesIn(paths[k]);
    }
}

I understand that "try-with-resources" is necessary: program works with it, not without it.

But I don't understand why and I don't know how it solved the original problem, which I now describe.

I originally had situated the "resources" above the try-block, like this, which seems perfectly natural:

      DirectoryStream<Path> paths = Files.newDirectoryStream(p);
      try {
           for...

With the program structure otherwise identical except for moving that one line as shown above, all files and subfolder had been successfully deleted from a folder, but DirectoryNotEmptyException was thrown. Windows Explorer confirmed that the directory was empty after the program terminated because of the exception.

Why was the exception thrown on an empty directory?

From the horse's mouth, "The try-with-resources statement ... declares ... an object that ... is closed at the end of the statement."

Closing happens at the end of the statement, so at the end of the loop. How did the exception not occur even with try-with-resources?

As it now is, after looping through the entire node, everything beneath it has been deleted.

So what did try-with-resources actually do to enable deleting an empty folder that could not be deleted without try-with-resources?

These don't seem like stupid questions or a trivial situation.

Did a DirectoryNotEmptyException actually occur anyway, but try-with-resources somehow handled it? I can't believe I'm asking that, since it does seem like a stupid question, but what actually happened to make the program run as expected?

3

There are 3 best solutions below

1
On BEST ANSWER

On Windows, you cannot delete a file or directory that is still open. (On Unix, on the other hand, this is not a problem - the file will be deleted from the directory structure when you delete it and from disk when you close it. But that's on Unix.)

So if you don't use the try-with-resources statement to close the directory stream, you will still have subdirectories open at the moment that you try to delete the files in the parent directory, and that attempt to the subdirectory that is still open will fail. Since you ignore exceptions (you just print them), the subsequent attempt to delete the parent directory will also fail with a DirectoryNotEmptyException since you didn't delete all the subdirectories.

You can verify if this is really the case. When you do not use try-with-resources, make sure that you explicitly close the directory stream after you delete all files in the directory (using paths.close();)

That should have the same effect as the try-with-resources block (unless an exception occurs - to guarantee exactly the same behavior as try-with-resources, you need to put paths.close(); in a finally block).

1
On

Erwin explains your question, but you also have a fairly serious problem in the end of your listing, at:

   for(int k = p.getNameCount() - 1; k >= 0; k--)
        deleteAllFilesIn(paths[k]);

You create a list of path parts, so for example, the parts in there would be:

  • k:/garbage/dir1/dir2
  • k:/garbage/dir1
  • k:/garbage
  • k:/

That means you'll eventually try to delete everything in k:. It will try to delete everything at k:\ (all files; it will fail overall if there are any non-empty subdirectories).

Assuming you only want to delete the lowest-level files, you probably want to change the deleteEverythingBelowThisNode() function.

0
On

(I hope it's OK to "answer my own question" to make the 'bottom line' of the thread show what wound up working. This way anyone who views the thread won't have to look hard for the solution.)

Anyway, here's deleteAllFilesBelowThisNode with @Erwin's suggestion.

static void deleteAllFilesBelowThisNode(String ps) throws IOException{

  Path p = Paths.get(ps);

  try (DirectoryStream<Path> paths = Files.newDirectoryStream(p))
  {
    for(Path q: paths){
    
      if(JOptionPane.showConfirmDialog(null,"Deleting " + q.toString(),"",
           JOptionPane.OK_CANCEL_OPTION) 
        != JOptionPane.OK_OPTION)
                                  System.exit(9);
      
      Files.delete(q);
      System.out.println(q.toString() + " deleted");
    }
  }
  finally{
    JOptionPane.showMessageDialog(null,"AHA!");
  }
}

I added the "AHA!" because I FINALLY realized what's going on.

EDIT

And I took out all that I so proudly added yesterday because once again I find myself as dense as mercury. The planet, the metal, the car... whatevvvvvvvvvvs.

I'd forgotten that there were three separate calls to DeleteAllFilesBelowThisNode, which really confused the heck out of me. Heck, it's MY dang program, but ... forest for trees, and all that. By not including the entire program, I fooled me. Fooled me good. Dern good.

I'm not really a moron.

*END EDIT

But I left this:

THANK YOU, ERWIN!

ANOTHER EDIT

Here's output, for understanding:

Delete all entries beneath K:\garbage\dir1\dir2\
  deleting K:\garbage\dir1\dir2\A.pdf ... deleted
  deleting K:\garbage\dir1\dir2\a1 and 2 ver 2.pdf ... deleted
  deleting K:\garbage\dir1\dir2\A1 and 2.pdf ... deleted
K:\garbage\dir1\dir2\ closed by finally. ----- THUS DELETEABLE

Delete all entries beneath K:\garbage\dir1\
  deleting K:\garbage\dir1\abc.pdf ... deleted
  deleting K:\garbage\dir1\b12.pdf ... deleted
  deleting K:\garbage\dir1\b36.pdf ... deleted
  deleting K:\garbage\dir1\dir2 ... ***DELETED***
K:\garbage\dir1\ closed by finally. ----- THUS DELETEABLE


Delete all entries beneath K:\garbage\
  deleting K:\garbage\dir1 ... ***DELETED***
  deleting K:\garbage\f_00000b ... deleted
  deleting K:\garbage\f_00000f ... deleted
  deleting K:\garbage\f_0000b3 ... deleted
K:\garbage\ closed by finally.

Here's entire program:

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import javax.swing.JOptionPane;

public class CreatingDirectories {

  static void deleteAllFilesBelowThisNode(String ps) throws IOException{

    try (DirectoryStream<Path> paths = Files.newDirectoryStream(Paths.get(ps)))
    {
      for(Path q: paths){
                         System.out.print("deleting " + q.toString() + " ... ");
        if(JOptionPane.showConfirmDialog(null,"Deleting " + q.toString(),"",
             JOptionPane.OK_CANCEL_OPTION) 
          != JOptionPane.OK_OPTION)
                                                               System.exit(9);
        Files.delete(q);
                                                  System.out.println("deleted");
      }
    }
    finally{         
      System.out.println("\n" + ps + " closed by finally.\n");
    }
  }
    
  static void iterativelyDeleteFoldersFromHereUpToRoot(Path p) throws IOException{

    String            sep        = p.getFileSystem().getSeparator();
    ArrayList<String> pathPieces = new ArrayList<>() ;
    String []         paths      = new String[p.getNameCount()];

    for(int i = 0 ; i < p.getNameCount() ; i++){

      pathPieces.add(p.getName(i).toString());

      paths[i] = p.getRoot().toString();
    }

    for(int i = 0; i < p.getNameCount() ; i++)
      for(int k = 0; k <= i; k++)
                                  paths[i] += pathPieces.get(k) + sep;

    for(int k = p.getNameCount() - 1; k >= 0; k--){

      System.out.println("\nDelete all entries beneath " + paths[k].toString());
    
      deleteAllFilesBelowThisNode(paths[k]);
    }
  }
    
  public static void main(String[] args) throws IOException {

    iterativelyDeleteFoldersFromHereUpToRoot(Paths.get("K:/garbage/dir1/dir2"));
  }
}