@typemf/generator 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +79 -0
- package/dist/cli.js +1612 -0
- package/dist/index.d.ts +741 -0
- package/dist/index.js +1678 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
- package/templates/typescript/eclass.njk +559 -0
- package/templates/typescript/eenum.njk +8 -0
- package/templates/typescript/efactory.njk +52 -0
- package/templates/typescript/epackage.njk +438 -0
- package/templates/typescript/epackageref.njk +46 -0
- package/templates/typescript/eswitch.njk +32 -0
- package/templates/typescript/etypeguards.njk +30 -0
- package/templates/typescript/main.njk +19 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
{% set pkgClass = packageClassName(package) %}
|
|
2
|
+
{% file pkgClass + ".ts" %}
|
|
3
|
+
{% for name in metaclassAccessorCollisionTypeNames(package) %}
|
|
4
|
+
{% set _ = useImport(name, "types", {foundational: true}) %}
|
|
5
|
+
{% endfor %}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Classifier/feature IDs, as pure numeric literals - safe to import from
|
|
9
|
+
* anywhere (Factory, Switch) with zero risk of the circular-import hazard
|
|
10
|
+
* that made a lazy, explicit init() necessary for the singleton itself
|
|
11
|
+
* (see {{ pkgClass }}Impl.ts) - these are just numbers, never require
|
|
12
|
+
* constructing anything.
|
|
13
|
+
*/
|
|
14
|
+
export const Ids = {
|
|
15
|
+
{% for classifier in package.getEClassifiers() %}
|
|
16
|
+
{{ classifier.getName() }}: { self: {{ classifier.getClassifierID() }}{% if isEClass(classifier) %}{% for feature in classifier.getEStructuralFeatures() %}, {{ feature.getName() }}: {{ feature.getFeatureID() }}{% endfor %}{% for op in classifier.getEOperations() %}, op{{ loop.index0 }}: {{ op.getOperationID() }}{% endfor %}{% endif %} },
|
|
17
|
+
{% endfor %}
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
{{ docComment(package) }}
|
|
21
|
+
export interface {{ pkgClass }} extends {{ useImport("EPackage", "types", {foundational: true}) }} {
|
|
22
|
+
{% for classifier in package.getEClassifiers() %}
|
|
23
|
+
{% set collision = metaclassAccessorCollision(classifier.getName(), package) %}
|
|
24
|
+
{% if isEClass(classifier) %}
|
|
25
|
+
get{{ classifier.getName() }}(): {{ useImport("EClass", "types", {foundational: true}) }};
|
|
26
|
+
{% if collision %}
|
|
27
|
+
get{{ classifier.getName() }}({{ paramList(collision) }}): {{ tsOperationReturnType(collision) }};
|
|
28
|
+
{% endif %}
|
|
29
|
+
{% for feature in classifier.getEStructuralFeatures() %}
|
|
30
|
+
get{{ classifier.getName() }}_{{ ucfirst(feature.getName()) }}(): {{ useImport("EReference", "types", {foundational: true}) if isEReference(feature) else useImport("EAttribute", "types", {foundational: true}) }};
|
|
31
|
+
{% endfor %}
|
|
32
|
+
{% elif isEEnum(classifier) %}
|
|
33
|
+
get{{ classifier.getName() }}(): {{ useImport("EEnum", "types", {foundational: true}) }};
|
|
34
|
+
{% if collision %}
|
|
35
|
+
get{{ classifier.getName() }}({{ paramList(collision) }}): {{ tsOperationReturnType(collision) }};
|
|
36
|
+
{% endif %}
|
|
37
|
+
{% else %}
|
|
38
|
+
get{{ classifier.getName() }}(): {{ useImport("EDataType", "types", {foundational: true}) }};
|
|
39
|
+
{% if collision %}
|
|
40
|
+
get{{ classifier.getName() }}({{ paramList(collision) }}): {{ tsOperationReturnType(collision) }};
|
|
41
|
+
{% endif %}
|
|
42
|
+
{% endif %}
|
|
43
|
+
{% endfor %}
|
|
44
|
+
}
|
|
45
|
+
{% endfile %}
|
|
46
|
+
{% file "impl/" + pkgClass + "Impl.ts" %}
|
|
47
|
+
{% for name in metaclassAccessorCollisionTypeNames(package) %}
|
|
48
|
+
{% set _ = useImport(name, "types", {foundational: true}) %}
|
|
49
|
+
{% endfor %}
|
|
50
|
+
{% set _ = useImport("EPackageImpl", "impl", {foundational: true}) %}
|
|
51
|
+
{% set _ = useImport("EClassImpl", "impl", {foundational: true}) %}
|
|
52
|
+
{% set _ = useImport("EAttributeImpl", "impl", {foundational: true}) %}
|
|
53
|
+
{% set _ = useImport("EReferenceImpl", "impl", {foundational: true}) %}
|
|
54
|
+
{% set _ = useImport("EOperationImpl", "impl", {foundational: true}) %}
|
|
55
|
+
{% set _ = useImport("EParameterImpl", "impl", {foundational: true}) %}
|
|
56
|
+
{% set _ = useImport("EDataTypeImpl", "impl", {foundational: true}) %}
|
|
57
|
+
{% set _ = useImport("EEnumImpl", "impl", {foundational: true}) %}
|
|
58
|
+
{% set _ = useImport("EEnumLiteralImpl", "impl", {foundational: true}) %}
|
|
59
|
+
{% set _ = useImport("EAnnotationImpl", "impl", {foundational: true}) %}
|
|
60
|
+
{% set _ = useImport("EFactoryImpl", "impl", {foundational: true}) %}
|
|
61
|
+
{% set _ = useImport(pkgClass, "root", {foundational: false}) %}
|
|
62
|
+
{% set _ = useImport("Ids", "root", {file: pkgClass, foundational: false}) %}
|
|
63
|
+
{% set _ = useImport(factoryClassName(package), "root", {foundational: false}) %}
|
|
64
|
+
{% set _ = useImport(factoryClassName(package) + "Impl", "impl", {foundational: false}) %}
|
|
65
|
+
{% set _ = useImport("set" + pkgClass + "Ref", "impl", {file: pkgClass + "Ref", foundational: false}) %}
|
|
66
|
+
|
|
67
|
+
{#-
|
|
68
|
+
Emits code reconstructing `element`'s real EAnnotations (source +
|
|
69
|
+
details) onto the runtime object built at `targetExpr`. Scoped to
|
|
70
|
+
package/classifier/feature/enum-literal only - EOperations have no
|
|
71
|
+
runtime representation in the converted metamodel yet, so there's
|
|
72
|
+
nowhere for an operation-level annotation to attach (see NOTES.md).
|
|
73
|
+
#}
|
|
74
|
+
{#-
|
|
75
|
+
Both macros exist for one reason: this template is shared between
|
|
76
|
+
ordinary metamodel generation and self-hosting Ecore.ecore, and only
|
|
77
|
+
self-hosting has the bootstrap-ordering hazard (a real, confirmed one,
|
|
78
|
+
not hypothetical - see NOTES.md) that eBasicSetValue/eBootstrapList exist to
|
|
79
|
+
route around. For an ordinary metamodel, the runtime metaclasses are
|
|
80
|
+
@typemf/core's own hand-written classes (which don't have
|
|
81
|
+
eBasicSetValue/eBootstrapList, and don't need to - they're not self-
|
|
82
|
+
describing), and Ids has no entry for foundational names like
|
|
83
|
+
"EClassifier"/"EClass" at all unless the metamodel genuinely declares
|
|
84
|
+
classifiers with those names. So the ordinary, reflective bean setter
|
|
85
|
+
call is correct and necessary there, and the bypass is correct and
|
|
86
|
+
necessary here - never both from the same call site.
|
|
87
|
+
#}
|
|
88
|
+
{#- The expression for classifier `c`: its field for a classifier of this package, Ecore's own accessor otherwise. #}
|
|
89
|
+
{% macro classifierExpr(c) -%}
|
|
90
|
+
{%- if c.getEPackage() == package -%}this._{{ c.getName() }}{%- else -%}{{ useImport("EcorePackageImpl", "impl", {foundational: true}) }}.eINSTANCE.get{{ c.getName() }}(){%- endif -%}
|
|
91
|
+
{%- endmacro %}
|
|
92
|
+
{% macro bootstrapSet(targetExpr, beanSetterName, idsExpr, valueExpr) %}
|
|
93
|
+
{% if options['generate-ecore'] %}
|
|
94
|
+
{{ targetExpr }}.eBasicSetValue({{ idsExpr }}, {{ valueExpr }});
|
|
95
|
+
{% else %}
|
|
96
|
+
{{ targetExpr }}.{{ beanSetterName }}({{ valueExpr }});
|
|
97
|
+
{% endif %}
|
|
98
|
+
{% endmacro %}
|
|
99
|
+
|
|
100
|
+
{% macro bootstrapAdd(targetExpr, beanGetterName, idsExpr, itemExpr) %}
|
|
101
|
+
{% if options['generate-ecore'] %}
|
|
102
|
+
{{ targetExpr }}.eBootstrapList({{ idsExpr }}).add({{ itemExpr }});
|
|
103
|
+
{% else %}
|
|
104
|
+
{{ targetExpr }}.{{ beanGetterName }}().add({{ itemExpr }});
|
|
105
|
+
{% endif %}
|
|
106
|
+
{% endmacro %}
|
|
107
|
+
|
|
108
|
+
{% macro emitAnnotations(targetExpr, element) %}
|
|
109
|
+
{#- Mode-conditional for a real, structural reason, not just a different method name: self-hosting's
|
|
110
|
+
generated EAnnotationImpl.ts (and, since the getDetails() migration, @typemf/core's own hand-written
|
|
111
|
+
one too - both now genuinely EList<EStringToStringMapEntry>, matching real EMF) still differ in HOW
|
|
112
|
+
a detail gets constructed during BOOTSTRAP specifically - the self-hosting branch bypasses the
|
|
113
|
+
reflective path entirely (eBasicSetValue/eBootstrapList), for the same bootstrap-ordering reason as
|
|
114
|
+
everywhere else in this file (the metaclass for EStringToStringMapEntry doesn't exist yet at the
|
|
115
|
+
point this code runs during self-construction); the ordinary branch has no such ordering problem and
|
|
116
|
+
uses the real, public API (setDetailValue) directly. #}
|
|
117
|
+
{% for annotation in element.getEAnnotations() %}
|
|
118
|
+
{
|
|
119
|
+
const annotation = new EAnnotationImpl();
|
|
120
|
+
{% if annotation.getSource() %}
|
|
121
|
+
{{ bootstrapSet("annotation", "setSource", "Ids.EAnnotation.source", jsString(annotation.getSource())) }}
|
|
122
|
+
{% endif %}
|
|
123
|
+
{% if options['generate-ecore'] %}
|
|
124
|
+
{% for entry in detailsEntries(annotation) %}
|
|
125
|
+
{
|
|
126
|
+
const entry = new {{ useImport("EStringToStringMapEntryImpl", "impl", {foundational: true}) }}();
|
|
127
|
+
entry.eBasicSetValue(Ids.EStringToStringMapEntry.key, {{ jsString(entry.key) }});
|
|
128
|
+
entry.eBasicSetValue(Ids.EStringToStringMapEntry.value, {{ jsString(entry.value) }});
|
|
129
|
+
annotation.eBootstrapList(Ids.EAnnotation.details).add(entry);
|
|
130
|
+
}
|
|
131
|
+
{% endfor %}
|
|
132
|
+
{% else %}
|
|
133
|
+
{% for entry in detailsEntries(annotation) %}
|
|
134
|
+
{{ useImport("setDetailValue", "root", {foundational: true}) }}(annotation.getDetails(), {{ jsString(entry.key) }}, {{ jsString(entry.value) }});
|
|
135
|
+
{% endfor %}
|
|
136
|
+
{% endif %}
|
|
137
|
+
{{ bootstrapAdd(targetExpr, "getEAnnotations", "Ids.EModelElement.eAnnotations", "annotation") }}
|
|
138
|
+
}
|
|
139
|
+
{% endfor %}
|
|
140
|
+
{% endmacro %}
|
|
141
|
+
|
|
142
|
+
{{ docComment(package) }}
|
|
143
|
+
export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
144
|
+
{% for classifier in package.getEClassifiers() %}
|
|
145
|
+
{% if isEClass(classifier) %}
|
|
146
|
+
private readonly _{{ classifier.getName() }}: EClassImpl;
|
|
147
|
+
{% for feature in classifier.getEStructuralFeatures() %}
|
|
148
|
+
private readonly _{{ classifier.getName() }}_{{ feature.getName() }}: {{ "EReferenceImpl" if isEReference(feature) else "EAttributeImpl" }};
|
|
149
|
+
{% endfor %}
|
|
150
|
+
{% for op in classifier.getEOperations() %}
|
|
151
|
+
{% set opIndex = loop.index0 %}
|
|
152
|
+
{#- Indexed by position, not name - operations can be overloaded (the same name, several signatures),
|
|
153
|
+
so a name alone would collide; position is always unique. #}
|
|
154
|
+
private readonly _{{ classifier.getName() }}_op{{ opIndex }}: EOperationImpl;
|
|
155
|
+
{% for param in op.getEParameters() %}
|
|
156
|
+
private readonly _{{ classifier.getName() }}_op{{ opIndex }}_p{{ loop.index0 }}: EParameterImpl;
|
|
157
|
+
{% endfor %}
|
|
158
|
+
{% endfor %}
|
|
159
|
+
{% elif isEEnum(classifier) %}
|
|
160
|
+
private readonly _{{ classifier.getName() }}: EEnumImpl;
|
|
161
|
+
{% else %}
|
|
162
|
+
private readonly _{{ classifier.getName() }}: EDataTypeImpl;
|
|
163
|
+
{% endif %}
|
|
164
|
+
{% endfor %}
|
|
165
|
+
|
|
166
|
+
private static _instance: {{ pkgClass }}Impl | undefined;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The one explicit entry point that constructs the singleton - call it
|
|
170
|
+
* yourself, with your own factory, before anything else in your program
|
|
171
|
+
* touches {{ pkgClass }}Impl.eINSTANCE, to use a custom factory (e.g. one
|
|
172
|
+
* overriding createX() to return your own derived classes). Whichever
|
|
173
|
+
* caller gets here FIRST wins; a later call with a *different* factory
|
|
174
|
+
* is logged and otherwise ignored, rather than silently replacing an
|
|
175
|
+
* already-in-use singleton or throwing on a caller who didn't cause the
|
|
176
|
+
* conflict.
|
|
177
|
+
*/
|
|
178
|
+
static init(factory?: {{ factoryClassName(package) }}): {{ pkgClass }}Impl {
|
|
179
|
+
if (!{{ pkgClass }}Impl._instance) {
|
|
180
|
+
{{ pkgClass }}Impl._instance = new {{ pkgClass }}Impl();
|
|
181
|
+
{{ pkgClass }}Impl._instance.setEFactoryInstance(factory ?? new {{ factoryClassName(package) }}Impl());
|
|
182
|
+
} else if (factory) {
|
|
183
|
+
console.warn(
|
|
184
|
+
'{{ pkgClass }}Impl.init() called again with a different factory after the singleton already ' +
|
|
185
|
+
'existed - ignoring the new factory and keeping the existing one.'
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
return {{ pkgClass }}Impl._instance;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
static get eINSTANCE(): {{ pkgClass }}Impl {
|
|
192
|
+
return {{ pkgClass }}Impl.init();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private constructor() {
|
|
196
|
+
super();
|
|
197
|
+
// Set immediately, before Pass 1 constructs any classifier (each of
|
|
198
|
+
// which may need this reference - see {{ pkgClass }}Ref.ts's own doc
|
|
199
|
+
// comment for why this is a late-bound reference rather than a
|
|
200
|
+
// direct import of {{ pkgClass }}Impl.
|
|
201
|
+
set{{ pkgClass }}Ref(this);
|
|
202
|
+
|
|
203
|
+
// Pass 1: create every classifier (empty shell - no features/supertypes
|
|
204
|
+
// yet), so later passes can reference any of them regardless of
|
|
205
|
+
// declaration order.
|
|
206
|
+
{% for classifier in package.getEClassifiers() %}
|
|
207
|
+
{% if isEClass(classifier) %}
|
|
208
|
+
this._{{ classifier.getName() }} = new EClassImpl();
|
|
209
|
+
{{ bootstrapSet("this._" + classifier.getName(), "setName", "Ids.ENamedElement.name", jsString(classifier.getName())) }}
|
|
210
|
+
this._{{ classifier.getName() }}.setClassifierID(Ids.{{ classifier.getName() }}.self);
|
|
211
|
+
{% if classifier.isAbstract() %}
|
|
212
|
+
{{ bootstrapSet("this._" + classifier.getName(), "setAbstract", "Ids.EClass.abstract", "true") }}
|
|
213
|
+
{% endif %}
|
|
214
|
+
{{ emitAnnotations("this._" + classifier.getName(), classifier) }}
|
|
215
|
+
{{ bootstrapAdd("this", "getEClassifiers", "Ids.EPackage.eClassifiers", "this._" + classifier.getName()) }}
|
|
216
|
+
{% elif isEEnum(classifier) %}
|
|
217
|
+
this._{{ classifier.getName() }} = new EEnumImpl();
|
|
218
|
+
{{ bootstrapSet("this._" + classifier.getName(), "setName", "Ids.ENamedElement.name", jsString(classifier.getName())) }}
|
|
219
|
+
this._{{ classifier.getName() }}.setClassifierID(Ids.{{ classifier.getName() }}.self);
|
|
220
|
+
{% for literal in classifier.getELiterals() %}
|
|
221
|
+
{
|
|
222
|
+
const literal = new EEnumLiteralImpl();
|
|
223
|
+
{{ bootstrapSet("literal", "setName", "Ids.ENamedElement.name", jsString(literal.getName())) }}
|
|
224
|
+
{{ bootstrapSet("literal", "setValue", "Ids.EEnumLiteral.value", literal.getValue()) }}
|
|
225
|
+
{{ bootstrapSet("literal", "setLiteral", "Ids.EEnumLiteral.literal", jsString(literal.getLiteral())) }}
|
|
226
|
+
{{ emitAnnotations("literal", literal) }}
|
|
227
|
+
{% if options['generate-ecore'] %}
|
|
228
|
+
// No addLiteral() here - this generated EEnumImpl doesn't have that
|
|
229
|
+
// hand-written convenience method (it's not a reflective feature),
|
|
230
|
+
// so the list-add and the eEnum backpointer (a real, modeled,
|
|
231
|
+
// non-changeable opposite - see NOTES.md) are wired manually.
|
|
232
|
+
this._{{ classifier.getName() }}.eBootstrapList(Ids.EEnum.eLiterals).add(literal);
|
|
233
|
+
literal.eBasicSetValue(Ids.EEnumLiteral.eEnum, this._{{ classifier.getName() }});
|
|
234
|
+
{% else %}
|
|
235
|
+
{#- addLiteral() no longer exists - @typemf/core's own EEnumImpl/EEnumLiteralImpl are
|
|
236
|
+
themselves now generated (self-hosted) classes, properly extending EObjectImpl, so the
|
|
237
|
+
ordinary getELiterals().add() path correctly wires the eEnum opposite automatically now
|
|
238
|
+
(the same standard mechanism every other generated opposite-maintained containment
|
|
239
|
+
reference already relies on) - the old convenience existed specifically to work around
|
|
240
|
+
hand-written EEnumLiteralImpl NOT extending EObjectImpl, which is no longer true. #}
|
|
241
|
+
this._{{ classifier.getName() }}.getELiterals().add(literal);
|
|
242
|
+
{% endif %}
|
|
243
|
+
}
|
|
244
|
+
{% endfor %}
|
|
245
|
+
{{ emitAnnotations("this._" + classifier.getName(), classifier) }}
|
|
246
|
+
{{ bootstrapAdd("this", "getEClassifiers", "Ids.EPackage.eClassifiers", "this._" + classifier.getName()) }}
|
|
247
|
+
{% else %}
|
|
248
|
+
this._{{ classifier.getName() }} = new EDataTypeImpl();
|
|
249
|
+
{{ bootstrapSet("this._" + classifier.getName(), "setName", "Ids.ENamedElement.name", jsString(classifier.getName())) }}
|
|
250
|
+
this._{{ classifier.getName() }}.setClassifierID(Ids.{{ classifier.getName() }}.self);
|
|
251
|
+
{{ emitAnnotations("this._" + classifier.getName(), classifier) }}
|
|
252
|
+
{{ bootstrapAdd("this", "getEClassifiers", "Ids.EPackage.eClassifiers", "this._" + classifier.getName()) }}
|
|
253
|
+
{% endif %}
|
|
254
|
+
{% endfor %}
|
|
255
|
+
|
|
256
|
+
// Pass 2: attributes/references, now that every classifier (including
|
|
257
|
+
// ones referenced as a feature's eType) already exists.
|
|
258
|
+
{% for classifier in eClassesOf(package) %}
|
|
259
|
+
{% for feature in classifier.getEStructuralFeatures() %}
|
|
260
|
+
this._{{ classifier.getName() }}_{{ feature.getName() }} = new {{ "EReferenceImpl" if isEReference(feature) else "EAttributeImpl" }}();
|
|
261
|
+
{% set fieldExpr = "this._" + classifier.getName() + "_" + feature.getName() %}
|
|
262
|
+
{{ bootstrapSet(fieldExpr, "setName", "Ids.ENamedElement.name", jsString(feature.getName())) }}
|
|
263
|
+
{% if feature.getEType() %}
|
|
264
|
+
{{ bootstrapSet(fieldExpr, "setEType", "Ids.ETypedElement.eType", classifierExpr(feature.getEType())) }}
|
|
265
|
+
{% endif %}
|
|
266
|
+
{{ bootstrapSet(fieldExpr, "setLowerBound", "Ids.ETypedElement.lowerBound", feature.getLowerBound()) }}
|
|
267
|
+
{{ bootstrapSet(fieldExpr, "setUpperBound", "Ids.ETypedElement.upperBound", feature.getUpperBound()) }}
|
|
268
|
+
{#- The rest of a feature's own boolean flags - derived/transient/volatile/unsettable/changeable
|
|
269
|
+
(EStructuralFeature) and ordered/unique (ETypedElement) - were a real, confirmed gap: nothing
|
|
270
|
+
here ever emitted bootstrap code for them at all, so every self-hosted feature silently fell
|
|
271
|
+
back to the runtime's own hardcoded field defaults regardless of what the model itself
|
|
272
|
+
declared. Confirmed directly against EClass.eAllGenericSuperTypes, which the real model marks
|
|
273
|
+
changeable="false" volatile="true" transient="true" derived="true" but which the generated
|
|
274
|
+
bootstrap (before this fix) never set any of - a real save/serialize failure downstream in
|
|
275
|
+
@typemf/xmi (a derived, never-persisted feature being written out as if it were real, stored
|
|
276
|
+
data) traced all the way back to this single missing set of bootstrapSet calls. Unconditional,
|
|
277
|
+
matching lowerBound/upperBound's own established pattern just above, rather than conditional
|
|
278
|
+
on differing from some assumed runtime default - simplest and least likely to silently drift
|
|
279
|
+
out of sync with whatever the runtime's own defaults happen to be. #}
|
|
280
|
+
{{ bootstrapSet(fieldExpr, "setDerived", "Ids.EStructuralFeature.derived", feature.isDerived()) }}
|
|
281
|
+
{{ bootstrapSet(fieldExpr, "setTransient", "Ids.EStructuralFeature.transient", feature.isTransient()) }}
|
|
282
|
+
{{ bootstrapSet(fieldExpr, "setVolatile", "Ids.EStructuralFeature.volatile", feature.isVolatile()) }}
|
|
283
|
+
{{ bootstrapSet(fieldExpr, "setUnsettable", "Ids.EStructuralFeature.unsettable", feature.isUnsettable()) }}
|
|
284
|
+
{{ bootstrapSet(fieldExpr, "setChangeable", "Ids.EStructuralFeature.changeable", feature.isChangeable()) }}
|
|
285
|
+
{{ bootstrapSet(fieldExpr, "setOrdered", "Ids.ETypedElement.ordered", feature.isOrdered()) }}
|
|
286
|
+
{{ bootstrapSet(fieldExpr, "setUnique", "Ids.ETypedElement.unique", feature.isUnique()) }}
|
|
287
|
+
{{ fieldExpr }}.setFeatureID(Ids.{{ classifier.getName() }}.{{ feature.getName() }});
|
|
288
|
+
{#- containerClass: the concrete implementation class the feature is declared on - the closest
|
|
289
|
+
TypeScript has to Java's own containerClass semantics (Class<?>, e.g. EClassifier.class in the
|
|
290
|
+
real EMF bootstrap this mirrors). Real EMF uses the DECLARED INTERFACE's class object; TypeScript
|
|
291
|
+
interfaces don't exist as runtime values at all, so the concrete Impl class - the only thing
|
|
292
|
+
actually inspectable at runtime here - is the deliberate, disclosed adaptation. #}
|
|
293
|
+
{{ fieldExpr }}.setContainerClass({{ useImport(classifier.getName() + "Impl", "impl", {foundational: false}) }});
|
|
294
|
+
{{ bootstrapSet(fieldExpr, "setEContainingClass", "Ids.EStructuralFeature.eContainingClass", "this._" + classifier.getName()) }}
|
|
295
|
+
{% if isEReference(feature) %}
|
|
296
|
+
{{ bootstrapSet(fieldExpr, "setContainment", "Ids.EReference.containment", 'true' if feature.isContainment() else 'false') }}
|
|
297
|
+
{% else %}
|
|
298
|
+
{% if feature.isID() %}
|
|
299
|
+
{{ bootstrapSet(fieldExpr, "setID", "Ids.EAttribute.iD", "true") }}
|
|
300
|
+
{% endif %}
|
|
301
|
+
{% endif %}
|
|
302
|
+
{{ emitAnnotations(fieldExpr, feature) }}
|
|
303
|
+
{{ bootstrapAdd("this._" + classifier.getName(), "getEStructuralFeatures", "Ids.EClass.eStructuralFeatures", fieldExpr) }}
|
|
304
|
+
{% endfor %}
|
|
305
|
+
{% endfor %}
|
|
306
|
+
|
|
307
|
+
// Pass 2.5: operations and their parameters - real EOperation/EParameter objects, not just the
|
|
308
|
+
// generated METHODS (which already existed) - eOperations used to be permanently empty at runtime;
|
|
309
|
+
// this is what actually populates it, the same way pass 2 populates eStructuralFeatures. Kept as
|
|
310
|
+
// its own pass, after every classifier and feature exists (an operation's return type or a
|
|
311
|
+
// parameter's type can reference either), rather than folded into the loop above.
|
|
312
|
+
{% for classifier in eClassesOf(package) %}
|
|
313
|
+
{% for op in classifier.getEOperations() %}
|
|
314
|
+
{% set opIndex = loop.index0 %}
|
|
315
|
+
{% set opFieldExpr = "this._" + classifier.getName() + "_op" + opIndex %}
|
|
316
|
+
{{ opFieldExpr }} = new EOperationImpl();
|
|
317
|
+
{{ bootstrapSet(opFieldExpr, "setName", "Ids.ENamedElement.name", jsString(op.getName())) }}
|
|
318
|
+
{% if op.getEType() %}
|
|
319
|
+
{{ bootstrapSet(opFieldExpr, "setEType", "Ids.ETypedElement.eType", classifierExpr(op.getEType())) }}
|
|
320
|
+
{% endif %}
|
|
321
|
+
{{ bootstrapSet(opFieldExpr, "setEContainingClass", "Ids.EOperation.eContainingClass", "this._" + classifier.getName()) }}
|
|
322
|
+
{{ opFieldExpr }}.setOperationID(Ids.{{ classifier.getName() }}.op{{ opIndex }});
|
|
323
|
+
{% for param in op.getEParameters() %}
|
|
324
|
+
{% set paramFieldExpr = opFieldExpr + "_p" + loop.index0 %}
|
|
325
|
+
{{ paramFieldExpr }} = new EParameterImpl();
|
|
326
|
+
{{ bootstrapSet(paramFieldExpr, "setName", "Ids.ENamedElement.name", jsString(param.getName())) }}
|
|
327
|
+
{% if param.getEType() %}
|
|
328
|
+
{{ bootstrapSet(paramFieldExpr, "setEType", "Ids.ETypedElement.eType", classifierExpr(param.getEType())) }}
|
|
329
|
+
{% endif %}
|
|
330
|
+
{{ bootstrapSet(paramFieldExpr, "setLowerBound", "Ids.ETypedElement.lowerBound", param.getLowerBound()) }}
|
|
331
|
+
{{ bootstrapSet(paramFieldExpr, "setUpperBound", "Ids.ETypedElement.upperBound", param.getUpperBound()) }}
|
|
332
|
+
{{ bootstrapSet(paramFieldExpr, "setEOperation", "Ids.EParameter.eOperation", opFieldExpr) }}
|
|
333
|
+
{{ bootstrapAdd(opFieldExpr, "getEParameters", "Ids.EOperation.eParameters", paramFieldExpr) }}
|
|
334
|
+
{% endfor %}
|
|
335
|
+
{% for exception in op.getEExceptions() %}
|
|
336
|
+
{{ bootstrapAdd(opFieldExpr, "getEExceptions", "Ids.EOperation.eExceptions", classifierExpr(exception)) }}
|
|
337
|
+
{% endfor %}
|
|
338
|
+
{{ emitAnnotations(opFieldExpr, op) }}
|
|
339
|
+
{{ bootstrapAdd("this._" + classifier.getName(), "getEOperations", "Ids.EClass.eOperations", opFieldExpr) }}
|
|
340
|
+
{% endfor %}
|
|
341
|
+
{% endfor %}
|
|
342
|
+
|
|
343
|
+
// Pass 3: every classifier's owning package (self-referential, so must
|
|
344
|
+
// come after pass 1 creates them all).
|
|
345
|
+
{% for classifier in package.getEClassifiers() %}
|
|
346
|
+
{{ bootstrapSet("this._" + classifier.getName(), "setEPackage", "Ids.EClassifier.ePackage", "this") }}
|
|
347
|
+
{% endfor %}
|
|
348
|
+
|
|
349
|
+
// Pass 4: supertypes.
|
|
350
|
+
{% for classifier in eClassesOf(package) %}
|
|
351
|
+
{% set parent = superType(classifier) %}
|
|
352
|
+
{% if parent %}
|
|
353
|
+
{{ bootstrapAdd("this._" + classifier.getName(), "getESuperTypes", "Ids.EClass.eSuperTypes", classifierExpr(parent)) }}
|
|
354
|
+
{% endif %}
|
|
355
|
+
{% endfor %}
|
|
356
|
+
|
|
357
|
+
// Pass 4b: opposites. Set after all bootstrap adds, so those adds don't trigger inverse updates.
|
|
358
|
+
// Opposites in another package are not emitted.
|
|
359
|
+
{% for classifier in eClassesOf(package) %}
|
|
360
|
+
{% for feature in classifier.getEStructuralFeatures() %}
|
|
361
|
+
{% set opposite = feature.getEOpposite() if isEReference(feature) else undefined %}
|
|
362
|
+
{% if opposite and opposite.getEContainingClass().getEPackage() == package %}
|
|
363
|
+
{{ bootstrapSet("this._" + classifier.getName() + "_" + feature.getName(), "setEOpposite", "Ids.EReference.eOpposite", "this._" + opposite.getEContainingClass().getName() + "_" + opposite.getName()) }}
|
|
364
|
+
{% endif %}
|
|
365
|
+
{% endfor %}
|
|
366
|
+
{% endfor %}
|
|
367
|
+
|
|
368
|
+
// Pass 5 (recompute EAll* caches, ordinary mode only) removed - stale post-swap.
|
|
369
|
+
// @typemf/core's own EClassImpl is now itself generated code (see NOTES.md's point 6/7
|
|
370
|
+
// write-ups), using the same automatic, get-bodied caching (EObjectImpl.getModelGeneration(),
|
|
371
|
+
// point 1) as self-hosted output always did - there is no recomputeAllLists() method to call
|
|
372
|
+
// anymore, in either mode, and none is needed.
|
|
373
|
+
|
|
374
|
+
// Pass 6: set the package's own name/nsURI/nsPrefix/annotations last,
|
|
375
|
+
// deliberately - these go through the ordinary reflective setters
|
|
376
|
+
// (this.setName(), etc.), which need the "name"/etc. feature's own
|
|
377
|
+
// metaclass object to already exist (it's built above, in the passes
|
|
378
|
+
// this follows). Calling them earlier - before those metaclasses
|
|
379
|
+
// exist - is a genuine bootstrap-ordering bug, confirmed directly by
|
|
380
|
+
// reproducing it: this line order isn't a style choice, the metaclass
|
|
381
|
+
// shells and feature wiring above are a real, load-bearing
|
|
382
|
+
// precondition for the reflective setter calls below to work at all.
|
|
383
|
+
this.setName({{ jsString(package.getName()) }});
|
|
384
|
+
this.setNsURI({{ jsString(package.getNsURI()) }});
|
|
385
|
+
this.setNsPrefix({{ jsString(package.getNsPrefix()) }});
|
|
386
|
+
{{ emitAnnotations("this", package) }}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
{% for classifier in package.getEClassifiers() %}
|
|
390
|
+
{% set collision = metaclassAccessorCollision(classifier.getName(), package) %}
|
|
391
|
+
{% if isEClass(classifier) %}
|
|
392
|
+
{% if collision %}
|
|
393
|
+
get{{ classifier.getName() }}(): {{ useImport("EClass", "types", {foundational: true}) }};
|
|
394
|
+
get{{ classifier.getName() }}({{ paramList(collision) }}): {{ tsOperationReturnType(collision) }};
|
|
395
|
+
get{{ classifier.getName() }}({{ optionalParamList(collision) }}): EClass | {{ tsOperationReturnType(collision) }} {
|
|
396
|
+
if ({{ collision.getEParameters().get(0).getName() }} === undefined) return this._{{ classifier.getName() }};
|
|
397
|
+
return super.get{{ classifier.getName() }}({{ argList(collision) }});
|
|
398
|
+
}
|
|
399
|
+
{% else %}
|
|
400
|
+
get{{ classifier.getName() }}(): {{ useImport("EClass", "types", {foundational: true}) }} {
|
|
401
|
+
return this._{{ classifier.getName() }};
|
|
402
|
+
}
|
|
403
|
+
{% endif %}
|
|
404
|
+
{% for feature in classifier.getEStructuralFeatures() %}
|
|
405
|
+
get{{ classifier.getName() }}_{{ ucfirst(feature.getName()) }}(): {{ useImport("EReference", "types", {foundational: true}) if isEReference(feature) else useImport("EAttribute", "types", {foundational: true}) }} {
|
|
406
|
+
return this._{{ classifier.getName() }}_{{ feature.getName() }};
|
|
407
|
+
}
|
|
408
|
+
{% endfor %}
|
|
409
|
+
{% elif isEEnum(classifier) %}
|
|
410
|
+
{% if collision %}
|
|
411
|
+
get{{ classifier.getName() }}(): {{ useImport("EEnum", "types", {foundational: true}) }};
|
|
412
|
+
get{{ classifier.getName() }}({{ paramList(collision) }}): {{ tsOperationReturnType(collision) }};
|
|
413
|
+
get{{ classifier.getName() }}({{ optionalParamList(collision) }}): EEnum | {{ tsOperationReturnType(collision) }} {
|
|
414
|
+
if ({{ collision.getEParameters().get(0).getName() }} === undefined) return this._{{ classifier.getName() }};
|
|
415
|
+
return super.get{{ classifier.getName() }}({{ argList(collision) }});
|
|
416
|
+
}
|
|
417
|
+
{% else %}
|
|
418
|
+
get{{ classifier.getName() }}(): {{ useImport("EEnum", "types", {foundational: true}) }} {
|
|
419
|
+
return this._{{ classifier.getName() }};
|
|
420
|
+
}
|
|
421
|
+
{% endif %}
|
|
422
|
+
{% else %}
|
|
423
|
+
{% if collision %}
|
|
424
|
+
get{{ classifier.getName() }}(): {{ useImport("EDataType", "types", {foundational: true}) }};
|
|
425
|
+
get{{ classifier.getName() }}({{ paramList(collision) }}): {{ tsOperationReturnType(collision) }};
|
|
426
|
+
get{{ classifier.getName() }}({{ optionalParamList(collision) }}): EDataType | {{ tsOperationReturnType(collision) }} {
|
|
427
|
+
if ({{ collision.getEParameters().get(0).getName() }} === undefined) return this._{{ classifier.getName() }};
|
|
428
|
+
return super.get{{ classifier.getName() }}({{ argList(collision) }});
|
|
429
|
+
}
|
|
430
|
+
{% else %}
|
|
431
|
+
get{{ classifier.getName() }}(): {{ useImport("EDataType", "types", {foundational: true}) }} {
|
|
432
|
+
return this._{{ classifier.getName() }};
|
|
433
|
+
}
|
|
434
|
+
{% endif %}
|
|
435
|
+
{% endif %}
|
|
436
|
+
{% endfor %}
|
|
437
|
+
}
|
|
438
|
+
{% endfile %}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{% file "impl/" + packageClassName(package) + "Ref.ts" %}
|
|
2
|
+
import type { {{ packageClassName(package) }} } from '../{{ packageClassName(package) }}.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A late-bound reference to the {{ packageClassName(package) }} singleton,
|
|
6
|
+
* set once by {{ packageClassName(package) }}Impl's own constructor after
|
|
7
|
+
* it finishes constructing itself.
|
|
8
|
+
*
|
|
9
|
+
* Every generated class needs a reference to the package singleton (for
|
|
10
|
+
* eClass(), for feature-ID lookups in field initializers/lazy getters,
|
|
11
|
+
* etc.) - and {{ packageClassName(package) }}Impl itself constructs
|
|
12
|
+
* (nearly) every one of those classes to build the runtime metamodel. For
|
|
13
|
+
* an ordinary generated package that mutual reference is harmless: the
|
|
14
|
+
* package singleton already exists in full by the time anything else is
|
|
15
|
+
* ever constructed. But self-hosting a metamodel that describes itself
|
|
16
|
+
* (as with real Ecore.ecore) makes this a genuine, confirmed hazard: a
|
|
17
|
+
* real class along the package's own superclass chain, or a class that is
|
|
18
|
+
* ALSO the superclass of something the package constructs directly (the
|
|
19
|
+
* "EFactory unification" / "EPackage unification" cases), can end up
|
|
20
|
+
* needing this reference while {{ packageClassName(package) }}Impl is
|
|
21
|
+
* still in the middle of constructing itself - a genuine circular
|
|
22
|
+
* dependency, not merely an ordering preference, confirmed directly
|
|
23
|
+
* against a real failing bundle ("Class extends value undefined is not a
|
|
24
|
+
* constructor or null") while building this generator, not assumed.
|
|
25
|
+
*
|
|
26
|
+
* This module has only a type-only import (erased at compile time, zero
|
|
27
|
+
* runtime footprint) - so importing FROM this module can never itself
|
|
28
|
+
* participate in a circular dependency, unlike importing
|
|
29
|
+
* {{ packageClassName(package) }}Impl (or anything re-exporting it)
|
|
30
|
+
* directly.
|
|
31
|
+
*/
|
|
32
|
+
let ref: {{ packageClassName(package) }} | undefined;
|
|
33
|
+
|
|
34
|
+
export function set{{ packageClassName(package) }}Ref(pkg: {{ packageClassName(package) }}): void {
|
|
35
|
+
ref = pkg;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function get{{ packageClassName(package) }}Ref(): {{ packageClassName(package) }} {
|
|
39
|
+
if (!ref) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
'get{{ packageClassName(package) }}Ref() called before the {{ packageClassName(package) }} singleton finished constructing itself.'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return ref;
|
|
45
|
+
}
|
|
46
|
+
{% endfile %}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{% file "util/" + switchClassName(package) + ".ts" %}
|
|
2
|
+
export abstract class {{ switchClassName(package) }}<T> {
|
|
3
|
+
doSwitch(eObject: {{ useImport("EObject", "types", {foundational: true}) }}): T | undefined {
|
|
4
|
+
switch (eObject.eClass().getClassifierID()) {
|
|
5
|
+
{% for classifier in concreteEClassesOf(package) %}
|
|
6
|
+
case {{ useImport("Ids", "root", {file: packageClassName(package), foundational: false}) }}.{{ classifier.getName() }}.self: {
|
|
7
|
+
// {{ classifier.getName() }}
|
|
8
|
+
{% for c in superTypeChain(classifier) %}
|
|
9
|
+
const result{{ loop.index }} = this.case{{ c.getName() }}(eObject as {{ useImport(c.getName(), "types", {foundational: false}) }});
|
|
10
|
+
if (result{{ loop.index }} !== undefined) return result{{ loop.index }};
|
|
11
|
+
{% endfor %}
|
|
12
|
+
return this.defaultCase(eObject);
|
|
13
|
+
}
|
|
14
|
+
{% endfor %}
|
|
15
|
+
default:
|
|
16
|
+
return this.defaultCase(eObject);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
{% for classifier in eClassesOf(package) %}
|
|
21
|
+
case{{ classifier.getName() }}(object: {{ useImport(classifier.getName(), "types", {foundational: false}) }}): T | undefined {
|
|
22
|
+
void object;
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
{% endfor %}
|
|
26
|
+
|
|
27
|
+
defaultCase(object: {{ useImport("EObject", "types", {foundational: true}) }}): T | undefined {
|
|
28
|
+
void object;
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
{% endfile %}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{% file "util/" + typeGuardsClassName(package) + ".ts" %}
|
|
2
|
+
/**
|
|
3
|
+
* Runtime type guards, one per classifier declared in types/ - the
|
|
4
|
+
* reflective counterpart to a hand-written structural guard like
|
|
5
|
+
*
|
|
6
|
+
* function isReference(feature: EStructuralFeature): feature is EReference {
|
|
7
|
+
* return "isContainment" in feature;
|
|
8
|
+
* }
|
|
9
|
+
*
|
|
10
|
+
* Structural (duck-typing) checks like that one work when a single
|
|
11
|
+
* classifier has some genuinely distinguishing own member - but nothing
|
|
12
|
+
* guarantees an arbitrary generated classifier has one (a subclass might
|
|
13
|
+
* add no own features at all, inheriting everything). These guards check
|
|
14
|
+
* the object's REAL runtime EClass against the metamodel's own
|
|
15
|
+
* eAllSuperTypes reflectively instead (via isSuperTypeOf()), which is
|
|
16
|
+
* correct for every classifier unconditionally, including abstract ones
|
|
17
|
+
* (correctly true for any instance of a concrete subclass) and dynamic
|
|
18
|
+
* instances (no dependency on the object's actual JS class/prototype at
|
|
19
|
+
* all, unlike `instanceof`).
|
|
20
|
+
*/
|
|
21
|
+
function hasEClass(obj: unknown): obj is {{ useImport("EObject", "types", {foundational: true}) }} {
|
|
22
|
+
return obj !== null && typeof obj === 'object' && typeof (obj as { eClass?: unknown }).eClass === 'function';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
{% for classifier in eClassesOf(package) %}
|
|
26
|
+
export function is{{ classifier.getName() }}(obj: unknown): obj is {{ useImport(classifier.getName(), "types", {foundational: false}) }} {
|
|
27
|
+
return hasEClass(obj) && {{ useImport("get" + packageClassName(package) + "Ref", "impl", {file: packageClassName(package) + "Ref", foundational: false}) }}().get{{ classifier.getName() }}().isSuperTypeOf(obj.eClass());
|
|
28
|
+
}
|
|
29
|
+
{% endfor %}
|
|
30
|
+
{% endfile %}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{% for classifier in package.getEClassifiers() %}
|
|
2
|
+
{% if classifier.getName() == "EObject" %}
|
|
3
|
+
{# Deliberately no files generated - see typescript-filters.ts's tsScalarType()
|
|
4
|
+
doc comment and eclass.njk's existing no-declared-supertype fallback,
|
|
5
|
+
which already resolve any reference to "EObject" to the real
|
|
6
|
+
@typemf/core EObject rather than a generated, shadowing one. #}
|
|
7
|
+
{% elif isEClass(classifier) %}
|
|
8
|
+
{% set eClass = classifier %}
|
|
9
|
+
{% include "eclass.njk" %}
|
|
10
|
+
{% elif isEEnum(classifier) %}
|
|
11
|
+
{% set eEnum = classifier %}
|
|
12
|
+
{% include "eenum.njk" %}
|
|
13
|
+
{% endif %}
|
|
14
|
+
{% endfor %}
|
|
15
|
+
{% include "epackage.njk" %}
|
|
16
|
+
{% include "epackageref.njk" %}
|
|
17
|
+
{% include "efactory.njk" %}
|
|
18
|
+
{% include "eswitch.njk" %}
|
|
19
|
+
{% include "etypeguards.njk" %}
|