package ru.vyarus.dropwizard.guice.module.context.debug.util; import com.google.common.collect.Lists; import java.util.List; import static ru.vyarus.dropwizard.guice.module.installer.util.Reporter.NEWLINE; import static ru.vyarus.dropwizard.guice.module.installer.util.Reporter.TAB; /** * Tree structure used for pretty console tree render. Tree rendered with one tab border. Subtrees separated by * empty line. * * @author Vyacheslav Rusakov * @since 18.07.2016 */ public class TreeNode { private static final String THROUGH = "\u2502 "; private static final String LEAF = "\u251c\u2500\u2500 "; private static final String LAST_LEAF = "\u2514\u2500\u2500 "; private final String name; private final List<TreeNode> children = Lists.newArrayList(); /** * Creates new node. * * @param name node name * @param args string format arguments */ public TreeNode(final String name, final Object... args) { this.name = String.format(name, args); } /** * @param name node name * @param args string format arguments * @return child node instance, already attached to current node */ public TreeNode child(final String name, final Object... args) { final TreeNode node = new TreeNode(name, args); children.add(node); return node; } /** * Add child node. Useful for situations when node could be appear empty (due to builder specifics) and * empty nodes must be avoided. In other cases {@link #child(String, Object...)} is simpler to use. * * @param node node to add */ public void child(final TreeNode node) { children.add(node); } /** * @return true when node is subtree, false otherwise */ public boolean hasChildren() { return !children.isEmpty(); } /** * Renders tree to provided builder. * * @param res target builder */ public void render(final StringBuilder res) { render(res, "", true, false); } private void render(final StringBuilder res, final String prefix, final boolean isTail, final boolean gapBefore) { if (prefix.isEmpty()) { // root node res.append(TAB).append(name).append(NEWLINE); } else { if (gapBefore) { // gap before or after subtree res.append(prefix).append(THROUGH).append(NEWLINE); } // child node res.append(prefix).append(isTail ? LAST_LEAF : LEAF).append(name).append(NEWLINE); } for (int i = 0; i < children.size(); i++) { final boolean last = i == children.size() - 1; final TreeNode child = children.get(i); final boolean afterSubtree = i > 0 && !child.hasChildren() && children.get(i - 1).hasChildren(); // empty line before subtree or before next child after subtree final boolean gap = child.hasChildren() || afterSubtree; child.render(res, prefix + (isTail ? TAB : THROUGH), last, gap); } } }