Skip to content

Commit

Permalink
feat(tools): add a mermaid visitor (#530)
Browse files Browse the repository at this point in the history
  • Loading branch information
dselman authored Sep 27, 2022
1 parent ac47ec0 commit 2a76a0c
Show file tree
Hide file tree
Showing 6 changed files with 370 additions and 2 deletions.
5 changes: 4 additions & 1 deletion packages/concerto-tools/lib/codegen/codegen.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const JavaVisitor = require('./fromcto/java/javavisitor');
const GraphQLVisitor = require('./fromcto/graphql/graphqlvisitor');
const CSharpVisitor = require('./fromcto/csharp/csharpvisitor');
const ODataVisitor = require('./fromcto/odata/odatavisitor');
const MermaidVisitor = require('./fromcto/mermaid/mermaidvisitor');

module.exports = {
AbstractPlugin,
Expand All @@ -37,6 +38,7 @@ module.exports = {
GraphQLVisitor,
CSharpVisitor,
ODataVisitor,
MermaidVisitor,
formats: {
golang: GoLangVisitor,
jsonschema: JSONSchemaVisitor,
Expand All @@ -46,6 +48,7 @@ module.exports = {
java: JavaVisitor,
graphql: GraphQLVisitor,
csharp: CSharpVisitor,
odata: ODataVisitor
odata: ODataVisitor,
mermaid: MermaidVisitor
}
};
240 changes: 240 additions & 0 deletions packages/concerto-tools/lib/codegen/fromcto/mermaid/mermaidvisitor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

/**
* Convert the contents of a ModelManager
* to Mermaid format file.
* Set a fileWriter property (instance of FileWriter) on the parameters
* object to control where the generated code is written to disk.
*
* @private
* @class
*/
class MermaidVisitor {
/**
* Visitor design pattern
* @param {Object} thing - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visit(thing, parameters) {
if (thing.isModelManager?.()) {
return this.visitModelManager(thing, parameters);
} else if (thing.isModelFile?.()) {
return this.visitModelFile(thing, parameters);
} else if (thing.isParticipant?.()) {
return this.visitParticipantDeclaration(thing, parameters);
} else if (thing.isTransaction?.()) {
return this.visitTransactionDeclaration(thing, parameters);
} else if (thing.isEvent?.()) {
return this.visitEventDeclaration(thing, parameters);
} else if (thing.isAsset?.()) {
return this.visitAssetDeclaration(thing, parameters);
} else if (thing.isEnum?.()) {
return this.visitEnumDeclaration(thing, parameters);
} else if (thing.isClassDeclaration?.()) {
return this.visitClassDeclaration(thing, parameters);
} else if (thing.isField?.()) {
return this.visitField(thing, parameters);
} else if (thing.isRelationship?.()) {
return this.visitRelationship(thing, parameters);
} else if (thing.isEnumValue?.()) {
return this.visitEnumValueDeclaration(thing, parameters);
} else {
throw new Error('Unrecognised ' + JSON.stringify(thing) );
}
}

/**
* Visitor design pattern
* @param {ModelManager} modelManager - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visitModelManager(modelManager, parameters) {
parameters.fileWriter.openFile('model.mmd');
parameters.fileWriter.writeLine(0, 'classDiagram');

modelManager.getModelFiles().forEach((decl) => {
decl.accept(this, parameters);
});

parameters.fileWriter.closeFile();

return null;
}

/**
* Visitor design pattern
* @param {ModelFile} modelFile - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visitModelFile(modelFile, parameters) {
modelFile.getAllDeclarations().forEach((decl) => {
decl.accept(this, parameters);
});
return null;
}

/**
* Visitor design pattern
* @param {ClassDeclaration} classDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visitAssetDeclaration(classDeclaration, parameters) {
return this.visitClassDeclaration(classDeclaration, parameters, 'asset');
}

/**
* Visitor design pattern
* @param {ClassDeclaration} classDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visitEnumDeclaration(classDeclaration, parameters) {
return this.visitClassDeclaration(classDeclaration, parameters, 'enumeration');
}

/**
* Visitor design pattern
* @param {ClassDeclaration} classDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visitEventDeclaration(classDeclaration, parameters) {
return this.visitClassDeclaration(classDeclaration, parameters, 'event');
}

/**
* Visitor design pattern
* @param {ClassDeclaration} classDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visitParticipantDeclaration(classDeclaration, parameters) {
return this.visitClassDeclaration(classDeclaration, parameters, 'participant');
}

/**
* Visitor design pattern
* @param {ClassDeclaration} classDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visitTransactionDeclaration(classDeclaration, parameters) {
return this.visitClassDeclaration(classDeclaration, parameters, 'transaction');
}


/**
* Visitor design pattern
* @param {ClassDeclaration} classDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @param {string} type - the type of the declaration
* @return {Object} the result of visiting or null
* @private
*/
visitClassDeclaration(classDeclaration, parameters, type = 'concept') {

if (classDeclaration.getOwnProperties().length > 0) {
parameters.fileWriter.writeLine(0, 'class ' + classDeclaration.getName() + ' {');
parameters.fileWriter.writeLine(0, '<< ' + type + '>>');

classDeclaration.getOwnProperties().forEach((property) => {
if (!property.isRelationship?.()) {
property.accept(this, parameters);
}
});

parameters.fileWriter.writeLine(0, '}\n');
}
else {
parameters.fileWriter.writeLine(0, 'class ' + classDeclaration.getName());
parameters.fileWriter.writeLine(0, '<< ' + type + '>>' + ' ' + classDeclaration.getName() + '\n');
}

classDeclaration.getOwnProperties().forEach((property) => {
if (property.isRelationship?.()) {
property.accept(this, parameters);
}
});

if (classDeclaration.getSuperType()) {
parameters.fileWriter.writeLine(0, classDeclaration.getName() + ' --|> ' + classDeclaration.getSuperTypeDeclaration().getName());
}

return null;
}

/**
* Visitor design pattern
* @param {Field} field - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visitField(field, parameters) {
let array = '';

if (field.isArray()) {
array = '[]';
}

parameters.fileWriter.writeLine(1, ' +' + field.getType() + array + ' ' + field.getName());

return null;
}

/**
* Visitor design pattern
* @param {EnumValueDeclaration} enumValueDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visitEnumValueDeclaration(enumValueDeclaration, parameters) {
parameters.fileWriter.writeLine(1, enumValueDeclaration.getName());
return null;
}

/**
* Visitor design pattern
* @param {RelationshipDeclaration} relationship - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visitRelationship(relationship, parameters) {
let array = '"1"';
if (relationship.isArray()) {
array = '"*"';
}
parameters.fileWriter.writeLine(0, relationship.getParent().getName() + ' "1"' + ' o-- ' + array + ' ' + relationship.getType() + ' : ' + relationship.getName());
return null;
}
}

module.exports = MermaidVisitor;
12 changes: 12 additions & 0 deletions packages/concerto-tools/test/codegen/fromcto/data/model/hr.cto
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,16 @@ participant Contractor extends Person {

participant Manager extends Employee {
--> Person[] reports optional
}

event CompanyEvent {
}

event Onboarded extends CompanyEvent {
--> Employee employee
}

transaction ChangeOfAddress {
--> Person Person
o Address newAddress
}
2 changes: 2 additions & 0 deletions packages/concerto-tools/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export var CodeGen: {
GraphQLVisitor: typeof import("./lib/codegen/fromcto/graphql/graphqlvisitor");
CSharpVisitor: typeof import("./lib/codegen/fromcto/csharp/csharpvisitor");
ODataVisitor: typeof import("./lib/codegen/fromcto/odata/odatavisitor");
MermaidVisitor: typeof import("./lib/codegen/fromcto/mermaid/mermaidvisitor");
formats: {
golang: typeof import("./lib/codegen/fromcto/golang/golangvisitor");
jsonschema: typeof import("./lib/codegen/fromcto/jsonschema/jsonschemavisitor");
Expand All @@ -19,6 +20,7 @@ export var CodeGen: {
graphql: typeof import("./lib/codegen/fromcto/graphql/graphqlvisitor");
csharp: typeof import("./lib/codegen/fromcto/csharp/csharpvisitor");
odata: typeof import("./lib/codegen/fromcto/odata/odatavisitor");
mermaid: typeof import("./lib/codegen/fromcto/mermaid/mermaidvisitor");
};
};
export var version: any;
4 changes: 3 additions & 1 deletion packages/concerto-tools/types/lib/codegen/codegen.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import JavaVisitor = require("./fromcto/java/javavisitor");
import GraphQLVisitor = require("./fromcto/graphql/graphqlvisitor");
import CSharpVisitor = require("./fromcto/csharp/csharpvisitor");
import ODataVisitor = require("./fromcto/odata/odatavisitor");
import MermaidVisitor = require("./fromcto/mermaid/mermaidvisitor");
export declare namespace formats {
export { GoLangVisitor as golang };
export { JSONSchemaVisitor as jsonschema };
Expand All @@ -18,5 +19,6 @@ export declare namespace formats {
export { GraphQLVisitor as graphql };
export { CSharpVisitor as csharp };
export { ODataVisitor as odata };
export { MermaidVisitor as mermaid };
}
export { AbstractPlugin, GoLangVisitor, JSONSchemaVisitor, XmlSchemaVisitor, PlantUMLVisitor, TypescriptVisitor, JavaVisitor, GraphQLVisitor, CSharpVisitor, ODataVisitor };
export { AbstractPlugin, GoLangVisitor, JSONSchemaVisitor, XmlSchemaVisitor, PlantUMLVisitor, TypescriptVisitor, JavaVisitor, GraphQLVisitor, CSharpVisitor, ODataVisitor, MermaidVisitor };
Loading

0 comments on commit 2a76a0c

Please sign in to comment.