/************************************************************************
* 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.domainquery.internal;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonValue;
import javax.json.stream.JsonGenerator;
import javax.json.stream.JsonGeneratorFactory;
import iot.jcypher.domainquery.ast.Parameter;
import iot.jcypher.domainquery.internal.RecordedQuery.Assignment;
import iot.jcypher.domainquery.internal.RecordedQuery.DOMatchRef;
import iot.jcypher.domainquery.internal.RecordedQuery.Invocation;
import iot.jcypher.domainquery.internal.RecordedQuery.Literal;
import iot.jcypher.domainquery.internal.RecordedQuery.Reference;
import iot.jcypher.domainquery.internal.RecordedQuery.Statement;
import iot.jcypher.query.writer.Format;
import iot.jcypher.query.writer.JSONWriter;
public class JSONConverter {
private static final String TYPE_KEY = "type";
private static final String ON_OBJECT_KEY = "onObjectRef";
private static final String RETURN_OBJECT_KEY = "returnObjectRef";
private static final String METHOD_KEY = "method";
private static final String STATEMENTS = "statements";
private static final String INVOCATION = "Invocation";
private static final String ASSIGNMENT = "Assignment";
private static final String LITERAL = "Literal";
private static final String PARAM = "Parameter";
private static final String PARAM_NAME = "paramName";
private static final String REFERENCE = "Reference";
private static final String PARAMETERS = "parameters";
private static final String AUGMENTATIONS = "augmentations";
private static final String GENERIC = "generic";
private static final String LITERAL_TYPE = "LiteralType";
private static final String LITERAL_VALUE = "LiteralValue";
private static final String REF_ID = "refId";
private static final String REF_TYPE = "RefType";
private static final String REF_VALUE = "RefValue";
private static final String DO_MATCH_REF = "DOMatchRef";
private static final String REF = "ref";
private static final String KEY = "key";
private static final String VALUE = "value";
private static final String ARRAY_PREF = "Array(";
private static final String ARRAY_POST = ")";
private Format prettyFormat = Format.NONE;
/**
* Answer a JSON representation of a recorded query
* @param query
* @return
*/
public String toJSON(RecordedQuery query) {
StringWriter sw = new StringWriter();
JsonGenerator generator;
if (this.prettyFormat != Format.NONE) {
JsonGeneratorFactory gf = JSONWriter.getPrettyGeneratorFactory();
generator = gf.createGenerator(sw);
} else
generator = Json.createGenerator(sw);
generator.writeStartObject();
writeQuery(query, generator);
generator.writeEnd();
generator.flush();
return sw.toString();
}
/**
* Build a recorded query from it's JSON representation
* @param json
* @return
*/
public RecordedQuery fromJSON(String json) {
RecordedQuery ret = new RecordedQuery(false);
StringReader sr = new StringReader(json);
JsonReader reader = Json.createReader(sr);
JsonObject jsonResult = reader.readObject();
ret.setGeneric(jsonResult.getBoolean(GENERIC));
JsonArray augmentations = jsonResult.getJsonArray(AUGMENTATIONS);
if (augmentations != null) {
Map<String, String> augs = new HashMap<String, String>();
Iterator<JsonValue> ait = augmentations.iterator();
while(ait.hasNext()) {
JsonObject a = (JsonObject) ait.next();
augs.put(a.getString(KEY), a.getString(VALUE));
}
ret.setAugmentations(augs);
}
JsonArray statements = jsonResult.getJsonArray(STATEMENTS);
Iterator<JsonValue> it = statements.iterator();
while(it.hasNext()) {
JsonValue s = it.next();
readStatement(s, ret.getStatements(), ret);
}
return ret;
}
void readStatement(JsonValue s, List<Statement> statements, RecordedQuery rq) {
Statement statement = null;
if (s instanceof JsonObject) {
JsonObject jobj = (JsonObject)s;
String typ = jobj.getString(TYPE_KEY);
if (ASSIGNMENT.equals(typ) || INVOCATION.equals(typ)) {
JsonArray params = jobj.getJsonArray(PARAMETERS);
List<Statement> parameters = new ArrayList<Statement>();
Iterator<JsonValue> it = params.iterator();
while(it.hasNext()) {
JsonValue v = it.next();
readStatement(v, parameters, rq);
}
if (ASSIGNMENT.equals(typ))
statement = rq.createStatement(Assignment.class);
else
statement = rq.createStatement(Invocation.class);
Invocation inv = (Invocation)statement;
inv.setParams(parameters);
inv.setOnObjectRef(jobj.getString(ON_OBJECT_KEY));
inv.setMethod(jobj.getString(METHOD_KEY));
inv.setReturnObjectRef(jobj.getString(RETURN_OBJECT_KEY));
} else if (LITERAL.equals(typ) || PARAM.equals(typ)) {
boolean isParam = PARAM.equals(typ);
statement = rq.createStatement(Literal.class);
String lTyp = jobj.getString(LITERAL_TYPE);
JsonValue jsVal = jobj.get(LITERAL_VALUE);
Object val = ConversionUtil.fromJSON(lTyp, jsVal);
if (isParam) {
String pName = jobj.getString(PARAM_NAME);
Parameter param = rq.getCreateParameter(pName); // get or create
param.setValue(val);
val = param;
}
((Literal)statement).setValue(val);
} else if (REFERENCE.equals(typ)) {
statement = rq.createStatement(Reference.class);
((Reference)statement).setRefId(jobj.getString(REF_ID));
String rTyp = jobj.getString(REF_TYPE);
String rVal = jobj.getString(REF_VALUE);
Object val = ConversionUtil.from(rTyp, rVal);
((Reference)statement).setValue(val);
} else if (DO_MATCH_REF.equals(typ)) {
statement = rq.createStatement(DOMatchRef.class);
((DOMatchRef)statement).setRef(jobj.getString(REF));
}
}
if (statement != null)
statements.add(statement);
}
private void writeQuery(RecordedQuery query, JsonGenerator generator) {
generator.write(GENERIC, query.isGeneric());
if (query.getAugmentations() != null) {
generator.writeStartArray(AUGMENTATIONS);
Iterator<Entry<String, String>> it = query.getAugmentations().entrySet().iterator();
while(it.hasNext()) {
Entry<String, String> entry = it.next();
generator.writeStartObject();
generator.write(KEY, entry.getKey());
generator.write(VALUE, entry.getValue());
generator.writeEnd();
}
generator.writeEnd();
}
generator.writeStartArray(STATEMENTS);
for(Statement s : query.getStatements()) {
writeStatement(s, generator);
}
generator.writeEnd();
}
private void writeStatement(Statement statement, JsonGenerator generator) {
generator.writeStartObject();
if (statement instanceof Invocation) {
Invocation inv = (Invocation)statement;
if (statement instanceof Assignment)
generator.write(TYPE_KEY, ASSIGNMENT);
else
generator.write(TYPE_KEY, INVOCATION);
generator.write(ON_OBJECT_KEY, inv.getOnObjectRef());
generator.write(METHOD_KEY, inv.getMethod());
generator.write(RETURN_OBJECT_KEY, inv.getReturnObjectRef());
generator.writeStartArray(PARAMETERS);
for (Statement s : inv.getParams()) {
writeStatement(s, generator);
}
generator.writeEnd();
} else if (statement instanceof Literal) {
Object val = ((Literal)statement).getRawValue();
writeLiteral(val, generator);
} else if (statement instanceof Reference) {
Reference ref = (Reference)statement;
generator.write(TYPE_KEY, REFERENCE);
generator.write(REF_ID, ref.getRefId());
Object val = ref.getValue();
if (val != null) {
generator.write(REF_TYPE, val.getClass().getName());
generator.write(REF_VALUE, val.toString());
}
} else if (statement instanceof DOMatchRef) {
generator.write(TYPE_KEY, DO_MATCH_REF);
generator.write(REF, ((DOMatchRef)statement).getRef());
}
generator.writeEnd();
}
private void writeLiteral(Object val, JsonGenerator generator) {
boolean isParam = val instanceof iot.jcypher.domainquery.ast.Parameter;
if (isParam) {
generator.write(TYPE_KEY, PARAM);
generator.write(PARAM_NAME, ((iot.jcypher.domainquery.ast.Parameter)val).getName());
val = ((iot.jcypher.domainquery.ast.Parameter)val).getValue();
} else
generator.write(TYPE_KEY, LITERAL);
if (val != null) {
boolean isColl = Collection.class.isAssignableFrom(val.getClass());
boolean isArray = val.getClass().isArray();
if (isArray) {
StringBuilder sb = new StringBuilder();
sb.append(ARRAY_PREF);
sb.append(val.getClass().getComponentType().getName());
sb.append(ARRAY_POST);
generator.write(LITERAL_TYPE, sb.toString());
} else
generator.write(LITERAL_TYPE, val.getClass().getName());
if (isArray || isColl) {
generator.writeStartArray(LITERAL_VALUE);
if (isColl) {
Iterator<?> it = ((Collection<?>)val).iterator();
while(it.hasNext()) {
generator.writeStartObject();
writeLiteral(it.next(), generator);
generator.writeEnd();
}
} else { // an array
for (int i = 0; i < Array.getLength(val); i++) {
generator.writeStartObject();
writeLiteral(Array.get(val, i), generator);
generator.writeEnd();
}
}
generator.writeEnd();
} else
generator.write(LITERAL_VALUE, val.toString());
}
}
/**
* Set the format for creating JSON representations (i.e use of indentation and new lines),
* <br/>the default is 'no pretty printing'.
* @param prettyFormat
* @return
*/
public JSONConverter setPrettyFormat(Format prettyFormat) {
this.prettyFormat = prettyFormat;
return this;
}
}