From 19a2aadf7c40f43dadceb126099e6f03c9edf068 Mon Sep 17 00:00:00 2001 From: Derk Date: Fri, 19 Oct 2018 13:09:54 +0200 Subject: [PATCH] from delegates to main functionality --- .../JavaParserJsonDeserializer.java | 92 ++++++++++++++----- .../JavaParserJsonSerializer.java | 56 ++++++++--- .../JavaParserJsonDeserializerTest.java | 91 ++++++++++++------ .../JavaParserJsonSerializerTest.java | 7 +- 4 files changed, 174 insertions(+), 72 deletions(-) diff --git a/javaparser-core-serialization/src/main/java/com/github/javaparser/serialization/JavaParserJsonDeserializer.java b/javaparser-core-serialization/src/main/java/com/github/javaparser/serialization/JavaParserJsonDeserializer.java index 0f71dac1c1..732b4f8f2d 100644 --- a/javaparser-core-serialization/src/main/java/com/github/javaparser/serialization/JavaParserJsonDeserializer.java +++ b/javaparser-core-serialization/src/main/java/com/github/javaparser/serialization/JavaParserJsonDeserializer.java @@ -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.*; @@ -42,44 +44,41 @@ public class JavaParserJsonDeserializer { public Node deserializeObject(JsonReader reader) { - return deserializeObject(reader, new HashMap<>()); - } - - public Node deserializeObject(JsonReader reader, Map delegates) { Log.info("Deserializing JSON to Node."); JsonObject jsonObject = reader.readObject(); - return deserializeObject(jsonObject, delegates); + return deserializeObject(jsonObject); } - private Node deserializeObject(JsonObject nodeJson, Map 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 parameters = new HashMap<>(); - List> jsonValuesForDelegates = new LinkedList<>(); + Map 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 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) { @@ -95,10 +94,19 @@ private Node deserializeObject(JsonObject nodeJson, Map delega } Node node = nodeMetaModel.construct(parameters); - for (Pair 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); @@ -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 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()); + } } } diff --git a/javaparser-core-serialization/src/main/java/com/github/javaparser/serialization/JavaParserJsonSerializer.java b/javaparser-core-serialization/src/main/java/com/github/javaparser/serialization/JavaParserJsonSerializer.java index ad079a1ba9..8dcb9f4021 100644 --- a/javaparser-core-serialization/src/main/java/com/github/javaparser/serialization/JavaParserJsonSerializer.java +++ b/javaparser-core-serialization/src/main/java/com/github/javaparser/serialization/JavaParserJsonSerializer.java @@ -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; @@ -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; @@ -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 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 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())); @@ -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); @@ -71,7 +66,7 @@ private void serialize(String nodeName, Node node, JsonGenerator generator, List NodeList list = (NodeList) value; generator.writeStartArray(name); for (Node n : list) { - serialize(null, n, generator, delegates); + serialize(null, n, generator); } generator.writeEnd(); } else if (propertyMetaModel.isEnumSet()) { @@ -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()); } @@ -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(); + } + } diff --git a/javaparser-core-serialization/src/test/java/com/github/javaparser/serialization/JavaParserJsonDeserializerTest.java b/javaparser-core-serialization/src/test/java/com/github/javaparser/serialization/JavaParserJsonDeserializerTest.java index 2c378c8df4..58efe84ad4 100644 --- a/javaparser-core-serialization/src/test/java/com/github/javaparser/serialization/JavaParserJsonDeserializerTest.java +++ b/javaparser-core-serialization/src/test/java/com/github/javaparser/serialization/JavaParserJsonDeserializerTest.java @@ -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; @@ -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 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 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 resolveDeclaration(Node node, Class resultClass) { + return null; + } + + @Override + public T toResolvedType(Type javaparserType, Class 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); } /** diff --git a/javaparser-core-serialization/src/test/java/com/github/javaparser/serialization/JavaParserJsonSerializerTest.java b/javaparser-core-serialization/src/test/java/com/github/javaparser/serialization/JavaParserJsonSerializerTest.java index 1fb58cc33d..5f65b5b02a 100644 --- a/javaparser-core-serialization/src/test/java/com/github/javaparser/serialization/JavaParserJsonSerializerTest.java +++ b/javaparser-core-serialization/src/test/java/com/github/javaparser/serialization/JavaParserJsonSerializerTest.java @@ -43,13 +43,10 @@ void test() { String serialized = serialize(cu, false); - assertEquals("{\"!\":\"com.github.javaparser.ast.CompilationUnit\",\"imports\":[],\"types\":[{\"!\":\"com.github.javaparser.ast.body.ClassOrInterfaceDeclaration\",\"extendedTypes\":[],\"implementedTypes\":[],\"isInterface\":\"false\",\"typeParameters\":[],\"members\":[{\"!\":\"com.github.javaparser.ast.body.FieldDeclaration\",\"modifiers\":[],\"variables\":[{\"!\":\"com.github.javaparser.ast.body.VariableDeclarator\",\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"identifier\":\"y\"},\"type\":{\"!\":\"com.github.javaparser.ast.type.ClassOrInterfaceType\",\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"identifier\":\"Y\"},\"scope\":{\"!\":\"com.github.javaparser.ast.type.ClassOrInterfaceType\",\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"identifier\":\"util\"},\"scope\":{\"!\":\"com.github.javaparser.ast.type.ClassOrInterfaceType\",\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"identifier\":\"java\"},\"annotations\":[]},\"annotations\":[]},\"annotations\":[]}}],\"annotations\":[]}],\"modifiers\":[],\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"identifier\":\"X\"},\"annotations\":[]}]}", serialized); + assertEquals("{\"!\":\"com.github.javaparser.ast.CompilationUnit\",\"range\":{\"beginLine\":1,\"beginColumn\":1,\"endLine\":1,\"endColumn\":23},\"tokenRange\":{\"beginToken\":{\"kind\":19,\"text\":\"class\"},\"endToken\":{\"kind\":0,\"text\":\"\"}},\"imports\":[],\"types\":[{\"!\":\"com.github.javaparser.ast.body.ClassOrInterfaceDeclaration\",\"range\":{\"beginLine\":1,\"beginColumn\":1,\"endLine\":1,\"endColumn\":23},\"tokenRange\":{\"beginToken\":{\"kind\":19,\"text\":\"class\"},\"endToken\":{\"kind\":95,\"text\":\"}\"}},\"extendedTypes\":[],\"implementedTypes\":[],\"isInterface\":\"false\",\"typeParameters\":[],\"members\":[{\"!\":\"com.github.javaparser.ast.body.FieldDeclaration\",\"range\":{\"beginLine\":1,\"beginColumn\":9,\"endLine\":1,\"endColumn\":22},\"tokenRange\":{\"beginToken\":{\"kind\":89,\"text\":\"java\"},\"endToken\":{\"kind\":98,\"text\":\";\"}},\"modifiers\":[],\"variables\":[{\"!\":\"com.github.javaparser.ast.body.VariableDeclarator\",\"range\":{\"beginLine\":1,\"beginColumn\":21,\"endLine\":1,\"endColumn\":21},\"tokenRange\":{\"beginToken\":{\"kind\":89,\"text\":\"y\"},\"endToken\":{\"kind\":89,\"text\":\"y\"}},\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"range\":{\"beginLine\":1,\"beginColumn\":21,\"endLine\":1,\"endColumn\":21},\"tokenRange\":{\"beginToken\":{\"kind\":89,\"text\":\"y\"},\"endToken\":{\"kind\":89,\"text\":\"y\"}},\"identifier\":\"y\"},\"type\":{\"!\":\"com.github.javaparser.ast.type.ClassOrInterfaceType\",\"range\":{\"beginLine\":1,\"beginColumn\":9,\"endLine\":1,\"endColumn\":19},\"tokenRange\":{\"beginToken\":{\"kind\":89,\"text\":\"java\"},\"endToken\":{\"kind\":89,\"text\":\"Y\"}},\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"range\":{\"beginLine\":1,\"beginColumn\":19,\"endLine\":1,\"endColumn\":19},\"tokenRange\":{\"beginToken\":{\"kind\":89,\"text\":\"Y\"},\"endToken\":{\"kind\":89,\"text\":\"Y\"}},\"identifier\":\"Y\"},\"scope\":{\"!\":\"com.github.javaparser.ast.type.ClassOrInterfaceType\",\"range\":{\"beginLine\":1,\"beginColumn\":9,\"endLine\":1,\"endColumn\":17},\"tokenRange\":{\"beginToken\":{\"kind\":89,\"text\":\"java\"},\"endToken\":{\"kind\":89,\"text\":\"util\"}},\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"range\":{\"beginLine\":1,\"beginColumn\":14,\"endLine\":1,\"endColumn\":17},\"tokenRange\":{\"beginToken\":{\"kind\":89,\"text\":\"util\"},\"endToken\":{\"kind\":89,\"text\":\"util\"}},\"identifier\":\"util\"},\"scope\":{\"!\":\"com.github.javaparser.ast.type.ClassOrInterfaceType\",\"range\":{\"beginLine\":1,\"beginColumn\":9,\"endLine\":1,\"endColumn\":12},\"tokenRange\":{\"beginToken\":{\"kind\":89,\"text\":\"java\"},\"endToken\":{\"kind\":89,\"text\":\"java\"}},\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"range\":{\"beginLine\":1,\"beginColumn\":9,\"endLine\":1,\"endColumn\":12},\"tokenRange\":{\"beginToken\":{\"kind\":89,\"text\":\"java\"},\"endToken\":{\"kind\":89,\"text\":\"java\"}},\"identifier\":\"java\"},\"annotations\":[]},\"annotations\":[]},\"annotations\":[]}}],\"annotations\":[]}],\"modifiers\":[],\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"range\":{\"beginLine\":1,\"beginColumn\":7,\"endLine\":1,\"endColumn\":7},\"tokenRange\":{\"beginToken\":{\"kind\":89,\"text\":\"X\"},\"endToken\":{\"kind\":89,\"text\":\"X\"}},\"identifier\":\"X\"},\"annotations\":[]}]}", serialized); } static String serialize(Node node, boolean prettyPrint) { - return serialize(node, prettyPrint, new LinkedList<>()); - } - static String serialize(Node node, boolean prettyPrint, List delegates) { Map config = new HashMap<>(); if (prettyPrint) { config.put(JsonGenerator.PRETTY_PRINTING, null); @@ -58,7 +55,7 @@ static String serialize(Node node, boolean prettyPrint, List