package org.openntf.domino.rest.resources.frames;
import com.ibm.commons.util.io.json.JsonException;
import com.ibm.commons.util.io.json.JsonJavaObject;
import com.ibm.commons.util.io.json.JsonParser;
import com.ibm.domino.httpmethod.PATCH;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Element;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.frames.EdgeFrame;
import com.tinkerpop.frames.VertexFrame;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriInfo;
import org.openntf.domino.big.NoteCoordinate;
import org.openntf.domino.big.ViewEntryCoordinate;
import org.openntf.domino.graph2.DGraphUtils;
import org.openntf.domino.graph2.DKeyResolver;
import org.openntf.domino.graph2.impl.DFramedTransactionalGraph;
import org.openntf.domino.rest.json.JsonGraphFactory;
import org.openntf.domino.rest.json.JsonGraphWriter;
import org.openntf.domino.rest.resources.AbstractResource;
import org.openntf.domino.rest.service.Headers;
import org.openntf.domino.rest.service.ODAGraphService;
import org.openntf.domino.rest.service.Parameters;
import org.openntf.domino.rest.service.Parameters.ParamMap;
import org.openntf.domino.rest.service.Routes;
import org.openntf.domino.types.CaseInsensitiveString;
import org.openntf.domino.utils.DominoUtils;
import org.openntf.domino.utils.Factory;
@Path(Routes.ROOT + "/" + Routes.FRAMED + "/" + Routes.NAMESPACE_PATH_PARAM)
public class FramedResource extends AbstractResource {
public FramedResource(ODAGraphService service) {
super(service);
}
@SuppressWarnings("unchecked")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getFramedObject(@Context final UriInfo uriInfo, @PathParam(Routes.NAMESPACE) final String namespace)
throws JsonException, IOException {
@SuppressWarnings("rawtypes")
DFramedTransactionalGraph graph = this.getGraph(namespace);
String jsonEntity = null;
ResponseBuilder builder = Response.ok();
ParamMap pm = Parameters.toParamMap(uriInfo);
StringWriter sw = new StringWriter();
JsonGraphWriter writer = new JsonGraphWriter(sw, graph, pm, false, true);
if (pm.get(Parameters.ID) != null) {
List<String> ids = pm.get(Parameters.ID);
if (ids.size() == 0) {
writer.outNull();
} else if (ids.size() == 1) {
String id = ids.get(0);
NoteCoordinate nc = null;
if (id.startsWith("E")) {
nc = ViewEntryCoordinate.Utils.getViewEntryCoordinate(id);
} else if (id.startsWith("V")) {
nc = ViewEntryCoordinate.Utils.getViewEntryCoordinate(id);
} else {
nc = NoteCoordinate.Utils.getNoteCoordinate(id);
}
if (nc == null) {
System.err.println("NoteCoordinate is null for id " + id);
}
if (graph == null) {
System.err.println("Graph is null for namespace " + namespace);
}
Object elem = graph.getElement(nc, null);
if (elem == null) {
throw new IllegalStateException("Graph element is null for id " + id);
}
writer.outObject(elem);
} else {
List<Object> maps = new ArrayList<Object>();
for (String id : ids) {
NoteCoordinate nc = NoteCoordinate.Utils.getNoteCoordinate(id);
maps.add(graph.getElement(nc, null));
}
writer.outArrayLiteral(maps);
}
jsonEntity = sw.toString();
} else if (pm.getKeys() != null) {
Class<?> type = null;
if (pm.getTypes() != null) {
List<CharSequence> types = pm.getTypes();
String typename = types.get(0).toString();
type = graph.getTypeRegistry().findClassByName(typename);
}
DKeyResolver resolver = graph.getKeyResolver(type);
List<CharSequence> keys = pm.getKeys();
if (keys.size() == 0) {
writer.outNull();
} else if (keys.size() == 1) {
CharSequence id = keys.get(0);
NoteCoordinate nc = resolver.resolveKey(type, id);
if (nc == null) {
System.err.println("NoteCoordinate is null for id " + id);
}
Object elem = graph.getElement(nc);
if (elem == null) {
elem = resolver.handleMissingKey(type, id);
if (elem == null) {
throw new IllegalStateException("Graph element is null for id " + id);
}
}
if (elem instanceof Vertex) {
// System.out.println("TEMP DEBUG Framing a vertex of type "
// + elem.getClass().getName());
Object vf = graph.frame((Vertex) elem, type);
writer.outObject(vf);
} else if (elem instanceof Edge) {
Object ef = graph.frame((Edge) elem, type);
writer.outObject(ef);
}
} else {
List<Object> maps = new ArrayList<Object>();
for (CharSequence id : keys) {
NoteCoordinate nc = resolver.resolveKey(type, id);
maps.add(graph.getElement(nc, null));
}
writer.outArrayLiteral(maps);
}
jsonEntity = sw.toString();
} else {
// System.out.println("TEMP DEBUG: ID was null therefore we can't report...");
MultivaluedMap<String, String> mvm = uriInfo.getQueryParameters();
for (String key : mvm.keySet()) {
// System.out.println("TEMP DEBUG: " + key + ": " +
// mvm.getFirst(key));
}
Map<String, Object> jsonMap = new LinkedHashMap<String, Object>();
jsonMap.put("namespace", namespace);
jsonMap.put("status", "active");
writer.outObject(jsonMap);
jsonEntity = sw.toString();
}
builder.type(MediaType.APPLICATION_JSON_TYPE).entity(jsonEntity);
Response response = builder.build();
return response;
}
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response putDocumentByUnid(String requestEntity, @Context final UriInfo uriInfo,
@PathParam(Routes.NAMESPACE) final String namespace,
@HeaderParam(Headers.IF_UNMODIFIED_SINCE) final String ifUnmodifiedSince) throws JsonException, IOException {
ParamMap pm = Parameters.toParamMap(uriInfo);
Response response = updateFrameByMetaid(requestEntity, namespace, ifUnmodifiedSince, pm, true);
return response;
}
// @OPTIONS
@PATCH
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response patchDocumentByUnid(String requestEntity, @Context final UriInfo uriInfo,
@PathParam(Routes.NAMESPACE) final String namespace,
@HeaderParam(Headers.IF_UNMODIFIED_SINCE) final String ifUnmodifiedSince) throws JsonException, IOException {
ParamMap pm = Parameters.toParamMap(uriInfo);
Response response = updateFrameByMetaid(requestEntity, namespace, ifUnmodifiedSince, pm, false);
return response;
}
protected Response updateFrameByMetaid(String requestEntity, String namespace, String ifUnmodifiedSince,
ParamMap pm, boolean isPut) throws JsonException, IOException {
Response result = null;
DFramedTransactionalGraph<?> graph = this.getGraph(namespace);
JsonJavaObject jsonItems = null;
JsonGraphFactory factory = JsonGraphFactory.instance;
StringWriter sw = new StringWriter();
JsonGraphWriter writer = new JsonGraphWriter(sw, graph, pm, false, true);
try {
StringReader reader = new StringReader(requestEntity);
try {
jsonItems = (JsonJavaObject) JsonParser.fromJson(factory, reader);
// System.out.println("TEMP DEBUG requestEntity " +
// requestEntity);
// System.out.println("TEMP DEBUG check " +
// jsonItems.get("FirstName"));
} finally {
reader.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
if (jsonItems != null) {
Map<CaseInsensitiveString, Object> cisMap = new HashMap<CaseInsensitiveString, Object>();
for (String jsonKey : jsonItems.keySet()) {
CaseInsensitiveString cis = new CaseInsensitiveString(jsonKey);
cisMap.put(cis, jsonItems.get(jsonKey));
}
List<String> ids = pm.get(Parameters.ID);
if (ids.size() == 0) {
// TODO no id
} else {
JsonFrameAdapter adapter = null;
for (String id : ids) {
NoteCoordinate nc = NoteCoordinate.Utils.getNoteCoordinate(id);
Object element = graph.getElement(nc, null);
if (element instanceof EdgeFrame) {
adapter = new JsonFrameAdapter(graph, (EdgeFrame) element, null);
} else if (element instanceof VertexFrame) {
adapter = new JsonFrameAdapter(graph, (VertexFrame) element, null);
} else if (element == null) {
throw new RuntimeException("Cannot force a metaversalid through REST API: " + id);
} else {
throw new RuntimeException("TODO"); // TODO
}
Iterator<String> frameProperties = adapter.getJsonProperties();
while (frameProperties.hasNext()) {
CaseInsensitiveString key = new CaseInsensitiveString(frameProperties.next());
if (!key.startsWith("@")) {
Object value = cisMap.get(key);
if (value != null) {
// if ("fullname".equals(key)) {
// System.out.println("TEMP DEBUG fullname: " +
// value);
// }
adapter.putJsonProperty(key.toString(), value);
cisMap.remove(key);
} else if (isPut) {
adapter.putJsonProperty(key.toString(), value);
}
}
}
for (CaseInsensitiveString cis : cisMap.keySet()) {
if (!cis.startsWith("@")) {
Object value = cisMap.get(cis);
if (value != null) {
adapter.putJsonProperty(cis.toString(), value);
}
}
}
writer.outObject(element);
}
graph.commit();
}
}
ResponseBuilder builder = Response.ok();
builder.type(MediaType.APPLICATION_JSON_TYPE).entity(sw.toString());
result = builder.build();
return result;
}
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("rawtypes")
public Response deleteFramedObject(String requestEntity, @Context final UriInfo uriInfo,
@PathParam(Routes.NAMESPACE) final String namespace) {
DFramedTransactionalGraph graph = this.getGraph(namespace);
String jsonEntity = null;
ResponseBuilder builder = Response.ok();
ParamMap pm = Parameters.toParamMap(uriInfo);
StringWriter sw = new StringWriter();
JsonGraphWriter writer = new JsonGraphWriter(sw, graph, pm, false, true);
JsonGraphFactory factory = JsonGraphFactory.instance;
Map<String, String> report = new HashMap<String, String>();
List<String> ids = pm.get(Parameters.ID);
if (ids.size() == 0) {
// TODO no id
} else {
for (String id : ids) {
try {
NoteCoordinate nc = NoteCoordinate.Utils.getNoteCoordinate(id);
Object element = graph.getElement(nc, null);
if (element instanceof Element) {
((Element) element).remove();
} else if (element instanceof VertexFrame) {
((VertexFrame) element).asVertex().remove();
} else if (element instanceof EdgeFrame) {
((EdgeFrame) element).asEdge().remove();
}
report.put(id, "deleted");
} catch (Throwable t) {
DominoUtils.handleException(t);
}
}
graph.commit();
}
try {
writer.outObject(report);
} catch (JsonException e) {
DominoUtils.handleException(e);
} catch (IOException e) {
DominoUtils.handleException(e);
}
builder.type(MediaType.APPLICATION_JSON_TYPE).entity(sw.toString());
Response response = builder.build();
return response;
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("rawtypes")
public Response createFramedObject(String requestEntity, @Context final UriInfo uriInfo,
@PathParam(Routes.NAMESPACE) final String namespace) {
// Factory.println("Processing a POST for " + namespace);
DFramedTransactionalGraph graph = this.getGraph(namespace);
String jsonEntity = null;
ResponseBuilder builder = Response.ok();
ParamMap pm = Parameters.toParamMap(uriInfo);
StringWriter sw = new StringWriter();
JsonGraphWriter writer = new JsonGraphWriter(sw, graph, pm, false, true);
JsonJavaObject jsonItems = null;
JsonGraphFactory factory = JsonGraphFactory.instance;
try {
StringReader reader = new StringReader(requestEntity);
try {
jsonItems = (JsonJavaObject) JsonParser.fromJson(factory, reader);
} finally {
reader.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
if (jsonItems != null) {
Map<CaseInsensitiveString, Object> cisMap = new HashMap<CaseInsensitiveString, Object>();
for (String jsonKey : jsonItems.keySet()) {
CaseInsensitiveString cis = new CaseInsensitiveString(jsonKey);
cisMap.put(cis, jsonItems.get(jsonKey));
}
List<String> ids = pm.get(Parameters.ID);
if (ids == null) {
Map<String, String> map = new HashMap<String, String>();
map.put("error", "Cannot POST to frame without an id parameter");
try {
writer.outObject(map);
} catch (JsonException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
if (ids.size() == 0) {
// TODO no id
} else {
for (String id : ids) {
// System.out.println("TEMP DEBUG POSTing to " + id);
NoteCoordinate nc = NoteCoordinate.Utils.getNoteCoordinate(id);
Object element = graph.getElement(nc, null);
if (element instanceof VertexFrame) {
VertexFrame parVertex = (VertexFrame) element;
Map<CaseInsensitiveString, Method> adders = graph.getTypeRegistry().getAdders(
parVertex.getClass());
CaseInsensitiveString rawLabel = new CaseInsensitiveString(jsonItems.getAsString("@label"));
Method method = adders.get(rawLabel);
if (method != null) {
String rawId = jsonItems.getAsString("@id");
NoteCoordinate othernc = NoteCoordinate.Utils.getNoteCoordinate(rawId);
Object otherElement = graph.getElement(othernc, null);
if (otherElement instanceof VertexFrame) {
VertexFrame otherVertex = (VertexFrame) otherElement;
try {
Object result = method.invoke(parVertex, otherVertex);
if (result == null) {
System.out.println("Invokation of method " + method.getName()
+ " on a vertex of type " + DGraphUtils.findInterface(parVertex)
+ " with an argument of type "
+ DGraphUtils.findInterface(otherVertex)
+ " resulted in null when we expected an Edge");
}
JsonFrameAdapter adapter = new JsonFrameAdapter(graph, (EdgeFrame) result, null);
Iterator<String> frameProperties = adapter.getJsonProperties();
while (frameProperties.hasNext()) {
CaseInsensitiveString key = new CaseInsensitiveString(
frameProperties.next());
if (!key.startsWith("@")) {
Object value = cisMap.get(key);
if (value != null) {
adapter.putJsonProperty(key.toString(), value);
cisMap.remove(key);
}
}
}
for (CaseInsensitiveString cis : cisMap.keySet()) {
if (!cis.startsWith("@")) {
Object value = cisMap.get(cis);
if (value != null) {
adapter.putJsonProperty(cis.toString(), value);
}
}
}
writer.outObject(result);
} catch (Exception e) {
e.printStackTrace();
}
} else {
Factory.println("otherElement is not a VertexFrame. It's a "
+ (otherElement == null ? "null" : DGraphUtils.findInterface(otherElement)
.getName()));
}
} else {
Class[] interfaces = element.getClass().getInterfaces();
String intList = "";
for (Class inter : interfaces) {
intList = intList + inter.getName() + ", ";
}
String methList = "";
for (CaseInsensitiveString key : adders.keySet()) {
methList = methList + key.toString() + ", ";
}
Factory.println("No method found for " + rawLabel + " on element " + intList + ": "
+ ((VertexFrame) element).asVertex().getId() + " methods " + methList);
}
} else {
org.openntf.domino.utils.Factory.println("element is not a VertexFrame. It's a "
+ element.getClass().getName());
}
}
}
graph.commit();
}
}
builder.type(MediaType.APPLICATION_JSON_TYPE).entity(sw.toString());
Response response = builder.build();
return response;
}
}