aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/docs/src/learn/language/classes/index.md
blob: 73108039ce292b2103366b4146ba95adc862bcd9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
---
SPDX-FileCopyrightText: 2024 The Refinery Authors
SPDX-License-Identifier: EPL-2.0
description: Metamodeling in Refinery
sidebar_position: 0
---

# Classes and references

Refinery supports _metamodeling_ to describe the desired structure of generated models.

The metamodeling facilities are inspired by object-oriented software and the [Eclipse Modeling Foundation](https://eclipse.dev/modeling/emf/) (EMF) Core, a lightweight framework for data models.
The textual syntax in Refinery for defining metamodels is largely compatible with [Xcore](https://wiki.eclipse.org/Xcore), a textual syntax for EMF metamodels.

## Classes

Classes are declared with the `class` keyword.

Like in many programming languages, class members are specified between curly braces `{}`.
If a class has no members, the declaration may be terminated with a `.` instead.

```refinery
% Class with no members.
class Region {}

% Alternative syntax without curly braces.
class State.
```

By default, a _new object_ is added to the partial model to represent the instances of a class.
For example, the new objects `Region::new` and `State::new` represent potential instances of the classes `Region` and `State`, respectively:

import NewObjectsSimple from './NewObjectsSimple.svg';

<NewObjectsSimple />

As you can see, no new objects represent potential nodes that are instanceof of both `Region` and `State`.
In fact, such instances are not permitted at all.
Each node must the instance of a _single most-specific class:_

import InvalidInstance from './InvalidInstance.svg';

<InvalidInstance />

### Inheritance

Like in object-oriented programming languages, classes may declare _superclasses_ with the `extends` keyword.
The inheritance hierarchy may not contain any cycles (a class cannot be a superclass of itself), but _multiple inheritance_ is allowed.

Classes that can't be instantiated directly (i.e., a subclass must be instantiated instead) can be marked with the `abstract` keyword.
Such classes do not have a _new object,_ since there are no direct instances to represent.

```refinery
abstract class CompositeElement.
class Region.
abstract class Vertex.
abstract class RegularState extends Vertex.
class State extends RegularState, CompositeElement.
```

Notice that the new object `State::new` is an instance of `CompositeElement`, `Vertex`, `RegularState`, and `State` as well.

import NewObjectsWithInheritance from './NewObjectsWithInheritance.svg';

<NewObjectsWithInheritance />

## References

The graph structure of model generated by Refinery is determined by the _references_ of the metamodel, which will appear as labeled edges between nodes (class instances).

References are declared as class members by providing the _target type,_ and optional _multiplicity,_ and the name of the reference:

```refinery
class Vertex.
class Transition {
    Vertex[1] source
    Vertex[1] target
}
```

import ReferencesSimple from './ReferencesSimple.svg';

<ReferencesSimple />

You may add the `refers` keyword for compatibility with [Xcore](https://wiki.eclipse.org/Xcore). The following specification is equivalent:

```refinery
class Vertex.
class Transition {
    refers Vertex[1] source
    refers Vertex[1] target
}
```

### Opposite constraints

The `opposite` keywords specifies that two references are in an _opposite_ relationship, i.e., if one reference is present in a direction, the other must be present between the same nodes in the opposite direction.

```
class Vertex {
    Transition[] outgoingTransition opposite source
    Transition[] incomingTransition opposite target
}
class Transition {
    Vertex[1] source opposite outgoingTransition
    Vertex[1] target opposite incomingTransition
}
```

import ReferencesOppositeInstance from './ReferencesOppositeInstance.svg';

<ReferencesOppositeInstance />

Opposites must be declared in pairs: it is a specification error to declare the `opposite` for one direction but not the other.

Unlike in EMF, references that are the `opposite` of themselves are also supported.
These must always be present in both directions between two nodes.
Thus, they correspond to undirected graph edges.

```refinery
class Person {
    Person[] friend opposite friend
}
```

import ReferencesOppositeSelf from './ReferencesOppositeSelf.svg';

<ReferencesOppositeSelf />

### Multiplicity

_Multiplicity constrains_ can be provided after the reference type in square braces.
They specify how many _outgoing_ references should exist for any given instance of the class.

:::info

To control the number of _incoming_ references, add an `opposite` reference with multiplicity constraint.

:::

A multiplicity constraint is of the form `[n..m]`, where the non-negative integer `n` is the _lower_ bound of outgoing references,
and `m` is a positive integer or `*` corresponding to the _upper_ bound of outgoing references.
The value of `*` represent a reference with _unbounded_ upper multiplicity.

If `n` = `m`, the shorter form `[n]` may be used.
The bound `[0..*]` may be abbreviated as `[]`.
If the multiplicity constraint is omitted, the bound `[0..1]` is assumed.

---

In the following model, the node `v1` satisfies all multiplicity constraints of `outgoingTransition`.
The node `v2` violates the lower bound constraint, while `v3` violates the upper bound constraint.
All `Transition` instances satisfy the multiplicity constrains associated with `source`.

```refinery
class Vertex {
    Transition[2..3] outgoingTransition opposite source
}
class Transition {
    Vertex[1] source opposite outgoingTransition
}
```

import MultiplicityConstraintsInstance from './MultiplicityConstraintsInstance.svg';

<MultiplicityConstraintsInstance />

### Containment hierarchy

To structure models and ensure their connectedness, Refinery supports _containment_ constraints.

References may be marked as _containment_ references with the `contains` keyword.

Classes that are the _target type_ of at least one _containment_ reference are considered `contained`.
An instance of a `contained` class must have exactly 1 incoming containment reference.
Instances of classes that are not `contained` must _not_ have any incoming containment references.

Containment references have to form a _forest_, i.e., they must not contain any cycles.
The _roots_ of the forest are instances of classes that are not `contained`, while `contained` classes for the internal nodes and leaves of the trees.

Opposites of _containment_ references have to be marked with the `container` keyword.
They must not specify any multiplicity constraint, since the multiplicity is already implied by the containment hierarchy.

---

In the following model, the instances of `Region` are the roots of the containment hierarchy.
The classes `Vertex` are `Transition` are both considered `contained`.

```refinery
class Region {
    contains Vertex[] vertices opposite region
}

class Vertex {
    container Region region opposite vertices
    contains Transition[] outgoingTransition opposite source
    Transition[] incomingTransition opposite target
}

class Transition {
    container Vertex source opposite outgoingTransition
    Vertex[1] target opposite incomingTransition
}
```

Containment edges are show with **thick** lines:

import ContainmentInstance from './ContainmentInstance.svg';

<ContainmentInstance />

Containment edges form trees, while non-containment references, such as `target`, may point across the containment hierarchy.