Skip to content

Commit

Permalink
mps-cli-ts: initial implementation of a serializer of root nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
ratiud committed Jan 7, 2024
1 parent b1d03b9 commit fa72d8a
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 39 deletions.
5 changes: 3 additions & 2 deletions mps-cli-ts/src/model/builder/root_node_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,12 @@ function populateNodePropertiesAndLinks(nodeElement : Element, node : SNode, imp
let property = child
const propertyRole = property.attributes.getNamedItem("role")!.value
const propertyValue = property.attributes.getNamedItem("value")!.value
const nodeProperty = registry.getPropertyByIndex(propertyRole)
const nodeProperty = registry.getPropertyByRole(propertyRole)
node.addProperty(nodeProperty, propertyValue)
} else if(child.tagName === "ref") {
let ref = child
const refRole = ref.attributes.getNamedItem("role")!.value
const refResolveInfo = ref.attributes.getNamedItem("resolve")!.value

var refNodeId : string;
var modelId : string;
Expand All @@ -69,7 +70,7 @@ function populateNodePropertiesAndLinks(nodeElement : Element, node : SNode, imp
refNodeId = ref.attributes.getNamedItem("node")!.value
}
const refLink = registry.getLinkByIndex(refRole)
node.addLink(refLink, new SNodeRef(modelId, refNodeId))
node.addLink(refLink, new SNodeRef(modelId, refNodeId, refResolveInfo))
}
}
}
Expand Down
9 changes: 5 additions & 4 deletions mps-cli-ts/src/model/builder/root_node_builder_fast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ function populateNodePropertiesAndLinks(node_json_element : any, node : SNode, i
for(var property_json_element of ensure_array(node_json_element["property"])) {
const propertyRole = property_json_element["@_role"] as string
const propertyValue = property_json_element["@_value"] as string
const nodeProperty = registry.getPropertyByIndex(propertyRole)
const nodeProperty = registry.getPropertyByRole(propertyRole)
node.addProperty(nodeProperty, propertyValue)
}

for(var ref_json_element of ensure_array(node_json_element["ref"])) {
const refRole = ref_json_element["@_role"] as string
const refResolveInfo = ref_json_element["@_resolve"] as string

var refNodeId : string;
var modelId : string;
Expand All @@ -68,7 +69,7 @@ function populateNodePropertiesAndLinks(node_json_element : any, node : SNode, i
refNodeId = ref_json_element["@_node"] as string
}
const refLink = registry.getLinkByIndex(refRole)
node.addLink(refLink, new SNodeRef(modelId, refNodeId))
node.addLink(refLink, new SNodeRef(modelId, refNodeId, refResolveInfo))
}

}
Expand All @@ -81,7 +82,7 @@ function buildRootNodeImports(imports_json_element : JsonElement) : SRootNodeImp
const modelRef = model_import_json_element["@_ref"] as string
const splits = modelRef.split("(")
const modelId = splits[0]
const modelName = splits[1].substring(0, splits[1].length)
const modelName = splits[1].substring(0, splits[1].length - 1)
const implicit = model_import_json_element["@_implicit"] as string

imports.imports.push(new SModelImport(importIndex, modelId, modelName, implicit === "true"))
Expand All @@ -103,7 +104,7 @@ function buildRootNodeRegistry(registry_json_element : JsonElement) : SRootNodeR
const conceptId = registry_language_concept_json_element["@_id"] as string
const conceptName = registry_language_concept_json_element["@_name"] as string

const conceptFlag = registry_language_concept_json_element["@_flag"] as string
const conceptFlag = registry_language_concept_json_element["@_flags"] as string
const conceptIndex = registry_language_concept_json_element["@_index"] as string

var myConcept = SLanguageBuilder.getConcept(myLanguage, conceptName, conceptId)
Expand Down
100 changes: 100 additions & 0 deletions mps-cli-ts/src/model/serializer/root_node_serializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { childrenByTagName } from "../builder/utils";
import { SAbstractConceptLink, SChildLink, SProperty, SReferenceLink } from "../sconcept";
import { SModel } from "../smodel";
import { SNode, SNodeRef, SRootNode, SRootNodeImports, SRootNodeRegistry } from "../snode";


