Element Copying

The DgnPlatform copying framework defines an algorithm to produce a valid copy of an element. More...

## Classes

struct  ElementCopyContext
Use the ElementCopyContext class to copy elements. More...

struct  ElementCopyContext::IEventHandler
Element copy event handler. More...

## Enumerations

enum  CopyContextLevelOption {
ByUserPreference = 0, CopyIfNotFound = 1, CopyIfDifferent = 2, CopyAlways = 3,
AlreadyRemapped = 4, CopyByNumber = 5
}
How to handle levels when copying an element between files. More...

enum  SharedCellNameConflicts { Undefined = -1, None = 0, HasDefId = 1, All = 2 }

## Functions

virtual void OnElementCopied (EditElementHandleR eh, ElementCopyContext &, bool isPre)=0

virtual void OnLevelCloned (ULong destLevelId, ULong sourceLevelId, bool uniqueName, ElementCopyContext &cc)=0

virtual void OnProcessDatabaseLinkages (EditElementHandleR eh, ElementCopyContext &)

## Intialization

~ElementCopyContext ()

ElementCopyContext (ElementCopyContext const &)

ElementCopyContext & operator= (ElementCopyContext const &)

ElementCopyContext (DgnModelRefP dest)
Intialize an ElementCopyContext. More...

## Copying an element

StatusInt DoCopy (EditElementHandleR eh)
Turn the specified element into a copy of the original. More...

## Source and destination models

void SetDestinationModelRef (DgnModelRefP)
Identify the model that is the destination of the copy. More...

DgnModelP GetDestinationModel ()
Query the model that is the destination of the copy. More...

DgnModelRefP GetDestinationModelRef ()
Query the model that is the destination of the copy. More...

void SetSourceModelRef (DgnModelRefP)
Identify the model that is the source of the copy. More...

DgnModelRefP GetSourceModelRef (ElementHandleCP el=0)
Query the model or reference attachment that is the source of the copy. More...

DgnModelP GetSourceCache (ElementHandleCP el=0)
Query the model that is the source of the copy. More...

bool IsCopyFromReference ()
Query if the source model is a reference attachment of the destination model. (Returns false if the destination model is itself a reference attachment to some other model.) More...

bool IsSameCache (ElementHandleCP eh=0)
Query if the copy starts and ends in the same model (possibly going through a reference transform). More...

bool IsSameFile (ElementHandleCP eh=0)
Query if the copy starts and ends in the same file (possibly different models). More...

void GetModels (DgnModelRefP *srcModelRefP, DgnModelRefP *destModelP, bool *sameCacheP, bool *sameFileP, ElementHandleCP elemHandle=0)
Query source and destination models. More...

void SetModelFromElmdscr (bool useElmdscr)
Set the copy context so that the model ref from the element descriptor is used as the source model ref for the copy, unless it is NULL in which case the source model ref is used. More...

bool GetModelFromElmdscr () const

## IEventHandler - Reacting to DoCopy

static void AddHandler (IEventHandler &handler)
Register a handler to react when DoCopy is called. More...

static void DropHandler (IEventHandler &handler)
Un-register a handler. More...

## Copy options

void SetWriteElements (bool writeAllElements)
Set the copy context value that determines whether the element descriptor being copied will get written to the destination model ref. More...

bool GetWriteElements () const
Get the current value of the write elements option. More...

void SetMatchDimToDestination (bool changeDimension)
Setting this option to false in the copy context will cause the element descriptor passed in not to take on the dimensionality (2D/3D) of the destination model ref. More...

bool GetMatchDimToDestination () const
Get the current value of the match element dimension to destination model option. More...

void SetScaleReferenceDimensions (bool)
Set to control how the value of a dimension in a reference is scaled when copied to the destination model. More...

bool GetScaleReferenceDimensions () const
Get the current value of the scale reference dimensions option. More...

void SetTransformToDestination (bool transformToDestination)
Defines whether copied elements should be scaled to match the units in the destination model. More...

bool GetTransformToDestination () const
Get the current value of transform to destination option. More...

void SetAssignNewGGs (bool assignNewGGs)
Defines whether copied elements should maintain their current graphic group or should be assigned to a new group. More...

