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