Skip to content

Commit

Permalink
should fix remarks in #1895 #1896 #1897.
Browse files Browse the repository at this point in the history
  • Loading branch information
dpnolte committed Oct 24, 2018
1 parent 9092072 commit d8524f1
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 24 deletions.
Expand Up @@ -27,6 +27,7 @@
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.metamodel.BaseNodeMetaModel;
import com.github.javaparser.metamodel.CommentMetaModel;
import com.github.javaparser.metamodel.PropertyMetaModel;
import com.github.javaparser.utils.Log;

Expand All @@ -42,13 +43,33 @@
* Deserializes the JSON file that was built by {@link JavaParserJsonSerializer}.
*/
public class JavaParserJsonDeserializer {

public static final String COMMENT_PROPERTY_KEY =
CommentMetaModel.NAME.substring(0, 1).toLowerCase() + CommentMetaModel.NAME.substring(1);

/**
* Deserializes json, contained by JsonReader, into AST node.
* The root node and all its child nodes will be deserialized.
* @param reader json-p reader (object-level reader, <a href="https://javaee.github.io/jsonp/"></a>see their docs</a>)
* @return the root level deserialized node
*/
public Node deserializeObject(JsonReader reader) {
Log.info("Deserializing JSON to Node.");
JsonObject jsonObject = reader.readObject();
return deserializeObject(jsonObject);
}

/**
* Recursive depth-first deserializing method that creates a Node instance from JsonObject.
*
* @param nodeJson json object at current level containg values as properties
* @return deserialized node including all children.
* @implNote the Node instance will be constructed by the properties defined in the meta model.
* Non meta properties will be set after Node is instantiated.
* @implNote comment is included in the property meta model, but not set when constructing the Node instance.
* That is, comment is not included in the constructor property list, and therefore needs to be set
* after constructing the node.
* See {@link com.github.javaparser.metamodel.BaseNodeMetaModel#construct(Map)} how the node is contructed
*/
private Node deserializeObject(JsonObject nodeJson) {
try {
String serializedNodeType = nodeJson.getString(SERIALIZED_CLASS_KEY);
Expand Down Expand Up @@ -94,10 +115,10 @@ private Node deserializeObject(JsonObject nodeJson) {
}

Node node = nodeMetaModel.construct(parameters);
// 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"));
// comment is in the property meta model, but not required as constructor parameter.
// Set it after construction
if (parameters.containsKey(COMMENT_PROPERTY_KEY)) {
node.setComment((Comment)parameters.get(COMMENT_PROPERTY_KEY));
}

for (String name : deferredJsonValues.keySet()) {
Expand All @@ -121,27 +142,45 @@ private NodeList<?> deserializeNodeList(JsonArray nodeListJson) {
return nodeListJson.stream().map(nodeJson -> deserializeObject((JsonObject) nodeJson)).collect(toNodeList());
}

/**
* Reads properties from json not included in meta model (i.e., Range and TokenRange).
* When read, it sets the deserialized value to the node instance.
* @param name property name for json value
* @param jsonValue json value that needs to be deserialized for this property
* @param node instance to which the deserialized value will be set to
* @return true if property is read from json and set to Node instance
*/
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")) {
if (name.equals(JavaParserJsonSerializer.RANGE_PROPERTY_KEY)) {
JsonObject jsonObject = (JsonObject)jsonValue;
Position begin = new Position(jsonObject.getInt("beginLine"), jsonObject.getInt("beginColumn"));
Position end = new Position(jsonObject.getInt("endLine"), jsonObject.getInt("endColumn"));
Position begin = new Position(
jsonObject.getInt(JavaParserJsonSerializer.RANGE_BEGIN_LINE_PROPERTY_KEY),
jsonObject.getInt(JavaParserJsonSerializer.RANGE_BEGIN_COLUMN_PROPERTY_KEY)
);
Position end = new Position(
jsonObject.getInt(JavaParserJsonSerializer.RANGE_END_LINE_PROPERTY_KEY),
jsonObject.getInt(JavaParserJsonSerializer.RANGE_END_COLUMN_PROPERTY_KEY)
);
node.setRange(new Range(begin, end));
return true;
}
return false;
}

protected boolean readTokenRange(String name, JsonValue jsonValue, Node node) {
if (name.equals("tokenRange")) {
if (name.equals(JavaParserJsonSerializer.TOKEN_RANGE_PROPERTY_KEY)) {
JsonObject jsonObject = (JsonObject)jsonValue;
JavaToken begin = readToken("beginToken", jsonObject);
JavaToken end = readToken("endToken", jsonObject);
JavaToken begin = readToken(
JavaParserJsonSerializer.TOKEN_RANGE_BEGIN_PROPERTY_KEY, jsonObject
);
JavaToken end = readToken(
JavaParserJsonSerializer.TOKEN_RANGE_END_PROPERTY_KEY, jsonObject
);
node.setTokenRange(new TokenRange(begin, end));
return true;
}
Expand All @@ -151,11 +190,20 @@ protected boolean readTokenRange(String name, 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")
tokenJson.getInt(JavaParserJsonSerializer.TOKEN_KIND_PROPERTY_KEY),
tokenJson.getString(JavaParserJsonSerializer.TOKEN_TEXT_PROPERTY_KEY)
);
}

/**
* This method sets symbol resolver to Node if it a instance of CompilationUnit
* and a SymbolResolver is configured in the static configuration. This is necessary to be able to resolve symbols
* within the cu after deserialization. Normally, when parsing java with JavaParser, the symbol resolver is injected
* to the cu as data element with key SYMBOL_RESOLVER_KEY.
* @param node instance to which symbol resolver will be set to when instance of a Compilation Unit
* @see com.github.javaparser.ast.Node#SYMBOL_RESOLVER_KEY
* @see com.github.javaparser.ParserConfiguration#ParserConfiguration()
*/
private void setSymbolResolverIfCompilationUnit(Node node) {
if (node instanceof CompilationUnit && JavaParser.getStaticConfiguration().getSymbolResolver().isPresent()) {
CompilationUnit cu = (CompilationUnit)node;
Expand Down
Expand Up @@ -40,13 +40,36 @@
*/
public class JavaParserJsonSerializer {
public static final String SERIALIZED_CLASS_KEY = "!";
public static final String RANGE_PROPERTY_KEY = "range";
public static final String RANGE_BEGIN_LINE_PROPERTY_KEY = "beginLine";
public static final String RANGE_BEGIN_COLUMN_PROPERTY_KEY = "beginColumn";
public static final String RANGE_END_LINE_PROPERTY_KEY = "endLine";
public static final String RANGE_END_COLUMN_PROPERTY_KEY = "endColumn";
public static final String TOKEN_RANGE_PROPERTY_KEY = "tokenRange";
public static final String TOKEN_RANGE_BEGIN_PROPERTY_KEY = "beginToken";
public static final String TOKEN_RANGE_END_PROPERTY_KEY = "endToken";
public static final String TOKEN_TEXT_PROPERTY_KEY = "text";
public static final String TOKEN_KIND_PROPERTY_KEY = "kind";

/**
* Serializes node and all its children into json. Any node siblings will be ignored.
* @param node the node that will be the root level json object
* @param generator the json-p generator for writing the json
* @see <a href="https://javaee.github.io/jsonp/">json-p</a>
*/
public void serialize(Node node, JsonGenerator generator) {
requireNonNull(node);
Log.info("Serializing Node to JSON.");
serialize(null, node, generator);
}

/**
* Recursive depth-first method that serializes nodes into json
* @param nodeName nullable String. If null, it is the root object, otherwise it is the property key for the object
* @param node the current node to be serialized
* @param generator the json-p generator for writing the json
*/

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 Down Expand Up @@ -86,6 +109,13 @@ private void serialize(String nodeName, Node node, JsonGenerator generator) {
generator.writeEnd();
}

/***
* This method writes json for properties not included in meta model (i.e., Range and TokenRange).
* This method could be overriden so that - for example - tokens are not written to json to save space
*
* @see com.github.javaparser.metamodel.BaseNodeMetaModel#getAllPropertyMetaModels()
*/

protected void writeNonMetaProperties(Node node, JsonGenerator generator) {
this.writeRange(node, generator);
this.writeTokens(node, generator);
Expand All @@ -94,29 +124,29 @@ protected void writeNonMetaProperties(Node node, JsonGenerator 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.writeStartObject(RANGE_PROPERTY_KEY);
generator.write(RANGE_BEGIN_LINE_PROPERTY_KEY, range.begin.line);
generator.write(RANGE_BEGIN_COLUMN_PROPERTY_KEY, range.begin.column);
generator.write(RANGE_END_LINE_PROPERTY_KEY, range.end.line);
generator.write(RANGE_END_COLUMN_PROPERTY_KEY, 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.writeStartObject(TOKEN_RANGE_PROPERTY_KEY);
writeToken(TOKEN_RANGE_BEGIN_PROPERTY_KEY, tokenRange.getBegin(), generator);
writeToken(TOKEN_RANGE_END_PROPERTY_KEY, 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.write(TOKEN_KIND_PROPERTY_KEY, token.getKind());
generator.write(TOKEN_TEXT_PROPERTY_KEY, token.getText());
generator.writeEnd();
}

Expand Down
Expand Up @@ -31,6 +31,8 @@
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.javadoc.Javadoc;
import com.github.javaparser.javadoc.JavadocBlockTag;
import com.github.javaparser.resolution.SymbolResolver;
import com.github.javaparser.resolution.types.ResolvedType;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -138,6 +140,33 @@ void testComment() {
assertEquals(methodDeclaration.getComment().get().getContent(), " line comment");
}

@Test
void testJavaDocComment() {
CompilationUnit cu = JavaParser.parse("public class X{ " +
" /**\n" +
" * Woke text.\n" +
" * @param a blub\n" +
" * @return true \n" +
" */" +
" public boolean test(int a) { return true; }\n" +
"}");
String serialized = serialize(cu, false);

CompilationUnit deserialized = (CompilationUnit)deserializer.deserializeObject(Json.createReader(new StringReader(serialized)));
ClassOrInterfaceDeclaration classDeclaration = deserialized.getClassByName("X").get();
MethodDeclaration methodDeclaration = classDeclaration.getMethods().get(0);
assertEquals(methodDeclaration.getJavadoc().isPresent(), true);
Javadoc javadoc = methodDeclaration.getJavadoc().get();

JavadocBlockTag paramBlockTag = javadoc.getBlockTags().get(0);
assertEquals(paramBlockTag.getTagName(), "param");
assertEquals(paramBlockTag.getContent().toText(), "blub");

JavadocBlockTag returnBlockTag = javadoc.getBlockTags().get(1);
assertEquals(returnBlockTag.getTagName(), "return");
assertEquals(returnBlockTag.getContent().toText(), "true");
}

@Test
void testNonMetaProperties() {
CompilationUnit cu = JavaParser.parse("public class X{} class Z{}");
Expand Down
Expand Up @@ -4,9 +4,10 @@
import com.github.javaparser.ast.Node;

public class CommentMetaModel extends NodeMetaModel {
public static final String NAME = "Comment";

CommentMetaModel(Optional<BaseNodeMetaModel> superBaseNodeMetaModel) {
super(superBaseNodeMetaModel, com.github.javaparser.ast.comments.Comment.class, "Comment", "com.github.javaparser.ast.comments", true, false);
super(superBaseNodeMetaModel, com.github.javaparser.ast.comments.Comment.class, NAME, "com.github.javaparser.ast.comments", true, false);
}

protected CommentMetaModel(Optional<BaseNodeMetaModel> superNodeMetaModel, Class<? extends Node> type, String name, String packageName, boolean isAbstract, boolean hasWildcard) {
Expand Down

0 comments on commit d8524f1

Please sign in to comment.