@itwin/ecschema-metadata
Version:
ECObjects core concepts in typescript
180 lines • 7.98 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnitGraph = exports.GraphUtils = void 0;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
const core_bentley_1 = require("@itwin/core-bentley");
const SchemaItem_1 = require("../Metadata/SchemaItem");
const SchemaKey_1 = require("../SchemaKey");
const ECObjects_1 = require("../ECObjects");
const UnitConversion_1 = require("./UnitConversion");
const Parser_1 = require("./Parser");
const Graph_1 = require("./Graph");
/** @internal */
class GraphUtils {
/**
* DFS traversal - Post order
* @param _graph Graph to traverse
* @param start Starting node
* @param keyFrom Get key from label
* @param op Reducing function
* @param initial Initial label
*/
static dfsReduce(_graph, key, op, initial, baseUnitsMap, accumulatedExponent) {
const outEdges = _graph.outEdges(key);
let t = initial;
if (outEdges.length > 0) {
t = outEdges.reduce((p, edge) => {
const { v, w } = edge;
const edgeExponent = _graph.edge(v, w).exponent;
return GraphUtils.dfsReduce(_graph, edge.w, op, p, baseUnitsMap, accumulatedExponent * edgeExponent);
}, t);
}
else {
if (baseUnitsMap.has(key)) {
const oldExponent = baseUnitsMap.get(key);
baseUnitsMap.set(key, oldExponent + accumulatedExponent);
}
else {
baseUnitsMap.set(key, accumulatedExponent);
}
}
return op(t, key);
}
}
exports.GraphUtils = GraphUtils;
/** @internal */
class UnitGraph {
_context;
_graph = new Graph_1.Graph();
_unitsInProgress = new Map();
constructor(_context) {
this._context = _context;
this._graph.setGraph("Unit tree processor");
}
/**
* Tries to find the unit/constant given by name in currentSchema
* @param name SchemaItem name or parsed definition to find unit of; Could be {schemaName}:{schemaItemName} or {alias}:{schemaItemName} or {schemaItemName}
* @param currentSchema schema to find name in; name could also be in a referenced schema of current schema
*/
async resolveUnit(name, currentSchema) {
let [schemaName] = SchemaItem_1.SchemaItem.parseFullName(name);
const [, schemaItemName] = SchemaItem_1.SchemaItem.parseFullName(name);
if (schemaName !== "") {
// Check if schemaName is schemaName or alias
const ref = currentSchema.getReferenceSync(schemaName);
const refName = currentSchema.getReferenceNameByAlias(schemaName);
if (ref) {
// Got schema by schemaName
schemaName = ref.name;
}
else if (refName) {
// Got schema by alias
schemaName = refName;
}
else {
// Didn't match any referenced schema, check if it is current schemaName or alias
if (schemaName === currentSchema.name || schemaName === currentSchema.alias)
schemaName = currentSchema.name;
}
// Create schema key with schema name
const schemaKey = new SchemaKey_1.SchemaKey(schemaName);
// Get schema with schema key
const schema = await this._context.getSchema(schemaKey);
if (!schema) {
throw new core_bentley_1.BentleyError(core_bentley_1.BentleyStatus.ERROR, "Cannot find schema", () => {
return { schema: schemaName };
});
}
else {
// Set currentSchema to look up schemaItem to be whatever is prefixed in name
currentSchema = schema;
}
// Update name to not have prefix
name = schemaItemName;
}
// Create schema item key with name and schema
const itemKey = new SchemaKey_1.SchemaItemKey(name, currentSchema.schemaKey);
// Get schema item with schema item key
const item = await this._context.getSchemaItem(itemKey);
if (!item)
throw new core_bentley_1.BentleyError(core_bentley_1.BentleyStatus.ERROR, "Cannot find schema item", () => {
return { item: name };
});
if (item.schemaItemType === ECObjects_1.SchemaItemType.Unit || item.schemaItemType === ECObjects_1.SchemaItemType.Constant)
return item;
throw new core_bentley_1.BentleyError(core_bentley_1.BentleyStatus.ERROR, "Item is neither a unit or a constant", () => {
return { itemType: item.key.fullName };
});
}
/**
* Adds unit and corresponding children to graph as well as edges between units
* @param unit Current unit to be added to graph
*/
async addUnit(unit) {
if (this._unitsInProgress.has(unit.key.fullName))
return this._unitsInProgress.get(unit.key.fullName);
if (this._graph.hasNode(unit.key.fullName))
return;
this._graph.setNode(unit.key.fullName, unit);
if (this.isIdentity(unit))
return;
const promise = this.addUnitToGraph(unit);
this._unitsInProgress.set(unit.key.fullName, promise);
await promise
.finally(() => this._unitsInProgress.delete(unit.key.fullName));
}
async addUnitToGraph(unit) {
const umap = (0, Parser_1.parseDefinition)(unit.definition);
const promiseArray = [];
for (const [key, value] of umap) {
promiseArray.push(this.resolveUnit(key, unit.schema).then((u) => [u, value]));
}
const resolved = await Promise.all(promiseArray);
const children = resolved.map(async ([u, def]) => {
await this.addUnit(u);
this._graph.setEdge(unit.key.fullName, u.key.fullName, {
exponent: def.exponent,
});
});
await Promise.all(children);
}
isIdentity(unit) {
return unit.definition === unit.name;
}
/**
* Reduce the tree to produce a single map
* @param unit Unit to be processed
* @param stopNodes The tree exploration should stop here
*/
reduce(unit, baseUnitsMap) {
const unitFullName = unit.key.fullName;
const innerMapStore = new Map();
const outerMapStore = GraphUtils.dfsReduce(this._graph, unitFullName, (p, c) => this.reducingFunction(p, c), innerMapStore, baseUnitsMap, 1);
return outerMapStore;
}
reducingFunction(innermapStore, unitFullName) {
const outEdges = this._graph.outEdges(unitFullName);
if (outEdges) {
const cmap = outEdges.reduce((pm, e) => {
const { exponent } = this._graph.edge(e.v, e.w);
const stored = innermapStore.get(e.w);
const map = stored ? stored : UnitConversion_1.UnitConversion.identity;
const emap = map.raise(exponent);
return pm ? pm.multiply(emap) : emap;
}, undefined);
const thisMap = this._graph.node(unitFullName) ? UnitConversion_1.UnitConversion.from(this._graph.node(unitFullName)) : UnitConversion_1.UnitConversion.identity;
const other = cmap || UnitConversion_1.UnitConversion.identity;
const result = other.compose(thisMap);
innermapStore.set(unitFullName, result);
}
else {
innermapStore.set(unitFullName, UnitConversion_1.UnitConversion.identity);
}
return innermapStore;
}
}
exports.UnitGraph = UnitGraph;
//# sourceMappingURL=UnitTree.js.map