On this page ...
Projections
A projection is defined using angular brackets,
in a style similar to Markdown. The appearance of the definition
closely resembles the resulting output. Everything within square
brackets ([]), except text enclosed in ${}, is interpreted
literally, including the indentation. For more details, refer to the information
on Indentation.
// Insurance/src/defs/editor-indentation.edit#L6-L14
Text {
[
This is
literal text
that is projected in the
editor
for every concept of type Text.
]
} Including Properties
When defining a projection for a concept or interface, you will likely need
to include its properties. This is done using the special notation ${}, which
instructs Freon to include a property according to the projection of its type.
For example, given the following metamodel:
// Insurance/src/defs/language-main.ast#L22-L32
// A BaseProduct defines all the different elements ('parts') that can be
// used to create a marketable InsuranceProduct.
concept BaseProduct {
name: identifier; // internal name
isUnderConstruction: boolean; // defines whether this base product is still 'raw'
theme: InsuranceTheme; // the 'kind' of insurance
parts: InsurancePart[]; // all parts of this product
// The following properties are present to show the different options for displaying booleans.
isApprovedLevel1: boolean;
isApprovedLevel2: boolean;
isApprovedLevel3: boolean; consider the property self.body, which is of type DocuExpression. It
will be displayed based on the projection defined for DocuExpression.
Meanwhile, self.declaredType is a property of the abstract type DocuType.
This property will be projected according to the definition of
the specific (non-abstract) subtype of DocuType encountered at runtime.
// Insurance/src/defs/editor-main-default.edit#L38-L49
InsuranceProduct {[
Insurance Product ${name} ( public name: ${productName} ) USES ${basedOn horizontal separator[, ]}
Themes: ${themes horizontal separator[, ]}
Premium: ${advertisedPremium} per ${nrPremiumDays}
Insured risks:
${parts vertical terminator [;]}
Calculation
[? Risk adjusted by = ${riskAdjustment} ]
calculated premium: ${calculation}
[?Helper functions:
${helpers vertical}]
]} self.declaredType.name is not a valid property projection.self is optional self may be omitted.Using Named Projections
By default, a property you include will be displayed using the projection
defined for its type. Freon determines this projection based on the
precedence specified in the .edit files.
If you want to use a specific projection from a different editor, you can use a named property projection. In this case, Freon will look for the projection in the editor with the specified name.
In the next example, the projection for self.parts:comments will first be searched
in the editor named comments. If it is not found there,
Freon will fall back to the standard precedence order of projections.
// Insurance/src/defs/editor-named-proj.edit#L3-L8
BaseProduct {[
/* In this projection 'self.parts' is always shown according to the projection */
/* defined for concept InsurancePart in the editor 'comments'. */
Base Products ${self.name} for ${self.theme}
${self.parts:comments}
]} Lists
For list properties, you can specify horizontal or vertical projection. Both options are optional. If
neither vertical nor horizontal is specified, the property
will be displayed as a vertical list by default.
You can also choose to project a list property as a table.
For a list, you can include the following options:
- A separator string, which will appear between each element.
- A terminator string, which will appear after each element.
- An initiator string, which will appear before each element.
All of these are optional. The default separator is a single space.
In the following example, the list parts is displayed vertically
with a ';' terminator. The list themes is displayed horizontally
with a ', ' separator. The list helpers is shown as a vertical
list without any separator, terminator, or initiator. In fact,
the vertical keyword could be omitted for helpers, as it is the
default projection for lists.
// Insurance/src/defs/editor-main-default.edit#L38-L49
InsuranceProduct {[
Insurance Product ${name} ( public name: ${productName} ) USES ${basedOn horizontal separator[, ]}
Themes: ${themes horizontal separator[, ]}
Premium: ${advertisedPremium} per ${nrPremiumDays}
Insured risks:
${parts vertical terminator [;]}
Calculation
[? Risk adjusted by = ${riskAdjustment} ]
calculated premium: ${calculation}
[?Helper functions:
${helpers vertical}]
]} horizontal and vertical.Tables
If a property is a list, you can choose to display it as a table. Tables can be either row-based or column-based. In a row-based table, each element of the list is displayed in a separate row, while in a column-based table, each element is displayed in a single column. The default is row-based.
Defining a table involves two steps:
- Add the keyword
tableto the list property you want to display as a table. Optionally, you can also specify eitherrowsorcolumns. - Define a table-projection for the type of the elements in the list. This projection specifies the table headers and
- determines how the elements of the list are arranged in rows or columns.
To project the parts property of concept BaseProduct as a row based table,
you can use the following code.
// Insurance/src/defs/editor-tables.edit#L13-L16
BaseProduct {[
Base Products ${name} for ${theme}
${parts table rows}
]} Given the above example, there should also be a projection tagged table for the
concept InsurancePart (the type of parts).
The example below defines four columns or rows, each with its own header.
// Insurance/src/defs/editor-tables.edit#L6-L11
InsurancePart{
table [
Name | risk | pay out | is approved
${name} | ${statisticalRisk} | ${maximumPayOut} | ${isApproved}
]
} name is a string, and will be displayed as an editable text,
and maximumPayout is a EuroLiteral Note that the properties can be lists themselves, which can be displayed as lists or tables.Optional Projections
When a property is marked optional in the language structure definition (the .ast files), the projection
of this property should also be optional. This is indicated by [?.
In the next example both the property riskAdjustment and helpers are only shown if they are present.
If they are not present, respectively the text Risk adjusted by = or Helper functions: is omitted as well.
Note that optional projections for non-optional properties are not allowed.
// Insurance/src/defs/editor-main-default.edit#L38-L49
InsuranceProduct {[
Insurance Product ${name} ( public name: ${productName} ) USES ${basedOn horizontal separator[, ]}
Themes: ${themes horizontal separator[, ]}
Premium: ${advertisedPremium} per ${nrPremiumDays}
Insured risks:
${parts vertical terminator [;]}
Calculation
[? Risk adjusted by = ${riskAdjustment} ]
calculated premium: ${calculation}
[?Helper functions:
${helpers vertical}]
]} Inherited Projections
Just as concepts can inherit from other concepts, projection definitions can also inherit from their super concepts.
To indicate the inclusion the syntax [=> SUPER] is used, where SUPER is the name of the super concept.
For instance, in the UML metamodel both AssociationClass and Class inherit from the abstract Classifier. When building an editor for the UML metamodel,
one may define the projections as follows. The entries for attributes, operations, and states will appear for both child concepts.
Classifier {
[
[?<attributes> ${self.attributes vertical terminator [;] }]
[?<operations> ${self.operations vertical terminator [;] }]
[?<states> ${self.states vertical separator [;] }]
]
}
AssociationClass {
[
[?${self.visibility}] <associationclass> ${self.name}
${self.end1} <-> ${self.end2}
[=> Classifier ]
<endassociationclass>
]
}
Class {
[
[?${visibility}] ${self.isAbstract [<abstract>]} <class> ${self.name}
[?<specializes> ${self.generalizations horizontal separator [, ] }]
[?<implements> ${self.interfaces horizontal separator [, ] }]
[=> Classifier]
<endclass>
]
}