package org.jboss.windup.graph;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.jboss.windup.util.Logging;
import org.jboss.windup.util.exception.WindupException;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Element;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.frames.FramedGraph;
import com.tinkerpop.frames.modules.MethodHandler;
/**
* Handles @MapInAdjacentProperties Map<String,String>.
*
* @author <a href="mailto:ozizka@redhat.com">Ondrej Zizka</a>
*/
public class MapInAdjacentPropertiesHandler implements MethodHandler<MapInAdjacentProperties>
{
private static final Logger log = Logging.get(MapInAdjacentPropertiesHandler.class);
@Override
public Class<MapInAdjacentProperties> getAnnotationType()
{
return MapInAdjacentProperties.class;
}
@Override
public Map<String, Serializable> processElement(Object frame, Method method, Object[] arguments, MapInAdjacentProperties annotation,
FramedGraph<?> framedGraph, Element elm)
{
if (!(elm instanceof Vertex))
throw new WindupException("@" + MapInAdjacentProperties.class.getSimpleName() + " is only supported on Vertex objects.");
String methodName = method.getName();
if (methodName.startsWith("get"))
return handleGetter((Vertex) elm, method, arguments, annotation, framedGraph);
if (methodName.startsWith("set"))
{
handleSetter((Vertex) elm, method, arguments, annotation, framedGraph);
return null;
}
throw new WindupException("Only get* and set* method names are supported for @" + MapInAdjacentProperties.class.getSimpleName());
}
/**
* Getter
*/
private Map<String, Serializable> handleGetter(Vertex vertex, Method method, Object[] args,
MapInAdjacentProperties ann, FramedGraph<?> framedGraph)
{
if (args != null && args.length != 0)
throw new WindupException("Method must take no arguments: " + method.getName());
// Find the map vertex.
Map<String, Serializable> map = new HashMap<>();
Iterable<Vertex> verts = vertex.getVertices(Direction.OUT, ann.label());
Vertex mapVertex = null;
final Iterator<Vertex> it = verts.iterator();
if (!it.hasNext())
{
// No map yet.
return map;
}
else
{
mapVertex = it.next();
if (it.hasNext())
{
// Multiple vertices behind edges with given label.
log.warning("Found multiple vertices for a map, using only first one; for: " + method.getName());
}
}
Set<String> keys = mapVertex.getPropertyKeys();
for (String key : keys)
{
final Object val = mapVertex.getProperty(key);
if (!(val instanceof String))
log.warning("@InProperties is meant for Map<String,Serializable>, but the value was: " + val.getClass());
map.put(key, "" + val);
}
return map;
}
/**
* Setter
*/
private void handleSetter(Vertex vertex, Method method, Object[] args, MapInAdjacentProperties ann,
FramedGraph<?> framedGraph)
{
// Argument.
if (args == null || args.length != 1)
throw new WindupException("Method must take one argument: " + method.getName());
if (!(args[0] instanceof Map))
throw new WindupException("Argument of " + method.getName() + " must be a Map, but is: " + args[0].getClass());
@SuppressWarnings("unchecked")
Map<String, Serializable> map = (Map<String, Serializable>) args[0];
// Find or create the map vertex.
Iterable<Vertex> verts = vertex.getVertices(Direction.OUT, ann.label());
Vertex mapVertex = null;
final Iterator<Vertex> it = verts.iterator();
if (!it.hasNext())
{
// No map vertex yet.
mapVertex = framedGraph.addVertex(null);
vertex.addEdge(ann.label(), mapVertex);
}
else
{
mapVertex = it.next();
if (it.hasNext())
{
// Multiple vertices behind edges with given label.
log.warning("Found multiple vertices for a map, using only first one; for: " + method.getName());
}
}
// For all keys in the old map...
Set<String> keys = mapVertex.getPropertyKeys();
Set<String> mapKeys = map.keySet();
for (String key : keys)
{
final Object val = mapVertex.getProperty(key);
if (!(val instanceof String))
{
log.warning("@InProperties is meant for Map<String,Serializable>, but the value was: " + val.getClass());
}
// ...either change to new value,
if (map.containsKey(key))
{
mapVertex.setProperty(key, map.get(key));
mapKeys.remove(key);
}
// or remove the old.
else
mapVertex.removeProperty(key);
}
// Add the new entries.
for (String key : mapKeys)
{
mapVertex.setProperty(key, map.get(key));
}
}
}