@itwin/ecschema-metadata
Version:
ECObjects core concepts in typescript
114 lines • 5.19 kB
JavaScript
;
/*---------------------------------------------------------------------------------------------
* 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