package bjc.utils.data; import java.util.Deque; import java.util.Iterator; import java.util.LinkedList; import java.util.NoSuchElementException; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import static bjc.utils.data.TopDownTransformResult.*; public class TopDownTransformIterator implements Iterator> { private Function picker; private BiFunction, Consumer>>, ITree> transform; private ITree preParent; private ITree postParent; private Deque> preChildren; private Deque> postChildren; private TopDownTransformIterator curChild; private boolean done; private boolean initial; private Deque>> toYield; private Iterator> curYield; public TopDownTransformIterator(Function pickr, BiFunction, Consumer>>, ITree> transfrm, ITree tree) { preParent = tree; preChildren = new LinkedList<>(); postChildren = new LinkedList<>(); toYield = new LinkedList<>(); picker = pickr; transform = transfrm; done = false; initial = true; } public void addYield(Iterator> src) { if(curYield != null) { toYield.push(curYield); } curYield = src; } public boolean hasNext() { return !done; } public ITree flushYields(ITree val) { if(curYield != null) { toYield.add(new SingleIterator<>(val)); if(curYield.hasNext()) { return curYield.next(); } else { while(toYield.size() != 0 && !curYield.hasNext()) { curYield = toYield.pop(); } if(toYield.size() == 0 && !curYield.hasNext()) { curYield = null; return val; } else { return curYield.next(); } } } else { return val; } } public ITree next() { if(done) throw new NoSuchElementException(); if(curYield != null) { if(curYield.hasNext()) { return curYield.next(); } else { while(toYield.size() != 0 && !curYield.hasNext()) { curYield = toYield.pop(); } if(toYield.size() == 0 && !curYield.hasNext()) { curYield = null; } else { return curYield.next(); } } } if(initial) { TopDownTransformResult res = picker.apply(preParent.getHead()); switch(res) { case PASSTHROUGH: postParent = new Tree<>(preParent.getHead()); if(preParent.getChildrenCount() != 0) { for(int i = 0; i < preParent.getChildrenCount(); i++) { preChildren.add(preParent.getChild(i)); } // Return whatever the first child is break; } else { done = true; return flushYields(postParent); } case SKIP: done = true; return flushYields(preParent); case TRANSFORM: done = true; return flushYields(transform.apply(preParent, this::addYield)); case RTRANSFORM: preParent = transform.apply(preParent, this::addYield); return flushYields(preParent); case PUSHDOWN: if(preParent.getChildrenCount() != 0) { for(int i = 0; i < preParent.getChildrenCount(); i++) { preChildren.add(preParent.getChild(i)); } // Return whatever the first child is break; } else { done = true; return flushYields(transform.apply(new Tree<>(preParent.getHead()), this::addYield)); } case PULLUP: ITree intRes = transform.apply(preParent, this::addYield); postParent = new Tree<>(intRes.getHead()); if(intRes.getChildrenCount() != 0) { for(int i = 0; i < intRes.getChildrenCount(); i++) { preChildren.add(intRes.getChild(i)); } // Return whatever the first child is break; } else { done = true; return flushYields(postParent); } default: throw new IllegalArgumentException("Unknown result type " + res); } if(res != RTRANSFORM) initial = false; } if(curChild == null || !curChild.hasNext()) { if(preChildren.size() != 0) { curChild = new TopDownTransformIterator<>(picker, transform, preChildren.pop()); ITree res = curChild.next(); System.out.println("\t\tTRACE: adding node " + res + " to children"); postChildren.add(res); return flushYields(res); } else { ITree res = null; if(postParent == null) { res = new Tree<>(preParent.getHead()); System.out.println("\t\tTRACE: adding nodes " + postChildren + " to " + res); for(ITree child : postChildren) { res.addChild(child); } // res = transform.apply(res, this::addYield); } else { res = postParent; System.out.println("\t\tTRACE: adding nodes " + postChildren + " to " + res); for(ITree child : postChildren) { res.addChild(child); } } done = true; return flushYields(res); } } else { ITree res = curChild.next(); System.out.println("\t\tTRACE: adding node " + res + " to children"); postChildren.add(res); return flushYields(res); } } }