Skip to content

Commit

Permalink
remove fromTransformation and preserveElementIdsForFiltering from imp…
Browse files Browse the repository at this point in the history
…orter options
  • Loading branch information
derbynn committed Oct 8, 2024
1 parent 2fda8ac commit 0212295
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 62 deletions.
105 changes: 56 additions & 49 deletions packages/transformer/src/IModelImporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,6 @@ export interface IModelImportOptions {
* @see [IModelImporter Options]($docs/learning/transformer/index.md#IModelImporter)
*/
autoExtendProjectExtents?: boolean | { excludeOutliers: boolean };
/**
* Indicates whether the importer is used in a transformation process.
* If `true`, the importer was passed into the transformer, or the transformer constructed this instance of the importer
*/
fromTransformation?: boolean;
/** See [IModelTransformOptions]($transformer) */
preserveElementIdsForFiltering?: boolean;
/** If `true`, simplify the element geometry for visualization purposes. For example, convert b-reps into meshes.
* @default false
*/
Expand Down Expand Up @@ -110,17 +103,17 @@ export class IModelImporter {

/**
* A set of elementIds that the transformer adds to while exporting elements to indicate that the element already exists in the target.
* Defaults to an empty set.
* Defaults to undefined.
* @note
*
* This is used as an optimization when `preserveElementIdsForFiltering` is set to `true`
* This is used as an optimization when the transformer `preserveElementIdsForFiltering` option is set to `true`
* In normal cases where this option set to `false`,
* the importer determines whether to insert or update based off of whether the ID is defined on the `elementProps` passed to `importElement`.
* However, with `preserveElementIdsForFiltering` set to `true`, IDs are always set, so we can't determine insert/update like the normal case.
* The transformer already knows if an element exists or not by the time `importElement` is called and pushes to this set with `markElementAsUpdate`.
* The transformer already knows if an element exists or not by the time `importElement` is called and pushes to this set with `markElementToUpdateForPreserveId`.
* @note This set should stay small, as right after the transformer pushes to it, the importer will remove from the set.
*/
private _elementsToUpdate = new Set<Id64String>([]);
public elementIdsToUpdateForPreserveId: Set<Id64String> | undefined;

/** The set of elements that should not be updated by this IModelImporter.
* Defaults to an empty set.
Expand Down Expand Up @@ -152,9 +145,6 @@ export class IModelImporter {
this.targetDb = targetDb;
this.options = {
autoExtendProjectExtents: options?.autoExtendProjectExtents ?? true,
fromTransformation: options?.fromTransformation ?? false,
preserveElementIdsForFiltering:
options?.preserveElementIdsForFiltering ?? false,
simplifyElementGeometry: options?.simplifyElementGeometry ?? false,
skipPropagateChangesToRootElements:
options?.skipPropagateChangesToRootElements ?? true,
Expand All @@ -176,13 +166,19 @@ export class IModelImporter {
}

/**
* Marks an element so that it can be updated during import when [[IModelImportOptions.preserveElementIdsForFiltering]] is set to true
* Marks an element so that it can be updated during import when the [[IModelTransformOptions.preserveElementIdsForFiltering]] is set to true.
*/
public markElementAsUpdate(elementId: Id64String) {
public markElementToUpdateForPreserveId(elementId: Id64String) {
if (!this.elementIdsToUpdateForPreserveId) {
throw new Error(
"The elementIdsToUpdateForPreserveId set is not initialized. Ensure this set is initialized when `preserveElementIdsForFiltering` is set true"
);
}

if (this._rootElementIds.has(elementId)) {
return;
}
this._elementsToUpdate.add(elementId);
this.elementIdsToUpdateForPreserveId.add(elementId);
}

/** Import the specified ModelProps (either as an insert or an update) into the target iModel. */
Expand Down Expand Up @@ -257,27 +253,45 @@ export class IModelImporter {
return `${modelProps.classFullName} [${modelProps.id!}]`;
}

/** Import the specified ElementProps (either as an insert or an update) into the target iModel. */
public importElement(elementProps: ElementProps): Id64String {
const tryUpdateElement = (elemProps: ElementProps): void => {
try {
/**
* Determines whether element IDs should be preserved for filtering.
*
* This function checks if the `elementIdsToUpdateForPreserveId` set is defined.
* If it is undefined, the function returns `false`, indicating that element IDs should not be preserved.
* Otherwise, it returns `true`. *
*/
private shouldPreserveElementIdsForFiltering(): boolean {
if (this.elementIdsToUpdateForPreserveId === undefined) {
return false;
}
return true;
}

/**
* Tries to update an element with the specified element properties.
* If a duplicate code error occurs, it assigns a new unique code value and retries the update
*/
private tryUpdateElement(elemProps: ElementProps) {
try {
this.onUpdateElement(elemProps);
} catch (err) {
if ((err as IModelError).errorNumber === IModelStatus.DuplicateCode) {
assert(
elemProps.code.value !== undefined,
"NULL code values are always considered unique and cannot clash"
);
this._duplicateCodeValueMap.set(elemProps.id!, elemProps.code.value);
// Using NULL code values as an alternative is not valid because definition elements cannot have NULL code values.
elemProps.code.value = Guid.createValue();
this.onUpdateElement(elemProps);
} catch (err) {
if ((err as IModelError).errorNumber === IModelStatus.DuplicateCode) {
assert(
elemProps.code.value !== undefined,
"NULL code values are always considered unique and cannot clash"
);
this._duplicateCodeValueMap.set(elemProps.id!, elemProps.code.value);
// Using NULL code values as an alternative is not valid because definition elements cannot have NULL code values.
elemProps.code.value = Guid.createValue();
this.onUpdateElement(elemProps);
} else {
throw err;
}
} else {
throw err;
}
};
}
}

/** Import the specified ElementProps (either as an insert or an update) into the target iModel. */
public importElement(elementProps: ElementProps): Id64String {
if (
undefined !== elementProps.id &&
this.doNotUpdateElement(elementProps.id)
Expand All @@ -288,7 +302,8 @@ export class IModelImporter {
);
return elementProps.id;
}
if (this.options.preserveElementIdsForFiltering) {

if (this.shouldPreserveElementIdsForFiltering()) {
if (elementProps.id === undefined) {
throw new IModelError(
IModelStatus.BadElement,
Expand All @@ -300,30 +315,22 @@ export class IModelImporter {
// since default subcategories always exist and always will be inserted after their categories, we treat them as an update
// to prevent duplicate inserts.
// Always present elements (0xe, 0x1, 0x10) also will be updated to prevent duplicate inserts.
// Otherwise we always insert during a preserveElementIdsForFiltering operation
if (
(isSubCategory(elementProps) && isDefaultSubCategory(elementProps)) ||
this._rootElementIds.has(elementProps.id)
) {
this.onUpdateElement(elementProps);
} else {
// if [[IModelImportOptions.fromTransformation]] is false, try and get the element (update if it exists)
const shouldUpdateElement =
!this.options.fromTransformation &&
this.targetDb.elements.tryGetElement(elementProps.id);
if (
this._elementsToUpdate.has(elementProps.id) ||
shouldUpdateElement
) {
tryUpdateElement(elementProps);
this._elementsToUpdate.delete(elementProps.id);
if (this.elementIdsToUpdateForPreserveId?.has(elementProps.id)) {
this.tryUpdateElement(elementProps);
this.elementIdsToUpdateForPreserveId?.delete(elementProps.id);
} else {
this.onInsertElement(elementProps);
}
}
} else {
if (undefined !== elementProps.id) {
tryUpdateElement(elementProps);
this.tryUpdateElement(elementProps);
} else {
this.onInsertElement(elementProps); // targetElementProps.id assigned by insertElement
}
Expand All @@ -339,7 +346,7 @@ export class IModelImporter {
/* eslint-disable deprecation/deprecation */
try {
const elementId = this.targetDb.nativeDb.insertElement(elementProps, {
forceUseId: this.options.preserveElementIdsForFiltering,
forceUseId: this.shouldPreserveElementIdsForFiltering(),
});
// set the id like [IModelDb.insertElement]($backend), does, the raw nativeDb method does not
elementProps.id = elementId;
Expand Down
19 changes: 6 additions & 13 deletions packages/transformer/src/IModelTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,8 +658,6 @@ export class IModelTransformer extends IModelExportHandler {
// initialize importer and targetDb
if (target instanceof IModelDb) {
this.importer = new IModelImporter(target, {
preserveElementIdsForFiltering:
this._options.preserveElementIdsForFiltering,
skipPropagateChangesToRootElements:
this._options.skipPropagateChangesToRootElements,
});
Expand All @@ -668,7 +666,6 @@ export class IModelTransformer extends IModelExportHandler {
this.validateSharedOptionsMatch();
}
this.targetDb = this.importer.targetDb;
this.importer.options.fromTransformation = true;
// create the IModelCloneContext, it must be initialized later
this.context = new IModelCloneContext(this.sourceDb, this.targetDb);

Expand Down Expand Up @@ -697,14 +694,6 @@ export class IModelTransformer extends IModelExportHandler {
* @note This expects that the importer is already set on the transformer.
*/
private validateSharedOptionsMatch() {
if (
Boolean(this._options.preserveElementIdsForFiltering) !==
this.importer.options.preserveElementIdsForFiltering
) {
const errMessage =
"A custom importer was passed as a target but its 'preserveElementIdsForFiltering' option is out of sync with the transformer's option.";
throw new Error(errMessage);
}
if (
Boolean(this._options.skipPropagateChangesToRootElements) !==
this.importer.options.skipPropagateChangesToRootElements
Expand Down Expand Up @@ -1943,16 +1932,20 @@ export class IModelTransformer extends IModelExportHandler {
: undefined;

if (this._options.preserveElementIdsForFiltering) {
const isValid = Id64.isValid(targetElementId);
// initialize set if undefined to indicate in importer that preserveElementIdsForFiltering option is true
if (!this.importer.elementIdsToUpdateForPreserveId) {
this.importer.elementIdsToUpdateForPreserveId = new Set<Id64String>([]);
}

const isValid = Id64.isValid(targetElementId);
if (isValid && targetElementId !== sourceElement.id) {
// Element found with different id
throw new Error(
`Element id(${sourceElement.id}) cannot be preserved. Found a different mapping(${targetElementId}) from source element`
);
} else if (isValid && targetElementId === sourceElement.id) {
// targetElementId is valid (indicating update)
this.importer.markElementAsUpdate(sourceElement.id);
this.importer.markElementToUpdateForPreserveId(sourceElement.id);
} else if (!isValid) {
const sourceInTargetElemProps =
this.targetDb.elements.tryGetElementProps(sourceElement.id);
Expand Down

0 comments on commit 0212295

Please sign in to comment.