mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-03 04:14:15 +01:00
Update requirementDb to class to encapsulate data
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import type { DiagramDB } from '../../diagram-api/types.js';
|
||||
import { log } from '../../logger.js';
|
||||
import type { Node, Edge } from '../../rendering-util/types.js';
|
||||
|
||||
@@ -22,320 +23,327 @@ import type {
|
||||
VerifyType,
|
||||
} from './types.js';
|
||||
|
||||
const RequirementType = {
|
||||
REQUIREMENT: 'Requirement',
|
||||
FUNCTIONAL_REQUIREMENT: 'Functional Requirement',
|
||||
INTERFACE_REQUIREMENT: 'Interface Requirement',
|
||||
PERFORMANCE_REQUIREMENT: 'Performance Requirement',
|
||||
PHYSICAL_REQUIREMENT: 'Physical Requirement',
|
||||
DESIGN_CONSTRAINT: 'Design Constraint',
|
||||
};
|
||||
export class RequirementDB implements DiagramDB {
|
||||
private relations: Relation[] = [];
|
||||
private latestRequirement: Requirement = this.getInitialRequirement();
|
||||
private requirements = new Map<string, Requirement>();
|
||||
private latestElement: Element = this.getInitialElement();
|
||||
private elements = new Map<string, Element>();
|
||||
private classes = new Map<string, RequirementClass>();
|
||||
private direction = 'TB';
|
||||
|
||||
const RiskLevel = {
|
||||
LOW_RISK: 'Low',
|
||||
MED_RISK: 'Medium',
|
||||
HIGH_RISK: 'High',
|
||||
};
|
||||
private RequirementType = {
|
||||
REQUIREMENT: 'Requirement',
|
||||
FUNCTIONAL_REQUIREMENT: 'Functional Requirement',
|
||||
INTERFACE_REQUIREMENT: 'Interface Requirement',
|
||||
PERFORMANCE_REQUIREMENT: 'Performance Requirement',
|
||||
PHYSICAL_REQUIREMENT: 'Physical Requirement',
|
||||
DESIGN_CONSTRAINT: 'Design Constraint',
|
||||
};
|
||||
|
||||
const VerifyType = {
|
||||
VERIFY_ANALYSIS: 'Analysis',
|
||||
VERIFY_DEMONSTRATION: 'Demonstration',
|
||||
VERIFY_INSPECTION: 'Inspection',
|
||||
VERIFY_TEST: 'Test',
|
||||
};
|
||||
private RiskLevel = {
|
||||
LOW_RISK: 'Low',
|
||||
MED_RISK: 'Medium',
|
||||
HIGH_RISK: 'High',
|
||||
};
|
||||
|
||||
const Relationships = {
|
||||
CONTAINS: 'contains',
|
||||
COPIES: 'copies',
|
||||
DERIVES: 'derives',
|
||||
SATISFIES: 'satisfies',
|
||||
VERIFIES: 'verifies',
|
||||
REFINES: 'refines',
|
||||
TRACES: 'traces',
|
||||
};
|
||||
private VerifyType = {
|
||||
VERIFY_ANALYSIS: 'Analysis',
|
||||
VERIFY_DEMONSTRATION: 'Demonstration',
|
||||
VERIFY_INSPECTION: 'Inspection',
|
||||
VERIFY_TEST: 'Test',
|
||||
};
|
||||
|
||||
let direction = 'TB';
|
||||
const getDirection = () => direction;
|
||||
const setDirection = (dir: string) => {
|
||||
direction = dir;
|
||||
};
|
||||
private Relationships = {
|
||||
CONTAINS: 'contains',
|
||||
COPIES: 'copies',
|
||||
DERIVES: 'derives',
|
||||
SATISFIES: 'satisfies',
|
||||
VERIFIES: 'verifies',
|
||||
REFINES: 'refines',
|
||||
TRACES: 'traces',
|
||||
};
|
||||
|
||||
const getInitialRequirement = (): Requirement => ({
|
||||
requirementId: '',
|
||||
text: '',
|
||||
risk: '' as RiskLevel,
|
||||
verifyMethod: '' as VerifyType,
|
||||
name: '',
|
||||
type: '' as RequirementType,
|
||||
cssStyles: [],
|
||||
classes: ['default'],
|
||||
});
|
||||
constructor() {
|
||||
this.clear();
|
||||
|
||||
const getInitialElement = (): Element => ({
|
||||
name: '',
|
||||
type: '',
|
||||
docRef: '',
|
||||
cssStyles: [],
|
||||
classes: ['default'],
|
||||
});
|
||||
// Needed for JISON since it only supports direct properties
|
||||
this.setDirection = this.setDirection.bind(this);
|
||||
this.addRequirement = this.addRequirement.bind(this);
|
||||
this.setNewReqId = this.setNewReqId.bind(this);
|
||||
this.setNewReqRisk = this.setNewReqRisk.bind(this);
|
||||
this.setNewReqText = this.setNewReqText.bind(this);
|
||||
this.setNewReqVerifyMethod = this.setNewReqVerifyMethod.bind(this);
|
||||
this.addElement = this.addElement.bind(this);
|
||||
this.setNewElementType = this.setNewElementType.bind(this);
|
||||
this.setNewElementDocRef = this.setNewElementDocRef.bind(this);
|
||||
this.addRelationship = this.addRelationship.bind(this);
|
||||
this.setCssStyle = this.setCssStyle.bind(this);
|
||||
this.setClass = this.setClass.bind(this);
|
||||
this.defineClass = this.defineClass.bind(this);
|
||||
this.setAccTitle = this.setAccTitle.bind(this);
|
||||
this.setAccDescription = this.setAccDescription.bind(this);
|
||||
}
|
||||
|
||||
// Update initial declarations
|
||||
let relations: Relation[] = [];
|
||||
let latestRequirement: Requirement = getInitialRequirement();
|
||||
let requirements = new Map<string, Requirement>();
|
||||
let latestElement: Element = getInitialElement();
|
||||
let elements = new Map<string, Element>();
|
||||
let classes = new Map<string, RequirementClass>();
|
||||
public getDirection() {
|
||||
return this.direction;
|
||||
}
|
||||
public setDirection(dir: string) {
|
||||
this.direction = dir;
|
||||
}
|
||||
|
||||
// Add reset functions
|
||||
const resetLatestRequirement = () => {
|
||||
latestRequirement = getInitialRequirement();
|
||||
};
|
||||
private resetLatestRequirement() {
|
||||
this.latestRequirement = this.getInitialRequirement();
|
||||
}
|
||||
|
||||
const resetLatestElement = () => {
|
||||
latestElement = getInitialElement();
|
||||
};
|
||||
private resetLatestElement() {
|
||||
this.latestElement = this.getInitialElement();
|
||||
}
|
||||
|
||||
const addRequirement = (name: string, type: RequirementType) => {
|
||||
if (!requirements.has(name)) {
|
||||
requirements.set(name, {
|
||||
name,
|
||||
type,
|
||||
requirementId: latestRequirement.requirementId,
|
||||
text: latestRequirement.text,
|
||||
risk: latestRequirement.risk,
|
||||
verifyMethod: latestRequirement.verifyMethod,
|
||||
private getInitialRequirement(): Requirement {
|
||||
return {
|
||||
requirementId: '',
|
||||
text: '',
|
||||
risk: '' as RiskLevel,
|
||||
verifyMethod: '' as VerifyType,
|
||||
name: '',
|
||||
type: '' as RequirementType,
|
||||
cssStyles: [],
|
||||
classes: ['default'],
|
||||
});
|
||||
};
|
||||
}
|
||||
resetLatestRequirement();
|
||||
|
||||
return requirements.get(name);
|
||||
};
|
||||
|
||||
const getRequirements = () => requirements;
|
||||
|
||||
const setNewReqId = (id: string) => {
|
||||
if (latestRequirement !== undefined) {
|
||||
latestRequirement.requirementId = id;
|
||||
}
|
||||
};
|
||||
|
||||
const setNewReqText = (text: string) => {
|
||||
if (latestRequirement !== undefined) {
|
||||
latestRequirement.text = text;
|
||||
}
|
||||
};
|
||||
|
||||
const setNewReqRisk = (risk: RiskLevel) => {
|
||||
if (latestRequirement !== undefined) {
|
||||
latestRequirement.risk = risk;
|
||||
}
|
||||
};
|
||||
|
||||
const setNewReqVerifyMethod = (verifyMethod: VerifyType) => {
|
||||
if (latestRequirement !== undefined) {
|
||||
latestRequirement.verifyMethod = verifyMethod;
|
||||
}
|
||||
};
|
||||
|
||||
const addElement = (name: string) => {
|
||||
if (!elements.has(name)) {
|
||||
elements.set(name, {
|
||||
name,
|
||||
type: latestElement.type,
|
||||
docRef: latestElement.docRef,
|
||||
private getInitialElement(): Element {
|
||||
return {
|
||||
name: '',
|
||||
type: '',
|
||||
docRef: '',
|
||||
cssStyles: [],
|
||||
classes: ['default'],
|
||||
});
|
||||
log.info('Added new element: ', name);
|
||||
};
|
||||
}
|
||||
resetLatestElement();
|
||||
|
||||
return elements.get(name);
|
||||
};
|
||||
|
||||
const getElements = () => elements;
|
||||
|
||||
const setNewElementType = (type: string) => {
|
||||
if (latestElement !== undefined) {
|
||||
latestElement.type = type;
|
||||
}
|
||||
};
|
||||
|
||||
const setNewElementDocRef = (docRef: string) => {
|
||||
if (latestElement !== undefined) {
|
||||
latestElement.docRef = docRef;
|
||||
}
|
||||
};
|
||||
|
||||
const addRelationship = (type: RelationshipType, src: string, dst: string) => {
|
||||
relations.push({
|
||||
type,
|
||||
src,
|
||||
dst,
|
||||
});
|
||||
};
|
||||
|
||||
const getRelationships = () => relations;
|
||||
|
||||
const clear = () => {
|
||||
relations = [];
|
||||
resetLatestRequirement();
|
||||
requirements = new Map();
|
||||
resetLatestElement();
|
||||
elements = new Map();
|
||||
classes = new Map();
|
||||
commonClear();
|
||||
};
|
||||
|
||||
export const setCssStyle = function (ids: string[], styles: string[]) {
|
||||
for (const id of ids) {
|
||||
const node = requirements.get(id) ?? elements.get(id);
|
||||
if (!styles || !node) {
|
||||
return;
|
||||
}
|
||||
for (const s of styles) {
|
||||
if (s.includes(',')) {
|
||||
node.cssStyles.push(...s.split(','));
|
||||
} else {
|
||||
node.cssStyles.push(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const setClass = function (ids: string[], classNames: string[]) {
|
||||
for (const id of ids) {
|
||||
const node = requirements.get(id) ?? elements.get(id);
|
||||
if (node) {
|
||||
for (const _class of classNames) {
|
||||
node.classes.push(_class);
|
||||
const styles = classes.get(_class)?.styles;
|
||||
if (styles) {
|
||||
node.cssStyles.push(...styles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const defineClass = function (ids: string[], style: string[]) {
|
||||
for (const id of ids) {
|
||||
let styleClass = classes.get(id);
|
||||
if (styleClass === undefined) {
|
||||
styleClass = { id, styles: [], textStyles: [] };
|
||||
classes.set(id, styleClass);
|
||||
}
|
||||
|
||||
if (style) {
|
||||
style.forEach(function (s) {
|
||||
if (/color/.exec(s)) {
|
||||
const newStyle = s.replace('fill', 'bgFill'); // .replace('color', 'fill');
|
||||
styleClass.textStyles.push(newStyle);
|
||||
}
|
||||
styleClass.styles.push(s);
|
||||
public addRequirement(name: string, type: RequirementType) {
|
||||
if (!this.requirements.has(name)) {
|
||||
this.requirements.set(name, {
|
||||
name,
|
||||
type,
|
||||
requirementId: this.latestRequirement.requirementId,
|
||||
text: this.latestRequirement.text,
|
||||
risk: this.latestRequirement.risk,
|
||||
verifyMethod: this.latestRequirement.verifyMethod,
|
||||
cssStyles: [],
|
||||
classes: ['default'],
|
||||
});
|
||||
}
|
||||
this.resetLatestRequirement();
|
||||
|
||||
requirements.forEach((value) => {
|
||||
if (value.classes.includes(id)) {
|
||||
value.cssStyles.push(...style.flatMap((s) => s.split(',')));
|
||||
}
|
||||
});
|
||||
elements.forEach((value) => {
|
||||
if (value.classes.includes(id)) {
|
||||
value.cssStyles.push(...style.flatMap((s) => s.split(',')));
|
||||
}
|
||||
return this.requirements.get(name);
|
||||
}
|
||||
|
||||
public getRequirements() {
|
||||
return this.requirements;
|
||||
}
|
||||
|
||||
public setNewReqId(id: string) {
|
||||
if (this.latestRequirement !== undefined) {
|
||||
this.latestRequirement.requirementId = id;
|
||||
}
|
||||
}
|
||||
|
||||
public setNewReqText(text: string) {
|
||||
if (this.latestRequirement !== undefined) {
|
||||
this.latestRequirement.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
public setNewReqRisk(risk: RiskLevel) {
|
||||
if (this.latestRequirement !== undefined) {
|
||||
this.latestRequirement.risk = risk;
|
||||
}
|
||||
}
|
||||
|
||||
public setNewReqVerifyMethod(verifyMethod: VerifyType) {
|
||||
if (this.latestRequirement !== undefined) {
|
||||
this.latestRequirement.verifyMethod = verifyMethod;
|
||||
}
|
||||
}
|
||||
|
||||
public addElement(name: string) {
|
||||
if (!this.elements.has(name)) {
|
||||
this.elements.set(name, {
|
||||
name,
|
||||
type: this.latestElement.type,
|
||||
docRef: this.latestElement.docRef,
|
||||
cssStyles: [],
|
||||
classes: ['default'],
|
||||
});
|
||||
log.info('Added new element: ', name);
|
||||
}
|
||||
this.resetLatestElement();
|
||||
|
||||
return this.elements.get(name);
|
||||
}
|
||||
|
||||
public getElements() {
|
||||
return this.elements;
|
||||
}
|
||||
|
||||
public setNewElementType(type: string) {
|
||||
if (this.latestElement !== undefined) {
|
||||
this.latestElement.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public setNewElementDocRef(docRef: string) {
|
||||
if (this.latestElement !== undefined) {
|
||||
this.latestElement.docRef = docRef;
|
||||
}
|
||||
}
|
||||
|
||||
public addRelationship(type: RelationshipType, src: string, dst: string) {
|
||||
this.relations.push({
|
||||
type,
|
||||
src,
|
||||
dst,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const getClasses = () => {
|
||||
return classes;
|
||||
};
|
||||
|
||||
const getData = () => {
|
||||
const config = getConfig();
|
||||
const nodes: Node[] = [];
|
||||
const edges: Edge[] = [];
|
||||
for (const requirement of requirements.values()) {
|
||||
const node = requirement as unknown as Node;
|
||||
node.id = requirement.name;
|
||||
node.cssStyles = requirement.cssStyles;
|
||||
node.cssClasses = requirement.classes.join(' ');
|
||||
node.shape = 'requirementBox';
|
||||
node.look = config.look;
|
||||
nodes.push(node);
|
||||
public getRelationships() {
|
||||
return this.relations;
|
||||
}
|
||||
|
||||
for (const element of elements.values()) {
|
||||
const node = element as unknown as Node;
|
||||
node.shape = 'requirementBox';
|
||||
node.look = config.look;
|
||||
node.id = element.name;
|
||||
node.cssStyles = element.cssStyles;
|
||||
node.cssClasses = element.classes.join(' ');
|
||||
|
||||
nodes.push(node);
|
||||
public clear() {
|
||||
this.relations = [];
|
||||
this.resetLatestRequirement();
|
||||
this.requirements = new Map();
|
||||
this.resetLatestElement();
|
||||
this.elements = new Map();
|
||||
this.classes = new Map();
|
||||
commonClear();
|
||||
}
|
||||
|
||||
for (const relation of relations) {
|
||||
let counter = 0;
|
||||
const isContains = relation.type === Relationships.CONTAINS;
|
||||
const edge: Edge = {
|
||||
id: `${relation.src}-${relation.dst}-${counter}`,
|
||||
start: requirements.get(relation.src)?.name ?? elements.get(relation.src)?.name,
|
||||
end: requirements.get(relation.dst)?.name ?? elements.get(relation.dst)?.name,
|
||||
label: `<<${relation.type}>>`,
|
||||
classes: 'relationshipLine',
|
||||
style: ['fill:none', isContains ? '' : 'stroke-dasharray: 10,7'],
|
||||
labelpos: 'c',
|
||||
thickness: 'normal',
|
||||
type: 'normal',
|
||||
pattern: isContains ? 'normal' : 'dashed',
|
||||
arrowTypeEnd: isContains ? 'requirement_contains' : 'requirement_arrow',
|
||||
look: config.look,
|
||||
};
|
||||
|
||||
edges.push(edge);
|
||||
counter++;
|
||||
public setCssStyle(ids: string[], styles: string[]) {
|
||||
for (const id of ids) {
|
||||
const node = this.requirements.get(id) ?? this.elements.get(id);
|
||||
if (!styles || !node) {
|
||||
return;
|
||||
}
|
||||
for (const s of styles) {
|
||||
if (s.includes(',')) {
|
||||
node.cssStyles.push(...s.split(','));
|
||||
} else {
|
||||
node.cssStyles.push(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { nodes, edges, other: {}, config, direction: getDirection() };
|
||||
};
|
||||
public setClass(ids: string[], classNames: string[]) {
|
||||
for (const id of ids) {
|
||||
const node = this.requirements.get(id) ?? this.elements.get(id);
|
||||
if (node) {
|
||||
for (const _class of classNames) {
|
||||
node.classes.push(_class);
|
||||
const styles = this.classes.get(_class)?.styles;
|
||||
if (styles) {
|
||||
node.cssStyles.push(...styles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
Relationships,
|
||||
RequirementType,
|
||||
RiskLevel,
|
||||
VerifyType,
|
||||
getConfig: () => getConfig().requirement,
|
||||
addRequirement,
|
||||
getRequirements,
|
||||
setNewReqId,
|
||||
setNewReqText,
|
||||
setNewReqRisk,
|
||||
setNewReqVerifyMethod,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setAccDescription,
|
||||
getAccDescription,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
getDirection,
|
||||
setDirection,
|
||||
addElement,
|
||||
getElements,
|
||||
setNewElementType,
|
||||
setNewElementDocRef,
|
||||
addRelationship,
|
||||
getRelationships,
|
||||
clear,
|
||||
setCssStyle,
|
||||
setClass,
|
||||
defineClass,
|
||||
getClasses,
|
||||
getData,
|
||||
};
|
||||
public defineClass(ids: string[], style: string[]) {
|
||||
for (const id of ids) {
|
||||
let styleClass = this.classes.get(id);
|
||||
if (styleClass === undefined) {
|
||||
styleClass = { id, styles: [], textStyles: [] };
|
||||
this.classes.set(id, styleClass);
|
||||
}
|
||||
|
||||
if (style) {
|
||||
style.forEach(function (s) {
|
||||
if (/color/.exec(s)) {
|
||||
const newStyle = s.replace('fill', 'bgFill'); // .replace('color', 'fill');
|
||||
styleClass.textStyles.push(newStyle);
|
||||
}
|
||||
styleClass.styles.push(s);
|
||||
});
|
||||
}
|
||||
|
||||
this.requirements.forEach((value) => {
|
||||
if (value.classes.includes(id)) {
|
||||
value.cssStyles.push(...style.flatMap((s) => s.split(',')));
|
||||
}
|
||||
});
|
||||
this.elements.forEach((value) => {
|
||||
if (value.classes.includes(id)) {
|
||||
value.cssStyles.push(...style.flatMap((s) => s.split(',')));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public getClasses() {
|
||||
return this.classes;
|
||||
}
|
||||
|
||||
public getData() {
|
||||
const config = getConfig();
|
||||
const nodes: Node[] = [];
|
||||
const edges: Edge[] = [];
|
||||
for (const requirement of this.requirements.values()) {
|
||||
const node = requirement as unknown as Node;
|
||||
node.id = requirement.name;
|
||||
node.cssStyles = requirement.cssStyles;
|
||||
node.cssClasses = requirement.classes.join(' ');
|
||||
node.shape = 'requirementBox';
|
||||
node.look = config.look;
|
||||
nodes.push(node);
|
||||
}
|
||||
|
||||
for (const element of this.elements.values()) {
|
||||
const node = element as unknown as Node;
|
||||
node.shape = 'requirementBox';
|
||||
node.look = config.look;
|
||||
node.id = element.name;
|
||||
node.cssStyles = element.cssStyles;
|
||||
node.cssClasses = element.classes.join(' ');
|
||||
|
||||
nodes.push(node);
|
||||
}
|
||||
|
||||
for (const relation of this.relations) {
|
||||
let counter = 0;
|
||||
const isContains = relation.type === this.Relationships.CONTAINS;
|
||||
const edge: Edge = {
|
||||
id: `${relation.src}-${relation.dst}-${counter}`,
|
||||
start: this.requirements.get(relation.src)?.name ?? this.elements.get(relation.src)?.name,
|
||||
end: this.requirements.get(relation.dst)?.name ?? this.elements.get(relation.dst)?.name,
|
||||
label: `<<${relation.type}>>`,
|
||||
classes: 'relationshipLine',
|
||||
style: ['fill:none', isContains ? '' : 'stroke-dasharray: 10,7'],
|
||||
labelpos: 'c',
|
||||
thickness: 'normal',
|
||||
type: 'normal',
|
||||
pattern: isContains ? 'normal' : 'dashed',
|
||||
arrowTypeEnd: isContains ? 'requirement_contains' : 'requirement_arrow',
|
||||
look: config.look,
|
||||
};
|
||||
|
||||
edges.push(edge);
|
||||
counter++;
|
||||
}
|
||||
|
||||
return { nodes, edges, other: {}, config, direction: this.getDirection() };
|
||||
}
|
||||
|
||||
public setAccTitle = setAccTitle;
|
||||
public getAccTitle = getAccTitle;
|
||||
public setAccDescription = setAccDescription;
|
||||
public getAccDescription = getAccDescription;
|
||||
public setDiagramTitle = setDiagramTitle;
|
||||
public getDiagramTitle = getDiagramTitle;
|
||||
public getConfig = () => getConfig().class;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import type { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import parser from './parser/requirementDiagram.jison';
|
||||
import db from './requirementDb.js';
|
||||
import { RequirementDB } from './requirementDb.js';
|
||||
import styles from './styles.js';
|
||||
import renderer from './requirementRenderer.js';
|
||||
|
||||
export const diagram: DiagramDefinition = {
|
||||
parser,
|
||||
db,
|
||||
get db() {
|
||||
return new RequirementDB();
|
||||
},
|
||||
renderer,
|
||||
styles,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user