UNPKG

@itwin/ecschema-metadata

Version:

ECObjects core concepts in typescript

180 lines • 7.98 kB
"use strict"; 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