AQL Query Language

AQL in MTL Templates

Learn how to integrate AQL queries seamlessly into MTL templates for powerful model-driven code generation.

In this tutorial, you’ll discover how AQL expressions work within MTL templates, how to navigate models during code generation, and how to build sophisticated template logic using AQL’s querying capabilities.

35 mins Estimated Time

Section 1

Understanding AQL-MTL Integration

AQL serves as the expression language within MTL templates. Every dynamic expression in an MTL template—from simple property access to complex model queries—uses AQL syntax.

This tight integration makes MTL templates both powerful and intuitive: you can navigate models naturally while generating code, creating templates that adapt to model structure and content.

MTL Template with AQL Expressions

Step 1

Set up a model for code generation examples.

This metamodel represents a web application with Controllers, Models, and Views. We’ll use this to demonstrate how AQL queries drive code generation patterns.

WebApp.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
4 name="WebApp" nsURI="http://www.example.org/webapp" nsPrefix="webapp">
5
6 <!-- WebApp: Root container for the web application -->
7 <eClassifiers xsi:type="ecore:EClass" name="WebApp">
8 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
9 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
10 <eStructuralFeatures xsi:type="ecore:EAttribute" name="baseUrl"
11 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
12 <eStructuralFeatures xsi:type="ecore:EAttribute" name="version"
13 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
14 <eStructuralFeatures xsi:type="ecore:EReference" name="pages" upperBound="-1"
15 eType="#//Page" containment="true"/>
16 <eStructuralFeatures xsi:type="ecore:EReference" name="entities" upperBound="-1"
17 eType="#//Entity" containment="true"/>
18 <eStructuralFeatures xsi:type="ecore:EReference" name="stylesheets" upperBound="-1"
19 eType="#//Stylesheet" containment="true"/>
20 </eClassifiers>
21
22 <!-- Page: Represents a web page with a route -->
23 <eClassifiers xsi:type="ecore:EClass" name="Page">
24 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
25 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
26 <eStructuralFeatures xsi:type="ecore:EAttribute" name="route" lowerBound="1"
27 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
28 <eStructuralFeatures xsi:type="ecore:EAttribute" name="title"
29 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
30 <eStructuralFeatures xsi:type="ecore:EAttribute" name="requiresAuth"
31 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="false"/>
32 <eStructuralFeatures xsi:type="ecore:EReference" name="components" upperBound="-1"
33 eType="#//Component" containment="true"/>
34 <eStructuralFeatures xsi:type="ecore:EReference" name="linkedPages" upperBound="-1"
35 eType="#//Page"/>
36 </eClassifiers>
37
38 <!-- Component: Abstract base for UI components -->
39 <eClassifiers xsi:type="ecore:EClass" name="Component" abstract="true">
40 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
41 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
42 <eStructuralFeatures xsi:type="ecore:EAttribute" name="cssClass"
43 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
44 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isVisible"
45 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="true"/>
46 </eClassifiers>
47
48 <!-- Form: Input form with fields and validation -->
49 <eClassifiers xsi:type="ecore:EClass" name="Form" eSuperTypes="#//Component">
50 <eStructuralFeatures xsi:type="ecore:EAttribute" name="action"
51 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
52 <eStructuralFeatures xsi:type="ecore:EAttribute" name="method"
53 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" defaultValueLiteral="POST"/>
54 <eStructuralFeatures xsi:type="ecore:EReference" name="fields" upperBound="-1"
55 eType="#//Field" containment="true"/>
56 <eStructuralFeatures xsi:type="ecore:EReference" name="boundEntity"
57 eType="#//Entity"/>
58 </eClassifiers>
59
60 <!-- Field: Form input field -->
61 <eClassifiers xsi:type="ecore:EClass" name="Field">
62 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
63 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
64 <eStructuralFeatures xsi:type="ecore:EAttribute" name="label"
65 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
66 <eStructuralFeatures xsi:type="ecore:EAttribute" name="fieldType" lowerBound="1"
67 eType="#//FieldType"/>
68 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isRequired"
69 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="false"/>
70 <eStructuralFeatures xsi:type="ecore:EAttribute" name="placeholder"
71 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
72 <eStructuralFeatures xsi:type="ecore:EAttribute" name="validationPattern"
73 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
74 <eStructuralFeatures xsi:type="ecore:EReference" name="boundAttribute"
75 eType="#//Attribute"/>
76 </eClassifiers>
77
78 <!-- FieldType: Enumeration of input types -->
79 <eClassifiers xsi:type="ecore:EEnum" name="FieldType">
80 <eLiterals name="text" value="0"/>
81 <eLiterals name="email" value="1"/>
82 <eLiterals name="password" value="2"/>
83 <eLiterals name="number" value="3"/>
84 <eLiterals name="date" value="4"/>
85 <eLiterals name="textarea" value="5"/>
86 <eLiterals name="select" value="6"/>
87 <eLiterals name="checkbox" value="7"/>
88 <eLiterals name="hidden" value="8"/>
89 </eClassifiers>
90
91 <!-- DataTable: Displays tabular data -->
92 <eClassifiers xsi:type="ecore:EClass" name="DataTable" eSuperTypes="#//Component">
93 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isPaginated"
94 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="true"/>
95 <eStructuralFeatures xsi:type="ecore:EAttribute" name="pageSize"
96 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt" defaultValueLiteral="10"/>
97 <eStructuralFeatures xsi:type="ecore:EReference" name="columns" upperBound="-1"
98 eType="#//Column" containment="true"/>
99 <eStructuralFeatures xsi:type="ecore:EReference" name="dataSource"
100 eType="#//Entity"/>
101 </eClassifiers>
102
103 <!-- Column: Table column definition -->
104 <eClassifiers xsi:type="ecore:EClass" name="Column">
105 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
106 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
107 <eStructuralFeatures xsi:type="ecore:EAttribute" name="header"
108 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
109 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isSortable"
110 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="false"/>
111 <eStructuralFeatures xsi:type="ecore:EAttribute" name="width"
112 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
113 <eStructuralFeatures xsi:type="ecore:EReference" name="boundAttribute"
114 eType="#//Attribute"/>
115 </eClassifiers>
116
117 <!-- Navigation: Menu or navigation component -->
118 <eClassifiers xsi:type="ecore:EClass" name="Navigation" eSuperTypes="#//Component">
119 <eStructuralFeatures xsi:type="ecore:EAttribute" name="navType"
120 eType="#//NavType" defaultValueLiteral="horizontal"/>
121 <eStructuralFeatures xsi:type="ecore:EReference" name="items" upperBound="-1"
122 eType="#//NavItem" containment="true"/>
123 </eClassifiers>
124
125 <!-- NavType: Navigation layout type -->
126 <eClassifiers xsi:type="ecore:EEnum" name="NavType">
127 <eLiterals name="horizontal" value="0"/>
128 <eLiterals name="vertical" value="1"/>
129 <eLiterals name="dropdown" value="2"/>
130 </eClassifiers>
131
132 <!-- NavItem: Navigation menu item -->
133 <eClassifiers xsi:type="ecore:EClass" name="NavItem">
134 <eStructuralFeatures xsi:type="ecore:EAttribute" name="label" lowerBound="1"
135 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
136 <eStructuralFeatures xsi:type="ecore:EAttribute" name="icon"
137 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
138 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isActive"
139 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="false"/>
140 <eStructuralFeatures xsi:type="ecore:EReference" name="targetPage"
141 eType="#//Page"/>
142 <eStructuralFeatures xsi:type="ecore:EReference" name="subItems" upperBound="-1"
143 eType="#//NavItem" containment="true"/>
144 </eClassifiers>
145
146 <!-- Entity: Data model entity -->
147 <eClassifiers xsi:type="ecore:EClass" name="Entity">
148 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
149 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
150 <eStructuralFeatures xsi:type="ecore:EAttribute" name="tableName"
151 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
152 <eStructuralFeatures xsi:type="ecore:EReference" name="attributes" upperBound="-1"
153 eType="#//Attribute" containment="true"/>
154 <eStructuralFeatures xsi:type="ecore:EReference" name="relationships" upperBound="-1"
155 eType="#//Relationship" containment="true"/>
156 </eClassifiers>
157
158 <!-- Attribute: Entity property -->
159 <eClassifiers xsi:type="ecore:EClass" name="Attribute">
160 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
161 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
162 <eStructuralFeatures xsi:type="ecore:EAttribute" name="dataType" lowerBound="1"
163 eType="#//DataType"/>
164 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isPrimaryKey"
165 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="false"/>
166 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isNullable"
167 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="true"/>
168 <eStructuralFeatures xsi:type="ecore:EAttribute" name="defaultValue"
169 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
170 <eStructuralFeatures xsi:type="ecore:EAttribute" name="maxLength"
171 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
172 </eClassifiers>
173
174 <!-- DataType: Attribute data types -->
175 <eClassifiers xsi:type="ecore:EEnum" name="DataType">
176 <eLiterals name="string" value="0"/>
177 <eLiterals name="integer" value="1"/>
178 <eLiterals name="decimal" value="2"/>
179 <eLiterals name="boolean" value="3"/>
180 <eLiterals name="date" value="4"/>
181 <eLiterals name="datetime" value="5"/>
182 <eLiterals name="text" value="6"/>
183 </eClassifiers>
184
185 <!-- Relationship: Entity relationship -->
186 <eClassifiers xsi:type="ecore:EClass" name="Relationship">
187 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
188 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
189 <eStructuralFeatures xsi:type="ecore:EAttribute" name="relationType" lowerBound="1"
190 eType="#//RelationType"/>
191 <eStructuralFeatures xsi:type="ecore:EReference" name="targetEntity" lowerBound="1"
192 eType="#//Entity"/>
193 </eClassifiers>
194
195 <!-- RelationType: Relationship cardinality -->
196 <eClassifiers xsi:type="ecore:EEnum" name="RelationType">
197 <eLiterals name="oneToOne" value="0"/>
198 <eLiterals name="oneToMany" value="1"/>
199 <eLiterals name="manyToOne" value="2"/>
200 <eLiterals name="manyToMany" value="3"/>
201 </eClassifiers>
202
203 <!-- Stylesheet: CSS stylesheet reference -->
204 <eClassifiers xsi:type="ecore:EClass" name="Stylesheet">
205 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
206 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
207 <eStructuralFeatures xsi:type="ecore:EAttribute" name="path"
208 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
209 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isExternal"
210 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="false"/>
211 </eClassifiers>
212
213</ecore:EPackage>

Step 2

Create a model instance for the web application.

This model defines a simple web application with user management functionality. The model provides rich data for demonstrating AQL-driven template generation.

