@itwin/core-backend 5.14.0-dev.22 → 5.14.0-dev.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
  2. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  3. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js +59 -1
  4. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  5. package/lib/cjs/internal/annotations/fieldSpecs.d.ts +82 -0
  6. package/lib/cjs/internal/annotations/fieldSpecs.d.ts.map +1 -0
  7. package/lib/cjs/internal/annotations/fieldSpecs.js +187 -0
  8. package/lib/cjs/internal/annotations/fieldSpecs.js.map +1 -0
  9. package/lib/cjs/internal/annotations/fields.d.ts +39 -2
  10. package/lib/cjs/internal/annotations/fields.d.ts.map +1 -1
  11. package/lib/cjs/internal/annotations/fields.js +140 -13
  12. package/lib/cjs/internal/annotations/fields.js.map +1 -1
  13. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
  14. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  15. package/lib/esm/annotations/ElementDrivesTextAnnotation.js +60 -2
  16. package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  17. package/lib/esm/internal/annotations/fieldSpecs.d.ts +82 -0
  18. package/lib/esm/internal/annotations/fieldSpecs.d.ts.map +1 -0
  19. package/lib/esm/internal/annotations/fieldSpecs.js +179 -0
  20. package/lib/esm/internal/annotations/fieldSpecs.js.map +1 -0
  21. package/lib/esm/internal/annotations/fields.d.ts +39 -2
  22. package/lib/esm/internal/annotations/fields.d.ts.map +1 -1
  23. package/lib/esm/internal/annotations/fields.js +140 -13
  24. package/lib/esm/internal/annotations/fields.js.map +1 -1
  25. package/lib/esm/test/AnnotationTestUtils.d.ts +9 -0
  26. package/lib/esm/test/AnnotationTestUtils.d.ts.map +1 -1
  27. package/lib/esm/test/AnnotationTestUtils.js +17 -0
  28. package/lib/esm/test/AnnotationTestUtils.js.map +1 -1
  29. package/lib/esm/test/annotations/FieldFormat.test.d.ts +2 -0
  30. package/lib/esm/test/annotations/FieldFormat.test.d.ts.map +1 -0
  31. package/lib/esm/test/annotations/FieldFormat.test.js +738 -0
  32. package/lib/esm/test/annotations/FieldFormat.test.js.map +1 -0
  33. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts +2 -0
  34. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts.map +1 -0
  35. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js +323 -0
  36. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js.map +1 -0
  37. package/lib/esm/test/annotations/FieldSpecs.test.d.ts +2 -0
  38. package/lib/esm/test/annotations/FieldSpecs.test.d.ts.map +1 -0
  39. package/lib/esm/test/annotations/FieldSpecs.test.js +54 -0
  40. package/lib/esm/test/annotations/FieldSpecs.test.js.map +1 -0
  41. package/lib/esm/test/annotations/Fields.test.js +221 -27
  42. package/lib/esm/test/annotations/Fields.test.js.map +1 -1
  43. package/package.json +14 -12
@@ -1,14 +1,51 @@
1
- import { FieldRun, FieldValue, RelationshipProps, TextBlock } from "@itwin/core-common";
1
+ import { FieldRun, RelationshipProps, TextBlock } from "@itwin/core-common";
2
2
  import { IModelDb } from "../../IModelDb";
3
3
  import { Id64String } from "@itwin/core-bentley";
4
+ import { FieldFormatting, ResolvedFieldValue } from "./fieldSpecs";
4
5
  import type { EditTxn } from "../../EditTxn";
6
+ /** The per-evaluation state [[updateField]] needs: which element's fields to recompute, how to
7
+ * read a field's property value, and how to format quantities.
8
+ */
5
9
  export interface UpdateFieldsContext {
10
+ /** When set, only fields whose [FieldPropertyHost.elementId]($common) matches are recomputed —
11
+ * an edit to one source element leaves the annotation's other fields untouched.
12
+ */
6
13
  readonly hostElementId: Id64String | undefined;
7
- getProperty(field: FieldRun): FieldValue | undefined;
14
+ /** Reads the value a field points at, or `undefined` if it cannot be resolved — including
15
+ * every field when the source element was deleted.
16
+ */
17
+ getProperty(field: FieldRun): ResolvedFieldValue | undefined;
18
+ /** The formats `"quantity"` and `"coordinate"` values resolve through. [[updateField]] builds
19
+ * a [FormatterSpec]($core-quantity) per field from the FormatSet named by
20
+ * [QuantityFieldFormatOptions.formatSet]($common), falling back to the adopted FormatSet and
21
+ * the schemas; a value none of them can format falls back to `value.toString()`.
22
+ * [[createUpdateContext]] always supplies the iModel's formatting; only hand-built contexts in
23
+ * tests that never format a quantity leave it out.
24
+ */
25
+ readonly formatting?: FieldFormatting;
8
26
  }
9
27
  export declare function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext;
28
+ /** Recomputes a single field's cached display string synchronously. Returns true iff
29
+ * cachedContent changed.
30
+ *
31
+ * Resolving the property value and formatting it are both fallible. A failure of either is
32
+ * logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other
33
+ * fields in the same block are unaffected.
34
+ */
10
35
  export declare function updateField(field: FieldRun, context: UpdateFieldsContext): boolean;
36
+ /** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number
37
+ * whose cached display string changed. Fields targeting an element other than
38
+ * `context.hostElementId` (when set) are skipped.
39
+ */
11
40
  export declare function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number;
41
+ /** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element
42
+ * change (`deleted=false`) or delete (`deleted=true`). Invoked from
43
+ * [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.
44
+ */
12
45
  export declare function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void;
46
+ /** Re-evaluates every field of the given annotation element against its current property
47
+ * values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when
48
+ * establishing / refreshing relationships.
49
+ */
13
50
  export declare function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void;
14
51
  //# sourceMappingURL=fields.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"AAKA,OAAO,EAA0C,QAAQ,EAAE,UAAU,EAAiD,iBAAiB,EAAE,SAAS,EAA8B,MAAM,oBAAoB,CAAC;AAC3M,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAyB,UAAU,EAAU,MAAM,qBAAqB,CAAC;AAKhF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AA0B7C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,UAAU,GAAG,SAAS,CAAC;IAE/C,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAA;CACrD;AAoKD,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAK9H;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAsBlF;AAID,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CASvF;AA0BD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAElG;AAED,wBAAgB,eAAe,CAAC,mBAAmB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAEnF"}
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"AAKA,OAAO,EAA0C,QAAQ,EAA8E,iBAAiB,EAAE,SAAS,EAA8B,MAAM,oBAAoB,CAAC;AAC5N,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAyB,UAAU,EAAU,MAAM,qBAAqB,CAAC;AAKhF,OAAO,EAAE,eAAe,EAAuC,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACxG,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AA0B7C;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,QAAQ,CAAC,aAAa,EAAE,UAAU,GAAG,SAAS,CAAC;IAE/C;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,kBAAkB,GAAG,SAAS,CAAC;IAE7D;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;CACvC;AA+OD,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAM9H;AA0CD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CA0BlF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CASvF;AAyBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAElG;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,mBAAmB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAEnF"}
@@ -15,7 +15,9 @@ const BackendLoggerCategory_1 = require("../../BackendLoggerCategory");
15
15
  const ElementDrivesTextAnnotation_1 = require("../../annotations/ElementDrivesTextAnnotation");
16
16
  const ecschema_metadata_1 = require("@itwin/ecschema-metadata");
17
17
  const ECSqlInstanceReshaper_1 = require("../ECSqlInstanceReshaper");
