@yellicode/elements
Version:
The meta model API for Yellicode - an extensible code generator.
217 lines (216 loc) • 12.5 kB
JavaScript
/*
* 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 };