Updated as per PR comments

This commit is contained in:
saurabhg772244
2025-01-22 17:53:51 +05:30
parent bde653b1c2
commit 1c45df4567
3 changed files with 122 additions and 171 deletions

View File

@@ -25,48 +25,39 @@ import {
STMT_STYLEDEF, STMT_STYLEDEF,
} from './stateCommon.js'; } from './stateCommon.js';
export class StateDB { const START_NODE = '[*]';
/** const START_TYPE = 'start';
* @private const END_NODE = START_NODE;
* @type {string} const END_TYPE = 'end';
*/
#START_NODE = '[*]';
/**
* @private
* @type {string}
*/
#START_TYPE = 'start';
/**
* @private
* @type {string}
*/
#END_NODE = this.#START_NODE;
/**
* @private
* @type {string}
*/
#END_TYPE = 'end';
/**
* @private
* @type {string}
*/
#COLOR_KEYWORD = 'color';
/**
* @private
* @type {string}
*/
#FILL_KEYWORD = 'fill';
/**
* @private
* @type {string}
*/
#BG_FILL = 'bgFill';
/**
* @private
* @type {string}
*/
#STYLECLASS_SEP = ',';
const COLOR_KEYWORD = 'color';
const FILL_KEYWORD = 'fill';
const BG_FILL = 'bgFill';
const STYLECLASS_SEP = ',';
/**
* Returns a new list of classes.
* In the future, this can be replaced with a class common to all diagrams.
* ClassDef information = { id: id, styles: [], textStyles: [] }
*
* @returns {Map<string, any>}
*/
function newClassesList() {
return new Map();
}
const newDoc = () => {
return {
/** @type {{ id1: string, id2: string, relationTitle: string }[]} */
relations: [],
states: new Map(),
documents: {},
};
};
const clone = (o) => JSON.parse(JSON.stringify(o));
export class StateDB {
constructor() { constructor() {
this.clear(); this.clear();
@@ -78,111 +69,71 @@ export class StateDB {
} }
/** /**
* Returns a new list of classes.
* In the future, this can be replaced with a class common to all diagrams.
* ClassDef information = { id: id, styles: [], textStyles: [] }
*
* @returns {Map<string, any>}
* @private * @private
* @type {Array}
*/ */
#newClassesList() { nodes = [];
return new Map();
}
/** /**
* @private * @private
* @type {Array} * @type {Array}
*/ */
#nodes = []; edges = [];
/**
* @private
* @type {Array}
*/
#edges = [];
/** /**
* @private * @private
* @type {string} * @type {string}
*/ */
#direction = DEFAULT_DIAGRAM_DIRECTION; direction = DEFAULT_DIAGRAM_DIRECTION;
/** /**
* @private * @private
* @type {Array} * @type {Array}
*/ */
#rootDoc = []; rootDoc = [];
/** /**
* @private * @private
* @type {Map<string, any>} * @type {Map<string, any>}
*/ */
#classes = this.#newClassesList(); // style classes defined by a classDef classes = newClassesList(); // style classes defined by a classDef
// --------------------------------------
/**
* @private
* @returns {Object}
*/
#newDoc() {
return {
/** @type {{ id1: string, id2: string, relationTitle: string }[]} */
relations: [],
states: new Map(),
documents: {},
};
}
/** /**
* @private * @private
* @type {Object} * @type {Object}
*/ */
#documents = { documents = {
root: this.#newDoc(), root: newDoc(),
}; };
/** /**
* @private * @private
* @type {Object} * @type {Object}
*/ */
#currentDocument = this.#documents.root; currentDocument = this.documents.root;
/** /**
* @private * @private
* @type {number} * @type {number}
*/ */
#startEndCount = 0; startEndCount = 0;
/** /**
* @private * @private
* @type {number} * @type {number}
*/ */
#dividerCnt = 0; dividerCnt = 0;
lineType = { static relationType = {
LINE: 0,
DOTTED_LINE: 1,
};
relationType = {
AGGREGATION: 0, AGGREGATION: 0,
EXTENSION: 1, EXTENSION: 1,
COMPOSITION: 2, COMPOSITION: 2,
DEPENDENCY: 3, DEPENDENCY: 3,
}; };
/**
* @private
* @param {Object} o
*/
#clone(o) {
return JSON.parse(JSON.stringify(o));
}
setRootDoc(o) { setRootDoc(o) {
log.info('Setting root doc', o); log.info('Setting root doc', o);
// rootDoc = { id: 'root', doc: o }; // rootDoc = { id: 'root', doc: o };
this.#rootDoc = o; this.rootDoc = o;
} }
getRootDoc() { getRootDoc() {
return this.#rootDoc; return this.rootDoc;
} }
/** /**
@@ -191,10 +142,10 @@ export class StateDB {
* @param {Object} node * @param {Object} node
* @param {boolean} first * @param {boolean} first
*/ */
#docTranslator(parent, node, first) { docTranslator(parent, node, first) {
if (node.stmt === STMT_RELATION) { if (node.stmt === STMT_RELATION) {
this.#docTranslator(parent, node.state1, true); this.docTranslator(parent, node.state1, true);
this.#docTranslator(parent, node.state2, false); this.docTranslator(parent, node.state2, false);
} else { } else {
if (node.stmt === STMT_STATE) { if (node.stmt === STMT_STATE) {
if (node.id === '[*]') { if (node.id === '[*]') {
@@ -213,8 +164,8 @@ export class StateDB {
let i; let i;
for (i = 0; i < node.doc.length; i++) { for (i = 0; i < node.doc.length; i++) {
if (node.doc[i].type === DIVIDER_TYPE) { if (node.doc[i].type === DIVIDER_TYPE) {
const newNode = this.#clone(node.doc[i]); const newNode = clone(node.doc[i]);
newNode.doc = this.#clone(currentDoc); newNode.doc = clone(currentDoc);
doc.push(newNode); doc.push(newNode);
currentDoc = []; currentDoc = [];
} else { } else {
@@ -228,19 +179,19 @@ export class StateDB {
stmt: STMT_STATE, stmt: STMT_STATE,
id: generateId(), id: generateId(),
type: 'divider', type: 'divider',
doc: this.#clone(currentDoc), doc: clone(currentDoc),
}; };
doc.push(this.#clone(newNode)); doc.push(clone(newNode));
node.doc = doc; node.doc = doc;
} }
node.doc.forEach((docNode) => this.#docTranslator(node, docNode, true)); node.doc.forEach((docNode) => this.docTranslator(node, docNode, true));
} }
} }
} }
getRootDocV2() { getRootDocV2() {
this.#docTranslator({ id: 'root' }, { id: 'root', doc: this.#rootDoc }, true); this.docTranslator({ id: 'root' }, { id: 'root', doc: this.rootDoc }, true);
return { id: 'root', doc: this.#rootDoc }; return { id: 'root', doc: this.rootDoc };
// Here // Here
} }
@@ -323,13 +274,13 @@ export class StateDB {
undefined, undefined,
this.getRootDocV2(), this.getRootDocV2(),
diagramStates, diagramStates,
this.#nodes, this.nodes,
this.#edges, this.edges,
true, true,
look, look,
this.#classes this.classes
); );
this.#nodes.forEach((node) => { this.nodes.forEach((node) => {
if (Array.isArray(node.label)) { if (Array.isArray(node.label)) {
// add the rest as description // add the rest as description
node.description = node.label.slice(1); node.description = node.label.slice(1);
@@ -370,9 +321,9 @@ export class StateDB {
) { ) {
const trimmedId = id?.trim(); const trimmedId = id?.trim();
// add the state if needed // add the state if needed
if (!this.#currentDocument.states.has(trimmedId)) { if (!this.currentDocument.states.has(trimmedId)) {
log.info('Adding state ', trimmedId, descr); log.info('Adding state ', trimmedId, descr);
this.#currentDocument.states.set(trimmedId, { this.currentDocument.states.set(trimmedId, {
id: trimmedId, id: trimmedId,
descriptions: [], descriptions: [],
type, type,
@@ -383,11 +334,11 @@ export class StateDB {
textStyles: [], textStyles: [],
}); });
} else { } else {
if (!this.#currentDocument.states.get(trimmedId).doc) { if (!this.currentDocument.states.get(trimmedId).doc) {
this.#currentDocument.states.get(trimmedId).doc = doc; this.currentDocument.states.get(trimmedId).doc = doc;
} }
if (!this.#currentDocument.states.get(trimmedId).type) { if (!this.currentDocument.states.get(trimmedId).type) {
this.#currentDocument.states.get(trimmedId).type = type; this.currentDocument.states.get(trimmedId).type = type;
} }
} }
@@ -403,7 +354,7 @@ export class StateDB {
} }
if (note) { if (note) {
const doc2 = this.#currentDocument.states.get(trimmedId); const doc2 = this.currentDocument.states.get(trimmedId);
doc2.note = note; doc2.note = note;
doc2.note.text = common.sanitizeText(doc2.note.text, getConfig()); doc2.note.text = common.sanitizeText(doc2.note.text, getConfig());
} }
@@ -428,32 +379,32 @@ export class StateDB {
} }
clear(saveCommon) { clear(saveCommon) {
this.#nodes = []; this.nodes = [];
this.#edges = []; this.edges = [];
this.#documents = { this.documents = {
root: this.#newDoc(), root: newDoc(),
}; };
this.#currentDocument = this.#documents.root; this.currentDocument = this.documents.root;
// number of start and end nodes; used to construct ids // number of start and end nodes; used to construct ids
this.#startEndCount = 0; this.startEndCount = 0;
this.classes = this.#newClassesList(); this.classes = newClassesList();
if (!saveCommon) { if (!saveCommon) {
commonClear(); commonClear();
} }
} }
getState(id) { getState(id) {
return this.#currentDocument.states.get(id); return this.currentDocument.states.get(id);
} }
getStates() { getStates() {
return this.#currentDocument.states; return this.currentDocument.states;
} }
logDocuments() { logDocuments() {
log.info('Documents = ', this.#documents); log.info('Documents = ', this.documents);
} }
getRelations() { getRelations() {
return this.#currentDocument.relations; return this.currentDocument.relations;
} }
/** /**
@@ -465,11 +416,11 @@ export class StateDB {
* @returns {string} - the id (original or constructed) * @returns {string} - the id (original or constructed)
* @private * @private
*/ */
#startIdIfNeeded(id = '') { startIdIfNeeded(id = '') {
let fixedId = id; let fixedId = id;
if (id === this.#START_NODE) { if (id === START_NODE) {
this.#startEndCount++; this.startEndCount++;
fixedId = `${this.#START_TYPE}${this.#startEndCount}`; fixedId = `${START_TYPE}${this.startEndCount}`;
} }
return fixedId; return fixedId;
} }
@@ -483,8 +434,8 @@ export class StateDB {
* @returns {string} - the type that should be used * @returns {string} - the type that should be used
* @private * @private
*/ */
#startTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) { startTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
return id === this.#START_NODE ? this.#START_TYPE : type; return id === START_NODE ? START_TYPE : type;
} }
/** /**
@@ -496,11 +447,11 @@ export class StateDB {
* @returns {string} - the id (original or constructed) * @returns {string} - the id (original or constructed)
* @private * @private
*/ */
#endIdIfNeeded(id = '') { endIdIfNeeded(id = '') {
let fixedId = id; let fixedId = id;
if (id === this.#END_NODE) { if (id === END_NODE) {
this.#startEndCount++; this.startEndCount++;
fixedId = `${this.#END_TYPE}${this.#startEndCount}`; fixedId = `${END_TYPE}${this.startEndCount}`;
} }
return fixedId; return fixedId;
} }
@@ -514,8 +465,8 @@ export class StateDB {
* @returns {string} - the type that should be used * @returns {string} - the type that should be used
* @private * @private
*/ */
#endTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) { endTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
return id === this.#END_NODE ? this.#END_TYPE : type; return id === END_NODE ? END_TYPE : type;
} }
/** /**
@@ -525,10 +476,10 @@ export class StateDB {
* @param relationTitle * @param relationTitle
*/ */
addRelationObjs(item1, item2, relationTitle) { addRelationObjs(item1, item2, relationTitle) {
let id1 = this.#startIdIfNeeded(item1.id.trim()); let id1 = this.startIdIfNeeded(item1.id.trim());
let type1 = this.#startTypeIfNeeded(item1.id.trim(), item1.type); let type1 = this.startTypeIfNeeded(item1.id.trim(), item1.type);
let id2 = this.#startIdIfNeeded(item2.id.trim()); let id2 = this.startIdIfNeeded(item2.id.trim());
let type2 = this.#startTypeIfNeeded(item2.id.trim(), item2.type); let type2 = this.startTypeIfNeeded(item2.id.trim(), item2.type);
this.addState( this.addState(
id1, id1,
@@ -551,7 +502,7 @@ export class StateDB {
item2.textStyles item2.textStyles
); );
this.#currentDocument.relations.push({ this.currentDocument.relations.push({
id1, id1,
id2, id2,
relationTitle: common.sanitizeText(relationTitle, getConfig()), relationTitle: common.sanitizeText(relationTitle, getConfig()),
@@ -569,14 +520,14 @@ export class StateDB {
if (typeof item1 === 'object') { if (typeof item1 === 'object') {
this.addRelationObjs(item1, item2, title); this.addRelationObjs(item1, item2, title);
} else { } else {
const id1 = this.#startIdIfNeeded(item1.trim()); const id1 = this.startIdIfNeeded(item1.trim());
const type1 = this.#startTypeIfNeeded(item1); const type1 = this.startTypeIfNeeded(item1);
const id2 = this.#endIdIfNeeded(item2.trim()); const id2 = this.endIdIfNeeded(item2.trim());
const type2 = this.#endTypeIfNeeded(item2); const type2 = this.endTypeIfNeeded(item2);
this.addState(id1, type1); this.addState(id1, type1);
this.addState(id2, type2); this.addState(id2, type2);
this.#currentDocument.relations.push({ this.currentDocument.relations.push({
id1, id1,
id2, id2,
title: common.sanitizeText(title, getConfig()), title: common.sanitizeText(title, getConfig()),
@@ -585,7 +536,7 @@ export class StateDB {
} }
addDescription(id, descr) { addDescription(id, descr) {
const theState = this.#currentDocument.states.get(id); const theState = this.currentDocument.states.get(id);
const _descr = descr.startsWith(':') ? descr.replace(':', '').trim() : descr; const _descr = descr.startsWith(':') ? descr.replace(':', '').trim() : descr;
theState.descriptions.push(common.sanitizeText(_descr, getConfig())); theState.descriptions.push(common.sanitizeText(_descr, getConfig()));
} }
@@ -599,8 +550,8 @@ export class StateDB {
} }
getDividerId() { getDividerId() {
this.#dividerCnt++; this.dividerCnt++;
return 'divider-id-' + this.#dividerCnt; return 'divider-id-' + this.dividerCnt;
} }
/** /**
@@ -612,19 +563,19 @@ export class StateDB {
*/ */
addStyleClass(id, styleAttributes = '') { addStyleClass(id, styleAttributes = '') {
// create a new style class object with this id // create a new style class object with this id
if (!this.#classes.has(id)) { if (!this.classes.has(id)) {
this.#classes.set(id, { id: id, styles: [], textStyles: [] }); // This is a classDef this.classes.set(id, { id: id, styles: [], textStyles: [] }); // This is a classDef
} }
const foundClass = this.#classes.get(id); const foundClass = this.classes.get(id);
if (styleAttributes !== undefined && styleAttributes !== null) { if (styleAttributes !== undefined && styleAttributes !== null) {
styleAttributes.split(this.#STYLECLASS_SEP).forEach((attrib) => { styleAttributes.split(STYLECLASS_SEP).forEach((attrib) => {
// remove any trailing ; // remove any trailing ;
const fixedAttrib = attrib.replace(/([^;]*);/, '$1').trim(); const fixedAttrib = attrib.replace(/([^;]*);/, '$1').trim();
// replace some style keywords // replace some style keywords
if (RegExp(this.#COLOR_KEYWORD).exec(attrib)) { if (RegExp(COLOR_KEYWORD).exec(attrib)) {
const newStyle1 = fixedAttrib.replace(this.#FILL_KEYWORD, this.#BG_FILL); const newStyle1 = fixedAttrib.replace(FILL_KEYWORD, BG_FILL);
const newStyle2 = newStyle1.replace(this.#COLOR_KEYWORD, this.#FILL_KEYWORD); const newStyle2 = newStyle1.replace(COLOR_KEYWORD, FILL_KEYWORD);
foundClass.textStyles.push(newStyle2); foundClass.textStyles.push(newStyle2);
} }
foundClass.styles.push(fixedAttrib); foundClass.styles.push(fixedAttrib);
@@ -637,7 +588,7 @@ export class StateDB {
* @returns {{} | any | classes} * @returns {{} | any | classes}
*/ */
getClasses() { getClasses() {
return this.#classes; return this.classes;
} }
/** /**
@@ -691,10 +642,10 @@ export class StateDB {
} }
getDirection() { getDirection() {
return this.#direction; return this.direction;
} }
setDirection(dir) { setDirection(dir) {
this.#direction = dir; this.direction = dir;
} }
trimColon(str) { trimColon(str) {
@@ -704,8 +655,8 @@ export class StateDB {
getData() { getData() {
const config = getConfig(); const config = getConfig();
return { return {
nodes: this.#nodes, nodes: this.nodes,
edges: this.#edges, edges: this.edges,
other: {}, other: {},
config, config,
direction: getDir(this.getRootDocV2()), direction: getDir(this.getRootDocV2()),

View File

@@ -81,7 +81,7 @@ describe('state db class', () => {
beforeEach(() => { beforeEach(() => {
stateDb = new StateDB(); stateDb = new StateDB();
}); });
// This is to ensure that functions used in flow JISON are exposed as function from FlowDb // This is to ensure that functions used in state JISON are exposed as function from StateDb
it('should have functions used in flow JISON as own property', () => { it('should have functions used in flow JISON as own property', () => {
const functionsUsedInParser = [ const functionsUsedInParser = [
'setRootDoc', 'setRootDoc',

View File

@@ -836,7 +836,7 @@ graph TD;A--x|text including URL space|B;`)
}); });
it('should not modify db when rendering different diagrams', async () => { it('should not modify db when rendering different diagrams', async () => {
const classDiagram1 = await mermaidAPI.getDiagramFromText( const stateDiagram1 = await mermaidAPI.getDiagramFromText(
`stateDiagram `stateDiagram
direction LR direction LR
[*] --> Still [*] --> Still
@@ -846,7 +846,7 @@ graph TD;A--x|text including URL space|B;`)
Moving --> Crash Moving --> Crash
Crash --> [*]` Crash --> [*]`
); );
const classDiagram2 = await mermaidAPI.getDiagramFromText( const stateDiagram2 = await mermaidAPI.getDiagramFromText(
`stateDiagram `stateDiagram
direction TB direction TB
[*] --> Still [*] --> Still
@@ -856,10 +856,10 @@ graph TD;A--x|text including URL space|B;`)
Moving --> Crash Moving --> Crash
Crash --> [*]` Crash --> [*]`
); );
expect(classDiagram1.db).not.toBe(classDiagram2.db); expect(stateDiagram1.db).not.toBe(stateDiagram2.db);
assert(classDiagram1.db instanceof StateDB); assert(stateDiagram1.db instanceof StateDB);
assert(classDiagram2.db instanceof StateDB); assert(stateDiagram2.db instanceof StateDB);
expect(classDiagram1.db.getDirection()).not.toEqual(classDiagram2.db.getDirection()); expect(stateDiagram1.db.getDirection()).not.toEqual(stateDiagram2.db.getDirection());
}); });
}); });