package org.jboss.windup.graph; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Set; import com.tinkerpop.blueprints.Direction; import com.tinkerpop.blueprints.Edge; import com.tinkerpop.blueprints.VertexQuery; import org.jboss.windup.graph.model.InMemoryVertexFrame; import org.jboss.windup.util.exception.WindupException; import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.frames.ClassUtilities; import com.tinkerpop.frames.Property; import com.tinkerpop.frames.VertexFrame; /** * This class represents the @Property elements of a framed element in memory. * * This in memory representation can be attached to the graph by calling "attach(FramedGraph)" * */ public class FramedElementInMemory<T extends VertexFrame> implements InvocationHandler { private final Class<T> type; private final Object id; private final Map<String, Object> values = new HashMap<>(); private static final Method attachMethod; private static final Method asVertexMethod; private static final Method hashCodeMethod; private static final Method equalsMethod; private static final Method toStringMethod; static { try { attachMethod = InMemoryVertexFrame.class.getMethod("attachToGraph", new Class[] { GraphContext.class }); asVertexMethod = VertexFrame.class.getMethod("asVertex"); hashCodeMethod = Object.class.getMethod("hashCode"); equalsMethod = Object.class.getMethod("equals", new Class[] { Object.class }); toStringMethod = Object.class.getMethod("toString"); } catch (NoSuchMethodException e) { throw new WindupException(e.getMessage(), e); } } public FramedElementInMemory(Class<T> type) { this(type, null); } public FramedElementInMemory(Class<T> type, Object id) { this.type = type; this.id = id; } public Object invoke(final Object proxy, final Method method, final Object[] arguments) { String methodName = method.getName(); if (method.equals(hashCodeMethod)) { return this.hashCode(); } else if (method.equals(equalsMethod)) { return this.equals(arguments[0]); } else if (method.equals(toStringMethod)) { return this.toString(); } else if (method.equals(attachMethod)) { attach((GraphContext)arguments[0]); return null; } else if (method.equals(asVertexMethod)) { return asVertex(); } final String propertyName; Property propertyAnnotation = method.getAnnotation(Property.class); if (propertyAnnotation == null) { Property windupPropertyAnnnotation = method.getAnnotation(Property.class); if (windupPropertyAnnnotation == null) { // if this is a getter or setter, try to find the property on the other method if (ClassUtilities.isGetMethod(method)) { // get the setter Method setMethod = ClassUtilities.getSetterMethodForGetter(method); windupPropertyAnnnotation = setMethod.getAnnotation(Property.class); } else if (ClassUtilities.isSetMethod(method)) { // get the getter Method getMethod = ClassUtilities.getGetterMethodForSetter(method); windupPropertyAnnnotation = getMethod.getAnnotation(Property.class); } if (windupPropertyAnnnotation == null) throw new WindupException("Method " + methodName + " called, but has no @Property annotation... only @Property methods are supported"); else propertyName = windupPropertyAnnnotation.value(); } else { propertyName = windupPropertyAnnnotation.value(); } } else { propertyName = propertyAnnotation.value(); } if (ClassUtilities.isGetMethod(method)) { return values.get(propertyName); } else if (ClassUtilities.isSetMethod(method)) { Object value = arguments[0]; if (value == null) { values.remove(propertyName); } else { values.put(propertyName, value); } return proxy; } else if (ClassUtilities.isRemoveMethod(method)) { values.remove(propertyName); return proxy; } else { throw new WindupException("Unrecognized method " + methodName + " called on in-memory Frame!"); } } private void attach(GraphContext context) { T element = context.getFramed().addVertex(null, this.type); Vertex v = element.asVertex(); for (Map.Entry<String, Object> entry : values.entrySet()) { v.setProperty(entry.getKey(), entry.getValue()); } } private Vertex asVertex() { return new Vertex() { @Override public Iterable<Edge> getEdges(Direction direction, String... labels) { throw new RuntimeException("Method not supported for FramedElementInMemory"); } @Override public Iterable<Vertex> getVertices(Direction direction, String... labels) { throw new RuntimeException("Method not supported for FramedElementInMemory"); } @Override public VertexQuery query() { throw new RuntimeException("Method not supported for FramedElementInMemory"); } @Override public Edge addEdge(String label, Vertex inVertex) { throw new RuntimeException("Method not supported for FramedElementInMemory"); } @Override public <T> T getProperty(String key) { return (T)values.get(key); } @Override public Set<String> getPropertyKeys() { return values.keySet(); } @Override public void setProperty(String key, Object value) { values.put(key, value); } @Override public <T> T removeProperty(String key) { return (T)values.remove(key); } @Override public void remove() { throw new RuntimeException("Method not supported for FramedElementInMemory"); } @Override public Object getId() { return id; } }; } @Override public String toString() { return "[Proxy for: " + type.getCanonicalName() + ", values: " + values + "]"; } }