@itwin/ecschema-metadata
Version:
ECObjects core concepts in typescript
384 lines • 18.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SchemaContext = exports.SchemaCache = 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 ECObjects_1 = require("./ECObjects");
const Exception_1 = require("./Exception");
const SchemaItem_1 = require("./Metadata/SchemaItem");
const SchemaKey_1 = require("./SchemaKey");
const ECClassHierarchy_1 = require("./utils/ECClassHierarchy");
/**
* @internal
*/
class SchemaMap extends Array {
}
/**
* @internal
*/
class SchemaCache {
_schema;
constructor() {
this._schema = new SchemaMap();
}
get count() { return this._schema.length; }
loadedSchemaExists(schemaKey) {
return undefined !== this._schema.find((entry) => entry.schemaInfo.schemaKey.matches(schemaKey, ECObjects_1.SchemaMatchType.Latest) && !entry.schemaPromise);
}
schemaPromiseExists(schemaKey) {
return undefined !== this._schema.find((entry) => entry.schemaInfo.schemaKey.matches(schemaKey, ECObjects_1.SchemaMatchType.Latest) && undefined !== entry.schemaPromise);
}
findEntry(schemaKey, matchType) {
return this._schema.find((entry) => entry.schemaInfo.schemaKey.matches(schemaKey, matchType));
}
removeSchemaPromise(schemaKey) {
const entry = this.findEntry(schemaKey, ECObjects_1.SchemaMatchType.Latest);
if (entry)
entry.schemaPromise = undefined;
}
removeEntry(schemaKey) {
this._schema = this._schema.filter((entry) => !entry.schemaInfo.schemaKey.matches(schemaKey));
}
/**
* Returns true if the schema exists in either the schema cache or the promise cache. SchemaMatchType.Latest used.
* @param schemaKey The key to search for.
*/
schemaExists(schemaKey) {
return this.loadedSchemaExists(schemaKey) || this.schemaPromiseExists(schemaKey);
}
/**
* Adds a promise to load the schema to the cache. Does not allow for duplicate schemas in the cache of schemas or cache of promises, checks using SchemaMatchType.Latest.
* When the promise completes the schema will be added to the schema cache and the promise will be removed from the promise cache
* @param schemaInfo An object with the schema key for the schema being loaded and it's references
* @param schema The partially loaded schema that the promise will fulfill
* @param schemaPromise The schema promise to add to the cache.
*/
async addSchemaPromise(schemaInfo, schema, schemaPromise) {
if (this.schemaExists(schemaInfo.schemaKey))
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.DuplicateSchema, `The schema, ${schemaInfo.schemaKey.toString()}, already exists within this cache.`);
this._schema.push({ schemaInfo, schema, schemaPromise });
// This promise is cached and will be awaited when the user requests the full schema.
// If the promise competes successfully before the user requests the schema it will be removed from the cache
// If it fails it will remain in the cache until the user awaits it and handles the error
schemaPromise.then(() => {
this.removeSchemaPromise(schemaInfo.schemaKey);
}).catch(() => {
// Leave the promise in the cache so that when the user requests the schema they get the rejection
});
}
/**
* Adds a schema to the cache. Does not allow for duplicate schemas, checks using SchemaMatchType.Latest.
* @param schema The schema to add to the cache.
*/
async addSchema(schema) {
if (this.schemaExists(schema.schemaKey))
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.DuplicateSchema, `The schema, ${schema.schemaKey.toString()}, already exists within this cache.`);
this._schema.push({ schemaInfo: schema, schema });
}
/**
* Adds a schema to the cache. Does not allow for duplicate schemas, checks using SchemaMatchType.Latest.
* @param schema The schema to add to the cache.
*/
addSchemaSync(schema) {
if (this.schemaExists(schema.schemaKey))
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.DuplicateSchema, `The schema, ${schema.schemaKey.toString()}, already exists within this cache.`);
this._schema.push({ schemaInfo: schema, schema });
}
/**
* Gets the schema which matches the provided SchemaKey.
* @param schemaKey The SchemaKey describing the schema to get from the cache.
* @param matchType The match type to use when locating the schema
*/
async getSchema(schemaKey, matchType = ECObjects_1.SchemaMatchType.Latest) {
if (this.count === 0)
return undefined;
const entry = this.findEntry(schemaKey, matchType);
if (!entry)
return undefined;
if (entry.schemaPromise) {
try {
const schema = await entry.schemaPromise;
return schema;
}
catch (e) {
this.removeEntry(schemaKey);
throw e;
}
}
return entry.schema;
}
/**
* Gets the schema info which matches the provided SchemaKey. The schema info may be returned before the schema is fully loaded.
* @param schemaKey The SchemaKey describing the schema to get from the cache.
* @param matchType The match type to use when locating the schema
*/
async getSchemaInfo(schemaKey, matchType = ECObjects_1.SchemaMatchType.Latest) {
if (this.count === 0)
return undefined;
const entry = this.findEntry(schemaKey, matchType);
if (entry)
return entry.schemaInfo;
return undefined;
}
/**
* Gets the schema which matches the provided SchemaKey. If the schema is partially loaded an exception will be thrown.
* @param schemaKey The SchemaKey describing the schema to get from the cache.
* @param matchType The match type to use when locating the schema
*/
getSchemaSync(schemaKey, matchType = ECObjects_1.SchemaMatchType.Latest) {
if (this.count === 0)
return undefined;
const entry = this.findEntry(schemaKey, matchType);
if (entry) {
if (entry.schemaPromise) {
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.UnableToLoadSchema, `The Schema ${schemaKey.toString()} is partially loaded so cannot be loaded synchronously.`);
}
return entry.schema;
}
return undefined;
}
/**
* Generator function that can iterate through each schema in _schema SchemaMap and items for each Schema.
* Does not include schema items from schemas that are not completely loaded yet.
*/
*getSchemaItems() {
for (const entry of this._schema) {
for (const schemaItem of entry.schema.getItems()) {
yield schemaItem;
}
}
}
/**
* Gets all the schemas from the schema cache.
* Does not include schemas from schemas that are not completely loaded yet.
* @returns An array of Schema objects.
*/
getAllSchemas() {
return this._schema.map((entry) => entry.schema);
}
}
exports.SchemaCache = SchemaCache;
/**
* The SchemaContext, context object is used to facilitate schema and schema item location.
*
* The context controls the lifetime of each schema that it knows about. It has to be explicitly removed from the context to delete a schema object.
*
* The context is made up of a group of Schema Locators.
* @public @preview
*/
class SchemaContext {
_locaters;
_knownSchemas;
_fallbackLocaterDefined;
_classHierarchy;
constructor() {
this._locaters = [];
this._knownSchemas = new SchemaCache();
this._locaters.push(this._knownSchemas);
this._fallbackLocaterDefined = false;
this._classHierarchy = new ECClassHierarchy_1.ECClassHierarchy();
}
get locaters() { return this._locaters; }
/** @internal */
get classHierarchy() {
return this._classHierarchy;
}
/**
* Adds a locater to the context.
*
* If no locaters are defined or a fallback locater is not defined, the new locater is added at the end of the locaters array.
* If a fallback locater is already defined, the new locater is inserted before the fallback locater.
*
* @param locater - The locater to be added.
*/
addLocater(locater) {
const insertIndex = (this._locaters.length === 0 || !this._fallbackLocaterDefined) ? this._locaters.length : this._locaters.length - 1;
this._locaters.splice(insertIndex, 0, locater);
}
/**
* Adds a fallback locater to the context.
*
* If a fallback locater is already defined, it replaces the existing one.
* Otherwise, it adds the new locater to the end of the locaters array and marks the fallback locater as defined.
*
* @param locater - The locater to be added as a fallback.
*/
addFallbackLocater(locater) {
if (this._fallbackLocaterDefined) {
this._locaters[this._locaters.length - 1] = locater;
}
else {
this._locaters.push(locater);
this._fallbackLocaterDefined = true;
}
}
/**
* Adds the schema to this context. Use addSchemaPromise instead when asynchronously loading schemas.
* @param schema The schema to add to this context
*/
async addSchema(schema) {
this.addSchemaSync(schema);
}
/**
* Adds the schema to this context
* @param schema The schema to add to this context
*/
addSchemaSync(schema) {
this._knownSchemas.addSchemaSync(schema);
}
/**
* Adds the given SchemaItem to the the SchemaContext by locating the schema, with the best match of SchemaMatchType.Exact, and
* @param schemaItem The SchemaItem to add
* @deprecated in 4.0 - will not be removed until after 2026-06-13. Use ecschema-editing package
*/
async addSchemaItem(schemaItem) {
const schema = await this.getSchema(schemaItem.key.schemaKey, ECObjects_1.SchemaMatchType.Exact);
if (!schema)
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.UnableToLocateSchema, `Unable to add the schema item ${schemaItem.name} to the schema ${schemaItem.key.schemaKey.toString()} because the schema could not be located.`);
schema.addItem(schemaItem);
}
/**
* Returns true if the schema is already in the context. SchemaMatchType.Latest is used to find a match.
* @param schemaKey
*/
schemaExists(schemaKey) {
return this._knownSchemas.schemaExists(schemaKey);
}
/**
* Adds a promise to load the schema to the cache. Does not allow for duplicate schemas in the cache of schemas or cache of promises, checks using SchemaMatchType.Latest.
* When the promise completes the schema will be added to the schema cache and the promise will be removed from the promise cache.
* Use this method over addSchema when asynchronously loading schemas
* @param schemaInfo An object with the schema key for the schema being loaded and it's references
* @param schema The partially loaded schema that the promise will fulfill
* @param schemaPromise The schema promise to add to the cache.
*/
async addSchemaPromise(schemaInfo, schema, schemaPromise) {
return this._knownSchemas.addSchemaPromise(schemaInfo, schema, schemaPromise);
}
/** Attempts to obtain a schema from this context that matches the specified criteria.
* @param schemaKey Identifies the schema to obtain.
* @param matchType Criteria by which to identify potentially matching schemas.
* @returns the schema matching the input criteria, or `undefined` if no such schema could be located.
*/
async getSchema(schemaKey, matchType = ECObjects_1.SchemaMatchType.Latest) {
// the first locater is _knownSchemas, so we don't have to check the cache explicitly here
for (const locater of this._locaters) {
const schema = await locater.getSchema(schemaKey, matchType, this);
if (undefined !== schema)
return schema;
}
return undefined;
}
/**
* Gets the schema info which matches the provided SchemaKey. The schema info may be returned before the schema is fully loaded.
* The fully loaded schema can be gotten later from the context using [[getSchema]].
* @param schemaKey The SchemaKey describing the schema to get from the cache.
* @param matchType The match type to use when locating the schema
*/
async getSchemaInfo(schemaKey, matchType) {
for (const locater of this._locaters) {
const schemaInfo = await locater.getSchemaInfo(schemaKey, matchType, this);
if (undefined !== schemaInfo)
return schemaInfo;
}
return undefined;
}
/** Attempts to obtain a schema from this context that matches the specified criteria.
* Will return undefined if the schema is partially loaded. Use [[getSchema]] to await until the schema is completely loaded.
* @param schemaKey Identifies the schema to obtain.
* @param matchType Criteria by which to identify potentially matching schemas.
* @returns the schema matching the input criteria, or `undefined` if no such schema could be located.
*/
getSchemaSync(schemaKey, matchType = ECObjects_1.SchemaMatchType.Latest) {
// the first locater is _knownSchemas, so we don't have to check the cache explicitly here
for (const locater of this._locaters) {
const schema = locater.getSchemaSync(schemaKey, matchType, this);
if (undefined !== schema)
return schema;
}
return undefined;
}
/**
* Attempts to get a Schema from the context's cache.
* Will await a partially loaded schema then return when it is completely loaded.
* @param schemaKey The SchemaKey to identify the Schema.
* @param matchType The SchemaMatch type to use. Default is SchemaMatchType.Latest.
* @internal
*/
async getCachedSchema(schemaKey, matchType = ECObjects_1.SchemaMatchType.Latest) {
return this._knownSchemas.getSchema(schemaKey, matchType);
}
/**
* Attempts to get a Schema from the context's cache.
* Will return undefined if the cached schema is partially loaded. Use [[getCachedSchema]] to await until the schema is completely loaded.
* @param schemaKey The SchemaKey to identify the Schema.
* @param matchType The SchemaMatch type to use. Default is SchemaMatchType.Latest.
* @internal
*/
getCachedSchemaSync(schemaKey, matchType = ECObjects_1.SchemaMatchType.Latest) {
return this._knownSchemas.getSchemaSync(schemaKey, matchType);
}
async getSchemaItem(schemaNameOrKey, itemNameOrCtor, itemConstructor) {
let schemaKey;
if (typeof schemaNameOrKey === "string") {
const [schemaName, itemName] = SchemaItem_1.SchemaItem.parseFullName(schemaNameOrKey);
schemaKey = (!schemaName) ? new SchemaKey_1.SchemaKey(itemName) : new SchemaKey_1.SchemaKey(schemaName);
}
else {
schemaKey = schemaNameOrKey.schemaKey;
}
const schema = await this.getSchema(schemaKey, ECObjects_1.SchemaMatchType.Latest);
if (!schema)
return undefined;
if (typeof itemNameOrCtor === "string") // Separate schema and item name arguments with/without an itemConstructor
return itemConstructor ? schema.getItem(itemNameOrCtor, itemConstructor) : schema.getItem(itemNameOrCtor);
if (typeof schemaNameOrKey === "string") // Single schema item full name argument with/without an itemConstructor
return itemNameOrCtor ? schema.lookupItem(schemaNameOrKey, itemNameOrCtor) : schema.lookupItem(schemaNameOrKey);
// Schema Item Key with/without an itemConstructor
return itemNameOrCtor ? schema.getItem(schemaNameOrKey.name, itemNameOrCtor) : schema.getItem(schemaNameOrKey.name);
}
getSchemaItemSync(schemaNameOrKey, itemNameOrCtor, itemConstructor) {
let schemaKey;
if (typeof schemaNameOrKey === "string") {
const [schemaName, itemName] = SchemaItem_1.SchemaItem.parseFullName(schemaNameOrKey);
schemaKey = (!schemaName) ? new SchemaKey_1.SchemaKey(itemName) : new SchemaKey_1.SchemaKey(schemaName);
}
else {
schemaKey = schemaNameOrKey.schemaKey;
}
const schema = this.getSchemaSync(schemaKey, ECObjects_1.SchemaMatchType.Latest);
if (!schema)
return undefined;
// Separate schema and item name arguments with/without an itemConstructor
if (typeof itemNameOrCtor === "string") {
return itemConstructor ? schema.getItemSync(itemNameOrCtor, itemConstructor) : schema.getItemSync(itemNameOrCtor);
}
if (typeof schemaNameOrKey === "string") // Single schema item full name argument with/without an itemConstructor
return itemNameOrCtor ? schema.lookupItemSync(schemaNameOrKey, itemNameOrCtor) : schema.lookupItemSync(schemaNameOrKey);
// Schema Item Key with/without an itemConstructor
return itemNameOrCtor ? schema.getItemSync(schemaNameOrKey.name, itemNameOrCtor) : schema.getItemSync(schemaNameOrKey.name);
}
/**
* Iterates through the items of each schema known to the context. This includes schemas added to the
* context using [[SchemaContext.addSchema]]. This does not include schemas that
* can be located by an ISchemaLocater instance added to the context.
* Does not include schema items from schemas that are not completely loaded yet.
*/
getSchemaItems() {
return this._knownSchemas.getSchemaItems();
}
/**
* Gets all the Schemas known by the context. This includes schemas added to the
* context using [[SchemaContext.addSchema]]. This does not include schemas that
* can be located by an ISchemaLocater instance added to the context. Does not
* include schemas that are partially loaded.
* @returns An array of Schema objects.
*/
getKnownSchemas() {
return this._knownSchemas.getAllSchemas();
}
}
exports.SchemaContext = SchemaContext;
//# sourceMappingURL=Context.js.map