bool GetAssignNewGGs () const
Get the current value of the assign new graphic group option. More...

void SetAssignNewNodes (bool assignNewNodes)
Defines whether copied elements should maintain their current text node numbers or should be assigned a new number. More...

bool GetAssignNewNodes () const
Get the current value of the assign new text node number option. More...

void SetViewport (ViewportP vp)
Sets the view that will be used to flatten the elements if the source model is 3D and the destination model is 2D. More...

ViewportP GetViewport () const
Get the current viewport used for a flatten direction when copying from a 3d source model to a 2d destination model. More...

void SetLevelHandling (CopyContextLevelOption levelOption)
Determine the way in which levels are brought from the source model ref to the destination model ref. More...

void SetKeepRefColorIndexOnCopy (bool)
Set to control how 0-255 color indices are remapped. Set to true to preserve color index instead of searching for a matching RGB. More...

bool GetKeepRefColorIndexOnCopy ()
Get the current value of the reference color index remap option. More...

void SetSharedCellNameConflicts (SharedCellNameConflicts)
Specify what to do when a name conflict is encountered when deep-copying a shared cell definition. More...

SharedCellNameConflicts GetSharedCellNameConflicts () const
Get the current value of the option that controls how shared cell name conflicts are resolved. More...

void SetDontPlaceTagsInGG (bool)
Set the option to control whether tag elements in a graphic group are copied to a new graphic group. More...

bool GetDontPlaceTagsInGG () const
Get the current value of the option for tags and graphic groups. More...

void SetDisableAnnotationScaling (bool b)
Direct that annotation scale should not be transformed during the copy. More...

bool GetDisableAnnotationScaling () const
Get current value of annotation scaling option. More...

## Detailed Description

The DgnPlatform copying framework defines an algorithm to produce a valid copy of an element.

The copying framework is implemented by the ElementCopyContext class, with help from DependencyManager, Bentley::DgnPlatform::ElementHandler, and Bentley::DgnPlatform::XAttributeHandler.

Copying an element is not usually as simple as duplicating its data. Copying must adjust:

• Units and dimensionality
• Reference attachment transforms
• Supporting data, such as levels, colors, styles, and shared cell definitions
• Dependency pointers and other relationships
• Graphic Groups

Most adjustments can be done in several ways. The copying framework algorithm includes the options and strategies that can be applied when making necessary adjustments.

The copying framework delegates the work of making adjustments to element and XAttribute handlers, as described in detail below.

The ElementCopyContext helper object holds remapping data that is used by the copying algorithm and by handlers. This remapping data can be applied to and shared by multiple elements for efficiency and to support dependency remapping.

The ElementCopyContext object can also hold custom data during a copy operation that can be accessed by handler methods across multiple elements. See ElementCopyContext::AddCustomData.

The ElementCopyContext::DoCopy method transforms the element that you pass to it from an element in the source model to an element in the destination model. That is, DoCopy modifies the element that you pass in. DoCopy optionally writes the transformed element to the destination model (this is the default). See ElementCopyContext::SetWriteElements.

See ElementCopying Code Samples for examples.

### Geometric Transformation

The copying framework may transform the units, dimensionality, and possibly the geometry of an element when copying between models. Other types of transformations may also be necessary. ElementCopyContext cooperates with element handlers to do these transformations. ElementCopyContext first calls:

• Handler::PreprocessCopy to do any general transformations that it requires.

ElementCopyContext then calls the following handler methods as necessary, depending on the source and destination models:

### Levels and Styles

Also, when copying an element between two files, ElementCopyContext ensures that all supporting data, including color tables, levels and styles, are present in the destination model, in order to create a copy that is equivalent to the source.

In some cases, ElementCopyContext must copy the supporting data from the source file to the destination file. In other cases, it may find that the necessary data is already present in the destination file. Styles and levels are resolved by name, so if a style with the same name exists in both the source and destination models, the element will be remapped to the style in the destination. For reasons of efficiency, you should use the same ElementCopyContext object when copying multiple elements.

ElementCopyContext modifies the transformed element to reference supporting data in the destination file.

Note: In order to ensure that all changes including copied levels and styles are written to the destination file, the ElementCopyContext object must be destroyed or you must call SetDestinationModelRef(NULL) on the ElementCopyContext object before you call ProcessChanges on the destination file.

