Skip to content

Commit 4cc379c

Browse files
committed
Deserialization of Lionweb nodes to dynamic nodes
1 parent d71d9a1 commit 4cc379c

File tree

5 files changed

+93
-18
lines changed

5 files changed

+93
-18
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
All notable changes to this project from version 1.2.0 upwards are documented in this file.
33
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
44

5+
## [1.6.2] – 2024-02-29
6+
7+
### Added
8+
- Deserialization of Lionweb nodes to dynamic nodes
9+
510
## [1.6.1] – 2024-02-28
611

712
### Added

src/interop/lionweb.ts

+42-14
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
InstantiationFacade, Language,
1111
Node as LionwebNode
1212
} from "@lionweb/core";
13-
import {Node} from "..";
13+
import {Node, NodeDefinition, PropertyDefinition} from "..";
1414

1515
export class TylasuNodeWrapper implements LionwebNode {
1616
id: Id;
@@ -38,6 +38,37 @@ export class LanguageMapping {
3838
}
3939
}
4040

41+
function featureToProperty(feature: Feature): PropertyDefinition {
42+
const def: PropertyDefinition = { name: feature.name };
43+
if (feature instanceof Containment) {
44+
def.child = true;
45+
def.multiple = feature.multiple;
46+
}
47+
return def
48+
}
49+
50+
export class DynamicNode extends Node {
51+
52+
protected readonly _nodeDefinition: NodeDefinition;
53+
54+
constructor(public readonly classifier: Classifier) {
55+
super();
56+
const properties = {};
57+
classifier.features.forEach(f => {
58+
properties[f.name] = featureToProperty(f);
59+
});
60+
this._nodeDefinition = {
61+
name: classifier.name,
62+
properties: properties,
63+
resolved: true
64+
};
65+
}
66+
67+
public get nodeDefinition(): NodeDefinition {
68+
return this._nodeDefinition;
69+
}
70+
}
71+
4172
export class TylasuInstantiationFacade implements InstantiationFacade<TylasuNodeWrapper> {
4273

4374
constructor(public languageMappings: LanguageMapping[] = [STARLASU_LANGUAGE_MAPPING]) {}
@@ -52,20 +83,18 @@ export class TylasuInstantiationFacade implements InstantiationFacade<TylasuNode
5283
for (const language of this.languageMappings) {
5384
const nodeType = language.classifiers.get(classifier);
5485
if (nodeType) {
55-
node = this.makeNode(nodeType, parent?.node, propertySettings);
56-
break;
86+
node = new nodeType() as Node;
5787
}
5888
}
59-
if (node) {
60-
return {
61-
id,
62-
parent,
63-
node,
64-
annotations: []
65-
};
66-
} else {
67-
throw new Error("Unknown classifier: " + classifier.id);
89+
if (!node) {
90+
node = new DynamicNode(classifier);
6891
}
92+
return {
93+
id,
94+
parent,
95+
node: this.setupNode(node, parent?.node, propertySettings),
96+
annotations: []
97+
};
6998
}
7099
setFeatureValue(node: TylasuNodeWrapper, feature: Feature, value: unknown): void {
71100
if (feature instanceof Containment) {
@@ -79,10 +108,9 @@ export class TylasuInstantiationFacade implements InstantiationFacade<TylasuNode
79108
}
80109
}
81110

82-
protected makeNode(nodeType: any, parent: Node | undefined, propertySettings: {
111+
protected setupNode(node: Node, parent: Node | undefined, propertySettings: {
83112
[p: string]: unknown
84113
}): Node {
85-
const node = new nodeType() as Node;
86114
Object.keys(propertySettings).forEach(k => {
87115
node[k] = propertySettings[k]; //TODO protect parent, origin etc.
88116
})

src/interop/strumenta-playground.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export abstract class TraceNode extends Node {
120120
return this.getPosition();
121121
}
122122

123-
protected get nodeDefinition(): NodeDefinition {
123+
get nodeDefinition(): NodeDefinition {
124124
return {
125125
package: this.eo.eClass.eContainer.get("name") as string,
126126
name: this.eo.eClass.get("name") as string,

src/model/model.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export abstract class Node extends Origin implements Destination {
146146
return Object.getOwnPropertyNames(props).filter(p => props[p].child);
147147
}
148148

149-
protected get nodeDefinition(): NodeDefinition | undefined {
149+
get nodeDefinition(): NodeDefinition | undefined {
150150
return getNodeDefinition(this);
151151
}
152152

tests/interop/lionweb.test.ts

+44-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ import FS_LANGUAGE_JSON from "./fs-language.json";
22
import FS_MODEL from "./fs-model.json";
33
import {expect} from "chai";
44
import {deserializeChunk, deserializeLanguages, SerializationChunk} from "@lionweb/core";
5-
import {Children, Node} from "../../src";
5+
import {Children, Node, walk} from "../../src";
66
import {
7+
DynamicNode,
78
findClassifier,
89
LanguageMapping,
910
STARLASU_LANGUAGE,
1011
STARLASU_LANGUAGE_MAPPING,
1112
TylasuInstantiationFacade
1213
} from "../../src/interop/lionweb";
14+
import {map, pipe, reduce} from "iter-ops";
1315

1416
abstract class File extends Node {
1517
name: string;
@@ -24,6 +26,9 @@ class TextFile extends File {
2426
contents: string;
2527
}
2628

29+
function printSequence(sequence: Generator<Node>): string {
30+
return pipe(sequence, map(n => n.name), reduce((s1, s2) => s1 + (s1 ? ", " : "") + s2, "")).first;
31+
}
2732

2833
describe('Lionweb integration', function() {
2934
const FS_LANGUAGE = deserializeLanguages(FS_LANGUAGE_JSON as SerializationChunk, STARLASU_LANGUAGE)[0];
@@ -32,7 +37,7 @@ describe('Lionweb integration', function() {
3237
FS_LANGUAGE_MAPPING.register(File, findClassifier(FS_LANGUAGE, "starlasu_language_com-strumenta-codeinsightstudio-model-filesystem_File"));
3338
FS_LANGUAGE_MAPPING.register(TextFile, findClassifier(FS_LANGUAGE, "starlasu_language_com-strumenta-codeinsightstudio-model-filesystem_TextFile"));
3439

35-
it("Can deserialize simple model",
40+
it("can deserialize simple model",
3641
function () {
3742
const nodes = deserializeChunk(FS_MODEL, new TylasuInstantiationFacade([FS_LANGUAGE_MAPPING]), [FS_LANGUAGE], []);
3843
expect(nodes).not.to.be.empty;
@@ -50,5 +55,42 @@ describe('Lionweb integration', function() {
5055
const file = dir.files[0] as TextFile;
5156
expect(file.name).to.equal("delegate.egl");
5257
expect(file.contents.substring(0, 10)).to.equal("Delegate F");
58+
59+
expect(printSequence(walk(root.node))).to.equal(
60+
"resources.zip, resources, delegate.egl, rosetta-code-count-examples-2.egl, " +
61+
"rosetta-code-count-examples-1.egl, sub1, sub2, foreach.egl, SQLDropTable.egl, for.egl, SQLBatch.egl, " +
62+
"SQLCreateTable.egl, SQLDropTable.egl, hello.egl, foreach.egl, Calc.egl, SQLBatch.egl, " +
63+
"multipleWhenCondition.egl, handler.egl, SQLCreateTable.egl, newExample.egl, SQLDropTable.egl, " +
64+
"nestedLoop.egl, for.egl");
65+
});
66+
67+
it("can deserialize simple model to dynamic nodes",
68+
function () {
69+
const nodes = deserializeChunk(FS_MODEL, new TylasuInstantiationFacade(), [FS_LANGUAGE], []);
70+
expect(nodes).not.to.be.empty;
71+
expect(nodes.length).to.equal(1);
72+
const root = nodes[0];
73+
expect(root.node).to.be.instanceof(DynamicNode);
74+
let dir = root.node as DynamicNode & any;
75+
expect(dir.nodeDefinition.name).to.equal("Directory");
76+
expect(dir.name).to.equal("resources.zip");
77+
expect(dir.files.length).to.equal(1);
78+
expect(dir.files[0]).to.be.instanceof(DynamicNode);
79+
dir = dir.files[0] as DynamicNode & any;
80+
expect(dir.nodeDefinition.name).to.equal("Directory");
81+
expect(dir.name).to.equal("resources");
82+
expect(dir.files.length).to.equal(15);
83+
expect(dir.files[0]).to.be.instanceof(DynamicNode);
84+
const file = dir.files[0] as DynamicNode & any;
85+
expect(file.nodeDefinition.name).to.equal("TextFile");
86+
expect(file.name).to.equal("delegate.egl");
87+
expect(file.contents.substring(0, 10)).to.equal("Delegate F");
88+
89+
expect(printSequence(walk(root.node))).to.equal(
90+
"resources.zip, resources, delegate.egl, rosetta-code-count-examples-2.egl, " +
91+
"rosetta-code-count-examples-1.egl, sub1, sub2, foreach.egl, SQLDropTable.egl, for.egl, SQLBatch.egl, " +
92+
"SQLCreateTable.egl, SQLDropTable.egl, hello.egl, foreach.egl, Calc.egl, SQLBatch.egl, " +
93+
"multipleWhenCondition.egl, handler.egl, SQLCreateTable.egl, newExample.egl, SQLDropTable.egl, " +
94+
"nestedLoop.egl, for.egl");
5395
});
5496
});

0 commit comments

Comments
 (0)