I implemented a solution to the challenge myself.
I am representing each node of a filesystem-hierarchy in a DirectoryNode. A helper-method createDirectoryTree(String... filePaths) creates a direcotry-tree.
Here is the usage example:
final String[] list = new String[]{
"mnt/sdcard/folder1/a/b/file1.file",
"mnt/sdcard/folder1/a/b/file2.file",
"mnt/sdcard/folder1/a/b/file3.file",
"mnt/sdcard/folder1/a/b/file4.file",
"mnt/sdcard/folder1/a/b/file5.file",
"mnt/sdcard/folder1/e/c/file6.file",
"mnt/sdcard/folder2/d/file7.file",
"mnt/sdcard/folder2/d/file8.file",
"mnt/sdcard/file9.file"
};
final DirectoryNode directoryRootNode = createDirectoryTree(list);
System.out.println(directoryRootNode);
The System.out.println -output is:
{value='mnt', children=[{value='sdcard', children=[{value='folder1',
children=[{value='a', children=[{value='b', children=[{value='file1.file',
children=[]}, {value='file2.file', children=[]}, {value='file3.file',
children=[]}, {value='file4.file', children=[]}, {value='file5.file',
children=[]}]}]}, {value='e', children=[{value='c',
children=[{value='file6.file', children=[]}]}]}]},
{value='folder2', children=[{value='d', children=[{value='file7.file',
children=[]}, {value='file8.file', children=[]}]}]},
{value='file9.file', children=[]}]}]}
Java source:
// DirectoriesHelper.java
import java.util.Optional;
public final class DirectoriesHelper {
private DirectoriesHelper() {}
public static Optional<DirectoryNode> createDirectoryTree(String... filePaths) {
DirectoryNode treeRootNode = null;
for (final String childPath : filePaths) {
final String path = childPath == null ? "" : childPath;
final String[] pathElements = path.split("/");
DirectoryNode movingNode = null;
for (final String pathElement : pathElements) {
movingNode = new DirectoryNode(movingNode, pathElement);
}
if (treeRootNode == null) {
treeRootNode = movingNode.getRoot();
} else {
treeRootNode.merge(movingNode.getRoot());
}
}
return Optional.ofNullable(treeRootNode);
}
}
// DirectoryNode.java
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
public class DirectoryNode implements Comparable<DirectoryNode> {
private final Set<DirectoryNode> children = new LinkedHashSet<>();
private final String value;
private final DirectoryNode parent;
public DirectoryNode(final DirectoryNode parent, final String value) {
this.parent = parent;
if (this.parent != null) {
this.parent.children.add(this);
}
this.value = value;
}
private static void appendDirectoryNode(
final Set<DirectoryNode> directoryNodes, final DirectoryNode node) {
if (node.isLeaf()) {
return;
}
directoryNodes.add(node);
node.getChildren().parallelStream()
.forEach(childNode -> appendDirectoryNode(directoryNodes, childNode));
}
public Set<DirectoryNode> getChildren() {
return children;
}
public String getValue() {
return value;
}
public DirectoryNode getParent() {
return parent;
}
public String getPath() {
if (getParent() == null) {
return value;
}
return parent.getPath() + "/" + value;
}
public int getLeafCount() {
int leafCount = isLeaf() ? 1 : 0;
for (final DirectoryNode child : children) {
leafCount += child.getLeafCount();
}
return leafCount;
}
public boolean isLeaf() {
return children.isEmpty();
}
/**
* @return Subnodes of this node, which are directory (non-leaf / no-children) nodes
*/
public Set<DirectoryNode> getDirectoryNodes() {
final Set<DirectoryNode> directoryNodes = Collections.synchronizedSortedSet(new TreeSet<>());
appendDirectoryNode(directoryNodes, this);
return directoryNodes;
}
public DirectoryNode getRoot() {
return parent == null ? this : parent.getRoot();
}
public boolean isRootNode() {
return equals(getRoot());
}
public void merge(final DirectoryNode that) {
if (!value.equals(that.value)) {
return;
} else if (children.isEmpty()) {
children.addAll(that.children);
return;
}
final DirectoryNode[] thisChildren = children.toArray(new DirectoryNode[children.size()]);
for (final DirectoryNode thisChild : thisChildren) {
for (final DirectoryNode thatChild : that.children) {
if (thisChild.value.equals(thatChild.value)) {
thisChild.merge(thatChild);
} else {
children.add(thatChild);
}
}
}
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final DirectoryNode that = (DirectoryNode) o;
return Objects.equals(value, that.value) && Objects.equals(parent, that.parent);
}
@Override
public int hashCode() {
return Objects.hash(value, parent);
}
@Override
public String toString() {
return "{" + "value='" + value + '\'' + ", children=" + children + '}';
}
@Override
public int compareTo(final DirectoryNode o) {
return getPath().compareTo(o.getPath());
}
}