From f4e9c3e8233ee47fa09c878af6af8e5ddd41ef60 Mon Sep 17 00:00:00 2001 From: Nekojimi Date: Sun, 17 Jul 2022 23:02:04 +0100 Subject: [PATCH] Initial version of a lot of behaviour. --- .../nodeprocessor/ClassMethodType.java | 30 +++ .../java/moe/nekojimi/nodeprocessor/Main.java | 178 +++++++++++++++++- .../moe/nekojimi/nodeprocessor/NodeSpec.java | 36 ++++ .../moe/nekojimi/nodeprocessor/Program.java | 108 +++++++++++ .../nodeprocessor/links/ConstantLink.java | 42 +++++ .../nekojimi/nodeprocessor/links/Link.java | 83 ++++++++ .../nodeprocessor/links/PullLink.java | 38 ++++ .../nodeprocessor/links/QueueLink.java | 42 +++++ .../nodeprocessor/nodes/DebugOutNode.java | 35 ++++ .../nodeprocessor/nodes/MethodNode.java | 76 ++++++++ .../nekojimi/nodeprocessor/nodes/Node.java | 158 ++++++++++++++++ .../nodeprocessor/nodes/ObjectNode.java | 160 ++++++++++++++++ .../nodeprocessor/nodes/SerialiserNode.java | 57 ++++++ .../nodeprocessor/nodes/ValueNode.java | 55 ++++++ .../nodeprocessor/ports/ConstantOutPort.java | 18 ++ .../nekojimi/nodeprocessor/ports/InPort.java | 46 +++++ .../nekojimi/nodeprocessor/ports/OutPort.java | 45 +++++ .../nekojimi/nodeprocessor/ports/Port.java | 83 ++++++++ .../nodeprocessor/ports/PullOutPort.java | 37 ++++ .../nodeprocessor/ports/QueueOutPort.java | 18 ++ 20 files changed, 1343 insertions(+), 2 deletions(-) create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/ClassMethodType.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/NodeSpec.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/Program.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/links/ConstantLink.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/links/Link.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/links/PullLink.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/links/QueueLink.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/nodes/DebugOutNode.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/nodes/MethodNode.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/nodes/Node.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/nodes/ObjectNode.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/nodes/SerialiserNode.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/nodes/ValueNode.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/ports/ConstantOutPort.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/ports/InPort.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/ports/OutPort.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/ports/Port.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/ports/PullOutPort.java create mode 100644 src/main/java/moe/nekojimi/nodeprocessor/ports/QueueOutPort.java diff --git a/src/main/java/moe/nekojimi/nodeprocessor/ClassMethodType.java b/src/main/java/moe/nekojimi/nodeprocessor/ClassMethodType.java new file mode 100644 index 0000000..578ebb9 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/ClassMethodType.java @@ -0,0 +1,30 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor; + +/** + * + * @author jimj316 + */ +public enum ClassMethodType +{ + /** + * takes no input, makes no changes, returns a value + * Should be added to an object node, given a constant port + */ + GETTER, + SETTER, // takes one input, changes one field, returns nothing + ADDER, // takes one input, makes other state changes, returns nothing + TAKER, // takes no input, makes other state changes, returns a value + CALLBACK, // takes a single argument of an abstract class + ACTION, // takes no input or output + + TRANSFORMER, // takes input, changes the object, then returns the object (eg. builder methods) + FUNCTION, // takes one or more unrelated types, and returns a result + CONSTANT, // static; no input, returns a value + FACTORY, + OTHER, +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/Main.java b/src/main/java/moe/nekojimi/nodeprocessor/Main.java index 67f2d1d..29b02e2 100644 --- a/src/main/java/moe/nekojimi/nodeprocessor/Main.java +++ b/src/main/java/moe/nekojimi/nodeprocessor/Main.java @@ -1,16 +1,190 @@ package moe.nekojimi.nodeprocessor; +import java.io.*; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.time.OffsetDateTime; +import java.util.*; +import java.util.stream.Collectors; +import moe.nekojimi.nodeprocessor.links.Link; +import moe.nekojimi.nodeprocessor.nodes.MethodNode; +import moe.nekojimi.nodeprocessor.nodes.DebugOutNode; +import moe.nekojimi.nodeprocessor.nodes.ValueNode; +import org.reflections.Reflections; +import org.reflections.scanners.SubTypesScanner; + /** * * @author Nekojimi */ public class Main { + + private static final Class[] testClasses = + { + String.class, + Integer.class, + Math.class, + HashMap.class, + OffsetDateTime.class, + + }; + /** * @param args the command line arguments */ - public static void main(String[] args) + public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, IOException + { + for (Class testClass : testClasses) + { + System.out.println(testClass.getName() + ":"); + Method[] methods = testClass.getMethods(); + for (Method method : methods) + { + if (method.getDeclaringClass() == testClass) + { + ClassMethodType type = guessMethodType(method); + System.out.println("\t" + method.toString()); + System.out.println("\t\t" + type.name()); + } + } + } +// helloWorldProgram(); + } + + private static void fibonnaciProgram() + { + Program program = new Program(); + } + + private static void addNumbersProgram() throws NoSuchMethodException { - System.out.println("Hello world!"); + Program program = new Program(); + ValueNode twoNode = new ValueNode<>(program, Integer.class); + twoNode.setValue(2); + ValueNode threeNode = new ValueNode<>(program, Integer.class); + threeNode.setValue(3); + + MethodNode methodNode = new MethodNode<>(program, Integer.class, Integer.class.getMethod("sum", Integer.class, Integer.class)); + + DebugOutNode debugOutNode = new DebugOutNode(program); + +// Link.makeLink(Integer.class, twoNode, fromPort, twoNode, toPort); + + program.start(); } + + private static void helloWorldProgram() throws SecurityException, NoSuchMethodException + { + Program program = new Program(); + ValueNode valueNode = new ValueNode<>(program, String.class); + valueNode.setValue("Hello world!"); + final Method method = String.class.getMethod("toUpperCase"); + MethodNode methodNode = new MethodNode<>(program, String.class, method); + DebugOutNode debugOutNode = new DebugOutNode(program); + + Link.makeLink(String.class, valueNode, "out", methodNode, "object"); + Link.makeLink(String.class, methodNode, "out", debugOutNode, "in"); + + program.start(); + } + + public static Set findAllClassesUsingClassLoader(String packageName) + { + InputStream stream = ClassLoader.getSystemClassLoader() + .getResourceAsStream(packageName.replaceAll("[.]", "/")); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + return reader.lines() + .filter(line -> line.endsWith(".class")) + .map(line -> getClass(line, packageName)) + .collect(Collectors.toSet()); + } + + public static Set findAllClassesUsingReflectionsLibrary(String packageName) + { + Reflections reflections = new Reflections(packageName, new SubTypesScanner(false)); + return reflections.getSubTypesOf(Object.class) + .stream() + .collect(Collectors.toSet()); + } + + private static Class getClass(String className, String packageName) + { + try + { + return Class.forName(packageName + "." + + className.substring(0, className.lastIndexOf('.'))); + } catch (ClassNotFoundException e) + { + // handle the exception + } + return null; + } + + private static ClassMethodType guessMethodType(Method method) + { + String name = method.getName(); + Class clazz = method.getDeclaringClass(); + Class returnType = method.getReturnType(); + boolean hasReturn = (returnType != Void.TYPE); + int paramCount = method.getParameterCount(); + Class firstParamType = null; + Parameter[] parameters = method.getParameters(); + if (paramCount > 0) + firstParamType = parameters[0].getType(); + boolean isStatic = Modifier.isStatic(method.getModifiers()); + + if (!isStatic) + { + if (hasReturn) + { + if (paramCount == 0) // methods that only return + { + if (name.startsWith("take") || name.startsWith("poll") || name.startsWith("remove") || name.startsWith("pop") || name.startsWith("read")) + return ClassMethodType.TAKER; + else + return ClassMethodType.GETTER; + } else if (clazz.isAssignableFrom(returnType)) + return ClassMethodType.TRANSFORMER; + else if (paramCount == 1 + && Modifier.isAbstract(parameters[0].getType().getModifiers()) + && !parameters[0].getType().isPrimitive() + && name.toLowerCase().matches("(on.+|.*listener.*|.*observer.*)")) + { + int abstractMethods = 0; + Method[] paramTypeMethods = parameters[0].getType().getMethods(); + for (Method paramTypeMethod : paramTypeMethods) + { + if (Modifier.isAbstract(paramTypeMethod.getModifiers())) + abstractMethods++; + } + if (abstractMethods >= 1) + return ClassMethodType.CALLBACK; + } else + return ClassMethodType.FUNCTION; + } else // returns nothing + { + if (paramCount == 1) + { + if (name.startsWith("give") || name.startsWith("add") || name.startsWith("push") || name.startsWith("write")) + return ClassMethodType.ADDER; + else + return ClassMethodType.SETTER; + } else if (paramCount == 0) // no input, return nothing + return ClassMethodType.ACTION; + } + } else // static method + { + if (clazz.isAssignableFrom(returnType)) + return ClassMethodType.FACTORY; + else if (hasReturn && paramCount > 0) + return ClassMethodType.FUNCTION; + else if (hasReturn && paramCount == 0) + return ClassMethodType.CONSTANT; + } + + return ClassMethodType.OTHER; + } + } diff --git a/src/main/java/moe/nekojimi/nodeprocessor/NodeSpec.java b/src/main/java/moe/nekojimi/nodeprocessor/NodeSpec.java new file mode 100644 index 0000000..f29cdad --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/NodeSpec.java @@ -0,0 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor; + +import com.amihaiemil.eoyaml.YamlMapping; +import moe.nekojimi.nodeprocessor.nodes.Node; + +/** + * Specifies a type of node that can exist. + * Holds a list of one or more methods and their archetypes to be added to the + * new node??? + * + * @author jimj316 + */ +public class NodeSpec +{ + + public static NodeSpec fromYAML(YamlMapping yaml) throws ClassNotFoundException + { + Class clazz = ClassLoader.getSystemClassLoader().loadClass(yaml.string("class")); + final NodeSpec nodeSpec = new NodeSpec(clazz); + return nodeSpec; + } + + public NodeSpec(Class clazz) + { + } + + public T construct() + { + return null; + } +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/Program.java b/src/main/java/moe/nekojimi/nodeprocessor/Program.java new file mode 100644 index 0000000..19b87f4 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/Program.java @@ -0,0 +1,108 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor; + +import java.util.*; +import moe.nekojimi.nodeprocessor.links.Link; +import moe.nekojimi.nodeprocessor.nodes.Node; + +/** + * + * @author jimj316 + */ +public class Program +{ + private final List nodes = new ArrayList<>(); + private final Set links = new HashSet<>(); + + private final Queue executionQueue = new LinkedList<>(); + private final Set idleNodes = new HashSet<>(); + + private Thread thread; + private State state = State.STOPPED; + + public void addNode(Node node) + { + if (!nodes.contains(node)) + nodes.add(node); + nodes.sort(Node.DEPENDENCY_ORDER); + } + + public void addLink(Link link) + { + links.add(link); + } + + public void queueNodeProcess(Node node) + { + executionQueue.add(node); + } + + public void setIdleNode(Node node, boolean idle) + { + if (idle) + idleNodes.add(node); + else + idleNodes.remove(node); + } + + public State getState() + { + return state; + } + + + public void start() + { + if (state != State.STOPPED) + throw new IllegalStateException("Thread isn't stopped!"); + if (thread == null) + thread = new Thread(runnable); + thread.start(); + } + + private final Runnable runnable = () -> + { + state = State.RUNNING; + for (Node node : nodes) + { + node.reset(); + if (node.getNumberOfInputs() == 0) + node.signal(); + } + while (!executionQueue.isEmpty()) + { + Node node = executionQueue.poll(); + boolean done = node.process(); + if (!done) + queueNodeProcess(node); + } + state = State.STOPPED; + }; + + public List getNodes() + { + return nodes; + } + + public Set getLinks() + { + return links; + } + + public Set getIdleNodes() + { + return idleNodes; + } + + public enum State + { + STOPPED, + PAUSED, + RUNNING, + IDLE + } +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/links/ConstantLink.java b/src/main/java/moe/nekojimi/nodeprocessor/links/ConstantLink.java new file mode 100644 index 0000000..5eba525 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/links/ConstantLink.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.links; + +import moe.nekojimi.nodeprocessor.ports.ConstantOutPort; +import moe.nekojimi.nodeprocessor.ports.InPort; + + +public class ConstantLink extends Link, ConstantOutPort> +{ + + T value = null; + + public ConstantLink(ConstantOutPort from, InPort to) + { + super(from, to); + } + + @Override + public T read() + { + return value; + } + + @Override + public void write(T val) + { + if (val != value) + signal(); + value = val; + } + + @Override + public boolean has() + { + return value != null; + } + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/links/Link.java b/src/main/java/moe/nekojimi/nodeprocessor/links/Link.java new file mode 100644 index 0000000..0eff2a2 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/links/Link.java @@ -0,0 +1,83 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.links; + +import moe.nekojimi.nodeprocessor.nodes.Node; +import moe.nekojimi.nodeprocessor.ports.ConstantOutPort; +import moe.nekojimi.nodeprocessor.ports.InPort; +import moe.nekojimi.nodeprocessor.ports.OutPort; +import moe.nekojimi.nodeprocessor.ports.QueueOutPort; + +/** + * + * @author jimj316 + */ +public abstract class Link, O extends OutPort> +{ + final O from; + private final I to; + + public Link(O from, I to) + { + this.from = from; + this.to = to; + connect(); + } + + public final void connect() + { + from.connect(this); + to.connect(this); + from.getNode().getProgram().addLink(this); + to.getNode().getProgram().addLink(this); + } + + public final void disconnect() + { + from.disconnect(this); + to.disconnect(this); + } + + public abstract T read(); + + public abstract void write(T val); + + public abstract boolean has(); + + public static Link makeLink(Class clazz, Node from, String fromPort, Node to, String toPort) + { + OutPort outPort = from.getOutPort(fromPort, clazz); + InPort inPort = to.getInPort(toPort, clazz); + if (outPort instanceof QueueOutPort) + { + QueueOutPort q = (QueueOutPort) outPort; + return new QueueLink<>(q, inPort); + } else if (outPort instanceof ConstantOutPort) + { + ConstantOutPort c = (ConstantOutPort) outPort; + return new ConstantLink<>(c, inPort); + } else + { + return null; + } + } + + protected void signal() + { + to.signal(); + } + + public O getFrom() + { + return from; + } + + public I getTo() + { + return to; + } + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/links/PullLink.java b/src/main/java/moe/nekojimi/nodeprocessor/links/PullLink.java new file mode 100644 index 0000000..00231ad --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/links/PullLink.java @@ -0,0 +1,38 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.links; + +import moe.nekojimi.nodeprocessor.ports.InPort; +import moe.nekojimi.nodeprocessor.ports.PullOutPort; + + +public class PullLink extends Link, PullOutPort> +{ + + public PullLink(PullOutPort from, InPort to) + { + super(from, to); + } + + @Override + public T read() + { + return from.pull(); + } + + @Override + public void write(T val) + { + // not used + } + + @Override + public boolean has() + { + return true; + } + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/links/QueueLink.java b/src/main/java/moe/nekojimi/nodeprocessor/links/QueueLink.java new file mode 100644 index 0000000..6a399d9 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/links/QueueLink.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.links; + +import java.util.LinkedList; +import java.util.Queue; +import moe.nekojimi.nodeprocessor.ports.InPort; +import moe.nekojimi.nodeprocessor.ports.QueueOutPort; + + +public class QueueLink extends Link, QueueOutPort> +{ + + private final Queue queue = new LinkedList<>(); + + public QueueLink(QueueOutPort from, InPort to) + { + super(from, to); + } + + @Override + public T read() + { + return queue.poll(); + } + + @Override + public void write(T val) + { + queue.add(val); + signal(); + } + + @Override + public boolean has() + { + return !queue.isEmpty(); + } +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/nodes/DebugOutNode.java b/src/main/java/moe/nekojimi/nodeprocessor/nodes/DebugOutNode.java new file mode 100644 index 0000000..1914b1e --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/nodes/DebugOutNode.java @@ -0,0 +1,35 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.nodes; + +import moe.nekojimi.nodeprocessor.Program; +import moe.nekojimi.nodeprocessor.ports.InPort; + +public class DebugOutNode extends Node +{ + + public DebugOutNode(Program scene) + { + super(scene); + addInPort(new InPort("in", Object.class, this)); + } + + @Override + public boolean process() + { + InPort inPort = getInPort("in", Object.class); + System.out.println(inPort.get()); + return true; + } + + @Override + public boolean check() + { + InPort inPort = getInPort("in", Object.class); + return inPort.has(); + } + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/nodes/MethodNode.java b/src/main/java/moe/nekojimi/nodeprocessor/nodes/MethodNode.java new file mode 100644 index 0000000..53e1b18 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/nodes/MethodNode.java @@ -0,0 +1,76 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.nodes; + +import moe.nekojimi.nodeprocessor.ports.InPort; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.util.logging.Level; +import java.util.logging.Logger; +import moe.nekojimi.nodeprocessor.Program; +import moe.nekojimi.nodeprocessor.ports.QueueOutPort; + +/** + * + * @author jimj316 + * @param the class that declares this method. + */ +public class MethodNode extends Node +{ + + private final InPort objectInPort; + private final QueueOutPort returnOutPort; + private final Method method; + + public MethodNode(Program scene, Class clazz, Method method) + { + super(scene); + assert (method.getDeclaringClass() == clazz); + if (Modifier.isStatic(method.getModifiers())) + objectInPort = null; + else + { + objectInPort = new InPort<>("object", clazz, this); + addInPort(objectInPort); + } + this.method = method; + Parameter[] parameters = method.getParameters(); + for (Parameter parameter : parameters) + addInPort(new InPort(parameter.getName(), parameter.getType(), this)); + Class returnType = method.getReturnType(); + returnOutPort = new QueueOutPort("out", returnType, this); + addOutPort(returnOutPort); + + setName(method.getDeclaringClass().getSimpleName() + " " + method.getName()); + } + + + @Override + public boolean process() + { + try + { + C object = null; + if (objectInPort != null) + object = objectInPort.get(); + Object ret = method.invoke(object); + returnOutPort.set(ret); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) + { + Logger.getLogger(MethodNode.class.getName()).log(Level.SEVERE, null, ex); + } + return true; + } + + @Override + public boolean check() + { + return getInPorts().stream().allMatch(in -> (in.has())); + } + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/nodes/Node.java b/src/main/java/moe/nekojimi/nodeprocessor/nodes/Node.java new file mode 100644 index 0000000..3ccaf4c --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/nodes/Node.java @@ -0,0 +1,158 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.nodes; + +import java.util.*; +import moe.nekojimi.nodeprocessor.ports.InPort; +import moe.nekojimi.nodeprocessor.ports.OutPort; +import moe.nekojimi.nodeprocessor.Program; + +/** + * + * @author jimj316 + */ +public abstract class Node +{ +// protected Map inLinks = new HashMap<>(); +// protected Map outLinks = new HashMap<>(); + + private final Program program; + private Map inPorts = new HashMap<>(); + private Map outPorts = new HashMap<>(); + private String name; + + public Node(Program program) + { + this.program = program; + program.addNode(this); + this.name = this.getClass().getSimpleName(); + } + + public String getName() + { + return name; + } + + protected void setName(String name) + { + this.name = name; + } + + protected final void addInPort(InPort port) + { + inPorts.putIfAbsent(getPortName(port.getClazz(), port.getName()), port); + } + protected final void addOutPort(OutPort port) + { + outPorts.putIfAbsent(getPortName(port.getClazz(), port.getName()), port); + } + + public final InPort getInPort(String name, Class clazz) + { + Class c = clazz; + InPort ret; + do + { + ret = inPorts.get(getPortName(c, name)); + c = c.getSuperclass(); + } while (ret == null && c != null); + return ret; + } + + public final OutPort getOutPort(String name, Class clazz) + { + Class c = clazz; + OutPort ret; + do + { + ret = outPorts.get(getPortName(clazz, name)); + c = c.getSuperclass(); + } while (ret == null && c != null); + return ret; + } + + public final Collection getInPorts() + { + return inPorts.values(); + } + + public final Collection getOutPorts() + { + return outPorts.values(); + } + + private static String getPortName(Class clazz, String name) + { + return clazz.getCanonicalName() + "-" + name; + } + + public int getNumberOfInputs() + { + return inPorts.values().stream().mapToInt((t) -> t.getNumberOfConnections()).sum(); + } + + public int getNumberOfOutputs() + { + return outPorts.values().stream().mapToInt((t) -> t.getNumberOfConnections()).sum(); + } + + public Program getProgram() + { + return program; + } + + public boolean dependsOn(Node other) + { + // TODO: this is a crude search that will enter an infinite loop if a node depends on itself + for (InPort port : inPorts.values()) + { + Set linkedNodes = port.getLinkedNodes(); + if (linkedNodes.contains(other)) + return true; + else + for (Node dependent : linkedNodes) + { + if (dependent.dependsOn(other)) + return true; + } + } + return false; + } + + /** + * Processes this node's inputs, and generates output (or performs some + * other useful function). + * + * @return true if there will be no more output until an input is received; + * false if process() should be called again. + */ + public abstract boolean process(); + + public abstract boolean check(); + + public void signal() + { + if (check()) + program.queueNodeProcess(this); + } + + public void reset() + { + // assume most nodes are stateless + // default implementation does nothing + } + + public static final Comparator DEPENDENCY_ORDER = (Node a, Node b) -> + { + int ret = 0; + if (a.dependsOn(b)) + ret--; + if (b.dependsOn(a)) + ret++; + return ret; + }; + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/nodes/ObjectNode.java b/src/main/java/moe/nekojimi/nodeprocessor/nodes/ObjectNode.java new file mode 100644 index 0000000..1ff62ff --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/nodes/ObjectNode.java @@ -0,0 +1,160 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.nodes; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; +import moe.nekojimi.nodeprocessor.Program; +import moe.nekojimi.nodeprocessor.ports.ConstantOutPort; +import moe.nekojimi.nodeprocessor.ports.InPort; +import moe.nekojimi.nodeprocessor.ports.OutPort; + +/** + * Represents an object that performs some function. + * Has a set of inputs that will cause the object to be (re) created when + * changed. + * + * @author jimj316 + * @param + */ +public class ObjectNode extends Node +{ + protected T object; + private final Constructor constructor; + private final Set composingInPorts = new HashSet<>(); + private final Set setterInPorts = new HashSet<>(); + private final Set getterOutPorts = new HashSet<>(); + private final OutPort objectOutPort; + private final Map composingValues = new HashMap<>(); + private final Class clazz; + + public ObjectNode(Program scene, Class clazz, Constructor constructor) + { + super(scene); + this.clazz = clazz; + this.constructor = constructor; + objectOutPort = new ConstantOutPort<>("this", clazz, this); + + addConstructor(); + addAutoMethods(); + } + + public void addAutoMethods() throws SecurityException + { + for (Method method : clazz.getMethods()) + { + String name = method.getName(); + Class returnType = method.getReturnType(); + Parameter[] parameters = method.getParameters(); + if (returnType == null && parameters.length == 1 && name.startsWith("set")) + { + // this is a setter method + InPort in = new InPort(name, parameters[0].getType(), this); + addInPort(in); + setterInPorts.add(in); + } else if (returnType != null && parameters.length == 0 && (name.startsWith("get") || name.startsWith("is"))) + { + // this is a getter method + ConstantOutPort out = new ConstantOutPort(name, returnType, this); + addOutPort(out); + getterOutPorts.add(out); + } + } + } + + public void addConstructor() + { + Parameter[] parameters = constructor.getParameters(); + for (Parameter parameter : parameters) + { + InPort in = new InPort(parameter.getName(), parameter.getType(), this); + addInPort(in); + composingInPorts.add(in); + } + } + + @Override + public void reset() + { + super.reset(); + if (object instanceof Closeable) + { + try + { + ((Closeable) object).close(); + } catch (IOException ex) + { + Logger.getLogger(ObjectNode.class.getName()).log(Level.SEVERE, null, ex); + } + } + object = null; + } + + @Override + public boolean process() + { + // check if the composing values have changed; if so delete the object + + // if the object doesn't exist, create it + if (object == null) + { + Constructor[] constructors = clazz.getConstructors(); + for (Constructor constructor : constructors) + { + Parameter[] parameters = constructor.getParameters(); + List args = new ArrayList<>(); + for (Parameter parameter : parameters) + { + InPort inPort = getInPort(parameter.getName(), parameter.getType()); + if (inPort.has()) + args.add(inPort.get()); + else + break; + } + if (args.size() == parameters.length) + { + try + { + object = (T) constructor.newInstance(args.toArray()); + objectOutPort.set(object); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) + { + Logger.getLogger(ObjectNode.class.getName()).log(Level.SEVERE, null, ex); + } + break; + } + } + } + + // now the object exists, do something with it + if (object != null) + { + // take input from setters + // give output to getters + + return doProcess(); + } + return true; + } + + protected boolean doProcess() + { + return true; + } + + @Override + public boolean check() + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/nodes/SerialiserNode.java b/src/main/java/moe/nekojimi/nodeprocessor/nodes/SerialiserNode.java new file mode 100644 index 0000000..fe29f56 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/nodes/SerialiserNode.java @@ -0,0 +1,57 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.nodes; + +import java.util.Iterator; +import moe.nekojimi.nodeprocessor.Program; +import moe.nekojimi.nodeprocessor.ports.InPort; +import moe.nekojimi.nodeprocessor.ports.OutPort; +import moe.nekojimi.nodeprocessor.ports.QueueOutPort; + +public class SerialiserNode extends Node +{ + + private final Class> aClass; + private final Class clazz; + + public SerialiserNode(Program scene, Class clazz) + { + super(scene); + Iterable it = () -> + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + }; + aClass = (Class>) it.getClass(); + addInPort(new InPort("in", aClass, this)); + this.clazz = clazz; + addOutPort(new QueueOutPort("out", clazz, this)); + } + + @Override + public boolean process() + { + InPort> inPort = getInPort("in", aClass); + OutPort outPort = getOutPort("out", clazz); + if (inPort.has()) + { + Iterable iterable = inPort.get(); + Iterator iterator = iterable.iterator(); + while (iterator.hasNext()) + { + outPort.set(iterator.next()); + } + } + return true; + } + + @Override + public boolean check() + { + InPort> inPort = getInPort("in", aClass); + return inPort.has(); + } + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/nodes/ValueNode.java b/src/main/java/moe/nekojimi/nodeprocessor/nodes/ValueNode.java new file mode 100644 index 0000000..3aa3846 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/nodes/ValueNode.java @@ -0,0 +1,55 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.nodes; + +import moe.nekojimi.nodeprocessor.Program; +import moe.nekojimi.nodeprocessor.ports.ConstantOutPort; +import moe.nekojimi.nodeprocessor.ports.InPort; + +public class ValueNode extends Node +{ + + private T value; + + private final ConstantOutPort outPort; + private final InPort inPort; + + public ValueNode(Program scene, Class clazz) + { + super(scene); + inPort = new InPort("set", clazz, this); + addInPort(inPort); + outPort = new ConstantOutPort("out", clazz, this); + addOutPort(outPort); + } + + public void setValue(T val) + { + value = val; + signal(); + } + + public T getValue() + { + return value; + } + + @Override + public boolean process() + { + if (inPort.has()) + value = inPort.get(); + outPort.set(value); + return true; + } + + @Override + public boolean check() + { + return inPort.has(); + } + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/ports/ConstantOutPort.java b/src/main/java/moe/nekojimi/nodeprocessor/ports/ConstantOutPort.java new file mode 100644 index 0000000..0eb5377 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/ports/ConstantOutPort.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.ports; + +import moe.nekojimi.nodeprocessor.nodes.Node; + +public class ConstantOutPort extends OutPort +{ + + public ConstantOutPort(String name, Class clazz, Node node) + { + super(name, clazz, node); + } + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/ports/InPort.java b/src/main/java/moe/nekojimi/nodeprocessor/ports/InPort.java new file mode 100644 index 0000000..140dd51 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/ports/InPort.java @@ -0,0 +1,46 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.ports; + +import moe.nekojimi.nodeprocessor.links.Link; +import moe.nekojimi.nodeprocessor.nodes.Node; + +/** + * + * @author jimj316 + */ +public class InPort extends Port +{ + + public InPort(String name, Class clazz, Node node) + { + super(name, clazz, node); + } + + public boolean has() + { + if (links.isEmpty()) + return false; + else + return links.stream().anyMatch((l) -> l.has()); + } + + public T get() + { + for (Link link : links) + { + if (link.has()) + return link.read(); + } + return null; + } + + public void signal() + { + node.signal(); + } + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/ports/OutPort.java b/src/main/java/moe/nekojimi/nodeprocessor/ports/OutPort.java new file mode 100644 index 0000000..f5885ad --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/ports/OutPort.java @@ -0,0 +1,45 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.ports; + +import moe.nekojimi.nodeprocessor.links.Link; +import moe.nekojimi.nodeprocessor.nodes.Node; + +/** + * + * @author jimj316 + */ +public abstract class OutPort extends Port +{ + + public OutPort(String name, Class clazz, Node node) + { + super(name, clazz, node); + } + + public void set(T value) + { + for (Link link : links) + { + link.write(value); + } + } + + @Override + public void connect(Link link) + { + super.connect(link); + // also re-order the links into dependency order + links.sort((Link a, Link b) -> + { + Node aNode = a.getTo().getNode(); + Node bNode = b.getTo().getNode(); + return Node.DEPENDENCY_ORDER.compare(aNode, bNode); + }); + } + + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/ports/Port.java b/src/main/java/moe/nekojimi/nodeprocessor/ports/Port.java new file mode 100644 index 0000000..97d41d3 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/ports/Port.java @@ -0,0 +1,83 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.ports; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import moe.nekojimi.nodeprocessor.links.Link; +import moe.nekojimi.nodeprocessor.nodes.Node; + +/** + * + * @author jimj316 + */ +public abstract class Port +{ + protected final String name; + protected final Class clazz; + protected final Node node; + protected final List> links = new ArrayList<>(); + + public String getName() + { + return name; + } + + public Class getClazz() + { + return clazz; + } + + public void connect(Link link) + { + links.add(link); + } + + public void disconnect(Link link) + { + links.remove(link); + } + + public Port(String name, Class clazz, Node node) + { + this.name = name; + this.clazz = clazz; + this.node = node; + } + + public Set getLinkedNodes() + { + Set ret = new HashSet<>(); + for (Link link : links) + { + Port otherSide; + if (link.getFrom() == this) + otherSide = link.getTo(); + else + otherSide = link.getFrom(); + ret.add(otherSide.node); + } + return ret; + } + + public int getNumberOfConnections() + { + return links.size(); + } + + public Node getNode() + { + return node; + } + + public List> getLinks() + { + return links; + } + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/ports/PullOutPort.java b/src/main/java/moe/nekojimi/nodeprocessor/ports/PullOutPort.java new file mode 100644 index 0000000..b4d7a99 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/ports/PullOutPort.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.ports; + +import java.util.function.Supplier; +import moe.nekojimi.nodeprocessor.nodes.Node; + +public class PullOutPort extends OutPort +{ + + private Supplier supplier; + + public PullOutPort(String name, Class clazz, Node node) + { + super(name, clazz, node); + } + + public void setSupplier(Supplier supplier) + { + this.supplier = supplier; + } + + @Override + public void set(T value) + { + // not used + } + + public T pull() + { + return supplier.get(); + } + +} diff --git a/src/main/java/moe/nekojimi/nodeprocessor/ports/QueueOutPort.java b/src/main/java/moe/nekojimi/nodeprocessor/ports/QueueOutPort.java new file mode 100644 index 0000000..2c29da0 --- /dev/null +++ b/src/main/java/moe/nekojimi/nodeprocessor/ports/QueueOutPort.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package moe.nekojimi.nodeprocessor.ports; + +import moe.nekojimi.nodeprocessor.nodes.Node; + +public class QueueOutPort extends OutPort +{ + + public QueueOutPort(String name, Class clazz, Node node) + { + super(name, clazz, node); + } + +}