RickGrimes
RickGrimes

Reputation: 49

print directory like tree command

I have to print a directory tree. I have the following code:

public static void main(String[] args) throws Exception
{

  File root = new File("C:\\Users\\User\\Desktop\\example");

  if (!root.isDirectory())
  {
    throw new IllegalArgumentException("Invalid directory: " + root.getName());
  }

  int level = 0;
  System.out.println(renderFolder(root, level, new StringBuilder(), false));
}

private static StringBuilder renderFolder(File folder, int level, StringBuilder sb, boolean isLast)
{
  indent(sb, level, isLast).append(folder.getName()).append("\n");

  File[] objects = folder.listFiles();

  for (int i = 0; i < objects.length; i++)
  {
    boolean last = ((i + 1) == objects.length);

    if (objects[i].isDirectory())
    {
      renderFolder(objects[i], level + 1, sb, last);
    }
  }
  return sb;
}


private static StringBuilder indent(StringBuilder sb, int level, boolean isLast)
{

  for (int i = 1; i < level; i++)
  {
     sb.append("\u2502   ");
  }

  if (level > 0)
  {
    sb.append(isLast
      ? "\u2514\u2500\u2500"
      : "\u251c\u2500\u2500");
  }

  return sb;
}

with this output:

example
├──a
└──b
│   └──b1
│   │   ├──b11
│   │   │   └──b111
│   │   └──b12
│   │   │   ├──b121
│   │   │   └──b122
│   │   │   │   └──b1221

I didn't manage to remove the useless deep lines. How to do this?
Also my answer is: "How can I keep lines connected(as much as the cmd's tree command)?"

Upvotes: 4

Views: 3586

Answers (3)

Bon
Bon

Reputation: 3103

Try this one, I added another parameter into your methods called hierarchyTree to indicate if at a certain level, we should print | or empty space.

It is used as a stack to easily maintain the folder levels based on the recursion level.

public static void main(final String[] args) {
    final File folder = new File("example");
    final StringBuilder sb = renderFolder(folder);
    System.out.println(sb.toString());
}

private static StringBuilder renderFolder(File folder) {
    return renderFolder(folder, 0, new StringBuilder(), false, new ArrayList<>());
}

private static StringBuilder renderFolder(File folder, int level, StringBuilder sb, boolean isLast, List<Boolean> hierarchyTree) {
    indent(sb, level, isLast, hierarchyTree).append(folder.getName()).append("\n");

    File[] objects = folder.listFiles(new FilenameFilter() {
        @Override
        public boolean accept(File current, String name) {
            return new File(current, name).isDirectory();
        }
    });

    for (int i = 0; i < objects.length; i++) {
        boolean last = ((i + 1) == objects.length);

        // this means if the current folder will still need to print subfolders at this level, if yes, then we need to continue print |
        hierarchyTree.add(i != objects.length - 1);
        renderFolder(objects[i], level + 1, sb, last, hierarchyTree);

        // pop the last value as we return from a lower level to a higher level
        hierarchyTree.remove(hierarchyTree.size() - 1);
    }
    return sb;
}


private static StringBuilder indent(StringBuilder sb, int level, boolean isLast, List<Boolean> hierarchyTree) {
    String indentContent = "\u2502   ";
    for (int i = 0; i < hierarchyTree.size() - 1; ++i) {
        // determines if we need to print | at this level to show the tree structure
        // i.e. if this folder has a sibling foler that is going to be printed later
        if (hierarchyTree.get(i)) {
            sb.append(indentContent);
        } else {
            sb.append("    "); // otherwise print empty space
        }
    }

    if (level > 0) {
        sb.append(isLast
                ? "\u2514\u2500\u2500"
                : "\u251c\u2500\u2500").append(" ");
    }

    return sb;
}

A sample result:

example
├── a
├── b
│   └── b1
│       ├── b11
│       │   └── b111
│       └── b12
│           ├── b121
│           └── b122
│               └── b1221
└── c

Upvotes: 2

M Oehm
M Oehm

Reputation: 29126

You must keep the information whether an item is the last one in the folder for all parent levels. That is, you should make isLast an array.

For example, when you try to print the item b111, that list should be [true, true, false, true]. The first entries tell you whether or not to print a vertical line, the last entry tells you wheher to print a corner or a T-junction.

Upvotes: 0

Kumaresan Perumal
Kumaresan Perumal

Reputation: 1956

import java.io.File;
public class FileAssert {

/**
 * Pretty print the directory tree and its file names.
 * 
 * @param folder
 *            must be a folder.
 * @return
 */
public static String printDirectoryTree(File folder) {
    if (!folder.isDirectory()) {
        throw new IllegalArgumentException("folder is not a Directory");
    }
    int indent = 0;
    StringBuilder sb = new StringBuilder();
    printDirectoryTree(folder, indent, sb);
    return sb.toString();
}

private static void printDirectoryTree(File folder, int indent,
        StringBuilder sb) {
    if (!folder.isDirectory()) {
        throw new IllegalArgumentException("folder is not a Directory");
    }
    sb.append(getIndentString(indent));
    sb.append("+--");
    sb.append(folder.getName());
    sb.append("/");
    sb.append("\n");
    for (File file : folder.listFiles()) {
        if (file.isDirectory()) {
            printDirectoryTree(file, indent + 1, sb);
        } else {
            printFile(file, indent + 1, sb);
        }
    }

}

private static void printFile(File file, int indent, StringBuilder sb) {
    sb.append(getIndentString(indent));
    sb.append("+--");
    sb.append(file.getName());
    sb.append("\n");
}

private static String getIndentString(int indent) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < indent; i++) {
        sb.append("|  ");
    }
    return sb.toString();
}
}

Upvotes: 2

Related Questions