Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
prompto.debug.Serializer Maven / Gradle / Ivy
package prompto.debug;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import prompto.debug.ack.Acknowledged;
import prompto.debug.ack.IAcknowledgement;
import prompto.debug.event.IDebugEvent;
import prompto.debug.request.IDebugRequest;
import prompto.debug.response.IDebugResponse;
import prompto.debug.stack.LeanStackFrame;
import prompto.debug.value.FatValue;
import prompto.debug.variable.FatVariable;
import prompto.debug.variable.LeanVariableList;
import prompto.debug.worker.LeanWorker;
import prompto.parser.Location;
import prompto.parser.Section;
import prompto.utils.Logger;
@SuppressWarnings({"unchecked", "rawtypes"})
public class Serializer {
static Logger logger = new Logger();
static ObjectMapper mapper = initMapper();
private static ObjectMapper initMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false);
mapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
return mapper;
}
static class DebugMessage {
String type;
T value;
public String getType() {
return type;
}
public T getValue() {
return value;
}
}
static class DebugRequestMessage extends DebugMessage {
}
static class DebugResponseMessage extends DebugMessage {
}
static class DebugEventMessage extends DebugMessage {
}
static class AcknowledgementMessage extends DebugMessage {
}
public static void writeMessage(OutputStream output, T value) throws Exception {
Map message = preSerializeObject(value);
mapper.writeValue(output, message);
output.flush();
}
private static Object safePreSerialize(Object value) {
try {
return preSerialize(value);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
private static Object preSerialize(Object value) throws Exception {
if(value==null || value instanceof Boolean || value instanceof String || value instanceof Number || value instanceof JsonNode)
return value;
else if(Enum.class.isAssignableFrom(value.getClass()))
return ((Enum)value).name();
else if(Iterable.class.isAssignableFrom(value.getClass()))
return preSerializeArray((Iterable>)value);
else
return preSerializeObject(value);
}
private static List preSerializeArray(Iterable> iterable) throws Exception {
return StreamSupport.stream(iterable.spliterator(), false)
.map(Serializer::safePreSerialize)
.collect(Collectors.toList());
}
private static Map preSerializeObject(Object object) throws Exception {
Map result = new HashMap<>();
result.put("type", getNameForClass(object.getClass()));
Map value = new HashMap<>();
preSerializeFields(object.getClass(), object, value);
result.put("value", value);
return result;
}
private static void preSerializeFields(Class> klass, Object value, Map map) throws Exception, IllegalAccessException {
if(klass.getSuperclass()!=Object.class)
preSerializeFields(klass.getSuperclass(), value, map);
for(Field field : klass.getDeclaredFields()) {
field.setAccessible(true);
Object fieldValue = field.get(value);
map.put(field.getName(), preSerialize(fieldValue));
}
}
public static String writeMessage(T value) throws Exception {
try(ByteArrayOutputStream output = new ByteArrayOutputStream()) {
writeMessage(output, value);
return output.toString();
}
}
public static T readMessage(String message) throws Exception {
try(InputStream input = new ByteArrayInputStream(message.getBytes())) {
return Serializer.readMessage(input);
}
}
public static T readMessage(InputStream input) throws Exception {
JsonNode content = mapper.readTree(input);
return (T)postDeserialize(content);
}
private static Object postDeserialize(JsonNode node) throws Exception {
if(node.isNull())
return null;
else if(node.isBoolean())
return node.booleanValue();
else if(node.isNumber())
return node.numberValue();
else if(node.isTextual())
return node.textValue();
else if(node.isArray())
return postDeserializeArray((ArrayNode)node);
else if(node.isObject())
return postDeserializeObject(null, (ObjectNode)node);
else
throw new UnsupportedOperationException(node.getNodeType().name());
}
private static Object safePostDeserialize(JsonNode node) {
try {
return postDeserialize(node);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
private static Object postDeserializeArray(ArrayNode node) {
return StreamSupport.stream(node.spliterator(), false)
.map(Serializer::safePostDeserialize)
.collect(Collectors.toList());
}
private static Object postDeserializeObject(Type type, ObjectNode node) throws Exception {
String typeName = node.get("type").asText();
Class> klass = getInstanceType(type, typeName);
Object value = klass.getDeclaredConstructor().newInstance();
JsonNode data = node.get("value");
postDeserializeFields(klass, value, data);
return value;
}
private static Class> getInstanceType(Type type, String typeName) throws Exception {
if(type instanceof Class) {
int modifiers = ((Class>)type).getModifiers();
if(!(Modifier.isInterface(modifiers) || Modifier.isAbstract(modifiers)))
return (Class>)type;
}
return getClassForName(typeName);
}
private static void postDeserializeFields(Class> klass, Object value, JsonNode node) throws Exception {
if(klass.getSuperclass()!=Object.class)
postDeserializeFields(klass.getSuperclass(), value, node);
for(Field field : klass.getDeclaredFields()) {
field.setAccessible(true);
JsonNode fieldNode = node.get(field.getName());
if(fieldNode==null)
logger.debug(()->"No data for field: " + field.getName());
else {
Type type = field.getGenericType();
field.set(value, postDeserialize(type, fieldNode));
}
}
}
private static Object postDeserialize(Type type, JsonNode node) throws Exception {
if(type == JsonNode.class) {
return node;
} else if(node.isTextual()) {
String value = node.textValue();
if(type==String.class)
return value;
else if(type instanceof Class && Enum.class.isAssignableFrom((Class)type)) {
return Enum.valueOf((Class)type, (String)value);
} else
throw new UnsupportedOperationException(type.getTypeName());
} else if(type instanceof Class && List.class.isAssignableFrom((Class)type) && node.isArray())
return postDeserializeList(type, (ArrayNode)node);
else if(type instanceof ParameterizedType && List.class==((ParameterizedType)type).getRawType() && node.isArray())
return postDeserializeList(type, (ArrayNode)node);
else if(node instanceof ObjectNode)
return postDeserializeObject(type, (ObjectNode)node);
else
return postDeserialize(node);
}
private static Object postDeserializeList(Type type, ArrayNode array) throws Exception {
Type itemType = getListItemType(type);
List list = type instanceof Class ? ((Class>)type).getDeclaredConstructor().newInstance() : new ArrayList();
for(JsonNode item : array) {
Object value = postDeserialize(itemType, item);
list.add(value);
}
return list;
}
private static Type getListItemType(Type type) {
ParameterizedType listType = null;
while(listType==null && type!=Object.class) {
if(type instanceof ParameterizedType) {
listType = (ParameterizedType)type;
return listType.getActualTypeArguments()[0];
} else if(type instanceof Class)
type = ((Class)type).getGenericSuperclass();
else
return Object.class;
}
return Object.class;
}
private static String getNameForClass(Class> klass) {
String name = klass.getSimpleName();
if(name.startsWith("Lean"))
name = name.substring("Lean".length());
else if(name.startsWith("Fat"))
name = name.substring("Fat".length());
return name;
}
private static Class> getClassForName(String typeName) throws Exception {
if(typeName.equals(Acknowledged.class.getSimpleName()))
return Acknowledged.class;
else if(typeName.equals("StackFrame"))
return LeanStackFrame.class;
else if(typeName.equals("Variable"))
return FatVariable.class;
else if(typeName.equals("Value"))
return FatValue.class;
else if(typeName.equals("Worker"))
return LeanWorker.class;
else if(typeName.equals("VariableList"))
return LeanVariableList.class;
else if(typeName.equals(Section.class.getSimpleName()))
return Section.class;
else if(typeName.equals(Location.class.getSimpleName()))
return Location.class;
else {
// by convention all implementations sit in the same package as their interface
Class> intf = null;
if(typeName.endsWith("DebugRequest"))
intf = IDebugRequest.class;
else if(typeName.endsWith("DebugResponse"))
intf = IDebugResponse.class;
else if(typeName.endsWith("DebugEvent"))
intf = IDebugEvent.class;
else {
logger.error(()->{return "Cannot resolve typeName: " + typeName;});
return null;
}
return Class.forName(intf.getPackage().getName() + "." + typeName);
}
}
public static IAcknowledgement readAcknowledgement(String message) throws Exception {
return Serializer.readMessage(message);
}
public static IDebugRequest readDebugRequest(String message) throws Exception {
return Serializer.readMessage(message);
}
public static IDebugResponse readDebugResponse(String message) throws Exception {
return Serializer.readMessage(message);
}
public static IDebugEvent readDebugEvent(String message) throws Exception {
return Serializer.readMessage(message);
}
public static IAcknowledgement readAcknowledgement(InputStream input) throws Exception {
return Serializer.readMessage(input);
}
public static IDebugRequest readDebugRequest(InputStream input) throws Exception {
return Serializer.readMessage(input);
}
public static IDebugResponse readDebugResponse(InputStream input) throws Exception {
return Serializer.readMessage(input);
}
public static IDebugEvent readDebugEvent(InputStream input) throws Exception {
return Serializer.readMessage(input);
}
}