Skip to content

Commit

Permalink
Merge pull request #1178 from jgreywolf/1119-SpecifyMethodReturnTypeI…
Browse files Browse the repository at this point in the history
…nClassDiagram

1119 specify method return type in class diagram
  • Loading branch information
knsv authored Jan 13, 2020
2 parents fdbc44e + fa1331f commit eade3d0
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 21 deletions.
15 changes: 15 additions & 0 deletions cypress/integration/rendering/classDiagram.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,4 +274,19 @@ describe('Class diagram', () => {
);
cy.get('svg');
});

it('11: should render a simple class diagram with return type on method', () => {
imgSnapshotTest(
`
classDiagram
class Class10~T~ {
int[] id
test(int[] ids) bool
testArray() bool[]
}
`,
{}
);
cy.get('svg');
});
});
15 changes: 9 additions & 6 deletions docs/classDiagram.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ Naming convention: a class name should be composed of alphanumeric (unicode allo

UML provides mechanisms to represent class members, such as attributes and methods, and additional information about them.

Mermaid distinguishes between attributes and functions/methods based on if the **parenthesis** `()` are present or not. The ones with `()` are treated as functions/methods, and others as attributes.
Mermaid distinguishes between attributes and functions/methods based on if the **parenthesis** `()` are present or not. The ones with `()` are treated as functions/methods, and others as attributes. To indicate a return type for a method, enclose the type within **square brackets** `[]`


There are two ways to define the members of a class, and regardless of whichever syntax is used to define the members, the output will still be same. The two different ways are :
Expand All @@ -115,16 +115,16 @@ There are two ways to define the members of a class, and regardless of whichever
class BankAccount
BankAccount : +String owner
BankAccount : +BigDecimal balance
BankAccount : +deposit(amount)
BankAccount : +withdrawl(amount)
BankAccount : +deposit(amount) bool
BankAccount : +withdrawal(amount)
```

``` mermaid
classDiagram
class BankAccount
BankAccount : +String owner
BankAccount : +BigDecimal balance
BankAccount : +deposit(amount)
BankAccount : +deposit(amount) : bool
BankAccount : +withdrawl(amount)
```

Expand All @@ -133,7 +133,7 @@ There are two ways to define the members of a class, and regardless of whichever
class BankAccount{
+String owner
+BigDecimal balance
+deposit(amount)
+deposit(amount) bool
+withdrawl(amount)
}
```
Expand All @@ -142,12 +142,15 @@ class BankAccount{
class BankAccount{
+String owner
+BigDecimal balance
+deposit(amount)
+deposit(amount) : bool
+withdrawl(amount)
}
```


#### Return Type
Optionally you can end the method/function definition with the data type that will be returned

#### Visibility
To specify the visibility of a class member (i.e. any attribute or method), these notations may be placed before the member's name, but it is optional:

Expand Down
32 changes: 31 additions & 1 deletion src/diagrams/class/classDiagram.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ describe('class diagram, ', function () {
parser.parse(str);
});

it('should handle parsing of method statements grouped by brackets', function () {
it('should handle parsing of method statements grouped by brackets', function () {
const str =
'classDiagram\n' +
'class Dummy_Class {\n' +
Expand All @@ -121,6 +121,36 @@ describe('class diagram, ', function () {
parser.parse(str);
});

it('should handle return types on methods', function () {
const str =
'classDiagram\n' +
'Object <|-- ArrayList\n' +
'Object : equals()\n' +
'Object : -Object[] objects\n' +
'Object : +getObjects() Object[]\n' +
'ArrayList : Dummy elementData\n' +
'ArrayList : getDummy() Dummy';

parser.parse(str);
});

it('should handle return types on methods grouped by brackets', function () {
const str =
'classDiagram\n' +
'class Dummy_Class {\n' +
'string data\n' +
'getDummy() Dummy\n' +
'}\n' +
'\n' +
'class Flight {\n' +
' int flightNumber\n' +
' datetime departureTime\n' +
' getDepartureTime() datetime\n' +
'}';

parser.parse(str);
});

it('should handle parsing of separators', function () {
const str =
'classDiagram\n' +
Expand Down
63 changes: 49 additions & 14 deletions src/diagrams/class/classRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,23 +288,14 @@ const drawClass = function(elem, classDef) {
}

const addTspan = function(textEl, txt, isFirst) {
let isMethod = txt.indexOf(')') > 1;
let displayText = txt;
let cssStyle = '';
let methodEnd = txt.indexOf(')') + 1;

if (methodEnd > 1 && methodEnd <= txt.length) {
let classifier = txt.substring(methodEnd);

switch (classifier) {
case '*':
cssStyle = 'font-style:italic;';
break;
case '$':
cssStyle = 'text-decoration:underline;';
break;
}

displayText = txt.substring(0, methodEnd);
if (isMethod) {
let method = buildDisplayTextForMethod(txt);
displayText = method.displayText;
cssStyle = method.cssStyle;
}

const tSpan = textEl
Expand All @@ -321,6 +312,50 @@ const drawClass = function(elem, classDef) {
}
};

const buildDisplayTextForMethod = function(txt) {
let regEx = /(\+|-|~|#)?(\w+)\s?\((\w+(<\w+>|\[\])?\s?(\w+)?)?\)\s?([*|$])?\s?(\w+(<\w+>|\[\])?)?/;

let cssStyle = '';
let displayText = txt;
let methodName = txt;
let classifier = '';

let parsedText = txt.match(regEx);

if (parsedText) {
let visibility = parsedText[1] ? parsedText[1].trim() : '';
methodName = parsedText[2] ? parsedText[2].trim() : '';
let parameters = parsedText[3] ? parsedText[3].trim() : '';
classifier = parsedText[6] ? parsedText[6].trim() : '';
let returnType = parsedText[7] ? ' : ' + parsedText[7].trim() : '';

displayText = visibility + methodName + '(' + parameters + ')' + returnType;
} else {
let methodEnd = displayText.indexOf(')') + 1;
classifier = displayText.substring(methodEnd, methodEnd + 1);
if (classifier !== '' && classifier !== ' ') {
displayText = displayText.replace(classifier, '');
}
}

switch (classifier) {
case '*':
cssStyle = 'font-style:italic;';
break;
case '$':
cssStyle = 'text-decoration:underline;';
break;
}

let method = {
methodname: methodName,
displayText: displayText,
cssStyle: cssStyle
};

return method;
};

const id = classDef.id;
const classInfo = {
id: id,
Expand Down

0 comments on commit eade3d0

Please sign in to comment.