Skip to content

Commit 5002944

Browse files
author
anquetil
committed
Second post in "famix-tools" serie: visitor generator
1 parent 09db99e commit 5002944

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
layout: post
3+
title: "Generating a visitor infrastructure for a given meta-model"
4+
date: 2025-02-26 16:57:00
5+
background: '/img/posts/2023-09-26-new-UMLDocumentor/bg-post.jpg'
6+
author: Nicolas Anquetil
7+
comments: true
8+
tags: famix-tools
9+
---
10+
11+
*This post is part of a serie dedicated to* Famix Tools
12+
13+
Once we have a model of a program in Famix, we often find ourselves wanting to ¨ go through it" to apply some systematic analysis or modification.
14+
For example one could want to export the model as source-code [https://github.com/moosetechnology/FAMIX2Java](https://github.com/moosetechnology/FAMIX2Java).
15+
16+
The [Visitor design pattern](https://en.wikipedia.org/wiki/Visitor_pattern) is well adapted for these tasks.
17+
Note that the [double-dispatch mechanism](https://en.wikipedia.org/wiki/Double_dispatch), which is an integral part of the visitor pattern, may also be useful to have entity specific actions even if one does not want to visit the entire model.
18+
19+
The Visitor pattern requires:
20+
- an `accept: aVisitor` method in every entity of the meta-model (eg.: in FamixJavaClass, FamixJavaMethod, FamixJavaAttribute,...)
21+
- `visitFamixXYZ: aFamixXYZ` for all entites to be visited, in the visitor class
22+
- the `accept:` methods invoke the correct `visitFamixXYZ:` method of the visitor depending on the class it is implemented in
23+
- the `visitFamixXYZ:` by default recursively visits all the "children" of the entity being visited (eg.: `visitFamixJavaClass:` should trigger the visit of the attributes and methods of the class)
24+
25+
For large meta-models, this can be cumbersome to implement as there may be many kinds of entities (43 for FamixJava) and the work is very repetitive.
26+
The tool **FamixVisitorCodeGenerator** can do all the work for you, automatically.
27+
28+
## FamixVisitorCodeGenerator
29+
30+
Taking advantage of the meta-description of the Famix entities and the reflective nature of Pharo, it generates the `accept:` and `visitFamixXYZ:` for all entities of a meta-model.
31+
32+
Usage exmaple:
33+
```smalltalk
34+
FamixVisitorCodeGenerator new
35+
package: 'Famix-Java-Entities' visitorClass: FamixJavaVisitor .
36+
```
37+
38+
or for a FAST meta-model:
39+
```smalltalk
40+
FamixVisitorCodeGenerator new
41+
package: 'FAST-Java-Entities' visitorClass: FASTJavaVisitor.
42+
```
43+
44+
The tool needs an empty visitor class (or trait) created by the user (`FamixJavaVisitor` in the example above), and a Pharo package containing the Famix classes of a meta-model (`Famix-Java-Entities` in the example above).
45+
From this it will:
46+
- create an `accept:` method in all the classes in the given Famix package;
47+
- the `accept:` methods are created as extensions made by the package of the visitor;
48+
- the `accept:` methods invoke the correct `visitFamixXYZ:` depending on the class they are implemented in.
49+
- a setter method allows to skip this part: `generateAccepts: false`
50+
- the `visitFamixXYZ:` methods are created in the visitor class (or trait) for a "maximal visit" (see below).
51+
52+
## Visiting methods
53+
54+
For a friendlier visitor, it is convenient that the visitor methods reproduce the inheritance hierarchy of the entities.
55+
For example, if `FamixJavaAttribute` and `FamixJavaParameter` both inherit from `FamixJavaVariable` entity, it is convenient that `visitFamixJavaAttribute:` and `visitFamixJavaParameter:` both call `visitFamixJavaVariable:` so that a common behaviour for the visit can be implemented only once.
56+
57+
Since Famix heavily relies on traits to compose meta-models, the same idea applies to used traits.
58+
For example many entities implements `FamixTSourceEntity` to get a `sourceAnchor`.
59+
For all these entities, and if we need a generic behavior based on the source anchor, it is convenient that all the `visitXYZ:` methods call `visitTSourceEntity:` where the common behavior can be implemented.
60+
61+
Finally it might be convenient that the `visitXYZ:` methods, visit all the entites related to the one we are visiting.
62+
For example when visiting a `FamixJavaVariable`, it might be interesting to recursively visit its `declaredType`.
63+
64+
All these conditions defines what we call a "maxium" visit of the meta-model.
65+
66+
## "Maximum" Visit
67+
68+
As described above, the maximum `visitXYZ:` methods will trigger a resursive visit of many things (super-class visit method, used traits visit method, all entities related to the one being visited).
69+
70+
One down side of this is that a maximum visit is not a viable one in Famix because all relationships are bi-directionnal, causing infinite loops in the visit.
71+
For example, if in a `FamixJavaVariable` we recursively visit its `declaredType`, then in the `FamixJavaTType` we will recursively visit the `typedEntities` including the one we just came from.
72+
73+
There could be various solution to this problem:
74+
- implement a memory mechanism in the visitor to remember what entities were already visited and do not go back to them.
75+
- do not visit all relationships of an entity, but only its "children" relationship
76+
- let the user handle the problem
77+
78+
For now the tool rely on the third solution.
79+
If an actual visitor inherits (or use) the generated visitor, it must take care or redefining the visit methods so that it does not enter in an infinite loop (implementing any of the two other solutions)
80+
81+
In this sense, the maximum visit methods can be viewed as cheat sheets, showing all the things that one could do when visiting an entity.
82+
83+
In the future we might implement the second solution (visit only children) as an option of the visitor.
84+
For now it is important to remember that **the generated visitor cannot be used as is**.
85+
Methods must be redefined to get a viable visitor.
86+
87+
## class vs. trait visitor
88+
89+
The natural action would be to define the visitor as aclass from which all required actual visitors will inherit.
90+
However, because visitors are so handy to go through the entire model, we discovered that we needed a lot of them (eg. visitors to create a copy of a model, to re-export a model) and they sometimes need to inherit from some other class.
91+
92+
As such, we rather recommend to create the maximal visitor as a trait that can be used by all actual visitors.
93+
This make no difference for the `FamixVisitorCodeGenerator` and it might prove very useful.

0 commit comments

Comments
 (0)