Skip to content

Commit 699507e

Browse files
committed
create collectors
1 parent 53e7403 commit 699507e

File tree

12 files changed

+2123
-502
lines changed

12 files changed

+2123
-502
lines changed

docs/parser-listener-abstraction-solution-2.md

Lines changed: 776 additions & 0 deletions
Large diffs are not rendered by default.

src/collectors/BaseCollector.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { ASTNodeType, SequenceASTNode } from '@/parser/types/astNode.types';
2+
3+
interface ICollector<R> {
4+
visitNode(node: SequenceASTNode): void
5+
traverseNode?(node: SequenceASTNode): void
6+
postVisitNode?(node: SequenceASTNode): void
7+
reset(): void;
8+
result(): R;
9+
}
10+
11+
export abstract class BaseCollector<R> implements ICollector<R> {
12+
protected shouldSkip = false;
13+
protected nodeHandlers = new Map<ASTNodeType, (node: SequenceASTNode) => void>();
14+
15+
constructor() {
16+
this.registerNodeHandlers();
17+
}
18+
19+
20+
visitNode(node: SequenceASTNode): void {
21+
if (this.shouldStartSkip(node)) {
22+
this.shouldSkip = true
23+
}
24+
25+
const handler = this.nodeHandlers.get(node.getType() as ASTNodeType);
26+
if (handler) {
27+
handler(node);
28+
} else {
29+
throw new Error(`Unable to handle ${node.getType()}`);
30+
}
31+
32+
if (this.shouldStartSkip(node)) {
33+
this.shouldSkip = true
34+
}
35+
}
36+
37+
protected registerNodeHandler(nodeType: ASTNodeType, handler: (node: SequenceASTNode) => void): void {
38+
this.nodeHandlers.set(nodeType, handler);
39+
}
40+
41+
postVisitNode(node: SequenceASTNode): void {
42+
if(this.shouldEndSkip(node)) {
43+
this.shouldSkip = false
44+
}
45+
}
46+
47+
protected abstract registerNodeHandlers(): void;
48+
49+
protected shouldStartSkip(_: SequenceASTNode): boolean {
50+
return false
51+
}
52+
53+
protected shouldEndSkip(_: SequenceASTNode): boolean {
54+
return false
55+
}
56+
57+
abstract traverseNode?(node: SequenceASTNode): void
58+
59+
abstract reset(): void;
60+
61+
abstract result(): R
62+
}

src/collectors/MessageCollector.ts

