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