@itwin/core-backend
Version:
iTwin.js backend components
154 lines • 8.15 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 Elements
*/
import { Id64 } from "@itwin/core-bentley";
import { QueryBinder, RelatedElement, traverseTextBlockComponent } from "@itwin/core-common";
import { ECVersion } from "@itwin/ecschema-metadata";
import { createUpdateContext, updateAllFields, updateElementFields, updateFields } from "../internal/annotations/fields";
import { _implicitTxn } from "../internal/Symbols";
import { ElementDrivesElement } from "../Relationship";
import { EditTxn } from "../EditTxn";
// ElementDrivesTextAnnotation was introduced in this version of BisCore - iModels with earlier versions cannot support field dependencies.
const minBisCoreVersion = new ECVersion(1, 0, 22);
/** Returns `true` if the specified `element` implements [[ITextAnnotation]].
* @beta
*/
export function isITextAnnotation(element) {
return ["getTextBlocks", "updateTextBlocks"].every((x) => x in element && typeof element[x] === "function");
}
/** A relationship in which the source element hosts one or more properties that are displayed by a target [[ITextAnnotation]] element.
* This relationship is used to automatically update the [FieldRun]($common)s contained in the target element when the source element is modified.
* An [[ITextAnnotation]] element should invoke [[updateFieldDependencies]] from its [[Element.onInserted]] and [[Element.onUpdated]] functions to
* establish or update the relationships required for the [FieldRun]($common)s it contains.
* @note This relationship was introduced in version 01.00.22 of the BisCore schema. [FieldRun]($common)s created in iModels that have not been upgraded to
* that version or newer will not automatically update. Use [[isSupportedForIModel]] to check.
* @beta
*/
export class ElementDrivesTextAnnotation extends ElementDrivesElement {
static get className() { return "ElementDrivesTextAnnotation"; }
static updateFieldDependenciesImpl(txn, annotationElementId) {
const iModel = txn.iModel;
const annotationElement = iModel.elements.tryGetElement(annotationElementId);
if (!annotationElement || !isITextAnnotation(annotationElement)) {
return;
}
// The native layer will allow us to insert relationships to invalid or non-existent source elements...errors will arise later. Prevent it.
function isValidSourceId(id) {
if (!Id64.isValidId64(id)) {
return false;
}
return iModel.withQueryReader("SELECT CodeValue FROM BisCore.Element WHERE ECInstanceId=?", (reader) => {
return reader.step();
}, new QueryBinder().bindId(1, id));
}
const sourceToRelationship = new Map();
const blocks = annotationElement.getTextBlocks();
let haveFields = false;
for (const block of blocks) {
for (const { child } of traverseTextBlockComponent(block.textBlock)) {
if (child.type === "field") {
haveFields = true;
if (isValidSourceId(child.propertyHost.elementId)) {
sourceToRelationship.set(child.propertyHost.elementId, null);
}
}
}
}
if (haveFields) {
iModel.requireMinimumSchemaVersion("BisCore", minBisCoreVersion, "Text fields");
updateAllFields(annotationElementId, txn);
}
const staleRelationships = new Set();
if (this.isSupportedForIModel(iModel)) {
annotationElement.iModel.withQueryReader("SELECT ECInstanceId, SourceECInstanceId FROM BisCore.ElementDrivesTextAnnotation WHERE TargetECInstanceId=:targetId", (reader) => {
for (const row of reader) {
const relationshipId = row[0];
const sourceId = row[1];
if (sourceToRelationship.has(sourceId)) {
sourceToRelationship.set(sourceId, relationshipId);
}
else {
staleRelationships.add(relationshipId);
}
}
}, new QueryBinder().bindId("targetId", annotationElement.id));
}
for (const [sourceId, relationshipId] of sourceToRelationship) {
if (relationshipId === null) {
txn.insertRelationship(ElementDrivesTextAnnotation.create(annotationElement.iModel, sourceId, annotationElement.id).toJSON());
}
}
if (staleRelationships.size > 0) {
const staleRelationshipProps = Array.from(staleRelationships).map((relationshipId) => annotationElement.iModel.relationships.getInstanceProps("BisCore.ElementDrivesTextAnnotation", relationshipId));
txn.deleteRelationships(staleRelationshipProps);
}
}
/** @internal */
static onRootChangedArg(arg) {
updateElementFields(arg.props, arg.indirectEditTxn, false);
}
/** @internal */
static onDeletedDependencyArg(arg) {
updateElementFields(arg.props, arg.indirectEditTxn, true);
}
/** Returns true if `iModel` contains a version of the BisCore schema new enough to support this relationship.
* If not, the schema should be updated before inserting any [FieldRun]($common)s, or those runs will not
* update when the source element changes.
*/
static isSupportedForIModel(iModel) {
return iModel.meetsMinimumSchemaVersion("BisCore", minBisCoreVersion);
}
static updateFieldDependencies(arg1, arg2) {
if (arg1 instanceof EditTxn) {
this.updateFieldDependenciesImpl(arg1, arg2);
return;
}
this.updateFieldDependenciesImpl(arg2[_implicitTxn], arg1);
}
/** Recompute the display strings of all [FieldRun]($common)s in a [TextBlock]($common).
* @returns the number of fields whose display strings were modified.
* @throws Error if evaluation of any field fails.
*/
static evaluateFields(args) {
return updateFields(args.block, createUpdateContext(undefined, args.iModel, false));
}
/** When copying an [[ITextAnnotation]] from one iModel into another, remaps the element Ids in any [FieldPropertyHost]($common) within the cloned element
* so that they refer to elements in the `context`'s target iModel, and sets any Ids that cannot be remapped to [Id64.invalid]($bentley).
* Implementations of `ITextAnnotation` should invoke this function from their implementations of [[Element.onCloned]].
*/
static remapFields(clone, context) {
if (!context.isBetweenIModels) {
return;
}
const updatedBlocks = [];
for (const block of clone.getTextBlocks()) {
let anyUpdated = false;
for (const { child } of traverseTextBlockComponent(block.textBlock)) {
if (child.type === "field") {
child.propertyHost.elementId = context.findTargetElementId(child.propertyHost.elementId);
anyUpdated = true;
}
}
if (anyUpdated) {
updatedBlocks.push(block);
}
}
if (updatedBlocks.length > 0) {
clone.updateTextBlocks(updatedBlocks);
}
}
}
/** Relationship indicating that the [[AnnotationTextStyle]] is being used as the default style for the [[ITextAnnotation]].
* @beta
*/
export class TextAnnotationUsesTextStyleByDefault extends RelatedElement {
static classFullName = "BisCore:TextAnnotationUsesTextStyleByDefault";
constructor(annotationTextStyleId, relClassName = TextAnnotationUsesTextStyleByDefault.classFullName) {
super({ id: annotationTextStyleId, relClassName });
}
}
//# sourceMappingURL=ElementDrivesTextAnnotation.js.map