### Dependencies and Other Relationships

Even when copying elements within a single model, you should use ElementCopyContext because it maintains dependencies between elements. ElementCopyContext collaborates with DependencyManagerLinkage and IXAttributePointerContainerHandler to allow handlers to remap their dependency pointers. A handler's Handler::PreprocessCopy method can also get involved in remapping dependency pointers.

If an element has XAttributes that point to other elements, it is the job of the handler of the element or of the XAttributes to control how copy affects those pointers. Some common patterns are:

• Deep-copying. In some cases, the pointed-to element, known as the "root", should be copied along with the dependent. This is called "deep copying".
• Singleton. In some cases, all dependents of a given type should point to a common root element. When a dependent is copied to a new model, then it should only deep-copy its root if no root exists in the destination model. Otherwise, it should remap its pointer to the existing root in the destination model.
• Optional remapping. In still other cases, the job of copying the root element is left to the user or to the application, and dependent's pointer will be remapped to the copy of the root if it was copied or revert the to original root if not.
• Drop. Finally, in some cases, the dependent may want to drop its pointer when it is copied.

The DgnPlatform element copying framework offers mechanisms to help handlers implement these patterns.

# Copying Framework Algorithm

-# The application creates an ElementCopyContext object, specifies the destination model, and sets copying options.
-# The application calls ElementCopyContext::DoCopy (dependent), which calls:
-# ElementCopyContext::IEventHandler::OnElementCopied with true
-# \ref XAttributeCopyingAlgorithm
-# Handler::PreprocessCopy
-# Note: The element handler can modify or discard XAttributes at this time
-# \ref DependencyPointerPreProcessing
-# Handler::ConvertTo2d or Handler::ConvertTo3d, if necessary
-# Handler::ChangeUnits, if necessary
-# Handler::ApplyTransform, if necessary
-# ElementCopyContext::IEventHandler::OnElementCopied with false
-# The application calls ElementCopyContext::DoCopy on other elements
-# The application calls DependencyManger::ProcessAffected, which calls
-# \ref DependencyPointerRemapping


# XAttribute Copying Algorithm

ElementCopyContext::DoCopy does the following:

-# For each XAttribute on dependent
-# Schedules a copy of the original XAttribute on dependent [\ref Note5]
-# Calls XAttributeHandler::PreprocessCopy [\ref Note5]
-# handler can make deep copies of its root elements, if desired, by calling ElementCopyContext::DeepCopyRoot. [\ref Note1]
-# or, handler can implement singleton semantics, if desired. [\ref Note2]
-# handler should then call PersistentElementPath::PreprocessCopy, which does this: [\ref Note3]
-# If requested, attempts to deep-copy the root:
-# If root has already been copied, then points to the copy.
-# Otherwise, copies the root by calling ElementCopyContext::DeepCopyRoot and points to the copy.
-# Otherwise, replaces the PersistentElementPath data with a remap key which identifies the original root element.
-# This root will be remapped by DependencyManger::ProcessAffected in the last step below.


# Dependency Pointer Pre-processing

Dependending on the copy options of the dependency pointer and on the type of the root, ElementCopyContext will either deep-copy a root element or set up for remapping.

If the strategy calls for deep-copying, ElementCopyContext will call Handler::OnPreProcessDeepCopy to allow the handler to decide how to handle the deep-copy. As outlined above, a handler might want to deep-copy the root, maintain a singleton reference, drop the reference, or opt for remapping.

If the strategy does not call for deep-copying or if the handler's OnPreProcessDeepCopy opts for remapping, then ElementCopyContext will set up the remapping process. Dependency pointer remapping is a three-part process.

1. When copying an element, ElementCopyContext::DoCopy first replaces dependency pointers with remap keys.
2. When ITxn::AddElement adds an element to the destination file, it updates the remap table in order to link the source element to the copied element.
3. Finally, when all elements have been copied, DependencyManager::ProcessAffected goes back and replaces remap keys with new dependency pointers.

ElementCopyContext conducts the first phase of dependency pointer copying by replacing each dependency pointer stored in a DependencyLinkage or an XAttribute with a 64 bit key that identifies the modelRef and elementRef of the target (root) element. This key identifies an entry in the element copying remap table. ElementCopyContext does this by:

