• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
3  * Copyright (C) 2011, 2013-2018 The JavaParser Team.
4  *
5  * This file is part of JavaParser.
6  *
7  * JavaParser can be used either under the terms of
8  * a) the GNU Lesser General Public License as published by
9  *     the Free Software Foundation, either version 3 of the License, or
10  *     (at your option) any later version.
11  * b) the terms of the Apache License
12  *
13  * You should have received a copy of both licenses in LICENCE.LGPL and
14  * LICENCE.APACHE. Please refer to those files for details.
15  *
16  * JavaParser is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU Lesser General Public License for more details.
20  */
21 package com.github.javaparser.serialization;
22 
23 import com.github.javaparser.*;
24 import com.github.javaparser.ast.CompilationUnit;
25 import com.github.javaparser.ast.Node;
26 import com.github.javaparser.ast.NodeList;
27 import com.github.javaparser.ast.comments.Comment;
28 import com.github.javaparser.metamodel.BaseNodeMetaModel;
29 import com.github.javaparser.metamodel.PropertyMetaModel;
30 import com.github.javaparser.utils.Log;
31 
32 import javax.json.*;
33 import java.util.*;
34 
35 import static com.github.javaparser.ast.NodeList.toNodeList;
36 import static com.github.javaparser.metamodel.JavaParserMetaModel.getNodeMetaModel;
37 import static com.github.javaparser.serialization.JavaParserJsonSerializer.*;
38 
39 /**
40  * Deserializes the JSON file that was built by {@link JavaParserJsonSerializer}.
41  */
42 public class JavaParserJsonDeserializer {
43     /**
44      * Deserializes json, contained by JsonReader, into AST node.
45      * The root node and all its child nodes will be deserialized.
46      * @param reader json-p reader (object-level reader, <a href="https://javaee.github.io/jsonp/">see their docs</a>)
47      * @return the root level deserialized node
48      */
deserializeObject(JsonReader reader)49     public Node deserializeObject(JsonReader reader) {
50         Log.info("Deserializing JSON to Node.");
51         JsonObject jsonObject = reader.readObject();
52         return deserializeObject(jsonObject);
53     }
54 
55     /**
56      * Recursive depth-first deserializing method that creates a Node instance from JsonObject.
57      *
58      * @param nodeJson json object at current level containg values as properties
59      * @return deserialized node including all children.
60      * @implNote the Node instance will be constructed by the properties defined in the meta model.
61      *           Non meta properties will be set after Node is instantiated.
62      * @implNote comment is included in the propertyKey meta model, but not set when constructing the Node instance.
63      *           That is, comment is not included in the constructor propertyKey list, and therefore needs to be set
64      *           after constructing the node.
65      *           See {@link com.github.javaparser.metamodel.BaseNodeMetaModel#construct(Map)} how the node is contructed
66      */
deserializeObject(JsonObject nodeJson)67     private Node deserializeObject(JsonObject nodeJson) {
68         try {
69             String serializedNodeType = nodeJson.getString(JsonNode.CLASS.propertyKey);
70             BaseNodeMetaModel nodeMetaModel = getNodeMetaModel(Class.forName(serializedNodeType))
71                     .orElseThrow(() -> new IllegalStateException("Trying to deserialize an unknown node type: " + serializedNodeType));
72             Map<String, Object> parameters = new HashMap<>();
73             Map<String, JsonValue> deferredJsonValues = new HashMap<>();
74 
75             for (String name : nodeJson.keySet()) {
76                 if (name.equals(JsonNode.CLASS.propertyKey)) {
77                     continue;
78                 }
79 
80                 Optional<PropertyMetaModel> optionalPropertyMetaModel = nodeMetaModel.getAllPropertyMetaModels().stream()
81                         .filter(mm -> mm.getName().equals(name))
82                         .findFirst();
83                 if (!optionalPropertyMetaModel.isPresent()) {
84                     deferredJsonValues.put(name, nodeJson.get(name));
85                     continue;
86                 }
87 
88                 PropertyMetaModel propertyMetaModel = optionalPropertyMetaModel.get();
89                 if (propertyMetaModel.isNodeList()) {
90                     JsonArray nodeListJson = nodeJson.getJsonArray(name);
91                     parameters.put(name, deserializeNodeList(nodeListJson));
92                 } else if (propertyMetaModel.isNode()) {
93                     parameters.put(name, deserializeObject(nodeJson.getJsonObject(name)));
94                 } else {
95                     Class<?> type = propertyMetaModel.getType();
96                     if (type == String.class) {
97                         parameters.put(name, nodeJson.getString(name));
98                     } else if (type == boolean.class) {
99                         parameters.put(name, Boolean.parseBoolean(nodeJson.getString(name)));
100                     } else if (Enum.class.isAssignableFrom(type)) {
101                         parameters.put(name, Enum.valueOf((Class<? extends Enum>) type, nodeJson.getString(name)));
102                     } else {
103                         throw new IllegalStateException("Don't know how to convert: " + type);
104                     }
105                 }
106             }
107 
108             Node node = nodeMetaModel.construct(parameters);
109             // COMMENT is in the propertyKey meta model, but not required as constructor parameter.
110             // Set it after construction
111             if (parameters.containsKey(JsonNode.COMMENT.propertyKey)) {
112                 node.setComment((Comment)parameters.get(JsonNode.COMMENT.propertyKey));
113             }
114 
115             for (String name : deferredJsonValues.keySet()) {
116                 if (!readNonMetaProperties(name, deferredJsonValues.get(name), node)) {
117                     throw new IllegalStateException("Unknown propertyKey: " + nodeMetaModel.getQualifiedClassName() + "." + name);
118                 }
119             }
120             setSymbolResolverIfCompilationUnit(node);
121 
122             return node;
123         } catch (ClassNotFoundException e) {
124             throw new RuntimeException(e);
125         }
126     }
127 
deserializeNodeList(JsonArray nodeListJson)128     private NodeList<?> deserializeNodeList(JsonArray nodeListJson) {
129         return nodeListJson.stream().map(nodeJson -> deserializeObject((JsonObject) nodeJson)).collect(toNodeList());
130     }
131 
132     /**
133      * Reads properties from json not included in meta model (i.e., RANGE and TOKEN_RANGE).
134      * When read, it sets the deserialized value to the node instance.
135      * @param name propertyKey name for json value
136      * @param jsonValue json value that needs to be deserialized for this propertyKey
137      * @param node instance to which the deserialized value will be set to
138      * @return true if propertyKey is read from json and set to Node instance
139      */
readNonMetaProperties(String name, JsonValue jsonValue, Node node)140     protected boolean readNonMetaProperties(String name, JsonValue jsonValue, Node node) {
141         return readRange(name, jsonValue, node)
142                 || readTokenRange(name, jsonValue, node);
143     }
144 
readRange(String name, JsonValue jsonValue, Node node)145     protected boolean readRange(String name, JsonValue jsonValue, Node node) {
146         if (name.equals(JsonNode.RANGE.propertyKey)) {
147             JsonObject jsonObject = (JsonObject)jsonValue;
148             Position begin = new Position(
149                     jsonObject.getInt(JsonRange.BEGIN_LINE.propertyKey),
150                     jsonObject.getInt(JsonRange.BEGIN_COLUMN.propertyKey)
151             );
152             Position end = new Position(
153                     jsonObject.getInt(JsonRange.END_LINE.propertyKey),
154                     jsonObject.getInt(JsonRange.END_COLUMN.propertyKey)
155             );
156             node.setRange(new Range(begin, end));
157             return true;
158         }
159         return false;
160     }
161 
readTokenRange(String name, JsonValue jsonValue, Node node)162     protected boolean readTokenRange(String name, JsonValue jsonValue, Node node) {
163         if (name.equals(JsonNode.TOKEN_RANGE.propertyKey)) {
164             JsonObject jsonObject = (JsonObject)jsonValue;
165             JavaToken begin = readToken(
166                     JsonTokenRange.BEGIN_TOKEN.propertyKey, jsonObject
167             );
168             JavaToken end = readToken(
169                     JsonTokenRange.END_TOKEN.propertyKey, jsonObject
170             );
171             node.setTokenRange(new TokenRange(begin, end));
172             return true;
173         }
174         return false;
175     }
176 
readToken(String name, JsonObject jsonObject)177     protected JavaToken readToken(String name, JsonObject jsonObject) {
178         JsonObject tokenJson = jsonObject.getJsonObject(name);
179         return new JavaToken(
180                 tokenJson.getInt(JsonToken.KIND.propertyKey),
181                 tokenJson.getString(JsonToken.TEXT.propertyKey)
182         );
183     }
184 
185     /**
186      * This method sets symbol resolver to Node if it is an instance of CompilationUnit
187      * and a SymbolResolver is configured in the static configuration. This is necessary to be able to resolve symbols
188      * within the cu after deserialization. Normally, when parsing java with JavaParser, the symbol resolver is injected
189      * to the cu as a data element with key SYMBOL_RESOLVER_KEY.
190      * @param node instance to which symbol resolver will be set to when instance of a Compilation Unit
191      * @see com.github.javaparser.ast.Node#SYMBOL_RESOLVER_KEY
192      * @see com.github.javaparser.ParserConfiguration#ParserConfiguration()
193      */
setSymbolResolverIfCompilationUnit(Node node)194     private void setSymbolResolverIfCompilationUnit(Node node) {
195         if (node instanceof CompilationUnit && StaticJavaParser.getConfiguration().getSymbolResolver().isPresent()) {
196             CompilationUnit cu = (CompilationUnit)node;
197             cu.setData(Node.SYMBOL_RESOLVER_KEY, StaticJavaParser.getConfiguration().getSymbolResolver().get());
198         }
199     }
200 
201 
202 }
203