UNPKG

@yellicode/elements

Version:

The meta model API for Yellicode - an extensible code generator.

217 lines (216 loc) 12.5 kB
/* * Copyright (c) 2020 Yellicode * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { StringUtility } from '@yellicode/core'; import { ElementTypeUtility } from './utils'; import * as Interfaces from "./interfaces"; import * as Classes from "./classes"; import { ProfileUtility } from './profile-utility'; var ProfileExtender = /** @class */ (function () { function ProfileExtender() { } ProfileExtender.applyProfiles = function (profiles) { return; // disabled in favor of modeler-generated profile API code // profiles.forEach(p => { // ProfileExtender.applyProfile(p as Interfaces.Profile); // }) }; ProfileExtender.applyProfile = function (profile) { return; // disabled in favor of modeler-generated profile API code // const stereotypes = ProfileUtility.getStereotypes(profile); // if (stereotypes.length === 0) // return; // ProfileExtender.addHasProfileGetters(profile); // ProfileExtender.addHasStereotypeGetters(stereotypes); }; /** * Extends all Package class types with getters for checking if the profile is applied to the Package. */ ProfileExtender.addHasProfileGetters = function (profile) { var classTypes = ProfileExtender.getClassTypesByElementType(Interfaces.ElementType.package); ProfileExtender.extendTypesWithGetter(classTypes, "hasProfile_" + (profile.safeName || profile.name), function () { return ProfileUtility.hasProfileId(this, profile.id); }); }; /** * Extends all extendable class types with getters for checking if a stereotype is applied. */ ProfileExtender.addHasStereotypeGetters = function (stereoTypes) { stereoTypes.forEach(function (st) { var metaClasses = ProfileUtility.getMetaClassesExtendedBy(st); metaClasses.forEach(function (metaClass) { var classTypes = ProfileExtender.getClassTypesByElementType(metaClass); ProfileExtender.extendTypesWithGetter(classTypes, "is_" + (st.safeName || st.name), function () { // console.log(`Checking stereotype ${st.name}`); return ProfileUtility.hasStereotypeId(this, st.id); }); ProfileExtender.extendTypesWithGetter(classTypes, "as_" + (st.safeName || st.name), function () { // console.log(`Checking stereotype ${st.name}`); return this; // just return the current instance, this getter is just syntactic sugar for the TS compiler }); }); }); }; ProfileExtender.addSubElementAccessors = function (stereoTypes, elementType, elementSubTypes) { var classTypes = ProfileExtender.getClassTypesByElementType(elementType); ProfileExtender.addSubSelementAccessorsForElementTypes(classTypes, stereoTypes, elementSubTypes); }; /** * Adds accessor functions for stereotypes to the package (and model) level, e.g. getMyStereotypeClasses(), * getMyStereotypeInterfaces(), etc */ ProfileExtender.addPackageStereotypeAccessors = function (profile, stereoTypes) { // We apply the accessors to package types var packageTypes = ProfileExtender.getClassTypesByElementType(Interfaces.ElementType.package); stereoTypes.forEach(function (st) { // Does the stereotype extend a packageable element? If not, don't add an accessor on the package level. var subPackagedElementTypes = st.extends.filter(function (ext) { return ElementTypeUtility.isPackageableElement(ext.metaClass); }).map(function (ext) { return ext.metaClass; }); if (subPackagedElementTypes.length === 0) return; // Add an accessor for each packageable element that the profile extends ProfileExtender.addSubSelementAccessorsForElementTypes(packageTypes, stereoTypes, subPackagedElementTypes); // Create a generic accessor for all packaged elements having the stereotype applied ProfileExtender.extendTypesWithFunction(packageTypes, "getPackagedElementsOf" + (st.safeName || st.name), function () { return ProfileUtility.filterByStereotypeId(this.packagedElements, st.id); }); }); // Create a accessor for access to packages having this profile applied (e.g. getPackagesOfMyProfile) ProfileExtender.extendTypesWithFunction(packageTypes, "getPackagesOf" + (profile.safeName || profile.name), function () { return ProfileUtility.filterByProfileId(this.packagedElements, profile.id); }); }; /** * Extends all classifier types with accessors to profile-specific stereotypes for Operations, Properties etc. */ ProfileExtender.addClassifierAccessors = function (stereoTypes) { // All these types are a MemberedClassifier, so can have Properties (OwnedAttributes) and OwnedOperations var classifierSubTypes = [Interfaces.ElementType.property, Interfaces.ElementType.operation]; stereoTypes.forEach(function (st) { var memberedClassifiersExtendedByStereotype = st.extends.filter(function (ext) { return ElementTypeUtility.isMemberedClassifier(ext.metaClass); }); memberedClassifiersExtendedByStereotype.forEach(function (extension) { // console.info(`Stereotype ${st.name} extends classifier ${StringUtil.capitalize(Interfaces.ElementType[extendedElementType])}`); var classTypes = ProfileExtender.getClassTypesByElementType(extension.metaClass); ProfileExtender.addSubSelementAccessorsForElementTypes(classTypes, stereoTypes, classifierSubTypes); }); }); }; ProfileExtender.addSubSelementAccessorsForElementTypes = function (classTypes, stereoTypes, elementTypes) { var classTypesDebugString = classTypes.map(function (t) { return t.name; }).join(', '); elementTypes.forEach(function (subElementType) { var stereoTypesExtendingSubType = stereoTypes.filter(function (s) { return s.extends.findIndex(function (t) { return t.metaClass === subElementType; }) > -1; }); if (stereoTypesExtendingSubType.length > 0) { // console.info(`ProfileExtender: Adding ${StringUtil.capitalize(Interfaces.ElementType[subElementType])} stereotype accessors to ${classTypesDebugString}.`); } else { // There are no Stereotypes that extend the subType, so there is no need to add an accessor // console.info(`ProfileExtender: Not adding ${StringUtil.capitalize(Interfaces.ElementType[subElementType])} stereotype accessors to ${classTypesDebugString}. There is no stereotype extending this element type.`); return; } // If this an "Owned" sub element (e.g. OwnedOperations, OwnedAttributes), add a prefix. var prefix = ElementTypeUtility.isPackageableElement(subElementType) ? '' : 'Owned'; stereoTypesExtendingSubType.forEach(function (st) { var pluralElementTypeName = ProfileExtender.getGetPluralElementTypeName(subElementType); var functionName = "get" + prefix + pluralElementTypeName + "Of" + (st.safeName || st.name); // E.g "getClassesOfMyStereotype" var accessorFunction = ProfileExtender.getAccessorFunctionForSubElementType(subElementType, st); if (accessorFunction) { ProfileExtender.extendTypesWithFunction(classTypes, functionName, accessorFunction); } else { // console.warn(`Failed adding accessor function '${functionName}' to ${classTypesDebugString}. Sub element type ${Interfaces.ElementType[subElementType]} is not supported.`); } }); }); }; ProfileExtender.getAccessorFunctionForSubElementType = function (subElementType, stereoType) { if (ElementTypeUtility.isPackageableElement(subElementType)) { // E.g. getMyStereotypeClasses return function () { return ProfileUtility.filterByStereotypeId(this.packagedElements, stereoType.id, subElementType); }; } switch (subElementType) { case Interfaces.ElementType.property: return function () { return ProfileUtility.filterByStereotypeId(this.ownedAttributes, stereoType.id); }; case Interfaces.ElementType.operation: return function () { return ProfileUtility.filterByStereotypeId(this.ownedOperations, stereoType.id); }; case Interfaces.ElementType.parameter: return function () { return ProfileUtility.filterByStereotypeId(this.ownedParameters, stereoType.id); }; case Interfaces.ElementType.enumerationLiteral: return function () { return ProfileUtility.filterByStereotypeId(this.ownedLiterals, stereoType.id); }; default: return null; } }; ProfileExtender.extendTypesWithFunction = function (classTypes, funcName, func) { classTypes.forEach(function (t) { // console.info(`ProfileExtender: Extending type ${t.name} with function '${funcName}'.`); t.prototype[funcName] = func; }); }; ProfileExtender.extendTypesWithGetter = function (classTypes, propertyName, getter) { classTypes.forEach(function (t) { // console.info(`ProfileExtender: Extending type ${t.name} with getter '${propertyName}'.`); Object.defineProperty(t.prototype, propertyName, { get: getter, enumerable: true, configurable: true, }); }); }; /** * Gets the actual Javascript implementation types that match the specified element type. */ ProfileExtender.getClassTypesByElementType = function (elementType) { switch (elementType) { case Interfaces.ElementType.package: return [Classes.Model, Classes.Package]; case Interfaces.ElementType.class: return [Classes.Class]; case Interfaces.ElementType.dataType: return [Classes.DataType, Classes.PrimitiveType]; case Interfaces.ElementType.interface: return [Classes.Interface]; case Interfaces.ElementType.enumeration: return [Classes.Enumeration]; case Interfaces.ElementType.enumerationLiteral: return [Classes.EnumerationLiteral]; case Interfaces.ElementType.property: return [Classes.Property]; case Interfaces.ElementType.operation: return [Classes.Operation]; case Interfaces.ElementType.parameter: return [Classes.Parameter]; default: console.warn("Failed getting class type(s) for element type '" + Interfaces.ElementType[elementType] + "'. The element type is not supported."); return []; } }; ProfileExtender.getGetPluralElementTypeName = function (elementType) { switch (elementType) { case Interfaces.ElementType.property: return 'Attributes'; case Interfaces.ElementType.class: return 'Classes'; case Interfaces.ElementType.enumerationLiteral: return 'Literals'; default: // By default, add ..s var singular = StringUtility.capitalize(Interfaces.ElementType[elementType]); return singular + "s"; } }; return ProfileExtender; }()); export { ProfileExtender };