UNPKG

@tripsnek/tmf

Version:

TypeScript Modeling Framework - A TypeScript port of the Eclipse Modeling Framework (EMF)

358 lines 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EcoreStringWriter = void 0; const eenum_impl_1 = require("../metamodel/eenum-impl"); const eclass_impl_1 = require("../metamodel/eclass-impl"); const edata_type_impl_1 = require("../metamodel/edata-type-impl"); const eattribute_impl_1 = require("../metamodel/eattribute-impl"); const tutils_1 = require("../tutils"); /** * Writes EPackage metamodels to Ecore XML string format. * This class handles the core writing logic without file system dependencies. */ class EcoreStringWriter { indentLevel = 0; indentString = ' '; /** * Converts an EPackage to Ecore XML string format. * @param ePackage The root package to serialize * @returns The XML string representation */ writeToString(ePackage) { const xml = this.buildPackageXml(ePackage, true); return `<?xml version="1.0" encoding="UTF-8"?>\n${xml}`; } /** * Builds the XML for a package. */ buildPackageXml(ePackage, isRoot = false) { const indent = this.getIndent(); let xml = ''; if (isRoot) { // Root package with full namespace declarations xml = `<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"`; } else { xml = `${indent}<eSubpackages`; } // Add package attributes xml += ` name="${this.escapeXml(ePackage.getName())}"`; if (ePackage.getNsURI()) { xml += ` nsURI="${this.escapeXml(ePackage.getNsURI())}"`; } if (ePackage.getNsPrefix()) { xml += ` nsPrefix="${this.escapeXml(ePackage.getNsPrefix())}"`; } xml += '>'; this.indentLevel++; // Add classifiers for (const classifier of ePackage.getEClassifiers()) { xml += '\n' + this.buildClassifierXml(classifier, ePackage); } // Add subpackages for (const subPackage of ePackage.getESubPackages()) { xml += '\n' + this.buildPackageXml(subPackage, false); } this.indentLevel--; if (isRoot) { xml += '\n</ecore:EPackage>'; } else { xml += `\n${indent}</eSubpackages>`; } return xml; } /** * Builds the XML for a classifier (EClass, EEnum, or EDataType). */ buildClassifierXml(classifier, containingPackage) { const indent = this.getIndent(); if (classifier instanceof eenum_impl_1.EEnumImpl) { return this.buildEnumXml(classifier); } else if (classifier instanceof eclass_impl_1.EClassImpl) { return this.buildClassXml(classifier, containingPackage); } else if (classifier instanceof edata_type_impl_1.EDataTypeImpl) { return this.buildDataTypeXml(classifier); } return ''; } /** * Builds the XML for an EClass. */ buildClassXml(eClass, containingPackage) { const indent = this.getIndent(); let xml = `${indent}<eClassifiers xsi:type="ecore:EClass" name="${this.escapeXml(eClass.getName())}"`; // Add super types if any const superTypes = eClass.getESuperTypes(); if (superTypes && superTypes.size() > 0) { const superTypeRefs = []; for (let i = 0; i < superTypes.size(); i++) { const superType = superTypes.get(i); superTypeRefs.push(this.getClassifierReference(superType, containingPackage)); } xml += ` eSuperTypes="${superTypeRefs.join(' ')}"`; } // Add abstract/interface flags if set if (eClass.isAbstract()) { xml += ' abstract="true"'; } if (eClass.isInterface()) { xml += ' interface="true"'; } // Check if class has content const hasFeatures = eClass.getEStructuralFeatures() && eClass.getEStructuralFeatures().size() > 0; const hasOperations = eClass.getEOperations() && eClass.getEOperations().size() > 0; if (!hasFeatures && !hasOperations) { xml += '/>'; return xml; } xml += '>'; this.indentLevel++; // Add operations if (hasOperations) { for (let i = 0; i < eClass.getEOperations().size(); i++) { const operation = eClass.getEOperations().get(i); xml += '\n' + this.buildOperationXml(operation, containingPackage); } } // Add structural features if (hasFeatures) { for (let i = 0; i < eClass.getEStructuralFeatures().size(); i++) { const feature = eClass.getEStructuralFeatures().get(i); xml += '\n' + this.buildFeatureXml(feature, containingPackage); } } this.indentLevel--; xml += `\n${indent}</eClassifiers>`; return xml; } /** * Builds the XML for an EOperation. */ buildOperationXml(operation, containingPackage) { const indent = this.getIndent(); let xml = `${indent}<eOperations name="${this.escapeXml(operation.getName())}"`; // Add return type if present if (operation.getEType()) { xml += ` eType="${this.getClassifierReference(operation.getEType(), containingPackage)}"`; } // Add upper bound if not 1 if (operation.getUpperBound() == -1) { xml += ` upperBound="${operation.getUpperBound()}"`; } // Check if operation has parameters const hasParameters = operation.getEParameters() && operation.getEParameters().size() > 0; if (!hasParameters) { xml += '/>'; return xml; } xml += '>'; this.indentLevel++; // Add parameters for (let i = 0; i < operation.getEParameters().size(); i++) { const param = operation.getEParameters().get(i); xml += '\n' + this.buildParameterXml(param, containingPackage); } this.indentLevel--; xml += `\n${indent}</eOperations>`; return xml; } /** * Builds the XML for an EParameter. */ buildParameterXml(parameter, containingPackage) { const indent = this.getIndent(); let xml = `${indent}<eParameters name="${this.escapeXml(parameter.getName())}"`; if (parameter.getUpperBound() == -1) { xml += ` upperBound="${parameter.getUpperBound()}"`; } //TODO: lower bounds for optional parameters? since we can support those in typescript if (parameter.getEType()) { xml += ` eType="${this.getClassifierReference(parameter.getEType(), containingPackage)}"`; } xml += '/>'; return xml; } /** * Builds the XML for a structural feature (EAttribute or EReference). */ buildFeatureXml(feature, containingPackage) { const indent = this.getIndent(); const isAttribute = feature instanceof eattribute_impl_1.EAttributeImpl; const featureType = isAttribute ? 'ecore:EAttribute' : 'ecore:EReference'; let xml = `${indent}<eStructuralFeatures xsi:type="${featureType}" name="${this.escapeXml(feature.getName())}"`; // Add type if (feature.getEType()) { xml += ` eType="${this.getClassifierReference(feature.getEType(), containingPackage)}"`; } //TODO: lowerbound? only seems to make sense if we have 'required' features if (feature.getUpperBound() == -1) { xml += ` upperBound="${feature.getUpperBound()}"`; } // Add feature-specific attributes if (isAttribute) { const attr = feature; if (attr.isId()) { xml += ' iD="true"'; } } else { const ref = feature; if (ref.isContainment()) { xml += ' containment="true"'; } if (ref.getEOpposite()) { xml += ` eOpposite="${this.getFeatureReference(ref.getEOpposite())}"`; } } // Add other properties if (feature.isTransient()) { xml += ' transient="true"'; } // if (feature.isDerived()) { // xml += ' derived="true"'; // } if (!feature.isChangeable()) { xml += ' changeable="false"'; } if (feature.isVolatile()) { xml += ' volatile="true"'; } if (feature.getDefaultValueLiteral()) { xml += ` defaultValueLiteral="${this.escapeXml(feature.getDefaultValueLiteral())}"`; } xml += '/>'; return xml; } /** * Builds the XML for an EEnum. */ buildEnumXml(eEnum) { const indent = this.getIndent(); let xml = `${indent}<eClassifiers xsi:type="ecore:EEnum" name="${this.escapeXml(eEnum.getName())}"`; const hasLiterals = eEnum.getELiterals() && eEnum.getELiterals().size() > 0; if (!hasLiterals) { xml += '/>'; return xml; } xml += '>'; this.indentLevel++; // Add enum literals for (let i = 0; i < eEnum.getELiterals().size(); i++) { const literal = eEnum.getELiterals().get(i); xml += '\n' + this.buildEnumLiteralXml(literal); } this.indentLevel--; xml += `\n${indent}</eClassifiers>`; return xml; } /** * Builds the XML for an EEnumLiteral. */ buildEnumLiteralXml(literal) { const indent = this.getIndent(); let xml = `${indent}<eLiterals name="${this.escapeXml(literal.getName())}"`; if (literal.getValue() !== undefined && literal.getValue() !== null) { xml += ` value="${literal.getValue()}"`; } if (literal.getLiteral() && literal.getLiteral() !== literal.getName()) { xml += ` literal="${this.escapeXml(literal.getLiteral())}"`; } xml += '/>'; return xml; } /** * Builds the XML for an EDataType. */ buildDataTypeXml(dataType) { const indent = this.getIndent(); let xml = `${indent}<eClassifiers xsi:type="ecore:EDataType" name="${this.escapeXml(dataType.getName())}"`; // if (dataType.getInstanceClassName()) { // xml += ` instanceClassName="${this.escapeXml(dataType.getInstanceClassName())}"`; // } xml += '/>'; return xml; } /** * Gets a reference string for a classifier. */ getClassifierReference(classifier, fromPackage) { // Check if it's a primitive type const primitiveTypes = tutils_1.TUtils.PRIMITIVES; if (primitiveTypes.includes(classifier.getName())) { return `ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//${classifier.getName()}`; } // Build reference path return this.buildReferencePath(classifier, fromPackage); } /** * Gets a reference string for a feature. */ getFeatureReference(feature) { const containingClass = feature.getEContainingClass(); if (!containingClass) { return `#//${feature.getName()}`; } //root packages don't require package paths if (!containingClass.getEPackage().getESuperPackage()) return `#//${containingClass.getName()}/${feature.getName()}`; else return `#//${this.getPackagePath(containingClass.getEPackage())}/${containingClass.getName()}/${feature.getName()}`; } /** * Builds a reference path to a classifier. */ buildReferencePath(classifier, fromPackage) { const classifierPackage = classifier.getEPackage(); // If in same package, use simple reference // if (classifierPackage === fromPackage) { // return `#//${classifier.getName()}`; // } // Build full path const path = this.getPackagePath(classifierPackage); if (path.length > 0) return `#//${path.join('/')}/${classifier.getName()}`; //root package elements don't require paths return `#//${classifier.getName()}`; } /** * Gets the path to a package from root. */ getPackagePath(pkg) { const path = []; let current = pkg; while (current) { path.unshift(current.getName()); current = current.getESuperPackage(); } // Remove root package name from path if (path.length > 0) { path.shift(); } return path; } /** * Escapes special XML characters. */ escapeXml(str) { if (!str) return ''; return str .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&apos;'); } /** * Gets the current indentation string. */ getIndent() { return this.indentString.repeat(this.indentLevel); } } exports.EcoreStringWriter = EcoreStringWriter; //# sourceMappingURL=ecore-string-writer.js.map