---
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';
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';
### 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';
## 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';
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';
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';
### 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';
### 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';
Containment edges form trees, while non-containment references, such as `target`, may point across the containment hierarchy.