18
- // Resolve the raw primitive value of the property that a field points to.
18
+ const fieldSpecs_1 = require("./fieldSpecs");
19
+ // Resolves the property a field points at into a [[ResolvedFieldValue]] — primitive value plus, for
20
+ // `"quantity"` / `"coordinate"` types, the property-side KoQ and persistence unit.
19
21
  function getFieldPropertyValue(field, iModel) {
20
22
  const host = field.propertyHost;
21
23
  const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);
@@ -23,7 +25,7 @@ function getFieldPropertyValue(field, iModel) {
23
25
  return undefined;
24
26
  }
25
27
  let ecClass = schemaItem;
26
- const { propertyName, accessors } = field.propertyPath;
28
+ const { propertyName, accessors, jsonAccessors } = field.propertyPath;
27
29
  let ecProp = ecClass.getPropertySync(propertyName);
28
30
  if (!ecProp) {
29
31
  return undefined;
@@ -115,15 +117,81 @@ function getFieldPropertyValue(field, iModel) {
115
117
  }
116
118
  }
117
119
  }
120
+ // The ultimate result must be a primitive value.
121
+ if (undefined === curValue.primitive) {
122
+ return undefined;
123
+ }
124
+ // `jsonAccessors` continue the walk inside a serialized JSON string. There is no schema behind a JSON blob, so the
125
+ // property must declare itself as JSON via its extended type; an un-indexed JSON property resolves to the raw string.
126
+ if (jsonAccessors && jsonAccessors.length > 0) {
127
+ if (!isJsonProperty(ecProp) || typeof curValue.primitive !== "string") {
128
+ return undefined;
129
+ }
130
+ return readJsonLeaf(curValue.primitive, jsonAccessors);
131
+ }
118
132
  const propertyType = determineFieldPropertyType(ecProp);
119
133
  if (!propertyType) {
120
134
  return undefined;
121
135
  }
122
- // The ultimate result must be a primitive value.
123
- if (undefined === curValue.primitive) {
136
+ // Property-side KoQ + persistence unit only. Overrides in `formatOptions.quantity` are
137
+ // merged at formatting time (see `collectFieldQuantityPairs`) so these serve as the fallback
138
+ // when the override doesn't resolve.
139
+ let kindOfQuantityFullName;
140
+ let persistenceUnitFullName;
141
+ if (propertyType === "quantity" || propertyType === "coordinate") {
142
+ const koq = ecProp.kindOfQuantity ? ecProp.getKindOfQuantitySync() : undefined;
143
+ kindOfQuantityFullName = koq?.fullName;
144
+ persistenceUnitFullName = koq?.persistenceUnit?.fullName;
145
+ }
146
+ return { value: curValue.primitive, type: propertyType, kindOfQuantityFullName, persistenceUnitFullName };
147
+ }
148
+ function isJsonProperty(prop) {
149
+ return prop.isPrimitive() && !prop.isArray() && prop.primitiveType === ecschema_metadata_1.PrimitiveType.String && prop.extendedTypeName === "Json";
150
+ }
151
+ /** Applies [FieldPropertyPath.jsonAccessors]($common) to a string property holding serialized JSON,
152
+ * as a walk entirely separate from the EC one: there is no schema behind a JSON blob, so no EC
153
+ * metadata is consulted.
154
+ *
155
+ * Returns `undefined` when `raw` is not JSON, when an accessor does not resolve, or when the path
156
+ * stops anywhere but a scalar — including on a JSON `null`, which is not a
157
+ * [FieldPrimitiveValue]($common).
158
+ */
159
+ function readJsonLeaf(raw, accessors) {
160
+ let cur;
161
+ try {
162
+ cur = JSON.parse(raw);
163
+ }
164
+ catch {
165
+ // Not valid JSON; treat as a normal string.
124
166
  return undefined;
125
167
  }
126
- return { value: curValue.primitive, type: propertyType };
168
+ for (const accessor of accessors) {
169
+ // A number indexes an array and a string keys an object; anything else doesn't resolve.
170
+ const isIndex = typeof accessor === "number";
171
+ if (typeof cur !== "object" || null === cur || Array.isArray(cur) !== isIndex) {
172
+ return undefined;
173
+ }
174
+ if (isIndex) {
175
+ cur = cur[accessor < 0 ? cur.length + accessor : accessor];
176
+ }
177
+ else {
178
+ // Own keys only, so a key missing from the JSON can't resolve through Object.prototype.
179
+ cur = Object.hasOwn(cur, accessor) ? cur[accessor] : undefined;
180
+ }
181
+ }
182
+ // JSON carries no type metadata, so the leaf's JavaScript type decides the field property type.
183
+ // A numeric leaf is typed `"quantity"` but has no property-side KoQ, so only the field's own
184
+ // `kindOfQuantity` + `persistenceUnit` overrides can name a format for it.
185
+ switch (typeof cur) {
186
+ case "number":
187
+ return { value: cur, type: "quantity" };
188
+ case "boolean":
189
+ return { value: cur, type: "boolean" };
190
+ case "string":
191
+ return { value: cur, type: "string" };
192
+ default:
193
+ return undefined;
194
+ }
127
195
  }
