Skip to content

Commit 0c12c05

Browse files
authored
Merge pull request #45 from LIonWeb-org/issue42
Implementing lower level unserialization
2 parents e631f21 + 7f7520f commit 0c12c05

File tree

5 files changed

+288
-15
lines changed

5 files changed

+288
-15
lines changed

core/build.gradle

+3-2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ publishing {
6262
}
6363
}
6464

65-
tasks.withType(JavaCompile) {
66-
options.release = 8
65+
compileJava {
66+
sourceCompatibility = '1.8'
67+
targetCompatibility = '1.8'
6768
}

core/src/main/java/org/lionweb/lioncore/java/serialization/JsonSerialization.java

+123-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.lionweb.lioncore.java.model.impl.M3Node;
1212
import org.lionweb.lioncore.java.self.LionCore;
1313

14+
import javax.annotation.Nullable;
1415
import java.util.*;
1516
import java.util.stream.Collectors;
1617

@@ -58,6 +59,7 @@ public static JsonSerialization getBasicSerialization() {
5859

5960
private Map<String, JsonObject> nodeIdToData = new HashMap<>();
6061
private Map<String, Node> nodeIdToNode = new HashMap<>();
62+
private Map<String, NodeData> nodeIdToNodeData = new HashMap<>();
6163

6264
private JsonSerialization() {
6365
// prevent public access
@@ -194,6 +196,19 @@ private <T extends Node> T populateProperties(T instance, JsonObject jsonObject)
194196
return instance;
195197
}
196198

199+
private NodeData populateProperties(NodeData instance, JsonObject jsonObject) {
200+
if (!jsonObject.has("properties") && jsonObject.get("properties").isJsonObject()) {
201+
return instance;
202+
}
203+
JsonObject properties = jsonObject.getAsJsonObject("properties");
204+
for (String propertyId : properties.keySet()) {
205+
String serializedValue = properties.get(propertyId).getAsString();
206+
instance.setPropertyValue(propertyId, serializedValue);
207+
}
208+
209+
return instance;
210+
}
211+
197212
private void populateLinks(Node node, JsonObject data) {
198213
if (data.has("children")) {
199214
JsonObject children = data.get("children").getAsJsonObject();
@@ -248,6 +263,40 @@ private void populateLinks(Node node, JsonObject data) {
248263
}
249264
}
250265

266+
private void populateLinks(NodeData node, JsonObject data) {
267+
if (data.has("children")) {
268+
JsonObject children = data.get("children").getAsJsonObject();
269+
for (String containmentID : children.keySet()) {
270+
JsonArray value = children.get(containmentID).getAsJsonArray();
271+
for (JsonElement childEl : value.asList()) {
272+
String childId = childEl.getAsString();
273+
node.addChild(containmentID, childId);
274+
}
275+
}
276+
}
277+
if (data.has("references")) {
278+
JsonObject references = data.get("references").getAsJsonObject();
279+
for (String referenceID : references.keySet()) {
280+
JsonArray value = references.get(referenceID).getAsJsonArray();
281+
for (JsonElement referredEl : value.asList()) {
282+
try {
283+
JsonObject referenceObj = referredEl.getAsJsonObject();
284+
String referredId = getAsStringOrNull(referenceObj.get("reference"));
285+
String resolveInfo = getAsStringOrNull(referenceObj.get("resolveInfo"));
286+
node.addReferenceValue(referenceID, new NodeData.RawReferenceValue(referredId, resolveInfo));
287+
} catch (Exception e) {
288+
throw new RuntimeException("Issue deserializing reference " + referenceID, e);
289+
}
290+
}
291+
}
292+
}
293+
if (data.has("parent")) {
294+
JsonElement parentValue = data.get("parent");
295+
String parentNodeID = parentValue instanceof JsonNull ? null : parentValue.getAsString();
296+
node.setParentNodeID(parentNodeID);
297+
}
298+
}
299+
251300
private String getAsStringOrNull(JsonElement element) {
252301
if (element == null || element.isJsonNull()) {
253302
return null;
@@ -256,7 +305,53 @@ private String getAsStringOrNull(JsonElement element) {
256305
}
257306
}
258307

259-
public List<Node> unserialize(JsonElement jsonElement) {
308+
/**
309+
* This will return a lower-level representation of the information stored in JSON.
310+
* It is intended to load broken models.
311+
*
312+
* Possible usages: repair a broken model, extract a metamodel from the model (“model archeology”), etc.
313+
*
314+
* This method follows a "best-effort" approach, try to limit exception thrown and return data whenever is possible,
315+
* in the measure that it is possible.
316+
*/
317+
public List<NodeData> rawUnserialization(JsonElement jsonElement) {
318+
if (jsonElement.isJsonObject()) {
319+
JsonObject topLevel = jsonElement.getAsJsonObject();
320+
if (!topLevel.has("nodes")) {
321+
return Collections.emptyList();
322+
}
323+
if (topLevel.get("nodes").isJsonArray()) {
324+
List<NodeData> nodes = topLevel.get("nodes").getAsJsonArray().asList().stream().map(element -> {
325+
try {
326+
NodeData nodeData = unserializeNodeData(element);
327+
if (nodeData != null && nodeData.getID() != null) {
328+
this.nodeIdToData.put(nodeData.getID(), element.getAsJsonObject());
329+
this.nodeIdToNodeData.put(nodeData.getID(), nodeData);
330+
}
331+
return nodeData;
332+
} catch (Exception e) {
333+
throw new RuntimeException("Issue while unserializing " + element, e);
334+
}
335+
}).filter(e -> e != null).collect(Collectors.toList());
336+
for (Map.Entry<String, JsonObject> entry : nodeIdToData.entrySet()) {
337+
try {
338+
populateLinks(nodeIdToNodeData.get(entry.getKey()), entry.getValue());
339+
} catch (Exception e) {
340+
throw new RuntimeException("Issue while unserializing " + entry, e);
341+
}
342+
}
343+
nodeIdToData.clear();
344+
nodeIdToNodeData.clear();
345+
return nodes;
346+
} else {
347+
return Collections.emptyList();
348+
}
349+
} else {
350+
return Collections.emptyList();
351+
}
352+
}
353+
354+
public List<Node> unserializeToNode(JsonElement jsonElement) {
260355
if (jsonElement.isJsonObject()) {
261356
JsonObject topLevel = jsonElement.getAsJsonObject();
262357
if (!topLevel.has("serializationFormatVersion")) {
@@ -315,6 +410,20 @@ private Node unserializeNode(JsonElement jsonElement) {
315410
}
316411
}
317412

413+
@Nullable
414+
private NodeData unserializeNodeData(JsonElement jsonElement) {
415+
if (jsonElement.isJsonObject()) {
416+
JsonObject jsonObject = jsonElement.getAsJsonObject();
417+
String conceptID = tryToGetStringProperty(jsonObject, CONCEPT_LABEL);
418+
String nodeID = tryToGetStringProperty(jsonObject, ID_LABEL);
419+
NodeData nodeData = new NodeData(nodeID, conceptID);
420+
populateProperties(nodeData, jsonObject);
421+
return nodeData;
422+
} else {
423+
return null;
424+
}
425+
}
426+
318427
private String getStringProperty(JsonObject jsonObject, String propertyName) {
319428
if (!jsonObject.has(propertyName)) {
320429
throw new IllegalArgumentException(propertyName + " property not found in " + jsonObject);
@@ -327,4 +436,17 @@ private String getStringProperty(JsonObject jsonObject, String propertyName) {
327436
}
328437
}
329438

439+
@Nullable
440+
private String tryToGetStringProperty(JsonObject jsonObject, String propertyName) {
441+
if (!jsonObject.has(propertyName)) {
442+
return null;
443+
}
444+
JsonElement value = jsonObject.get(propertyName);
445+
if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isString()) {
446+
return value.getAsJsonPrimitive().getAsString();
447+
} else {
448+
return null;
449+
}
450+
}
451+
330452
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package org.lionweb.lioncore.java.serialization;
2+
3+
import javax.annotation.Nullable;
4+
import java.util.*;
5+
import java.util.function.Function;
6+
7+
/**
8+
* Lower level representation of Node which is used to load broken nodes during serialization.
9+
*/
10+
public class NodeData {
11+
private String id;
12+
private String conceptId;
13+
private String parentNodeID;
14+
private Map<String, String> propertyValues = new HashMap<>();
15+
private Map<String, List<String>> containmentsValues = new HashMap<>();
16+
private Map<String, List<RawReferenceValue>> referencesValues = new HashMap<>();
17+
18+
public String getParentNodeID() {
19+
return parentNodeID;
20+
}
21+
22+
public void setParentNodeID(String parentNodeID) {
23+
this.parentNodeID = parentNodeID;
24+
}
25+
26+
public List<String> getChildren() {
27+
List<String> children = new ArrayList<>();
28+
for (List<String> ch : containmentsValues.values()) {
29+
children.addAll(ch);
30+
}
31+
return children;
32+
}
33+
34+
public static class RawReferenceValue {
35+
public String referredId;
36+
public String resolveInfo;
37+
38+
public String getReferredId() {
39+
return referredId;
40+
}
41+
42+
public void setReferredId(String referredId) {
43+
this.referredId = referredId;
44+
}
45+
46+
public String getResolveInfo() {
47+
return resolveInfo;
48+
}
49+
50+
public void setResolveInfo(String resolveInfo) {
51+
this.resolveInfo = resolveInfo;
52+
}
53+
54+
public RawReferenceValue(String referredId, String resolveInfo) {
55+
this.referredId = referredId;
56+
this.resolveInfo = resolveInfo;
57+
}
58+
59+
@Override
60+
public boolean equals(Object o) {
61+
if (this == o) return true;
62+
if (!(o instanceof RawReferenceValue)) return false;
63+
RawReferenceValue that = (RawReferenceValue) o;
64+
return Objects.equals(referredId, that.referredId) && Objects.equals(resolveInfo, that.resolveInfo);
65+
}
66+
67+
@Override
68+
public int hashCode() {
69+
return Objects.hash(referredId, resolveInfo);
70+
}
71+
}
72+
73+
public NodeData() {
74+
75+
}
76+
77+
public NodeData(String id, String conceptId) {
78+
setID(id);
79+
setConceptID(conceptId);
80+
}
81+
82+
public String getConceptID() {
83+
return conceptId;
84+
}
85+
86+
public void setConceptID(String conceptId) {
87+
this.conceptId = conceptId;
88+
}
89+
90+
@Nullable
91+
public String getID() {
92+
return id;
93+
}
94+
95+
96+
public void setID(String id) {
97+
this.id = id;
98+
}
99+
100+
public void setPropertyValue(String propertyId, String serializedValue) {
101+
this.propertyValues.put(propertyId, serializedValue);
102+
}
103+
104+
public void addChild(String containmentID, String childId) {
105+
this.containmentsValues.computeIfAbsent(containmentID, s -> new ArrayList<>()).add(childId);
106+
}
107+
108+
public void addReferenceValue(String referenceID, RawReferenceValue referenceValue) {
109+
this.referencesValues.computeIfAbsent(referenceID, s -> new ArrayList<>()).add(referenceValue);
110+
}
111+
112+
@Nullable
113+
public String getPropertyValue(String propertyId) {
114+
return this.propertyValues.get(propertyId);
115+
}
116+
117+
@Nullable
118+
public List<RawReferenceValue> getReferenceValues(String referenceID) {
119+
return this.referencesValues.getOrDefault(referenceID, new ArrayList<>());
120+
}
121+
}

0 commit comments

Comments
 (0)