package xapi.gen; import xapi.fu.Lazy; import xapi.fu.Out1; import xapi.gen.NodeWithParentWithChildren.ChildStack; import static xapi.fu.Immutable.immutable1; import java.util.Iterator; /** * @author James X. Nelson (james@wetheinter.net) * Created on 12/12/15. */ public abstract class NodeWithParentWithChildren < Parent extends GenBuffer <?, Parent>, Self extends GenBuffer <Parent, Self>, Child extends GenBuffer<Self, ? extends Child>, Stack extends ChildStack<? extends Child> > extends NodeWithParent<Parent, Self> { protected static class ChildStack <V> { private ChildStack next; private final Out1<V> value; public static <V> ChildStack<V> of(V value) { return new ChildStack<>(immutable1(value)); } public ChildStack(Out1<V> value) { this.value = value; } public void setNext(ChildStack newTail) { next = newTail; } public ChildStack getNext() { return next; } public boolean hasNext() { return next != null; } public V getValue() { return value.out1(); } } protected class ChildIterator implements Iterator<Child> { ChildStack<? extends Child> start; protected ChildIterator() { synchronized (head) { // Forces a refresh of our thread's local copy of memory before preparing to iterate. // This could be made cheaper with extensive use of volatile start = head.out1(); } } @Override public boolean hasNext() { return start.hasNext(); } @Override public Child next() { start = start.getNext(); return start.getValue(); } } protected Out1<Stack> head; protected volatile ChildStack tail; public NodeWithParentWithChildren() { head = Lazy.ofNullable(this::newStack, null); } protected NodeWithParentWithChildren(Self node) { this(); this.node = node; } public final void addChild(Child child) { final ChildStack newTail = newStack(child); synchronized (head) { if (tail == null) { head.out1().setNext(tail = newTail); } else { tail.setNext(newTail); tail = newTail; } } onChildAdded(child); } protected void onChildAdded(Child child) { // intentionally empty } protected abstract Stack newStack(Child child); public Iterable<Child> children() { return ChildIterator::new; } }