• Collaborating with DependencyManagerLinkages to update DependencyLinkages.
• Calling XAttributeHandler::_OnPreprocessCopy on the XAttributes of the copied element.

# Dependency Pointer Remapping

DependencyManager::ProcessAffected conducts the final phase of dependency pointer copying by replacing the remap keys stored in DependencyLinkages and XAttributes with element identifiers to link copied dependent elements with original or copied root elements. DependencyManager::ProcessAffected does this by:

• Collaborating with DependencyManagerLinkages to update DependencyLinkages.
• Collaborating with XAttributeHandler to update XAttributes as follows:
1. For each element added in previous steps
1. For each XAttribute on the element
1. ProcessAffected calls IXAttributePointerContainerHandler::OnPreprocessCopyRemapIds
1. IXAttributePointerContainerHandler handler is expected to call PersistentElementPath::PreprocessCopyRemapIds, which does this:
1. Uses the stored remap key to look up the remap table entry.
2. If entry contains destination remap key, then extract destination element and point to it.
3. Otherwise, set PEP to NULL (unresolved).
2. IXAttributePointerContainerHandler handler can check to see if the remap failed.
1. Return non-zero to have the XAttribute removed from the host element. [Note4]

Whenever remapping fails, ProcessAffected calls Handler::_OnPostprocessCopyRemapRestore on the affected dependent element. This method allows the handler of the dependent element to repair the dependency pointer, e.g., turn a far reference into a near reference.

### Notes

1. Decide if you want a deep copy or not. If so, call ElementCopyContext::DeepCopyRoot on the root. This sets up a remap table entry for it.

2. Maintaining a singleton is a special case of making a deep copy. Rather than always deep-copying the root, you search for it in the destination model (by some means known to your handler) and only deep-copy it if not found.

3. The PersistentElementPath::CopyOption argument controls how PersistentElementPath::PreprocessCopy handles pointers.

If you made a deep copy of the root or if the root is a singleton, call PersistentElementPath::PreprocessCopy and pass COPYCONTEXT_DeepCopyRootsAlways. That tells PreprocessCopy to point to the copy immediately. If you did not make a deep copy and want to preserve the original reference or if you want to leave the copying up to the caller, then call PreprocessCopy with COPYOPTION_RemapRootsWithinSelection. That tells PreprocessCopy to store a remap key, to be resolved later by PreprocessCopyRemapIds.

4. If your XAttribute handler's PreprocessCopyRemapIds method returns non-zero, then your XAttribute will be removed from the host element. The handler might do this if it detects a broken link. Or, the handler might do this in order to implement the policy of always dropping a pointer when the dependent is copied.

5. If the caller schedules an XAttribute on the element descriptor before calling ElementCopyContext::DoCopy, then ElementCopyContext::DoCopy will not try to schedule another copy of that XAttribute and will not call its PreprocessCopy method.

## Enumeration Type Documentation

 enum CopyContextLevelOption
strong

How to handle levels when copying an element between files.

Bentley::DgnPlatform::ElementCopyContext::SetLevelHandling
Enumerator
ByUserPreference

The value of the user preference determines the behavior.

CopyIfNotFound

Only copy levels that are not found in destination model ref.

CopyIfDifferent

Copy any levels that are not found or differ from those in the destination model ref.

CopyAlways

Copy all levels from the source to the destination, overwriting any that exist in the destination model ref.

Do not copy any levels from the source model ref and assume that the levels have been converted to match the destination model ref.

CopyByNumber

Copy levels using level number not name to compare (V7 workmode).

 enum SharedCellNameConflicts
strong
Remarks
Required library : DgnPlatform<ApiNumber>.lib i.e. DgnPlatform5.lib
Enumerator
Undefined
None
HasDefId
All

## Function Documentation

 static void AddHandler ( IEventHandler & handler )
static

Register a handler to react when DoCopy is called.

Parameters
 [in] handler the handler to register. This function calls AddRef on handler.
 StatusInt DoCopy ( EditElementHandleR eh )

Turn the specified element into a copy of the original.

Units and ids are remapped from the source to the destination model.

Parameters
 eh The element that is to be transformed.
Returns
non-zero error status if remapping failed or write to cache failed.
Remarks
See GetWriteElements for whether the transformed element is actually written to the destination model.
Invokes the following handler methods:
The model of eh will be used as the source modelref.
In order to ensure that all changes are written to the destination file, this ElementCopyContext object must be destroyed or you must call SetDestinationModelRef(NULL) before you call ProcessChanges on the destination file.
IEventHandler
 static void DropHandler ( IEventHandler & handler )
static

Un-register a handler.

Parameters
 [in] handler the handler to un-register. This function calls Release on handler.
 ElementCopyContext ( ElementCopyContext const & )
 ElementCopyContext ( DgnModelRefP dest )
explicit

Intialize an ElementCopyContext.

Parameters
 dest The destination model
Remarks
This constructor sets the following defaults:
• SetDestinationModelRef (destModel);
• SetWriteElements (true);
• SetAssignNewGGs (true);
• SetAssignNewNodes (true);
• SetTransformToDestination (true);
• SetMatchDimToDestination (true);
• SetSharedCellNameConflicts (SharedCellNameConflicts::HasDefId);
• SetLevelHandling (CopyContextLevelOption::CopyIfNotFound); All other options are set to false.
Be sure to call SetSourceModelRef before calling DoCopy
 bool GetAssignNewGGs ( ) const

Get the current value of the assign new graphic group option.

 bool GetAssignNewNodes ( ) const

Get the current value of the assign new text node number option.

 DgnModelP GetDestinationModel ( )

Query the model that is the destination of the copy.

 DgnModelRefP GetDestinationModelRef ( )

Query the model that is the destination of the copy.

 bool GetDisableAnnotationScaling ( ) const

Get current value of annotation scaling option.

 bool GetDontPlaceTagsInGG ( ) const

Get the current value of the option for tags and graphic groups.

 bool GetKeepRefColorIndexOnCopy ( )

Get the current value of the reference color index remap option.

 bool GetMatchDimToDestination ( ) const

Get the current value of the match element dimension to destination model option.

 bool GetModelFromElmdscr ( ) const
 void GetModels ( DgnModelRefP * srcModelRefP, DgnModelRefP * destModelP, bool * sameCacheP, bool * sameFileP, ElementHandleCP elemHandle = 0 )

Query source and destination models.

Parameters
 srcModelRefP [optional] source model or reference attachment destModelP [optional] destination model or reference attachment. sameCacheP [optional] are source and destination the same cache? (Possibly involving a reference transform) sameFileP [optional] are source and destination models in the same file? elemHandle [optional] If not NULL, get source model from this element, if ElementCopyContext has none or if CLONE_OPTIONS_ModelFromElmdscr was specified
 bool GetScaleReferenceDimensions ( ) const

Get the current value of the scale reference dimensions option.

 SharedCellNameConflicts GetSharedCellNameConflicts ( ) const

Get the current value of the option that controls how shared cell name conflicts are resolved.

 DgnModelP GetSourceCache ( ElementHandleCP el = 0 )

Query the model that is the source of the copy.

 DgnModelRefP GetSourceModelRef ( ElementHandleCP el = 0 )

Query the model or reference attachment that is the source of the copy.

 bool GetTransformToDestination ( ) const

Get the current value of transform to destination option.

 ViewportP GetViewport ( ) const

Get the current viewport used for a flatten direction when copying from a 3d source model to a 2d destination model.

 bool GetWriteElements ( ) const

Get the current value of the write elements option.

 bool IsCopyFromReference ( )

Query if the source model is a reference attachment of the destination model. (Returns false if the destination model is itself a reference attachment to some other model.)

 bool IsSameCache ( ElementHandleCP eh = 0 )

Query if the copy starts and ends in the same model (possibly going through a reference transform).

 bool IsSameFile ( ElementHandleCP eh = 0 )

Query if the copy starts and ends in the same file (possibly different models).

 virtual void OnElementCopied ( EditElementHandleR eh, ElementCopyContext & , bool isPre )
pure virtual
 virtual void OnLevelCloned ( ULong destLevelId, ULong sourceLevelId, bool uniqueName, ElementCopyContext & cc )
