package co.codewizards.cloudstore.core.dto;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import co.codewizards.cloudstore.core.util.AssertUtil;
public class RepoFileDtoTreeNode implements Iterable<RepoFileDtoTreeNode> {
/**
* Create a single tree from the given {@code repoFileDtos}.
* <p>
* The given {@code repoFileDtos} must meet the following criteria:
* <ul>
* <li>It must not be <code>null</code>.
* <li>It may be empty.
* <li>If it is <i>not</i> empty, it may contain any number of elements, but:
* <ul>
* <li>It must contain exactly one root-node (with
* {@link RepoFileDto#getParentEntityID() RepoFileDto.parentEntityID} being <code>null</code>).
* <li>It must resolve completely, i.e. there must be a {@code RepoFileDto} for every
* referenced {@code parentEntityID}.
* </ul>
* </ul>
* @param repoFileDtos the Dtos to be organized in a tree structure. Must not be <code>null</code>. If
* empty, the method result will be <code>null</code>.
* @return the tree's root node. <code>null</code>, if {@code repoFileDtos} is empty.
* Never <code>null</code>, if {@code repoFileDtos} contains at least one element.
* @throws IllegalArgumentException if the given {@code repoFileDtos} does not meet the criteria stated above.
*/
public static RepoFileDtoTreeNode createTree(final Collection<RepoFileDto> repoFileDtos) throws IllegalArgumentException {
AssertUtil.assertNotNull(repoFileDtos, "repoFileDtos");
if (repoFileDtos.isEmpty())
return null;
final Map<Long, RepoFileDtoTreeNode> id2RepoFileDtoTreeNode = new HashMap<Long, RepoFileDtoTreeNode>();
for (final RepoFileDto repoFileDto : repoFileDtos) {
id2RepoFileDtoTreeNode.put(repoFileDto.getId(), new RepoFileDtoTreeNode(repoFileDto));
}
RepoFileDtoTreeNode rootNode = null;
for (final RepoFileDtoTreeNode node : id2RepoFileDtoTreeNode.values()) {
final Long parentId = node.getRepoFileDto().getParentId();
if (parentId == null) {
if (rootNode != null)
throw new IllegalArgumentException("Multiple root nodes!");
rootNode = node;
}
else {
final RepoFileDtoTreeNode parentNode = id2RepoFileDtoTreeNode.get(parentId);
if (parentNode == null)
throw new IllegalArgumentException("parentEntityID unknown: " + parentId);
parentNode.addChild(node);
}
}
if (rootNode == null)
throw new IllegalArgumentException("There is no root node!");
return rootNode;
}
private static final Comparator<RepoFileDtoTreeNode> nodeComparatorByNameOnly = new Comparator<RepoFileDtoTreeNode>() {
@Override
public int compare(RepoFileDtoTreeNode node0, RepoFileDtoTreeNode node1) {
final String name0 = node0.getRepoFileDto().getName();
final String name1 = node1.getRepoFileDto().getName();
return name0.compareTo(name1);
}
};
private RepoFileDtoTreeNode parent;
private final RepoFileDto repoFileDto;
private final SortedSet<RepoFileDtoTreeNode> children = new TreeSet<RepoFileDtoTreeNode>(nodeComparatorByNameOnly);
private List<RepoFileDtoTreeNode> flattenedTreeList;
protected RepoFileDtoTreeNode(final RepoFileDto repoFileDto) {
this.repoFileDto = AssertUtil.assertNotNull(repoFileDto, "repoFileDto");
}
public RepoFileDto getRepoFileDto() {
return repoFileDto;
}
public RepoFileDtoTreeNode getParent() {
return parent;
}
protected void setParent(final RepoFileDtoTreeNode parent) {
this.parent = parent;
}
public Set<RepoFileDtoTreeNode> getChildren() {
return Collections.unmodifiableSet(children);
}
protected void addChild(final RepoFileDtoTreeNode child) {
child.setParent(this);
children.add(child);
}
/**
* Gets the path from the root to the current node.
* <p>
* The path's elements are separated by a slash ("/").
* @return the path from the root to the current node. Never <code>null</code>.
*/
public String getPath() {
final RepoFileDtoTreeNode parent = getParent();
if (parent == null)
return getRepoFileDto().getName();
else
return parent.getPath() + '/' + getRepoFileDto().getName();
}
public List<RepoFileDtoTreeNode> getLeafs() {
final List<RepoFileDtoTreeNode> leafs = new ArrayList<RepoFileDtoTreeNode>();
populateLeafs(this, leafs);
return leafs;
}
private void populateLeafs(final RepoFileDtoTreeNode node, final List<RepoFileDtoTreeNode> leafs) {
if (node.getChildren().isEmpty()) {
leafs.add(node);
}
for (final RepoFileDtoTreeNode child : node.getChildren()) {
populateLeafs(child, leafs);
}
}
@Override
public Iterator<RepoFileDtoTreeNode> iterator() {
return getFlattenedTreeList().iterator();
}
public int size() {
return getFlattenedTreeList().size();
}
private List<RepoFileDtoTreeNode> getFlattenedTreeList() {
if (flattenedTreeList == null) {
final List<RepoFileDtoTreeNode> list = new ArrayList<RepoFileDtoTreeNode>();
flattenTree(list, this);
flattenedTreeList = list;
}
return flattenedTreeList;
}
private void flattenTree(final List<RepoFileDtoTreeNode> result, final RepoFileDtoTreeNode node) {
result.add(node);
for (final RepoFileDtoTreeNode child : node.getChildren()) {
flattenTree(result, child);
}
}
public RepoFileDtoTreeNode getRoot() {
final RepoFileDtoTreeNode parent = getParent();
if (parent == null)
return this;
else
return parent.getRoot();
}
}