Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Class: Support for abstract methods #1131

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions cypress/integration/rendering/classDiagram.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,31 @@ describe('Class diagram', () => {
cy.get('svg');
});

it('5: should render a simple class diagram with Generic class', () => {
it('5: should render a simple class diagram with abstract method', () => {
imgSnapshotTest(
`
classDiagram
Class01 <|-- AveryLongClass : Cool
Class01 : someMethod()*
`,
{}
);
cy.get('svg');
});

it('6: should render a simple class diagram with static method', () => {
imgSnapshotTest(
`
classDiagram
Class01 <|-- AveryLongClass : Cool
Class01 : someMethod()$
`,
{}
);
cy.get('svg');
});

it('7: should render a simple class diagram with Generic class', () => {
imgSnapshotTest(
`
classDiagram
Expand All @@ -184,7 +208,7 @@ describe('Class diagram', () => {
cy.get('svg');
});

it('6: should render a simple class diagram with Generic class and relations', () => {
it('8: should render a simple class diagram with Generic class and relations', () => {
imgSnapshotTest(
`
classDiagram
Expand Down
30 changes: 19 additions & 11 deletions docs/classDiagram.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,10 @@ 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.

#### 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 is it optional:
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.

- `+` Public
- `-` Private
- `#` Protected
- `~` Package

Mermaid distinguishes between attributes and functions/methods based on if the **parenthesis** `()` are present or not. The one with `()` are treated as functions/methods, and others as attributes.

There are two ways to define the members of a class, and regardless of the whichever syntax is used to define the members, the output will still be same. The two different ways are :
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 :
- Associate a member of a class using **:** (colon) followed by member name, useful to define one member at a time. For example:

```
Expand All @@ -125,7 +118,7 @@ There are two ways to define the members of a class, and regardless of the which
BankAccount : +deposit(amount)
BankAccount : +withdrawl(amount)
```
```mermaid
``` mermaid
classDiagram
class BankAccount
BankAccount : +String owner
Expand All @@ -150,7 +143,22 @@ class BankAccount{
+BigDecimal balance
+deposit(amount)
+withdrawl(amount)
}```
}
```


#### 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:

- `+` Public
- `-` Private
- `#` Protected
- `~` Package

>_note_ you can also include additional _classifers_ to a method definition by adding the following notations to the end of the method, i.e.: after the `()`:
> - `*` Abstract e.g.: `someAbstractMethod()*`
> - `$` Static e.g.: `someStaticMethod()$`


## Defining Relationship
A relationship is a general term covering the specific types of logical connections found on class and object diagrams.
Expand Down
2 changes: 1 addition & 1 deletion src/diagrams/class/classDb.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export const addMember = function(className, member) {
if (memberString.startsWith('<<') && memberString.endsWith('>>')) {
// Remove leading and trailing brackets
theClass.annotations.push(memberString.substring(2, memberString.length - 2));
} else if (memberString.endsWith(')')) {
} else if (memberString.indexOf(')') > 0) {
theClass.methods.push(memberString);
} else if (memberString) {
theClass.members.push(memberString);
Expand Down
22 changes: 22 additions & 0 deletions src/diagrams/class/classDiagram.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,5 +442,27 @@ describe('class diagram, ', function () {
expect(testClass.methods[0]).toBe('test()');
expect(testClass.methods[1]).toBe('foo()');
});

it('should handle abstract methods', function () {
const str = 'classDiagram\n' + 'class Class1\n' + 'Class1 : someMethod()*';
parser.parse(str);

const testClass = parser.yy.getClass('Class1');
expect(testClass.annotations.length).toBe(0);
expect(testClass.members.length).toBe(0);
expect(testClass.methods.length).toBe(1);
expect(testClass.methods[0]).toBe('someMethod()*');
});

it('should handle static methods', function () {
const str = 'classDiagram\n' + 'class Class1\n' + 'Class1 : someMethod()$';
parser.parse(str);

const testClass = parser.yy.getClass('Class1');
expect(testClass.annotations.length).toBe(0);
expect(testClass.members.length).toBe(0);
expect(testClass.methods.length).toBe(1);
expect(testClass.methods[0]).toBe('someMethod()$');
});
});
});
26 changes: 25 additions & 1 deletion src/diagrams/class/classRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,34 @@ const drawClass = function(elem, classDef) {
logger.info('Rendering class ' + classDef);

const addTspan = function(textEl, txt, isFirst) {
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);
}

const tSpan = textEl
.append('tspan')
.attr('x', conf.padding)
.text(txt);
.text(displayText);

if (cssStyle !== '') {
tSpan.attr('style', cssStyle);
}

if (!isFirst) {
tSpan.attr('dy', conf.textHeight);
}
Expand Down