@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.
@@ -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" %}