Skip to content

Commit

Permalink
from delegates to main functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
dpnolte committed Oct 19, 2018
1 parent ef3ecbb commit 19a2aad
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 72 deletions.
Expand Up @@ -20,13 +20,15 @@
*/
package com.github.javaparser.serialization;

import com.github.javaparser.*;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.metamodel.BaseNodeMetaModel;
import com.github.javaparser.metamodel.PropertyMetaModel;
import com.github.javaparser.utils.Log;
import com.github.javaparser.utils.Pair;

import javax.json.*;
import java.util.*;
Expand All @@ -42,44 +44,41 @@
public class JavaParserJsonDeserializer {

public Node deserializeObject(JsonReader reader) {
return deserializeObject(reader, new HashMap<>());
}

public Node deserializeObject(JsonReader reader, Map<String, Delegate> delegates) {
Log.info("Deserializing JSON to Node.");
JsonObject jsonObject = reader.readObject();
return deserializeObject(jsonObject, delegates);
return deserializeObject(jsonObject);
}

private Node deserializeObject(JsonObject nodeJson, Map<String, Delegate> delegates) {
private Node deserializeObject(JsonObject nodeJson) {
try {
String serializedNodeType = nodeJson.getString(SERIALIZED_CLASS_KEY);
BaseNodeMetaModel nodeMetaModel = getNodeMetaModel(Class.forName(serializedNodeType))
.orElseThrow(() -> new IllegalStateException("Trying to deserialize an unknown node type: " + serializedNodeType));
Map<String, Object> parameters = new HashMap<>();
List<Pair<String, JsonValue>> jsonValuesForDelegates = new LinkedList<>();
Map<String, JsonValue> deferredJsonValues = new HashMap<>();

for (String name : nodeJson.keySet()) {
if (name.equals(SERIALIZED_CLASS_KEY)) {
continue;
} else if (delegates.containsKey(name)) {
jsonValuesForDelegates.add(
new Pair<>(name, nodeJson.get(name))
);
continue;
}

PropertyMetaModel propertyMetaModel = nodeMetaModel.getAllPropertyMetaModels().stream()
Optional<PropertyMetaModel> optionalPropertyMetaModel = nodeMetaModel.getAllPropertyMetaModels().stream()
.filter(mm -> mm.getName().equals(name))
.findFirst().orElseThrow(() -> new IllegalStateException("Unknown property: " + nodeMetaModel.getQualifiedClassName() + "." + name));
.findFirst();
if (!optionalPropertyMetaModel.isPresent()) {
deferredJsonValues.put(name, nodeJson.get(name));
continue;
}

PropertyMetaModel propertyMetaModel = optionalPropertyMetaModel.get();
if (propertyMetaModel.isNodeList()) {
JsonArray nodeListJson = nodeJson.getJsonArray(name);
parameters.put(name, deserializeNodeList(nodeListJson, delegates));
parameters.put(name, deserializeNodeList(nodeListJson));
} else if (propertyMetaModel.isEnumSet()) {
JsonArray enumSetJson = nodeJson.getJsonArray(name);
parameters.put(name, deserializeEnumSet(enumSetJson));
} else if (propertyMetaModel.isNode()) {
parameters.put(name, deserializeObject(nodeJson.getJsonObject(name), delegates));
parameters.put(name, deserializeObject(nodeJson.getJsonObject(name)));
} else {
Class<?> type = propertyMetaModel.getType();
if (type == String.class) {
Expand All @@ -95,10 +94,19 @@ private Node deserializeObject(JsonObject nodeJson, Map<String, Delegate> delega
}

Node node = nodeMetaModel.construct(parameters);
for (Pair<String, JsonValue> nameAndValue : jsonValuesForDelegates) {
delegates.get(nameAndValue.a).fromJson(nameAndValue.a, nameAndValue.b, node);
// Note: comment is a property meta model, but it is not listed as constructor parameter and not attached to node
// @see BaseNodeMetaModel.getConstructorParameters
if (parameters.containsKey("comment")) {
node.setComment((Comment)parameters.get("comment"));
}

for (String name : deferredJsonValues.keySet()) {
if (!readNonMetaProperties(name, deferredJsonValues.get(name), node)) {
throw new IllegalStateException("Unknown property: " + nodeMetaModel.getQualifiedClassName() + "." + name);
}
}
ensureSymbolResolverIsAttached(node);

return node;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
Expand All @@ -109,11 +117,49 @@ private EnumSet<?> deserializeEnumSet(JsonArray enumSetJson) {
return enumSetJson.stream().map(v -> (JsonString) v).map(s -> Modifier.valueOf(s.getString())).collect(Collectors.toCollection(() -> EnumSet.noneOf(Modifier.class)));
}

private NodeList<?> deserializeNodeList(JsonArray nodeListJson, Map<String, Delegate> delegates) {
return nodeListJson.stream().map(nodeJson -> deserializeObject((JsonObject) nodeJson, delegates)).collect(toNodeList());
private NodeList<?> deserializeNodeList(JsonArray nodeListJson) {
return nodeListJson.stream().map(nodeJson -> deserializeObject((JsonObject) nodeJson)).collect(toNodeList());
}

protected boolean readNonMetaProperties(String name, JsonValue jsonValue, Node node) {
return readRange(name, jsonValue, node)
|| readTokenRange(name, jsonValue, node);
}

protected boolean readRange(String name, JsonValue jsonValue, Node node) {
if (name.equals("range")) {
JsonObject jsonObject = (JsonObject)jsonValue;
Position begin = new Position(jsonObject.getInt("beginLine"), jsonObject.getInt("beginColumn"));
Position end = new Position(jsonObject.getInt("endLine"), jsonObject.getInt("endColumn"));
node.setRange(new Range(begin, end));
return true;
}
return false;
}

protected boolean readTokenRange(String name, JsonValue jsonValue, Node node) {
if (name.equals("tokenRange")) {
JsonObject jsonObject = (JsonObject)jsonValue;
JavaToken begin = readToken("beginToken", jsonObject);
JavaToken end = readToken("endToken", jsonObject);
node.setTokenRange(new TokenRange(begin, end));
return true;
}
return false;
}

interface Delegate {
void fromJson(String propertyName, JsonValue jsonValue, Node node);
protected JavaToken readToken(String name, JsonObject jsonObject) {
JsonObject tokenJson = jsonObject.getJsonObject(name);
return new JavaToken(
tokenJson.getInt("kind"),
tokenJson.getString("text")
);
}

private void ensureSymbolResolverIsAttached(Node node) {
if (node instanceof CompilationUnit && JavaParser.getStaticConfiguration().getSymbolResolver().isPresent()) {
CompilationUnit cu = (CompilationUnit)node;
cu.setData(Node.SYMBOL_RESOLVER_KEY, JavaParser.getStaticConfiguration().getSymbolResolver().get());
}
}
}
Expand Up @@ -20,6 +20,9 @@
*/
package com.github.javaparser.serialization;

import com.github.javaparser.JavaToken;
import com.github.javaparser.Range;
import com.github.javaparser.TokenRange;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.metamodel.BaseNodeMetaModel;
Expand All @@ -29,8 +32,6 @@

import javax.json.stream.JsonGenerator;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;

import static java.util.Objects.requireNonNull;

Expand All @@ -41,16 +42,12 @@ public class JavaParserJsonSerializer {
public static final String SERIALIZED_CLASS_KEY = "!";

public void serialize(Node node, JsonGenerator generator) {
serialize(node, generator, new LinkedList<>());
}

public void serialize(Node node, JsonGenerator generator, List<Delegate> delegates) {
requireNonNull(node);
Log.info("Serializing Node to JSON.");
serialize(null, node, generator, delegates);
serialize(null, node, generator);
}

private void serialize(String nodeName, Node node, JsonGenerator generator, List<Delegate> delegates) {
private void serialize(String nodeName, Node node, JsonGenerator generator) {
requireNonNull(node);
BaseNodeMetaModel nodeMetaModel = JavaParserMetaModel.getNodeMetaModel(node.getClass()).orElseThrow(() -> new IllegalStateException("Unknown Node: " + node.getClass()));

Expand All @@ -60,9 +57,7 @@ private void serialize(String nodeName, Node node, JsonGenerator generator, List
generator.writeStartObject(nodeName);
}
generator.write(SERIALIZED_CLASS_KEY, node.getClass().getName());
for (Delegate delegate : delegates) {
delegate.toJson(node, generator);
}
this.writeNonMetaProperties(node, generator);
for (PropertyMetaModel propertyMetaModel : nodeMetaModel.getAllPropertyMetaModels()) {
String name = propertyMetaModel.getName();
Object value = propertyMetaModel.getValue(node);
Expand All @@ -71,7 +66,7 @@ private void serialize(String nodeName, Node node, JsonGenerator generator, List
NodeList<Node> list = (NodeList<Node>) value;
generator.writeStartArray(name);
for (Node n : list) {
serialize(null, n, generator, delegates);
serialize(null, n, generator);
}
generator.writeEnd();
} else if (propertyMetaModel.isEnumSet()) {
Expand All @@ -82,7 +77,7 @@ private void serialize(String nodeName, Node node, JsonGenerator generator, List
}
generator.writeEnd();
} else if (propertyMetaModel.isNode()) {
serialize(name, (Node) value, generator, delegates);
serialize(name, (Node) value, generator);
} else {
generator.write(name, value.toString());
}
Expand All @@ -91,7 +86,38 @@ private void serialize(String nodeName, Node node, JsonGenerator generator, List
generator.writeEnd();
}

interface Delegate {
void toJson(Node node, JsonGenerator generator);
protected void writeNonMetaProperties(Node node, JsonGenerator generator) {
this.writeRange(node, generator);
this.writeTokens(node, generator);
}

protected void writeRange(Node node, JsonGenerator generator) {
if (node.getRange().isPresent()) {
Range range = node.getRange().get();
generator.writeStartObject("range");
generator.write("beginLine", range.begin.line);
generator.write("beginColumn", range.begin.column);
generator.write("endLine", range.end.line);
generator.write("endColumn", range.end.column);
generator.writeEnd();
}
}

protected void writeTokens(Node node, JsonGenerator generator) {
if (node.getTokenRange().isPresent()) {
TokenRange tokenRange = node.getTokenRange().get();
generator.writeStartObject("tokenRange");
writeToken("beginToken", tokenRange.getBegin(), generator);
writeToken("endToken", tokenRange.getEnd(), generator);
generator.writeEnd();
}
}

protected void writeToken(String name, JavaToken token, JsonGenerator generator) {
generator.writeStartObject(name);
generator.write("kind", token.getKind());
generator.write("text", token.getText());
generator.writeEnd();
}

}
Expand Up @@ -23,10 +23,16 @@
import com.github.javaparser.JavaParser;
import com.github.javaparser.Position;
import com.github.javaparser.Range;
import com.github.javaparser.TokenRange;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.resolution.SymbolResolver;
import com.github.javaparser.resolution.types.ResolvedType;
import org.junit.jupiter.api.Test;

import javax.json.Json;
Expand Down Expand Up @@ -114,41 +120,68 @@ void testPrimitiveType() {
}

@Test
void testDelegate() {
void testComment() {
CompilationUnit cu = JavaParser.parse("/* block comment */\npublic class X{ \n // line comment\npublic void test() {}\n}");
String serialized = serialize(cu, false);

CompilationUnit deserialized = (CompilationUnit)deserializer.deserializeObject(Json.createReader(new StringReader(serialized)));
ClassOrInterfaceDeclaration classXDeclaration = deserialized.getClassByName("X").get();
assertEquals(classXDeclaration.getComment().isPresent(), true);

Comment comment = classXDeclaration.getComment().get();
assertEquals(comment.getClass().getName(), "com.github.javaparser.ast.comments.BlockComment");
assertEquals(comment.getContent(), " block comment ");

MethodDeclaration methodDeclaration = classXDeclaration.getMethods().get(0);
assertEquals(methodDeclaration.getComment().isPresent(), true);
assertEquals(methodDeclaration.getComment().get().getClass().getName(), "com.github.javaparser.ast.comments.LineComment");
assertEquals(methodDeclaration.getComment().get().getContent(), " line comment");
}

@Test
void testNonMetaProperties() {
CompilationUnit cu = JavaParser.parse("public class X{} class Z{}");
List<JavaParserJsonSerializer.Delegate> serializerDelegates = new LinkedList<>();
JavaParserJsonSerializer.Delegate rangeSerializerDelegate = (node, generator) -> {
if (node.getRange().isPresent()) {
Range range = node.getRange().get();
generator.writeStartObject("range");
generator.write("beginLine", range.begin.line);
generator.write("beginColumn", range.begin.column);
generator.write("endLine", range.end.line);
generator.write("endColumn", range.end.column);
generator.writeEnd();
}
};
serializerDelegates.add(rangeSerializerDelegate);
String serialized = serialize(cu, false, serializerDelegates);

Map<String, JavaParserJsonDeserializer.Delegate> deserializerDelegates = new HashMap<>();
JavaParserJsonDeserializer.Delegate rangeDeserializerDelegate = (propertyName, jsonValue, node) -> {
JsonObject jsonNode = (JsonObject)jsonValue;
Position begin = new Position(jsonNode.getInt("beginLine"), jsonNode.getInt("beginColumn"));
Position end = new Position(jsonNode.getInt("endLine"), jsonNode.getInt("endColumn"));
node.setRange(new Range(begin, end));
};
deserializerDelegates.put("range", rangeDeserializerDelegate);
String serialized = serialize(cu, false);

CompilationUnit deserialized = (CompilationUnit)deserializer.deserializeObject(Json.createReader(new StringReader(serialized)));

Node deserialized = deserializer.deserializeObject(
Json.createReader(new StringReader(serialized)),
deserializerDelegates
);
assertEquals(deserialized.getRange().isPresent(), true);
Range range = deserialized.getRange().get();
assertEquals(range.begin.line, 1);
assertEquals(range.begin.line, 1);
assertEquals(range.end.line, 1);
assertEquals(range.end.column, 26);

assertEquals(deserialized.getTokenRange().isPresent(), true);
TokenRange tokenRange = deserialized.getTokenRange().get();
assertEquals(tokenRange.getBegin().getText(), "public");
assertEquals(tokenRange.getEnd().getText(), "");
}

@Test
void testAttachingSymbolResolver() {
SymbolResolver stubResolver = new SymbolResolver() {
@Override
public <T> T resolveDeclaration(Node node, Class<T> resultClass) {
return null;
}

@Override
public <T> T toResolvedType(Type javaparserType, Class<T> resultClass) {
return null;
}

@Override
public ResolvedType calculateType(Expression expression) {
return null;
}
};
JavaParser.getStaticConfiguration().setSymbolResolver(stubResolver);
CompilationUnit cu = JavaParser.parse("public class X{} class Z{}");
String serialized = serialize(cu, false);

CompilationUnit deserialized = (CompilationUnit)deserializer.deserializeObject(Json.createReader(new StringReader(serialized)));
assertEquals(deserialized.containsData(Node.SYMBOL_RESOLVER_KEY), true);
assertEquals(deserialized.getData(Node.SYMBOL_RESOLVER_KEY), stubResolver);
}

/**
Expand Down

0 comments on commit 19a2aad

Please sign in to comment.