print directory tree

This is how I did it.

The Code

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();
}
}

The Result

+--folder1/
|  +--a.txt
|  +--folder2/
|  |  +--b1.txt
|  |  +--b2.txt
|  |  +--b3.txt
|  |  +--folder3/
|  +--folder4/

The UnitTest

import static org.junit.Assert.*;

import java.io.File;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class FileAssertTest {

@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private File folder1;

@Before
public void setUp() {
    folder1 = temporaryFolder.newFolder("folder1");
}

@Test
public void testPrintDirectoryTreeWhenFolderIsEmpty() {
    // Invoke
    String actual = FileAssert.printDirectoryTree(folder1);
    // Verify
    assertEquals("+--folder1/\n", actual);
}

private static final String EXPECTED_FCOF = "" + "+--folder1/\n"
        + "|  +--a.txt\n";

@Test
public void testPrintDirectoryTreeWhenFolderContainsOneFile()
        throws Exception {
    // Setup
    File aFile = new File(folder1, "a.txt");
    assertTrue(aFile.createNewFile());
    // Invoke
    String actual = FileAssert.printDirectoryTree(folder1);
    // Verify
    assertEquals(EXPECTED_FCOF, actual);
}

private static final String EXPECTED_COMPLEX = "+--folder1/\n"
        + "|  +--a.txt\n" + "|  +--folder2/\n" + "|  |  +--b1.txt\n"
        + "|  |  +--b2.txt\n" + "|  |  +--b3.txt\n" + "|  |  +--folder3/\n"
        + "|  +--folder4/\n";

@Test
public void testPrintDirectoryTreeComplex() throws Exception {
    // Setup
    File aFile = new File(folder1, "a.txt");
    assertTrue(aFile.createNewFile());
    File folder2 = new File(folder1, "folder2");
    assertTrue(folder2.mkdir());
    File b1File = new File(folder2, "b1.txt");
    assertTrue(b1File.createNewFile());
    File b2File = new File(folder2, "b2.txt");
    assertTrue(b2File.createNewFile());
    File folder3 = new File(folder2, "folder3");
    assertTrue(folder3.mkdir());
    File b3File = new File(folder2, "b3.txt");
    assertTrue(b3File.createNewFile());
    File folder4 = new File(folder1, "folder4");
    assertTrue(folder4.mkdir());

    // Invoke
    String actual = FileAssert.printDirectoryTree(folder1);
    // Verify
    assertEquals(EXPECTED_COMPLEX, actual);
}

}

file directory printing pretty-printing


This is my solution:

(The TreeNode Class is copied from there.)

public static String renderDirectoryTree(TreeNode<FileInformation> tree) {
    List<StringBuilder> lines = renderDirectoryTreeLines(tree);
    String newline = System.getProperty("line.separator");
    StringBuilder sb = new StringBuilder(lines.size() * 20);
    for (StringBuilder line : lines) {
        sb.append(line);
        sb.append(newline);
    }
    return sb.toString();
}

public static List<StringBuilder> renderDirectoryTreeLines(TreeNode<FileInformation> tree) {
    List<StringBuilder> result = new LinkedList<>();
    result.add(new StringBuilder().append(tree.getData().getPath().getFileName()));
    Iterator<TreeNode<FileInformation>> iterator = tree.getChildren().iterator();
    while (iterator.hasNext()) {
        List<StringBuilder> subtree = renderDirectoryTreeLines(iterator.next());
        if (iterator.hasNext()) {
            addSubtree(result, subtree);
        } else {
            addLastSubtree(result, subtree);
        }
    }
    return result;
}

private static void addSubtree(List<StringBuilder> result, List<StringBuilder> subtree) {
    Iterator<StringBuilder> iterator = subtree.iterator();
    //subtree generated by renderDirectoryTreeLines has at least one line which is tree.getData()
    result.add(iterator.next().insert(0, "├── "));
    while (iterator.hasNext()) {
        result.add(iterator.next().insert(0, "│   "));
    }
}

private static void addLastSubtree(List<StringBuilder> result, List<StringBuilder> subtree) {
    Iterator<StringBuilder> iterator = subtree.iterator();
    //subtree generated by renderDirectoryTreeLines has at least one line which is tree.getData()
    result.add(iterator.next().insert(0, "└── "));
    while (iterator.hasNext()) {
        result.add(iterator.next().insert(0, "    "));
    }
}

with this output:

DirectoryCatalog
├── .git
│   ├── COMMIT_EDITMSG
│   ├── config
│   ├── description
│   ├── HEAD
│   ├── hooks
│   │   ├── applypatch-msg.sample
│   │   ├── commit-msg.sample
│   │   ├── post-commit.sample
│   │   ├── post-receive.sample
│   │   ├── post-update.sample
│   │   ├── pre-applypatch.sample
│   │   ├── pre-commit.sample
│   │   ├── pre-push.sample
│   │   ├── pre-rebase.sample
│   │   ├── prepare-commit-msg.sample
│   │   └── update.sample
│   ├── index
│   ├── info
│   │   └── exclude
│   ├── logs
│   │   ├── HEAD
│   │   └── refs
│   │       ├── heads
│   │       │   └── master
│   │       └── remotes
│   │           └── origin
│   │               └── master
│   ├── objects
│   │   ├── 0b
│   │   │   └── b3fb0a15268c9770220938b1048305429527c7
│   │   ├── 0d
│   │   │   └── e508f20f1cb44ff543cec54ea93a71e5e40d1d
│   │   ├── 10
│   │   │   └── 9c753f3aab08f167f8409e6c9abec27cad6548
│   │   ├── 15
│   │   │   └── 7602556ebd120e656ae8dcd00cb361bfb3ef79
│   │   ├── 16
│   │   │   └── 952a5d1f5f3d15199f3e85b3d895a81990024e
│   │   ├── 1c
│   │   │   └── 5b36cd4d41b9b7d0df46d4184512098186c904
│   │   ├── 20
│   │   │   └── ae5cc82ebbb85f9161d8fd9783905e4cbee72c
│   │   ├── 21
│   │   │   └── e42add0fb2205a75ff621dbeb6d76455816b79
│   │   ├── 23
│   │   │   └── af4eaa4007ca941e562406b7b329c6ba4b395e
│   │   ├── 28
│   │   │   └── 6caf4b7be3ac85a476f3782220fe167a040971
│   │   ├── 2b
│   │   │   └── f6743fb97162e999fb6429510bb8a52114bc31
│   │   ├── 35
│   │   │   └── acd4ab6acb599d4e3244be2a8dc3a0161bcb3c
│   │   ├── 46
│   │   │   └── 1ef7c4db86e0466bb8f1f81dc25f12e3598db6
│   │   ├── 47
│   │   │   └── 4ce7f18330e5295ab1ef2eea66505235819e22
│   │   ├── 4b
│   │   │   └── 825dc642cb6eb9a060e54bf8d69288fbee4904
│   │   ├── 4e
│   │   │   └── 4e047c077c7825a845deebda7f97832cee847b
│   │   ├── 4f
│   │   │   └── 20b118269d71535e469966fdb5fe8a78ae6c9a
│   │   ├── 51
│   │   │   └── 677b295b4cd9931354dd52fb6050015d524fea
│   │   ├── 52
│   │   │   └── 9c34997d8262bd207037f0373703d68d4e1a4e
│   │   ├── 5b
│   │   │   └── 928f45816cc51609861f0930251accbe7de7f9
│   │   ├── 5d
│   │   │   └── 7cdbe029c6eff99c72d09b0a81347b605e77d7
│   │   ├── 61
│   │   │   └── 2b9d86f6ac365ede894e13a6273fd13833f44f
│   │   ├── 67
│   │   │   └── 2209cb240eac0303d6e3b6c7eb04242d661fad
│   │   ├── 6b
│   │   │   ├── c84e19913908f16141677b9d66abdb620350a0
│   │   │   └── d278b63be854ccd3c02b9038ee43366021b1a1
│   │   ├── 6c
│   │   │   └── 9d10e91679988caaee6759d48daf336f28a9a3
│   │   ├── 70
│   │   │   └── 4284c78f1c664c1348af5a784cca55c51fd66a
│   │   ├── 72
│   │   │   └── 1ab5fe4e4226930668048d97331103a92751be
│   │   ├── 78
│   │   │   └── 64a41eaf50f32ae8d2cab1acd200c405215c1a
│   │   ├── 82
│   │   │   └── 36f772feca12b3e301eea50774458a7e77c7fb
│   │   ├── 83
│   │   │   └── bc1e2a1f3187f28d63d7fa334744810d80c20d
│   │   ├── 87
│   │   │   └── 3ce97f624e9a1ae4a1bbf41218d50dc3503d6f
│   │   ├── 8a
│   │   │   └── 0b282aa6885fb573c106b3551f7275c5f17e8e
│   │   ├── 91
│   │   │   └── a7e269e19dfc62e27137a0b57ef3e430cee4fd
│   │   ├── 94
│   │   │   ├── 065a39b0dd7ce05604b96aff84d984493553e2
│   │   │   └── 5cbc994fb2d251437e71edf3a6eb289c5836ec
│   │   ├── 95
│   │   │   └── f481326b62c9adff2a0dad47480e279adb518f
│   │   ├── 9e
│   │   │   └── 1f7de67de38e67479cb8e38ad92d6354838393
│   │   ├── a1
│   │   │   └── e385dff1fb08b7a45ea52af43b22d240f0adab
│   │   ├── a7
│   │   │   └── 8934a5a81d95a85b9c97a3e1c1429186feb83d
│   │   ├── ab
│   │   │   └── 855c3379bdb3e4a22605de58f3438148d6ffe7
│   │   ├── b7
│   │   │   └── 64eafa2ba4e7f8f52a4e96da61b3d77f262d16
│   │   ├── bd
│   │   │   └── 9374662b409bb364779212874e7cf611541c38
│   │   ├── c1
│   │   │   └── f858c163f9c8cb89a145a60e87c9e35c97fb93
│   │   ├── c3
│   │   │   └── 35d07a1078656646ca6e980b468b938106d357
│   │   ├── c5
│   │   │   └── ac562c59d6a0430fa0ccd1fa6318fa5d48604f
│   │   ├── c8
│   │   │   └── f3d9284a65afbff6640da7970664ee2134c85b
│   │   ├── c9
│   │   │   └── 7a8bdb9088d370da7e88784a7a093b971aa23a
│   │   ├── cc
│   │   │   └── a6a5141b614b5e351c7e5b6b677ef09a931786
│   │   ├── d1
│   │   │   └── 76229a9e1d0172f84d060904d171be51f9d0f6
│   │   ├── d3
│   │   │   ├── 2f0a1ba59a0c52d3acc62de1d624fb68423ee3
│   │   │   └── b9dfcb3216a2feab03b1ae96280e3c1c963953
│   │   ├── e0
│   │   │   └── 6d2081865a766a8668acc12878f98b27fc9ea0
│   │   ├── e5
│   │   │   └── 3106578cd8adcd085c5c63c05fc49d5a5a3dbb
│   │   ├── info
│   │   └── pack
│   └── refs
│       ├── heads
│       │   └── master
│       ├── remotes
│       │   └── origin
│       │       └── master
│       └── tags
├── .gitattributes
├── .gitignore
├── .gradle
│   └── 2.2.1
│       └── taskArtifacts
│           ├── cache.properties
│           ├── cache.properties.lock
│           ├── fileHashes.bin
│           ├── fileSnapshots.bin
│           ├── outputFileStates.bin
│           └── taskArtifacts.bin
├── .idea
│   ├── .name
│   ├── compiler.xml
│   ├── copyright
│   │   └── profiles_settings.xml
│   ├── encodings.xml
│   ├── gradle.xml
│   ├── libraries
│   │   ├── Gradle__commons_io_commons_io_2_4.xml
│   │   ├── Gradle__junit_junit_4_11.xml
│   │   └── Gradle__org_hamcrest_hamcrest_core_1_3.xml
│   ├── misc.xml
│   ├── modules.xml
│   ├── scopes
│   │   └── scope_settings.xml
│   ├── uiDesigner.xml
│   ├── vcs.xml
│   └── workspace.xml
├── app
│   ├── app.iml
│   ├── build
│   │   ├── classes
│   │   │   └── main
│   │   │       └── gq
│   │   │           └── baijie
│   │   │               └── catalog
│   │   │                   ├── controllor
│   │   │                   │   ├── FilesScanner$MyFileVisitor.class
│   │   │                   │   └── FilesScanner.class
│   │   │                   ├── entity
│   │   │                   │   └── FileInformation.class
│   │   │                   ├── Main.class
│   │   │                   └── util
│   │   │                       ├── FileAssert.class
│   │   │                       ├── Hash.class
│   │   │                       ├── HEX.class
│   │   │                       ├── Printer$1.class
│   │   │                       ├── Printer$DirectoryTreeRender.class
│   │   │                       ├── Printer.class
│   │   │                       └── TreeNode.class
│   │   ├── dependency-cache
│   │   ├── resources
│   │   │   └── main
│   │   └── tmp
│   │       └── compileJava
│   ├── build.gradle
│   └── src
│       ├── main
│       │   └── java
│       │       └── gq
│       │           └── baijie
│       │               └── catalog
│       │                   ├── controllor
│       │                   │   └── FilesScanner.java
│       │                   ├── entity
│       │                   │   └── FileInformation.java
│       │                   ├── Main.java
│       │                   └── util
│       │                       ├── FileAssert.java
│       │                       ├── Hash.java
│       │                       ├── HEX.java
│       │                       ├── Printer.java
│       │                       └── TreeNode.java
│       └── test
│           └── java
├── DirectoryCatalog.iml
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── LICENSE
├── README.md
├── settings.gradle
└── temp
    ├── 150110161006.txt
    ├── 150110173453.txt
    ├── 150110175353.txt
    ├── 150111111311.txt
    ├── 150111112500.txt
    ├── 150111113411.txt
    ├── 150111155619.txt
    ├── 150111161958.txt
    ├── 150111162230.txt
    ├── 150111162632.txt
    ├── 150111163225.txt
    ├── 150111163330.txt
    ├── 150111164526.txt
    ├── 150111205412.txt
    ├── 150111210322.txt
    ├── 150111210506.txt
    ├── 150111210927.txt
    ├── 150111211249.txt
    ├── 150111211321.txt
    ├── 150111220750.txt
    ├── 150111220822.txt
    ├── 150111221437.txt
    ├── 150111221842.txt
    ├── 150111222437.txt
    ├── 150111234344.txt
    ├── 150111234433.txt
    └── 150112000034.txt