webapp-model.xmi
1<?xml version="1.0" encoding="UTF-8"?>
2<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
3 xmlns:webapp="http://www.example.org/webapp">
4
5 <webapp:WebApp name="TaskManager" baseUrl="https://tasks.example.com" version="1.0.0">
6
7 <!-- Data Entities -->
8 <entities name="User" tableName="users">
9 <attributes name="id" dataType="integer" isPrimaryKey="true" isNullable="false"/>
10 <attributes name="email" dataType="string" isNullable="false" maxLength="255"/>
11 <attributes name="username" dataType="string" isNullable="false" maxLength="50"/>
12 <attributes name="passwordHash" dataType="string" isNullable="false"/>
13 <attributes name="createdAt" dataType="datetime" isNullable="false"/>
14 <attributes name="isActive" dataType="boolean" defaultValue="true"/>
15 <relationships name="tasks" relationType="oneToMany" targetEntity="//@entities.1"/>
16 <relationships name="projects" relationType="manyToMany" targetEntity="//@entities.2"/>
17 </entities>
18
19 <entities name="Task" tableName="tasks">
20 <attributes name="id" dataType="integer" isPrimaryKey="true" isNullable="false"/>
21 <attributes name="title" dataType="string" isNullable="false" maxLength="200"/>
22 <attributes name="description" dataType="text" isNullable="true"/>
23 <attributes name="priority" dataType="integer" defaultValue="1"/>
24 <attributes name="dueDate" dataType="date" isNullable="true"/>
25 <attributes name="isCompleted" dataType="boolean" defaultValue="false"/>
26 <attributes name="createdAt" dataType="datetime" isNullable="false"/>
27 <relationships name="assignee" relationType="manyToOne" targetEntity="//@entities.0"/>
28 <relationships name="project" relationType="manyToOne" targetEntity="//@entities.2"/>
29 <relationships name="comments" relationType="oneToMany" targetEntity="//@entities.3"/>
30 </entities>
31
32 <entities name="Project" tableName="projects">
33 <attributes name="id" dataType="integer" isPrimaryKey="true" isNullable="false"/>
34 <attributes name="name" dataType="string" isNullable="false" maxLength="100"/>
35 <attributes name="description" dataType="text" isNullable="true"/>
36 <attributes name="startDate" dataType="date" isNullable="true"/>
37 <attributes name="endDate" dataType="date" isNullable="true"/>
38 <attributes name="budget" dataType="decimal" isNullable="true"/>
39 <relationships name="tasks" relationType="oneToMany" targetEntity="//@entities.1"/>
40 <relationships name="members" relationType="manyToMany" targetEntity="//@entities.0"/>
41 </entities>
42
43 <entities name="Comment" tableName="comments">
44 <attributes name="id" dataType="integer" isPrimaryKey="true" isNullable="false"/>
45 <attributes name="content" dataType="text" isNullable="false"/>
46 <attributes name="createdAt" dataType="datetime" isNullable="false"/>
47 <relationships name="author" relationType="manyToOne" targetEntity="//@entities.0"/>
48 <relationships name="task" relationType="manyToOne" targetEntity="//@entities.1"/>
49 </entities>
50
51 <!-- Stylesheets -->
52 <stylesheets name="Bootstrap" path="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" isExternal="true"/>
53 <stylesheets name="Custom Styles" path="/css/app.css" isExternal="false"/>
54 <stylesheets name="Font Awesome" path="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" isExternal="true"/>
55
56 <!-- Pages -->
57 <pages name="Dashboard" route="/" title="Dashboard - TaskManager" requiresAuth="true"
58 linkedPages="//@pages.1 //@pages.2 //@pages.3 //@pages.4">
59
60 <!-- Main Navigation -->
61 <components xsi:type="webapp:Navigation" name="MainNav" cssClass="navbar navbar-expand-lg" navType="horizontal">
62 <items label="Dashboard" icon="fa-home" isActive="true" targetPage="//@pages.0"/>
63 <items label="Tasks" icon="fa-tasks" targetPage="//@pages.1"/>
64 <items label="Projects" icon="fa-folder" targetPage="//@pages.2">
65 <subItems label="All Projects" targetPage="//@pages.2"/>
66 <subItems label="New Project" targetPage="//@pages.3"/>
67 </items>
68 <items label="Profile" icon="fa-user" targetPage="//@pages.4"/>
69 </components>
70
71 <!-- Recent Tasks Table -->
72 <components xsi:type="webapp:DataTable" name="RecentTasks" cssClass="table table-striped"
73 isPaginated="true" pageSize="5" dataSource="//@entities.1">
74 <columns name="title" header="Task" isSortable="true" width="40%" boundAttribute="//@entities.1/@attributes.1"/>
75 <columns name="priority" header="Priority" isSortable="true" width="15%" boundAttribute="//@entities.1/@attributes.3"/>
76 <columns name="dueDate" header="Due Date" isSortable="true" width="20%" boundAttribute="//@entities.1/@attributes.4"/>
77 <columns name="isCompleted" header="Status" isSortable="false" width="15%" boundAttribute="//@entities.1/@attributes.5"/>
78 </components>
79 </pages>
80
81 <pages name="TaskList" route="/tasks" title="Tasks - TaskManager" requiresAuth="true"
82 linkedPages="//@pages.0 //@pages.2">
83
84 <!-- Tasks Navigation (reuse pattern) -->
85 <components xsi:type="webapp:Navigation" name="TaskNav" cssClass="navbar" navType="horizontal">
86 <items label="All Tasks" isActive="true" targetPage="//@pages.1"/>
87 <items label="My Tasks" targetPage="//@pages.1"/>
88 <items label="Completed" targetPage="//@pages.1"/>
89 </components>
90
91 <!-- Full Tasks Table -->
92 <components xsi:type="webapp:DataTable" name="AllTasks" cssClass="table table-hover"
93 isPaginated="true" pageSize="20" dataSource="//@entities.1">
94 <columns name="id" header="#" isSortable="true" width="5%" boundAttribute="//@entities.1/@attributes.0"/>
95 <columns name="title" header="Title" isSortable="true" width="35%" boundAttribute="//@entities.1/@attributes.1"/>
96 <columns name="description" header="Description" isSortable="false" width="30%" boundAttribute="//@entities.1/@attributes.2"/>
97 <columns name="priority" header="Priority" isSortable="true" width="10%" boundAttribute="//@entities.1/@attributes.3"/>
98 <columns name="dueDate" header="Due" isSortable="true" width="10%" boundAttribute="//@entities.1/@attributes.4"/>
99 <columns name="isCompleted" header="Done" isSortable="true" width="10%" boundAttribute="//@entities.1/@attributes.5"/>
100 </components>
101
102 <!-- New Task Form -->
103 <components xsi:type="webapp:Form" name="NewTaskForm" cssClass="card p-4"
104 action="/api/tasks" method="POST" boundEntity="//@entities.1">
105 <fields name="title" label="Task Title" fieldType="text" isRequired="true"
106 placeholder="Enter task title" boundAttribute="//@entities.1/@attributes.1"/>
107 <fields name="description" label="Description" fieldType="textarea" isRequired="false"
108 placeholder="Describe the task..." boundAttribute="//@entities.1/@attributes.2"/>
109 <fields name="priority" label="Priority" fieldType="select" isRequired="true"
110 boundAttribute="//@entities.1/@attributes.3"/>
111 <fields name="dueDate" label="Due Date" fieldType="date" isRequired="false"
112 boundAttribute="//@entities.1/@attributes.4"/>
113 </components>
114 </pages>
115
116 <pages name="Projects" route="/projects" title="Projects - TaskManager" requiresAuth="true"
117 linkedPages="//@pages.0 //@pages.1 //@pages.3">
118
119 <!-- Projects Table -->
120 <components xsi:type="webapp:DataTable" name="ProjectsTable" cssClass="table table-bordered"
121 isPaginated="true" pageSize="10" dataSource="//@entities.2">
122 <columns name="name" header="Project Name" isSortable="true" width="30%" boundAttribute="//@entities.2/@attributes.1"/>
123 <columns name="description" header="Description" isSortable="false" width="40%" boundAttribute="//@entities.2/@attributes.2"/>
124 <columns name="startDate" header="Start" isSortable="true" width="15%" boundAttribute="//@entities.2/@attributes.3"/>
125 <columns name="endDate" header="End" isSortable="true" width="15%" boundAttribute="//@entities.2/@attributes.4"/>
126 </components>
127 </pages>
128
129 <pages name="NewProject" route="/projects/new" title="New Project - TaskManager" requiresAuth="true"
130 linkedPages="//@pages.2">
131
132 <!-- New Project Form -->
133 <components xsi:type="webapp:Form" name="ProjectForm" cssClass="card shadow p-4"
134 action="/api/projects" method="POST" boundEntity="//@entities.2">
135 <fields name="name" label="Project Name" fieldType="text" isRequired="true"
136 placeholder="Enter project name" validationPattern="^[A-Za-z0-9 ]{3,100}$"
137 boundAttribute="//@entities.2/@attributes.1"/>
138 <fields name="description" label="Description" fieldType="textarea" isRequired="false"
139 placeholder="Describe the project..." boundAttribute="//@entities.2/@attributes.2"/>
140 <fields name="startDate" label="Start Date" fieldType="date" isRequired="true"
141 boundAttribute="//@entities.2/@attributes.3"/>
142 <fields name="endDate" label="End Date" fieldType="date" isRequired="false"
143 boundAttribute="//@entities.2/@attributes.4"/>
144 <fields name="budget" label="Budget" fieldType="number" isRequired="false"
145 placeholder="0.00" boundAttribute="//@entities.2/@attributes.5"/>
146 </components>
147 </pages>
148
149 <pages name="Profile" route="/profile" title="Profile - TaskManager" requiresAuth="true"
150 linkedPages="//@pages.0">
151
152 <!-- Profile Form -->
153 <components xsi:type="webapp:Form" name="ProfileForm" cssClass="container mt-4"
154 action="/api/users/profile" method="POST" boundEntity="//@entities.0">
155 <fields name="email" label="Email Address" fieldType="email" isRequired="true"
156 validationPattern="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
157 boundAttribute="//@entities.0/@attributes.1"/>
158 <fields name="username" label="Username" fieldType="text" isRequired="true"
159 placeholder="Your username" boundAttribute="//@entities.0/@attributes.2"/>
160 <fields name="currentPassword" label="Current Password" fieldType="password" isRequired="false"
161 placeholder="Enter current password"/>
162 <fields name="newPassword" label="New Password" fieldType="password" isRequired="false"
163 placeholder="Enter new password"/>
164 </components>
165 </pages>
166
167 <pages name="Login" route="/login" title="Login - TaskManager" requiresAuth="false"
168 linkedPages="//@pages.6">
169
170 <!-- Login Form -->
171 <components xsi:type="webapp:Form" name="LoginForm" cssClass="card shadow-lg mx-auto"
172 action="/api/auth/login" method="POST" boundEntity="//@entities.0">
173 <fields name="email" label="Email" fieldType="email" isRequired="true"
174 placeholder="your@email.com" boundAttribute="//@entities.0/@attributes.1"/>
175 <fields name="password" label="Password" fieldType="password" isRequired="true"
176 placeholder="Enter your password"/>
177 <fields name="remember" label="Remember me" fieldType="checkbox" isRequired="false"/>
178 </components>
179 </pages>
180
181 <pages name="Register" route="/register" title="Register - TaskManager" requiresAuth="false"
182 linkedPages="//@pages.5">
183
184 <!-- Registration Form -->
185 <components xsi:type="webapp:Form" name="RegisterForm" cssClass="card shadow-lg mx-auto"
186 action="/api/auth/register" method="POST" boundEntity="//@entities.0">
187 <fields name="username" label="Username" fieldType="text" isRequired="true"
188 placeholder="Choose a username" validationPattern="^[a-zA-Z0-9_]{3,20}$"
189 boundAttribute="//@entities.0/@attributes.2"/>
190 <fields name="email" label="Email" fieldType="email" isRequired="true"
191 placeholder="your@email.com" validationPattern="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
192 boundAttribute="//@entities.0/@attributes.1"/>
193 <fields name="password" label="Password" fieldType="password" isRequired="true"
194 placeholder="Create a password"/>
195 <fields name="confirmPassword" label="Confirm Password" fieldType="password" isRequired="true"
196 placeholder="Confirm your password"/>
197 <fields name="terms" label="I accept the terms and conditions" fieldType="checkbox" isRequired="true"/>
198 </components>
199 </pages>
200
201 </webapp:WebApp>
202</xmi:XMI>

Step 3

Create a basic MTL template with AQL expressions.

This template shows how AQL expressions appear within MTL syntax. Square brackets contain AQL expressions that are evaluated during template execution.

BasicGeneration.mtl
1[comment encoding = UTF-8 /]
2[module BasicTemplate('http://www.example.org/webapp')]
3
4[comment]
5 Basic MTL Template with AQL Expressions
6
7 This template demonstrates how AQL expressions are embedded
8 within MTL templates for model-to-text transformations.
9
10 Key concept: AQL provides the query language for navigating
11 and filtering model elements, whilst MTL provides the template
12 structure for generating text output.
13[/comment]
14
15[comment ================================================================ /]
16[comment SIMPLE NAVIGATION /]
17[comment ================================================================ /]
18
19[template main(app : WebApp)]
20[comment]
21 AQL navigation expressions access model properties directly.
22 The expression 'app.name' navigates to the name attribute.
23[/comment]
24[file ('README.md', 'overwrite', 'UTF-8')]
25# [app.name/]
26
27Version: [app.version/]
28Base URL: [app.baseUrl/]
29
30## Overview
31
32This web application contains:
33- [app.pages->size()/] pages
34- [app.entities->size()/] data entities
35- [app.stylesheets->size()/] stylesheets
36
37[comment]
38 AQL's ->size() operation counts collection elements.
39 This is one of the most common collection operations.
40[/comment]
41
42## Pages
43
44[for (page : Page | app.pages)]
45- **[page.name/]**: `[page.route/]`
46[/for]
47
48## Entities
49
50[for (entity : Entity | app.entities)]
51- [entity.name/] (table: [entity.tableName/])
52[/for]
53
54Generated with swift-mtl
55[/file]
56[/template]

Step 4

Generate code to see AQL expressions in action.

Run the template to see how AQL expressions are evaluated and their results inserted into the generated output. This demonstrates the fundamental AQL-MTL integration.

Terminal
1# Basic MTL Generation with AQL Expressions
2# This script demonstrates running a basic MTL template
3
4# Run the basic template to generate documentation
5swift-mtl generate \
6 --template BasicGeneration.mtl \
7 --model webapp-model.xmi \
8 --metamodel WebApp.ecore \
9 --output ./generated
10
11# Output directory structure:
12# generated/
13# README.md
14
15# Example generated README.md content:
16# -----------------------------------------
17# # TaskManager
18#
19# Version: 1.0.0
20# Base URL: https://tasks.example.com
21#
22# ## Overview
23#
24# This web application contains:
25# - 7 pages
26# - 4 data entities
27# - 3 stylesheets
28#
29# ## Pages
30#
31# - **Dashboard**: `/`
32# - **TaskList**: `/tasks`
33# - **Projects**: `/projects`
34# ...
35# -----------------------------------------
36
37# The AQL expressions in the template are evaluated:
38# [app.name/] -> TaskManager
39# [app.version/] -> 1.0.0
40# [app.pages->size()/] -> 7
41# [app.entities->size()/] -> 4
42
43# View the generated file
44cat ./generated/README.md

Section 3

Collection Operations in Templates

AQL’s collection operations become particularly powerful within MTL templates. They enable templates to generate repetitive code structures based on model collections.

Combine MTL’s iteration constructs with AQL’s filtering and transformation operations to create sophisticated code generation patterns.

Step 1

Use AQL collections in for loops.

MTL’s [for] construct iterates over AQL collection expressions. This enables generating code for each element in model collections.

CollectionIteration.mtl
1[comment encoding = UTF-8 /]
2[module CollectionLoops('http://www.example.org/webapp')]
3
4[comment]
5 Collection Iteration with AQL in MTL
6
7 This template demonstrates using AQL collections within
8 MTL's [for] loops for iterative code generation.
9
10 The for loop iterates over AQL collection expressions,
11 generating output for each element.
12[/comment]
13
14[template main(app : WebApp)]
15[file ('collection-loops.txt', 'overwrite', 'UTF-8')]
16Collection Iteration with AQL
17=============================
18
19Application: [app.name/]
20
21[comment ================================================================ /]
22[comment BASIC FOR LOOP /]
23[comment ================================================================ /]
24
251. Basic For Loop
26-----------------
27[comment]
28 [for (var : Type | collection)]
29 Iterates over each element in the collection.
30 The variable 'var' represents the current element.
31[/comment]
32
33All pages:
34[for (page : Page | app.pages)]
35- [page.name/]
36[/for]
37
38All entities:
39[for (entity : Entity | app.entities)]
40- [entity.name/]
41[/for]
42
43[comment ================================================================ /]
44[comment NESTED FOR LOOPS /]
45[comment ================================================================ /]
46
472. Nested For Loops
48-------------------
49[comment]
50 Nest for loops to iterate through hierarchical data.
51 Inner loops can access outer loop variables.
52[/comment]
53
54Entity attribute listing:
55[for (entity : Entity | app.entities)]
56[entity.name/]:
57[for (attr : Attribute | entity.attributes)]
58 - [attr.name/]: [attr.dataType/]
59[/for]
60
61[/for]
62
63Page component listing:
64[for (page : Page | app.pages)]
65[page.name/]:
66[for (comp : Component | page.components)]
67 - [comp.name/]
68[/for]
69
70[/for]
71
72[comment ================================================================ /]
73[comment FOR LOOPS WITH SEPARATORS /]
74[comment ================================================================ /]
75
763. For Loops with Separators
77----------------------------
78[comment]
79 Use [separator] to add content between elements
80 (but not after the last element).
81[/comment]
82
83Entity names (comma-separated): [for (entity : Entity | app.entities) separator(', ')][entity.name/][/for]
84
85Page routes (pipe-separated): [for (page : Page | app.pages) separator(' | ')][page.route/][/for]
86
87Attribute types: [for (attr : Attribute | app.entities->first().attributes) separator(', ')][attr.name/]:[attr.dataType/][/for]
88
89[comment ================================================================ /]
90[comment FOR LOOPS WITH INDEX /]
91[comment ================================================================ /]
92
934. For Loops with Index
94-----------------------
95[comment]
96 Use i() to get the current iteration index (1-based).
97 Useful for numbering or conditional logic.
98[/comment]
99
100Numbered page list:
101[for (page : Page | app.pages)]
102[i/]. [page.name/] ([page.route/])
103[/for]
104
105Numbered entity list:
106[for (entity : Entity | app.entities)]
107[i/]. [entity.name/]
108[/for]
109
110[comment ================================================================ /]
111[comment FOR LOOPS WITH BEFORE/AFTER /]
112[comment ================================================================ /]
113
1145. For Loops with Before/After
115------------------------------
116[comment]
117 [before] executes once before the loop if collection is non-empty.
118 [after] executes once after the loop if collection is non-empty.
119[/comment]
120
121[for (entity : Entity | app.entities)]
122[before]
123=== Entity List Start ===
124[/before]
125* [entity.name/]
126[after]
127=== Entity List End ===
128[/after]
129[/for]
130
131[comment ================================================================ /]
132[comment DEEPLY NESTED LOOPS /]
133[comment ================================================================ /]
134
1356. Deeply Nested Loops
136----------------------
137[comment]
138 Navigate through multiple levels of containment
139 using nested for loops.
140[/comment]
141
142Complete form field analysis:
143[for (page : Page | app.pages)]
144[for (comp : Component | page.components->select(c | c.oclIsTypeOf(Form)))]
145[let form = comp.oclAsType(Form)]
146[page.name/] > [form.name/]:
147[for (field : Field | form.fields)]
148 [i/]. [field.name/] ([field.fieldType/])
149[if (field.isRequired)]
150 * Required
151[/if]
152[if (field.placeholder <> null and field.placeholder.size() > 0)]
153 * Placeholder: "[field.placeholder/]"
154[/if]
155[/for]
156[/let]
157[/for]
158[/for]
159
160[/file]
161[/template]

Step 2

Filter collections before iteration.

Apply AQL filtering operations within for loop expressions. Generate code only for elements that meet specific criteria.

FilteredIteration.mtl
1[comment encoding = UTF-8 /]
2[module FilteredLoops('http://www.example.org/webapp')]
3
4[comment]
5 Filtered Collection Iteration in MTL
6
7 This template demonstrates filtering collections
8 within for loop expressions using AQL's select
9 and reject operations.
10
11 Filtering before iteration generates code only
12 for elements that meet specific criteria.
13[/comment]
14
15[template main(app : WebApp)]
16[file ('filtered-loops.txt', 'overwrite', 'UTF-8')]
17Filtered Collection Iteration
18=============================
19
20Application: [app.name/]
21
22[comment ================================================================ /]
23[comment SELECT IN FOR LOOPS /]
24[comment ================================================================ /]
25
261. Select in For Loops
27----------------------
28[comment]
29 Use ->select(condition) to filter collections
30 before iterating over them.
31[/comment]
32
33Protected pages only:
34[for (page : Page | app.pages->select(p | p.requiresAuth))]
35- [page.name/] ([page.route/]) - requires authentication
36[/for]
37
38Public pages only:
39[for (page : Page | app.pages->select(p | not p.requiresAuth))]
40- [page.name/] ([page.route/]) - public access
41[/for]
42
43[comment ================================================================ /]
44[comment REJECT IN FOR LOOPS /]
45[comment ================================================================ /]
46
472. Reject in For Loops
48----------------------
49[comment]
50 Use ->reject(condition) to exclude elements
51 that match the condition.
52[/comment]
53
54Non-primary-key attributes per entity:
55[for (entity : Entity | app.entities)]
56[entity.name/]:
57[for (attr : Attribute | entity.attributes->reject(a | a.isPrimaryKey))]
58 - [attr.name/]: [attr.dataType/]
59[/for]
60
61[/for]
62
63[comment ================================================================ /]
64[comment MULTIPLE CONDITIONS /]
65[comment ================================================================ /]
66
673. Multiple Filter Conditions
68-----------------------------
69[comment]
70 Combine multiple conditions with 'and' / 'or'
71 for sophisticated filtering.
72[/comment]
73
74Required string attributes:
75[for (entity : Entity | app.entities)]
76[let reqStrings = entity.attributes->select(a | a.dataType = DataType::string and not a.isNullable)]
77[if (reqStrings->notEmpty())]
78[entity.name/]:
79[for (attr : Attribute | reqStrings)]
80 - [attr.name/] (required string, max: [if (attr.maxLength > 0)][attr.maxLength/][else]unlimited[/if])
81[/for]
82[/if]
83[/let]
84[/for]
85
86[comment ================================================================ /]
87[comment FILTERING BY TYPE /]
88[comment ================================================================ /]
89
904. Filtering by Type
91--------------------
92[comment]
93 Use oclIsTypeOf() and oclIsKindOf() to filter
94 collections by element type.
95[/comment]
96
97Forms in the application:
98[for (page : Page | app.pages)]
99[let forms = page.components->select(c | c.oclIsTypeOf(Form))]
100[if (forms->notEmpty())]
101[page.name/]:
102[for (comp : Component | forms)]
103 - [comp.name/] (action: [comp.oclAsType(Form).action/])
104[/for]
105[/if]
106[/let]
107[/for]
108
109DataTables in the application:
110[for (page : Page | app.pages)]
111[let tables = page.components->select(c | c.oclIsTypeOf(DataTable))]
112[if (tables->notEmpty())]
113[page.name/]:
114[for (comp : Component | tables)]
115 - [comp.name/] (rows: [comp.oclAsType(DataTable).pageSize/])
116[/for]
117[/if]
118[/let]
119[/for]
120
121[comment ================================================================ /]
122[comment CHAINED FILTERING /]
123[comment ================================================================ /]
124
1255. Chained Filtering
126--------------------
127[comment]
128 Chain multiple select/reject operations for
129 multi-step filtering.
130[/comment]
131
132Non-PK, non-nullable attributes with max length set:
133[for (entity : Entity | app.entities)]
134[let filtered = entity.attributes
135 ->reject(a | a.isPrimaryKey)
136 ->select(a | not a.isNullable)
137 ->select(a | a.maxLength > 0)]
138[if (filtered->notEmpty())]
139[entity.name/]:
140[for (attr : Attribute | filtered)]
141 - [attr.name/] (max: [attr.maxLength/])
142[/for]
143[/if]
144[/let]
145[/for]
146
147[comment ================================================================ /]
148[comment STRING-BASED FILTERING /]
149[comment ================================================================ /]
150
1516. String-Based Filtering
152-------------------------
153[comment]
154 Use string operations in filter conditions
155 for name-based or pattern matching.
156[/comment]
157
158Attributes containing 'Date' in name:
159[for (entity : Entity | app.entities)]
160[let dateAttrs = entity.attributes->select(a | a.name.contains('Date') or a.name.contains('date'))]
161[if (dateAttrs->notEmpty())]
162[entity.name/]:
163[for (attr : Attribute | dateAttrs)]
164 - [attr.name/]: [attr.dataType/]
165[/for]
166[/if]
167[/let]
168[/for]
169
170External stylesheets:
171[for (style : Stylesheet | app.stylesheets->select(s | s.isExternal))]
172- [style.name/]: [style.path/]
173[/for]
174
175Internal stylesheets:
176[for (style : Stylesheet | app.stylesheets->select(s | not s.isExternal))]
177- [style.name/]: [style.path/]
178[/for]
179
180[/file]
181[/template]

Step 3

Transform collections with collect.

Use AQL’s collect operation to transform collections before iteration. This enables sophisticated data reshaping within templates.

CollectTransform.mtl
1[comment encoding = UTF-8 /]
2[module CollectTemplate('http://www.example.org/webapp')]
3
4[comment]
5 Collection Transformation with Collect in MTL
6
7 This template demonstrates using AQL's collect operation
8 to transform collections before iteration or output.
9
10 Collect maps each element to a new value using an
11 expression, similar to map() in functional programming.
12[/comment]
13
14[template main(app : WebApp)]
15[file ('collect-transform.txt', 'overwrite', 'UTF-8')]
16Collection Transformation with Collect
17======================================
18
19Application: [app.name/]
20
21[comment ================================================================ /]
22[comment BASIC COLLECT /]
23[comment ================================================================ /]
24
251. Basic Collect - Extract Properties
26-------------------------------------
27[comment]
28 Use ->collect(expr) to extract a single property
29 from each element, creating a collection of values.
30[/comment]
31
32All page names: [app.pages->collect(p | p.name)->sep(', ')/]
33
34All page routes: [app.pages->collect(p | p.route)->sep(', ')/]
35
36All entity table names: [app.entities->collect(e | e.tableName)->sep(', ')/]
37
38[comment ================================================================ /]
39[comment COLLECT WITH TRANSFORMATION /]
40[comment ================================================================ /]
41
422. Collect with Transformation
43------------------------------
44[comment]
45 Apply transformations within collect expressions
46 to modify the extracted values.
47[/comment]
48
49Entity names (uppercase): [app.entities->collect(e | e.name.toUpperCase())->sep(', ')/]
50
51Page routes (with base URL): [app.pages->collect(p | app.baseUrl + p.route)->sep('\n')/]
52
53[comment ================================================================ /]
54[comment COLLECT AND ITERATE /]
55[comment ================================================================ /]
56
573. Collect Then Iterate
58-----------------------
59[comment]
60 Collect values first, then iterate over the
61 resulting collection.
62[/comment]
63
64All attribute names across entities:
65[let allAttrNames = app.entities->collect(e | e.attributes)->flatten()->collect(a | a.name)]
66[for (name : String | allAttrNames->asSet())]
67- [name/]
68[/for]
69[/let]
70
71[comment ================================================================ /]
72[comment NESTED COLLECT WITH FLATTEN /]
73[comment ================================================================ /]
74
754. Nested Collect with Flatten
76------------------------------
77[comment]
78 When collecting collections, use flatten to
79 merge nested collections into a single level.
80[/comment]
81
82All attributes (flattened from entities):
83[for (attr : Attribute | app.entities->collect(e | e.attributes)->flatten())]
84- [attr.name/] ([attr.dataType/])
85[/for]
86
87All form fields (flattened from pages/forms):
88[let allFields = app.pages
89 ->collect(p | p.components->select(c | c.oclIsTypeOf(Form)))
90 ->flatten()
91 ->collect(c | c.oclAsType(Form).fields)
92 ->flatten()]
93[for (field : Field | allFields)]
94- [field.name/] ([field.fieldType/])
95[/for]
96[/let]
97
98[comment ================================================================ /]
99[comment COLLECT FOR STATISTICS /]
100[comment ================================================================ /]
101
1025. Collect for Statistics
103-------------------------
104[comment]
105 Use collect to extract numeric values for
106 statistical operations like sum, min, max.
107[/comment]
108
109Statistics:
110- Total pages: [app.pages->size()/]
111- Total entities: [app.entities->size()/]
112- Total attributes: [app.entities->collect(e | e.attributes->size())->sum()/]
113- Total relationships: [app.entities->collect(e | e.relationships->size())->sum()/]
114- Average attributes per entity: [app.entities->collect(e | e.attributes->size())->sum() / app.entities->size()/]
115
116DataTable page sizes:
117[let pageSizes = app.pages
118 ->collect(p | p.components->select(c | c.oclIsTypeOf(DataTable)))
119 ->flatten()
120 ->collect(c | c.oclAsType(DataTable).pageSize)]
121- Configured tables: [pageSizes->size()/]
122[if (pageSizes->notEmpty())]
123- Total rows visible: [pageSizes->sum()/]
124- Smallest page size: [pageSizes->min()/]
125- Largest page size: [pageSizes->max()/]
126[/if]
127[/let]
128
129[comment ================================================================ /]
130[comment COLLECT WITH CONDITIONAL /]
131[comment ================================================================ /]
132
1336. Collect with Conditional Expression
134--------------------------------------
135[comment]
136 Use if-then-else within collect to conditionally
137 transform elements.
138[/comment]
139
140Attribute nullability labels:
141[for (entity : Entity | app.entities)]
142[entity.name/]:
143[let labels = entity.attributes->collect(a |
144 a.name + ': ' + if a.isNullable then 'optional' else 'required' endif)]
145[for (label : String | labels)]
146 [label/]
147[/for]
148[/let]
149[/for]
150
151[/file]
152[/template]

Step 4

Generate complex structures from collections.

Run templates with collection operations to see how AQL queries shape the generated code structure. Collections drive code organisation.

Terminal
1# Collection Operations Output Examples
2# This script demonstrates running MTL templates with collection operations
3
4# Run the collection loops template
5swift-mtl generate \
6 --template CollectionLoops.mtl \
7 --model webapp-model.xmi \
8 --metamodel WebApp.ecore \
9 --output ./generated
10
11echo "Generated: collection-loops.txt"
12
13# Run the filtered loops template
14swift-mtl generate \
15 --template FilteredLoops.mtl \
16 --model webapp-model.xmi \
17 --metamodel WebApp.ecore \
18 --output ./generated
19
20echo "Generated: filtered-loops.txt"
21
22# Run the collect transformation template
23swift-mtl generate \
24 --template CollectTransform.mtl \
25 --model webapp-model.xmi \
26 --metamodel WebApp.ecore \
27 --output ./generated
28
29echo "Generated: collect-transform.txt"
30
31# Example output from filtered-loops.txt:
32# -----------------------------------------
33# Filtered Collection Iteration
34# =============================
35#
36# Application: TaskManager
37#
38# Protected pages only:
39# - Dashboard (/) - requires authentication
40# - TaskList (/tasks) - requires authentication
41# - Projects (/projects) - requires authentication
42# - NewProject (/projects/new) - requires authentication
43# - Profile (/profile) - requires authentication
44#
45# Public pages only:
46# - Login (/login) - public access
47# - Register (/register) - public access
48# -----------------------------------------
49
50# Example output from collect-transform.txt:
51# -----------------------------------------
52# Collection Transformation with Collect
53# ======================================
54#
55# All page names: Dashboard, TaskList, Projects, NewProject, Profile, Login, Register
56#
57# All entity table names: users, tasks, projects, comments
58#
59# Statistics:
60# - Total pages: 7
61# - Total entities: 4
62# - Total attributes: 24
63# - Total relationships: 9
64# - Average attributes per entity: 6
65# -----------------------------------------
66
67# View generated output
68echo ""
69echo "=== Collection Loops Output ==="
70cat ./generated/collection-loops.txt | head -40
71
72echo ""
73echo "=== Filtered Loops Output ==="
74cat ./generated/filtered-loops.txt | head -40
75
76echo ""
77echo "=== Collect Transform Output ==="
78cat ./generated/collect-transform.txt | head -40

Section 4

Conditional Logic with AQL

AQL’s boolean expressions integrate with MTL’s conditional statements to create adaptive templates. Templates can generate different code based on model properties and relationships.

This enables templates to respond intelligently to model variations, generating appropriate code for different scenarios.

Step 1

Use AQL boolean expressions in conditions.

MTL’s [if] statements use AQL boolean expressions as conditions. This enables templates to make decisions based on model content.

ConditionalLogic.mtl
1[comment encoding = UTF-8 /]
2[module ConditionalTemplate('http://www.example.org/webapp')]
3
4[comment]
5 Conditional Logic with AQL in MTL Templates
6
7 This template demonstrates using AQL boolean expressions
8 within MTL's [if] statements for conditional generation.
9
10 Conditional logic enables templates to generate different
11 output based on model properties and structure.
12[/comment]
13
14[template main(app : WebApp)]
15[file ('conditional-logic.txt', 'overwrite', 'UTF-8')]
16Conditional Logic with AQL Expressions
17======================================
18
19Application: [app.name/]
20
21[comment ================================================================ /]
22[comment SIMPLE BOOLEAN CONDITIONS /]
23[comment ================================================================ /]
24
251. Simple Boolean Conditions
26----------------------------
27[comment]
28 Use boolean properties directly in if conditions.
29 The condition is true when the property is true.
30[/comment]
31
32Page access levels:
33[for (page : Page | app.pages)]
34- [page.name/]: [if (page.requiresAuth)]Protected[else]Public[/if]
35[/for]
36
37[comment ================================================================ /]
38[comment COMPARISON CONDITIONS /]
39[comment ================================================================ /]
40
412. Comparison Conditions
42------------------------
43[comment]
44 Use comparison operators: =, <>, <, >, <=, >=
45 for numeric and string comparisons.
46[/comment]
47
48Entity complexity analysis:
49[for (entity : Entity | app.entities)]
50[let attrCount = entity.attributes->size()]
51- [entity.name/]: [attrCount/] attributes - [if (attrCount <= 3)]Simple[elseif (attrCount <= 6)]Medium[else]Complex[/if]
52[/let]
53[/for]
54
55[comment ================================================================ /]
56[comment STRING CONDITIONS /]
57[comment ================================================================ /]
58
593. String Conditions
60--------------------
61[comment]
62 Use string operations in conditions:
63 .startsWith(), .endsWith(), .contains(), etc.
64[/comment]
65
66Route categorisation:
67[for (page : Page | app.pages)]
68- [page.route/]: [if (page.route = '/')]Home[elseif (page.route.startsWith('/api'))]API[elseif (page.route.contains('/new'))]Creation[else]Standard[/if]
69[/for]
70
71[comment ================================================================ /]
72[comment COLLECTION CONDITIONS /]
73[comment ================================================================ /]
74
754. Collection Conditions
76------------------------
77[comment]
78 Use collection operations that return booleans:
79 ->isEmpty(), ->notEmpty(), ->includes(), etc.
80[/comment]
81
82Entity relationship status:
83[for (entity : Entity | app.entities)]
84- [entity.name/]: [if (entity.relationships->isEmpty())]Standalone[else]Has [entity.relationships->size()/] relationship(s)[/if]
85[/for]
86
87Page component status:
88[for (page : Page | app.pages)]
89- [page.name/]: [if (page.components->notEmpty())][page.components->size()/] component(s)[else]Empty page[/if]
90[/for]
91
92[comment ================================================================ /]
93[comment TYPE CONDITIONS /]
94[comment ================================================================ /]
95
965. Type Conditions
97------------------
98[comment]
99 Use oclIsTypeOf() and oclIsKindOf() to check
100 element types for type-based conditional output.
101[/comment]
102
103Component classification:
104[for (page : Page | app.pages)]
105[page.name/]:
106[for (comp : Component | page.components)]
107 - [comp.name/]: [if (comp.oclIsTypeOf(Form))]Form[elseif (comp.oclIsTypeOf(DataTable))]Data Table[elseif (comp.oclIsTypeOf(Navigation))]Navigation[else]Component[/if]
108[/for]
109
110[/for]
111
112[comment ================================================================ /]
113[comment ENUMERATION CONDITIONS /]
114[comment ================================================================ /]
115
1166. Enumeration Conditions
117-------------------------
118[comment]
119 Compare enumeration values using = and <>.
120 Reference enum literals with EnumType::literal.
121[/comment]
122
123Attribute type classification:
124[for (entity : Entity | app.entities)]
125[entity.name/]:
126[for (attr : Attribute | entity.attributes)]
127 - [attr.name/]: [if (attr.dataType = DataType::string or attr.dataType = DataType::text)]Text[elseif (attr.dataType = DataType::integer or attr.dataType = DataType::decimal)]Numeric[elseif (attr.dataType = DataType::date or attr.dataType = DataType::datetime)]Temporal[elseif (attr.dataType = DataType::boolean)]Boolean[else]Other[/if]
128[/for]
129
130[/for]
131
132[/file]
133[/template]

Step 2

Combine multiple conditions.

Use AQL’s and, or, and not operators to create complex conditional logic. Templates can respond to sophisticated model patterns.

ComplexConditions.mtl
1[comment encoding = UTF-8 /]
2[module ComplexConditions('http://www.example.org/webapp')]
3
4[comment]
5 Complex Conditional Logic in MTL Templates
6
7 This template demonstrates combining multiple AQL
8 conditions using 'and', 'or', and 'not' operators.
9
10 Complex conditions enable sophisticated decision-making
11 based on multiple model properties.
12[/comment]
13
14[template main(app : WebApp)]
15[file ('complex-conditions.txt', 'overwrite', 'UTF-8')]
16Complex Conditional Logic with AQL
17==================================
18
19Application: [app.name/]
20
21[comment ================================================================ /]
22[comment AND OPERATOR /]
23[comment ================================================================ /]
24
251. AND Operator - All Conditions Must Be True
26---------------------------------------------
27[comment]
28 Use 'and' to require multiple conditions.
29 All conditions must be true for the block to execute.
30[/comment]
31
32Required string attributes (not null AND string type AND not nullable):
33[for (entity : Entity | app.entities)]
34[entity.name/]:
35[for (attr : Attribute | entity.attributes)]
36[if (attr.dataType = DataType::string and not attr.isNullable and not attr.isPrimaryKey)]
37 - [attr.name/] (required string)
38[/if]
39[/for]
40
41[/for]
42
43[comment ================================================================ /]
44[comment OR OPERATOR /]
45[comment ================================================================ /]
46
472. OR Operator - Any Condition Can Be True
48------------------------------------------
49[comment]
50 Use 'or' to allow alternative conditions.
51 At least one condition must be true.
52[/comment]
53
54Temporal attributes (date OR datetime):
55[for (entity : Entity | app.entities)]
56[entity.name/]:
57[for (attr : Attribute | entity.attributes)]
58[if (attr.dataType = DataType::date or attr.dataType = DataType::datetime)]
59 - [attr.name/]: [attr.dataType/]
60[/if]
61[/for]
62
63[/for]
64
65Authentication-related pages (login OR register route):
66[for (page : Page | app.pages)]
67[if (page.route = '/login' or page.route = '/register')]
68- [page.name/] ([page.route/]) - Authentication page
69[/if]
70[/for]
71
72[comment ================================================================ /]
73[comment NOT OPERATOR /]
74[comment ================================================================ /]
75
763. NOT Operator - Negating Conditions
77-------------------------------------
78[comment]
79 Use 'not' to negate a condition.
80 The block executes when the condition is false.
81[/comment]
82
83Non-primary-key attributes:
84[for (entity : Entity | app.entities)]
85[entity.name/]:
86[for (attr : Attribute | entity.attributes)]
87[if (not attr.isPrimaryKey)]
88 - [attr.name/]
89[/if]
90[/for]
91
92[/for]
93
94[comment ================================================================ /]
95[comment COMBINED OPERATORS /]
96[comment ================================================================ /]
97
984. Combined Operators
99---------------------
100[comment]
101 Combine and, or, not for sophisticated logic.
102 Use parentheses to control precedence.
103[/comment]
104
105Complex attribute filter (string type AND (nullable OR has default)):
106[for (entity : Entity | app.entities)]
107[entity.name/]:
108[for (attr : Attribute | entity.attributes)]
109[if (attr.dataType = DataType::string and (attr.isNullable or (attr.defaultValue <> null and attr.defaultValue.size() > 0)))]
110 - [attr.name/]: optional string
111[/if]
112[/for]
113
114[/for]
115
116Non-trivial pages (protected AND has components AND has links):
117[for (page : Page | app.pages)]
118[if (page.requiresAuth and page.components->notEmpty() and page.linkedPages->notEmpty())]
119- [page.name/] - full-featured protected page
120[/if]
121[/for]
122
123[comment ================================================================ /]
124[comment NESTED CONDITIONALS /]
125[comment ================================================================ /]
126
1275. Nested Conditionals
128----------------------
129[comment]
130 Nest if statements for multi-level decisions.
131 Inner conditions are evaluated only when outer is true.
132[/comment]
133
134Page security analysis:
135[for (page : Page | app.pages)]
136[page.name/]:
137[if (page.requiresAuth)]
138 Access: Protected
139 [if (page.components->exists(c | c.oclIsTypeOf(Form)))]
140 Type: Authenticated data entry
141 [if (page.components->select(c | c.oclIsTypeOf(Form))->size() > 1)]
142 Note: Multiple forms present
143 [/if]
144 [elseif (page.components->exists(c | c.oclIsTypeOf(DataTable)))]
145 Type: Authenticated data viewing
146 [else]
147 Type: Authenticated information
148 [/if]
149[else]
150 Access: Public
151 [if (page.route.contains('login') or page.route.contains('register'))]
152 Type: Authentication flow
153 [else]
154 Type: Public information
155 [/if]
156[/if]
157
158[/for]
159
160[comment ================================================================ /]
161[comment CONDITIONAL WITH COMPUTATION /]
162[comment ================================================================ /]
163
1646. Conditional with Computation
165-------------------------------
166[comment]
167 Use computed values in conditions.
168 Let bindings help make complex conditions readable.
169[/comment]
170
171Entity analysis:
172[for (entity : Entity | app.entities)]
173[let attrCount = entity.attributes->size()]
174[let relCount = entity.relationships->size()]
175[let hasTimestamps = entity.attributes->exists(a | a.name.endsWith('At') or a.name.contains('Date'))]
176[entity.name/]:
177[if (attrCount > 5 and relCount > 1)]
178 Category: Core entity (many attributes and relationships)
179[elseif (attrCount > 3 or relCount > 0)]
180 Category: Standard entity
181[else]
182 Category: Simple entity
183[/if]
184[if (hasTimestamps)]
185 Features: Timestamped
186[/if]
187[/let]
188[/let]
189[/let]
190
191[/for]
192
193[/file]
194[/template]

Step 3

Use quantifiers in template conditions.

Apply AQL’s exists and forAll operations in template conditions. Generate code based on the existence or universality of model patterns.

