generated from Nekojimi/JavaMavenTemplate
parent
b35bd6c2d5
commit
f4e9c3e823
@ -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, |
||||
} |
@ -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<Integer> twoNode = new ValueNode<>(program, Integer.class); |
||||
twoNode.setValue(2); |
||||
ValueNode<Integer> threeNode = new ValueNode<>(program, Integer.class); |
||||
threeNode.setValue(3); |
||||
|
||||
MethodNode<Integer> 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<String> valueNode = new ValueNode<>(program, String.class); |
||||
valueNode.setValue("Hello world!"); |
||||
final Method method = String.class.getMethod("toUpperCase"); |
||||
MethodNode<String> 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<Class> 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<Class> 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; |
||||
} |
||||
|
||||
} |
||||
|
@ -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<T extends Node> |
||||
{ |
||||
|
||||
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<T> clazz) |
||||
{ |
||||
} |
||||
|
||||
public T construct() |
||||
{ |
||||
return null; |
||||
} |
||||
} |
@ -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<Node> nodes = new ArrayList<>(); |
||||
private final Set<Link> links = new HashSet<>(); |
||||
|
||||
private final Queue<Node> executionQueue = new LinkedList<>(); |
||||
private final Set<Node> 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<Node> getNodes() |
||||
{ |
||||
return nodes; |
||||
} |
||||
|
||||
public Set<Link> getLinks() |
||||
{ |
||||
return links; |
||||
} |
||||
|
||||
public Set<Node> getIdleNodes() |
||||
{ |
||||
return idleNodes; |
||||
} |
||||
|
||||
public enum State |
||||
{ |
||||
STOPPED, |
||||
PAUSED, |
||||
RUNNING, |
||||
IDLE |
||||
} |
||||
} |
@ -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<T> extends Link<T, InPort<T>, ConstantOutPort<T>> |
||||
{ |
||||
|
||||
T value = null; |
||||
|
||||
public ConstantLink(ConstantOutPort<T> from, InPort<T> 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; |
||||
} |
||||
|
||||
} |
@ -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<T, I extends InPort<T>, O extends OutPort<T>> |
||||
{ |
||||
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 <T> Link<T, ?, ?> makeLink(Class<T> clazz, Node from, String fromPort, Node to, String toPort) |
||||
{ |
||||
OutPort<T> outPort = from.getOutPort(fromPort, clazz); |
||||
InPort<T> inPort = to.getInPort(toPort, clazz); |
||||
if (outPort instanceof QueueOutPort) |
||||
{ |
||||
QueueOutPort<T> q = (QueueOutPort<T>) outPort; |
||||
return new QueueLink<>(q, inPort); |
||||
} else if (outPort instanceof ConstantOutPort) |
||||
{ |
||||
ConstantOutPort<T> c = (ConstantOutPort<T>) outPort; |
||||
return new ConstantLink<>(c, inPort); |
||||
} else |
||||
{ |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
protected void signal() |
||||
{ |
||||
to.signal(); |
||||
} |
||||
|
||||
public O getFrom() |
||||
{ |
||||
return from; |
||||
} |
||||
|
||||
public I getTo() |
||||
{ |
||||
return to; |
||||
} |
||||
|
||||
} |
@ -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<T> extends Link<T, InPort<T>, PullOutPort<T>> |
||||
{ |
||||
|
||||
public PullLink(PullOutPort<T> from, InPort<T> 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; |
||||
} |
||||
|
||||
} |
@ -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<T> extends Link<T, InPort<T>, QueueOutPort<T>> |
||||
{ |
||||
|
||||
private final Queue<T> queue = new LinkedList<>(); |
||||
|
||||
public QueueLink(QueueOutPort<T> from, InPort<T> 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(); |
||||
} |
||||
} |
@ -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<Object> inPort = getInPort("in", Object.class); |
||||
System.out.println(inPort.get()); |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean check() |
||||
{ |
||||
InPort<Object> inPort = getInPort("in", Object.class); |
||||
return inPort.has(); |
||||
} |
||||
|
||||
} |
@ -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 <C> the class that declares this method. |
||||
*/ |
||||
public class MethodNode<C> extends Node |
||||
{ |
||||
|
||||
private final InPort<C> objectInPort; |
||||
private final QueueOutPort returnOutPort; |
||||
private final Method method; |
||||
|
||||
public MethodNode(Program scene, Class<C> 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())); |
||||
} |
||||
|
||||
} |
@ -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<String, Link> inLinks = new HashMap<>();
|
||||
// protected Map<String, Link> outLinks = new HashMap<>();
|
||||
|
||||
private final Program program; |
||||
private Map<String, InPort> inPorts = new HashMap<>(); |
||||
private Map<String, OutPort> 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 <X> InPort<X> getInPort(String name, Class<X> clazz) |
||||
{ |
||||
Class<? super X> c = clazz; |
||||
InPort ret; |
||||
do |
||||
{ |
||||
ret = inPorts.get(getPortName(c, name)); |
||||
c = c.getSuperclass(); |
||||
} while (ret == null && c != null); |
||||
return ret; |
||||
} |
||||
|
||||
public final <X> OutPort<X> getOutPort(String name, Class<X> clazz) |
||||
{ |
||||
Class<? super X> c = clazz; |
||||
OutPort ret; |
||||
do |
||||
{ |
||||
ret = outPorts.get(getPortName(clazz, name)); |
||||
c = c.getSuperclass(); |
||||
} while (ret == null && c != null); |
||||
return ret; |
||||
} |
||||
|
||||
public final Collection<InPort> getInPorts() |
||||
{ |
||||
return inPorts.values(); |
||||
} |
||||
|
||||
public final Collection<OutPort> getOutPorts() |
||||
{ |
||||
return outPorts.values(); |
||||
} |
||||
|
||||
private static <X> String getPortName(Class<X> 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<Node> 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<Node> DEPENDENCY_ORDER = (Node a, Node b) -> |
||||
{ |
||||
int ret = 0; |
||||
if (a.dependsOn(b)) |
||||
ret--; |
||||
if (b.dependsOn(a)) |
||||
ret++; |
||||
return ret; |
||||
}; |
||||
|
||||
} |
@ -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 <T> |
||||
*/ |
||||
public class ObjectNode<T> extends Node |
||||
{ |
||||
protected T object; |
||||
private final Constructor<T> constructor; |
||||
private final Set<InPort> composingInPorts = new HashSet<>(); |
||||
private final Set<InPort> setterInPorts = new HashSet<>(); |
||||
private final Set<OutPort> getterOutPorts = new HashSet<>(); |
||||
private final OutPort<T> objectOutPort; |
||||
private final Map<String, Object> composingValues = new HashMap<>(); |
||||
private final Class<T> clazz; |
||||
|
||||
public ObjectNode(Program scene, Class<T> clazz, Constructor<T> 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<Object> 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.
|
||||
} |
||||
} |
@ -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<T> extends Node |
||||
{ |
||||
|
||||
private final Class<Iterable<T>> aClass; |
||||
private final Class<T> clazz; |
||||
|
||||
public SerialiserNode(Program scene, Class<T> clazz) |
||||
{ |
||||
super(scene); |
||||
Iterable<T> it = () -> |
||||
{ |
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}; |
||||
aClass = (Class<Iterable<T>>) it.getClass(); |
||||
addInPort(new InPort("in", aClass, this)); |
||||
this.clazz = clazz; |
||||
addOutPort(new QueueOutPort("out", clazz, this)); |
||||
} |
||||
|
||||
@Override |
||||
public boolean process() |
||||
{ |
||||
InPort<Iterable<T>> inPort = getInPort("in", aClass); |
||||
OutPort<T> outPort = getOutPort("out", clazz); |
||||
if (inPort.has()) |
||||
{ |
||||
Iterable<T> iterable = inPort.get(); |
||||
Iterator<T> iterator = iterable.iterator(); |
||||
while (iterator.hasNext()) |
||||
{ |
||||
outPort.set(iterator.next()); |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean check() |
||||
{ |
||||
InPort<Iterable<T>> inPort = getInPort("in", aClass); |
||||
return inPort.has(); |
||||
} |
||||
|
||||
} |
@ -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<T> extends Node |
||||
{ |
||||
|
||||
private T value; |
||||
|
||||
private final ConstantOutPort<T> outPort; |
||||
private final InPort<T> inPort; |
||||
|
||||
public ValueNode(Program scene, Class<T> 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(); |
||||
} |
||||
|
||||
} |
@ -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<T> extends OutPort<T> |
||||
{ |
||||
|
||||
public ConstantOutPort(String name, Class<T> clazz, Node node) |
||||
{ |
||||
super(name, clazz, node); |
||||
} |
||||
|
||||
} |
@ -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<T> extends Port<T> |
||||
{ |
||||
|
||||
public InPort(String name, Class<T> 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<T, ?, ?> link : links) |
||||
{ |
||||
if (link.has()) |
||||
return link.read(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public void signal() |
||||
{ |
||||
node.signal(); |
||||
} |
||||
|
||||
} |
@ -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<T> extends Port<T> |
||||
{ |
||||
|
||||
public OutPort(String name, Class<T> clazz, Node node) |
||||
{ |
||||
super(name, clazz, node); |
||||
} |
||||
|
||||
public void set(T value) |
||||
{ |
||||
for (Link<T, ?, ?> link : links) |
||||
{ |
||||
link.write(value); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void connect(Link<T, ?, ?> 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); |
||||
}); |
||||
} |
||||
|
||||
|
||||
} |
@ -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<T> |
||||
{ |
||||
protected final String name; |
||||
protected final Class<T> clazz; |
||||
protected final Node node; |
||||
protected final List<Link<T, ?, ?>> links = new ArrayList<>(); |
||||
|
||||
public String getName() |
||||
{ |
||||
return name; |
||||
} |
||||
|
||||
public Class<T> getClazz() |
||||
{ |
||||
return clazz; |
||||
} |
||||
|
||||
public void connect(Link<T, ?, ?> link) |
||||
{ |
||||
links.add(link); |
||||
} |
||||
|
||||
public void disconnect(Link<T, ?, ?> link) |
||||
{ |
||||
links.remove(link); |
||||
} |
||||
|
||||
public Port(String name, Class<T> clazz, Node node) |
||||
{ |
||||
this.name = name; |
||||
this.clazz = clazz; |
||||
this.node = node; |
||||
} |
||||
|
||||
public Set<Node> getLinkedNodes() |
||||
{ |
||||
Set<Node> ret = new HashSet<>(); |
||||
for (Link<T, ?, ?> 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<Link<T, ?, ?>> getLinks() |
||||
{ |
||||
return links; |
||||
} |
||||
|
||||
} |
@ -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<T> extends OutPort<T> |
||||
{ |
||||
|
||||
private Supplier<T> supplier; |
||||
|
||||
public PullOutPort(String name, Class<T> clazz, Node node) |
||||
{ |
||||
super(name, clazz, node); |
||||
} |
||||
|
||||
public void setSupplier(Supplier<T> supplier) |
||||
{ |
||||
this.supplier = supplier; |
||||
} |
||||
|
||||
@Override |
||||
public void set(T value) |
||||
{ |
||||
// not used
|
||||
} |
||||
|
||||
public T pull() |
||||
{ |
||||
return supplier.get(); |
||||
} |
||||
|
||||
} |
@ -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<T> extends OutPort<T> |
||||
{ |
||||
|
||||
public QueueOutPort(String name, Class<T> clazz, Node node) |
||||
{ |
||||
super(name, clazz, node); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue