@itwin/ecschema-metadata
Version:
ECObjects core concepts in typescript
293 lines • 12.8 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Metadata
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MutableEntityClass = exports.EntityClass = void 0;
exports.createNavigationProperty = createNavigationProperty;
exports.createNavigationPropertySync = createNavigationPropertySync;
const DelayedPromise_1 = require("../DelayedPromise");
const XmlSerializationUtils_1 = require("../Deserialization/XmlSerializationUtils");
const ECObjects_1 = require("../ECObjects");
const Exception_1 = require("../Exception");
const Class_1 = require("./Class");
const Mixin_1 = require("./Mixin");
const Property_1 = require("./Property");
const RelationshipClass_1 = require("./RelationshipClass");
/**
* A Typescript class representation of an ECEntityClass.
* @public @preview
*/
class EntityClass extends Class_1.ECClass {
schemaItemType = EntityClass.schemaItemType;
/** @internal */
static get schemaItemType() { return ECObjects_1.SchemaItemType.EntityClass; }
_mixins;
get mixins() {
if (!this._mixins)
return [];
return this._mixins;
}
*getMixinsSync() {
if (!this._mixins)
return;
for (const mixin of this._mixins) {
const mObj = this.schema.lookupItemSync(mixin, Mixin_1.Mixin);
if (mObj) {
yield mObj;
}
}
}
/**
*
* @param mixin
* @internal
*/
addMixin(mixin) {
if (!this._mixins)
this._mixins = [];
this.schema.context.classHierarchy.addBaseClass(this.key, mixin.key, true);
this._mixins.push(new DelayedPromise_1.DelayedPromiseWithProps(mixin.key, async () => mixin));
return;
}
/**
* Searches the base class, if one exists, first then any mixins that exist for the property with the name provided.
* @param name The name of the property to find.
*/
async getInheritedProperty(name) {
let inheritedProperty = await super.getInheritedProperty(name);
if (!inheritedProperty && this._mixins) {
const mixinProps = await Promise.all(this._mixins.map(async (mixin) => (await mixin).getProperty(name)));
mixinProps.some((prop) => {
inheritedProperty = prop;
return inheritedProperty !== undefined;
});
}
return inheritedProperty;
}
/**
* Searches the base class, if one exists, first then any mixins that exist for the property with the name provided.
* @param name The name of the property to find.
*/
getInheritedPropertySync(name) {
const inheritedProperty = super.getInheritedPropertySync(name);
if (inheritedProperty)
return inheritedProperty;
if (!this._mixins) {
return undefined;
}
for (const mixin of this._mixins) {
const mObj = this.schema.lookupItemSync(mixin);
if (mObj && Class_1.ECClass.isECClass(mObj)) {
const result = mObj.getPropertySync(name, true);
if (result) {
return result;
}
}
}
return undefined;
}
/**
*
* @param cache
* @returns
*
* @internal
*/
async buildPropertyCache() {
const cache = new Map();
const baseClass = await this.baseClass;
if (baseClass) {
for (const property of await baseClass.getProperties()) {
if (!cache.has(property.name.toUpperCase()))
cache.set(property.name.toUpperCase(), property);
}
}
for (const mixin of this.mixins) {
const mixinObj = await mixin;
const mixinProps = mixinObj.getPropertiesSync();
for (const property of mixinProps) {
if (!cache.has(property.name.toUpperCase()))
cache.set(property.name.toUpperCase(), property);
}
}
const localProps = await this.getProperties(true);
if (localProps) {
for (const property of localProps) {
cache.set(property.name.toUpperCase(), property);
}
}
return cache;
}
/**
*
* @param cache
* @internal
*/
buildPropertyCacheSync() {
const cache = new Map();
const baseClass = this.getBaseClassSync();
if (baseClass) {
Array.from(baseClass.getPropertiesSync()).forEach((property) => {
if (!cache.has(property.name.toUpperCase()))
cache.set(property.name.toUpperCase(), property);
});
}
for (const mixin of this.getMixinsSync()) {
const mixinProps = mixin.getPropertiesSync();
for (const property of mixinProps) {
if (!cache.has(property.name.toUpperCase()))
cache.set(property.name.toUpperCase(), property);
}
}
const localProps = this.getPropertiesSync(true);
if (localProps) {
Array.from(localProps).forEach(property => {
cache.set(property.name.toUpperCase(), property);
});
}
return cache;
}
/**
*
* @param name
* @param relationship
* @param direction
* @internal
*/
async createNavigationProperty(name, relationship, direction) {
return this.addProperty(await createNavigationProperty(this, name, relationship, direction));
}
/**
*
* @param name
* @param relationship
* @param direction
* @internal
*/
createNavigationPropertySync(name, relationship, direction) {
return this.addProperty(createNavigationPropertySync(this, name, relationship, direction));
}
/**
* Save this EntityClass' properties to an object for serializing to JSON.
* @param standalone Serialization includes only this object (as opposed to the full schema).
* @param includeSchemaVersion Include the Schema's version information in the serialized object.
*/
toJSON(standalone = false, includeSchemaVersion = false) {
const schemaJson = super.toJSON(standalone, includeSchemaVersion);
if (this.mixins.length > 0)
schemaJson.mixins = this.mixins.map((mixin) => mixin.fullName);
return schemaJson;
}
/** @internal */
async toXml(schemaXml) {
const itemElement = await super.toXml(schemaXml);
for (const lazyMixin of this.mixins) {
const mixin = await lazyMixin;
const mixinElement = schemaXml.createElement("BaseClass");
const mixinName = XmlSerializationUtils_1.XmlSerializationUtils.createXmlTypedName(this.schema, mixin.schema, mixin.name);
mixinElement.textContent = mixinName;
itemElement.appendChild(mixinElement);
}
return itemElement;
}
async fromJSON(entityClassProps) {
this.fromJSONSync(entityClassProps);
}
fromJSONSync(entityClassProps) {
super.fromJSONSync(entityClassProps);
if (undefined !== entityClassProps.mixins) {
if (!this._mixins)
this._mixins = [];
for (const name of entityClassProps.mixins) {
const mixinSchemaItemKey = this.schema.getSchemaItemKey(name);
if (!mixinSchemaItemKey)
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.InvalidECJson, `The ECEntityClass ${this.name} has a mixin ("${name}") that cannot be found.`);
if (!this._mixins.find((value) => mixinSchemaItemKey.matchesFullName(value.fullName))) {
this.schema.context.classHierarchy.addBaseClass(this.key, mixinSchemaItemKey, true);
this._mixins.push(new DelayedPromise_1.DelayedPromiseWithProps(mixinSchemaItemKey, async () => {
const mixin = await this.schema.lookupItem(mixinSchemaItemKey, Mixin_1.Mixin);
if (undefined === mixin)
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.InvalidECJson, `The ECEntityClass ${this.name} has a mixin ("${name}") that cannot be found.`);
return mixin;
}));
}
}
}
}
/**
* Type guard to check if the SchemaItem is of type EntityClass.
* @param item The SchemaItem to check.
* @returns True if the item is an EntityClass, false otherwise.
*/
static isEntityClass(item) {
if (item && item.schemaItemType === ECObjects_1.SchemaItemType.EntityClass)
return true;
return false;
}
/**
* Type assertion to check if the SchemaItem is of type EntityClass.
* @param item The SchemaItem to check.
* @returns The item cast to EntityClass if it is an EntityClass, undefined otherwise.
* @internal
*/
static assertIsEntityClass(item) {
if (!this.isEntityClass(item))
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.InvalidSchemaItemType, `Expected '${ECObjects_1.SchemaItemType.EntityClass}' (EntityClass)`);
}
}
exports.EntityClass = EntityClass;
/**
* Hackish approach that works like a "friend class" so we can access protected members without making them public.
* @internal
*/
class MutableEntityClass extends EntityClass {
}
exports.MutableEntityClass = MutableEntityClass;
/** @internal */
async function createNavigationProperty(ecClass, name, relationship, direction) {
if (await ecClass.getProperty(name, true))
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.DuplicateProperty, `An ECProperty with the name ${name} already exists in the class ${ecClass.name}.`);
let resolvedRelationship;
if (typeof (relationship) === "string") {
resolvedRelationship = await ecClass.schema.lookupItem(relationship, RelationshipClass_1.RelationshipClass);
}
else
resolvedRelationship = relationship;
if (!resolvedRelationship)
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.InvalidType, `The provided RelationshipClass, ${relationship}, is not a valid RelationshipClassInterface.`); // eslint-disable-line @typescript-eslint/no-base-to-string
if (typeof (direction) === "string") {
const tmpDirection = (0, ECObjects_1.parseStrengthDirection)(direction);
if (undefined === tmpDirection)
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.InvalidStrengthDirection, `The provided StrengthDirection, ${direction}, is not a valid StrengthDirection.`);
direction = tmpDirection;
}
const lazyRelationship = new DelayedPromise_1.DelayedPromiseWithProps(resolvedRelationship.key, async () => resolvedRelationship);
return new Property_1.NavigationProperty(ecClass, name, lazyRelationship, direction);
}
/** @internal */
function createNavigationPropertySync(ecClass, name, relationship, direction) {
if (ecClass.getPropertySync(name, true))
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.DuplicateProperty, `An ECProperty with the name ${name} already exists in the class ${ecClass.name}.`);
let resolvedRelationship;
if (typeof (relationship) === "string") {
resolvedRelationship = ecClass.schema.lookupItemSync(relationship, RelationshipClass_1.RelationshipClass);
}
else
resolvedRelationship = relationship;
if (!resolvedRelationship)
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.InvalidType, `The provided RelationshipClass, ${relationship}, is not a valid RelationshipClassInterface.`); // eslint-disable-line @typescript-eslint/no-base-to-string
if (typeof (direction) === "string") {
const tmpDirection = (0, ECObjects_1.parseStrengthDirection)(direction);
if (undefined === tmpDirection)
throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.InvalidStrengthDirection, `The provided StrengthDirection, ${direction}, is not a valid StrengthDirection.`);
direction = tmpDirection;
}
const lazyRelationship = new DelayedPromise_1.DelayedPromiseWithProps(resolvedRelationship.key, async () => resolvedRelationship);
return new Property_1.NavigationProperty(ecClass, name, lazyRelationship, direction);
}
//# sourceMappingURL=EntityClass.js.map