package com.levigo.samplelib.util; import java.util.Iterator; import java.util.NoSuchElementException; public abstract class TreeWalker<T> { /** * Generates an empty {@link Iterable}. * @param <T> * @return */ public static <T> Iterable<T> empty() { return new Iterable<T>() { @Override public Iterator<T> iterator() { return new Iterator<T>() { @Override public boolean hasNext() { return false; } @Override public T next() { throw new NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; } public enum TraversalOrder { /** * Parents will be processed before traversing the children */ PARENT_FIRST, /** * children will be processed before the parent */ CHILD_FIRST } public static final class TraversalContext { /* default */int depth; public int getDepth() { return depth; } } /** * Load an {@link Iterable} representing all children, or an empty {@link Iterable} using {@link #empty()}. * @param parent the parent object for which the children shall be loaded. * @return an {@link Iterable} representing all children, or an empty {@link Iterable} using {@link #empty()} */ protected abstract Iterable<T> loadChildren(T parent, TraversalContext context); protected abstract void process(T obj, TraversalContext context); public void walk(T obj) { walk(obj, null); } public void walk(T obj, TraversalOrder order) { doWalk(obj, order, new TraversalContext()); } private void doWalk(T obj, TraversalOrder order, TraversalContext context) { if (obj == null) { throw new IllegalArgumentException("obj must not be null"); } if (order == null) order = TraversalOrder.PARENT_FIRST; if (context == null) context = new TraversalContext(); if (order == TraversalOrder.PARENT_FIRST) process(obj, context); context.depth++; for (T child : loadChildren(obj, context)) doWalk(child, order, context); context.depth--; if (order == TraversalOrder.CHILD_FIRST) process(obj, context); } }