QuantifierConditions.mtl
1[comment encoding = UTF-8 /]
2[module QuantifierConditions('http://www.example.org/webapp')]
3
4[comment]
5 Quantifier Operations in Conditional Generation
6
7 This template demonstrates using AQL's quantifier
8 operations (exists, forAll) within conditional statements.
9
10 Quantifiers test whether conditions hold across
11 collection elements - essential for validation logic.
12[/comment]
13
14[template main(app : WebApp)]
15[file ('quantifier-conditions.txt', 'overwrite', 'UTF-8')]
16Quantifier Operations in Conditions
17===================================
18
19Application: [app.name/]
20
21[comment ================================================================ /]
22[comment EXISTS OPERATION /]
23[comment ================================================================ /]
24
251. Exists - At Least One Element Matches
26----------------------------------------
27[comment]
28 ->exists(condition) returns true if ANY element
29 in the collection satisfies the condition.
30[/comment]
31
32[if (app.pages->exists(p | p.requiresAuth))]
33This application has protected pages requiring authentication.
34[else]
35All pages are publicly accessible.
36[/if]
37
38[if (app.entities->exists(e | e.relationships->notEmpty()))]
39The data model includes entity relationships.
40[/if]
41
42[if (app.stylesheets->exists(s | s.isExternal))]
43External stylesheets (CDN resources) are configured.
44[/if]
45
46[comment ================================================================ /]
47[comment FORALL OPERATION /]
48[comment ================================================================ /]
49
502. ForAll - All Elements Match
51------------------------------
52[comment]
53 ->forAll(condition) returns true if ALL elements
54 satisfy the condition (or collection is empty).
55[/comment]
56
57[if (app.entities->forAll(e | e.attributes->exists(a | a.isPrimaryKey)))]
58[OK] All entities have primary keys defined.
59[else]
60[WARNING] Some entities lack primary keys.
61[/if]
62
63[if (app.pages->forAll(p | p.title <> null and p.title.size() > 0))]
64[OK] All pages have titles set.
65[else]
66[WARNING] Some pages are missing titles.
67[/if]
68
69[if (app.entities->forAll(e | e.tableName <> null and e.tableName.size() > 0))]
70[OK] All entities have table names defined.
71[else]
72[WARNING] Some entities lack table name configuration.
73[/if]
74
75[comment ================================================================ /]
76[comment EXISTS IN LOOPS /]
77[comment ================================================================ /]
78
793. Exists Within Iteration
80--------------------------
81[comment]
82 Use exists to conditionally include content
83 based on nested collection properties.
84[/comment]
85
86Entity relationship status:
87[for (entity : Entity | app.entities)]
88[entity.name/]:
89[if (entity.relationships->exists(r | r.relationType = RelationType::oneToMany))]
90 - Has one-to-many relationships
91[/if]
92[if (entity.relationships->exists(r | r.relationType = RelationType::manyToMany))]
93 - Has many-to-many relationships
94[/if]
95[if (entity.relationships->exists(r | r.relationType = RelationType::manyToOne))]
96 - Has many-to-one relationships
97[/if]
98[if (not entity.relationships->exists(r | true))]
99 - No relationships defined
100[/if]
101
102[/for]
103
104[comment ================================================================ /]
105[comment FORALL IN LOOPS /]
106[comment ================================================================ /]
107
1084. ForAll Within Iteration
109--------------------------
110[comment]
111 Use forAll to validate that all nested elements
112 meet certain criteria.
113[/comment]
114
115Entity validation report:
116[for (entity : Entity | app.entities)]
117[entity.name/]:
118[if (entity.attributes->forAll(a | a.name <> null and a.name.size() > 0))]
119 [PASS] All attributes are named
120[else]
121 [FAIL] Some attributes lack names
122[/if]
123[if (entity.attributes->forAll(a | a.dataType <> null))]
124 [PASS] All attributes have data types
125[else]
126 [FAIL] Some attributes lack data types
127[/if]
128[if (entity.attributes->reject(a | a.isPrimaryKey)->forAll(a | a.isNullable or (a.defaultValue <> null and a.defaultValue.size() > 0)))]
129 [PASS] All non-PK attributes are nullable or have defaults
130[else]
131 [INFO] Some non-PK attributes are required without defaults
132[/if]
133
134[/for]
135
136[comment ================================================================ /]
137[comment COMBINED QUANTIFIERS /]
138[comment ================================================================ /]
139
1405. Combined Quantifier Patterns
141-------------------------------
142[comment]
143 Combine exists and forAll for comprehensive
144 validation and conditional logic.
145[/comment]
146
147Application validation summary:
148
149Schema validation:
150[if (app.entities->forAll(e | e.attributes->exists(a | a.isPrimaryKey)))]
151 [PASS] Every entity has a primary key
152[else]
153 [FAIL] Not all entities have primary keys
154[/if]
155
156Security validation:
157[if (app.pages->exists(p | not p.requiresAuth) and app.pages->exists(p | p.requiresAuth))]
158 [INFO] Mixed access - both public and protected pages
159[elseif (app.pages->forAll(p | p.requiresAuth))]
160 [INFO] Fully protected - all pages require authentication
161[else]
162 [INFO] Fully public - no authentication required
163[/if]
164
165UI completeness:
166[if (app.pages->forAll(p | p.components->notEmpty()))]
167 [PASS] All pages have components
168[else]
169 [WARN] Some pages have no components
170[/if]
171
172[comment ================================================================ /]
173[comment NESTED QUANTIFIERS /]
174[comment ================================================================ /]
175
1766. Nested Quantifier Expressions
177--------------------------------
178[comment]
179 Quantifiers can be nested for complex validation.
180 Keep nesting shallow for readability.
181[/comment]
182
183Deep validation:
184[if (app.pages->forAll(p | p.components->forAll(c | c.name <> null)))]
185 [PASS] All components on all pages are named
186[else]
187 [FAIL] Some components lack names
188[/if]
189
190[if (app.pages->exists(p | p.components->exists(c | c.oclIsTypeOf(Form))))]
191 [INFO] Application includes data entry forms
192[/if]
193
194Form completeness:
195[for (page : Page | app.pages)]
196[let forms = page.components->select(c | c.oclIsTypeOf(Form))->collect(c | c.oclAsType(Form))]
197[if (forms->notEmpty())]
198[page.name/]:
199[if (forms->forAll(f | f.fields->notEmpty()))]
200 [PASS] All forms have fields
201[else]
202 [WARN] Some forms are empty
203[/if]
204[if (forms->forAll(f | f.boundEntity <> null))]
205 [PASS] All forms are bound to entities
206[else]
207 [INFO] Some forms are not bound to entities
208[/if]
209[/if]
210[/let]
211[/for]
212
213[/file]
214[/template]

Step 4

Generate adaptive code structures.

Run templates with conditional logic to see how AQL expressions create adaptive code generation. The same template generates different code for different models.

Terminal
1# Adaptive Code Generation Output Examples
2# This script demonstrates running MTL templates with conditional logic
3
4# Run the conditional template
5swift-mtl generate \
6 --template ConditionalTemplate.mtl \
7 --model webapp-model.xmi \
8 --metamodel WebApp.ecore \
9 --output ./generated
10
11echo "Generated: conditional-logic.txt"
12
13# Run the complex conditions template
14swift-mtl generate \
15 --template ComplexConditions.mtl \
16 --model webapp-model.xmi \
17 --metamodel WebApp.ecore \
18 --output ./generated
19
20echo "Generated: complex-conditions.txt"
21
22# Run the quantifier conditions template
23swift-mtl generate \
24 --template QuantifierConditions.mtl \
25 --model webapp-model.xmi \
26 --metamodel WebApp.ecore \
27 --output ./generated
28
29echo "Generated: quantifier-conditions.txt"
30
31# Example output from conditional-logic.txt:
32# -----------------------------------------
33# Conditional Logic with AQL Expressions
34# ======================================
35#
36# Application: TaskManager
37#
38# Page access levels:
39# - Dashboard: Protected
40# - TaskList: Protected
41# - Projects: Protected
42# - NewProject: Protected
43# - Profile: Protected
44# - Login: Public
45# - Register: Public
46#
47# Entity complexity analysis:
48# - User: 6 attributes - Medium
49# - Task: 7 attributes - Complex
50# - Project: 6 attributes - Medium
51# - Comment: 3 attributes - Simple
52# -----------------------------------------
53
54# Example output from quantifier-conditions.txt:
55# -----------------------------------------
56# Application validation summary:
57#
58# Schema validation:
59# [PASS] Every entity has a primary key
60#
61# Security validation:
62# [INFO] Mixed access - both public and protected pages
63#
64# UI completeness:
65# [PASS] All pages have components
66# -----------------------------------------
67
68# View generated output
69echo ""
70echo "=== Conditional Logic Output ==="
71cat ./generated/conditional-logic.txt | head -40
72
73echo ""
74echo "=== Complex Conditions Output ==="
75cat ./generated/complex-conditions.txt | head -40
76
77echo ""
78echo "=== Quantifier Conditions Output ==="
79cat ./generated/quantifier-conditions.txt | head -50

Section 5

Advanced AQL-MTL Patterns

Advanced patterns combine AQL’s full querying power with MTL’s template features. These patterns enable sophisticated code generation scenarios and complex model analysis.

Master these patterns to create professional-grade code generators that handle real-world modeling scenarios.

Step 1

Create reusable AQL expressions with let bindings.

Use MTL’s [let] construct to bind complex AQL expressions to variables. This improves template readability and avoids repeated computation.

LetBindings.mtl
1[comment encoding = UTF-8 /]
2[module LetTemplate('http://www.example.org/webapp')]
3
4[comment]
5 Let Bindings in MTL Templates
6
7 This template demonstrates using MTL's [let] construct
8 to bind complex AQL expressions to named variables.
9
10 Let bindings improve readability, avoid repeated
11 computation, and make templates more maintainable.
12[/comment]
13
14[template main(app : WebApp)]
15[file ('let-bindings.txt', 'overwrite', 'UTF-8')]
16Let Bindings in MTL Templates
17=============================
18
19Application: [app.name/]
20
21[comment ================================================================ /]
22[comment BASIC LET BINDINGS /]
23[comment ================================================================ /]
24
251. Basic Let Bindings
26---------------------
27[comment]
28 [let varName = expression]
29 ... use varName ...
30 [/let]
31
32 The variable is only in scope within the let block.
33[/comment]
34
35[let pageCount = app.pages->size()]
36[let entityCount = app.entities->size()]
37[let stylesheetCount = app.stylesheets->size()]
38Application statistics:
39- Pages: [pageCount/]
40- Entities: [entityCount/]
41- Stylesheets: [stylesheetCount/]
42- Total elements: [pageCount + entityCount + stylesheetCount/]
43[/let]
44[/let]
45[/let]
46
47[comment ================================================================ /]
48[comment LET FOR FILTERED COLLECTIONS /]
49[comment ================================================================ /]
50
512. Let for Filtered Collections
52-------------------------------
53[comment]
54 Bind filtered collections to avoid repeating
55 the filter expression multiple times.
56[/comment]
57
58[let protectedPages = app.pages->select(p | p.requiresAuth)]
59[let publicPages = app.pages->reject(p | p.requiresAuth)]
60Access configuration:
61- Protected pages: [protectedPages->size()/]
62[for (page : Page | protectedPages)]
63 - [page.name/] ([page.route/])
64[/for]
65- Public pages: [publicPages->size()/]
66[for (page : Page | publicPages)]
67 - [page.name/] ([page.route/])
68[/for]
69[/let]
70[/let]
71
72[comment ================================================================ /]
73[comment NESTED LET BINDINGS /]
74[comment ================================================================ /]
75
763. Nested Let Bindings
77----------------------
78[comment]
79 Let bindings can be nested. Inner bindings can
80 reference outer bindings.
81[/comment]
82
83[for (entity : Entity | app.entities)]
84[let allAttrs = entity.attributes]
85[let pkAttrs = allAttrs->select(a | a.isPrimaryKey)]
86[let nonPkAttrs = allAttrs->reject(a | a.isPrimaryKey)]
87[let requiredAttrs = nonPkAttrs->select(a | not a.isNullable)]
88[let optionalAttrs = nonPkAttrs->select(a | a.isNullable)]
89
90[entity.name/] breakdown:
91 Total: [allAttrs->size()/]
92 Primary key(s): [pkAttrs->size()/]
93 Required: [requiredAttrs->size()/]
94 Optional: [optionalAttrs->size()/]
95[/let]
96[/let]
97[/let]
98[/let]
99[/let]
100[/for]
101
102[comment ================================================================ /]
103[comment LET FOR COMPUTED VALUES /]
104[comment ================================================================ /]
105
1064. Let for Computed Values
107--------------------------
108[comment]
109 Use let for computed strings, formatted values,
110 or any expression result to be reused.
111[/comment]
112
113[for (page : Page | app.pages)]
114[let componentCount = page.components->size()]
115[let hasNav = page.components->exists(c | c.oclIsTypeOf(Navigation))]
116[let hasForms = page.components->exists(c | c.oclIsTypeOf(Form))]
117[let hasTables = page.components->exists(c | c.oclIsTypeOf(DataTable))]
118[let features = if hasNav then 'Nav ' else '' endif + if hasForms then 'Forms ' else '' endif + if hasTables then 'Tables' else '' endif]
119
120[page.name/]:
121 Components: [componentCount/]
122 Features: [if (features.size() > 0)][features/][else]None[/if]
123[/let]
124[/let]
125[/let]
126[/let]
127[/let]
128[/for]
129
130[comment ================================================================ /]
131[comment LET FOR COMPLEX NAVIGATION /]
132[comment ================================================================ /]
133
1345. Let for Complex Navigation
135-----------------------------
136[comment]
137 Bind intermediate navigation results for clarity
138 when dealing with long navigation chains.
139[/comment]
140
141[for (page : Page | app.pages)]
142[let forms = page.components->select(c | c.oclIsTypeOf(Form))->collect(c | c.oclAsType(Form))]
143[let boundForms = forms->select(f | f.boundEntity <> null)]
144[let unboundForms = forms->reject(f | f.boundEntity <> null)]
145[if (forms->notEmpty())]
146[page.name/] form analysis:
147 Total forms: [forms->size()/]
148 Bound to entities: [boundForms->size()/]
149 Unbound: [unboundForms->size()/]
150[for (form : Form | boundForms)]
151 - [form.name/] -> [form.boundEntity.name/] ([form.fields->size()/] fields)
152[/for]
153[/if]
154[/let]
155[/let]
156[/let]
157[/for]
158
159[comment ================================================================ /]
160[comment LET WITH AGGREGATIONS /]
161[comment ================================================================ /]
162
1636. Let with Aggregations
164------------------------
165[comment]
166 Bind aggregated values for statistics and summaries.
167[/comment]
168
169[let totalAttrs = app.entities->collect(e | e.attributes->size())->sum()]
170[let totalRels = app.entities->collect(e | e.relationships->size())->sum()]
171[let avgAttrs = totalAttrs / app.entities->size()]
172[let maxAttrs = app.entities->collect(e | e.attributes->size())->max()]
173[let minAttrs = app.entities->collect(e | e.attributes->size())->min()]
174
175Data model statistics:
176- Total attributes: [totalAttrs/]
177- Total relationships: [totalRels/]
178- Average attributes per entity: [avgAttrs/]
179- Largest entity: [maxAttrs/] attributes
180- Smallest entity: [minAttrs/] attributes
181[/let]
182[/let]
183[/let]
184[/let]
185[/let]
186
187[/file]
188[/template]

