UNPKG

@itwin/ecschema-metadata

Version:

ECObjects core concepts in typescript

114 lines 5.19 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); exports.SchemaGraph = void 0; const ECObjects_1 = require("../ECObjects"); const Exception_1 = require("../Exception"); /** * Utility class for detecting cyclic references in a Schema graph. * @internal */ class SchemaGraph { _schemas = []; constructor() { } find(schemaKey) { return this._schemas.find((info) => info.schemaKey.matches(schemaKey, ECObjects_1.SchemaMatchType.Latest)); } /** * Detected cyclic references in a schema and throw an exception if a cycle is found. */ throwIfCycles() { const cycles = this.detectCycles(); if (cycles) { const result = cycles.map((cycle) => `${cycle.schema.schemaKey.name} --> ${cycle.refSchema.schemaKey.name}`).join(", "); throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.InvalidECJson, `Schema '${this._schemas[0].schemaKey.name}' has reference cycles: ${result}`); } } /** * Detected cyclic references in a schema. * @returns An array describing the cycle if there is a cycle or undefined if no cycles found. */ detectCycles() { const visited = {}; const recStack = {}; const cycles = []; for (const schema of this._schemas) { if (this.detectCycleUtil(schema, visited, recStack, cycles)) { return cycles.length > 0 ? cycles : undefined; } } return undefined; } detectCycleUtil(schema, visited, recStack, cycles) { let cycleFound = false; if (!visited[schema.schemaKey.name]) { visited[schema.schemaKey.name] = true; recStack[schema.schemaKey.name] = true; for (const refKey of schema.references) { const refSchema = this.find(refKey.schemaKey); if (undefined === refSchema) throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.UnableToLoadSchema, `Could not find the schema info for ref schema ${refKey.schemaKey.toString()} for schema ${schema.schemaKey.toString()}`); if (!visited[refKey.schemaKey.name] && this.detectCycleUtil(refSchema, visited, recStack, cycles)) { cycles.push({ schema, refSchema }); cycleFound = true; } else if (recStack[refKey.schemaKey.name]) { cycles.push({ schema, refSchema }); cycleFound = true; } } } if (!cycleFound) recStack[schema.schemaKey.name] = false; return cycleFound; } /** * Generates a SchemaGraph for the input schema using the context to find info on referenced schemas. Use the generateGraphSync if you have the fully loaded Schema. * @param schema The SchemaInfo to build the graph from * @param context The SchemaContext used to locate info on the referenced schemas * @returns A SchemaGraph that can be used to detect schema cycles */ static async generateGraph(schema, context) { const graph = new SchemaGraph(); const genGraph = async (s) => { if (graph.find(s.schemaKey)) return; graph._schemas.push(s); for (const refSchema of s.references) { if (!graph.find(refSchema.schemaKey)) { const refInfo = await context.getSchemaInfo(refSchema.schemaKey, ECObjects_1.SchemaMatchType.LatestWriteCompatible); if (undefined === refInfo) { throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.UnableToLocateSchema, `Could not locate the referenced schema, ${refSchema.schemaKey.name}.${refSchema.schemaKey.version.toString()}, of ${s.schemaKey.name} when populating the graph for ${schema.schemaKey.name}`); } await genGraph(refInfo); } } }; await genGraph(schema); return graph; } /** * Generates a SchemaGraph for the input schema. Use the generateGraph if you just have schema info. * @param schema The Schema to build the graph from. * @returns A SchemaGraph that can be used to detect schema cycles */ static generateGraphSync(schema) { const graph = new SchemaGraph(); const genGraph = (s) => { if (graph.find(s.schemaKey)) return; graph._schemas.push(s); for (const refSchema of s.references) { if (!graph.find(refSchema.schemaKey)) genGraph(refSchema); } }; genGraph(schema); return graph; } } exports.SchemaGraph = SchemaGraph; //# sourceMappingURL=SchemaGraph.js.map