export function serialize_root_node(model : SModel, root_node : SRootNode) : string {
var res = ""
res += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n"
res += `<model ref="${model.id}(${model.name})" content="root">` + "\n"
var PADDING = " "
res += PADDING + "<persistence version=\"9\" />" + "\n"

res += serialize_imports(root_node, PADDING);
res += serialize_registry(root_node, PADDING);
res += serialize_nodes(root_node.registry!, root_node.imports!, root_node, undefined, PADDING);

res += "</model>\n\n"
return res;
}

function serialize_nodes(registry : SRootNodeRegistry, imports : SRootNodeImports, node : SNode, role : string | undefined, padding : string) : string {
var res = role === undefined ?
`${padding}<node concept="${registry.getIndexForConcept(node.myConcept)}" id="${node.id}">\n` :
`${padding}<node concept="${registry.getIndexForConcept(node.myConcept)}" id="${node.id}" role="${role}">\n`
padding = increase_indent(padding)

for(const prop2Val of node.properties) {
res += `${padding}<property role="${registry.getRoleForProperty(prop2Val[0])}" value="${prop2Val[1]}" />\n`
}

for(const refLink of node.links.filter(it => it[0] instanceof SReferenceLink)) {
const refNode = (refLink[1][0] as SNodeRef)
res += `${padding}<ref role="${registry.getIndexForLink(refLink[0])}" to="${imports.getModelIndexByModelId(refNode.modelId)}:${refNode.nodeId}" resolve="${refNode.resolveInfo}" />\n`
}
for(const childLink of node.links.filter(it => it[0] instanceof SChildLink)) {
for(const child of childLink[1]!) {
res += serialize_nodes(registry, imports, child as SNode, registry.getIndexForLink(childLink[0]), padding)
}
}
padding = decrease_indent(padding)
res += `${padding}</node>\n`
return res;
}

function serialize_registry(root_node : SRootNode, padding : string) : string {
var res = `${padding}<registry>\n`
padding = increase_indent(padding)
for(var languageRegistry of root_node.registry?.languages!) {
res += `${padding}<language id="${languageRegistry.language.id}" name="${languageRegistry.language.name}">\n`
padding = increase_indent(padding)
for(var concept of languageRegistry.usedConcepts) {
const noPropertiesAndLinks = concept.propertiesRegistries.length === 0 && concept.linksRegistries.length === 0
res += `${padding}<concept id="${concept.myConcept.id}" name="${concept.myConcept.name}" flags="${concept.myConceptFlag}" index="${concept.myConceptIndex}"${noPropertiesAndLinks ? " /" : ""}>\n`
padding = increase_indent(padding)
for(var prop of concept.propertiesRegistries) {
res += `${padding}<property id="${prop.property.id}" name="${prop.property.name}" index="${prop.index}" />\n`
}
padding = decrease_indent(padding)
padding = increase_indent(padding)
for(var link of concept.linksRegistries) {
const linkKind = link.link instanceof SChildLink ? "child" : "reference"
res += `${padding}<${linkKind} id="${link.link.id}" name="${link.link.name}" index="${link.index}" />\n`
}
padding = decrease_indent(padding)
if (!noPropertiesAndLinks) {
res += `${padding}</concept>\n`
}
}
padding = decrease_indent(padding)
res += `${padding}</language>\n`
}
padding = decrease_indent(padding)
res += `${padding}</registry>\n`

return res;
}

function serialize_imports(root_node : SRootNode, padding : string) : string {
if(root_node.imports?.imports.length == 0) {
return padding + "<imports />\n";
}

var res = padding + "<imports>\n";
padding = increase_indent(padding)
for(var crtImport of root_node.imports?.imports!) {
res += `${padding}<import index="${crtImport.myModelIndex}" ref="${crtImport.myModelId}(${crtImport.myModelName})" implicit="${crtImport.implicit}" />\n`
}
padding = decrease_indent(padding)
res += `${padding}</imports>\n`
return res;
}

function increase_indent(padding : string) : string {
return padding + " "
}

function decrease_indent(padding : string) : string {
return padding.substring(2)
}
8 changes: 8 additions & 0 deletions mps-cli-ts/src/model/smodel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,12 @@ export class SModel {
return undefined
}