Step 2

Build query-driven template libraries.

Create templates that serve as AQL query libraries. These templates can be imported and reused across multiple generation scenarios.

QueryLibrary.mtl
1[comment encoding = UTF-8 /]
2[module QueryLibrary('http://www.example.org/webapp')]
3
4[comment]
5 Reusable AQL Query Templates
6
7 This module demonstrates creating a library of reusable
8 query templates that can be imported and invoked from
9 other templates.
10
11 Query libraries centralise complex AQL expressions,
12 promoting code reuse and maintainability.
13[/comment]
14
15[comment ================================================================ /]
16[comment QUERY TEMPLATES (HELPERS) /]
17[comment ================================================================ /]
18
19[comment]
20 Query templates return computed values.
21 They encapsulate complex AQL expressions.
22[/comment]
23
24[template public getProtectedPages(app : WebApp)]
25[app.pages->select(p | p.requiresAuth)/]
26[/template]
27
28[template public getPublicPages(app : WebApp)]
29[app.pages->select(p | not p.requiresAuth)/]
30[/template]
31
32[template public getPrimaryKeyAttributes(entity : Entity)]
33[entity.attributes->select(a | a.isPrimaryKey)/]
34[/template]
35
36[template public getRequiredAttributes(entity : Entity)]
37[entity.attributes->reject(a | a.isPrimaryKey)->select(a | not a.isNullable)/]
38[/template]
39
40[template public getOptionalAttributes(entity : Entity)]
41[entity.attributes->select(a | a.isNullable)/]
42[/template]
43
44[template public getStringAttributes(entity : Entity)]
45[entity.attributes->select(a | a.dataType = DataType::string or a.dataType = DataType::text)/]
46[/template]
47
48[template public getNumericAttributes(entity : Entity)]
49[entity.attributes->select(a | a.dataType = DataType::integer or a.dataType = DataType::decimal)/]
50[/template]
51
52[template public getDateAttributes(entity : Entity)]
53[entity.attributes->select(a | a.dataType = DataType::date or a.dataType = DataType::datetime)/]
54[/template]
55
56[template public getFormsOnPage(page : Page)]
57[page.components->select(c | c.oclIsTypeOf(Form))->collect(c | c.oclAsType(Form))/]
58[/template]
59
60[template public getTablesOnPage(page : Page)]
61[page.components->select(c | c.oclIsTypeOf(DataTable))->collect(c | c.oclAsType(DataTable))/]
62[/template]
63
64[comment ================================================================ /]
65[comment FORMATTED OUTPUT TEMPLATES /]
66[comment ================================================================ /]
67
68[comment]
69 Output templates generate formatted text.
70 They can be invoked from other templates.
71[/comment]
72
73[template public formatAttributeType(attr : Attribute)]
74[if (attr.dataType = DataType::string)]String[if (attr.maxLength > 0)]([attr.maxLength/])[/if][elseif (attr.dataType = DataType::integer)]Int[elseif (attr.dataType = DataType::decimal)]Decimal[elseif (attr.dataType = DataType::boolean)]Bool[elseif (attr.dataType = DataType::date)]Date[elseif (attr.dataType = DataType::datetime)]DateTime[elseif (attr.dataType = DataType::text)]Text[else][attr.dataType/][/if][/template]
75
76[template public formatFieldType(field : Field)]
77[if (field.fieldType = FieldType::text)]text[elseif (field.fieldType = FieldType::email)]email[elseif (field.fieldType = FieldType::password)]password[elseif (field.fieldType = FieldType::number)]number[elseif (field.fieldType = FieldType::date)]date[elseif (field.fieldType = FieldType::textarea)]textarea[elseif (field.fieldType = FieldType::select)]select[elseif (field.fieldType = FieldType::checkbox)]checkbox[elseif (field.fieldType = FieldType::hidden)]hidden[else][field.fieldType/][/if][/template]
78
79[template public formatRelationType(rel : Relationship)]
80[if (rel.relationType = RelationType::oneToOne)]1:1[elseif (rel.relationType = RelationType::oneToMany)]1:N[elseif (rel.relationType = RelationType::manyToOne)]N:1[elseif (rel.relationType = RelationType::manyToMany)]N:N[else][rel.relationType/][/if][/template]
81
82[comment ================================================================ /]
83[comment MAIN TEMPLATE - USING THE LIBRARY /]
84[comment ================================================================ /]
85
86[template main(app : WebApp)]
87[file ('query-library-demo.txt', 'overwrite', 'UTF-8')]
88Query Library Demonstration
89===========================
90
91Application: [app.name/]
92
93Using Query Templates
94---------------------
95
96Protected pages (via query template):
97[for (page : Page | app.pages->select(p | p.requiresAuth))]
98- [page.name/]
99[/for]
100
101Public pages (via query template):
102[for (page : Page | app.pages->select(p | not p.requiresAuth))]
103- [page.name/]
104[/for]
105
106Entity attribute breakdown (using helper queries):
107[for (entity : Entity | app.entities)]
108[entity.name/]:
109 Primary keys: [entity.attributes->select(a | a.isPrimaryKey)->collect(a | a.name)->sep(', ')/]
110 Required: [entity.attributes->reject(a | a.isPrimaryKey)->select(a | not a.isNullable)->collect(a | a.name)->sep(', ')/]
111 Optional: [entity.attributes->select(a | a.isNullable)->collect(a | a.name)->sep(', ')/]
112
113[/for]
114
115Formatted attribute types:
116[for (entity : Entity | app.entities)]
117[entity.name/]:
118[for (attr : Attribute | entity.attributes)]
119 - [attr.name/]: [formatAttributeType(attr)/][if (attr.isPrimaryKey)] (PK)[/if][if (not attr.isNullable and not attr.isPrimaryKey)] (required)[/if]
120[/for]
121
122[/for]
123
124Formatted relationship types:
125[for (entity : Entity | app.entities)]
126[if (entity.relationships->notEmpty())]
127[entity.name/]:
128[for (rel : Relationship | entity.relationships)]
129 - [rel.name/] [formatRelationType(rel)/] [rel.targetEntity.name/]
130[/for]
131
132[/if]
133[/for]
134
135Forms and their fields (using helper queries):
136[for (page : Page | app.pages)]
137[let pageForms = page.components->select(c | c.oclIsTypeOf(Form))->collect(c | c.oclAsType(Form))]
138[if (pageForms->notEmpty())]
139[page.name/]:
140[for (form : Form | pageForms)]
141 [form.name/]:
142[for (field : Field | form.fields)]
143 - [field.name/]: [formatFieldType(field)/][if (field.isRequired)] (required)[/if]
144[/for]
145[/for]
146
147[/if]
148[/let]
149[/for]
150
151[/file]
152[/template]

Step 3

Handle cross-reference resolution.

Use AQL to navigate cross-model references and generate code that maintains referential integrity across generated artefacts.

