/************************************************************************
* Copyright (c) 2016 IoT-Solutions e.U.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
************************************************************************/
package iot.jcypher.query.result.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.json.JsonArray;
import javax.json.JsonNumber;
import javax.json.JsonObject;
import javax.json.JsonString;
import javax.json.JsonValue;
import javax.json.JsonValue.ValueType;
import iot.jcypher.graph.GrAccess;
import iot.jcypher.graph.GrLabel;
import iot.jcypher.graph.SyncState;
import iot.jcypher.query.result.util.ResultHandler.AContentHandler;
import iot.jcypher.query.result.util.ResultHandler.ElemType;
import iot.jcypher.query.result.util.ResultHandler.ElementInfo;
import iot.jcypher.query.result.util.ResultHandler.PathInfo;
import iot.jcypher.query.result.util.ResultHandler.RelationInfo;
import iot.jcypher.util.ResultSettings;
public class JSONContentHandler extends AContentHandler {
private JsonObject jsonResult;
// needed to support multiple queries
private int queryIndex;
private List<String> columns;
private Map<String, Integer> columnIndices;
public JSONContentHandler(JsonObject jsonResult, int queryIndex) {
this.jsonResult = jsonResult;
this.queryIndex = queryIndex;
this.columnIndices = new HashMap<String, Integer>();
}
@Override
public List<String> getColumns() {
if (this.columns == null) {
List<String> colmns = new ArrayList<String>();
JsonArray cols = ((JsonObject)this.jsonResult.getJsonArray("results").get(
this.queryIndex)).getJsonArray("columns");
int sz = cols.size();
for (int i = 0;i < sz; i++) {
colmns.add(cols.getString(i));
}
this.columns = colmns;
}
return this.columns;
}
@Override
public Iterator<RowOrRecord> getDataIterator() {
return new RowIterator();
}
@Override
public int getColumnIndex(String colKey) {
Integer idx = this.columnIndices.get(colKey);
if (idx == null) {
List<String> cols = getColumns();
for (int i = 0; i < cols.size(); i++) {
if (cols.get(i).equals(colKey)) {
idx = new Integer(i);
}
}
if (idx == null)
idx = new Integer(-1);
this.columnIndices.put(colKey, idx);
}
return idx.intValue();
}
@Override
public Object convertContentValue(Object value) {
if (value instanceof JsonValue) {
JsonValue val = (JsonValue) value;
Object ret = null;
ValueType typ = val.getValueType();
if (typ == ValueType.NUMBER)
ret = ((JsonNumber)val).bigDecimalValue();
else if (typ == ValueType.STRING)
ret = ((JsonString)val).getString();
else if (typ == ValueType.FALSE)
ret = Boolean.FALSE;
else if (typ == ValueType.TRUE)
ret = Boolean.TRUE;
else if (typ == ValueType.ARRAY) {
JsonArray arr = (JsonArray)val;
List<Object> vals = new ArrayList<Object>();
int sz = arr.size();
for (int i = 0; i < sz; i++) {
JsonValue v = arr.get(i);
vals.add(convertContentValue(v));
}
ret = vals;
} else if (typ == ValueType.OBJECT) {
//JsonObject obj = (JsonObject)val;
}
return ret;
}
return value;
}
@Override
public Iterator<PropEntry> getPropertiesIterator(long id, int rowIndex, ElemType typ) {
JsonObject propertiesObject = getPropertiesObject(id, rowIndex, typ);
Iterator<Entry<String, JsonValue>> esIt = propertiesObject.entrySet().iterator();
return new PropertiesIterator(esIt);
}
@Override
public String getRelationType(long relationId, int rowIndex) {
if (rowIndex >= 0) {
JsonObject graphObject = getGraphObject(rowIndex);
JsonArray elemsArray = graphObject.getJsonArray("relationships");
int sz = elemsArray.size();
for (int i = 0; i < sz; i++) {
JsonObject elem = elemsArray.getJsonObject(i);
String idStr = elem.getString("id");
long elemId;
try {
elemId = Long.parseLong(idStr);
} catch (Throwable e) {
throw new RuntimeException(e);
}
if (relationId == elemId) {
return elem.getString("type");
}
}
}
return null;
}
@Override
public List<GrLabel> getNodeLabels(long nodeId, int rowIndex) {
List<GrLabel> labels = new ArrayList<GrLabel>();
if (rowIndex >= 0) {
JsonArray labelsArray = getNodeLabelsObject(nodeId, rowIndex);
int sz = labelsArray.size();
for (int i = 0; i < sz; i++) {
GrLabel label = GrAccess.createLabel(labelsArray.getString(i));
GrAccess.setState(label, SyncState.SYNC);
labels.add(label);
}
}
return labels;
}
private JsonArray getNodeLabelsObject(long id, int rowIndex) {
JsonObject graphObject = getGraphObject(rowIndex);
JsonArray elemsArray = graphObject.getJsonArray("nodes");
int sz = elemsArray.size();
for (int i = 0; i < sz; i++) {
JsonObject elem = elemsArray.getJsonObject(i);
String idStr = elem.getString("id");
long elemId;
try {
elemId = Long.parseLong(idStr);
} catch (Throwable e) {
throw new RuntimeException(e);
}
if (id == elemId) {
return elem.getJsonArray("labels");
}
}
return null;
}
private JsonArray getRestArray(JsonObject dataObject) {
return dataObject.getJsonArray("rest");
}
private JsonValue getRestValue(JsonArray restArray, int colIdx) {
return restArray.get(colIdx);
}
private JsonArray getDataArray() {
return ((JsonObject)jsonResult.getJsonArray("results").get(
queryIndex)).getJsonArray("data");
}
private JsonObject getDataObject(int rowIndex) {
JsonArray datas = getDataArray();
return datas.getJsonObject(rowIndex);
}
private JsonObject getGraphObject(int rowIndex) {
JsonObject dataObject = getDataObject(rowIndex);
return dataObject.getJsonObject("graph");
}
private JsonObject getPropertiesObject(long id, int rowIndex, ElemType typ) {
JsonObject graphObject = getGraphObject(rowIndex);
JsonArray elemsArray = null;
if (typ == ElemType.NODE)
elemsArray = graphObject.getJsonArray("nodes");
else if (typ == ElemType.RELATION)
elemsArray = graphObject.getJsonArray("relationships");
int sz = elemsArray.size();
for (int i = 0; i < sz; i++) {
JsonObject elem = elemsArray.getJsonObject(i);
String idStr = elem.getString("id");
long elemId;
try {
elemId = Long.parseLong(idStr);
} catch (Throwable e) {
throw new RuntimeException(e);
}
if (id == elemId) {
return elem.getJsonObject("properties");
}
}
return null;
}
/************************************/
public class RowIterator implements Iterator<RowOrRecord> {
private Iterator<JsonValue> jsonIterator;
public RowIterator() {
super();
JsonArray datas = getDataArray();
this.jsonIterator = datas.iterator();
}
@Override
public boolean hasNext() {
return this.jsonIterator.hasNext();
}
@Override
public RowOrRecord next() {
JsonValue nextVal = this.jsonIterator.next();
return new Row(nextVal);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
/*****************************/
public class Row extends RowOrRecord {
private JsonValue jsonValue;
public Row(JsonValue jsonValue) {
super();
this.jsonValue = jsonValue;
}
@Override
public ElementInfo getElementInfo(String colKey) {
int colIdx = getColumnIndex(colKey);
if (colIdx == -1)
throw new RuntimeException("no result column: " + colKey);
JsonObject dataObject = (JsonObject) this.jsonValue;
JsonArray restArray = getRestArray(dataObject);
JsonValue obj = getRestValue(restArray, colIdx);
obj = this.handleArrayCase(obj);
if (obj.getValueType() == ValueType.OBJECT) {
JsonObject restObject = (JsonObject)obj;
if (restObject.containsKey("self")) {
String selfString = restObject.getString("self");
if (selfString != null) {
ElementInfo ei = ElementInfo.parse(selfString);
return ei;
}
}
} else if (obj.getValueType() == ValueType.NULL) {
ElementInfo ei = ElementInfo.nullElement();
return ei;
}
return null;
}
@Override
public RelationInfo getRelationInfo(String colKey) {
JsonObject dataObject = (JsonObject) this.jsonValue;
JsonArray restArray = getRestArray(dataObject);
JsonValue restObject = getRestValue(restArray, getColumnIndex(colKey));
restObject = this.handleArrayCase(restObject);
String startString = ((JsonObject)restObject).getString("start");
String endString = ((JsonObject)restObject).getString("end");
RelationInfo ri = RelationInfo.parse(startString, endString);
return ri;
}
@Override
public PathInfo getPathInfo(String colKey) {
PathInfo pathInfo = null;
int colIdx = getColumnIndex(colKey);
if (colIdx == -1)
throw new RuntimeException("no result column: " + colKey);
JsonObject dataObject = (JsonObject) this.jsonValue;
JsonArray restArray = getRestArray(dataObject);
JsonValue restValue = getRestValue(restArray, colIdx);
restValue = this.handleArrayCase(restValue);
if (restValue.getValueType() == ValueType.OBJECT) {
JsonObject pathObject = (JsonObject) restValue;
String str = pathObject.getString("start");
long startId = Long.parseLong(str.substring(str.lastIndexOf('/') + 1));
str = pathObject.getString("end");
long endId = Long.parseLong(str.substring(str.lastIndexOf('/') + 1));
JsonArray rels = pathObject.getJsonArray("relationships");
List<Long> relIds = new ArrayList<Long>();
int sz = rels.size();
for (int i = 0; i < sz; i++) {
String rel = rels.getString(i);
long rid = Long.parseLong(rel.substring(rel.lastIndexOf('/') + 1));
relIds.add(Long.valueOf(rid));
}
pathInfo = new PathInfo(startId, endId, relIds, pathObject);
}
return pathInfo;
}
@Override
public long gePathtNodeIdAt(PathInfo pathInfo, int index) {
Object obj = pathInfo.getContentObject();
JsonArray nodes = null;
if (obj instanceof JsonArray)
nodes = (JsonArray)obj;
else if (obj instanceof JsonObject) {
nodes = ((JsonObject)obj).getJsonArray("nodes");
pathInfo.setContentObject(nodes);
}
String str = nodes.getString(index);
return Long.parseLong(str.substring(str.lastIndexOf('/') + 1));
}
@SuppressWarnings("unchecked")
@Override
public <T> void addValue(String colKey, List<T> vals) {
int colIdx = getColumnIndex(colKey);
if (colIdx == -1)
throw new RuntimeException("no result column: " + colKey);
JsonValue restVal = getRestValue(getRestArray((JsonObject) this.jsonValue), colIdx);
Object v = convertContentValue(restVal);
if (v != null)
vals.add((T) v);
else {
if (ResultHandler.includeNullValues.get().booleanValue()
|| ResultSettings.includeNullValuesAndDuplicates.get().booleanValue())
vals.add((T) v);
}
}
private JsonValue handleArrayCase(JsonValue obj) {
if (obj.getValueType() == ValueType.ARRAY && ((JsonArray)obj).size() > 0)
return ((JsonArray)obj).get(0);
return obj;
}
}
}
/************************************/
public class PropertiesIterator implements Iterator<PropEntry> {
private Iterator<Entry<String, JsonValue>> iterator;
public PropertiesIterator(Iterator<Entry<String, JsonValue>> iterator) {
super();
this.iterator = iterator;
}
@Override
public boolean hasNext() {
return this.iterator.hasNext();
}
@Override
public PropEntry next() {
Entry<String, JsonValue> next = this.iterator.next();
return new PropEntry(next.getKey(), next.getValue());
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}