/*
* esodata - data structures and other things, of varying utility
* Copyright 2022, Ben Culkin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package bjc.data;
import static bjc.data.TopDownTransformResult.RTRANSFORM;
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;
/*
* @TODO 10/11/17 Ben Culkin :TopDownStep
*
* Figure out what is broken with this, and fix it so that step-wise
* iteration works correctly.
*/
/**
* An iterative top-down transform of a tree.
*
* @author EVE
*
* @param
* The type of the nodes in the tree.
*/
public class TopDownTransformIterator
implements Iterator> {
/**
* Alias type for a tree transformation.
*
* @author student
*
* @param
* The type contained in the tree.
*/
public interface TreeTransform extends BiFunction,
Consumer>>, Tree> {
// Alias type; no body is needed
}
/*
* The function that picks how to transform a given node
*/
private final Function picker;
/*
* The transform to apply to a given node.
*/
private final TreeTransform transform;
private Tree preParent;
private Tree postParent;
private final Deque> preChildren;
private final Deque> postChildren;
private TopDownTransformIterator curChild;
private boolean done;
private boolean initial;
private final Deque>> toYield;
private Iterator> currYield;
/**
* Create a new tree iterator.
*
* @param pickr
* The function to use to pick how to process nodes.
* @param transfrm
* The transform to apply to the nodes.
* @param tree
* The tree to transform.
*/
public TopDownTransformIterator(
final Function pickr,
final TreeTransform transfrm,
final Tree tree) {
preParent = tree;
preChildren = new LinkedList<>();
postChildren = new LinkedList<>();
toYield = new LinkedList<>();
picker = pickr;
transform = transfrm;
done = false;
initial = true;
}
/**
* Add a set of nodes to yield.
*
* @param src
* The nodes to yield.
*/
public void addYield(final Iterator> src) {
if (currYield != null) toYield.push(currYield);
currYield = src;
}
@Override
public boolean hasNext() {
return !done;
}
/**
* Get the next yielded value.
*
* @param val
* The sentinel value to yield.
*
* @return The next yielded value.
*/
public Tree flushYields(final Tree val) {
if (currYield != null) {
/*
* We have non-sentinel values to yield.
*/
/*
* Add the sentinel to yield later.
*/
toYield.add(new SingleIterator<>(val));
if (currYield.hasNext()) return currYield.next();
while (toYield.size() != 0 && !currYield.hasNext()) {
currYield = toYield.pop();
}
if (toYield.size() == 0 && !currYield.hasNext()) {
currYield = null;
return val;
}
return currYield.next();
}
return val;
}
@Override
public Tree next() {
if (done) throw new NoSuchElementException();
/*
* Flush any values that need to be yielded.
*/
if (currYield != null) {
Tree yeld = flushYields(null);
if (yeld != null) return yeld;
}
if (initial) {
/*
* Get the way we are transforming.
*/
final TopDownTransformResult res = picker.apply(preParent.getHead());
switch (res) {
case PASSTHROUGH:
postParent = new SimpleTree<>(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;
}
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;
}
done = true;
return flushYields(
transform.apply(new SimpleTree<>(preParent.getHead()), this::addYield));
case PULLUP:
final Tree intRes
= transform.apply(preParent, this::addYield);
postParent = new SimpleTree<>(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;
}
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());
final Tree res = curChild.next();
// System.out.println("\t\tTRACE: adding node " + res + " to children");
postChildren.add(res);
return flushYields(res);
}
Tree res = null;
if (postParent == null) {
res = new SimpleTree<>(preParent.getHead());
// System.out.println("\t\tTRACE: adding nodes " + postChildren + " to " +
// res);
for (final Tree 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 (final Tree child : postChildren) {
res.addChild(child);
}
}
done = true;
return flushYields(res);
}
final Tree res = curChild.next();
// System.out.println("\t\tTRACE: adding node " + res + " to children");
postChildren.add(res);
return flushYields(res);
}
}