CrossReferences.mtl
1[comment encoding = UTF-8 /]
2[module CrossReferences('http://www.example.org/webapp')]
3
4[comment]
5 Cross-Reference Resolution in MTL Templates
6
7 This template demonstrates using AQL to navigate
8 cross-model references and generate code that maintains
9 referential integrity across generated artefacts.
10
11 Cross-references link model elements that are not
12 in a containment relationship, requiring careful
13 navigation and resolution.
14[/comment]
15
16[template main(app : WebApp)]
17[file ('cross-references.txt', 'overwrite', 'UTF-8')]
18Cross-Reference Resolution with AQL
19===================================
20
21Application: [app.name/]
22
23[comment ================================================================ /]
24[comment NAVIGATING CROSS-REFERENCES /]
25[comment ================================================================ /]
26
271. Navigating Cross-References
28------------------------------
29[comment]
30 Cross-references connect related elements.
31 Navigate them like containment references.
32[/comment]
33
34Entity relationships (cross-references to other entities):
35[for (entity : Entity | app.entities)]
36[entity.name/]:
37[if (entity.relationships->notEmpty())]
38[for (rel : Relationship | entity.relationships)]
39 - [rel.name/] -> [rel.targetEntity.name/] ([rel.relationType/])
40[/for]
41[else]
42 (no relationships)
43[/if]
44
45[/for]
46
47[comment ================================================================ /]
48[comment PAGE LINK REFERENCES /]
49[comment ================================================================ /]
50
512. Page Link References
52-----------------------
53[comment]
54 Pages can link to other pages, creating a navigation
55 graph. Follow these references to understand flow.
56[/comment]
57
58Page navigation graph:
59[for (page : Page | app.pages)]
60[page.name/] -> [if (page.linkedPages->notEmpty())][page.linkedPages->collect(p | p.name)->sep(', ')/][else](no outbound links)[/if]
61[/for]
62
63Incoming links analysis:
64[for (page : Page | app.pages)]
65[let incomingLinks = app.pages->select(p | p.linkedPages->includes(page))]
66[page.name/] <- [if (incomingLinks->notEmpty())][incomingLinks->collect(p | p.name)->sep(', ')/][else](no incoming links)[/if]
67[/let]
68[/for]
69
70[comment ================================================================ /]
71[comment FORM-ENTITY BINDINGS /]
72[comment ================================================================ /]
73
743. Form-Entity Bindings
75-----------------------
76[comment]
77 Forms can be bound to entities, creating cross-references
78 between UI components and data models.
79[/comment]
80
81Form-entity binding map:
82[for (page : Page | app.pages)]
83[for (comp : Component | page.components->select(c | c.oclIsTypeOf(Form)))]
84[let form = comp.oclAsType(Form)]
85[page.name/]/[form.name/]:
86[if (form.boundEntity <> null)]
87 Bound to: [form.boundEntity.name/]
88 Entity attributes: [form.boundEntity.attributes->collect(a | a.name)->sep(', ')/]
89 Form fields: [form.fields->collect(f | f.name)->sep(', ')/]
90[else]
91 Not bound to any entity
92[/if]
93[/let]
94[/for]
95[/for]
96
97[comment ================================================================ /]
98[comment FIELD-ATTRIBUTE BINDINGS /]
99[comment ================================================================ /]
100
1014. Field-Attribute Bindings
102---------------------------
103[comment]
104 Form fields can be bound to entity attributes,
105 connecting UI input to data storage.
106[/comment]
107
108Field-attribute mapping:
109[for (page : Page | app.pages)]
110[for (comp : Component | page.components->select(c | c.oclIsTypeOf(Form)))]
111[let form = comp.oclAsType(Form)]
112[if (form.fields->exists(f | f.boundAttribute <> null))]
113[page.name/]/[form.name/] field bindings:
114[for (field : Field | form.fields)]
115[if (field.boundAttribute <> null)]
116 [field.name/] ([field.fieldType/]) -> [field.boundAttribute.name/] ([field.boundAttribute.dataType/])
117[else]
118 [field.name/] ([field.fieldType/]) -> (unbound)
119[/if]
120[/for]
121
122[/if]
123[/let]
124[/for]
125[/for]
126
127[comment ================================================================ /]
128[comment TABLE-ENTITY BINDINGS /]
129[comment ================================================================ /]
130
1315. DataTable-Entity Bindings
132----------------------------
133[comment]
134 DataTables bind to entities as data sources,
135 and columns bind to attributes.
136[/comment]
137
138DataTable data source mappings:
139[for (page : Page | app.pages)]
140[for (comp : Component | page.components->select(c | c.oclIsTypeOf(DataTable)))]
141[let table = comp.oclAsType(DataTable)]
142[page.name/]/[table.name/]:
143[if (table.dataSource <> null)]
144 Data source: [table.dataSource.name/]
145 Columns:
146[for (col : Column | table.columns)]
147 [col.header/] -> [if (col.boundAttribute <> null)][col.boundAttribute.name/][else](unbound)[/if]
148[/for]
149[else]
150 No data source configured
151[/if]
152[/let]
153[/for]
154[/for]
155
156[comment ================================================================ /]
157[comment NAVIGATION TARGET REFERENCES /]
158[comment ================================================================ /]
159
1606. Navigation Target References
161-------------------------------
162[comment]
163 Navigation items reference target pages,
164 building the application's navigation structure.
165[/comment]
166
167Navigation structure:
168[for (page : Page | app.pages)]
169[for (comp : Component | page.components->select(c | c.oclIsTypeOf(Navigation)))]
170[let nav = comp.oclAsType(Navigation)]
171[page.name/]/[nav.name/] ([nav.navType/]):
172[for (item : NavItem | nav.items)]
173 [item.label/] -> [if (item.targetPage <> null)][item.targetPage.name/] ([item.targetPage.route/])[else](no target)[/if]
174[for (subItem : NavItem | item.subItems)]
175 [subItem.label/] -> [if (subItem.targetPage <> null)][subItem.targetPage.name/][else](no target)[/if]
176[/for]
177[/for]
178[/let]
179[/for]
180[/for]
181
182[comment ================================================================ /]
183[comment REFERENTIAL INTEGRITY CHECK /]
184[comment ================================================================ /]
185
1867. Referential Integrity Validation
187-----------------------------------
188[comment]
189 Validate that all cross-references point to
190 valid targets within the model.
191[/comment]
192
193Reference validation:
194
195Entity relationship targets:
196[for (entity : Entity | app.entities)]
197[for (rel : Relationship | entity.relationships)]
198[if (app.entities->includes(rel.targetEntity))]
199[OK] [entity.name/].[rel.name/] -> [rel.targetEntity.name/]
200[else]
201[ERROR] [entity.name/].[rel.name/] -> invalid target
202[/if]
203[/for]
204[/for]
205
206Page link targets:
207[for (page : Page | app.pages)]
208[for (linked : Page | page.linkedPages)]
209[if (app.pages->includes(linked))]
210[OK] [page.name/] -> [linked.name/]
211[else]
212[ERROR] [page.name/] -> invalid page reference
213[/if]
214[/for]
215[/for]
216
217[/file]
218[/template]

Step 4

Create comprehensive code generation pipelines.

Combine all AQL-MTL patterns to create sophisticated code generators. See how queries drive every aspect of code structure and content.

Terminal
1#!/bin/bash
2#
3# Comprehensive Code Generation Example
4# Demonstrates running MTL templates with AQL expressions
5#
6# This script shows how to invoke the swift-mtl tool to generate
7# code from a web application model using the templates that
8# demonstrate AQL expressions within MTL.
9#
10
11set -e
12
13# Configuration
14MODEL_FILE="webapp-instance.xmi"
15METAMODEL_FILE="webapp-metamodel.ecore"
16OUTPUT_DIR="./generated"
17
18echo "=========================================="
19echo "AQL in MTL - Comprehensive Generation"
20echo "=========================================="
21echo ""
22
23# Check for required files
24if [ ! -f "$MODEL_FILE" ]; then
25 echo "Error: Model file '$MODEL_FILE' not found"
26 exit 1
27fi
28
29if [ ! -f "$METAMODEL_FILE" ]; then
30 echo "Error: Metamodel file '$METAMODEL_FILE' not found"
31 exit 1
32fi
33
34# Create output directory
35mkdir -p "$OUTPUT_DIR"
36echo "Output directory: $OUTPUT_DIR"
37echo ""
38
39# ============================================
40# Step 1: Basic Template - Navigation Demo
41# ============================================
42echo "Step 1: Running basic template (navigation expressions)..."
43swift-mtl generate \
44 --template aql-04-step-03-basic-template.mtl \
45 --model "$MODEL_FILE" \
46 --metamodel "$METAMODEL_FILE" \
47 --output "$OUTPUT_DIR/docs"
48
49echo " Generated: README.md"
50echo ""
51
52# ============================================
53# Step 2: Collection Operations Demo
54# ============================================
55echo "Step 2: Running collection operations template..."
56swift-mtl generate \
57 --template aql-04-step-05-collection-operations.mtl \
58 --model "$MODEL_FILE" \
59 --metamodel "$METAMODEL_FILE" \
60 --output "$OUTPUT_DIR/docs"
61
62echo " Generated: collection-ops.txt"
63echo ""
64
65# ============================================
66# Step 3: Select and Reject Demo
67# ============================================
68echo "Step 3: Running select/reject template..."
69swift-mtl generate \
70 --template aql-04-step-06-select-and-reject.mtl \
71 --model "$MODEL_FILE" \
72 --metamodel "$METAMODEL_FILE" \
73 --output "$OUTPUT_DIR/docs"
74
75echo " Generated: filtering.txt"
76echo ""
77
78# ============================================
79# Step 4: String Operations Demo
80# ============================================
81echo "Step 4: Running string operations template..."
82swift-mtl generate \
83 --template aql-04-step-10-string-operations.mtl \
84 --model "$MODEL_FILE" \
85 --metamodel "$METAMODEL_FILE" \
86 --output "$OUTPUT_DIR/docs"
87
88echo " Generated: string-ops.txt"
89echo ""
90
91# ============================================
92# Step 5: Type Operations Demo
93# ============================================
94echo "Step 5: Running type operations template..."
95swift-mtl generate \
96 --template aql-04-step-11-type-operations.mtl \
97 --model "$MODEL_FILE" \
98 --metamodel "$METAMODEL_FILE" \
99 --output "$OUTPUT_DIR/docs"
100
101echo " Generated: type-ops.txt"
102echo ""
103
104# ============================================
105# Step 6: File Generation Demo
106# ============================================
107echo "Step 6: Running file generation template..."
108swift-mtl generate \
109 --template aql-04-step-18-file-generation.mtl \
110 --model "$MODEL_FILE" \
111 --metamodel "$METAMODEL_FILE" \
112 --output "$OUTPUT_DIR"
113
114echo " Generated: README.md, *.model.swift, *ViewModel.swift"
115echo " Generated: Routes.swift, styles/app.css"
116echo ""
117
118# ============================================
119# Step 7: Advanced Patterns (Full Application)
120# ============================================
121echo "Step 7: Running advanced patterns template..."
122swift-mtl generate \
123 --template aql-04-step-19-advanced-patterns.mtl \
124 --model "$MODEL_FILE" \
125 --metamodel "$METAMODEL_FILE" \
126 --output "$OUTPUT_DIR"
127
128echo " Generated: AppConfiguration.swift"
129echo " Generated: schema.sql"
130echo " Generated: APIRoutes.swift"
131echo " Generated: ViewModels/*.swift"
132echo " Generated: Validation.swift"
133echo ""
134
135# ============================================
136# Summary of Generated Files
137# ============================================
138echo "=========================================="
139echo "Generation Complete!"
140echo "=========================================="
141echo ""
142echo "Generated file structure:"
143echo ""
144
145# Display generated structure (if tree is available)
146if command -v tree &> /dev/null; then
147 tree "$OUTPUT_DIR"
148else
149 find "$OUTPUT_DIR" -type f | sort | while read -r file; do
150 echo " $file"
151 done
152fi
153
154echo ""
155echo "Key AQL features demonstrated:"
156echo " - Navigation: app.pages, entity.attributes"
157echo " - Collection ops: ->size(), ->select(), ->reject()"
158echo " - Quantifiers: ->exists(), ->forAll()"
159echo " - String ops: .toUpperCase(), .substituteAll()"
160echo " - Type ops: oclIsTypeOf(), oclAsType()"
161echo " - Conditionals: if-then-else expressions"
162echo " - Let bindings: [let x = expression]"
163echo " - Sorting: ->sortedBy()"
164echo " - Arithmetic: +, -, *, /, ->sum()"
165echo ""
166
167# ============================================
168# AQL Expression Examples Summary
169# ============================================
170echo "=========================================="
171echo "AQL Expression Quick Reference"
172echo "=========================================="
173echo ""
174echo "Navigation:"
175echo " app.pages - Access collection"
176echo " page.components - Chained navigation"
177echo " comp.oclAsType(Form).fields - Type cast navigation"
178echo ""
179echo "Filtering:"
180echo " ->select(p | p.requiresAuth) - Keep matching"
181echo " ->reject(a | a.isPrimaryKey) - Exclude matching"
182echo " ->exists(a | a.isPrimaryKey) - Any match?"
183echo " ->forAll(a | a.name <> null) - All match?"
184echo ""
185echo "Transformation:"
186echo " ->collect(e | e.name) - Map to values"
187echo " ->sortedBy(e | e.name) - Order by key"
188echo " ->flatten() - Flatten nested"
189echo ""
190echo "Aggregation:"
191echo " ->size() - Count elements"
192echo " ->sum() - Sum numbers"
193echo " ->first(), ->last() - Get endpoints"
194echo ""
195echo "String Operations:"
196echo " .toUpperCase(), .toLowerCase() - Case conversion"
197echo " .substituteAll('a', 'b') - Replace all"
198echo " .startsWith(), .endsWith() - Prefix/suffix test"
199echo ""
200echo "Type Operations:"
201echo " .oclIsTypeOf(Form) - Exact type check"
202echo " .oclIsKindOf(Component) - Inheritance check"
203echo " .oclAsType(Form) - Cast to type"
204echo ""
205echo "Conditional Expressions:"
206echo " if cond then val1 else val2 endif - Inline conditional"
207echo ""

Check Your Understanding

Question 1 of 4

How are AQL expressions used within MTL templates?

Question 2 of 4

What happens when you use [for (item : Type | collection.select(x | x.property > 5))] in an MTL template?

Question 3 of 4

When should you use [let variable = aqlExpression] in templates?

Question 4 of 4

How do you safely handle optional references in MTL templates?

Complex Queries

Master advanced AQL query patterns for sophisticated model analysis and cross-model operations.