pure virtual
 virtual void OnProcessDatabaseLinkages ( EditElementHandleR eh, ElementCopyContext & )
virtual
 ElementCopyContext& operator= ( ElementCopyContext const & )
 void SetAssignNewGGs ( bool assignNewGGs )

Defines whether copied elements should maintain their current graphic group or should be assigned to a new group.

Note that all elements with the same graphic group number will be remapped to the same new number as long as the same context is used.

Remarks
This is defaulted to true in the constructor.
Parameters
 assignNewGGs defines whether to assign new graphic groups or maintain the current values.
 void SetAssignNewNodes ( bool assignNewNodes )

Defines whether copied elements should maintain their current text node numbers or should be assigned a new number.

Remarks
This is defaulted to true in the constructor.
If the destination file is the masterfile, then new text node numbers are allocated from the tcb.
Parameters
 assignNewNodes defines whether to assign new text node numbers or maintain the current values.
 void SetDestinationModelRef ( DgnModelRefP )

Identify the model that is the destination of the copy.

Remarks
In order to ensure that all changes are written to the destination file, this ElementCopyContext object must be destroyed or you must call SetDestinationModelRef(NULL) before you call ProcessChanges on the destination file.
 void SetDisableAnnotationScaling ( bool b )

Direct that annotation scale should not be transformed during the copy.

 void SetDontPlaceTagsInGG ( bool )

Set the option to control whether tag elements in a graphic group are copied to a new graphic group.

 void SetKeepRefColorIndexOnCopy ( bool )

Set to control how 0-255 color indices are remapped. Set to true to preserve color index instead of searching for a matching RGB.

 void SetLevelHandling ( CopyContextLevelOption levelOption )

Determine the way in which levels are brought from the source model ref to the destination model ref.

Remarks
This is defaulted to CopyContextLevelOption::CopyIfNotFound in the constructor.
Parameters
 levelOption determines how the levels are handled during a copy. See CopyContextLevelOption.
 void SetMatchDimToDestination ( bool changeDimension )

Setting this option to false in the copy context will cause the element descriptor passed in not to take on the dimensionality (2D/3D) of the destination model ref.

The value will always be treated as true if the elements are being written to the file (see SetWriteElements).

Remarks
This is defaulted to true in the constructor.
Parameters
 changeDimension true to allow elements to be converted to the same dimensionality as the destination model ref; false to keep the elements as they are.
 void SetModelFromElmdscr ( bool useElmdscr )

Set the copy context so that the model ref from the element descriptor is used as the source model ref for the copy, unless it is NULL in which case the source model ref is used.

The primary reason to use this option is for cells that may be from multiple model refs.

Remarks
This is defaulted to false in the constructor.
Parameters
 useElmdscr true to use the model refs on the element descriptors; false to use the source model set for this copy context.
 void SetScaleReferenceDimensions ( bool )

Set to control how the value of a dimension in a reference is scaled when copied to the destination model.

 void SetSharedCellNameConflicts ( SharedCellNameConflicts )

Specify what to do when a name conflict is encountered when deep-copying a shared cell definition.

This defaults to SharedCellNameConflicts::HasDefId.

 void SetSourceModelRef ( DgnModelRefP )

Identify the model that is the source of the copy.

The source may be a reference attachment.

Remarks
If the source modelRef is changing, this function will invalidate existing remap info.
 void SetTransformToDestination ( bool transformToDestination )

Defines whether copied elements should be scaled to match the units in the destination model.

Remarks
This is defaulted to true.
Parameters
 transformToDestination defines whether to scale the elements by the difference in units between the models.
 void SetViewport ( ViewportP vp )

Sets the view that will be used to flatten the elements if the source model is 3D and the destination model is 2D.

Remarks
This is defaulted to top in the constructor.
Parameters
 vp defines the view to use for flattening if necessary.
 void SetWriteElements ( bool writeAllElements )

Set the copy context value that determines whether the element descriptor being copied will get written to the destination model ref.

Regardless of the value passed in, the additional necessary information (styles, levels, etc.) will be copied to the destination model ref.

Remarks
This is defaulted to true in the constructor.
Parameters
 writeAllElements true to copy all elements to the destination model ref; false to just copy necessary additional elements.
 ~ElementCopyContext ( )