mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-16 22:09:57 +02:00
1240-refactor for class renderer
prep for future functionality work
This commit is contained in:
@@ -121,6 +121,8 @@ describe('class diagram, ', function () {
|
|||||||
' flightNumber : Integer\n' +
|
' flightNumber : Integer\n' +
|
||||||
' departureTime : Date\n' +
|
' departureTime : Date\n' +
|
||||||
'}';
|
'}';
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle class definitions', function() {
|
it('should handle class definitions', function() {
|
||||||
|
@@ -1,137 +0,0 @@
|
|||||||
export const addTspan = function(textEl, txt, isFirst, conf) {
|
|
||||||
let member = parseMember(txt);
|
|
||||||
|
|
||||||
const tSpan = textEl
|
|
||||||
.append('tspan')
|
|
||||||
.attr('x', conf.padding)
|
|
||||||
.text(member.displayText);
|
|
||||||
|
|
||||||
if (member.cssStyle !== '') {
|
|
||||||
tSpan.attr('style', member.cssStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isFirst) {
|
|
||||||
tSpan.attr('dy', conf.textHeight);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildFieldDisplay = function(parsedText) {
|
|
||||||
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
|
||||||
let fieldType = parsedText[2] ? parsedText[2].trim() : '';
|
|
||||||
let genericType = parsedText[3] ? parseGenericTypes(parsedText[3]) : '';
|
|
||||||
let fieldName = parsedText[4] ? parsedText[4].trim() : '';
|
|
||||||
|
|
||||||
return {
|
|
||||||
displayText: visibility + fieldType + genericType + ' ' + fieldName,
|
|
||||||
cssStyle: ''
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildMethodDisplay = function(parsedText) {
|
|
||||||
let cssStyle = '';
|
|
||||||
let displayText = parsedText;
|
|
||||||
|
|
||||||
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
|
||||||
let methodName = parsedText[2] ? parsedText[2].trim() : '';
|
|
||||||
let parameters = parsedText[3] ? parseGenericTypes(parsedText[3]) : '';
|
|
||||||
let classifier = parsedText[6] ? parsedText[6].trim() : '';
|
|
||||||
let returnType = parsedText[7] ? ' : ' + parseGenericTypes(parsedText[7]).trim() : '';
|
|
||||||
|
|
||||||
displayText = visibility + methodName + '(' + parameters + ')' + returnType;
|
|
||||||
|
|
||||||
cssStyle = parseClassifier(classifier);
|
|
||||||
|
|
||||||
let member = {
|
|
||||||
displayText: displayText,
|
|
||||||
cssStyle: cssStyle
|
|
||||||
};
|
|
||||||
|
|
||||||
return member;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildLegacyDisplay = function(text) {
|
|
||||||
// if for some reason we dont have any match, use old format to parse text
|
|
||||||
let memberText = '';
|
|
||||||
let cssStyle = '';
|
|
||||||
let returnType = '';
|
|
||||||
let methodStart = text.indexOf('(');
|
|
||||||
let methodEnd = text.indexOf(')');
|
|
||||||
|
|
||||||
if (methodStart > 1 && methodEnd > methodStart && methodEnd <= text.length) {
|
|
||||||
let parsedText = text.match(/(\+|-|~|#)?(\w+)/);
|
|
||||||
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
|
||||||
let methodName = parsedText[2];
|
|
||||||
let parameters = text.substring(methodStart + 1, methodEnd);
|
|
||||||
let classifier = text.substring(methodEnd, methodEnd + 1);
|
|
||||||
cssStyle = parseClassifier(classifier);
|
|
||||||
|
|
||||||
memberText = visibility + methodName + '(' + parseGenericTypes(parameters.trim()) + ')';
|
|
||||||
|
|
||||||
if (methodEnd < memberText.length) {
|
|
||||||
returnType = text.substring(methodEnd + 2).trim();
|
|
||||||
if (returnType !== '') {
|
|
||||||
returnType = ' : ' + parseGenericTypes(returnType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// finally - if all else fails, just send the text back as written (other than parsing for generic types)
|
|
||||||
memberText = parseGenericTypes(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
let member = {
|
|
||||||
displayText: memberText + returnType,
|
|
||||||
cssStyle: cssStyle
|
|
||||||
};
|
|
||||||
|
|
||||||
return member;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseGenericTypes = function(text) {
|
|
||||||
let cleanedText = text;
|
|
||||||
|
|
||||||
if (text.indexOf('~') != -1) {
|
|
||||||
cleanedText = cleanedText.replace('~', '<');
|
|
||||||
cleanedText = cleanedText.replace('~', '>');
|
|
||||||
|
|
||||||
return parseGenericTypes(cleanedText);
|
|
||||||
} else {
|
|
||||||
return cleanedText;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseMember = function(text) {
|
|
||||||
const fieldRegEx = /^(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+)$/;
|
|
||||||
const methodRegEx = /^(\+|-|~|#)?(\w+)\s?\(\s*(\w+(~\w+~|\[\])?\s*(\w+)?)?\s*\)\s?([*|$])?\s?(\w+(~\w+~|\[\])?)?\s*$/;
|
|
||||||
//const methodRegEx = /(\+|-|~|#)?(\w+)\s?\(\s*(\w+(~\w+~|\[\])?\s*(\w+)?)?\s*\)\s?([*|$])?\s?(\w+(~\w+~|\[\])?)?/;
|
|
||||||
|
|
||||||
let fieldMatch = text.match(fieldRegEx);
|
|
||||||
let methodMatch = text.match(methodRegEx);
|
|
||||||
|
|
||||||
if (fieldMatch) {
|
|
||||||
return buildFieldDisplay(fieldMatch);
|
|
||||||
} else if (methodMatch) {
|
|
||||||
return buildMethodDisplay(methodMatch);
|
|
||||||
} else {
|
|
||||||
return buildLegacyDisplay(text);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const parseClassifier = function(classifier) {
|
|
||||||
switch (classifier) {
|
|
||||||
case '*':
|
|
||||||
return 'font-style:italic;';
|
|
||||||
case '$':
|
|
||||||
return 'text-decoration:underline;';
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
addTspan,
|
|
||||||
buildFieldDisplay,
|
|
||||||
buildLegacyDisplay,
|
|
||||||
buildMethodDisplay,
|
|
||||||
parseGenericTypes,
|
|
||||||
parseMember
|
|
||||||
};
|
|
@@ -3,9 +3,8 @@ import dagre from 'dagre';
|
|||||||
import graphlib from 'graphlib';
|
import graphlib from 'graphlib';
|
||||||
import { logger } from '../../logger';
|
import { logger } from '../../logger';
|
||||||
import classDb, { lookUpDomId } from './classDb';
|
import classDb, { lookUpDomId } from './classDb';
|
||||||
import utils from '../../utils';
|
|
||||||
import { parser } from './parser/classDiagram';
|
import { parser } from './parser/classDiagram';
|
||||||
import memberRenderer from './classMemberRenderer';
|
import svgDraw from './svgDraw';
|
||||||
|
|
||||||
parser.yy = classDb;
|
parser.yy = classDb;
|
||||||
|
|
||||||
@@ -135,285 +134,6 @@ const insertMarkers = function(elem) {
|
|||||||
.attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z');
|
.attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z');
|
||||||
};
|
};
|
||||||
|
|
||||||
let edgeCount = 0;
|
|
||||||
const drawEdge = function(elem, path, relation) {
|
|
||||||
const getRelationType = function(type) {
|
|
||||||
switch (type) {
|
|
||||||
case classDb.relationType.AGGREGATION:
|
|
||||||
return 'aggregation';
|
|
||||||
case classDb.relationType.EXTENSION:
|
|
||||||
return 'extension';
|
|
||||||
case classDb.relationType.COMPOSITION:
|
|
||||||
return 'composition';
|
|
||||||
case classDb.relationType.DEPENDENCY:
|
|
||||||
return 'dependency';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
path.points = path.points.filter(p => !Number.isNaN(p.y));
|
|
||||||
|
|
||||||
// The data for our line
|
|
||||||
const lineData = path.points;
|
|
||||||
|
|
||||||
// This is the accessor function we talked about above
|
|
||||||
const lineFunction = d3
|
|
||||||
.line()
|
|
||||||
.x(function(d) {
|
|
||||||
return d.x;
|
|
||||||
})
|
|
||||||
.y(function(d) {
|
|
||||||
return d.y;
|
|
||||||
})
|
|
||||||
.curve(d3.curveBasis);
|
|
||||||
|
|
||||||
const svgPath = elem
|
|
||||||
.append('path')
|
|
||||||
.attr('d', lineFunction(lineData))
|
|
||||||
.attr('id', 'edge' + edgeCount)
|
|
||||||
.attr('class', 'relation');
|
|
||||||
let url = '';
|
|
||||||
if (conf.arrowMarkerAbsolute) {
|
|
||||||
url =
|
|
||||||
window.location.protocol +
|
|
||||||
'//' +
|
|
||||||
window.location.host +
|
|
||||||
window.location.pathname +
|
|
||||||
window.location.search;
|
|
||||||
url = url.replace(/\(/g, '\\(');
|
|
||||||
url = url.replace(/\)/g, '\\)');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (relation.relation.lineType == 1) {
|
|
||||||
svgPath.attr('class', 'relation dashed-line');
|
|
||||||
}
|
|
||||||
if (relation.relation.type1 !== 'none') {
|
|
||||||
svgPath.attr(
|
|
||||||
'marker-start',
|
|
||||||
'url(' + url + '#' + getRelationType(relation.relation.type1) + 'Start' + ')'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (relation.relation.type2 !== 'none') {
|
|
||||||
svgPath.attr(
|
|
||||||
'marker-end',
|
|
||||||
'url(' + url + '#' + getRelationType(relation.relation.type2) + 'End' + ')'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let x, y;
|
|
||||||
const l = path.points.length;
|
|
||||||
// Calculate Label position
|
|
||||||
let labalPosition = utils.calcLabelPosition(path.points);
|
|
||||||
x = labalPosition.x;
|
|
||||||
y = labalPosition.y;
|
|
||||||
|
|
||||||
let p1_card_x, p1_card_y;
|
|
||||||
// p1_card_padd_x = conf.padding * 2,
|
|
||||||
// p1_card_padd_y = conf.padding;
|
|
||||||
let p2_card_x, p2_card_y;
|
|
||||||
// p2_card_padd_x = conf.padding * 2,
|
|
||||||
// p2_card_padd_y = -conf.padding / 2;
|
|
||||||
if (l % 2 !== 0 && l > 1) {
|
|
||||||
let cardinality_1_point = utils.calcCardinalityPosition(
|
|
||||||
relation.relation.type1 !== 'none',
|
|
||||||
path.points,
|
|
||||||
path.points[0]
|
|
||||||
);
|
|
||||||
let cardinality_2_point = utils.calcCardinalityPosition(
|
|
||||||
relation.relation.type2 !== 'none',
|
|
||||||
path.points,
|
|
||||||
path.points[l - 1]
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.debug('cardinality_1_point ' + JSON.stringify(cardinality_1_point));
|
|
||||||
logger.debug('cardinality_2_point ' + JSON.stringify(cardinality_2_point));
|
|
||||||
|
|
||||||
p1_card_x = cardinality_1_point.x;
|
|
||||||
p1_card_y = cardinality_1_point.y;
|
|
||||||
p2_card_x = cardinality_2_point.x;
|
|
||||||
p2_card_y = cardinality_2_point.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof relation.title !== 'undefined') {
|
|
||||||
const g = elem.append('g').attr('class', 'classLabel');
|
|
||||||
const label = g
|
|
||||||
.append('text')
|
|
||||||
.attr('class', 'label')
|
|
||||||
.attr('x', x)
|
|
||||||
.attr('y', y)
|
|
||||||
.attr('fill', 'red')
|
|
||||||
.attr('text-anchor', 'middle')
|
|
||||||
.text(relation.title);
|
|
||||||
|
|
||||||
window.label = label;
|
|
||||||
const bounds = label.node().getBBox();
|
|
||||||
|
|
||||||
g.insert('rect', ':first-child')
|
|
||||||
.attr('class', 'box')
|
|
||||||
.attr('x', bounds.x - conf.padding / 2)
|
|
||||||
.attr('y', bounds.y - conf.padding / 2)
|
|
||||||
.attr('width', bounds.width + conf.padding)
|
|
||||||
.attr('height', bounds.height + conf.padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Rendering relation ' + JSON.stringify(relation));
|
|
||||||
if (typeof relation.relationTitle1 !== 'undefined' && relation.relationTitle1 !== 'none') {
|
|
||||||
const g = elem.append('g').attr('class', 'cardinality');
|
|
||||||
g.append('text')
|
|
||||||
.attr('class', 'type1')
|
|
||||||
.attr('x', p1_card_x)
|
|
||||||
.attr('y', p1_card_y)
|
|
||||||
.attr('fill', 'black')
|
|
||||||
.attr('font-size', '6')
|
|
||||||
.text(relation.relationTitle1);
|
|
||||||
}
|
|
||||||
if (typeof relation.relationTitle2 !== 'undefined' && relation.relationTitle2 !== 'none') {
|
|
||||||
const g = elem.append('g').attr('class', 'cardinality');
|
|
||||||
g.append('text')
|
|
||||||
.attr('class', 'type2')
|
|
||||||
.attr('x', p2_card_x)
|
|
||||||
.attr('y', p2_card_y)
|
|
||||||
.attr('fill', 'black')
|
|
||||||
.attr('font-size', '6')
|
|
||||||
.text(relation.relationTitle2);
|
|
||||||
}
|
|
||||||
|
|
||||||
edgeCount++;
|
|
||||||
};
|
|
||||||
|
|
||||||
const drawClass = function(elem, classDef) {
|
|
||||||
logger.info('Rendering class ' + classDef);
|
|
||||||
|
|
||||||
let cssClassStr = 'classGroup ';
|
|
||||||
if (classDef.cssClasses.length > 0) {
|
|
||||||
cssClassStr = cssClassStr + classDef.cssClasses.join(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = classDef.id;
|
|
||||||
const classInfo = {
|
|
||||||
id: id,
|
|
||||||
label: classDef.id,
|
|
||||||
width: 0,
|
|
||||||
height: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
// add class group
|
|
||||||
const g = elem
|
|
||||||
.append('g')
|
|
||||||
.attr('id', lookUpDomId(id))
|
|
||||||
.attr('class', cssClassStr);
|
|
||||||
|
|
||||||
// add title
|
|
||||||
let title;
|
|
||||||
if (classDef.link) {
|
|
||||||
title = g
|
|
||||||
.append('svg:a')
|
|
||||||
.attr('xlink:href', classDef.link)
|
|
||||||
.attr('target', '_blank')
|
|
||||||
.append('text')
|
|
||||||
.attr('y', conf.textHeight + conf.padding)
|
|
||||||
.attr('x', 0);
|
|
||||||
} else {
|
|
||||||
title = g
|
|
||||||
.append('text')
|
|
||||||
.attr('y', conf.textHeight + conf.padding)
|
|
||||||
.attr('x', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add annotations
|
|
||||||
let isFirst = true;
|
|
||||||
classDef.annotations.forEach(function(member) {
|
|
||||||
const titleText2 = title.append('tspan').text('«' + member + '»');
|
|
||||||
if (!isFirst) titleText2.attr('dy', conf.textHeight);
|
|
||||||
isFirst = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
let classTitleString = classDef.id;
|
|
||||||
|
|
||||||
if (classDef.type !== undefined && classDef.type !== '') {
|
|
||||||
classTitleString += '<' + classDef.type + '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
const classTitle = title
|
|
||||||
.append('tspan')
|
|
||||||
.text(classTitleString)
|
|
||||||
.attr('class', 'title');
|
|
||||||
|
|
||||||
// If class has annotations the title needs to have an offset of the text height
|
|
||||||
if (!isFirst) classTitle.attr('dy', conf.textHeight);
|
|
||||||
|
|
||||||
const titleHeight = title.node().getBBox().height;
|
|
||||||
|
|
||||||
const membersLine = g
|
|
||||||
.append('line') // text label for the x axis
|
|
||||||
.attr('x1', 0)
|
|
||||||
.attr('y1', conf.padding + titleHeight + conf.dividerMargin / 2)
|
|
||||||
.attr('y2', conf.padding + titleHeight + conf.dividerMargin / 2);
|
|
||||||
|
|
||||||
const members = g
|
|
||||||
.append('text') // text label for the x axis
|
|
||||||
.attr('x', conf.padding)
|
|
||||||
.attr('y', titleHeight + conf.dividerMargin + conf.textHeight)
|
|
||||||
.attr('fill', 'white')
|
|
||||||
.attr('class', 'classText');
|
|
||||||
|
|
||||||
isFirst = true;
|
|
||||||
classDef.members.forEach(function(member) {
|
|
||||||
memberRenderer.addTspan(members, member, isFirst, conf);
|
|
||||||
isFirst = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
const membersBox = members.node().getBBox();
|
|
||||||
|
|
||||||
const methodsLine = g
|
|
||||||
.append('line') // text label for the x axis
|
|
||||||
.attr('x1', 0)
|
|
||||||
.attr('y1', conf.padding + titleHeight + conf.dividerMargin + membersBox.height)
|
|
||||||
.attr('y2', conf.padding + titleHeight + conf.dividerMargin + membersBox.height);
|
|
||||||
|
|
||||||
const methods = g
|
|
||||||
.append('text') // text label for the x axis
|
|
||||||
.attr('x', conf.padding)
|
|
||||||
.attr('y', titleHeight + 2 * conf.dividerMargin + membersBox.height + conf.textHeight)
|
|
||||||
.attr('fill', 'white')
|
|
||||||
.attr('class', 'classText');
|
|
||||||
|
|
||||||
isFirst = true;
|
|
||||||
|
|
||||||
classDef.methods.forEach(function(method) {
|
|
||||||
memberRenderer.addTspan(methods, method, isFirst, conf);
|
|
||||||
isFirst = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
const classBox = g.node().getBBox();
|
|
||||||
const rect = g
|
|
||||||
.insert('rect', ':first-child')
|
|
||||||
.attr('x', 0)
|
|
||||||
.attr('y', 0)
|
|
||||||
.attr('width', classBox.width + 2 * conf.padding)
|
|
||||||
.attr('height', classBox.height + conf.padding + 0.5 * conf.dividerMargin);
|
|
||||||
|
|
||||||
const rectWidth = rect.node().getBBox().width;
|
|
||||||
|
|
||||||
// Center title
|
|
||||||
// We subtract the width of each text element from the class box width and divide it by 2
|
|
||||||
title.node().childNodes.forEach(function(x) {
|
|
||||||
x.setAttribute('x', (rectWidth - x.getBBox().width) / 2);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (classDef.tooltip) {
|
|
||||||
title.insert('title').text(classDef.tooltip);
|
|
||||||
}
|
|
||||||
|
|
||||||
membersLine.attr('x2', rectWidth);
|
|
||||||
methodsLine.attr('x2', rectWidth);
|
|
||||||
|
|
||||||
classInfo.width = rectWidth;
|
|
||||||
classInfo.height = classBox.height + conf.padding + 0.5 * conf.dividerMargin;
|
|
||||||
|
|
||||||
idCache[id] = classInfo;
|
|
||||||
return classInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setConf = function(cnf) {
|
export const setConf = function(cnf) {
|
||||||
const keys = Object.keys(cnf);
|
const keys = Object.keys(cnf);
|
||||||
|
|
||||||
@@ -434,7 +154,7 @@ export const draw = function(text, id) {
|
|||||||
|
|
||||||
logger.info('Rendering diagram ' + text);
|
logger.info('Rendering diagram ' + text);
|
||||||
|
|
||||||
/// / Fetch the default direction, use TD if none was found
|
// Fetch the default direction, use TD if none was found
|
||||||
const diagram = d3.select(`[id='${id}']`);
|
const diagram = d3.select(`[id='${id}']`);
|
||||||
insertMarkers(diagram);
|
insertMarkers(diagram);
|
||||||
|
|
||||||
@@ -457,7 +177,8 @@ export const draw = function(text, id) {
|
|||||||
const keys = Object.keys(classes);
|
const keys = Object.keys(classes);
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
const classDef = classes[keys[i]];
|
const classDef = classes[keys[i]];
|
||||||
const node = drawClass(diagram, classDef);
|
const node = svgDraw.drawClass(diagram, classDef, conf);
|
||||||
|
idCache[node.id] = node;
|
||||||
|
|
||||||
// Add nodes to the graph. The first argument is the node id. The second is
|
// Add nodes to the graph. The first argument is the node id. The second is
|
||||||
// metadata about the node. In this case we're going to add labels to each of
|
// metadata about the node. In this case we're going to add labels to each of
|
||||||
@@ -481,6 +202,7 @@ export const draw = function(text, id) {
|
|||||||
relation.title || 'DEFAULT'
|
relation.title || 'DEFAULT'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
dagre.layout(g);
|
dagre.layout(g);
|
||||||
g.nodes().forEach(function(v) {
|
g.nodes().forEach(function(v) {
|
||||||
if (typeof v !== 'undefined' && typeof g.node(v) !== 'undefined') {
|
if (typeof v !== 'undefined' && typeof g.node(v) !== 'undefined') {
|
||||||
@@ -495,10 +217,11 @@ export const draw = function(text, id) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
g.edges().forEach(function(e) {
|
g.edges().forEach(function(e) {
|
||||||
if (typeof e !== 'undefined' && typeof g.edge(e) !== 'undefined') {
|
if (typeof e !== 'undefined' && typeof g.edge(e) !== 'undefined') {
|
||||||
logger.debug('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(g.edge(e)));
|
logger.debug('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(g.edge(e)));
|
||||||
drawEdge(diagram, g.edge(e), g.edge(e).relation);
|
svgDraw.drawEdge(diagram, g.edge(e), g.edge(e).relation, conf);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
413
src/diagrams/class/svgDraw.js
Normal file
413
src/diagrams/class/svgDraw.js
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
import * as d3 from 'd3';
|
||||||
|
import classDb, { lookUpDomId } from './classDb';
|
||||||
|
import utils from '../../utils';
|
||||||
|
import { logger } from '../../logger';
|
||||||
|
|
||||||
|
let edgeCount = 0;
|
||||||
|
export const drawEdge = function(elem, path, relation, conf) {
|
||||||
|
const getRelationType = function(type) {
|
||||||
|
switch (type) {
|
||||||
|
case classDb.relationType.AGGREGATION:
|
||||||
|
return 'aggregation';
|
||||||
|
case classDb.relationType.EXTENSION:
|
||||||
|
return 'extension';
|
||||||
|
case classDb.relationType.COMPOSITION:
|
||||||
|
return 'composition';
|
||||||
|
case classDb.relationType.DEPENDENCY:
|
||||||
|
return 'dependency';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
path.points = path.points.filter(p => !Number.isNaN(p.y));
|
||||||
|
|
||||||
|
// The data for our line
|
||||||
|
const lineData = path.points;
|
||||||
|
|
||||||
|
// This is the accessor function we talked about above
|
||||||
|
const lineFunction = d3
|
||||||
|
.line()
|
||||||
|
.x(function(d) {
|
||||||
|
return d.x;
|
||||||
|
})
|
||||||
|
.y(function(d) {
|
||||||
|
return d.y;
|
||||||
|
})
|
||||||
|
.curve(d3.curveBasis);
|
||||||
|
|
||||||
|
const svgPath = elem
|
||||||
|
.append('path')
|
||||||
|
.attr('d', lineFunction(lineData))
|
||||||
|
.attr('id', 'edge' + edgeCount)
|
||||||
|
.attr('class', 'relation');
|
||||||
|
let url = '';
|
||||||
|
if (conf.arrowMarkerAbsolute) {
|
||||||
|
url =
|
||||||
|
window.location.protocol +
|
||||||
|
'//' +
|
||||||
|
window.location.host +
|
||||||
|
window.location.pathname +
|
||||||
|
window.location.search;
|
||||||
|
url = url.replace(/\(/g, '\\(');
|
||||||
|
url = url.replace(/\)/g, '\\)');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relation.relation.lineType == 1) {
|
||||||
|
svgPath.attr('class', 'relation dashed-line');
|
||||||
|
}
|
||||||
|
if (relation.relation.type1 !== 'none') {
|
||||||
|
svgPath.attr(
|
||||||
|
'marker-start',
|
||||||
|
'url(' + url + '#' + getRelationType(relation.relation.type1) + 'Start' + ')'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (relation.relation.type2 !== 'none') {
|
||||||
|
svgPath.attr(
|
||||||
|
'marker-end',
|
||||||
|
'url(' + url + '#' + getRelationType(relation.relation.type2) + 'End' + ')'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let x, y;
|
||||||
|
const l = path.points.length;
|
||||||
|
// Calculate Label position
|
||||||
|
let labelPosition = utils.calcLabelPosition(path.points);
|
||||||
|
x = labelPosition.x;
|
||||||
|
y = labelPosition.y;
|
||||||
|
|
||||||
|
let p1_card_x, p1_card_y;
|
||||||
|
let p2_card_x, p2_card_y;
|
||||||
|
|
||||||
|
if (l % 2 !== 0 && l > 1) {
|
||||||
|
let cardinality_1_point = utils.calcCardinalityPosition(
|
||||||
|
relation.relation.type1 !== 'none',
|
||||||
|
path.points,
|
||||||
|
path.points[0]
|
||||||
|
);
|
||||||
|
let cardinality_2_point = utils.calcCardinalityPosition(
|
||||||
|
relation.relation.type2 !== 'none',
|
||||||
|
path.points,
|
||||||
|
path.points[l - 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.debug('cardinality_1_point ' + JSON.stringify(cardinality_1_point));
|
||||||
|
logger.debug('cardinality_2_point ' + JSON.stringify(cardinality_2_point));
|
||||||
|
|
||||||
|
p1_card_x = cardinality_1_point.x;
|
||||||
|
p1_card_y = cardinality_1_point.y;
|
||||||
|
p2_card_x = cardinality_2_point.x;
|
||||||
|
p2_card_y = cardinality_2_point.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof relation.title !== 'undefined') {
|
||||||
|
const g = elem.append('g').attr('class', 'classLabel');
|
||||||
|
const label = g
|
||||||
|
.append('text')
|
||||||
|
.attr('class', 'label')
|
||||||
|
.attr('x', x)
|
||||||
|
.attr('y', y)
|
||||||
|
.attr('fill', 'red')
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.text(relation.title);
|
||||||
|
|
||||||
|
window.label = label;
|
||||||
|
const bounds = label.node().getBBox();
|
||||||
|
|
||||||
|
g.insert('rect', ':first-child')
|
||||||
|
.attr('class', 'box')
|
||||||
|
.attr('x', bounds.x - conf.padding / 2)
|
||||||
|
.attr('y', bounds.y - conf.padding / 2)
|
||||||
|
.attr('width', bounds.width + conf.padding)
|
||||||
|
.attr('height', bounds.height + conf.padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('Rendering relation ' + JSON.stringify(relation));
|
||||||
|
if (typeof relation.relationTitle1 !== 'undefined' && relation.relationTitle1 !== 'none') {
|
||||||
|
const g = elem.append('g').attr('class', 'cardinality');
|
||||||
|
g.append('text')
|
||||||
|
.attr('class', 'type1')
|
||||||
|
.attr('x', p1_card_x)
|
||||||
|
.attr('y', p1_card_y)
|
||||||
|
.attr('fill', 'black')
|
||||||
|
.attr('font-size', '6')
|
||||||
|
.text(relation.relationTitle1);
|
||||||
|
}
|
||||||
|
if (typeof relation.relationTitle2 !== 'undefined' && relation.relationTitle2 !== 'none') {
|
||||||
|
const g = elem.append('g').attr('class', 'cardinality');
|
||||||
|
g.append('text')
|
||||||
|
.attr('class', 'type2')
|
||||||
|
.attr('x', p2_card_x)
|
||||||
|
.attr('y', p2_card_y)
|
||||||
|
.attr('fill', 'black')
|
||||||
|
.attr('font-size', '6')
|
||||||
|
.text(relation.relationTitle2);
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeCount++;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const drawClass = function(elem, classDef, conf) {
|
||||||
|
logger.info('Rendering class ' + classDef);
|
||||||
|
|
||||||
|
let cssClassStr = 'classGroup ';
|
||||||
|
if (classDef.cssClasses.length > 0) {
|
||||||
|
cssClassStr = cssClassStr + classDef.cssClasses.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = classDef.id;
|
||||||
|
const classInfo = {
|
||||||
|
id: id,
|
||||||
|
label: classDef.id,
|
||||||
|
width: 0,
|
||||||
|
height: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// add class group
|
||||||
|
const g = elem
|
||||||
|
.append('g')
|
||||||
|
.attr('id', lookUpDomId(id))
|
||||||
|
.attr('class', cssClassStr);
|
||||||
|
|
||||||
|
// add title
|
||||||
|
let title;
|
||||||
|
if (classDef.link) {
|
||||||
|
title = g
|
||||||
|
.append('svg:a')
|
||||||
|
.attr('xlink:href', classDef.link)
|
||||||
|
.attr('target', '_blank')
|
||||||
|
.append('text')
|
||||||
|
.attr('y', conf.textHeight + conf.padding)
|
||||||
|
.attr('x', 0);
|
||||||
|
} else {
|
||||||
|
title = g
|
||||||
|
.append('text')
|
||||||
|
.attr('y', conf.textHeight + conf.padding)
|
||||||
|
.attr('x', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add annotations
|
||||||
|
let isFirst = true;
|
||||||
|
classDef.annotations.forEach(function(member) {
|
||||||
|
const titleText2 = title.append('tspan').text('«' + member + '»');
|
||||||
|
if (!isFirst) titleText2.attr('dy', conf.textHeight);
|
||||||
|
isFirst = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
let classTitleString = classDef.id;
|
||||||
|
|
||||||
|
if (classDef.type !== undefined && classDef.type !== '') {
|
||||||
|
classTitleString += '<' + classDef.type + '>';
|
||||||
|
}
|
||||||
|
|
||||||
|
const classTitle = title
|
||||||
|
.append('tspan')
|
||||||
|
.text(classTitleString)
|
||||||
|
.attr('class', 'title');
|
||||||
|
|
||||||
|
// If class has annotations the title needs to have an offset of the text height
|
||||||
|
if (!isFirst) classTitle.attr('dy', conf.textHeight);
|
||||||
|
|
||||||
|
const titleHeight = title.node().getBBox().height;
|
||||||
|
|
||||||
|
const membersLine = g
|
||||||
|
.append('line') // text label for the x axis
|
||||||
|
.attr('x1', 0)
|
||||||
|
.attr('y1', conf.padding + titleHeight + conf.dividerMargin / 2)
|
||||||
|
.attr('y2', conf.padding + titleHeight + conf.dividerMargin / 2);
|
||||||
|
|
||||||
|
const members = g
|
||||||
|
.append('text') // text label for the x axis
|
||||||
|
.attr('x', conf.padding)
|
||||||
|
.attr('y', titleHeight + conf.dividerMargin + conf.textHeight)
|
||||||
|
.attr('fill', 'white')
|
||||||
|
.attr('class', 'classText');
|
||||||
|
|
||||||
|
isFirst = true;
|
||||||
|
classDef.members.forEach(function(member) {
|
||||||
|
addTspan(members, member, isFirst, conf);
|
||||||
|
isFirst = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const membersBox = members.node().getBBox();
|
||||||
|
|
||||||
|
const methodsLine = g
|
||||||
|
.append('line') // text label for the x axis
|
||||||
|
.attr('x1', 0)
|
||||||
|
.attr('y1', conf.padding + titleHeight + conf.dividerMargin + membersBox.height)
|
||||||
|
.attr('y2', conf.padding + titleHeight + conf.dividerMargin + membersBox.height);
|
||||||
|
|
||||||
|
const methods = g
|
||||||
|
.append('text') // text label for the x axis
|
||||||
|
.attr('x', conf.padding)
|
||||||
|
.attr('y', titleHeight + 2 * conf.dividerMargin + membersBox.height + conf.textHeight)
|
||||||
|
.attr('fill', 'white')
|
||||||
|
.attr('class', 'classText');
|
||||||
|
|
||||||
|
isFirst = true;
|
||||||
|
|
||||||
|
classDef.methods.forEach(function(method) {
|
||||||
|
addTspan(methods, method, isFirst, conf);
|
||||||
|
isFirst = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const classBox = g.node().getBBox();
|
||||||
|
const rect = g
|
||||||
|
.insert('rect', ':first-child')
|
||||||
|
.attr('x', 0)
|
||||||
|
.attr('y', 0)
|
||||||
|
.attr('width', classBox.width + 2 * conf.padding)
|
||||||
|
.attr('height', classBox.height + conf.padding + 0.5 * conf.dividerMargin);
|
||||||
|
|
||||||
|
const rectWidth = rect.node().getBBox().width;
|
||||||
|
|
||||||
|
// Center title
|
||||||
|
// We subtract the width of each text element from the class box width and divide it by 2
|
||||||
|
title.node().childNodes.forEach(function(x) {
|
||||||
|
x.setAttribute('x', (rectWidth - x.getBBox().width) / 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (classDef.tooltip) {
|
||||||
|
title.insert('title').text(classDef.tooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
membersLine.attr('x2', rectWidth);
|
||||||
|
methodsLine.attr('x2', rectWidth);
|
||||||
|
|
||||||
|
classInfo.width = rectWidth;
|
||||||
|
classInfo.height = classBox.height + conf.padding + 0.5 * conf.dividerMargin;
|
||||||
|
|
||||||
|
return classInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseMember = function(text) {
|
||||||
|
const fieldRegEx = /^(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+)$/;
|
||||||
|
const methodRegEx = /^(\+|-|~|#)?(\w+)\s?\(\s*(\w+(~\w+~|\[\])?\s*(\w+)?)?\s*\)\s?([*|$])?\s?(\w+(~\w+~|\[\])?)?\s*$/;
|
||||||
|
|
||||||
|
let fieldMatch = text.match(fieldRegEx);
|
||||||
|
let methodMatch = text.match(methodRegEx);
|
||||||
|
|
||||||
|
if (fieldMatch) {
|
||||||
|
return buildFieldDisplay(fieldMatch);
|
||||||
|
} else if (methodMatch) {
|
||||||
|
return buildMethodDisplay(methodMatch);
|
||||||
|
} else {
|
||||||
|
return buildLegacyDisplay(text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildFieldDisplay = function(parsedText) {
|
||||||
|
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
||||||
|
let fieldType = parsedText[2] ? parsedText[2].trim() : '';
|
||||||
|
let genericType = parsedText[3] ? parseGenericTypes(parsedText[3]) : '';
|
||||||
|
let fieldName = parsedText[4] ? parsedText[4].trim() : '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
displayText: visibility + fieldType + genericType + ' ' + fieldName,
|
||||||
|
cssStyle: ''
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildMethodDisplay = function(parsedText) {
|
||||||
|
let cssStyle = '';
|
||||||
|
let displayText = parsedText;
|
||||||
|
|
||||||
|
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
||||||
|
let methodName = parsedText[2] ? parsedText[2].trim() : '';
|
||||||
|
let parameters = parsedText[3] ? parseGenericTypes(parsedText[3]) : '';
|
||||||
|
let classifier = parsedText[6] ? parsedText[6].trim() : '';
|
||||||
|
let returnType = parsedText[7] ? ' : ' + parseGenericTypes(parsedText[7]).trim() : '';
|
||||||
|
|
||||||
|
displayText = visibility + methodName + '(' + parameters + ')' + returnType;
|
||||||
|
|
||||||
|
cssStyle = parseClassifier(classifier);
|
||||||
|
|
||||||
|
let member = {
|
||||||
|
displayText: displayText,
|
||||||
|
cssStyle: cssStyle
|
||||||
|
};
|
||||||
|
|
||||||
|
return member;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildLegacyDisplay = function(text) {
|
||||||
|
// if for some reason we dont have any match, use old format to parse text
|
||||||
|
let memberText = '';
|
||||||
|
let cssStyle = '';
|
||||||
|
let returnType = '';
|
||||||
|
let methodStart = text.indexOf('(');
|
||||||
|
let methodEnd = text.indexOf(')');
|
||||||
|
|
||||||
|
if (methodStart > 1 && methodEnd > methodStart && methodEnd <= text.length) {
|
||||||
|
let parsedText = text.match(/(\+|-|~|#)?(\w+)/);
|
||||||
|
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
||||||
|
let methodName = parsedText[2];
|
||||||
|
let parameters = text.substring(methodStart + 1, methodEnd);
|
||||||
|
let classifier = text.substring(methodEnd, methodEnd + 1);
|
||||||
|
cssStyle = parseClassifier(classifier);
|
||||||
|
|
||||||
|
memberText = visibility + methodName + '(' + parseGenericTypes(parameters.trim()) + ')';
|
||||||
|
|
||||||
|
if (methodEnd < memberText.length) {
|
||||||
|
returnType = text.substring(methodEnd + 2).trim();
|
||||||
|
if (returnType !== '') {
|
||||||
|
returnType = ' : ' + parseGenericTypes(returnType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// finally - if all else fails, just send the text back as written (other than parsing for generic types)
|
||||||
|
memberText = parseGenericTypes(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
let member = {
|
||||||
|
displayText: memberText + returnType,
|
||||||
|
cssStyle: cssStyle
|
||||||
|
};
|
||||||
|
|
||||||
|
return member;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addTspan = function(textEl, txt, isFirst, conf) {
|
||||||
|
let member = parseMember(txt);
|
||||||
|
|
||||||
|
const tSpan = textEl
|
||||||
|
.append('tspan')
|
||||||
|
.attr('x', conf.padding)
|
||||||
|
.text(member.displayText);
|
||||||
|
|
||||||
|
if (member.cssStyle !== '') {
|
||||||
|
tSpan.attr('style', member.cssStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFirst) {
|
||||||
|
tSpan.attr('dy', conf.textHeight);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseGenericTypes = function(text) {
|
||||||
|
let cleanedText = text;
|
||||||
|
|
||||||
|
if (text.indexOf('~') != -1) {
|
||||||
|
cleanedText = cleanedText.replace('~', '<');
|
||||||
|
cleanedText = cleanedText.replace('~', '>');
|
||||||
|
|
||||||
|
return parseGenericTypes(cleanedText);
|
||||||
|
} else {
|
||||||
|
return cleanedText;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseClassifier = function(classifier) {
|
||||||
|
switch (classifier) {
|
||||||
|
case '*':
|
||||||
|
return 'font-style:italic;';
|
||||||
|
case '$':
|
||||||
|
return 'text-decoration:underline;';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
drawClass,
|
||||||
|
drawEdge,
|
||||||
|
parseMember
|
||||||
|
};
|
@@ -1,11 +1,11 @@
|
|||||||
/* eslint-env jasmine */
|
/* eslint-env jasmine */
|
||||||
import memberRenderer from './classMemberRenderer';
|
import svgDraw from './svgDraw';
|
||||||
|
|
||||||
describe('class member Renderer, ', function () {
|
describe('class member Renderer, ', function () {
|
||||||
describe('when parsing text to build method display string', function () {
|
describe('when parsing text to build method display string', function () {
|
||||||
it('should handle simple method declaration', function () {
|
it('should handle simple method declaration', function () {
|
||||||
const str = 'foo()';
|
const str = 'foo()';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('foo()');
|
expect(actual.displayText).toBe('foo()');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -13,7 +13,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle public visibility', function () {
|
it('should handle public visibility', function () {
|
||||||
const str = '+foo()';
|
const str = '+foo()';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('+foo()');
|
expect(actual.displayText).toBe('+foo()');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -21,7 +21,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle private visibility', function () {
|
it('should handle private visibility', function () {
|
||||||
const str = '-foo()';
|
const str = '-foo()';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('-foo()');
|
expect(actual.displayText).toBe('-foo()');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -29,7 +29,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle protected visibility', function () {
|
it('should handle protected visibility', function () {
|
||||||
const str = '#foo()';
|
const str = '#foo()';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('#foo()');
|
expect(actual.displayText).toBe('#foo()');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -37,7 +37,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle package/internal visibility', function () {
|
it('should handle package/internal visibility', function () {
|
||||||
const str = '~foo()';
|
const str = '~foo()';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('~foo()');
|
expect(actual.displayText).toBe('~foo()');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -45,7 +45,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should ignore unknown character for visibility', function () {
|
it('should ignore unknown character for visibility', function () {
|
||||||
const str = '!foo()';
|
const str = '!foo()';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('foo()');
|
expect(actual.displayText).toBe('foo()');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -53,7 +53,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle abstract classifier', function () {
|
it('should handle abstract classifier', function () {
|
||||||
const str = 'foo()*';
|
const str = 'foo()*';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('foo()');
|
expect(actual.displayText).toBe('foo()');
|
||||||
expect(actual.cssStyle).toBe('font-style:italic;');
|
expect(actual.cssStyle).toBe('font-style:italic;');
|
||||||
@@ -61,7 +61,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle static classifier', function () {
|
it('should handle static classifier', function () {
|
||||||
const str = 'foo()$';
|
const str = 'foo()$';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('foo()');
|
expect(actual.displayText).toBe('foo()');
|
||||||
expect(actual.cssStyle).toBe('text-decoration:underline;');
|
expect(actual.cssStyle).toBe('text-decoration:underline;');
|
||||||
@@ -69,7 +69,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should ignore unknown character for classifier', function () {
|
it('should ignore unknown character for classifier', function () {
|
||||||
const str = 'foo()!';
|
const str = 'foo()!';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('foo()');
|
expect(actual.displayText).toBe('foo()');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -77,7 +77,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle simple method declaration with parameters', function () {
|
it('should handle simple method declaration with parameters', function () {
|
||||||
const str = 'foo(int id)';
|
const str = 'foo(int id)';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('foo(int id)');
|
expect(actual.displayText).toBe('foo(int id)');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -85,7 +85,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle simple method declaration with single item in parameters', function () {
|
it('should handle simple method declaration with single item in parameters', function () {
|
||||||
const str = 'foo(id)';
|
const str = 'foo(id)';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('foo(id)');
|
expect(actual.displayText).toBe('foo(id)');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -93,7 +93,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle simple method declaration with single item in parameters with extra spaces', function () {
|
it('should handle simple method declaration with single item in parameters with extra spaces', function () {
|
||||||
const str = ' foo ( id) ';
|
const str = ' foo ( id) ';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('foo(id)');
|
expect(actual.displayText).toBe('foo(id)');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -101,7 +101,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle method declaration with return value', function () {
|
it('should handle method declaration with return value', function () {
|
||||||
const str = 'foo(id) int';
|
const str = 'foo(id) int';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('foo(id) : int');
|
expect(actual.displayText).toBe('foo(id) : int');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -109,7 +109,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle method declaration with generic return value', function () {
|
it('should handle method declaration with generic return value', function () {
|
||||||
const str = 'foo(id) List~int~';
|
const str = 'foo(id) List~int~';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('foo(id) : List<int>');
|
expect(actual.displayText).toBe('foo(id) : List<int>');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -117,7 +117,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle method declaration with generic parameter', function () {
|
it('should handle method declaration with generic parameter', function () {
|
||||||
const str = 'foo(List~int~)';
|
const str = 'foo(List~int~)';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('foo(List<int>)');
|
expect(actual.displayText).toBe('foo(List<int>)');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -125,33 +125,17 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle method declaration with all possible markup', function () {
|
it('should handle method declaration with all possible markup', function () {
|
||||||
const str = '+foo ( List~int~ ids )* List~Item~';
|
const str = '+foo ( List~int~ ids )* List~Item~';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('+foo(List<int> ids) : List<Item>');
|
expect(actual.displayText).toBe('+foo(List<int> ids) : List<Item>');
|
||||||
expect(actual.cssStyle).toBe('font-style:italic;');
|
expect(actual.cssStyle).toBe('font-style:italic;');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when parsing text for generic types', function () {
|
|
||||||
it('should handle open and close brackets in correct order', function () {
|
|
||||||
const str = 'foo(List~Item~)';
|
|
||||||
let actual = memberRenderer.parseGenericTypes(str);
|
|
||||||
|
|
||||||
expect(actual).toBe('foo(List<Item>)');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle open and close brackets in correct order with multiple usages', function () {
|
|
||||||
const str = 'foo(List~Item~) List~Item~';
|
|
||||||
let actual = memberRenderer.parseGenericTypes(str);
|
|
||||||
|
|
||||||
expect(actual).toBe('foo(List<Item>) List<Item>');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when parsing text to build field display string', function () {
|
describe('when parsing text to build field display string', function () {
|
||||||
it('should handle simple field declaration', function () {
|
it('should handle simple field declaration', function () {
|
||||||
const str = 'int[] ids';
|
const str = 'int[] ids';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('int[] ids');
|
expect(actual.displayText).toBe('int[] ids');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
||||||
@@ -159,7 +143,7 @@ describe('class member Renderer, ', function () {
|
|||||||
|
|
||||||
it('should handle field declaration with generic type', function () {
|
it('should handle field declaration with generic type', function () {
|
||||||
const str = 'List~int~ ids';
|
const str = 'List~int~ ids';
|
||||||
let actual = memberRenderer.parseMember(str);
|
let actual = svgDraw.parseMember(str);
|
||||||
|
|
||||||
expect(actual.displayText).toBe('List<int> ids');
|
expect(actual.displayText).toBe('List<int> ids');
|
||||||
expect(actual.cssStyle).toBe('');
|
expect(actual.cssStyle).toBe('');
|
Reference in New Issue
Block a user