Whitespace-only changes.
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import { BaseCollector } from './BaseCollector';
2+
import { Participants } from '@/parser/Participants';
3+
import {
4+
SequenceASTNode,
5+
ParticipantNode,
6+
MessageNode,
7+
CreationNode,
8+
FragmentNode,
9+
RetNode,
10+
GroupNode,
11+
ToNode,
12+
FromNode
13+
} from '@/parser/types/astNode.types';
14+
15+
export class ParticipantCollector extends BaseCollector<Participants> {
16+
private participants = new Participants();
17+
private groupId?: string;
18+
19+
registerNodeHandlers(): void {
20+
this.nodeHandlers.set('ParticipantNode', node => this.ParticipantNode(node as ParticipantNode));
21+
this.nodeHandlers.set('MessageNode', node => this.MessageNode(node as MessageNode));
22+
this.nodeHandlers.set('CreationNode', node => this.CreationNode(node as CreationNode));
23+
this.nodeHandlers.set('FragmentNode', node => this.GroupNode(node as FragmentNode));
24+
this.nodeHandlers.set('RetNode', node => this.RetNode(node as RetNode));
25+
this.nodeHandlers.set('ParametersNode', node => this.ParametersNode(node));
26+
this.nodeHandlers.set('ConditionNode', node => this.ConditionNode(node));
27+
// Both FromNode and ToNode use the same logic
28+
this.nodeHandlers.set("FromNode", node => this.FromOrToNode(node as FromNode));
29+
this.nodeHandlers.set("ToNode", node => this.FromOrToNode(node as ToNode));
30+
}
31+
32+
FromOrToNode(node: ToNode): void {
33+
if (this.shouldSkip) return;
34+
let participant = node.getText();
35+
const participantInstance = this.participants.Get(participant);
36+
37+
// Skip adding participant position if label is present
38+
if (participantInstance?.label) {
39+
this.participants.Add(participant, { isStarter: false });
40+
} else if (participantInstance?.assignee) {
41+
// If the participant has an assignee, calculate the position of the ctor and store it only.
42+
// Let's say the participant name is `"${assignee}:${type}"`, we need to get the position of ${type}
43+
// e.g. ret = new A() "ret:A".method()
44+
const range = node.getRange();
45+
const start = range[0] + participantInstance.assignee.length + 2;
46+
const position: [number, number] = [start, range[1]];
47+
const assigneePosition: [number, number] = [
48+
range[0] + 1,
49+
range[0] + participantInstance.assignee.length + 1,
50+
];
51+
this.participants.Add(participant, {
52+
isStarter: false,
53+
position: position,
54+
assigneePosition: assigneePosition,
55+
});
56+
} else {
57+
this.participants.Add(participant, {
58+
isStarter: false,
59+
position: node.getRange(),
60+
});
61+
}
62+
}
63+
64+
ParticipantNode(node: ParticipantNode): void {
65+
if (this.shouldSkip) return;
66+
67+
this.participants.Add(node.getName(), {
68+
isStarter: node.isStarter(),
69+
type: node.getType(),
70+
stereotype: node.getStereotype(),
71+
width: node.getWidth(),
72+
groupId: this.groupId || node.getGroupId(),
73+
label: node.getLabel(),
74+
explicit: node.isExplicit(),
75+
color: node.getColor(),
76+
comment: node.getComment(),
77+
position: node.getRange(),
78+
});
79+
}
80+
81+
MessageNode(node: MessageNode): void {
82+
if (this.shouldSkip) return;
83+
84+
const from = node.getFrom();
85+
const to = node.getTo();
86+
87+
if (from) {
88+
this.participants.Add(from, {
89+
isStarter: false,
90+
position: node.getRange(),
91+
});
92+
}
93+
94+
if (to) {
95+
const participantInstance = this.participants.Get(to);
96+
if (participantInstance?.label) {
97+
this.participants.Add(to, { isStarter: false });
98+
} else if (participantInstance?.assignee) {
99+
// Handle assignee position calculation similar to ToCollector
100+
const range = node.getRange();
101+
if (range) {
102+
const start = range[0] + participantInstance.assignee.length + 2;
103+
const position: [number, number] = [start, range[1]];
104+
const assigneePosition: [number, number] = [
105+
range[0] + 1,
106+
range[0] + participantInstance.assignee.length + 1,
107+
];
108+
this.participants.Add(to, {
109+
isStarter: false,
110+
position: position,
111+
assigneePosition: assigneePosition,
112+
});
113+
}
114+
} else {
115+
this.participants.Add(to, {
116+
isStarter: false,
117+
position: node.getRange(),
118+
});
119+
}
120+
}
121+
}
122+
123+
CreationNode(node: CreationNode): void {
124+
if (this.shouldSkip) return;
125+
126+
const owner = node.getOwner();
127+
const assignee = node.getAssignee();
128+
const assigneePosition = node.getAssigneePosition();
129+
130+
const participantInstance = this.participants.Get(owner);
131+
132+
if (!participantInstance?.label) {
133+
this.participants.Add(owner, {
134+
isStarter: false,
135+
position: node.getRange(),
136+
assignee,
137+
assigneePosition,
138+
});
139+
} else {
140+
this.participants.Add(owner, {
141+
isStarter: false,
142+
});
143+
}
144+
}
145+
146+
GroupNode(node: GroupNode): void {
147+
this.groupId = node.getText();
148+
}
149+
150+
RetNode(node: RetNode): void {
151+
if (node.getAsyncMessage()) {
152+
return;
153+
}
154+
155+
const returnFrom = node.getFrom();
156+
const returnTo = node.getTo();
157+
158+
if (returnFrom) {
159+
this.participants.Add(returnFrom, {
160+
isStarter: false,
161+
position: node.getRange(),
162+
});
163+
}
164+
165+
if (returnTo) {
166+
this.participants.Add(returnTo, {
167+
isStarter: false,
168+
position: node.getRange(),
169+
});
170+
}
171+
}
172+
173+
ParametersNode(_: SequenceASTNode): void {
174+
this.shouldSkip = true;
175+
}
176+
177+
ConditionNode(_: SequenceASTNode): void {
178+
this.shouldSkip = true;
179+
}
180+
181+
postVisitNode(node: SequenceASTNode): void {
182+
super.postVisitNode(node);
183+
184+
const nodeType = node.getType();
185+
186+
// Handle group exit
187+
if (nodeType === 'GroupNode') {
188+
this.groupId = undefined;
189+
}
190+
191+
// Exit blind mode
192+
if (nodeType === 'ParametersNode' || nodeType === 'ConditionNode') {
193+
this.shouldSkip = false;
194+
}
195+
}
196+
197+
traverseNode?(node: SequenceASTNode): void {
198+
// Default traversal implementation if needed
199+
}
200+
201+
reset(): void {
202+
this.participants = new Participants();
203+
this.groupId = undefined;
204+
this.shouldSkip = false;
205+
this.shouldSkip = false;
206+
}
207+
208+
result(): Participants {
209+
return this.participants;
210+
}
211+
}

