alexiil.node.core.NodeGraph.java Source code

Java tutorial

Introduction

Here is the source code for alexiil.node.core.NodeGraph.java

Source

/* Copyright (c) 2015 AlexIIL
 *
 * See the file "LICENSE" for copying permission. */
package alexiil.node.core;

import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.List;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Queues;

public class NodeGraph {
    private final List<INode> nodes = Lists.newArrayList();

    private final Multimap<GraphConnection<?>, GraphConnection<?>> connections = HashMultimap.create();

    private boolean isIterating = false;

    List<INode> getNodes() {
        return Collections.unmodifiableList(nodes);
    }

    public <N extends INode> N addCopyOf(N node, String name) {
        INode copy = node.createCopy(this, name);
        addNode(copy);
        return (N) copy;
    }

    public void addNode(INode node) {
        if (node == null)
            throw new NullPointerException("node");
        if (nodes.contains(node))
            throw new IllegalArgumentException("Already contained the node!");
        if (node.getGraph() != this)
            throw new IllegalArgumentException("The node was contained within a different graph!");
        nodes.add(node);
    }

    public INode getNode(String name) {
        for (INode node : nodes) {
            if (node.getName().equals(name))
                return node;
        }
        return null;
    }

    /** @param node The node to check
     * @throws IllegalArgumentException if the node was invalid */
    private void checkNode(INode node, String name) throws IllegalArgumentException {
        if (node == null)
            throw new NullPointerException(name + " was null!");
        if (!nodes.contains(node))
            throw new IllegalArgumentException("Did not contain " + node.getName() + " for " + name);
    }

    public void connectIO(INode fromNode, String fromName, INode toNode, String toName) {
        checkNode(fromNode, "fromNode");
        checkNode(toNode, "toNode");

        if (nodes.indexOf(fromNode) >= nodes.indexOf(toNode))
            throw new IllegalArgumentException("Bad order!");
        if (!fromNode.getOutputs().containsKey(fromName))
            throw new IllegalArgumentException("Did not contain the output key " + fromName);
        if (!toNode.getInputs().containsKey(toName))
            throw new IllegalArgumentException("Did not contain the input key " + toName);

        // Ignore the generic type warning, its fine :)
        GraphConnection out = fromNode.getOutputs().get(fromName);
        GraphConnection in = toNode.getInputs().get(toName);
        // Basically if (out instanceof in)
        if (in.getTypeClass().isAssignableFrom(out.getTypeClass()))
            connect(out, in, in.getTypeClass());
        else
            throw new IllegalArgumentException("The nodes did not share a common class!");
    }

    private <T> void connect(GraphConnection<? extends T> out, GraphConnection<T> in, Class<T> typeClass) {
        connections.put(out, in);
        in.connectedOutput = out;
        out.connectedInputs.add(in);
    }

    public void printState() {
        for (INode node : nodes) {
            System.out.println("Node #" + nodes.indexOf(node) + " called " + node.getName());
            if (node instanceof AbstractNode) {
                AbstractNode abs = (AbstractNode) node;
                abs.printState();
            }
        }
    }

    public <E> GraphConnection<E> provideOutputConnection(INode node, String outputName, Class<E> clazz) {
        GraphConnection<E> conn = new GraphConnection<E>(clazz, node, outputName);
        // Hmmm. I feel like I should do something here
        return conn;
    }

    public <E> GraphConnection<E> provideInputConnection(INode node, String inputName, Class<E> clazz) {
        GraphConnection<E> conn = new GraphConnection<E>(clazz, node, inputName);
        // Hmmm. I feel like I should do something here
        return conn;
    }

    public void iterate() {
        if (isIterating)
            return;
        isIterating = true;
        boolean computed;
        do {
            propagateOutputRequests();
            int n = 0;
            computed = false;
            for (INode node : nodes) {
                System.out.println("    Loop START " + n);
                computed |= node.computeIfCan();
                System.out.println("    Loop END " + n++);
            }
        } while (computed);
        isIterating = false;
    }

    /** Pushes the requests up the list of nodes from bottom up. Returns something? Does it even need to? */
    private void propagateOutputRequests() {
        int i = nodes.size();
        for (INode node : Lists.reverse(nodes)) {
            System.out.println("Request START " + --i);
            node.askForElements();
            for (GraphConnection<?> conn : node.getInputs().values()) {
                if (conn.getRequestedElements() > 0) {
                    GraphConnection<?> out = conn.connectedOutput;
                    int max = out.getRequestedElements();
                    for (GraphConnection<?> conn2 : out.connectedInputs) {
                        max = Math.max(max, conn2.getRequestedElements());
                    }
                    out.requestUpTo(max);
                }
            }
        }
    }

    public class GraphConnection<E> {
        final List<GraphConnection<? super E>> connectedInputs = Lists.newArrayList();
        GraphConnection<? extends E> connectedOutput;
        private final Class<E> clazz;
        private final INode node;
        private final String name;

        private final Deque<E> internalDeque = Queues.newArrayDeque();
        private int requested = 0;

        public GraphConnection(Class<E> clazz, INode node, String name) {
            this.clazz = clazz;
            this.node = node;
            this.name = name;
        }

        public Class<E> getTypeClass() {
            return clazz;
        }

        public INode getNode() {
            return node;
        }

        public void push(E val) {
            String call = connectedOutput == null ? "output" : "input";
            System.out.println("State of " + call + " " + name + " for node " + node.getName() + " before pushing");
            ((AbstractNode) node).printState();
            if (requested > 0) {
                requested--;
            }
            if (connectedOutput == null) {
                for (GraphConnection<? super E> conn : connectedInputs) {
                    conn.push(val);
                }
            } else {
                internalDeque.push(val);
            }
            System.out.println("State of " + call + " " + name + " for node " + node.getName() + " AFTER pushing");
            ((AbstractNode) node).printState();
        }

        public E pop() {
            if (getRemainingElements() == 0)
                throw new IllegalStateException("Not enough elements!");
            return internalDeque.pop();
        }

        public int getRemainingElements() {
            return internalDeque.size();
        }

        public int getRequestedElements() {
            return requested;
        }

        public String getName() {
            return name;
        }

        /** Requests that the number of elements be available to use */
        public void requestUpTo(int count) {
            String call = connectedOutput == null ? "output" : "input";
            System.out.println("State of " + call + " " + name + " for node " + node.getName()
                    + " before requesting " + count);
            ((AbstractNode) node).printState();

            requested = Math.max(requested, count - getRemainingElements());

            System.out.println(
                    "State of " + call + " " + name + " for node " + node.getName() + " AFTER requesting " + count);
            ((AbstractNode) node).printState();
        }

        @Override
        public String toString() {
            String out = (connectedOutput == null ? "null"
                    : connectedOutput.node.getName() + "." + connectedOutput.name);

            String[] ins = new String[connectedInputs.size()];
            for (int i = 0; i < ins.length; i++) {
                ins[i] = connectedInputs.get(i).node.getName() + "." + connectedInputs.get(i).name;
            }
            return "requested = " + requested + ", input = " + out + ", outputs = " + Arrays.toString(ins)
                    + ", elements = " + internalDeque;
        }
    }
}