UNPKG

@yellicode/elements

Version:

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

140 lines (139 loc) 6.68 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 * as toposort from 'toposort'; import { isPackage, isClassifier, isBehavioredClassifier, isMemberedClassifier, isType } from '../utils'; /** * A bitwise enumeration used to specify what types of dependency to include * during a DependencySortTransform. */ export var DependencyKind; (function (DependencyKind) { DependencyKind[DependencyKind["none"] = 0] = "none"; DependencyKind[DependencyKind["generalizations"] = 1] = "generalizations"; DependencyKind[DependencyKind["interfaceRealizations"] = 2] = "interfaceRealizations"; DependencyKind[DependencyKind["attributes"] = 4] = "attributes"; DependencyKind[DependencyKind["operationParameters"] = 8] = "operationParameters"; DependencyKind[DependencyKind["all"] = 15] = "all"; })(DependencyKind || (DependencyKind = {})); /** * Sorts the types within a package (and within each nested package) based on their dependencies, in such a way * that dependencies appear before dependents. Dependencies are determined based on generalizations, interface * realizations, attributes and operation parameters. */ var DependencySortTransform = /** @class */ (function () { /** * Constructor. Creates a new DependencySortTransform instance. * @param dependencyKind An optional DependencyKind value (or a bitwise combination of values) * that indicates what types of dependency must be taken into account. The default is DependencyKind.All. */ function DependencySortTransform(dependencyKind) { this._options = dependencyKind == null ? DependencyKind.all : dependencyKind; } DependencySortTransform.prototype.transform = function (pack) { if (!pack) return pack; this.transformPackageRecursive(pack); return pack; }; DependencySortTransform.prototype.transformPackageRecursive = function (pack) { var _this = this; if (!pack.packagedElements) return; // Get all siblings that are relevant for building a depencency graph. var allTypes = pack.packagedElements.filter(function (pe) { return isType(pe); }); // Build a dependency graph of each element, see https://www.npmjs.com/package/toposort for the docs var graph = []; allTypes.forEach(function (t) { var elementDependencies = DependencySortTransform.getTypeDependencies(t, _this._options, allTypes); if (elementDependencies.length > 0) { elementDependencies.forEach(function (dependency) { graph.push([t, dependency]); }); } }); if (graph.length > 0) { // Sort, and reverse because we need a dependency graph pack.packagedElements = toposort.array(pack.packagedElements, graph).reverse(); } // Transform nested packages, recursively pack.packagedElements.forEach(function (element) { if (isPackage(element)) { _this.transformPackageRecursive(element); } }); }; DependencySortTransform.getTypeDependencies = function (element, options, allTypes) { var result = []; // Dependencies based on generalizations if (options & DependencyKind.generalizations && isClassifier(element)) { DependencySortTransform.pushGeneralizationDependencies(element, allTypes, result); } // Dependencies based on interface realization if (options & DependencyKind.interfaceRealizations && isBehavioredClassifier(element)) { DependencySortTransform.pushInterfaceRealizationDependencies(element, allTypes, result); } // Dependencies based on members if (isMemberedClassifier(element)) { if (options & DependencyKind.attributes) { DependencySortTransform.pushOwnedAttributeDependencies(element, allTypes, result); } if (options & DependencyKind.operationParameters) { DependencySortTransform.pushOwnedOperationDependencies(element, allTypes, result); } } return result; }; DependencySortTransform.pushGeneralizationDependencies = function (element, allTypes, dependencies) { if (!element.generalizations || !element.generalizations.length) return; element.generalizations.forEach(function (g) { if (g.general === element) return; if (allTypes.indexOf(g.general) > -1 && dependencies.indexOf(g.general) === -1) { dependencies.push(g.general); } }); }; DependencySortTransform.pushInterfaceRealizationDependencies = function (element, allTypes, dependencies) { if (!element.interfaceRealizations || !element.interfaceRealizations.length) return; element.interfaceRealizations.forEach(function (ir) { if (allTypes.indexOf(ir.contract) > -1 && dependencies.indexOf(ir.contract) === -1) { dependencies.push(ir.contract); } }); }; DependencySortTransform.pushOwnedOperationDependencies = function (element, allTypes, dependencies) { if (!element.ownedOperations || !element.ownedOperations.length) return; element.ownedOperations.forEach(function (op) { if (!op.ownedParameters || !op.ownedParameters.length) return; op.ownedParameters.forEach(function (p) { if (!p.type) return; if (allTypes.indexOf(p.type) > -1 && dependencies.indexOf(p.type) === -1) { dependencies.push(p.type); } }); }); }; DependencySortTransform.pushOwnedAttributeDependencies = function (element, allTypes, dependencies) { if (!element.ownedAttributes || !element.ownedAttributes.length) return; element.ownedAttributes.forEach(function (att) { if (!att.type) return; if (allTypes.indexOf(att.type) > -1 && dependencies.indexOf(att.type) === -1) { dependencies.push(att.type); } }); }; return DependencySortTransform; }()); export { DependencySortTransform };