findRootNodesByName(rootNodeName : string) : SRootNode[] {
const res : SRootNode[] = []
for(const crtNode of this.rootNodes) {
if(crtNode.getProperty("name") === rootNodeName)
res.push(crtNode)
}
return res
}
}
71 changes: 51 additions & 20 deletions mps-cli-ts/src/model/snode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { SRepository } from "./srepository";
export class SNode {
myConcept : SConcept;
id : string;
links : Map<SAbstractConceptLink, (SNode | SNodeRef)[]> = new Map<SAbstractConceptLink, (SNode | SNodeRef)[]>
properties : Map<SProperty, string> = new Map<SProperty, string>
links : [SAbstractConceptLink, (SNode | SNodeRef)[]][] = []
properties : [SProperty, string][] = []
myParent : SNode | undefined;

constructor(myConcept : SConcept, id : string, parent : SNode | undefined) {
Expand All @@ -16,30 +16,30 @@ export class SNode {
}

addLink(link : SAbstractConceptLink, node : SNode | SNodeRef) {
var nodesForLink = this.links.get(link);
if (nodesForLink == null) {
nodesForLink = [];
this.links.set(link, nodesForLink)
var nodesForLink = this.links.find(it => it[0] === link);
if (nodesForLink == undefined) {
this.links.push([link, [node]])
} else {
nodesForLink[1].push(node)
}
nodesForLink.push(node)
}

allLinkedNodes() : (SNode | SNodeRef)[] {
var res : (SNode | SNodeRef)[] = []
this.links.forEach((linkedNodes : (SNode | SNodeRef)[], link : SAbstractConceptLink) => {
res = res.concat(linkedNodes)
this.links.forEach(it => {
res = res.concat(it[1])
})
return res
}

addProperty(property : SProperty, value : string) {
this.properties.set(property, value);
this.properties.push([property, value]);
}

getProperty(propertyName : string) : string | undefined {
for(const prop of this.properties.keys())
if(prop.name === propertyName)
return this.properties.get(prop);
for(const prop of this.properties)
if(prop[0].name === propertyName)
return prop[1];
return undefined
}

Expand All @@ -48,15 +48,15 @@ export class SNode {
if (includeSelf && (concept === undefined || this.myConcept == concept)) { res.push(this) }

const linksToVisit : [SChildLink, SNode[]][] = []
const myChildren = Array.from(this.links.entries()).filter(it => it[0] instanceof SChildLink)
const myChildren = Array.from(this.links).filter(it => it[0] instanceof SChildLink)
myChildren.forEach(it => linksToVisit.push(it as [SChildLink, SNode[]]))

while(linksToVisit.length > 0) {
const crtLink = linksToVisit.pop()!
res.push(...crtLink?.[1])

for(const childNode of crtLink?.[1]) {
const myChildren = Array.from(childNode.links.entries()).filter(it => it[0] instanceof SChildLink)
const myChildren = childNode.links.filter(it => it[0] instanceof SChildLink)
myChildren.forEach(it => linksToVisit.push(it as [SChildLink, SNode[]]))
}
}
Expand All @@ -65,9 +65,9 @@ export class SNode {

getLinkedNodes(linkName : string) : (SNode | SNodeRef)[] {
const res : (SNode | SNodeRef)[] = []
this.links.forEach((linkedNodes : (SNode | SNodeRef)[], link : SAbstractConceptLink) => {
if (link.name === linkName) {
res.push(...linkedNodes)
this.links.forEach(it => {
if (it[0].name === linkName) {
res.push(...it[1])
}
});
return res
Expand All @@ -88,10 +88,12 @@ export class SRootNode extends SNode {
export class SNodeRef {
modelId : string
nodeId : string
resolveInfo : string;

constructor(modelId : string, nodeId : string) {
constructor(modelId : string, nodeId : string, resolveInfo : string) {
this.modelId = modelId
this.nodeId = nodeId
this.resolveInfo = resolveInfo
}

resolve(repo : SRepository) : SNode | undefined {
Expand All @@ -114,6 +116,14 @@ export class SRootNodeImports {
return "undefined";
}

getModelIndexByModelId(modelId : string) : string {
for(const imp of this.imports) {
if (imp.myModelId === modelId)
return imp.myModelIndex
}
return "undefined";
}

}

export class SRootNodeRegistry {
Expand All @@ -127,13 +137,34 @@ export class SRootNodeRegistry {
return this.index2concepts.get(index)!;
}

getIndexForConcept(concept : SConcept) : string | undefined {
for(const entry of this.index2concepts.entries())
if(entry[1] === concept)
return entry[0]
return undefined;
}

getLinkByIndex(index : string) : SAbstractConceptLink {
return this.index2links.get(index)!;
}

getPropertyByIndex(index : string) : SProperty {
getIndexForLink(link : SAbstractConceptLink) : string | undefined {
for(const entry of this.index2links.entries())
if(entry[1] === link)
return entry[0]
return undefined;
}

getPropertyByRole(index : string) : SProperty {
return this.index2properties.get(index)!;
}

getRoleForProperty(property : SProperty) : string | undefined {
for(const entry of this.index2properties.entries())
if(entry[1] === property)
return entry[0]
return undefined;
}
}

export class SModelImport {
Expand Down
17 changes: 15 additions & 2 deletions mps-cli-ts/src/model/srepository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SModel } from "./smodel";
import { SAbstractModule } from "./smodule";
import { SNode } from "./snode";
import { SNode, SRootNode } from "./snode";


export class SRepository {
Expand Down Expand Up @@ -60,7 +60,7 @@ export class SRepository {
return undefined;
}

findModelByName(modelName : string) : SModel[] {
findModelsByName(modelName : string) : SModel[] {
const res : SModel[] = []
for(const crtModule of this.modules) {
for(const crtModel of crtModule.models) {
Expand All @@ -71,5 +71,18 @@ export class SRepository {
return res;
}

findRootNodesByName(rootNodeName : string) : SRootNode[] {
const res : SRootNode[] = []
for(const crtModule of this.modules) {
for(const crtModel of crtModule.models) {
for(const crtRoot of crtModel.rootNodes) {
if (crtRoot.getProperty("name") === rootNodeName)
res.push(crtRoot)
}
}
}
return res;
}

}

7 changes: 3 additions & 4 deletions mps-cli-ts/tests/node.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,12 @@ describe('testing building of root nodes', () => {
assert.equal(rootNode.myConcept.name, "mps.cli.landefs.people.structure.PersonsContainer")
assert.equal(rootNode.id, "4Yb5JA31NUu")
assert.equal(rootNode.allLinkedNodes().length, 2)
assert.equal(rootNode.properties.size, 1)
const propertiesKeysArray = Array.from(rootNode.properties.keys());
assert.equal(rootNode.properties.length, 1)

const nameProperty = propertiesKeysArray[0]
const nameProperty = rootNode.properties[0][0]
assert.equal(nameProperty.name, "name");
assert.equal(nameProperty.id, "1169194664001");
assert.equal(rootNode.properties.get(nameProperty), "_010_classical_authors");
assert.equal(rootNode.getProperty(nameProperty.name), "_010_classical_authors");
assert.equal(rootNode.getProperty("name"), "_010_classical_authors");

const descendants = rootNode.descendants(undefined, false)
Expand Down
6 changes: 3 additions & 3 deletions mps-cli-ts/tests/project.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ describe("testing building of the model from a directory with solutions", () =>
assert.equal(repo.modules.length, 2);

assert.equal(repo.getNodesWithPropertyAndValue("name").length, 11)
assert.equal(repo.findModelByName("mps.cli.lanuse.library_second.library_top").length, 1)
assert.equal(repo.findModelByName("mps.cli.lanuse.library_second.library_top")[0].rootNodes.length, 1)
assert.equal(repo.findModelByName("mps.cli.lanuse.library_second.library_top")[0].rootNodes[0].getProperty("name"), "_library")
assert.equal(repo.findModelsByName("mps.cli.lanuse.library_second.library_top").length, 1)
assert.equal(repo.findModelsByName("mps.cli.lanuse.library_second.library_top")[0].rootNodes.length, 1)
assert.equal(repo.findModelsByName("mps.cli.lanuse.library_second.library_top")[0].rootNodes[0].getProperty("name"), "_library")
assert.equal(repo.getNodesWithPropertyAndValue("name", "_library").length, 1)
assert.equal(repo.getNodesWithPropertyAndValue("name", "Mark Twain").length, 1)
const markTwain = repo.getNodesWithPropertyAndValue("name", "Mark Twain")[0]
Expand Down
Loading

0 comments on commit fa72d8a

Please sign in to comment.