package org.neo4j.jdbc.rest;
import org.codehaus.jackson.JsonNode;
import java.util.*;
/**
* @author mh
* @since 12.06.12
*/
// TODO Implement support for streaming Cypher here implements Iterator<Map<String,Object>>
public class ResultParser {
private final JsonNode node;
private List<String> columns;
private int cols;
private final Object[] rowData;
public ResultParser(JsonNode node) {
this.node = node;
this.columns = parseColumns();
this.cols = columns.size();
rowData = new Object[cols];
}
public List<String> getColumns() {
return columns;
}
public List<String> parseColumns() {
List<String> columns = new ArrayList<String>(20);
for (JsonNode column : node.get("columns")) {
String textValue = column.getTextValue();
columns.add(textValue);
}
return columns;
}
Iterator<Object[]> streamData() {
final Iterator<JsonNode> rows = node.get("data").iterator();
return new Iterator<Object[]>() {
public boolean hasNext() { return rows.hasNext(); }
public Object[] next() { return parseRow(rows.next()); }
public void remove() { throw new UnsupportedOperationException(); }
};
}
private Object[] parseRow(JsonNode row) {
int i=0;
for (JsonNode cell : row) {
rowData[i++]=toObject(cell);
}
for (;i<cols;i++) {
rowData[i]=null;
}
return rowData;
}
private Object toObject(JsonNode cell) {
if (cell.isObject()) {
if (cell.has("length") && cell.has("nodes") && cell.has("relationships")) {
return toPath(cell);
}
Map<String, Object> result = toPropertyContainer(cell);
return addRelationshipInfo(cell, result);
}
if (cell.isArray()) {
ArrayList<Object> result = new ArrayList<Object>(cell.size());
for (JsonNode node : cell) {
result.add(toObject(node));
}
return result;
}
if (cell.isTextual()) return cell.getTextValue();
if (cell.isBoolean()) return cell.getBooleanValue();
if (cell.isNumber()) return cell.getNumberValue();
return cell.getTextValue();
}
private Object addRelationshipInfo(JsonNode cell, Map<String, Object> result) {
if (cell.has("start")) result.put("_start", idOf(cell.get("start")));
if (cell.has("end")) result.put("_end", idOf(cell.get("end")));
if (cell.has("type")) result.put("_type", cell.get("type").getTextValue());
return result;
}
private Map<String, Object> toPropertyContainer(JsonNode cell) {
Map<String, Object> result = new TreeMap<String, Object>();
String idField = cell.has("type") ? "_rel_id" : "_node_id";
result.put(idField, idOf(cell.get("self")));
JsonNode data = cell.get("data");
if (data != null && data.isObject() && data.size() > 0) {
Iterator<String> fieldNames = data.getFieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
result.put(fieldName, toObject(data.get(fieldName)));
}
}
return result;
}
private ArrayList<Object> toPath(JsonNode cell) {
ArrayList<Object> path = new ArrayList<Object>(cell.get("length").getIntValue() + 1);
Iterator<JsonNode> nodes = cell.get("nodes").iterator();
Iterator<JsonNode> relationships = cell.get("relationships").iterator();
while (nodes.hasNext()) {
path.add(map("_node_id", idOf(nodes.next())));
if (relationships.hasNext()) {
path.add(map("_rel_id", idOf(relationships.next())));
}
}
return path;
}
private Map<String, Object> map(String key, Object value) {
TreeMap<String, Object> result = new TreeMap<String, Object>();
result.put(key, value);
return result;
}
private Long idOf(JsonNode node) {
if (node == null) {
return null;
}
String uri = node.getTextValue();
if (uri == null) return null;
int idx = uri.lastIndexOf("/");
return idx == -1 ? null : Long.valueOf(uri.substring(idx + 1));
}
}