@itwin/presentation-components
Version:
React components based on iTwin.js Presentation library
153 lines • 6.43 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Internal
*/
import { compareStrings, Guid, Id64, LRUDictionary } from "@itwin/core-bentley";
import { QueryBinder, QueryRowFormat } from "@itwin/core-common";
/* c8 ignore start */
/** @internal */
export class ECClassInfo {
id;
name;
label;
_baseClasses;
_derivedClasses;
constructor(id, name, label, _baseClasses, _derivedClasses) {
this.id = id;
this.name = name;
this.label = label;
this._baseClasses = _baseClasses;
this._derivedClasses = _derivedClasses;
}
get baseClassIds() {
return Array.from(this._baseClasses);
}
get derivedClassIds() {
return Array.from(this._derivedClasses);
}
isBaseOf(idOrInfo) {
if (typeof idOrInfo === "string") {
return idOrInfo === this.id || this._derivedClasses.has(idOrInfo);
}
return idOrInfo.id === this.id || this._derivedClasses.has(idOrInfo.id);
}
isDerivedFrom(idOrInfo) {
if (typeof idOrInfo === "string") {
return idOrInfo === this.id || this._baseClasses.has(idOrInfo);
}
return idOrInfo.id === this.id || this._baseClasses.has(idOrInfo.id);
}
}
/** @internal */
export class ECMetadataProvider {
_queryReaderFactory;
_classInfoCache = new LRUDictionary(50, compareKeys);
#componentId;
#componentName;
constructor(_queryReaderFactory) {
this._queryReaderFactory = _queryReaderFactory;
this.#componentId = Guid.createValue();
this.#componentName = "ECMetadataProvider";
}
async getECClassInfo(idNameOrSchema, className) {
// load class info using class id
if (Id64.isId64(idNameOrSchema)) {
return this.getClassInfoById(idNameOrSchema);
}
// load class info using class full name: <schemaName>:<className>
const fullName = className ? `${idNameOrSchema}:${className}` : idNameOrSchema;
return this.getClassInfoByFullName(fullName);
}
async getClassInfoById(id) {
let classInfo = this._classInfoCache.get({ id, name: "" });
if (!classInfo) {
const classQuery = `
${classQueryBase}
WHERE classDef.ECInstanceId = :id
`;
classInfo = await this.createECClassInfo(this._queryReaderFactory(classQuery, QueryBinder.from({ id }), {
rowFormat: QueryRowFormat.UseJsPropertyNames,
restartToken: `${this.#componentName}/${this.#componentId}/class-by-id/${id}`,
}));
classInfo && this._classInfoCache.set({ id: classInfo.id, name: classInfo.name }, classInfo);
}
return classInfo;
}
async getClassInfoByFullName(name) {
let classInfo = this._classInfoCache.get({ id: "", name });
if (!classInfo) {
const classQuery = `
${classQueryBase}
WHERE classDef.Name = :className AND schemaDef.Name = :schemaName
`;
const [schemaName, className] = this.splitFullClassName(name);
classInfo = await this.createECClassInfo(this._queryReaderFactory(classQuery, QueryBinder.from({ schemaName, className }), {
rowFormat: QueryRowFormat.UseJsPropertyNames,
restartToken: `${this.#componentName}/${this.#componentId}/class-by-name/${name}`,
}));
classInfo && this._classInfoCache.set({ id: classInfo.id, name: classInfo.name }, classInfo);
}
return classInfo;
}
async createECClassInfo(reader) {
while (await reader.step()) {
const classHierarchy = await this.queryClassHierarchyInfo(reader.current.id);
return new ECClassInfo(reader.current.id, reader.current.name, reader.current.label, classHierarchy.baseClasses, classHierarchy.derivedClasses);
}
return undefined;
}
async queryClassHierarchyInfo(id) {
const classHierarchyQuery = `
SELECT chc.TargetECInstanceId baseId, chc.SourceECInstanceId derivedId
FROM meta.ClassHasAllBaseClasses chc
WHERE chc.SourceECInstanceId = :id OR chc.TargetECInstanceId = :id
`;
const hierarchy = { baseClasses: new Set(), derivedClasses: new Set() };
const reader = this._queryReaderFactory(classHierarchyQuery, QueryBinder.from({ id }), {
rowFormat: QueryRowFormat.UseJsPropertyNames,
restartToken: `${this.#componentName}/${this.#componentId}/class-hierarchy/${id}`,
});
while (await reader.step()) {
if (reader.current.baseId === id) {
hierarchy.derivedClasses.add(reader.current.derivedId);
}
if (reader.current.derivedId === id) {
hierarchy.baseClasses.add(reader.current.baseId);
}
}
return hierarchy;
}
splitFullClassName(fullName) {
const [schemaName, className] = fullName.split(fullName.includes(".") ? "." : ":");
return [schemaName, className];
}
}
const classQueryBase = `
SELECT classDef.ECInstanceId id, (schemaDef.Name || ':' || classDef.Name) name, COALESCE(classDef.DisplayLabel, classDef.name) label
FROM meta.ECClassDef classDef
JOIN meta.ECSchemaDef schemaDef ON classDef.Schema.Id = schemaDef.ECInstanceId
`;
const metadataProviders = new Map();
/** @internal */
export function getIModelMetadataProvider(imodel) {
let metadataProvider = metadataProviders.get(imodel.key);
if (!metadataProvider) {
metadataProvider = new ECMetadataProvider((ecsql, params, config) => imodel.createQueryReader(ecsql, params, config));
metadataProviders.set(imodel.key, metadataProvider);
/* c8 ignore next 3 */
imodel.onClose.addOnce(() => {
metadataProviders.delete(imodel.key);
});
}
return metadataProvider;
}
function compareKeys(lhs, rhs) {
if (lhs.id.length !== 0 && rhs.id.length !== 0) {
return compareStrings(lhs.id, rhs.id);
}
return compareStrings(lhs.name, rhs.name);
}
//# sourceMappingURL=ECMetadataProvider.js.map