Skip to content

Commit 160a28f

Browse files
pmconnekhanaffan
andauthored
Make element cloning async (#8615)
Co-authored-by: affank <khanaffan@gmail.com>
1 parent 727e0e3 commit 160a28f

File tree

12 files changed

+160
-72
lines changed

12 files changed

+160
-72
lines changed

common/api/core-backend.api.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,11 @@ export class AnnotationTextStyle extends DefinitionElement {
326326
description?: string;
327327
static deserialize(props: DeserializeEntityArgs): AnnotationTextStyleProps;
328328
static fromJSON(props: AnnotationTextStyleProps, iModel: IModelDb): AnnotationTextStyle;
329+
// (undocumented)
330+
protected static onCloned(context: IModelElementCloneContext, srcProps: AnnotationTextStyleProps, dstProps: AnnotationTextStyleProps): Promise<void>;
329331
protected static onInsert(arg: OnElementPropsArg): void;
330332
protected static onUpdate(arg: OnElementPropsArg): void;
331-
static remapTextStyleId(sourceTextStyleId: Id64String, context: IModelElementCloneContext): Id64String;
333+
static remapTextStyleId(sourceTextStyleId: Id64String, context: IModelElementCloneContext): Promise<Id64String>;
332334
static serialize(props: AnnotationTextStyleProps, iModel: IModelDb): ECSqlRow;
333335
settings: TextStyleSettings;
334336
toJSON(): AnnotationTextStyleProps;
@@ -1900,7 +1902,7 @@ export abstract class DisplayStyle extends DefinitionElement {
19001902
// (undocumented)
19011903
loadScheduleScript(): RenderSchedule.ScriptReference | undefined;
19021904
// @alpha (undocumented)
1903-
protected static onCloned(context: IModelElementCloneContext, sourceElementProps: DisplayStyleProps, targetElementProps: DisplayStyleProps): void;
1905+
protected static onCloned(context: IModelElementCloneContext, sourceElementProps: DisplayStyleProps, targetElementProps: DisplayStyleProps): Promise<void>;
19041906
// @beta (undocumented)
19051907
static serialize(props: DisplayStyleProps, iModel: IModelDb): ECSqlRow;
19061908
// (undocumented)
@@ -1928,7 +1930,7 @@ export class DisplayStyle3d extends DisplayStyle {
19281930
static create(iModelDb: IModelDb, definitionModelId: Id64String, name: string, options?: DisplayStyleCreationOptions): DisplayStyle3d;
19291931
static insert(iModelDb: IModelDb, definitionModelId: Id64String, name: string, options?: DisplayStyleCreationOptions): Id64String;
19301932
// @alpha (undocumented)
1931-
protected static onCloned(context: IModelElementCloneContext, sourceElementProps: DisplayStyle3dProps, targetElementProps: DisplayStyle3dProps): void;
1933+
protected static onCloned(context: IModelElementCloneContext, sourceElementProps: DisplayStyle3dProps, targetElementProps: DisplayStyle3dProps): Promise<void>;
19321934
// (undocumented)
19331935
get settings(): DisplayStyle3dSettings;
19341936
}
@@ -2449,7 +2451,7 @@ class Element_2 extends Entity {
24492451
// @beta
24502452
protected static onChildUpdated(arg: OnChildElementIdArg): void;
24512453
// @beta
2452-
protected static onCloned(_context: IModelElementCloneContext, _sourceProps: ElementProps, _targetProps: ElementProps): void;
2454+
protected static onCloned(_context: IModelElementCloneContext, _sourceProps: ElementProps, _targetProps: ElementProps): Promise<void> | void;
24532455
// @beta
24542456
protected static onDelete(arg: OnElementIdArg): void;
24552457
// @beta
@@ -3869,7 +3871,7 @@ export class IModelElementCloneContext {
38693871
[Symbol.dispose](): void;
38703872
constructor(sourceDb: IModelDb, targetDb?: IModelDb);
38713873
// @internal
3872-
cloneElement(sourceElement: Element_2, cloneOptions?: IModelJsNative.CloneElementOptions): ElementProps;
3874+
cloneElement(sourceElement: Element_2, cloneOptions?: IModelJsNative.CloneElementOptions): Promise<ElementProps>;
38733875
static create(...args: ConstructorParameters<typeof IModelElementCloneContext>): Promise<IModelElementCloneContext>;
38743876
// @deprecated (undocumented)
38753877
dispose(): void;
@@ -5281,7 +5283,7 @@ export class RenderMaterialElement extends DefinitionElement {
52815283
static deserialize(props: DeserializeEntityArgs): RenderMaterialProps;
52825284
static insert(iModelDb: IModelDb, definitionModelId: Id64String, materialName: string, params: RenderMaterialElementParams): Id64String;
52835285
// @beta (undocumented)
5284-
protected static onCloned(context: IModelElementCloneContext, sourceProps: ElementProps, targetProps: ElementProps): void;
5286+
protected static onCloned(context: IModelElementCloneContext, sourceProps: ElementProps, targetProps: ElementProps): Promise<void>;
52855287
paletteName: string;
52865288
// @beta
52875289
static serialize(props: RenderMaterialProps, iModel: IModelDb): ECSqlRow;
@@ -5335,7 +5337,7 @@ export class RenderTimeline extends InformationRecordElement {
53355337
// (undocumented)
53365338
static fromJSON(props: RenderTimelineProps, iModel: IModelDb): RenderTimeline;
53375339
// @alpha (undocumented)
5338-
protected static onCloned(context: IModelElementCloneContext, sourceProps: RenderTimelineProps, targetProps: RenderTimelineProps): void;
5340+
protected static onCloned(context: IModelElementCloneContext, sourceProps: RenderTimelineProps, targetProps: RenderTimelineProps): Promise<void>;
53395341
// @beta
53405342
static remapScript(context: IModelElementCloneContext, input: RenderSchedule.ScriptProps): RenderSchedule.ScriptProps;
53415343
scriptProps: RenderSchedule.ScriptProps;
@@ -6461,7 +6463,7 @@ export class TextAnnotation2d extends AnnotationElement2d {
64616463
// @internal (undocumented)
64626464
getTextBlocks(): Iterable<TextBlockAndId>;
64636465
// @internal (undocumented)
6464-
protected static onCloned(context: IModelElementCloneContext, srcProps: TextAnnotation2dProps, dstProps: TextAnnotation2dProps): void;
6466+
protected static onCloned(context: IModelElementCloneContext, srcProps: TextAnnotation2dProps, dstProps: TextAnnotation2dProps): Promise<void>;
64656467
// @beta
64666468
protected static onInsert(arg: OnElementPropsArg): void;
64676469
// @internal (undocumented)
@@ -6510,7 +6512,7 @@ export class TextAnnotation3d extends GraphicalElement3d {
65106512
// @internal (undocumented)
65116513
getTextBlocks(): Iterable<TextBlockAndId>;
65126514
// @internal (undocumented)
6513-
protected static onCloned(context: IModelElementCloneContext, srcProps: TextAnnotation3dProps, dstProps: TextAnnotation3dProps): void;
6515+
protected static onCloned(context: IModelElementCloneContext, srcProps: TextAnnotation3dProps, dstProps: TextAnnotation3dProps): Promise<void>;
65146516
// @beta
65156517
protected static onInsert(arg: OnElementPropsArg): void;
65166518
// @internal (undocumented)
@@ -6952,7 +6954,7 @@ export abstract class ViewDefinition extends DefinitionElement {
69526954
loadCategorySelector(): CategorySelector;
69536955
loadDisplayStyle(): DisplayStyle;
69546956
// @beta (undocumented)
6955-
protected static onCloned(context: IModelElementCloneContext, sourceElementProps: ViewDefinitionProps, targetElementProps: ViewDefinitionProps): void;
6957+
protected static onCloned(context: IModelElementCloneContext, sourceElementProps: ViewDefinitionProps, targetElementProps: ViewDefinitionProps): Promise<void>;
69566958
// @beta (undocumented)
69576959
static readonly requiredReferenceKeys: ReadonlyArray<string>;
69586960
// @alpha (undocumented)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@itwin/core-backend",
5+
"comment": "Element.onCloned can now invoke asynchronous operations.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@itwin/core-backend"
10+
}

core/backend/src/DisplayStyle.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ export abstract class DisplayStyle extends DefinitionElement {
8787
}
8888

8989
/** @alpha */
90-
protected static override onCloned(context: IModelElementCloneContext, sourceElementProps: DisplayStyleProps, targetElementProps: DisplayStyleProps): void {
91-
super.onCloned(context, sourceElementProps, targetElementProps);
90+
protected static override async onCloned(context: IModelElementCloneContext, sourceElementProps: DisplayStyleProps, targetElementProps: DisplayStyleProps): Promise<void> {
91+
await super.onCloned(context, sourceElementProps, targetElementProps);
9292

9393
if (!context.isBetweenIModels || !targetElementProps.jsonProperties?.styles)
9494
return;
@@ -252,8 +252,8 @@ export class DisplayStyle3d extends DisplayStyle {
252252
}
253253

254254
/** @alpha */
255-
protected static override onCloned(context: IModelElementCloneContext, sourceElementProps: DisplayStyle3dProps, targetElementProps: DisplayStyle3dProps): void {
256-
super.onCloned(context, sourceElementProps, targetElementProps);
255+
protected static override async onCloned(context: IModelElementCloneContext, sourceElementProps: DisplayStyle3dProps, targetElementProps: DisplayStyle3dProps): Promise<void> {
256+
await super.onCloned(context, sourceElementProps, targetElementProps);
257257
if (context.isBetweenIModels) {
258258
const convertTexture = (id: string) => Id64.isValidId64(id) ? context.findTargetElementId(id) : id;
259259

core/backend/src/Element.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ export class Element extends Entity {
387387
* @note If you override this method, you must call super.
388388
* @beta
389389
*/
390-
protected static onCloned(_context: IModelElementCloneContext, _sourceProps: ElementProps, _targetProps: ElementProps): void { }
390+
protected static onCloned(_context: IModelElementCloneContext, _sourceProps: ElementProps, _targetProps: ElementProps): Promise<void> | void { }
391391

392392
/** Called when a *root* element in a subgraph is changed and before its outputs are processed.
393393
* This special callback is made when:
@@ -2167,8 +2167,8 @@ export class RenderTimeline extends InformationRecordElement {
21672167
}
21682168

21692169
/** @alpha */
2170-
protected static override onCloned(context: IModelElementCloneContext, sourceProps: RenderTimelineProps, targetProps: RenderTimelineProps): void {
2171-
super.onCloned(context, sourceProps, targetProps);
2170+
protected static override async onCloned(context: IModelElementCloneContext, sourceProps: RenderTimelineProps, targetProps: RenderTimelineProps): Promise<void> {
2171+
await super.onCloned(context, sourceProps, targetProps);
21722172
if (context.isBetweenIModels)
21732173
targetProps.script = JSON.stringify(this.remapScript(context, this.parseScriptProps(targetProps.script)));
21742174
}

core/backend/src/IModelElementCloneContext.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export class IModelElementCloneContext {
148148
/** Clone the specified source Element into ElementProps for the target iModel.
149149
* @internal
150150
*/
151-
public cloneElement(sourceElement: Element, cloneOptions?: IModelJsNative.CloneElementOptions): ElementProps {
151+
public async cloneElement(sourceElement: Element, cloneOptions?: IModelJsNative.CloneElementOptions): Promise<ElementProps> {
152152
const targetElementProps: ElementProps = this._nativeContext.cloneElement(sourceElement.id, cloneOptions);
153153
// Ensure that all NavigationProperties in targetElementProps have a defined value so "clearing" changes will be part of the JSON used for update
154154
// eslint-disable-next-line @typescript-eslint/no-deprecated
@@ -170,7 +170,7 @@ export class IModelElementCloneContext {
170170
}
171171
const jsClass = this.sourceDb.getJsClass<typeof Element>(sourceElement.classFullName);
172172
// eslint-disable-next-line @typescript-eslint/dot-notation
173-
jsClass["onCloned"](this, sourceElement.toJSON(), targetElementProps);
173+
await jsClass["onCloned"](this, sourceElement.toJSON(), targetElementProps);
174174
return targetElementProps;
175175
}
176176

core/backend/src/Material.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ export class RenderMaterialElement extends DefinitionElement {
218218
}
219219

220220
/** @beta */
221-
protected static override onCloned(context: IModelElementCloneContext, sourceProps: ElementProps, targetProps: ElementProps) {
222-
super.onCloned(context, sourceProps, targetProps);
221+
protected static override async onCloned(context: IModelElementCloneContext, sourceProps: ElementProps, targetProps: ElementProps) {
222+
await super.onCloned(context, sourceProps, targetProps);
223223
for (const mapName in sourceProps.jsonProperties?.materialAssets?.renderMaterial?.Map ?? {}) {
224224
if (typeof mapName !== "string")
225225
continue;

core/backend/src/ViewDefinition.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,8 +312,8 @@ export abstract class ViewDefinition extends DefinitionElement {
312312
};
313313

314314
/** @beta */
315-
protected static override onCloned(context: IModelElementCloneContext, sourceElementProps: ViewDefinitionProps, targetElementProps: ViewDefinitionProps): void {
316-
super.onCloned(context, sourceElementProps, targetElementProps);
315+
protected static override async onCloned(context: IModelElementCloneContext, sourceElementProps: ViewDefinitionProps, targetElementProps: ViewDefinitionProps): Promise<void> {
316+
await super.onCloned(context, sourceElementProps, targetElementProps);
317317
if (context.isBetweenIModels && targetElementProps.jsonProperties && targetElementProps.jsonProperties.viewDetails) {
318318
const acsId: Id64String = Id64.fromJSON(targetElementProps.jsonProperties.viewDetails.acs);
319319
if (Id64.isValidId64(acsId)) {

core/backend/src/annotations/ElementDrivesTextAnnotation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export class ElementDrivesTextAnnotation extends ElementDrivesElement {
148148

149149
/** When copying an [[ITextAnnotation]] from one iModel into another, remaps the element Ids in any [FieldPropertyHost]($common) within the cloned element
150150
* so that they refer to elements in the `context`'s target iModel, and sets any Ids that cannot be remapped to [Id64.invalid]($bentley).
151-
* Implementations of `ITextAnnotation` should invoke this function from their implementations of [[Element._onCloned]].
151+
* Implementations of `ITextAnnotation` should invoke this function from their implementations of [[Element.onCloned]].
152152
*/
153153
public static remapFields(clone: ITextAnnotation, context: IModelElementCloneContext): void {
154154
if (!context.isBetweenIModels) {

core/backend/src/annotations/TextAnnotationElement.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -297,15 +297,15 @@ export class TextAnnotation2d extends AnnotationElement2d /* implements ITextAnn
297297
}
298298

299299
/** @internal */
300-
protected static override onCloned(context: IModelElementCloneContext, srcProps: TextAnnotation2dProps, dstProps: TextAnnotation2dProps): void {
301-
super.onCloned(context, srcProps, dstProps);
300+
protected static override async onCloned(context: IModelElementCloneContext, srcProps: TextAnnotation2dProps, dstProps: TextAnnotation2dProps): Promise<void> {
301+
await super.onCloned(context, srcProps, dstProps);
302302

303303
const srcElem = TextAnnotation2d.fromJSON(srcProps, context.sourceDb);
304304
ElementDrivesTextAnnotation.remapFields(srcElem, context);
305305
const anno = srcElem.getAnnotation();
306306
dstProps.textAnnotationData = anno ? JSON.stringify({ version: TEXT_ANNOTATION_JSON_VERSION, data: anno.toJSON() }) : undefined;
307307

308-
remapTextStyle(context, srcElem, dstProps);
308+
return remapTextStyle(context, srcElem, dstProps);
309309
}
310310
}
311311

@@ -484,24 +484,24 @@ export class TextAnnotation3d extends GraphicalElement3d /* implements ITextAnno
484484
}
485485

486486
/** @internal */
487-
protected static override onCloned(context: IModelElementCloneContext, srcProps: TextAnnotation3dProps, dstProps: TextAnnotation3dProps): void {
488-
super.onCloned(context, srcProps, dstProps);
487+
protected static override async onCloned(context: IModelElementCloneContext, srcProps: TextAnnotation3dProps, dstProps: TextAnnotation3dProps): Promise<void> {
488+
await super.onCloned(context, srcProps, dstProps);
489489

490490
const srcElem = TextAnnotation3d.fromJSON(srcProps, context.sourceDb);
491491
ElementDrivesTextAnnotation.remapFields(srcElem, context);
492492
const anno = srcElem.getAnnotation();
493493
dstProps.textAnnotationData = anno ? JSON.stringify({ version: TEXT_ANNOTATION_JSON_VERSION, data: anno.toJSON() }) : undefined;
494494

495-
remapTextStyle(context, srcElem, dstProps);
495+
return remapTextStyle(context, srcElem, dstProps);
496496
}
497497
}
498498

499-
function remapTextStyle(
499+
async function remapTextStyle(
500500
context: IModelElementCloneContext,
501501
srcElem: TextAnnotation2d | TextAnnotation3d,
502502
dstProps: TextAnnotation2dProps | TextAnnotation3dProps
503-
): void {
504-
const dstStyleId = AnnotationTextStyle.remapTextStyleId(srcElem.defaultTextStyle?.id ?? Id64.invalid, context);
503+
): Promise<void> {
504+
const dstStyleId = await AnnotationTextStyle.remapTextStyleId(srcElem.defaultTextStyle?.id ?? Id64.invalid, context);
505505
dstProps.defaultTextStyle = Id64.isValid(dstStyleId) ? new TextAnnotationUsesTextStyleByDefault(dstStyleId).toJSON() : undefined;
506506
}
507507

@@ -721,10 +721,10 @@ export class AnnotationTextStyle extends DefinitionElement {
721721
* corresponding to `sourceTextStyleId`, or [Id64.invalid]($bentley) if no corresponding text style exists.
722722
* If a text style with the same [Code]($common) exists in the target iModel, the style Id will be remapped to refer to that style.
723723
* Otherwise, a copy of the style will be imported into the target iModel and its element Id returned.
724-
* Implementations of [[ITextAnnotation]] should invoke this function when implementing their [[Element._onCloned]] method.
724+
* Implementations of [[ITextAnnotation]] should invoke this function when implementing their [[Element.onCloned]] method.
725725
* @throws Error if an attempt to import the text style failed.
726726
*/
727-
public static remapTextStyleId(sourceTextStyleId: Id64String, context: IModelElementCloneContext): Id64String {
727+
public static async remapTextStyleId(sourceTextStyleId: Id64String, context: IModelElementCloneContext): Promise<Id64String> {
728728
// No remapping necessary if there's no text style or we're not copying to a different iModel.
729729
if (!Id64.isValid(sourceTextStyleId) || !context.isBetweenIModels) {
730730
return sourceTextStyleId;
@@ -749,9 +749,28 @@ export class AnnotationTextStyle extends DefinitionElement {
749749
}
750750

751751
// Copy the style into the target iModel and remap its Id.
752-
const dstStyleProps = context.cloneElement(srcStyle);
752+
const dstStyleProps = await context.cloneElement(srcStyle);
753753
dstStyleId = context.targetDb.elements.insertElement(dstStyleProps);
754754
context.remapElement(sourceTextStyleId, dstStyleId);
755755
return dstStyleId;
756756
}
757+
758+
protected static override async onCloned(context: IModelElementCloneContext, srcProps: AnnotationTextStyleProps, dstProps: AnnotationTextStyleProps): Promise<void> {
759+
await super.onCloned(context, srcProps, dstProps);
760+
if (!context.isBetweenIModels) {
761+
return;
762+
}
763+
764+
const settingsProps = AnnotationTextStyle.parseTextStyleSettings(srcProps.settings);
765+
const font = TextStyleSettings.fromJSON(settingsProps?.data).font;
766+
767+
const fontsToEmbed = [];
768+
for (const file of context.sourceDb.fonts.queryEmbeddedFontFiles()) {
769+
if (file.type === font.type && file.faces.some((face) => face.familyName === font.name)) {
770+
fontsToEmbed.push(file);
771+
}
772+
}
773+
774+
await Promise.all(fontsToEmbed.map(async (file) => context.targetDb.fonts.embedFontFile({ file })));
775+
}
757776
}

0 commit comments

Comments
 (0)