package org.openntf.domino.rest.resources.frames;
import com.ibm.commons.util.io.json.JsonObject;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.frames.EdgeFrame;
import com.tinkerpop.frames.VertexFrame;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openntf.domino.graph2.DEdgeList;
import org.openntf.domino.graph2.DGraphUtils;
import org.openntf.domino.graph2.annotations.FramedEdgeList;
import org.openntf.domino.graph2.annotations.FramedVertexList;
import org.openntf.domino.graph2.builtin.DEdgeFrame;
import org.openntf.domino.graph2.builtin.DVertexFrame;
import org.openntf.domino.graph2.builtin.ViewVertex;
import org.openntf.domino.graph2.impl.DEdge;
import org.openntf.domino.graph2.impl.DFramedTransactionalGraph;
import org.openntf.domino.graph2.impl.DProxyVertex;
import org.openntf.domino.graph2.impl.DVertex;
import org.openntf.domino.graph2.impl.DVertexList;
import org.openntf.domino.rest.service.Parameters;
import org.openntf.domino.rest.service.Parameters.ParamMap;
import org.openntf.domino.types.CaseInsensitiveString;
import org.openntf.domino.utils.TypeUtils;
public class JsonFrameAdapter implements JsonObject {
private final static List<String> EMPTY_STRINGS = new ArrayList<String>();
// TODO NTF Add support for modification date checking prior to permitting
// PUT/PATCH
static private boolean ifUnmodifiedSince(Object object, Date ifUnmodifiedSince) {
if (object instanceof DEdgeFrame) {
Date mod = ((DEdgeFrame) object).getModified();
return !mod.after(ifUnmodifiedSince);
} else if (object instanceof DVertexFrame) {
Date mod = ((DVertexFrame) object).getModified();
return !mod.after(ifUnmodifiedSince);
} else {
throw new IllegalArgumentException("Cannot verify modification time of a non-framed object: "
+ object.getClass().getName());
}
}
public static VertexFrame toVertexFrame(DFramedTransactionalGraph graph, Edge edge, Vertex source) {
VertexFrame result = null;
Vertex other = null;
if (edge instanceof DEdge) {
try {
other = ((DEdge) edge).getOtherVertex(source);
} catch (Throwable t) {
t.printStackTrace();
}
} else {
System.out.println("TEMP DEBUG edge is actually a " + edge.getClass().getName());
}
result = (VertexFrame) graph.frame(other, null);
return result;
}
protected Object frame_;
@SuppressWarnings("rawtypes")
protected DFramedTransactionalGraph graph_;
protected Class<?> type_;
protected Map<CaseInsensitiveString, Method> getters_;
protected Map<CaseInsensitiveString, Method> incidences_;
protected Map<CaseInsensitiveString, Method> counters_;
protected Map<CaseInsensitiveString, Method> setters_;
protected ParamMap parameters_;
@SuppressWarnings("rawtypes")
public JsonFrameAdapter(DFramedTransactionalGraph graph, EdgeFrame frame, ParamMap pm) {
graph_ = graph;
frame_ = frame;
parameters_ = pm;
type_ = graph_.getTypeManager().resolve(frame);
}
@SuppressWarnings("rawtypes")
public JsonFrameAdapter(DFramedTransactionalGraph graph, VertexFrame frame, ParamMap pm) {
graph_ = graph;
frame_ = frame;
parameters_ = pm;
type_ = graph_.getTypeManager().resolve(frame);
}
public Object getFrame() {
return frame_;
}
@SuppressWarnings("rawtypes")
public DFramedTransactionalGraph getGraph() {
return graph_;
}
public List<CharSequence> getProperties() {
if (parameters_ != null) {
return parameters_.getProperties();
}
return null;
}
public List<CharSequence> getInProperties() {
if (parameters_ != null) {
return parameters_.getInProperties();
}
return null;
}
public List<CharSequence> getOutProperties() {
if (parameters_ != null) {
return parameters_.getOutProperties();
}
return null;
}
public List<CharSequence> getLabels() {
if (parameters_ != null) {
return parameters_.getLabels();
}
return null;
}
public List<CharSequence> getFilterKeys() {
if (parameters_ != null) {
return parameters_.getFilterKeys();
}
return null;
}
public List<CharSequence> getFilterValues() {
if (parameters_ != null) {
return parameters_.getFilterValues();
}
return null;
}
public List<CharSequence> getOrderBys() {
if (parameters_ != null) {
return parameters_.getOrderBys();
}
return null;
}
public int getStart() {
if (parameters_ != null) {
return parameters_.getStart();
}
return 0;
}
public int getCount() {
if (parameters_ != null) {
return parameters_.getCount();
}
return 0;
}
public boolean getIncludeEdges() {
if (parameters_ != null) {
return parameters_.getIncludeEdges();
}
return false;
}
public boolean getDescending() {
if (parameters_ != null) {
return parameters_.getDescending();
}
return false;
}
public boolean getIncludeCounts() {
if (parameters_ != null) {
return parameters_.getIncludeCounts();
}
return false;
}
public boolean getIncludeVertices() {
if (parameters_ != null) {
return parameters_.getIncludeVertices();
}
return false;
}
public Map<CaseInsensitiveString, Method> getCounters() {
if (counters_ == null) {
counters_ = getGraph().getTypeRegistry().getCounters(type_);
}
if (counters_.size() == 0) {
// System.out.println("TEMP DEBUG No counters found for type " +
// type_.getName() + " and we found "
// + getIncidences().size() + " incidences");
}
return counters_;
}
public Map<CaseInsensitiveString, Method> getGetters() {
if (getters_ == null) {
getters_ = getGraph().getTypeRegistry().getPropertiesGetters(type_);
}
return getters_;
}
public Map<CaseInsensitiveString, Method> getIncidences() {
if (incidences_ == null) {
incidences_ = getGraph().getTypeRegistry().getIncidences(type_);
}
return incidences_;
}
public Map<CaseInsensitiveString, Method> getSetters() {
if (setters_ == null) {
setters_ = getGraph().getTypeRegistry().getPropertiesSetters(type_);
}
return setters_;
}
@Override
public Iterator<String> getJsonProperties() {
// System.out
// .println("TEMP DEBUG getting Json properties list for a frame of type "
// + frame_.getClass().getName());
List<String> result = new ArrayList<String>();
result.add("@id");
result.add("@type");
Collection<CharSequence> props = getProperties();
if (props == null) {
props = new ArrayList<CharSequence>();
props.addAll(getGetters().keySet());
if (props == null || props.size() < 3) {
if (frame_ instanceof DVertexFrame) {
Set<CharSequence> raw = ((DVertexFrame) frame_).asMap().keySet();
props.addAll(CaseInsensitiveString.toCaseInsensitive(raw));
} else if (frame_ instanceof DEdgeFrame) {
// Set<CharSequence> raw = ((DEdgeFrame)
// frame_).asMap().keySet();
// props.addAll(CaseInsensitiveString.toCaseInsensitive(raw));
}
}
}
for (CharSequence cis : props) {
result.add(cis.toString());
// System.out.println("Adding " + cis.toString());
}
Object frame = getFrame();
if (frame instanceof VertexFrame && getIncludeEdges()) {
result.add("@edges");
}
if (frame instanceof VertexFrame && getIncludeCounts()) {
for (CaseInsensitiveString key : getCounters().keySet()) {
result.add("@counts" + key.toString());
}
}
if (frame instanceof VertexFrame) {
Vertex v = ((VertexFrame) frame).asVertex();
if (v instanceof DProxyVertex) {
result.add("@proxyid");
}
}
if (frame instanceof VertexFrame && getLabels() != null) {
for (CharSequence cis : getLabels()) {
result.add("#" + cis.toString());
}
}
if (frame instanceof EdgeFrame) {
result.add("@in");
result.add("@out");
}
if (frame instanceof ViewVertex.Contains) {
Edge edge = ((ViewVertex.Contains) frame).asEdge();
if (edge instanceof DEdge) {
result.addAll(((DEdge) edge).getDelegate().keySet());
}
}
return result.iterator();
}
@Override
public Object getJsonProperty(String paramKey) {
Object result = null;
Object frame = getFrame();
if (frame != null) {
CaseInsensitiveString key = new CaseInsensitiveString(paramKey);
if (key.equals("@id")) {
if (frame instanceof EdgeFrame) {
result = ((EdgeFrame) frame).asEdge().getId().toString();
}
if (frame instanceof VertexFrame) {
result = ((VertexFrame) frame).asVertex().getId().toString();
}
} else if (key.equals("@proxyid")) {
// System.out.println("TEMP DEBUG @proxyid requested");
if (frame instanceof VertexFrame) {
Vertex v = ((VertexFrame) frame).asVertex();
if (v instanceof DProxyVertex) {
result = ((DProxyVertex) v).getProperty(DProxyVertex.PROXY_ITEM, String.class);
}
}
} else if (key.equals("@type")) {
if (frame instanceof EdgeFrame) {
result = type_;
} else if (frame instanceof VertexFrame) {
result = type_;
}
} else if (key.equals("@in") && frame instanceof EdgeFrame) {
if (getInProperties() == null) {
// why not just make a frame adapter with the vertex?
// because that's another I/O operation. We already have the
// information needed to
DEdge dedge = (DEdge) ((EdgeFrame) frame).asEdge();
Map<String, String> minProps = new LinkedHashMap<String, String>();
minProps.put("@id", dedge.getVertexId(Direction.IN).toString());
Class<?> inType = graph_.getTypeRegistry().getInType(type_);
if (inType == null) {
minProps.put("@type", "Vertex");
} else {
minProps.put("@type", inType.getName());
}
result = minProps;
} else {
ParamMap inMap = new ParamMap();
inMap.put(Parameters.PROPS, CaseInsensitiveString.toStrings(getInProperties()));
if (getIncludeEdges()) {
inMap.put(Parameters.EDGES, EMPTY_STRINGS);
}
if (getIncludeCounts()) {
inMap.put(Parameters.COUNTS, EMPTY_STRINGS);
}
Method inMethod = graph_.getTypeRegistry().getIn(type_);
if (inMethod != null) {
try {
Object raw = inMethod.invoke(frame, (Object[]) null);
if (raw instanceof VertexFrame) {
VertexFrame inFrame = (VertexFrame) raw;
result = new JsonFrameAdapter(graph_, inFrame, inMap);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
} else if (key.equals("@out") && frame instanceof EdgeFrame) {
if (getOutProperties() == null) {
// why not just make a frame adapter with the vertex?
// because that's another I/O operation. We already have the
// information needed to
DEdge dedge = (DEdge) ((EdgeFrame) frame).asEdge();
Map<String, String> minProps = new LinkedHashMap<String, String>();
minProps.put("@id", dedge.getVertexId(Direction.OUT).toString());
Class<?> outType = graph_.getTypeRegistry().getOutType(type_);
if (outType == null) {
minProps.put("@type", "Vertex");
} else {
minProps.put("@type", outType.getName());
}
result = minProps;
} else {
ParamMap outMap = new ParamMap();
outMap.put(Parameters.PROPS, CaseInsensitiveString.toStrings(getOutProperties()));
if (getIncludeEdges()) {
outMap.put(Parameters.EDGES, EMPTY_STRINGS);
}
if (getIncludeCounts()) {
outMap.put(Parameters.COUNTS, EMPTY_STRINGS);
}
Method outMethod = graph_.getTypeRegistry().getOut(type_);
if (outMethod != null) {
try {
Object raw = outMethod.invoke(frame, (Object[]) null);
if (raw instanceof VertexFrame) {
VertexFrame outFrame = (VertexFrame) raw;
result = new JsonFrameAdapter(graph_, outFrame, outMap);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
} else if (key.equals("@edges")) {
Map<String, Integer> edgeCounts = new LinkedHashMap<String, Integer>();
Set<CaseInsensitiveString> counterKeys = getCounters().keySet();
// System.out.println("TEMP DEBUG Found " + counterKeys.size() +
// " edge types");
for (CaseInsensitiveString label : counterKeys) {
Method crystal = getCounters().get(label);
if (crystal != null) {
// System.out.println("TEMP DEBUG Found method for " +
// key);
try {
Object raw = crystal.invoke(getFrame(), (Object[]) null);
if (raw instanceof Integer) {
edgeCounts.put(label.toString(), (Integer) raw);
} else {
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
// System.out.println("TEMP DEBUG No method found for key "
// + key);
}
}
result = edgeCounts;
} else if (key.startsWith("@counts")) {
String label = key.toString().substring("@counts".length());
Method crystal = getCounters().get(new CaseInsensitiveString(label));
if (crystal != null) {
try {
Object raw = crystal.invoke(getFrame(), (Object[]) null);
if (raw instanceof Integer) {
result = raw;
} else {
// System.out.println("TEMP DEBUG Invokation of a counter resulted in a "
// + (raw == null ? "null" :
// raw.getClass().getName()));
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
// System.out.println("TEMP DEBUG No method found for key "
// + label);
}
} else if (key.startsWith("#") && frame instanceof VertexFrame) {
CharSequence label = key.subSequence(1, key.length());
// System.out.println("DEBUG: Attempting to get edges with label "
// + label);
Method crystal = getIncidences().get(label);
if (crystal != null) {
try {
try {
result = crystal.invoke(frame, (Object[]) null);
} catch (Throwable t) {
System.err.println("TEMP DEBUG Ignoring an issue with an invokation of "
+ crystal.getName()
+ " on a "
+ DGraphUtils.findInterface(frame).getName()
+ ": "
+ t.getClass().getName()
+ (t.getCause() != null ? (" caused by a " + t.getCause().getClass().getName())
: ""));
}
if (result != null) {
if (!(result instanceof Iterable)) {
if (result instanceof EdgeFrame) {
Vertex v = ((VertexFrame) frame).asVertex();
List<Edge> edges = new org.openntf.domino.graph2.impl.DEdgeList((DVertex) v);
edges.add(((EdgeFrame) result).asEdge());
result = new FramedEdgeList(getGraph(), ((VertexFrame) frame).asVertex(), edges,
crystal.getReturnType());
}
}
if (getIncludeVertices()) {
// System.out.println("TEMP DEBUG: Turning EdgeList into VertexList");
if (result instanceof DEdgeList) {
result = ((DEdgeList) result).toVertexList();
} else if (result instanceof FramedEdgeList) {
result = ((FramedEdgeList<?>) result).toVertexList();
} else {
System.err.println("TEMP DEBUG: Expected a DEdgeList but got a "
+ result.getClass().getName());
}
}
if (getFilterKeys() != null) {
if (result instanceof DEdgeList) {
// System.out.println("TEMP DEBUG: Applying a filter to a DEdgeList");
List<CharSequence> filterKeys = getFilterKeys();
List<CharSequence> filterValues = getFilterValues();
for (int i = 0; i < filterKeys.size(); i++) {
result = ((DEdgeList) result).applyFilter(filterKeys.get(i).toString(),
filterValues.get(i).toString());
}
} else if (result instanceof DVertexList) {
// System.out.println("TEMP DEBUG: Applying a filter to a DVertexList");
List<CharSequence> filterKeys = getFilterKeys();
List<CharSequence> filterValues = getFilterValues();
for (int i = 0; i < filterKeys.size(); i++) {
result = ((DVertexList) result).applyFilter(filterKeys.get(i).toString(),
filterValues.get(i).toString());
}
} else if (result instanceof FramedEdgeList) {
// System.out.println("TEMP DEBUG: Applying a filter to a FramedEdgeList");
List<CharSequence> filterKeys = getFilterKeys();
List<CharSequence> filterValues = getFilterValues();
for (int i = 0; i < filterKeys.size(); i++) {
result = ((FramedEdgeList<?>) result).applyFilter(filterKeys.get(i).toString(),
filterValues.get(i).toString());
}
} else if (result instanceof FramedVertexList) {
List<CharSequence> filterKeys = getFilterKeys();
List<CharSequence> filterValues = getFilterValues();
for (int i = 0; i < filterKeys.size(); i++) {
String curkey = filterKeys.get(i).toString();
String curvalue = filterValues.get(i).toString();
// System.out.println("TEMP DEBUG: Applying a filter to a FramedVertexList - "
// + curkey + ":" + curvalue);
result = ((FramedVertexList<?>) result).applyFilter(curkey, curvalue);
}
}
}
if (getOrderBys() != null) {
if (result instanceof FramedEdgeList) {
// System.out.println("Ordering an edge list");
result = ((FramedEdgeList<?>) result).sortBy(getOrderBys(), getDescending());
} else if (result instanceof FramedVertexList) {
// System.out.println("Ordering a vertex list");
result = ((FramedVertexList<?>) result).sortBy(getOrderBys(), getDescending());
}
}
if (getStart() > 0) {
if (getCount() > 0) {
if (result instanceof FramedEdgeList) {
result = ((FramedEdgeList<?>) result).subList(getStart(), getStart()
+ getCount());
} else if (result instanceof FramedVertexList) {
result = ((FramedVertexList<?>) result).subList(getStart(), getStart()
+ getCount());
}
} else {
if (result instanceof FramedEdgeList) {
result = ((FramedEdgeList<?>) result).subList(getStart(),
((FramedEdgeList<?>) result).size());
} else if (result instanceof FramedVertexList) {
result = ((FramedVertexList<?>) result).subList(getStart(),
((FramedVertexList<?>) result).size());
}
}
}
// if (result instanceof List) {
// System.out.println("Result is a " +
// result.getClass().getName() + " with "
// + ((List) result).size() + " elements");
// } else {
// System.out.println("Result is a " +
// result.getClass().getName());
// }
if (result instanceof FramedVertexList) {
ParamMap listMap = new ParamMap();
if (getIncludeEdges()) {
listMap.put(Parameters.EDGES, EMPTY_STRINGS);
}
if (getIncludeCounts()) {
listMap.put(Parameters.COUNTS, EMPTY_STRINGS);
}
listMap.put(Parameters.PROPS, CaseInsensitiveString.toStrings(this.getProperties()));
result = new JsonFrameListAdapter(getGraph(), (FramedVertexList<?>) result, listMap);
}
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.err.println("No method found for key " + label);
}
} else {
// System.out.println("TEMP DEBUG finding property " + key);
Method crystal = getGetters().get(key);
if (crystal != null) {
try {
result = crystal.invoke(frame, (Object[]) null);
// if (frame instanceof VertexFrame) {
// Vertex v = ((VertexFrame) frame).asVertex();
// if (v instanceof DProxyVertex) {
// System.out.println("TEMP DEBUG using a proxy vertex");
// }
// System.out.println("TEMP DEBUG invoking getter for "
// + crystal.getName());
// }
} catch (Exception e) {
if (frame instanceof EdgeFrame) {
result = ((EdgeFrame) frame).asEdge().getProperty(paramKey);
} else if (frame instanceof VertexFrame) {
result = ((VertexFrame) frame).asVertex().getProperty(paramKey);
// System.out.println("TEMP DEBUG using getProperty for key "
// + key);
} else {
System.err.println("Trying to get property " + paramKey + " from an object "
+ frame.getClass().getName());
}
}
} else {
if (frame instanceof ViewVertex.Contains) {
result = ((EdgeFrame) frame).asEdge().getProperty(paramKey);
} else if (frame instanceof VertexFrame) {
result = ((VertexFrame) frame).asVertex().getProperty(paramKey);
} else if (frame instanceof EdgeFrame) {
result = ((EdgeFrame) frame).asEdge().getProperty(paramKey);
} else {
System.err.println("No method found for key " + paramKey);
}
}
}
} else {
System.err.println("Unable to get property " + paramKey + " on a null object");
}
return result;
}
@Override
public void putJsonProperty(String paramKey, Object value) {
Object frame = getFrame();
if (frame != null) {
CaseInsensitiveString key = new CaseInsensitiveString(paramKey);
Method crystal = getSetters().get(key);
if (crystal != null) {
Class<?> type = null;
try {
Class<?>[] types = crystal.getParameterTypes();
if (types != null && types.length > 0) {
type = types[0];
if (!(type.isPrimitive() && value == null)) {
Object newValue = TypeUtils.convertToTarget(value, type, null);
crystal.invoke(frame, newValue);
}
} else {
crystal.invoke(frame, value);
}
} catch (Exception e) {
System.err.println("Exception trying to replace property " + paramKey + " with a value of "
+ String.valueOf(value) + " of type "
+ (value != null ? value.getClass().getName() : "null")
+ (type != null ? " when what we need is a " + type.getName() : ""));
e.printStackTrace();
}
} else {
if (frame instanceof EdgeFrame) {
((EdgeFrame) frame).asEdge().setProperty(paramKey, value);
} else if (frame instanceof VertexFrame) {
((VertexFrame) frame).asVertex().setProperty(paramKey, value);
}
}
}
}
}