1

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?

DSlomer64
  • 4,234
  • 4
  • 53
  • 88
  • 2
    `AT THE END of the statement` means at the end of the corresponding `try` block. – Sotirios Delimanolis Feb 27 '14 at 01:07
  • @SotiriosDelimanolis--and the try block contains a loop, during which all files and empty folders are getting deleted with no problem (no exceptions) AND THEN the close occurs. But WITHOUT try-with-resources, an exception occurs DURING looping. How does using try-with-resources prevent the exception if it only closes file at end of loop? – DSlomer64 Feb 27 '14 at 17:32
  • Can you print the stack trace? Where is the `DirectoryNotEmptyException` thrown? – Sotirios Delimanolis Feb 27 '14 at 19:34
  • I get the exception in both cases. – Sotirios Delimanolis Feb 27 '14 at 19:36
  • Exception in thread "main" java.nio.file.DirectoryNotEmptyException: K:\garbage\dir1 at sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:264) at sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:103) at java.nio.file.Files.delete(Files.java:1077) at creatingdirectories.CreatingDirectories.deleteAllFilesIn(CreatingDirectories.java:45) at creatingdirectories.CreatingDirectories.deleteNoMatterWhat(CreatingDirectories.java:74) at creatingdirectories.CreatingDirectories.main(CreatingDirectories.java:79) Java Result: 1 – DSlomer64 Mar 01 '14 at 01:18
  • Line 45: Files.delete(q); Line 74: deleteAllFilesIn(paths[k]); – DSlomer64 Mar 01 '14 at 01:18
  • I do NOT get exception when using try(DirectoryStream paths = Files.newDirectoryStream(p)){...} – DSlomer64 Mar 01 '14 at 01:22
  • I have edited original question to show what directory structure looks like. – DSlomer64 Mar 01 '14 at 01:23

3 Answers3

2

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).

Erwin Bolwidt
  • 30,799
  • 15
  • 56
  • 79
  • @Erwin--It makes perfect sense that, if a directory (or file) is still open, Windows won't let me delete it. I might post my own 'answer' to show the code with the change that you recommended, but here's the essense:` try{ paths = Files.newDirectoryStream(p); for(Path q: paths){ ... Files.delete(q); } finally{ paths.close(); } } ` – DSlomer64 Mar 02 '14 at 22:56
1

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.

user207421
  • 305,947
  • 44
  • 307
  • 483
user1676075
  • 3,056
  • 1
  • 19
  • 26
  • @user1676075--Code's OK as is. I traced and find that there are only 3 nodes, the list ending at k:/garbage. Maybe you overlooked the `-1` in `for( k = p.getNameCount() - 1...`. Besides, I have dialog to prevent runaway deletion. It's not a serious program; just having fun learning about directory streams. – DSlomer64 Mar 02 '14 at 22:22
0

(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"));
  }
}
tom_mai78101
  • 2,383
  • 2
  • 32
  • 59
DSlomer64
  • 4,234
  • 4
  • 53
  • 88