src/collectors/UnifiedCollector.ts

Whitespace-only changes.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// src/components/optimized/OptimizedStatement.tsx
2+
import React from 'react';
3+
import { SequenceASTNode } from '@/parser/types/astNode.types';
4+
import { areNodesEqual } from '../optimizations/nodeComparisons';
5+
6+
interface StatementProps {
7+
node: SequenceASTNode;
8+
origin: string;
9+
number?: string;
10+
collapsed?: boolean;
11+
}
12+
13+
/**
14+
* Optimized Statement component with custom memoization
15+
*/
16+
export const Statement = React.memo<StatementProps>(
17+
({ node, origin, number, collapsed }) => {
18+
// Extract properties once using getters
19+
// These values are memoized inside the adapter
20+
const nodeType = node.getType();
21+
const comment = node.getComment?.() || '';
22+
23+
// Use React.useMemo for expensive computations
24+
const subProps = React.useMemo(() => ({
25+
className: cn('text-left text-sm text-skin-message', {
26+
hidden: collapsed && !node.isReturn?.(),
27+
}),
28+
node,
29+
origin,
30+
comment,
31+
number,
32+
}), [node, origin, comment, number, collapsed]);
33+
34+
// Render appropriate component based on type
35+
if (node.isFragment?.()) {
36+
const fragmentNode = node as FragmentNode;
37+
switch (fragmentNode.getFragmentType()) {
38+
case 'loop': return <FragmentLoop {...subProps} />;
39+
case 'alt': return <FragmentAlt {...subProps} />;
40+
case 'par': return <FragmentPar {...subProps} />;
41+
default: return null;
42+
}
43+
}
44+
45+
if (node.isCreation?.()) {
46+
return <Creation {...subProps} />;
47+
}
48+
49+
if (node.isMessage?.()) {
50+
return <Interaction {...subProps} />;
51+
}
52+
53+
if (node.isAsyncMessage?.()) {
54+
return <InteractionAsync {...subProps} />;
55+
}
56+
57+
return null;
58+
},
59+
areNodesEqual // Custom comparison function
60+
);
61+
62+
Statement.displayName = 'Statement';

src/parser/Participants.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
export type Position = [number, number];
22

33
interface ParticipantOptions {
4-
isStarter?: boolean;
5-
stereotype?: string;
6-
width?: number;
7-
groupId?: number | string;
8-
label?: string;
9-
explicit?: boolean;
10-
type?: string;
11-
color?: string;
12-
comment?: string;
13-
assignee?: string;
14-
position?: Position;
15-
assigneePosition?: Position;
4+
isStarter?: boolean | null;
5+
stereotype?: string | null;
6+
width?: number | null;
7+
groupId?: number | string | null;
8+
label?: string | null;
9+
explicit?: boolean | null;
10+
type?: string | null;
11+
color?: string | null;
12+
comment?: string | null;
13+
assignee?: string | null;
14+
position?: Position | null;
15+
assigneePosition?: Position | null;
1616
}
1717

1818
export const blankParticipant = {
@@ -33,16 +33,16 @@ export const blankParticipant = {
3333

3434
export class Participant {
3535
name: string;
36-
private stereotype: string | undefined;
37-
private width: number | undefined;
38-
private groupId: number | string | undefined;
39-
explicit: boolean | undefined;
40-
isStarter: boolean | undefined;
41-
label: string | undefined;
42-
private type: string | undefined;
43-
private color: string | undefined;
44-
private comment: string | undefined;
45-
private assignee: string | undefined;
36+
stereotype: string | null | undefined;
37+
width: number | null | undefined;
38+
groupId: number | string | null | undefined;
39+
explicit: boolean | null | undefined;
40+
isStarter: boolean | null | undefined;
41+
label: string | null | undefined;
42+
type: string | null | undefined;
43+
color: string | null | undefined;
44+
comment: string | null | undefined;
45+
assignee: string | null | undefined;
4646
positions: Set<Position> = new Set();
4747
assigneePositions: Set<Position> = new Set();
4848

0 commit comments

Comments
 (0)