@typemf/xmi 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/dist/index.js ADDED
@@ -0,0 +1,667 @@
1
+ // src/emf-fragment.ts
2
+ function allStructuralFeaturesOf(eClass) {
3
+ const seen = /* @__PURE__ */ new Set();
4
+ const result = [];
5
+ function visit(c) {
6
+ if (seen.has(c)) return;
7
+ seen.add(c);
8
+ for (const parent of c.getESuperTypes()) visit(parent);
9
+ result.push(...c.getEStructuralFeatures());
10
+ }
11
+ visit(eClass);
12
+ return result;
13
+ }
14
+ function allAttributesOf(eClass) {
15
+ return allStructuralFeaturesOf(eClass).filter((f) => !("isContainment" in f));
16
+ }
17
+ function computeEmfFragment(target, roots) {
18
+ const idAttribute = allAttributesOf(target.eClass()).find((attr) => attr.isID());
19
+ if (idAttribute && target.eIsSet(idAttribute)) {
20
+ return String(target.eGet(idAttribute));
21
+ }
22
+ const segments = [];
23
+ let current = target;
24
+ let container = current.eContainer();
25
+ while (container) {
26
+ const feature = current.eContainingFeature();
27
+ if (!feature) {
28
+ throw new Error("Object has a container but no containing feature - inconsistent containment state.");
29
+ }
30
+ let segment = `@${feature.getName()}`;
31
+ if (feature.isMany()) {
32
+ const siblings = [...container.eGet(feature)];
33
+ const index = siblings.indexOf(current);
34
+ if (index === -1) {
35
+ throw new Error(`Object not found within its own containing feature '${feature.getName()}'.`);
36
+ }
37
+ segment += `.${index}`;
38
+ }
39
+ segments.unshift(segment);
40
+ current = container;
41
+ container = current.eContainer();
42
+ }
43
+ const rootIndex = roots.indexOf(current);
44
+ if (rootIndex === -1) {
45
+ throw new Error("Target object is not reachable from the given roots via containment.");
46
+ }
47
+ const path = segments.join("/");
48
+ if (roots.length === 1) {
49
+ return path === "" ? "/" : `//${path}`;
50
+ }
51
+ return path === "" ? `/${rootIndex}` : `/${rootIndex}/${path}`;
52
+ }
53
+ function resolveEmfFragment(fragment, roots) {
54
+ if (fragment === "/") {
55
+ return roots.length === 1 ? roots[0] : void 0;
56
+ }
57
+ if (fragment.startsWith("//")) {
58
+ const rest = fragment.slice(2);
59
+ if (roots.length !== 1) return void 0;
60
+ if (rest.startsWith("@")) {
61
+ return walkSegments(rest, roots[0]);
62
+ }
63
+ return walkNamedSegments(rest, roots[0]);
64
+ }
65
+ if (fragment.startsWith("/")) {
66
+ const rest = fragment.slice(1);
67
+ const slashIndex = rest.indexOf("/");
68
+ const rootIndexToken = slashIndex === -1 ? rest : rest.slice(0, slashIndex);
69
+ const rootIndex = Number(rootIndexToken);
70
+ if (!Number.isInteger(rootIndex) || rootIndex < 0 || rootIndex >= roots.length) return void 0;
71
+ const root = roots[rootIndex];
72
+ if (slashIndex === -1) return root;
73
+ return walkSegments(rest.slice(slashIndex + 1), root);
74
+ }
75
+ return findByIdValue(fragment, roots);
76
+ }
77
+ function walkNamedSegments(path, start) {
78
+ const segments = path.split("/");
79
+ let current = start;
80
+ for (const name of segments) {
81
+ if (!current) return void 0;
82
+ const currentClass = current.eClass();
83
+ const eClassifiersFeature = allStructuralFeaturesOf(currentClass).filter((feature) => feature.getName() === "eClassifiers").at(0);
84
+ if (eClassifiersFeature) {
85
+ current = findNamedChild(current, eClassifiersFeature, name);
86
+ continue;
87
+ }
88
+ const eStructuralFeaturesFeature = allStructuralFeaturesOf(currentClass).filter((feature) => feature.getName() === "eStructuralFeatures").at(0);
89
+ if (eStructuralFeaturesFeature) {
90
+ current = findNamedChild(current, eStructuralFeaturesFeature, name);
91
+ continue;
92
+ }
93
+ return void 0;
94
+ }
95
+ return current;
96
+ }
97
+ function findNamedChild(container, listFeature, name) {
98
+ const children = [...container.eGet(listFeature)];
99
+ return children.find((child) => {
100
+ const nameFeature = allStructuralFeaturesOf(child.eClass()).filter((feature) => feature.getName() === "name").at(0);
101
+ return nameFeature !== void 0 && child.eGet(nameFeature) === name;
102
+ });
103
+ }
104
+ function walkSegments(path, start) {
105
+ let current = start;
106
+ for (const segment of path.split("/")) {
107
+ if (!segment.startsWith("@")) return void 0;
108
+ const dotIndex = segment.indexOf(".");
109
+ const featureName = dotIndex === -1 ? segment.slice(1) : segment.slice(1, dotIndex);
110
+ const feature = allStructuralFeaturesOf(current.eClass()).filter((feature2) => feature2.getName() === featureName).at(0);
111
+ if (!feature) return void 0;
112
+ if (feature.isMany()) {
113
+ const indexToken = dotIndex === -1 ? void 0 : segment.slice(dotIndex + 1);
114
+ const index = Number(indexToken);
115
+ if (indexToken === void 0 || !Number.isInteger(index)) return void 0;
116
+ const list = [...current.eGet(feature)];
117
+ const next = list[index];
118
+ if (!next) return void 0;
119
+ current = next;
120
+ } else {
121
+ current = current.eGet(feature);
122
+ }
123
+ }
124
+ return current;
125
+ }
126
+ function findByIdValue(idValue, roots) {
127
+ for (const root of roots) {
128
+ const found = findByIdValueIn(root, idValue);
129
+ if (found) return found;
130
+ }
131
+ return void 0;
132
+ }
133
+ function findByIdValueIn(obj, idValue) {
134
+ const idAttribute = allAttributesOf(obj.eClass()).find((attr) => attr.isID());
135
+ if (idAttribute && obj.eIsSet(idAttribute) && String(obj.eGet(idAttribute)) === idValue) {
136
+ return obj;
137
+ }
138
+ for (const child of obj.eContents()) {
139
+ const found = findByIdValueIn(child, idValue);
140
+ if (found) return found;
141
+ }
142
+ return void 0;
143
+ }
144
+
145
+ // src/namespace-collector.ts
146
+ var NamespaceCollector = class {
147
+ prefixToUri = /* @__PURE__ */ new Map();
148
+ uriToPrefix = /* @__PURE__ */ new Map();
149
+ prefixFor(pkg) {
150
+ const nsURI = pkg.getNsURI();
151
+ if (!nsURI) {
152
+ throw new Error(`EPackage '${pkg.getName()}' has no nsURI set - cannot serialize instances of it.`);
153
+ }
154
+ const existing = this.uriToPrefix.get(nsURI);
155
+ if (existing) return existing;
156
+ const base = pkg.getNsPrefix() || pkg.getName() || "ns";
157
+ let candidate = base;
158
+ let suffix = 0;
159
+ while (this.prefixToUri.has(candidate) && this.prefixToUri.get(candidate) !== nsURI) {
160
+ suffix += 1;
161
+ candidate = `${base}${suffix}`;
162
+ }
163
+ this.prefixToUri.set(candidate, nsURI);
164
+ this.uriToPrefix.set(nsURI, candidate);
165
+ return candidate;
166
+ }
167
+ entries() {
168
+ return [...this.prefixToUri.entries()];
169
+ }
170
+ };
171
+
172
+ // src/xmi-resource-factory.ts
173
+ import { ResourceImpl } from "@typemf/core";
174
+
175
+ // src/xmi-serializer.ts
176
+ import {
177
+ EcorePackageImpl,
178
+ isEClassifier,
179
+ ProxyEObjectImpl,
180
+ URI,
181
+ computeFragment as coreComputeFragment,
182
+ getResourceOf
183
+ } from "@typemf/core";
184
+
185
+ // src/xml-text.ts
186
+ function escapeAttributeValue(value) {
187
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/\r/g, "&#13;").replace(/\n/g, "&#10;").replace(/\t/g, "&#9;");
188
+ }
189
+ function escapeText(value) {
190
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
191
+ }
192
+
193
+ // src/xml-dom.ts
194
+ async function parseXmlDocument(text) {
195
+ const globalDOMParser = globalThis.DOMParser;
196
+ if (globalDOMParser) {
197
+ const doc2 = new globalDOMParser().parseFromString(text, "application/xml");
198
+ const parseError = doc2.getElementsByTagName("parsererror")[0];
199
+ if (parseError) {
200
+ throw new Error(`Failed to parse XML: ${parseError.textContent ?? "unknown parse error"}`);
201
+ }
202
+ return doc2;
203
+ }
204
+ const xmldom = await import("@xmldom/xmldom");
205
+ const errors = [];
206
+ const parser = new xmldom.DOMParser({
207
+ onError: (level, message) => {
208
+ if (level === "error" || level === "fatalError") errors.push(message);
209
+ }
210
+ });
211
+ const doc = parser.parseFromString(text, "application/xml");
212
+ if (errors.length > 0) {
213
+ throw new Error(`Failed to parse XML: ${errors.join("; ")}`);
214
+ }
215
+ return doc;
216
+ }
217
+
218
+ // src/xmi-serializer.ts
219
+ var XMI_NS = "http://www.omg.org/XMI";
220
+ var XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
221
+ var XmiSerializer = class {
222
+ computeFragment(target, roots) {
223
+ return computeEmfFragment(target, roots);
224
+ }
225
+ resolveFragment(fragment, roots) {
226
+ return resolveEmfFragment(fragment, roots);
227
+ }
228
+ async serialize(roots, resource) {
229
+ if (roots.length !== 1) {
230
+ throw new Error(
231
+ `XmiSerializer currently only supports exactly one root object (got ${roots.length}) - multi-root documents need the <xmi:XMI> wrapper form, not yet implemented. See NOTES.md.`
232
+ );
233
+ }
234
+ const root = roots[0];
235
+ const namespaces = new NamespaceCollector();
236
+ const rootPrefix = namespaces.prefixFor(requirePackage(root.eClass()));
237
+ const parts = writeObjectParts(root, `${rootPrefix}:${root.eClass().getName()}`, roots, resource, namespaces);
238
+ const nsAttrs = [
239
+ ["xmlns:xmi", XMI_NS],
240
+ ["xmlns:xsi", XSI_NS],
241
+ ...namespaces.entries().map(([prefix, uri]) => [`xmlns:${prefix}`, uri])
242
+ ];
243
+ const schemaLocationAttrs = [];
244
+ const rootPackage = requirePackage(root.eClass());
245
+ const rootPackageResource = getResourceOf(rootPackage);
246
+ if (rootPackageResource && rootPackageResource !== resource) {
247
+ const location = relativeOrAbsolutePath(resource.getURI(), rootPackageResource.getURI());
248
+ schemaLocationAttrs.push(["xsi:schemaLocation", `${rootPackage.getNsURI()} ${location}`]);
249
+ }
250
+ const allAttrs = [
251
+ ["xmi:version", "2.0"],
252
+ ...nsAttrs,
253
+ ...schemaLocationAttrs,
254
+ ...parts.attributes
255
+ ];
256
+ const attrStr = allAttrs.map(([k, v]) => ` ${k}="${escapeAttributeValue(v)}"`).join("");
257
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
258
+ ` + (parts.childrenXml ? `<${parts.tagName}${attrStr}>${parts.childrenXml}</${parts.tagName}>` : `<${parts.tagName}${attrStr}/>`);
259
+ return new TextEncoder().encode(xml);
260
+ }
261
+ /**
262
+ * Root-element only (not a full-tree walk), all declared xmlns:* values (not filtered to ones
263
+ * some xsi:type actually uses) - the simpler of the two real tradeoffs this method has for XMI,
264
+ * chosen deliberately: this project's own writer always declares every namespace a document
265
+ * needs on the root element (see NamespaceCollector/the ['xmlns:xmi', ...] block above), so this
266
+ * is exactly correct for anything this project produces. A third-party or hand-edited XMI file
267
+ * that declares a namespace deeper in the tree (legal XML, just non-standard practice for this
268
+ * kind of document) would have that namespace missed here - an accepted, documented limitation,
269
+ * not an oversight.
270
+ */
271
+ async peekReferencedNsURIs(content) {
272
+ const text = new TextDecoder().decode(content);
273
+ const doc = await parseXmlDocument(text);
274
+ const root = doc.documentElement;
275
+ if (!root) return [];
276
+ const nsURIs = [];
277
+ for (const attr of Array.from(root.attributes)) {
278
+ if (!attr.name.startsWith("xmlns:")) continue;
279
+ if (attr.value === XMI_NS || attr.value === XSI_NS) continue;
280
+ nsURIs.push(attr.value);
281
+ }
282
+ return nsURIs;
283
+ }
284
+ async deserialize(content, resource) {
285
+ const text = new TextDecoder().decode(content);
286
+ const doc = await parseXmlDocument(text);
287
+ const root = doc.documentElement;
288
+ if (!root) {
289
+ throw new Error(`'${resource.getURI().toString()}' has no document element - not a valid XML document.`);
290
+ }
291
+ const packageRegistry = resource.getResourceSet()?.getPackageRegistry();
292
+ if (!packageRegistry) {
293
+ throw new Error(
294
+ `Cannot deserialize '${resource.getURI().toString()}': its Resource has no ResourceSet, which is needed to look up EPackages by namespace URI.`
295
+ );
296
+ }
297
+ const ctx = { packageRegistry };
298
+ const elementToObj = /* @__PURE__ */ new Map();
299
+ const rootEClass = resolveElementEClass(root, packageRegistry);
300
+ const rootObj = constructShell(root, rootEClass, ctx, elementToObj);
301
+ const roots = [rootObj];
302
+ completeObject(root, rootObj, ctx, elementToObj, roots);
303
+ return roots;
304
+ }
305
+ };
306
+ function writeObjectParts(obj, tagName, roots, sourceResource, namespaces) {
307
+ const attributes = [];
308
+ let childrenXml = "";
309
+ for (const feature of obj.eClass().getEAllStructuralFeatures()) {
310
+ if (feature.isDerived() || feature.isTransient()) continue;
311
+ if (!obj.eIsSet(feature)) continue;
312
+ if (!isReferenceFeature(feature)) {
313
+ if (feature.isMany()) {
314
+ for (const value of obj.eGet(feature)) {
315
+ childrenXml += `<${feature.getName()}>${escapeText(encodeAttributeValue(value, feature))}</${feature.getName()}>`;
316
+ }
317
+ } else {
318
+ attributes.push([feature.getName(), encodeAttributeValue(obj.eGet(feature), feature)]);
319
+ }
320
+ continue;
321
+ }
322
+ const refFeature = feature;
323
+ const values = refFeature.isMany() ? [...obj.eGet(feature)] : [obj.eGet(feature)];
324
+ if (refFeature.isContainment()) {
325
+ for (const child of values) {
326
+ const childTag = childElementTag(child, refFeature, namespaces);
327
+ const childParts = writeObjectParts(child, feature.getName(), roots, sourceResource, namespaces);
328
+ const attrs = childTag.xsiType ? [["xsi:type", childTag.xsiType], ...childParts.attributes] : childParts.attributes;
329
+ childrenXml += renderElement({
330
+ tagName: childParts.tagName,
331
+ attributes: attrs,
332
+ childrenXml: childParts.childrenXml
333
+ });
334
+ }
335
+ continue;
336
+ }
337
+ const encoded = values.map((v) => encodeReferenceValue(v, refFeature, roots, sourceResource, namespaces));
338
+ const anyCrossDocument = encoded.some((e) => e.crossDocument);
339
+ if (anyCrossDocument) {
340
+ for (const e of encoded) {
341
+ const attrs = [["href", e.value]];
342
+ if (e.xsiType) attrs.unshift(["xsi:type", e.xsiType]);
343
+ childrenXml += renderElement({ tagName: feature.getName(), attributes: attrs, childrenXml: "" });
344
+ }
345
+ } else {
346
+ const anyPolymorphic = encoded.some((e) => e.xsiType);
347
+ const tokens = encoded.map(
348
+ (e) => anyPolymorphic ? `${e.xsiType ?? sameTypeToken(refFeature, namespaces)} ${e.value}` : e.value
349
+ );
350
+ attributes.push([feature.getName(), tokens.join(" ")]);
351
+ }
352
+ }
353
+ return { tagName, attributes, childrenXml };
354
+ }
355
+ function renderElement(parts) {
356
+ const attrStr = parts.attributes.map(([k, v]) => ` ${k}="${escapeAttributeValue(v)}"`).join("");
357
+ return parts.childrenXml ? `<${parts.tagName}${attrStr}>${parts.childrenXml}</${parts.tagName}>` : `<${parts.tagName}${attrStr}/>`;
358
+ }
359
+ function childElementTag(child, feature, namespaces) {
360
+ const declared = feature.getEType();
361
+ const actual = child.eClass();
362
+ if (declared === actual) return {};
363
+ const prefix = namespaces.prefixFor(requirePackage(actual));
364
+ return { xsiType: `${prefix}:${actual.getName()}` };
365
+ }
366
+ function sameTypeToken(feature, namespaces) {
367
+ const declared = feature.getEType();
368
+ const prefix = namespaces.prefixFor(requirePackage(declared));
369
+ return `${prefix}:${declared.getName()}`;
370
+ }
371
+ function encodeReferenceValue(target, feature, roots, sourceResource, namespaces) {
372
+ const declared = feature.getEType();
373
+ if (target.eIsProxy()) {
374
+ const proxy = target;
375
+ const actual2 = proxy.eClass();
376
+ const xsiType2 = declared !== actual2 ? `${namespaces.prefixFor(requirePackage(actual2))}:${actual2.getName()}` : void 0;
377
+ return { value: proxy.getProxyURI().toString(), crossDocument: true, xsiType: xsiType2 };
378
+ }
379
+ const targetResource = getResourceOf(target);
380
+ const actual = target.eClass();
381
+ const xsiType = declared !== actual ? `${namespaces.prefixFor(requirePackage(actual))}:${actual.getName()}` : void 0;
382
+ if (isReachableFromRoots(target, roots)) {
383
+ return { value: fragmentForTarget(target, targetResource, roots), crossDocument: false, xsiType };
384
+ }
385
+ const ecoreOwnPath = ecoreOwnFragmentPath(target);
386
+ if (ecoreOwnPath !== void 0) {
387
+ return { value: `${EcorePackageImpl.eINSTANCE.getNsURI()}#${ecoreOwnPath}`, crossDocument: true, xsiType };
388
+ }
389
+ if (targetResource) {
390
+ return {
391
+ value: `${targetResource.getURI().toString()}#${fragmentForTarget(target, targetResource, roots)}`,
392
+ crossDocument: true,
393
+ xsiType
394
+ };
395
+ }
396
+ throw new Error(
397
+ `Cannot encode a reference to an object of class '${actual.getName()}': it has no resource of its own, is not part of Ecore's own metamodel, and is not reachable from this document's own roots via containment.`
398
+ );
399
+ }
400
+ function isReachableFromRoots(target, roots) {
401
+ let current = target;
402
+ while (current) {
403
+ if (roots.includes(current)) return true;
404
+ current = current.eContainer();
405
+ }
406
+ return false;
407
+ }
408
+ function ecoreOwnFragmentPath(target) {
409
+ const ecore = EcorePackageImpl.eINSTANCE;
410
+ if (isEClassifier(target)) {
411
+ return target.getEPackage() === ecore ? `//${target.getName() ?? ""}` : void 0;
412
+ }
413
+ const containingClass = hasContainingClass(target) ? target.getEContainingClass() : void 0;
414
+ if (containingClass && containingClass.getEPackage() === ecore && isNamed(target)) {
415
+ return `//${containingClass.getName() ?? ""}/${target.getName() ?? ""}`;
416
+ }
417
+ return void 0;
418
+ }
419
+ function hasContainingClass(obj) {
420
+ return typeof obj.getEContainingClass === "function";
421
+ }
422
+ function isNamed(obj) {
423
+ return typeof obj.getName === "function";
424
+ }
425
+ function fragmentForTarget(target, owningResource, fallbackRoots) {
426
+ if (!owningResource) return computeEmfFragment(target, fallbackRoots);
427
+ const roots = owningResource.getContents().toArray();
428
+ const serializer = owningResource.getSerializer();
429
+ return serializer.computeFragment ? serializer.computeFragment(target, roots) : coreComputeFragment(target, roots);
430
+ }
431
+ function isReferenceFeature(feature) {
432
+ return "isContainment" in feature;
433
+ }
434
+ function requirePackage(eClass) {
435
+ const pkg = eClass.getEPackage();
436
+ if (!pkg) throw new Error(`EClass '${eClass.getName()}' has no owning EPackage - cannot serialize instances of it.`);
437
+ return pkg;
438
+ }
439
+ function encodeAttributeValue(value, feature) {
440
+ const eType = feature.getEType();
441
+ if (eType?.getName() === "EDate" && value instanceof Date) {
442
+ return value.toISOString();
443
+ }
444
+ return String(value);
445
+ }
446
+ function resolveElementEClass(element, packageRegistry) {
447
+ const xsiType = element.getAttributeNS(XSI_NS, "type");
448
+ if (xsiType) {
449
+ return resolvePrefixedName(xsiType, element, packageRegistry);
450
+ }
451
+ const nsURI = element.namespaceURI;
452
+ if (!nsURI) {
453
+ throw new Error(`Element <${element.tagName}> has no namespace - cannot determine its EPackage.`);
454
+ }
455
+ const pkg = packageRegistry.getPackage(nsURI);
456
+ if (!pkg) {
457
+ throw new Error(`No EPackage registered for namespace '${nsURI}' (element <${element.tagName}>).`);
458
+ }
459
+ const classifier = pkg.getEClassifier(element.localName);
460
+ if (!classifier) {
461
+ throw new Error(`Package '${nsURI}' has no classifier named '${element.localName}'.`);
462
+ }
463
+ return classifier;
464
+ }
465
+ function resolveContainmentChildEClass(element, declared, packageRegistry) {
466
+ const xsiType = element.getAttributeNS(XSI_NS, "type");
467
+ if (xsiType) return resolvePrefixedName(xsiType, element, packageRegistry);
468
+ return declared;
469
+ }
470
+ function resolvePrefixedName(token, contextElement, packageRegistry) {
471
+ const [prefix, name] = token.includes(":") ? token.split(":") : [void 0, token];
472
+ const nsURI = prefix ? contextElement.lookupNamespaceURI(prefix) : contextElement.namespaceURI;
473
+ if (!nsURI) {
474
+ throw new Error(`Could not resolve namespace prefix '${prefix ?? "<default>"}' for type token '${token}'.`);
475
+ }
476
+ const pkg = packageRegistry.getPackage(nsURI);
477
+ if (!pkg) {
478
+ throw new Error(`No EPackage registered for namespace '${nsURI}' (type token '${token}').`);
479
+ }
480
+ const classifier = pkg.getEClassifier(name);
481
+ if (!classifier) {
482
+ throw new Error(`Package '${nsURI}' has no classifier named '${name}'.`);
483
+ }
484
+ return classifier;
485
+ }
486
+ function constructShell(element, eClass, ctx, elementToObj) {
487
+ const obj = eClass.getEPackage()?.getEFactoryInstance()?.create(eClass);
488
+ if (!obj) throw new Error("Could not create object");
489
+ elementToObj.set(element, obj);
490
+ const nameFeature = allStructuralFeaturesOf(eClass).filter((f) => f.getName() === "name").at(0);
491
+ if (nameFeature) {
492
+ const nameAttrValue = element.getAttribute("name");
493
+ if (nameAttrValue !== null) {
494
+ obj.eSet(nameFeature, decodeAttributeValue(nameAttrValue, nameFeature));
495
+ }
496
+ }
497
+ const idFeature = eClass.getEIDAttribute();
498
+ const idFeatureName = idFeature?.getName();
499
+ if (idFeature && idFeatureName && idFeature !== nameFeature) {
500
+ const idAttrValue = element.getAttribute(idFeatureName);
501
+ if (idAttrValue !== null) {
502
+ obj.eSet(idFeature, decodeAttributeValue(idAttrValue, idFeature));
503
+ }
504
+ }
505
+ for (let i = 0; i < element.childNodes.length; i++) {
506
+ const node = element.childNodes.item(i);
507
+ if (!node || node.nodeType !== 1) continue;
508
+ const child = node;
509
+ const feature = allStructuralFeaturesOf(eClass).filter((feature2) => feature2.getName() === child.localName).at(0);
510
+ if (!feature || !isReferenceFeature(feature)) continue;
511
+ const refFeature = feature;
512
+ if (!refFeature.isContainment()) continue;
513
+ const declaredEClass = refFeature.getEType();
514
+ const childEClass = resolveContainmentChildEClass(child, declaredEClass, ctx.packageRegistry);
515
+ const childObj = constructShell(child, childEClass, ctx, elementToObj);
516
+ if (feature.isMany()) {
517
+ const list = obj.eGet(feature);
518
+ list.add(childObj);
519
+ } else {
520
+ obj.eSet(feature, childObj);
521
+ }
522
+ }
523
+ return obj;
524
+ }
525
+ function completeObject(element, obj, ctx, elementToObj, roots) {
526
+ const eClass = obj.eClass();
527
+ for (let i = 0; i < element.attributes.length; i++) {
528
+ const attr = element.attributes.item(i);
529
+ if (attr.namespaceURI === XMI_NS || attr.namespaceURI === XSI_NS) continue;
530
+ if (attr.name.startsWith("xmlns")) continue;
531
+ const feature = allStructuralFeaturesOf(eClass).filter((feature2) => feature2.getName() === attr.localName).at(0);
532
+ if (!feature) continue;
533
+ if (!isReferenceFeature(feature)) {
534
+ if (feature.isMany()) {
535
+ continue;
536
+ }
537
+ obj.eSet(feature, decodeAttributeValue(attr.value, feature));
538
+ continue;
539
+ }
540
+ const refFeature = feature;
541
+ const resolved = decodeAttributeFormReference(attr.value, refFeature, element, roots, ctx.packageRegistry);
542
+ if (refFeature.isMany()) {
543
+ const list = obj.eGet(refFeature);
544
+ for (const r of resolved) list.add(r);
545
+ } else {
546
+ obj.eSet(refFeature, resolved[0]);
547
+ }
548
+ }
549
+ for (let i = 0; i < element.childNodes.length; i++) {
550
+ const node = element.childNodes.item(i);
551
+ if (!node || node.nodeType !== 1) continue;
552
+ const child = node;
553
+ const feature = allStructuralFeaturesOf(eClass).filter((feature2) => feature2.getName() === child.localName).at(0);
554
+ if (!feature) continue;
555
+ if (!isReferenceFeature(feature)) {
556
+ const value = decodeAttributeValue(child.textContent ?? "", feature);
557
+ if (feature.isMany()) {
558
+ const list = obj.eGet(feature);
559
+ list.add(value);
560
+ } else {
561
+ obj.eSet(feature, value);
562
+ }
563
+ continue;
564
+ }
565
+ const refFeature = feature;
566
+ if (refFeature.isContainment()) {
567
+ const childObj = elementToObj.get(child);
568
+ if (!childObj) {
569
+ throw new Error(
570
+ `Internal error: no shell was constructed for <${child.tagName}> during pass 1 - constructShell and completeObject have diverged on which children count as containment.`
571
+ );
572
+ }
573
+ completeObject(child, childObj, ctx, elementToObj, roots);
574
+ continue;
575
+ }
576
+ const href = child.getAttribute("href");
577
+ if (!href) {
578
+ throw new Error(`<${child.tagName}> under <${element.tagName}> is a non-containment reference but has no href.`);
579
+ }
580
+ const xsiType = child.getAttributeNS(XSI_NS, "type");
581
+ const declared = refFeature.getEType();
582
+ const proxyEClass = xsiType ? resolvePrefixedName(xsiType, child, ctx.packageRegistry) : declared;
583
+ const proxy = new ProxyEObjectImpl(proxyEClass, URI.parse(href));
584
+ if (feature.isMany()) {
585
+ const list = obj.eGet(feature);
586
+ list.add(proxy);
587
+ } else {
588
+ obj.eSet(feature, proxy);
589
+ }
590
+ }
591
+ }
592
+ function decodeAttributeFormReference(rawValue, feature, contextElement, roots, packageRegistry) {
593
+ const tokens = rawValue.split(/\s+/).filter((t) => t.length > 0);
594
+ const paired = tokens.length > 0 && tokens.length % 2 === 0 && tokens.every((t, i) => i % 2 === 0 ? looksLikeTypeToken(t) : true);
595
+ const pairs = paired ? tokens.filter((_, i) => i % 2 === 1).map((fragment, i) => ({ typeToken: tokens[i * 2], fragment })) : tokens.map((fragment) => ({ typeToken: void 0, fragment }));
596
+ return pairs.map(({ typeToken, fragment }) => {
597
+ if (isAbsoluteUri(fragment)) {
598
+ const declared = feature.getEType();
599
+ const proxyEClass = typeToken ? resolvePrefixedName(typeToken, contextElement, packageRegistry) : declared;
600
+ return new ProxyEObjectImpl(proxyEClass, URI.parse(fragment));
601
+ }
602
+ const normalized = fragment.startsWith("#") ? fragment.slice(1) : fragment;
603
+ const resolved = resolveEmfFragment(normalized, roots);
604
+ if (!resolved) {
605
+ throw new Error(
606
+ `Unresolved same-document reference '${fragment}' on feature '${feature.getName()}': no object matches this fragment in this document.`
607
+ );
608
+ }
609
+ return resolved;
610
+ });
611
+ }
612
+ function relativeOrAbsolutePath(fromUri, toUri) {
613
+ if (fromUri.getScheme() !== toUri.getScheme()) return toUri.toString();
614
+ const fromSegments = fromUri.getPath().split("/").filter((s) => s.length > 0);
615
+ const toSegments = toUri.getPath().split("/").filter((s) => s.length > 0);
616
+ const fromDir = fromSegments.slice(0, -1);
617
+ const toDir = toSegments.slice(0, -1);
618
+ const toFile = toSegments[toSegments.length - 1] ?? "";
619
+ let commonLength = 0;
620
+ while (commonLength < fromDir.length && commonLength < toDir.length && fromDir[commonLength] === toDir[commonLength]) {
621
+ commonLength++;
622
+ }
623
+ const upSegments = fromDir.slice(commonLength).map(() => "..");
624
+ const downSegments = toDir.slice(commonLength);
625
+ const relativeSegments = [...upSegments, ...downSegments, toFile];
626
+ return relativeSegments.join("/");
627
+ }
628
+ function looksLikeTypeToken(token) {
629
+ return token.includes(":") && !token.startsWith("/") && !token.startsWith("#");
630
+ }
631
+ function isAbsoluteUri(token) {
632
+ return /^[a-zA-Z][a-zA-Z0-9+.-]*:(\/\/|#)/.test(token);
633
+ }
634
+ function decodeAttributeValue(value, feature) {
635
+ const eType = feature.getEType();
636
+ const typeName = eType?.getName();
637
+ if (typeName === "EDate") return new Date(value);
638
+ if (typeName === "EInt" || typeName === "ELong" || typeName === "EDouble" || typeName === "EFloat")
639
+ return Number(value);
640
+ if (typeName === "EBoolean") return value === "true";
641
+ return value;
642
+ }
643
+
644
+ // src/xmi-resource-factory.ts
645
+ var XmiResourceFactory = class {
646
+ createResource(uri) {
647
+ return new ResourceImpl(uri, new XmiSerializer());
648
+ }
649
+ };
650
+ function registerXmiFormat(registry) {
651
+ registry.registerForExtension("xmi", new XmiResourceFactory());
652
+ registry.registerForExtension("ecore", new XmiResourceFactory());
653
+ }
654
+ export {
655
+ NamespaceCollector,
656
+ XmiResourceFactory,
657
+ XmiSerializer,
658
+ allAttributesOf,
659
+ allStructuralFeaturesOf,
660
+ computeEmfFragment,
661
+ escapeAttributeValue,
662
+ escapeText,
663
+ parseXmlDocument,
664
+ registerXmiFormat,
665
+ resolveEmfFragment
666
+ };
667
+ //# sourceMappingURL=index.js.map