/// <summary> /// Clones a sub-hierarchy of a composite hierarchical asset. /// </summary> /// <param name="sourceNodeContainer">The container in which are the nodes of the hierarchy to clone, used to extract metadata (overrides, etc.) if needed.</param> /// <param name="targetNodeContainer">The container in which the nodes of the cloned hierarchy should be created, used to re-apply metadata (overrides, etc.) if needed.</param> /// <param name="asset">The asset from which to clone sub-hierarchies.</param> /// <param name="sourceRootIds">The ids that are the roots of the sub-hierarchies to clone.</param> /// <param name="flags">The flags customizing the cloning operation.</param> /// <param name="idRemapping">A dictionary containing the remapping of <see cref="IIdentifiable.Id"/> if <see cref="AssetClonerFlags.GenerateNewIdsForIdentifiableObjects"/> has been passed to the cloner.</param> /// <returns>A <see cref="AssetCompositeHierarchyData{TAssetPartDesign, TAssetPart}"/> corresponding to the cloned parts.</returns> /// <remarks>The parts passed to this methods must be independent in the hierarchy.</remarks> public static AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart> CloneSubHierarchies([NotNull] AssetNodeContainer sourceNodeContainer, [NotNull] AssetNodeContainer targetNodeContainer, [NotNull] AssetCompositeHierarchy <TAssetPartDesign, TAssetPart> asset, [NotNull] IEnumerable <Guid> sourceRootIds, SubHierarchyCloneFlags flags, [NotNull] out Dictionary <Guid, Guid> idRemapping) { if (sourceNodeContainer == null) { throw new ArgumentNullException(nameof(sourceNodeContainer)); } if (targetNodeContainer == null) { throw new ArgumentNullException(nameof(targetNodeContainer)); } if (asset == null) { throw new ArgumentNullException(nameof(asset)); } if (sourceRootIds == null) { throw new ArgumentNullException(nameof(sourceRootIds)); } // Extract the actual sub hierarchies to clone from the asset into a new instance of AssetCompositeHierarchyData var subTreeHierarchy = new AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart>(); foreach (var rootId in sourceRootIds) { if (!asset.Hierarchy.Parts.ContainsKey(rootId)) { throw new ArgumentException(@"The source root parts must be parts of this asset.", nameof(sourceRootIds)); } subTreeHierarchy.RootParts.Add(asset.Hierarchy.Parts[rootId].Part); subTreeHierarchy.Parts.Add(asset.Hierarchy.Parts[rootId]); foreach (var subTreePart in asset.EnumerateChildParts(asset.Hierarchy.Parts[rootId].Part, true)) { subTreeHierarchy.Parts.Add(asset.Hierarchy.Parts[subTreePart.Id]); } } var assetType = asset.GetType(); // Create a new empty asset of the same type, and assign the sub hierachies to clone to it var cloneAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(assetType); cloneAsset.Hierarchy = subTreeHierarchy; var assetDefinition = AssetQuantumRegistry.GetDefinition(assetType); // We get the node corresponding to the new asset in the source NodeContainer, to be able to generate metadata (overrides, object references) needed for cloning. var rootNode = sourceNodeContainer.GetOrCreateNode(cloneAsset); var externalReferences = ExternalReferenceCollector.GetExternalReferences(assetDefinition, rootNode); var overrides = (flags & SubHierarchyCloneFlags.RemoveOverrides) == 0 ? GenerateOverridesForSerialization(rootNode) : null; // Now we ready to clone, let's just translate the flags and pass everything to the asset cloner. var clonerFlags = AssetClonerFlags.None; if ((flags & SubHierarchyCloneFlags.GenerateNewIdsForIdentifiableObjects) != 0) { clonerFlags |= AssetClonerFlags.GenerateNewIdsForIdentifiableObjects; } if ((flags & SubHierarchyCloneFlags.CleanExternalReferences) != 0) { clonerFlags |= AssetClonerFlags.ClearExternalReferences; } // We don't need to clone the asset itself, just the hierarchy. The asset itself is just useful so the property graph is in a normal context to do what we need. var clonedHierarchy = AssetCloner.Clone(subTreeHierarchy, clonerFlags, externalReferences, out idRemapping); if ((flags & SubHierarchyCloneFlags.RemoveOverrides) == 0) { // We need to propagate the override information to the nodes of the cloned objects into the target node container. // Let's reuse our temporary asset, and get its node in the target node container. rootNode = targetNodeContainer.GetOrCreateNode(cloneAsset); // Replace the initial hierarchy by the cloned one (through the Update method, in case the target container is the same as the source one). rootNode[nameof(AssetCompositeHierarchy <TAssetPartDesign, TAssetPart> .Hierarchy)].Update(clonedHierarchy); // Remap the paths to overriden properties in case we generated new ids for identifiable objects. AssetCloningHelper.RemapIdentifiablePaths(overrides, idRemapping); // Finally apply the overrides that come from the source parts. ApplyOverrides((IAssetNode)rootNode, overrides); } return(clonedHierarchy); }
/// <summary> /// Clones a sub-hierarchy of a composite hierarchical asset. /// </summary> /// <param name="nodeContainer">The container in which are the nodes of the hierarchy to clone and in which to create nodes for the cloned hierarchy, used to propagate metadata (overrides, etc.) if needed.</param> /// <param name="asset">The asset from which to clone sub-hierarchies.</param> /// <param name="sourceRootIds">The ids that are the roots of the sub-hierarchies to clone.</param> /// <param name="flags">The flags customizing the cloning operation.</param> /// <param name="idRemapping">A dictionary containing the remapping of <see cref="IIdentifiable.Id"/> if <see cref="AssetClonerFlags.GenerateNewIdsForIdentifiableObjects"/> has been passed to the cloner.</param> /// <returns>A <see cref="AssetCompositeHierarchyData{TAssetPartDesign, TAssetPart}"/> corresponding to the cloned parts.</returns> /// <remarks>The parts passed to this methods must be independent in the hierarchy.</remarks> public static AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart> CloneSubHierarchies([NotNull] AssetNodeContainer nodeContainer, [NotNull] AssetCompositeHierarchy <TAssetPartDesign, TAssetPart> asset, [NotNull] IEnumerable <Guid> sourceRootIds, SubHierarchyCloneFlags flags, [NotNull] out Dictionary <Guid, Guid> idRemapping) { return(CloneSubHierarchies(nodeContainer, nodeContainer, asset, sourceRootIds, flags, out idRemapping)); }