/*******************************************************************************
* Copyright (c) 2017 Red Hat.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat - Initial Contribution
*******************************************************************************/
package org.eclipse.che.api.languageserver.generator;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static org.eclipse.che.api.languageserver.generator.DtoGenerator.INDENT;
import static org.eclipse.che.api.languageserver.generator.DtoGenerator.dtoName;
/**
* This class generates property conversion code from json properties to dto
* fields.
*
* @author Thomas Mäder
*/
public class FromJsonGenerator extends ConversionGenerator {
private final JsonImpl json;
public FromJsonGenerator(JsonImpl json) {
this.json = json;
}
public void generateFromJson(String indent, PrintWriter out, Method m, String objectName, String jsonName) {
Type paramType = m.getGenericParameterTypes()[0];
String fieldName = fieldName(m);
String jsonValName = jsonName + fieldName + "Json";
String valName = jsonName + fieldName + "Val";
out.println(indent + String.format("%1$s %2$s = %3$s.get(\"%4$s\");", json.element(), jsonValName, jsonName, fieldName));
out.println(indent + String.format("if (%1$s != null && !(%2$s)) {", jsonValName, json.isNull(jsonValName)));
generateFromJson(indent + INDENT, out, valName, jsonValName, m.getGenericParameterTypes()[0]);
out.println(String.format(indent + INDENT + "%1$s.%2$s((%3$s)%4$s);", objectName, m.getName(), paramType.getTypeName(), valName));
out.println(indent + "}");
}
private void generateFromJson(String indent, PrintWriter out, String varName, String valueAccess, Type paramType) {
if (List.class.isAssignableFrom(getRawClass(paramType))) {
generateListConversion(indent, out, varName, valueAccess, paramType);
} else if (Map.class.isAssignableFrom(getRawClass(paramType))) {
generateMapConversion(indent, out, varName, valueAccess, paramType);
} else if (Either.class.isAssignableFrom(getRawClass(paramType))) {
generateEitherConversion(indent, out, varName, valueAccess, paramType);
} else {
out.println(indent + String.format("%1$s %2$s = %3$s;", getRawClass(paramType).getName(), varName,
fromJsonConversion(getRawClass(paramType), valueAccess)));
}
}
private void generateMapConversion(String indent, PrintWriter out, String varName, String jsonValName, Type paramType) {
ParameterizedType genericType = (ParameterizedType)paramType;
Type containedType = genericType.getActualTypeArguments()[1];
String objectName = varName + "o";
String containedName = objectName + "X";
out.println(indent + String.format("HashMap<String, %1$s> %2$s= new HashMap<String, %3$s>();", containedType.getTypeName(), varName,
containedType.getTypeName()));
out.println(indent + String.format("%1$s %2$s = %3$s;", json.object(), objectName, json.objectValue(jsonValName)));
json.iterateObject(indent, out, objectName, (String keyName, String valueName) -> {
generateFromJson(indent + INDENT, out, containedName, valueName, containedType);
out.println(indent + INDENT + String.format("%1$s.put(%2$s, %3$s);", varName, keyName, containedName));
});
}
private void generateListConversion(String indent, PrintWriter out, String varName, String jsonValName, Type paramType) {
Type containedType = containedType(paramType);
out.println(indent + String.format("ArrayList<%1$s> %2$s= new ArrayList<%3$s>();", containedType.getTypeName(), varName,
containedType.getTypeName()));
String arrayName = varName + "a";
String containedName = arrayName + "X";
String indexName = arrayName + "i";
out.println(indent + String.format("%1$s %2$s = %3$s;", json.array(), arrayName, json.arrayValue(jsonValName)));
out.println(indent + String.format("for(int %1$s= 0; %1$s < %2$s.size(); %1$s++) {", indexName, arrayName));
generateFromJson(indent + INDENT, out, containedName, arrayName + ".get(" + indexName + ")", containedType);
out.println(indent + INDENT + String.format("%1$s.add(%2$s);", varName, containedName));
out.println(indent + "}");
}
private String getJsonDecision(Class<?> cls) {
if (cls.isArray() || List.class.isAssignableFrom(cls)) {
return "JsonDecision.LIST";
}
if (Boolean.class.isAssignableFrom(cls)) {
return "JsonDecision.BOOLEAN";
}
if (Number.class.isAssignableFrom(cls)) {
return "JsonDecision.NUMBER";
}
if (Character.class.isAssignableFrom(cls) || String.class.isAssignableFrom(cls) || Enum.class.isAssignableFrom(cls)) {
return "JsonDecision.STRING";
}
return "JsonDecision.OBJECT";
}
private void generateEitherConversion(String indent, PrintWriter out, String varName, String valueAccess, Type paramType) {
String innerName = varName + "e";
String typesName = innerName + "cls";
Collection<Type> allDisjoinTypes = EitherUtil.getAllDisjoinTypes(EitherUtil.getLeftDisjointType(paramType));
Set<Class<?>> leftClasses = allDisjoinTypes.stream().map(t -> getRawClass(t)).collect(Collectors.toSet());
out.println(indent + String.format("JsonDecision[] %1$s= new JsonDecision[] {", typesName));
boolean first = true;
for (Class<?> startClass : leftClasses) {
out.print(indent + INDENT + getJsonDecision(startClass));
if (first) {
first = false;
} else {
out.print(",");
}
out.println();
}
out.println(indent + "};");
out.println(indent + String.format("%1$s %2$s;", paramType.getTypeName(), varName));
out.println(indent + String.format("if (EitherUtil.matches(%1$s, %2$s)) {", valueAccess, typesName));
generateFromJson(indent + INDENT, out, innerName, valueAccess, EitherUtil.getLeftDisjointType(paramType));
out.println(indent + INDENT + String.format("%1$s= Either.forLeft(%2$s);", varName, innerName));
out.println(indent + "} else {");
generateFromJson(indent + INDENT, out, innerName, valueAccess, EitherUtil.getRightDisjointType(paramType));
out.println(indent + INDENT + String.format("%1$s= Either.forRight(%2$s);", varName, innerName));
out.println(indent + "}");
}
@SuppressWarnings("unchecked")
private String fromJsonConversion(Class<?> t, String valueName) {
if (Enum.class.isAssignableFrom(t)) {
return String.format("%1$s.valueOf(%2$s);", t.getName(), json.asString(valueName));
} else if (String.class.isAssignableFrom(t)) {
return String.format("%1$s;", json.asString(valueName));
} else if (Number.class.isAssignableFrom(t)) {
return String.format("(%1$s)%2$s;", primitiveCast((Class<? extends Number>)t), json.asDouble(valueName));
} else if (isSimpleNumberType(t)) {
return String.format("(%1$s)%2$s;", t.getSimpleName(), json.asDouble(valueName));
} else if (t == boolean.class || Boolean.class.isAssignableFrom(t)) {
return json.asBoolean(valueName);
} else if (t == Object.class) {
return valueName;
} else {
return String.format("%1$s.fromJson((%2$s)%3$s);", dtoName(t), json.object(), valueName);
}
}
private Object primitiveCast(Class<? extends Number> t) {
if (t == Integer.class) {
return "int";
} else {
return t.getSimpleName().toLowerCase();
}
}
}