128
196
  function determineFieldPropertyType(prop) {
129
197
  if (prop.isEnumeration()) {
@@ -145,16 +213,18 @@ function determineFieldPropertyType(prop) {
145
213
  case ecschema_metadata_1.PrimitiveType.DateTime:
146
214
  return "datetime";
147
215
  case ecschema_metadata_1.PrimitiveType.Double:
216
+ case ecschema_metadata_1.PrimitiveType.Integer:
148
217
  case ecschema_metadata_1.PrimitiveType.Long:
218
+ // Any numeric property is a potential quantity. Classifying one as "quantity" is not an
219
+ // assertion that it *has* units -- it only decides whether the KoQ/units pipeline is
220
+ // consulted. A number that resolves no spec falls back to the same `toString()` the
221
+ // "string" formatter would have produced, so counts and identifiers still render bare.
149
222
  return "quantity";
150
223
  case ecschema_metadata_1.PrimitiveType.Point2d:
151
224
  case ecschema_metadata_1.PrimitiveType.Point3d:
152
225
  return "coordinate";
153
226
  case ecschema_metadata_1.PrimitiveType.Binary:
154
227
  return prop.extendedTypeName === "BeGuid" ? "string" : undefined;
155
- case ecschema_metadata_1.PrimitiveType.Integer:
156
- case ecschema_metadata_1.PrimitiveType.Long:
157
- return "string";
158
228
  default:
159
229
  return undefined;
160
230
  }
@@ -165,9 +235,53 @@ function createUpdateContext(hostElementId, iModel, deleted) {
165
235
  return {
166
236
  hostElementId,
167
237
  getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),
238
+ formatting: (0, fieldSpecs_1.getFieldFormatting)(iModel),
168
239
  };
169
240
  }
170
- // Recompute the display value of a single field, return false if it couldn't be evaluated.
241
+ /** Resolves the [FormatterSpec]($core-quantity) this field should render its magnitudes through,
242
+ * returning a callback bound to it, or `undefined` when not a quantity or coordinate, no formats
243
+ * were supplied, or no format resolves for any of the (KindOfQuantity, persistence unit) pairs the
244
+ * field may format through. In that last case the shortfall is logged: a persistence unit or
245
+ * format unit outside the bundled BIS set, a KindOfQuantity with no presentation format, or a
246
+ * format whose units belong to a different phenomenon than the persisted value. A
247
+ * `persistenceUnit` override that disagrees with the property's own unit is ignored and logged
248
+ * too; the property's unit is what the stored magnitude means.
249
+ */
250
+ function resolveFormatMagnitude(value, field, context) {
251
+ const formatting = context.formatting;
252
+ if (!formatting || (value.type !== "quantity" && value.type !== "coordinate")) {
253
+ return undefined;
254
+ }
255
+ const quantityOptions = field.formatOptions?.quantity;
256
+ const { spec, candidates, ignoredPersistenceUnit } = (0, fieldSpecs_1.lookupFieldSpec)(quantityOptions, value, formatting);
257
+ if (ignoredPersistenceUnit) {
258
+ core_bentley_1.Logger.logWarning(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, "Ignoring persistenceUnit override that disagrees with the property's own persistence unit", () => ({
259
+ elementId: field.propertyHost.elementId,
260
+ propertyName: field.propertyPath.propertyName,
261
+ persistenceUnit: ignoredPersistenceUnit,
262
+ propertyPersistenceUnit: value.persistenceUnitFullName,
263
+ }));
264
+ }
265
+ if (!spec) {
266
+ if (candidates.length > 0) {
267
+ core_bentley_1.Logger.logWarning(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, "No format resolved for text annotation field; rendering raw value", () => ({
268
+ elementId: field.propertyHost.elementId,
269
+ propertyName: field.propertyPath.propertyName,
270
+ formatSet: quantityOptions?.formatSet,
271
+ tried: candidates.map((c) => `${c.name} in ${c.persistenceUnitName}`),
272
+ }));
273
+ }
274
+ return undefined;
275
+ }
276
+ return (magnitude) => spec.applyFormatting(magnitude);
277
+ }
278
+ /** Recomputes a single field's cached display string synchronously. Returns true iff
279
+ * cachedContent changed.
280
+ *
281
+ * Resolving the property value and formatting it are both fallible. A failure of either is
282
+ * logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other
283
+ * fields in the same block are unaffected.
284
+ */
171
285
  function updateField(field, context) {
172
286
  if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {
173
287
  return false;
@@ -176,7 +290,11 @@ function updateField(field, context) {
176
290
  try {
177
291
  const propValue = context.getProperty(field);
178
292
  if (undefined !== propValue) {
179
- newContent = (0, core_common_1.formatFieldValue)(propValue, field.formatOptions);
293
+ newContent = (0, core_common_1.formatFieldValue)({
294
+ value: propValue,
295
+ options: field.formatOptions,
296
+ formatMagnitude: resolveFormatMagnitude(propValue, field, context),
297
+ });
180
298
  }
181
299
  }
182
300
  catch (err) {
@@ -189,8 +307,10 @@ function updateField(field, context) {
189
307
  field.setCachedContent(newContent);
190
308
  return true;
191
309
  }
192
- // Re-evaluates the display strings for all fields that target the element specified by `context` and returns the number
193
- // of fields whose display strings changed as a result.
310
+ /** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number
311
+ * whose cached display string changed. Fields targeting an element other than
312
+ * `context.hostElementId` (when set) are skipped.
313
+ */
194
314
  function updateFields(textBlock, context) {
195
315
  let numUpdated = 0;
196
316
  for (const { child } of (0, core_common_1.traverseTextBlockComponent)(textBlock)) {
@@ -222,10 +342,17 @@ function doUpdateFields(txn, annotationId, sourceId, deleted) {
222
342
  core_bentley_1.Logger.logError(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, err);
223
343
  }
224
344
  }
225
- // Invoked by ElementDrivesTextAnnotation to update fields in target element when source element changes or is deleted.
345
+ /** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element
346
+ * change (`deleted=false`) or delete (`deleted=true`). Invoked from
347
+ * [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.
348
+ */
226
349
  function updateElementFields(props, txn, deleted) {
227
350
  doUpdateFields(txn, props.targetId, props.sourceId, deleted);
228
351
  }
352
+ /** Re-evaluates every field of the given annotation element against its current property
353
+ * values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when
354
+ * establishing / refreshing relationships.
355
+ */
229
356
  function updateAllFields(annotationElementId, txn) {
230
357
  doUpdateFields(txn, annotationElementId, undefined, false);
231
358
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { FieldPrimitiveValue, FieldPropertyType, FieldRun, FieldValue, formatFieldValue, QueryBinder, QueryRowFormat, RelationshipProps, TextBlock, traverseTextBlockComponent } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"../../IModelDb\";\r\nimport { assert, expectDefined, Id64String, Logger } from \"@itwin/core-bentley\";\r\nimport { BackendLoggerCategory } from \"../../BackendLoggerCategory\";\r\nimport { isITextAnnotation } from \"../../annotations/ElementDrivesTextAnnotation\";\r\nimport { AnyClass, EntityClass, PrimitiveType, Property, PropertyType, StructArrayProperty } from \"@itwin/ecschema-metadata\";\r\nimport { reshapePropertyValue } from \"../ECSqlInstanceReshaper\";\r\nimport type { EditTxn } from \"../../EditTxn\";\r\ninterface FieldStructValue { [key: string]: any }\r\n\r\n// An intermediate value obtained while evaluating a FieldPropertyPath.\r\ntype FieldValueType = {\r\n primitive: FieldPrimitiveValue;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct: FieldStructValue;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray: FieldPrimitiveValue[];\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray: FieldStructValue[];\r\n}\r\n\r\nexport interface UpdateFieldsContext {\r\n readonly hostElementId: Id64String | undefined;\r\n\r\n getProperty(field: FieldRun): FieldValue | undefined\r\n}\r\n\r\n// Resolve the raw primitive value of the property that a field points to.\r\nfunction getFieldPropertyValue(field: FieldRun, iModel: IModelDb): FieldValue | undefined {\r\n const host = field.propertyHost;\r\n const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);\r\n if (!EntityClass.isEntityClass(schemaItem)) {\r\n return undefined;\r\n }\r\n\r\n let ecClass: AnyClass = schemaItem;\r\n const { propertyName, accessors } = field.propertyPath;\r\n let ecProp = ecClass.getPropertySync(propertyName);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n const isAspect = ecClass.isSync(\"ElementAspect\", \"BisCore\");\r\n const where = ` WHERE ${isAspect ? \"Element.Id\" : \"ECInstanceId\"}=:elementId`;\r\n // `propertyName` may itself be a struct/array/point/navigation property, so its value can't be\r\n // decomposed into scalar sub-columns ahead of time. Query using the non-deprecated\r\n // UseECSqlPropertyNames format and reshape the value into the legacy UseJsPropertyNames shape using\r\n // ECSchema metadata (see ECSqlInstanceReshaper for why a naive, non-schema-aware rename isn't safe here).\r\n let curValue: FieldValueType | undefined = iModel.withQueryReader(`SELECT ${propertyName} FROM ${host.schemaName}.${host.className} ${where}`, (reader): FieldValueType | undefined => {\r\n if (!reader.step()) {\r\n return undefined;\r\n }\r\n\r\n const rawRootValue = reader.current[0];\r\n if (undefined === rawRootValue) {\r\n return undefined;\r\n }\r\n\r\n ecProp = expectDefined(ecProp);\r\n const rootValue = reshapePropertyValue(rawRootValue, ecProp, iModel);\r\n if (ecProp.isArray()) {\r\n return ecProp.isStruct() ? { structArray: rootValue } : { primitiveArray: rootValue };\r\n }\r\n\r\n if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n return { struct: rootValue };\r\n }\r\n\r\n if (ecProp.isPrimitive()) {\r\n return {\r\n primitive: ecProp.primitiveType === PrimitiveType.DateTime ? new Date(rootValue) : rootValue,\r\n };\r\n }\r\n\r\n return undefined;\r\n }, new QueryBinder().bindId(\"elementId\", host.elementId), { rowFormat: QueryRowFormat.UseECSqlPropertyNames });\r\n\r\n if (undefined === curValue) {\r\n return undefined;\r\n }\r\n\r\n if (accessors) {\r\n for (const accessor of accessors) {\r\n if (undefined !== curValue.primitive) {\r\n // Can't index into a primitive.\r\n return undefined;\r\n }\r\n\r\n if (typeof accessor === \"number\") {\r\n const array: FieldPrimitiveValue[] | FieldStructValue[] | undefined = curValue.primitiveArray ?? curValue.structArray;\r\n if (!array) {\r\n return undefined;\r\n }\r\n\r\n const index: number = accessor < 0 ? (array.length + accessor) : accessor;\r\n const item: FieldPrimitiveValue | FieldStructValue = array[index];\r\n if (undefined === item) {\r\n return undefined;\r\n } else if (curValue.primitiveArray) {\r\n curValue = { primitive: curValue.primitiveArray[index] };\r\n } else {\r\n assert(undefined !== curValue.structArray);\r\n assert(ecProp instanceof StructArrayProperty);\r\n\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: curValue.structArray[index] };\r\n }\r\n } else {\r\n if (undefined === curValue.struct) {\r\n return undefined;\r\n }\r\n\r\n const item: any = curValue.struct[accessor];\r\n if (undefined === item) {\r\n return undefined;\r\n }\r\n\r\n ecProp = ecClass.getPropertySync(accessor);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n if (ecProp.isArray()) {\r\n curValue = ecProp.isStruct() ? { structArray: item } : { primitiveArray: item };\r\n } else if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: item };\r\n } else if (ecProp.isPrimitive()) {\r\n curValue = { primitive: item };\r\n } else {\r\n return undefined;\r\n }\r\n }\r\n }\r\n }\r\n\r\n const propertyType = determineFieldPropertyType(ecProp);\r\n if (!propertyType) {\r\n return undefined;\r\n }\r\n\r\n // The ultimate result must be a primitive value.\r\n if (undefined === curValue.primitive) {\r\n return undefined;\r\n }\r\n\r\n return { value: curValue.primitive, type: propertyType };\r\n}\r\n\r\nfunction determineFieldPropertyType(prop: Property): FieldPropertyType | undefined {\r\n if (prop.isEnumeration()) {\r\n switch (prop.propertyType) {\r\n case PropertyType.Integer_Enumeration:\r\n return \"int-enum\";\r\n case PropertyType.String_Enumeration:\r\n return \"string-enum\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n if (prop.isPrimitive()) {\r\n switch (prop.primitiveType) {\r\n case PrimitiveType.Boolean:\r\n return \"boolean\";\r\n case PrimitiveType.String:\r\n return prop.extendedTypeName === \"DateTime\" ? \"datetime\" : \"string\";\r\n case PrimitiveType.DateTime:\r\n return \"datetime\";\r\n case PrimitiveType.Double:\r\n case PrimitiveType.Long:\r\n return \"quantity\";\r\n case PrimitiveType.Point2d:\r\n case PrimitiveType.Point3d:\r\n return \"coordinate\";\r\n case PrimitiveType.Binary:\r\n return prop.extendedTypeName === \"BeGuid\" ? \"string\" : undefined;\r\n case PrimitiveType.Integer:\r\n case PrimitiveType.Long:\r\n return \"string\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nexport function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext {\r\n return {\r\n hostElementId,\r\n getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),\r\n };\r\n}\r\n\r\n// Recompute the display value of a single field, return false if it couldn't be evaluated.\r\nexport function updateField(field: FieldRun, context: UpdateFieldsContext): boolean {\r\n if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {\r\n return false;\r\n }\r\n\r\n let newContent: string | undefined;\r\n try {\r\n const propValue = context.getProperty(field);\r\n if (undefined !== propValue) {\r\n newContent = formatFieldValue(propValue, field.formatOptions);\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n\r\n newContent = newContent ?? FieldRun.invalidContentIndicator;\r\n if (newContent === field.cachedContent) {\r\n return false;\r\n }\r\n\r\n field.setCachedContent(newContent);\r\n return true;\r\n}\r\n\r\n// Re-evaluates the display strings for all fields that target the element specified by `context` and returns the number\r\n// of fields whose display strings changed as a result.\r\nexport function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number {\r\n let numUpdated = 0;\r\n for (const { child } of traverseTextBlockComponent(textBlock)) {\r\n if (child.type === \"field\" && updateField(child, context)) {\r\n ++numUpdated;\r\n }\r\n }\r\n\r\n return numUpdated;\r\n}\r\n\r\nfunction doUpdateFields(txn: EditTxn, annotationId: Id64String, sourceId: Id64String | undefined, deleted: boolean): void {\r\n const iModel = txn.iModel;\r\n try {\r\n const target = iModel.elements.getElement(annotationId);\r\n if (isITextAnnotation(target)) {\r\n const context = createUpdateContext(sourceId, iModel, deleted);\r\n const updatedBlocks = [];\r\n for (const block of target.getTextBlocks()) {\r\n if (updateFields(block.textBlock, context)) {\r\n updatedBlocks.push(block);\r\n }\r\n }\r\n\r\n if (updatedBlocks.length > 0) {\r\n target.updateTextBlocks(updatedBlocks);\r\n target.update(txn);\r\n }\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n}\r\n\r\n// Invoked by ElementDrivesTextAnnotation to update fields in target element when source element changes or is deleted.\r\nexport function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void {\r\n doUpdateFields(txn, props.targetId, props.sourceId, deleted);\r\n}\r\n\r\nexport function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void {\r\n doUpdateFields(txn, annotationElementId, undefined, false);\r\n}\r\n"]}
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { FieldPrimitiveValue, FieldPropertyType, FieldRun, FieldValue, formatFieldValue, FormatMagnitude, QueryBinder, QueryRowFormat, RelationshipProps, TextBlock, traverseTextBlockComponent } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"../../IModelDb\";\r\nimport { assert, expectDefined, Id64String, Logger } from \"@itwin/core-bentley\";\r\nimport { BackendLoggerCategory } from \"../../BackendLoggerCategory\";\r\nimport { isITextAnnotation } from \"../../annotations/ElementDrivesTextAnnotation\";\r\nimport { AnyClass, EntityClass, PrimitiveType, Property, PropertyType, StructArrayProperty } from \"@itwin/ecschema-metadata\";\r\nimport { reshapePropertyValue } from \"../ECSqlInstanceReshaper\";\r\nimport { FieldFormatting, getFieldFormatting, lookupFieldSpec, ResolvedFieldValue } from \"./fieldSpecs\";\r\nimport type { EditTxn } from \"../../EditTxn\";\r\ninterface FieldStructValue { [key: string]: any }\r\n\r\n// An intermediate value obtained while evaluating a FieldPropertyPath.\r\ntype FieldValueType = {\r\n primitive: FieldPrimitiveValue;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct: FieldStructValue;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray: FieldPrimitiveValue[];\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray: FieldStructValue[];\r\n}\r\n\r\n/** The per-evaluation state [[updateField]] needs: which element's fields to recompute, how to\r\n * read a field's property value, and how to format quantities.\r\n */\r\nexport interface UpdateFieldsContext {\r\n /** When set, only fields whose [FieldPropertyHost.elementId]($common) matches are recomputed —\r\n * an edit to one source element leaves the annotation's other fields untouched.\r\n */\r\n readonly hostElementId: Id64String | undefined;\r\n\r\n /** Reads the value a field points at, or `undefined` if it cannot be resolved — including\r\n * every field when the source element was deleted.\r\n */\r\n getProperty(field: FieldRun): ResolvedFieldValue | undefined;\r\n\r\n /** The formats `\"quantity\"` and `\"coordinate\"` values resolve through. [[updateField]] builds\r\n * a [FormatterSpec]($core-quantity) per field from the FormatSet named by\r\n * [QuantityFieldFormatOptions.formatSet]($common), falling back to the adopted FormatSet and\r\n * the schemas; a value none of them can format falls back to `value.toString()`.\r\n * [[createUpdateContext]] always supplies the iModel's formatting; only hand-built contexts in\r\n * tests that never format a quantity leave it out.\r\n */\r\n readonly formatting?: FieldFormatting;\r\n}\r\n\r\n// Resolves the property a field points at into a [[ResolvedFieldValue]] — primitive value plus, for\r\n// `\"quantity\"` / `\"coordinate\"` types, the property-side KoQ and persistence unit.\r\nfunction getFieldPropertyValue(field: FieldRun, iModel: IModelDb): ResolvedFieldValue | undefined {\r\n const host = field.propertyHost;\r\n const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);\r\n if (!EntityClass.isEntityClass(schemaItem)) {\r\n return undefined;\r\n }\r\n\r\n let ecClass: AnyClass = schemaItem;\r\n const { propertyName, accessors, jsonAccessors } = field.propertyPath;\r\n let ecProp = ecClass.getPropertySync(propertyName);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n const isAspect = ecClass.isSync(\"ElementAspect\", \"BisCore\");\r\n const where = ` WHERE ${isAspect ? \"Element.Id\" : \"ECInstanceId\"}=:elementId`;\r\n // `propertyName` may itself be a struct/array/point/navigation property, so its value can't be\r\n // decomposed into scalar sub-columns ahead of time. Query using the non-deprecated\r\n // UseECSqlPropertyNames format and reshape the value into the legacy UseJsPropertyNames shape using\r\n // ECSchema metadata (see ECSqlInstanceReshaper for why a naive, non-schema-aware rename isn't safe here).\r\n let curValue: FieldValueType | undefined = iModel.withQueryReader(`SELECT ${propertyName} FROM ${host.schemaName}.${host.className} ${where}`, (reader): FieldValueType | undefined => {\r\n if (!reader.step()) {\r\n return undefined;\r\n }\r\n\r\n const rawRootValue = reader.current[0];\r\n if (undefined === rawRootValue) {\r\n return undefined;\r\n }\r\n\r\n ecProp = expectDefined(ecProp);\r\n const rootValue = reshapePropertyValue(rawRootValue, ecProp, iModel);\r\n if (ecProp.isArray()) {\r\n return ecProp.isStruct() ? { structArray: rootValue } : { primitiveArray: rootValue };\r\n }\r\n\r\n if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n return { struct: rootValue };\r\n }\r\n\r\n if (ecProp.isPrimitive()) {\r\n return {\r\n primitive: ecProp.primitiveType === PrimitiveType.DateTime ? new Date(rootValue) : rootValue,\r\n };\r\n }\r\n\r\n return undefined;\r\n }, new QueryBinder().bindId(\"elementId\", host.elementId), { rowFormat: QueryRowFormat.UseECSqlPropertyNames });\r\n\r\n if (undefined === curValue) {\r\n return undefined;\r\n }\r\n\r\n if (accessors) {\r\n for (const accessor of accessors) {\r\n if (undefined !== curValue.primitive) {\r\n // Can't index into a primitive.\r\n return undefined;\r\n }\r\n\r\n if (typeof accessor === \"number\") {\r\n const array: FieldPrimitiveValue[] | FieldStructValue[] | undefined = curValue.primitiveArray ?? curValue.structArray;\r\n if (!array) {\r\n return undefined;\r\n }\r\n\r\n const index: number = accessor < 0 ? (array.length + accessor) : accessor;\r\n const item: FieldPrimitiveValue | FieldStructValue = array[index];\r\n if (undefined === item) {\r\n return undefined;\r\n } else if (curValue.primitiveArray) {\r\n curValue = { primitive: curValue.primitiveArray[index] };\r\n } else {\r\n assert(undefined !== curValue.structArray);\r\n assert(ecProp instanceof StructArrayProperty);\r\n\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: curValue.structArray[index] };\r\n }\r\n } else {\r\n if (undefined === curValue.struct) {\r\n return undefined;\r\n }\r\n\r\n const item: any = curValue.struct[accessor];\r\n if (undefined === item) {\r\n return undefined;\r\n }\r\n\r\n ecProp = ecClass.getPropertySync(accessor);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n if (ecProp.isArray()) {\r\n curValue = ecProp.isStruct() ? { structArray: item } : { primitiveArray: item };\r\n } else if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: item };\r\n } else if (ecProp.isPrimitive()) {\r\n curValue = { primitive: item };\r\n } else {\r\n return undefined;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The ultimate result must be a primitive value.\r\n if (undefined === curValue.primitive) {\r\n return undefined;\r\n }\r\n\r\n // `jsonAccessors` continue the walk inside a serialized JSON string. There is no schema behind a JSON blob, so the\r\n // property must declare itself as JSON via its extended type; an un-indexed JSON property resolves to the raw string.\r\n if (jsonAccessors && jsonAccessors.length > 0) {\r\n if (!isJsonProperty(ecProp) || typeof curValue.primitive !== \"string\") {\r\n return undefined;\r\n }\r\n\r\n return readJsonLeaf(curValue.primitive, jsonAccessors);\r\n }\r\n\r\n const propertyType = determineFieldPropertyType(ecProp);\r\n if (!propertyType) {\r\n return undefined;\r\n }\r\n\r\n // Property-side KoQ + persistence unit only. Overrides in `formatOptions.quantity` are\r\n // merged at formatting time (see `collectFieldQuantityPairs`) so these serve as the fallback\r\n // when the override doesn't resolve.\r\n let kindOfQuantityFullName: string | undefined;\r\n let persistenceUnitFullName: string | undefined;\r\n if (propertyType === \"quantity\" || propertyType === \"coordinate\") {\r\n const koq = ecProp.kindOfQuantity ? ecProp.getKindOfQuantitySync() : undefined;\r\n kindOfQuantityFullName = koq?.fullName;\r\n persistenceUnitFullName = koq?.persistenceUnit?.fullName;\r\n }\r\n\r\n return { value: curValue.primitive, type: propertyType, kindOfQuantityFullName, persistenceUnitFullName };\r\n}\r\n\r\nfunction isJsonProperty(prop: Property): boolean {\r\n return prop.isPrimitive() && !prop.isArray() && prop.primitiveType === PrimitiveType.String && prop.extendedTypeName === \"Json\";\r\n}\r\n\r\n/** Applies [FieldPropertyPath.jsonAccessors]($common) to a string property holding serialized JSON,\r\n * as a walk entirely separate from the EC one: there is no schema behind a JSON blob, so no EC\r\n * metadata is consulted.\r\n *\r\n * Returns `undefined` when `raw` is not JSON, when an accessor does not resolve, or when the path\r\n * stops anywhere but a scalar — including on a JSON `null`, which is not a\r\n * [FieldPrimitiveValue]($common).\r\n */\r\nfunction readJsonLeaf(raw: string, accessors: ReadonlyArray<string | number>): FieldValue | undefined {\r\n let cur: any;\r\n try {\r\n cur = JSON.parse(raw);\r\n } catch {\r\n // Not valid JSON; treat as a normal string.\r\n return undefined;\r\n }\r\n\r\n for (const accessor of accessors) {\r\n // A number indexes an array and a string keys an object; anything else doesn't resolve.\r\n const isIndex = typeof accessor === \"number\";\r\n if (typeof cur !== \"object\" || null === cur || Array.isArray(cur) !== isIndex) {\r\n return undefined;\r\n }\r\n\r\n if (isIndex) {\r\n cur = cur[accessor < 0 ? cur.length + accessor : accessor];\r\n } else {\r\n // Own keys only, so a key missing from the JSON can't resolve through Object.prototype.\r\n cur = Object.hasOwn(cur, accessor) ? cur[accessor] : undefined;\r\n }\r\n }\r\n\r\n // JSON carries no type metadata, so the leaf's JavaScript type decides the field property type.\r\n // A numeric leaf is typed `\"quantity\"` but has no property-side KoQ, so only the field's own\r\n // `kindOfQuantity` + `persistenceUnit` overrides can name a format for it.\r\n switch (typeof cur) {\r\n case \"number\":\r\n return { value: cur, type: \"quantity\" };\r\n case \"boolean\":\r\n return { value: cur, type: \"boolean\" };\r\n case \"string\":\r\n return { value: cur, type: \"string\" };\r\n default:\r\n return undefined;\r\n }\r\n}\r\n\r\nfunction determineFieldPropertyType(prop: Property): FieldPropertyType | undefined {\r\n if (prop.isEnumeration()) {\r\n switch (prop.propertyType) {\r\n case PropertyType.Integer_Enumeration:\r\n return \"int-enum\";\r\n case PropertyType.String_Enumeration:\r\n return \"string-enum\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n if (prop.isPrimitive()) {\r\n switch (prop.primitiveType) {\r\n case PrimitiveType.Boolean:\r\n return \"boolean\";\r\n case PrimitiveType.String:\r\n return prop.extendedTypeName === \"DateTime\" ? \"datetime\" : \"string\";\r\n case PrimitiveType.DateTime:\r\n return \"datetime\";\r\n case PrimitiveType.Double:\r\n case PrimitiveType.Integer:\r\n case PrimitiveType.Long:\r\n // Any numeric property is a potential quantity. Classifying one as \"quantity\" is not an\r\n // assertion that it *has* units -- it only decides whether the KoQ/units pipeline is\r\n // consulted. A number that resolves no spec falls back to the same `toString()` the\r\n // \"string\" formatter would have produced, so counts and identifiers still render bare.\r\n return \"quantity\";\r\n case PrimitiveType.Point2d:\r\n case PrimitiveType.Point3d:\r\n return \"coordinate\";\r\n case PrimitiveType.Binary:\r\n return prop.extendedTypeName === \"BeGuid\" ? \"string\" : undefined;\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nexport function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext {\r\n return {\r\n hostElementId,\r\n getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),\r\n formatting: getFieldFormatting(iModel),\r\n };\r\n}\r\n\r\n/** Resolves the [FormatterSpec]($core-quantity) this field should render its magnitudes through,\r\n * returning a callback bound to it, or `undefined` when not a quantity or coordinate, no formats\r\n * were supplied, or no format resolves for any of the (KindOfQuantity, persistence unit) pairs the\r\n * field may format through. In that last case the shortfall is logged: a persistence unit or\r\n * format unit outside the bundled BIS set, a KindOfQuantity with no presentation format, or a\r\n * format whose units belong to a different phenomenon than the persisted value. A\r\n * `persistenceUnit` override that disagrees with the property's own unit is ignored and logged\r\n * too; the property's unit is what the stored magnitude means.\r\n */\r\nfunction resolveFormatMagnitude(value: ResolvedFieldValue, field: FieldRun, context: UpdateFieldsContext): FormatMagnitude | undefined {\r\n const formatting = context.formatting;\r\n if (!formatting || (value.type !== \"quantity\" && value.type !== \"coordinate\")) {\r\n return undefined;\r\n }\r\n\r\n const quantityOptions = field.formatOptions?.quantity;\r\n const { spec, candidates, ignoredPersistenceUnit } = lookupFieldSpec(quantityOptions, value, formatting);\r\n if (ignoredPersistenceUnit) {\r\n Logger.logWarning(BackendLoggerCategory.IModelDb, \"Ignoring persistenceUnit override that disagrees with the property's own persistence unit\", () => ({\r\n elementId: field.propertyHost.elementId,\r\n propertyName: field.propertyPath.propertyName,\r\n persistenceUnit: ignoredPersistenceUnit,\r\n propertyPersistenceUnit: value.persistenceUnitFullName,\r\n }));\r\n }\r\n if (!spec) {\r\n if (candidates.length > 0) {\r\n Logger.logWarning(BackendLoggerCategory.IModelDb, \"No format resolved for text annotation field; rendering raw value\", () => ({\r\n elementId: field.propertyHost.elementId,\r\n propertyName: field.propertyPath.propertyName,\r\n formatSet: quantityOptions?.formatSet,\r\n tried: candidates.map((c) => `${c.name} in ${c.persistenceUnitName}`),\r\n }));\r\n }\r\n return undefined;\r\n }\r\n\r\n return (magnitude) => spec.applyFormatting(magnitude);\r\n}\r\n\r\n/** Recomputes a single field's cached display string synchronously. Returns true iff\r\n * cachedContent changed.\r\n *\r\n * Resolving the property value and formatting it are both fallible. A failure of either is\r\n * logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other\r\n * fields in the same block are unaffected.\r\n */\r\nexport function updateField(field: FieldRun, context: UpdateFieldsContext): boolean {\r\n if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {\r\n return false;\r\n }\r\n\r\n let newContent: string | undefined;\r\n try {\r\n const propValue = context.getProperty(field);\r\n if (undefined !== propValue) {\r\n newContent = formatFieldValue({\r\n value: propValue,\r\n options: field.formatOptions,\r\n formatMagnitude: resolveFormatMagnitude(propValue, field, context),\r\n });\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n\r\n newContent = newContent ?? FieldRun.invalidContentIndicator;\r\n if (newContent === field.cachedContent) {\r\n return false;\r\n }\r\n\r\n field.setCachedContent(newContent);\r\n return true;\r\n}\r\n\r\n/** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number\r\n * whose cached display string changed. Fields targeting an element other than\r\n * `context.hostElementId` (when set) are skipped.\r\n */\r\nexport function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number {\r\n let numUpdated = 0;\r\n for (const { child } of traverseTextBlockComponent(textBlock)) {\r\n if (child.type === \"field\" && updateField(child, context)) {\r\n ++numUpdated;\r\n }\r\n }\r\n\r\n return numUpdated;\r\n}\r\n\r\nfunction doUpdateFields(txn: EditTxn, annotationId: Id64String, sourceId: Id64String | undefined, deleted: boolean): void {\r\n const iModel = txn.iModel;\r\n try {\r\n const target = iModel.elements.getElement(annotationId);\r\n if (isITextAnnotation(target)) {\r\n const context = createUpdateContext(sourceId, iModel, deleted);\r\n const updatedBlocks = [];\r\n for (const block of target.getTextBlocks()) {\r\n if (updateFields(block.textBlock, context)) {\r\n updatedBlocks.push(block);\r\n }\r\n }\r\n\r\n if (updatedBlocks.length > 0) {\r\n target.updateTextBlocks(updatedBlocks);\r\n target.update(txn);\r\n }\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n}\r\n\r\n/** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element\r\n * change (`deleted=false`) or delete (`deleted=true`). Invoked from\r\n * [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.\r\n */\r\nexport function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void {\r\n doUpdateFields(txn, props.targetId, props.sourceId, deleted);\r\n}\r\n\r\n/** Re-evaluates every field of the given annotation element against its current property\r\n * values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when\r\n * establishing / refreshing relationships.\r\n */\r\nexport function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void {\r\n doUpdateFields(txn, annotationElementId, undefined, false);\r\n}\r\n"]}
@@ -1,13 +1,44 @@
1
1
  /** @packageDocumentation
2
2
  * @module Elements
3
3
  */
4
- import { Id64String } from "@itwin/core-bentley";
4
+ import { BeEvent, Id64String } from "@itwin/core-bentley";
5
5
  import { RelatedElement, TextBlock } from "@itwin/core-common";
6
+ import { UnitSystemKey } from "@itwin/core-quantity";
7
+ import { FormatSet } from "@itwin/ecschema-metadata";
6
8
  import { Element } from "../Element";
7
9
  import { IModelDb } from "../IModelDb";
8
10
  import { IModelElementCloneContext } from "../IModelElementCloneContext";
9
11
  import { ElementDrivesElement, OnDependencyArg } from "../Relationship";
10
12
  import { EditTxn } from "../EditTxn";
13
+ /** Configures how `"quantity"` and `"coordinate"` [FieldRun]($common)s in an iModel are
14
+ * formatted. Passed to [[ElementDrivesTextAnnotation.registerFieldFormatting]].
15
+ * @beta
16
+ */
17
+ export interface FieldFormattingArgs {
18
+ /** The iModel whose annotations this configuration formats. Its `schemaContext` supplies the
19
+ * KindOfQuantity presentation formats that every FormatSet falls back to.
20
+ */
21
+ iModel: IModelDb;
22
+ /** The FormatSet adopted for this iModel. It applies to every [FieldRun]($common) that does
23
+ * not name a different one via [QuantityFieldFormatOptions.formatSet]($common), and takes
24
+ * precedence over the schema's own presentation formats.
25
+ */
26
+ formatSet?: FormatSet;
27
+ /** Additional FormatSets addressable per-field, each paired with the id that
28
+ * [FieldRun]($common)s reference via [QuantityFieldFormatOptions.formatSet]($common).
29
+ * The id must be unique; if two entries share an id the last one wins. A field naming an id
30
+ * absent from this list falls back to [[formatSet]].
31
+ */
32
+ formatSets?: ReadonlyArray<{
33
+ id: string;
34
+ formatSet: FormatSet;
35
+ }>;
36
+ /** Unit system used to pick a KindOfQuantity's presentation format when the schema offers
37
+ * several. Defaults to [[formatSet]]'s own `unitSystem`, or `"metric"` when no FormatSet is
38
+ * adopted.
39
+ */
40
+ unitSystem?: UnitSystemKey;
41
+ }
11
42
  /** Describes one of potentially many [TextBlock]($common)s hosted by an [[ITextAnnotation]].
12
43
  * For example, a [[TextAnnotation2d]] hosts only a single text block, but an element representing a table may
13
44
  * host one text block for each cell in the table, in which case it might use the combination of row and column
@@ -66,9 +97,7 @@ export declare class ElementDrivesTextAnnotation extends ElementDrivesElement {
66
97
  * update when the source element changes.
67
98
  */
68
99
  static isSupportedForIModel(iModel: IModelDb): boolean;
69
- /** Examines all of the [FieldRun]($common)s within the specified [[ITextAnnotation]] and ensures that the appropriate
70
- * `ElementDrivesTextAnnotation` relationships exist between the fields' source elements and this target element.
71
- * It also deletes any stale relationships left over from fields that were deleted or whose source elements changed.
100
+ /** Ensures the `ElementDrivesTextAnnotation` relationships for the [FieldRun]($common)s in the specified annotation are up to date.
72
101
  * @deprecated in 5.9.0 - will not be removed until after 2027-05-04. Use ElementDrivesTextAnnotation.updateFieldDependencies(txn, ...) instead.
73
102
  */
74
103
  static updateFieldDependencies(annotationElementId: Id64String, iModel: IModelDb): void;
@@ -78,10 +107,66 @@ export declare class ElementDrivesTextAnnotation extends ElementDrivesElement {
78
107
  */
79
108
  static updateFieldDependencies(txn: EditTxn, annotationElementId: Id64String): void;
80
109
  /** Recompute the display strings of all [FieldRun]($common)s in a [TextBlock]($common).
110
+ *
111
+ * `"quantity"` and `"coordinate"` fields are formatted through the FormatSets configured for
112
+ * `args.iModel` by [[registerFieldFormatting]], or otherwise through the format each
113
+ * KindOfQuantity's schema declares.
114
+ *
115
+ * A field whose format cannot be resolved falls back to `value.toString()` and is logged. A
116
+ * field whose property cannot be resolved, or whose format throws, is logged and rendered as
117
+ * [FieldRun.invalidContentIndicator]($common); one bad field does not abandon the rest of the
118
+ * block.
81
119
  * @returns the number of fields whose display strings were modified.
82
- * @throws Error if evaluation of any field fails.
83
120
  */
84
121
  static evaluateFields(args: EvaluateFieldsArgs): number;
122
+ /** Configures how `"quantity"` and `"coordinate"` [FieldRun]($common)s in `args.iModel` are
123
+ * formatted by [[evaluateFields]] and by `TxnManager` field-update callbacks.
124
+ *
125
+ * Calling this is optional. An iModel with no registration formats every field using the
126
+ * presentation format its schema declares for the KindOfQuantity, in the metric unit system.
127
+ * Register to layer application [FormatSet]($ecschema-metadata)s over those schema defaults,
128
+ * or to select a different unit system. Formats resolve in this order:
129
+ *
130
+ * 1. The FormatSet named by the field's [QuantityFieldFormatOptions.formatSet]($common).
131
+ * 2. The FormatSet adopted for the iModel ([[FieldFormattingArgs.formatSet]]).
132
+ * 3. The KindOfQuantity's presentation format for [[FieldFormattingArgs.unitSystem]].
133
+ * 4. `value.toString()`, with a warning logged.
134
+ *
135
+ * Units resolve through the bundled BIS [BasicUnitsProvider]($core-quantity) only. A field
136
+ * whose persistence unit, or whose format's units, are defined solely by the iModel's own
137
+ * schemas falls to step 4.
138
+ *
139
+ * Pass the iModel-wide default as `formatSet` and any per-field alternatives as `formatSets`,
140
+ * keyed by the id that [FieldRun]($common)s name via
141
+ * [QuantityFieldFormatOptions.formatSet]($common):
142
+ *
143
+ * ```ts
144
+ * ElementDrivesTextAnnotation.registerFieldFormatting({
145
+ * iModel,
146
+ * formatSet: metricFormatSet,
147
+ * formatSets: [{ id: imperialFormatSetId, formatSet: imperial }],
148
+ * });
149
+ * ```
150
+ *
151
+ * Each call replaces any prior registration for the same iModel; calling it with only
152
+ * `iModel` reverts to the schema defaults. Registering does not re-evaluate existing
153
+ * annotations; listen to [[onFieldFormattingChanged]] to do so. The registration lives as long
154
+ * as the `IModelDb` object and is released with it.
155
+ * @see [Quantity formatting for text annotation fields]($docs/learning/backend/TextAnnotationFields.md)
156
+ * @beta
157
+ */
158
+ static registerFieldFormatting(args: FieldFormattingArgs): void;
159
+ /** Raised after every [[registerFieldFormatting]] call, once the new configuration is in
160
+ * place, whether or not it differs from the previous one. Applications that cache formatted
161
+ * output, or that want to re-evaluate existing annotations against a newly adopted FormatSet,
162
+ * should listen here. A listener that throws does not undo the registration; its error is
163
+ * reported through [UnexpectedErrors]($bentley) rather than to the caller of
164
+ * `registerFieldFormatting`.
165
+ * @beta
166
+ */
167
+ static readonly onFieldFormattingChanged: BeEvent<(args: {
168
+ iModel: IModelDb;
169
+ }) => void>;
85
170
  /** When copying an [[ITextAnnotation]] from one iModel into another, remaps the element Ids in any [FieldPropertyHost]($common) within the cloned element
86
171
  * so that they refer to elements in the `context`'s target iModel, and sets any Ids that cannot be remapped to [Id64.invalid]($bentley).
87
172
  * Implementations of `ITextAnnotation` should invoke this function from their implementations of [[Element.onCloned]].
@@ -1 +1 @@
1
- {"version":3,"file":"ElementDrivesTextAnnotation.d.ts","sourceRoot":"","sources":["../../../src/annotations/ElementDrivesTextAnnotation.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAO,EAAQ,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAe,cAAc,EAAE,SAAS,EAA8B,MAAM,oBAAoB,CAAC;AAExG,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AAGzE,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,sBAAsB;IACtB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,gIAAgI;IAChI,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;CACtB;AAKD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,oCAAoC,CAAC;IACxD,sFAAsF;IACtF,aAAa,IAAI,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC1C,mGAAmG;IACnG,gBAAgB,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;CACtD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,eAAe,GAAG,OAAO,CAExF;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,KAAK,EAAE,SAAS,CAAC;IACjB,oGAAoG;IACpG,MAAM,EAAE,QAAQ,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,qBAAa,2BAA4B,SAAQ,oBAAoB;IACnE,WAA2B,SAAS,IAAI,MAAM,CAA0C;IAExF,OAAO,CAAC,MAAM,CAAC,2BAA2B;IAuE1C,gBAAgB;WACO,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAInE,gBAAgB;WACO,sBAAsB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAIzE;;;OAGG;WACW,oBAAoB,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO;IAI7D;;;;OAIG;WACW,uBAAuB,CAAC,mBAAmB,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI;IAE9F;;;OAGG;WACW,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,mBAAmB,EAAE,UAAU,GAAG,IAAI;IAU1F;;;OAGG;WACW,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM;IAI9D;;;OAGG;WACW,WAAW,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,yBAAyB,GAAG,IAAI;CAwB5F;AAED;;GAEG;AACH,qBAAa,oCAAqC,SAAQ,cAAc;IACtE,OAAc,aAAa,SAAkD;gBAC1D,qBAAqB,EAAE,UAAU,EAAE,YAAY,GAAE,MAA2D;CAGhI"}
1
+ {"version":3,"file":"ElementDrivesTextAnnotation.d.ts","sourceRoot":"","sources":["../../../src/annotations/ElementDrivesTextAnnotation.ts"],"names":[],"mappings":"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"}
@@ -5,10 +5,11 @@
5
5
  /** @packageDocumentation
6
6
  * @module Elements
7
7
  */
8
- import { Id64 } from "@itwin/core-bentley";
8
+ import { BeEvent, Id64 } from "@itwin/core-bentley";
9
9
  import { QueryBinder, RelatedElement, traverseTextBlockComponent } from "@itwin/core-common";
10
10
  import { ECVersion } from "@itwin/ecschema-metadata";
11
11
  import { createUpdateContext, updateAllFields, updateElementFields, updateFields } from "../internal/annotations/fields";
12
+ import { createFieldFormatting, setFieldFormatting } from "../internal/annotations/fieldSpecs";
12
13
  import { _implicitTxn } from "../internal/Symbols";
13
14
  import { ElementDrivesElement } from "../Relationship";
14
15
  import { EditTxn } from "../EditTxn";
@@ -110,12 +111,69 @@ export class ElementDrivesTextAnnotation extends ElementDrivesElement {
110
111
  this.updateFieldDependenciesImpl(arg2[_implicitTxn], arg1);
111
112
  }
112
113
  /** Recompute the display strings of all [FieldRun]($common)s in a [TextBlock]($common).
114
+ *
115
+ * `"quantity"` and `"coordinate"` fields are formatted through the FormatSets configured for
116
+ * `args.iModel` by [[registerFieldFormatting]], or otherwise through the format each
117
+ * KindOfQuantity's schema declares.
118
+ *
119
+ * A field whose format cannot be resolved falls back to `value.toString()` and is logged. A
120
+ * field whose property cannot be resolved, or whose format throws, is logged and rendered as
121
+ * [FieldRun.invalidContentIndicator]($common); one bad field does not abandon the rest of the
122
+ * block.
113
123
  * @returns the number of fields whose display strings were modified.
114
- * @throws Error if evaluation of any field fails.
115
124
  */
116
125
  static evaluateFields(args) {
117
126
  return updateFields(args.block, createUpdateContext(undefined, args.iModel, false));
118
127
  }
128
+ /** Configures how `"quantity"` and `"coordinate"` [FieldRun]($common)s in `args.iModel` are
129
+ * formatted by [[evaluateFields]] and by `TxnManager` field-update callbacks.
130
+ *
131
+ * Calling this is optional. An iModel with no registration formats every field using the
132
+ * presentation format its schema declares for the KindOfQuantity, in the metric unit system.
133
+ * Register to layer application [FormatSet]($ecschema-metadata)s over those schema defaults,
134
+ * or to select a different unit system. Formats resolve in this order:
135
+ *
136
+ * 1. The FormatSet named by the field's [QuantityFieldFormatOptions.formatSet]($common).
137
+ * 2. The FormatSet adopted for the iModel ([[FieldFormattingArgs.formatSet]]).
138
+ * 3. The KindOfQuantity's presentation format for [[FieldFormattingArgs.unitSystem]].
139
+ * 4. `value.toString()`, with a warning logged.
140
+ *
141
+ * Units resolve through the bundled BIS [BasicUnitsProvider]($core-quantity) only. A field
142
+ * whose persistence unit, or whose format's units, are defined solely by the iModel's own
143
+ * schemas falls to step 4.
144
+ *
145
+ * Pass the iModel-wide default as `formatSet` and any per-field alternatives as `formatSets`,
146
+ * keyed by the id that [FieldRun]($common)s name via
147
+ * [QuantityFieldFormatOptions.formatSet]($common):
148
+ *
149
+ * ```ts
150
+ * ElementDrivesTextAnnotation.registerFieldFormatting({
151
+ * iModel,
152
+ * formatSet: metricFormatSet,
153
+ * formatSets: [{ id: imperialFormatSetId, formatSet: imperial }],
154
+ * });
155
+ * ```
156
+ *
157
+ * Each call replaces any prior registration for the same iModel; calling it with only
158
+ * `iModel` reverts to the schema defaults. Registering does not re-evaluate existing
159
+ * annotations; listen to [[onFieldFormattingChanged]] to do so. The registration lives as long
160
+ * as the `IModelDb` object and is released with it.
161
+ * @see [Quantity formatting for text annotation fields]($docs/learning/backend/TextAnnotationFields.md)
162
+ * @beta
163
+ */
164
+ static registerFieldFormatting(args) {
165
+ setFieldFormatting(args.iModel, createFieldFormatting(args));
166
+ this.onFieldFormattingChanged.raiseEvent({ iModel: args.iModel });
167
+ }
168
+ /** Raised after every [[registerFieldFormatting]] call, once the new configuration is in
169
+ * place, whether or not it differs from the previous one. Applications that cache formatted
170
+ * output, or that want to re-evaluate existing annotations against a newly adopted FormatSet,
171
+ * should listen here. A listener that throws does not undo the registration; its error is
172
+ * reported through [UnexpectedErrors]($bentley) rather than to the caller of
173
+ * `registerFieldFormatting`.
174
+ * @beta
175
+ */
176
+ static onFieldFormattingChanged = new BeEvent();
119
177
  /** When copying an [[ITextAnnotation]] from one iModel into another, remaps the element Ids in any [FieldPropertyHost]($common) within the cloned element
120
178
  * so that they refer to elements in the `context`'s target iModel, and sets any Ids that cannot be remapped to [Id64.invalid]($bentley).
121
179
  * Implementations of `ITextAnnotation` should invoke this function from their implementations of [[Element.onCloned]].