@itwin/core-backend
Version:
iTwin.js backend components
237 lines • 9.92 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.createUpdateContext = createUpdateContext;
exports.updateField = updateField;
exports.updateFields = updateFields;
exports.updateElementFields = updateElementFields;
const core_common_1 = require("@itwin/core-common");
const core_bentley_1 = require("@itwin/core-bentley");
const BackendLoggerCategory_1 = require("../../BackendLoggerCategory");
const ElementDrivesTextAnnotation_1 = require("../../annotations/ElementDrivesTextAnnotation");
const ecschema_metadata_1 = require("@itwin/ecschema-metadata");
// Resolve the raw primitive value of the property that a field points to.
function getFieldProperty(field, iModel) {
const host = field.propertyHost;
const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);
if (!ecschema_metadata_1.EntityClass.isEntityClass(schemaItem)) {
return undefined;
}
let ecClass = schemaItem;
const { propertyName, accessors } = field.propertyPath;
let ecProp = ecClass.getPropertySync(propertyName);
if (!ecProp) {
return undefined;
}
const isAspect = ecClass.isSync("ElementAspect", "BisCore");
const where = ` WHERE ${isAspect ? "Element.Id" : "ECInstanceId"}=${host.elementId}`;
// eslint-disable-next-line @typescript-eslint/no-deprecated
let curValue = iModel.withPreparedStatement(`SELECT ${propertyName} FROM ${host.schemaName}.${host.className} ${where}`, (stmt) => {
if (stmt.step() !== core_bentley_1.DbResult.BE_SQLITE_ROW) {
return undefined;
}
const rootValue = stmt.getValue(0);
if (undefined === rootValue || rootValue.isNull) {
return undefined;
}
switch (rootValue.columnInfo.getType()) {
case core_common_1.ECSqlValueType.Blob:
return { primitive: rootValue.getBlob() };
case core_common_1.ECSqlValueType.Boolean:
return { primitive: rootValue.getBoolean() };
case core_common_1.ECSqlValueType.DateTime:
return { primitive: rootValue.getDateTime() };
case core_common_1.ECSqlValueType.Double:
return { primitive: rootValue.getDouble() };
case core_common_1.ECSqlValueType.Guid:
return { primitive: rootValue.getGuid() };
case core_common_1.ECSqlValueType.Int:
case core_common_1.ECSqlValueType.Int64:
return { primitive: rootValue.getInteger() };
case core_common_1.ECSqlValueType.Point2d:
return { primitive: rootValue.getXAndY() };
case core_common_1.ECSqlValueType.Point3d:
return { primitive: rootValue.getXYAndZ() };
case core_common_1.ECSqlValueType.String:
return { primitive: rootValue.getString() };
case core_common_1.ECSqlValueType.Struct: {
(0, core_bentley_1.assert)(ecProp.isStruct());
ecClass = ecProp.structClass;
return { struct: rootValue.getStruct() };
}
case core_common_1.ECSqlValueType.PrimitiveArray: {
return { primitiveArray: rootValue.getArray() };
}
case core_common_1.ECSqlValueType.StructArray: {
return { structArray: rootValue.getArray() };
}
// Unsupported:
// case ECSqlValueType.Geometry:
// case ECSqlValueType.Navigation:
// case ECSqlValueType.Id:
}
return undefined;
});
if (undefined === curValue) {
return undefined;
}
if (accessors) {
for (const accessor of accessors) {
if (undefined !== curValue.primitive) {
// Can't index into a primitive.
return undefined;
}
if (typeof accessor === "number") {
const array = curValue.primitiveArray ?? curValue.structArray;
if (!array) {
return undefined;
}
const index = accessor < 0 ? (array.length + accessor) : accessor;
const item = array[index];
if (undefined === item) {
return undefined;
}
else if (curValue.primitiveArray) {
curValue = { primitive: curValue.primitiveArray[index] };
}
else {
(0, core_bentley_1.assert)(undefined !== curValue.structArray);
(0, core_bentley_1.assert)(ecProp instanceof ecschema_metadata_1.StructArrayProperty);
ecClass = ecProp.structClass;
curValue = { struct: curValue.structArray[index] };
}
}
else {
if (undefined === curValue.struct) {
return undefined;
}
const item = curValue.struct[accessor];
if (undefined === item) {
return undefined;
}
ecProp = ecClass.getPropertySync(accessor);
if (!ecProp) {
return undefined;
}
if (ecProp.isArray()) {
curValue = ecProp.isStruct() ? { structArray: item } : { primitiveArray: item };
}
else if (ecProp.isStruct()) {
ecClass = ecProp.structClass;
curValue = { struct: item };
}
else if (ecProp.isPrimitive()) {
curValue = { primitive: item };
}
else {
return undefined;
}
}
}
}
if (field.propertyPath.jsonAccessors) {
if (!ecProp.isPrimitive() || ecProp.isArray() || ecProp.extendedTypeName !== "Json" || typeof curValue.primitive !== "string") {
return undefined;
}
let json = JSON.parse(curValue.primitive);
for (const accessor of field.propertyPath.jsonAccessors) {
if (typeof accessor === "number") {
if (!Array.isArray(json)) {
return undefined;
}
json = json[accessor < 0 ? json.length + accessor : accessor];
}
else {
if (typeof json !== "object" || json === null) {
return undefined;
}
json = json[accessor];
}
}
switch (typeof json) {
case "string":
case "number":
case "boolean":
curValue = { primitive: json };
break;
default:
return undefined;
}
}
// The ultimate result must be a primitive value.
if (undefined === curValue.primitive) {
return undefined;
}
return {
value: curValue.primitive,
metadata: { property: ecProp },
};
}
function createUpdateContext(hostElementId, iModel, deleted) {
return {
hostElementId,
getProperty: deleted ? () => undefined : (field) => getFieldProperty(field, iModel),
};
}
// Recompute the display value of a single field, return false if it couldn't be evaluated.
function updateField(field, context) {
if (context.hostElementId !== field.propertyHost.elementId) {
return false;
}
let newContent;
try {
const prop = context.getProperty(field);
if (undefined !== prop) {
// ###TODO formatting etc.
// eslint-disable-next-line @typescript-eslint/no-base-to-string
newContent = prop.value.toString();
}
}
catch (err) {
core_bentley_1.Logger.logException(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, err);
}
newContent = newContent ?? core_common_1.FieldRun.invalidContentIndicator;
if (newContent === field.cachedContent) {
return false;
}
field.setCachedContent(newContent);
return true;
}
// Re-evaluates the display strings for all fields that target the element specified by `context` and returns the number
// of fields whose display strings changed as a result.
function updateFields(textBlock, context) {
let numUpdated = 0;
for (const paragraph of textBlock.paragraphs) {
for (const run of paragraph.runs) {
if (run.type === "field" && updateField(run, context)) {
++numUpdated;
}
}
}
return numUpdated;
}
// Invoked by ElementDrivesTextAnnotation to update fields in target element when source element changes or is deleted.
function updateElementFields(props, iModel, deleted) {
try {
const target = iModel.elements.getElement(props.targetId);
if ((0, ElementDrivesTextAnnotation_1.isITextAnnotation)(target)) {
const context = createUpdateContext(props.sourceId, iModel, deleted);
const updatedBlocks = [];
for (const block of target.getTextBlocks()) {
if (updateFields(block.textBlock, context)) {
updatedBlocks.push(block);
}
}
if (updatedBlocks.length > 0) {
target.updateTextBlocks(updatedBlocks);
}
}
}
catch (err) {
core_bentley_1.Logger.logException(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, err);
}
}
//# sourceMappingURL=fields.js.map
;