From d5b3067fd8e874e34ea696a9e5ed6664ebb20a13 Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Mon, 19 Aug 2024 10:32:21 +0200 Subject: [PATCH] Fix serialization of custom properties --- .../ExampleDWH/mappings/DWH.mapping.cm | 12 +++---- .../ExampleCRM/diagrams/CRM.diagram.cm | 18 +++++----- .../ExampleCRM/entities/Address.entity.cm | 12 +++---- .../Address_Customer.relationship.cm | 12 +++---- .../language-server/cross-model-serializer.ts | 34 +++++++++++++++---- 5 files changed, 55 insertions(+), 33 deletions(-) diff --git a/examples/mapping-example/ExampleDWH/mappings/DWH.mapping.cm b/examples/mapping-example/ExampleDWH/mappings/DWH.mapping.cm index f029cee2..dd18ecd0 100644 --- a/examples/mapping-example/ExampleDWH/mappings/DWH.mapping.cm +++ b/examples/mapping-example/ExampleDWH/mappings/DWH.mapping.cm @@ -34,7 +34,7 @@ mapping: - Customer conditions: - CalcAgeSourceObject.BirthDate = Customer.BirthDate - customProperties: + customProperties: - name: Author value: "CrossBreeze" - name: ExampleMappingSource @@ -58,14 +58,14 @@ mapping: expression: "Fixed String" - attribute: Today customProperties: - - name: Author - value: "CrossBreeze" - - name: ExampleEntityTargetAttribute - customProperties: + - name: Author + value: "CrossBreeze" + - name: ExampleEntityTargetAttribute + customProperties: - name: Author value: "CrossBreeze" - name: ExampleMappingTarget - customProperties: + customProperties: - name: Author value: "CrossBreeze" - name: ExampleMapping \ No newline at end of file diff --git a/examples/mapping-example/Sources/ExampleCRM/diagrams/CRM.diagram.cm b/examples/mapping-example/Sources/ExampleCRM/diagrams/CRM.diagram.cm index e466e842..47350887 100644 --- a/examples/mapping-example/Sources/ExampleCRM/diagrams/CRM.diagram.cm +++ b/examples/mapping-example/Sources/ExampleCRM/diagrams/CRM.diagram.cm @@ -22,9 +22,9 @@ systemDiagram: width: 159.649658203125 height: 96 customProperties: - - name: Author - value: "CrossBreeze" - - name: ExampleNode + - name: Author + value: "CrossBreeze" + - name: ExampleNode edges: - id: CustomerToOrder relationship: Order_Customer @@ -35,10 +35,10 @@ systemDiagram: sourceNode: AddressNode targetNode: CustomerNode customProperties: - - name: Author - value: "CrossBreeze" - - name: ExampleEdge + - name: Author + value: "CrossBreeze" + - name: ExampleEdge customProperties: - - name: Author - value: "CrossBreeze" - - name: ExampleDiagram \ No newline at end of file + - name: Author + value: "CrossBreeze" + - name: ExampleDiagram \ No newline at end of file diff --git a/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm b/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm index b61297bd..7175b8fa 100644 --- a/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm +++ b/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm @@ -14,10 +14,10 @@ entity: name: "CountryCode" datatype: "Text" customProperties: - - name: Author - value: "CrossBreeze" - - name: ExampleEntityAttribute + - name: Author + value: "CrossBreeze" + - name: ExampleEntityAttribute customProperties: - - name: Author - value: "CrossBreeze" - - name: ExampleEntity \ No newline at end of file + - name: Author + value: "CrossBreeze" + - name: ExampleEntity \ No newline at end of file diff --git a/examples/mapping-example/Sources/ExampleCRM/relationships/Address_Customer.relationship.cm b/examples/mapping-example/Sources/ExampleCRM/relationships/Address_Customer.relationship.cm index c14516c7..e41c5a62 100644 --- a/examples/mapping-example/Sources/ExampleCRM/relationships/Address_Customer.relationship.cm +++ b/examples/mapping-example/Sources/ExampleCRM/relationships/Address_Customer.relationship.cm @@ -8,10 +8,10 @@ relationship: - parent: Customer.Id child: ExampleCRM.Address.CustomerID customProperties: - - name: Author - value: "CrossBreeze" - - name: ExampleRelationshipAttribute + - name: Author + value: "CrossBreeze" + - name: ExampleRelationshipAttribute customProperties: - - name: Author - value: "CrossBreeze" - - name: ExampleRelationship \ No newline at end of file + - name: Author + value: "CrossBreeze" + - name: ExampleRelationship \ No newline at end of file diff --git a/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts b/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts index 6fb089dc..b9987738 100644 --- a/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts +++ b/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts @@ -50,7 +50,8 @@ const PROPERTY_ORDER = [ 'source', 'conditions', 'expression', - 'customProperties' + 'customProperties', + 'value' ]; const ID_OR_IDREF = [ @@ -64,7 +65,6 @@ const ID_OR_IDREF = [ 'sources', 'target', 'attribute', - 'from', 'join', 'conditions', 'parent', @@ -96,13 +96,23 @@ export class CrossModelSerializer implements Serializer { return this.serializeValue(newRoot, CrossModelSerializer.INDENTATION_AMOUNT_OBJECT * -1, 'root'); } - private serializeValue(value: any, indentationLevel: number, key: string): string { + private serializeValue(value: any, indentationLevel: number, key: string, container?: Record): string { + if (this.isCustomProperty(container)) { + // custom properties need a special handling because they use properties that are already used differently in other places + // 'name:' typically used as a string but used as an identifier in the custom property + // 'value:' used for ID-based references in other places but used as a string value in the custom property + return key === 'name' ? value : JSON.stringify(value); + } + if (Array.isArray(value)) { return this.serializeArray(value, indentationLevel, key); } else if (typeof value === 'object' && value !== undefined) { return this.serializeObject(value, indentationLevel + CrossModelSerializer.INDENTATION_AMOUNT_OBJECT, key); + } else if (this.isIdOrIdRef(key)) { + // for IDs and references (based on IDs) we guarantee that they do not contain spaces or other characters that break the string + return value; } else { - return this.isIdOrIdRef(key) ? value : JSON.stringify(value); + return JSON.stringify(value); } } @@ -117,14 +127,16 @@ export class CrossModelSerializer implements Serializer { .sort((left, right) => PROPERTY_ORDER.indexOf(left[0]) - PROPERTY_ORDER.indexOf(right[0])) .map(([objKey, objValue]) => { if (Array.isArray(objValue) && objValue.length === 0) { + // skip empty arrays return; } if (objKey === 'identifier' && objValue === false) { + // skip false identifiers for better readability return; } const propKey = this.serializeKey(objKey); - const propValue = this.serializeValue(objValue, indentationLevel, propKey); + const propValue = this.serializeValue(objValue, indentationLevel, propKey, obj); if (typeof objValue === 'object') { return `${indentation}${propKey}:${CrossModelSerializer.CHAR_NEWLINE}${propValue}`; } else { @@ -143,7 +155,7 @@ export class CrossModelSerializer implements Serializer { private serializeArray(arr: any[], indentationLevel: number, key: string): string { const serializedItems = arr .filter(item => item !== undefined) - .map(item => this.serializeValue(item, indentationLevel, key)) + .map(item => this.serializeValue(item, indentationLevel, key, arr)) .map(item => this.ensureArrayItem(item, indentationLevel + CrossModelSerializer.INDENTATION_AMOUNT_ARRAY)) .join(CrossModelSerializer.CHAR_NEWLINE); return serializedItems; @@ -214,4 +226,14 @@ export class CrossModelSerializer implements Serializer { const right = obj.expression.right.$type === StringLiteral ? quote(obj.expression.right.value) : obj.expression.right.value; return [left, obj.expression.op, right].join(' '); } + + private isCustomProperty(obj: any): obj is { name: string; value?: string } { + // we need to be strict here cause other objects may also have name or value properties + return ( + typeof obj === 'object' && + 'name' in obj && + typeof (obj as any).name === 'string' && + (Object.keys(obj).length === 1 || (Object.keys(obj).length === 2 && 'value' in obj && typeof (obj as any).value === 'string')) + ); + } }