Evaluation.java

/**
 *
 * This file is part of TRIO.
 *
 * TRIO is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * TRIO 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with TRIO.  If not, see <http://www.gnu.org/licenses/>.
 */
package eu.diversify.trio.core;

import eu.diversify.trio.util.Require;
import java.util.*;

/**
 * Iterate through the sub parts of a system and notifies the associated
 * listener of the progress.
 */
public class Evaluation {

    public static Evaluation evaluate(SystemVisitor visitor) {
        return new Evaluation(visitor);
    }

    public static Evaluation evaluate(SystemVisitor... visitors) {
        return new Evaluation(new Dispatcher(visitors));
    }

    private final SystemVisitor listener;
    private final Deque<Memento> stack;

    public Evaluation(SystemVisitor listener) {
        this.stack = new LinkedList<Memento>();
        this.listener = listener;
    }

    public void on(SystemPart singleRoot) {
        on(Collections.singleton(singleRoot));
    }

    public void on(Collection<SystemPart> roots) {
        this.stack.clear();
        this.stack.push(new Memento(roots));
        run();
    }

    private void run() {
        while (hasNext()) {
            next();
        }
    }

    public boolean hasNext() {
        return !stack.isEmpty();
    }

    public SystemPart next() {
        Require.isTrue(hasNext(), "There is no next element");

        final SystemPart next = currentPart().next();
        push(next);
        backtrack();
        return next;
    }

    private void push(final SystemPart next) {
        final Memento memento = new Memento(next);
        stack.push(memento);
        if (listener != null) {
            next.begin(listener);
        }
    }

    private void backtrack() {
        while (!stack.isEmpty() && !currentPart().hasNext()) {
            pop();
        }

        assert stack.isEmpty() || currentPart().hasNext(): "Wrong usage of the stack";
    }

    private void pop() {
        final Memento memento = stack.pop();
        if (listener != null) {
            memento.end(listener);
        }
    }

    private Memento currentPart() {
        return stack.peek();
    }

    private static class Memento {

        private final SystemPart parent;
        private final Iterator<SystemPart> children;

        public Memento(Collection<SystemPart> children) {
            this(null, children.iterator());
        }

        public Memento(SystemPart parent) {
            this(parent, parent.subParts().iterator());
        }

        public Memento(SystemPart parent, Iterator<SystemPart> iterator) {
            assert iterator != null: "A 'null' iterator shall not be pushed on the stack!";

            this.parent = parent;
            this.children = iterator;
        }

        public boolean hasNext() {
            return this.children.hasNext();
        }

        public SystemPart next() {
            return this.children.next();
        }

        public void end(SystemVisitor visitor) {
            if (parent != null) {
                parent.end(visitor);
            }
        }

    }

}