Ejemplo n.º 1
0
        public override string ReadMemberName(ref ObjectContext objectContext, string memberName, out bool skipMember)
        {
            var objectType = objectContext.Instance.GetType();

            OverrideType[] overrideTypes;
            var            realMemberName = TrimAndParseOverride(memberName, out overrideTypes);

            // For member names, we have a single override, so we always take the last one of the array (In case of legacy property serialized with ~Name)
            var overrideType = overrideTypes[overrideTypes.Length - 1];

            if (overrideType != OverrideType.Base)
            {
                YamlAssetMetadata <OverrideType> overrides;
                if (!objectContext.SerializerContext.Properties.TryGetValue(OverrideDictionaryKey, out overrides))
                {
                    overrides = new YamlAssetMetadata <OverrideType>();
                    objectContext.SerializerContext.Properties.Add(OverrideDictionaryKey, overrides);
                }

                var path = GetCurrentPath(ref objectContext, true);
                path.PushMember(realMemberName);
                overrides.Set(path, overrideType);
            }

            var resultMemberName = base.ReadMemberName(ref objectContext, realMemberName, out skipMember);

            return(resultMemberName);
        }
Ejemplo n.º 2
0
            public override object ConvertFrom(ref ObjectContext context, Scalar fromScalar)
            {
                Guid identifier;

                if (!TryParse(fromScalar.Value, out identifier))
                {
                    throw new YamlException($"Unable to deserialize reference: [{fromScalar.Value}]");
                }

                // Add the path to the currently deserialized object to the list of object references
                YamlAssetMetadata <Guid> objectReferences;

                if (!context.SerializerContext.Properties.TryGetValue(AssetObjectSerializerBackend.ObjectReferencesKey, out objectReferences))
                {
                    objectReferences = new YamlAssetMetadata <Guid>();
                    context.SerializerContext.Properties.Add(AssetObjectSerializerBackend.ObjectReferencesKey, objectReferences);
                }
                var path = AssetObjectSerializerBackend.GetCurrentPath(ref context, true);

                objectReferences.Set(path, identifier);

                // Return default(T)
                //return !context.Descriptor.Type.IsValueType ? null : Activator.CreateInstance(context.Descriptor.Type);
                // Return temporary proxy instance
                var proxy = (IIdentifiable)AbstractObjectInstantiator.CreateConcreteInstance(context.Descriptor.Type);

                proxy.Id = identifier;
                return(proxy);
            }
Ejemplo n.º 3
0
        /// <summary>
        /// Updates the paths in the given <see cref="YamlAssetMetadata{T}"/> instance to reflect that new <see cref="Guid"/> have been generated after cloning,
        /// when <see cref="AssetClonerFlags.GenerateNewIdsForIdentifiableObjects"/> has been used.
        /// </summary>
        /// <typeparam name="T">The type of content in the metadata.</typeparam>
        /// <param name="metadata">The metadata to update.</param>
        /// <param name="idRemapping">A dictionary representing the mapping between initial ids and their corresponding id in the cloned object.</param>
        /// <param name="basePath">If not null, this method will apply the remapping only for paths that are contained in the given base path.</param>
        public static void RemapIdentifiablePaths <T>(YamlAssetMetadata <T> metadata, Dictionary <Guid, Guid> idRemapping, YamlAssetPath basePath = null)
        {
            // Early exit if nothing to remap
            if (metadata == null || idRemapping == null)
            {
                return;
            }

            var replacements = new List <Tuple <YamlAssetPath, YamlAssetPath, T> >();

            foreach (var entry in metadata)
            {
                // Skip paths that doesn't start with the given base path.
                if (basePath != null && !entry.Key.StartsWith(basePath))
                {
                    continue;
                }

                var newPath = new YamlAssetPath(entry.Key.Elements.Select(x => FixupIdentifier(x, idRemapping)));
                replacements.Add(Tuple.Create(entry.Key, newPath, entry.Value));
            }

            // First remove everything, then re-add everything, in case we have a collision between an old path and a new path
            foreach (var replacement in replacements)
            {
                metadata.Remove(replacement.Item1);
            }
            foreach (var replacement in replacements)
            {
                metadata.Set(replacement.Item2, replacement.Item3);
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Fix up references represented by the <paramref name="objectReferences"/> dictionary into the <paramref name="root"/> object, by visiting the object
        /// to find all <see cref="IIdentifiable"/> instances it references, and modify the references described by <paramref name="objectReferences"/> to point
        /// to the proper identifiable object matching the same <see cref="Guid"/>.
        /// </summary>
        /// <param name="root">The root object to fix up.</param>
        /// <param name="objectReferences">The path to each object reference and the <see cref="Guid"/> of the tar</param>
        /// <param name="clearBrokenObjectReferences">If true, any object refernce that cannot be resolved will be reset to null.</param>
        /// <param name="throwOnDuplicateIds">If true, an exception will be thrown if two <see cref="IIdentifiable"/></param>
        /// <param name="logger">An optional logger.</param>
        public static void RunFixupPass(object root, YamlAssetMetadata <Guid> objectReferences, bool clearBrokenObjectReferences, bool throwOnDuplicateIds, [CanBeNull] ILogger logger = null)
        {
            // First collect IIdentifiable objects
            var referenceTargets = CollectReferenceableObjects(root, objectReferences, throwOnDuplicateIds, logger);

            // Then resolve and update object references
            FixupReferences(root, objectReferences, referenceTargets, clearBrokenObjectReferences, logger);
        }
 public void Run(Asset asset, List <ItemToReload> itemsToReload)
 {
     Reset();
     ItemsToReload    = itemsToReload;
     root             = asset;
     ObjectReferences = new YamlAssetMetadata <Guid>();
     Visit(asset);
     ItemsToReload = null;
 }
Ejemplo n.º 6
0
        public override object ReadDictionaryKey(ref ObjectContext objectContext, Type keyType)
        {
            var key = objectContext.Reader.Peek <Scalar>();

            OverrideType[] overrideTypes;
            var            keyName = TrimAndParseOverride(key.Value, out overrideTypes);

            key.Value = keyName;

            var keyValue = base.ReadDictionaryKey(ref objectContext, keyType);

            if (overrideTypes[0] != OverrideType.Base)
            {
                YamlAssetMetadata <OverrideType> overrides;
                if (!objectContext.SerializerContext.Properties.TryGetValue(OverrideDictionaryKey, out overrides))
                {
                    overrides = new YamlAssetMetadata <OverrideType>();
                    objectContext.SerializerContext.Properties.Add(OverrideDictionaryKey, overrides);
                }

                var    path = GetCurrentPath(ref objectContext, true);
                ItemId id;
                object actualKey;
                if (YamlAssetPath.IsCollectionWithIdType(objectContext.Descriptor.Type, keyValue, out id, out actualKey))
                {
                    path.PushItemId(id);
                }
                else
                {
                    path.PushIndex(key);
                }
                overrides.Set(path, overrideTypes[0]);
            }

            if (overrideTypes.Length > 1 && overrideTypes[1] != OverrideType.Base)
            {
                ItemId id;
                object actualKey;
                if (YamlAssetPath.IsCollectionWithIdType(objectContext.Descriptor.Type, keyValue, out id, out actualKey))
                {
                    YamlAssetMetadata <OverrideType> overrides;
                    if (!objectContext.SerializerContext.Properties.TryGetValue(OverrideDictionaryKey, out overrides))
                    {
                        overrides = new YamlAssetMetadata <OverrideType>();
                        objectContext.SerializerContext.Properties.Add(OverrideDictionaryKey, overrides);
                    }

                    var path = GetCurrentPath(ref objectContext, true);
                    path.PushIndex(actualKey);
                    overrides.Set(path, overrideTypes[1]);
                }
            }

            return(keyValue);
        }
Ejemplo n.º 7
0
        public static void SerializeAndCompare(object instance, YamlAssetMetadata <OverrideType> overrides, string expectedYaml)
        {
            var stream   = new MemoryStream();
            var metadata = new AttachedYamlAssetMetadata();

            metadata.AttachMetadata(AssetObjectSerializerBackend.OverrideDictionaryKey, overrides);
            AssetFileSerializer.Default.Save(stream, instance, metadata, null);
            stream.Position = 0;
            var streamReader = new StreamReader(stream);
            var yaml         = streamReader.ReadToEnd();

            Assert.Equal(expectedYaml, yaml);
        }
Ejemplo n.º 8
0
        private static string SerializeAsString(object instance, YamlAssetMetadata <Guid> objectReferences)
        {
            using (var stream = new MemoryStream())
            {
                var metadata = new AttachedYamlAssetMetadata();
                if (objectReferences != null)
                {
                    metadata.AttachMetadata(AssetObjectSerializerBackend.ObjectReferencesKey, objectReferences);
                }

                new YamlAssetSerializer().Save(stream, instance, metadata);
                stream.Flush();
                stream.Position = 0;
                return(new StreamReader(stream).ReadToEnd());
            }
        }
Ejemplo n.º 9
0
        public void TestAbstracteferenceObjectAbstractSerialization()
        {
            var obj = new Container {
                Referenceable3 = new Referenceable {
                    Id = GuidGenerator.Get(1), Value = "Test"
                }
            };

            obj.Referenceable4 = (Referenceable)obj.Referenceable3;
            var objectReferences = new YamlAssetMetadata <Guid>();
            var path             = new YamlAssetPath();

            path.PushMember(nameof(Container.Referenceable4));
            objectReferences.Set(path, obj.Referenceable4.Id);
            var yaml = SerializeAsString(obj, objectReferences);

            Assert.AreEqual(AbstractReferenceAbstractObjectYaml, yaml);
        }
Ejemplo n.º 10
0
        public void TestConcreteReferenceConcreteObjectSerialization()
        {
            var obj = new Container {
                Referenceable1 = new Referenceable {
                    Id = GuidGenerator.Get(1), Value = "Test"
                }
            };

            obj.Referenceable2 = obj.Referenceable1;
            var objectReferences = new YamlAssetMetadata <Guid>();
            var path             = new YamlAssetPath();

            path.PushMember(nameof(Container.Referenceable2));
            objectReferences.Set(path, obj.Referenceable2.Id);
            var yaml = SerializeAsString(obj, objectReferences);

            Assert.AreEqual(ConcreteReferenceConcreteObjectYaml, yaml);
        }
Ejemplo n.º 11
0
        public void TestAbstractNonIdentifiableReferenceableDictionarySerialization()
        {
            var obj  = new NonIdentifiableCollectionContainer();
            var item = new Referenceable {
                Id = GuidGenerator.Get(1), Value = "Test"
            };

            obj.AbstractRefDictionary.Add("Item1", item);
            obj.AbstractRefDictionary.Add("Item2", item);
            var objectReferences = new YamlAssetMetadata <Guid>();
            var path             = new YamlAssetPath();

            path.PushMember(nameof(CollectionContainer.AbstractRefDictionary));
            path.PushIndex("Item1");
            objectReferences.Set(path, GuidGenerator.Get(1));
            var yaml = SerializeAsString(obj, objectReferences);

            Assert.AreEqual(AbstractNonIdentifiableReferenceableDictionaryYaml, yaml);
        }
Ejemplo n.º 12
0
        public void TestConcreteNonIdentifiableReferenceableListSerialization()
        {
            var obj  = new NonIdentifiableCollectionContainer();
            var item = new Referenceable {
                Id = GuidGenerator.Get(1), Value = "Test"
            };

            obj.ConcreteRefList.Add(item);
            obj.ConcreteRefList.Add(item);
            var objectReferences = new YamlAssetMetadata <Guid>();
            var path             = new YamlAssetPath();

            path.PushMember(nameof(CollectionContainer.ConcreteRefList));
            path.PushIndex(0);
            objectReferences.Set(path, GuidGenerator.Get(1));
            var yaml = SerializeAsString(obj, objectReferences);

            Assert.AreEqual(ConcreteNonIdentifiableReferenceableListYaml, yaml);
        }
 private static YamlAssetMetadata <T> RemoveFirstIndexInYamlPath <T>([CanBeNull] YamlAssetMetadata <T> metadata, NodeIndex index)
 {
     if (metadata != null)
     {
         // If we had an index we need to remove it from our override paths
         if (index != NodeIndex.Empty)
         {
             var fixedMetadata = new YamlAssetMetadata <T>();
             foreach (var entry in metadata)
             {
                 // We're filling a new dictionary because we are destroying hash codes by muting the keys
                 var newPath = entry.Key.Elements.Count > 0 ? new YamlAssetPath(entry.Key.Elements.Skip(1)) : entry.Key;
                 fixedMetadata.Set(newPath, entry.Value);
             }
             metadata = fixedMetadata;
         }
     }
     return(metadata);
 }
        private static void PostAssemblyReloading(IUndoRedoService actionService, SessionNodeContainer nodeContainer, ReloadingVisitor reloaderVisitor, ILogger log, Dictionary <AssetViewModel, List <ItemToReload> > assetItemsToReload)
        {
            log?.Info("Updating components with newly loaded assemblies");

            // Recreate new objects from Yaml streams
            foreach (var asset in assetItemsToReload)
            {
                // Deserialize objects with reloaded types from Yaml
                reloaderVisitor.Run(asset.Key.Asset, asset.Value);

                // Set new values
                var overrides = new YamlAssetMetadata <OverrideType>();
                foreach (var itemToReload in asset.Value)
                {
                    // Set (members) or add nodes (collections) with values created using newly loaded assemblies
                    ReplaceNode(actionService, asset.Key, itemToReload);
                    if (itemToReload.Overrides != null)
                    {
                        var extendedPath = itemToReload.GraphPath.Clone();
                        if (itemToReload.GraphPathIndex != NodeIndex.Empty)
                        {
                            extendedPath.PushIndex(itemToReload.GraphPathIndex);
                        }

                        var pathToPrepend = AssetNodeMetadataCollectorBase.ConvertPath(extendedPath);

                        foreach (var entry in itemToReload.Overrides)
                        {
                            var path = pathToPrepend.Append(entry.Key);
                            overrides.Set(path, entry.Value);
                        }
                    }
                }

                FixupObjectReferences.RunFixupPass(asset.Key.Asset, reloaderVisitor.ObjectReferences, true, false, log);

                var rootNode = (IAssetNode)nodeContainer.GetNode(asset.Key.Asset);
                AssetPropertyGraph.ApplyOverrides(rootNode, overrides);
            }
        }
Ejemplo n.º 15
0
 public static void FixupReferences([NotNull] object root, [NotNull] YamlAssetMetadata <Guid> objectReferences, [NotNull] Dictionary <Guid, IIdentifiable> referenceTargets, bool clearMissingReferences, [NotNull] Action <MemberPath, object, object> applyAction, ILogger logger = null)
 {
     foreach (var objectReference in objectReferences)
     {
         var path = objectReference.Key.ToMemberPath(root);
         if (!referenceTargets.TryGetValue(objectReference.Value, out IIdentifiable target))
         {
             logger?.Warning($"Unable to resolve target object [{objectReference.Value}] of reference [{objectReference.Key}]");
             if (clearMissingReferences)
             {
                 applyAction(path, root, null);
             }
         }
         else
         {
             var current = path.GetValue(root);
             if (!Equals(current, target))
             {
                 applyAction(path, root, target);
             }
         }
     }
 }
Ejemplo n.º 16
0
        public void TestConcreteReferenceableDictionarySerialization()
        {
            var obj  = new CollectionContainer();
            var item = new Referenceable {
                Id = GuidGenerator.Get(1), Value = "Test"
            };

            obj.ConcreteRefDictionary.Add("Item1", item);
            obj.ConcreteRefDictionary.Add("Item2", item);
            var objectReferences = new YamlAssetMetadata <Guid>();
            var ids = CollectionItemIdHelper.GetCollectionItemIds(obj.ConcreteRefDictionary);

            ids["Item1"] = IdentifierGenerator.Get(1);
            ids["Item2"] = IdentifierGenerator.Get(2);
            var path = new YamlAssetPath();

            path.PushMember(nameof(CollectionContainer.ConcreteRefDictionary));
            path.PushItemId(IdentifierGenerator.Get(1));
            objectReferences.Set(path, GuidGenerator.Get(1));
            var yaml = SerializeAsString(obj, objectReferences);

            Assert.AreEqual(ConcreteReferenceableDictionaryYaml, yaml);
        }
Ejemplo n.º 17
0
        public void TestAbstractReferenceableListSerialization()
        {
            var obj  = new CollectionContainer();
            var item = new Referenceable {
                Id = GuidGenerator.Get(1), Value = "Test"
            };

            obj.AbstractRefList.Add(item);
            obj.AbstractRefList.Add(item);
            var objectReferences = new YamlAssetMetadata <Guid>();
            var ids = CollectionItemIdHelper.GetCollectionItemIds(obj.AbstractRefList);

            ids[0] = IdentifierGenerator.Get(1);
            ids[1] = IdentifierGenerator.Get(2);
            var path = new YamlAssetPath();

            path.PushMember(nameof(CollectionContainer.AbstractRefList));
            path.PushItemId(IdentifierGenerator.Get(1));
            objectReferences.Set(path, GuidGenerator.Get(1));
            var yaml = SerializeAsString(obj, objectReferences);

            Assert.AreEqual(AbstractReferenceableListYaml, yaml);
        }
Ejemplo n.º 18
0
 public static void FixupReferences([NotNull] object root, [NotNull] YamlAssetMetadata <Guid> objectReferences, [NotNull] Dictionary <Guid, IIdentifiable> referenceTargets, bool clearMissingReferences, ILogger logger = null)
 {
     FixupReferences(root, objectReferences, referenceTargets, clearMissingReferences, (p, r, v) => p.Apply(r, MemberPathAction.ValueSet, v));
 }
Ejemplo n.º 19
0
        public static Dictionary <Guid, IIdentifiable> CollectReferenceableObjects(object root, YamlAssetMetadata <Guid> objectReferences, bool throwOnDuplicateIds, [CanBeNull] ILogger logger = null)
        {
            var hashSet = new HashSet <MemberPath>(objectReferences.Select(x => x.Key.ToMemberPath(root)));
            var visitor = new FixupObjectReferenceVisitor(hashSet, throwOnDuplicateIds, logger);

            visitor.Visit(root);
            return(visitor.ReferenceableObjects);
        }
Ejemplo n.º 20
0
        /// <inheritdoc />
        public override bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath)
        {
            if (targetRootObject == null)
            {
                throw new ArgumentNullException(nameof(targetRootObject));
            }
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            var asset = (Asset)targetRootObject;
            var targetPropertyGraph = graphContainer.TryGetGraph(asset.Id);

            // We use a container object in case the data itself is an object reference
            var container = isRootDataObjectReference ? new FixupContainer {
                Data = data
            } : data;
            var rootNode           = targetPropertyGraph.Container.NodeContainer.GetOrCreateNode(container);
            var externalReferences = ExternalReferenceCollector.GetExternalReferences(targetPropertyGraph.Definition, rootNode);

            try
            {
                // Clone to create new ids for any IIdentifiable, except passed external references that will be maintained
                Dictionary <Guid, Guid> idRemapping;
                data = AssetCloner.Clone <object>(data, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, externalReferences, out idRemapping);
            }
            // TODO: have a proper exception type for serialization failure
            catch (Exception)
            {
                // Note: this can fail if the type doesn't have a binary serializer.
                return(false);
            }

            var  targetTypeDescriptor = TypeDescriptorFactory.Default.Find(targetMemberType);
            bool result;

            switch (targetTypeDescriptor.Category)
            {
            case DescriptorCategory.Collection:
                result = ConvertForCollection((CollectionDescriptor)targetTypeDescriptor, ref data);
                break;

            case DescriptorCategory.Dictionary:
                result = ConvertForDictionary((DictionaryDescriptor)targetTypeDescriptor, ref data);
                break;

            case DescriptorCategory.Primitive:
            case DescriptorCategory.Object:
            case DescriptorCategory.NotSupportedObject:
            case DescriptorCategory.Nullable:
                result = ConvertForProperty(targetTypeDescriptor.Type, ref data);
                break;

            case DescriptorCategory.Array:
            case DescriptorCategory.Custom:
                throw new NotSupportedException();

            default:
                throw new ArgumentOutOfRangeException();
            }

            if (!result)
            {
                return(false);
            }

            // Collect all referenceable objects from the target asset (where we're pasting)
            var referenceableObjects = IdentifiableObjectCollector.Collect(targetPropertyGraph.Definition, targetPropertyGraph.RootNode);

            // We use a container object in case the data itself is an object reference
            container = isRootDataObjectReference ? new FixupContainer {
                Data = data
            } : data;
            rootNode = targetPropertyGraph.Container.NodeContainer.GetOrCreateNode(container);

            // Generate YAML paths for the external reference so we can go through the normal deserialization fixup method.
            var externalReferenceIds = new HashSet <Guid>(externalReferences.Select(x => x.Id));
            var visitor = new ObjectReferencePathGenerator(targetPropertyGraph.Definition)
            {
                ShouldOutputReference = x => externalReferenceIds.Contains(x)
            };

            visitor.Visit(rootNode);

            // Fixup external references
            FixupObjectReferences.FixupReferences(container, visitor.Result, referenceableObjects, true);

            data = (container as FixupContainer)?.Data ?? data;
            return(true);
        }
Ejemplo n.º 21
0
 /// <inheritdoc />
 public abstract bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath);
Ejemplo n.º 22
0
        /// <inheritdoc />
        public override bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath)
        {
            if (targetRootObject == null)
            {
                throw new ArgumentNullException(nameof(targetRootObject));
            }
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            var collectionDescriptor = (CollectionDescriptor)TypeDescriptorFactory.Default.Find(targetRootObject.GetType());

            var collection = data as IList <AssetItem>;

            if (collection == null)
            {
                collection = (IList <AssetItem>)Activator.CreateInstance(collectionDescriptor.Type, true);
                collectionDescriptor.Add(collection, data);
            }

            for (var i = 0; i < collection.Count; i++)
            {
                var assetItem = collection[i];
                // If the asset already exists, clone it with new identifiers
                if (session.GetAssetById(assetItem.Id) != null)
                {
                    // Create a derived asset and restore archetype to handle asset-specific cloning process.
                    Dictionary <Guid, Guid> idRemapping;
                    var clone = AssetCloner.Clone(assetItem.Asset, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, out idRemapping);

                    var assetType = assetItem.Asset.GetType();
                    if (assetType.HasInterface(typeof(AssetCompositeHierarchy <,>)))
                    {
                        try
                        {
                            // TODO: Find a way to fallback to the asset or generalize for all asset composite
                            dynamic assetComposite = clone;
                            // Remap indices of parts in Hierarchy.Part
                            var path = basePath.Clone();
                            path.PushItemId(CollectionItemIdHelper.GetCollectionItemIds(collection)[i]);
                            AssetCloningHelper.RemapIdentifiablePaths(overrides, idRemapping, path);
                            AssetPartsAnalysis.GenerateNewBaseInstanceIds(assetComposite.Hierarchy);
                        }
                        catch (RuntimeBinderException e)
                        {
                            e.Ignore();
                        }
                    }
                    // FIXME: rework this
                    var postProcessor = session.ServiceProvider.Get <ICopyPasteService>().PostProcessors.FirstOrDefault(p => p.Accept(assetType));
                    postProcessor?.PostPasteDeserialization(clone);
                    collection[i] = new AssetItem(assetItem.Location, clone);
                }
            }

            // Get the fixed-up value
            data = collection;
            return(true);
        }
Ejemplo n.º 23
0
        /// <inheritdoc />
        public override bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath)
        {
            var asset     = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)targetRootObject;
            var hierarchy = data as AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart>;

            if (hierarchy == null)
            {
                return(false);
            }

            // Create a temporary asset to host the hierarchy to paste, so we have a property graph to manipulate it.
            var tempAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(asset.GetType());

            tempAsset.Hierarchy = hierarchy;
            // Use temporary containers so that any created nodes are discarded after the processing.
            var tempNodeContainer = new AssetNodeContainer {
                NodeBuilder = { NodeFactory = new AssetNodeFactory() }
            };
            var definition = AssetQuantumRegistry.GetDefinition(asset.GetType());
            var rootNode   = tempNodeContainer.GetOrCreateNode(tempAsset);

            // If different asset or if at least one part already exists, create a custom clone.
            if (asset.Id != sourceId || hierarchy.Parts.Values.Any(part => asset.ContainsPart(part.Part.Id)))
            {
                // Clone again to create new ids for any IIdentifiable, but keep references to external object intact.
                var cloneExternalReferences = ExternalReferenceCollector.GetExternalReferences(definition, rootNode);
                hierarchy = AssetCloner.Clone(hierarchy, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, cloneExternalReferences, out var idRemapping);
                // Remap indices of parts in Hierarchy.Part
                AssetCloningHelper.RemapIdentifiablePaths(overrides, idRemapping, basePath);
                // Make new base instances ids in case the part are inherited.
                AssetPartsAnalysis.GenerateNewBaseInstanceIds(hierarchy);
                // Update the temporary asset with this cloned hierarchy.
                rootNode[nameof(AssetCompositeHierarchy <TAssetPartDesign, TAssetPart> .Hierarchy)].Update(hierarchy);
            }

            // Collect all referenceable objects from the target asset (where we're pasting)
            var targetPropertyGraph  = graphContainer.TryGetGraph(asset.Id);
            var referenceableObjects = IdentifiableObjectCollector.Collect(targetPropertyGraph.Definition, targetPropertyGraph.RootNode);

            // Replace references in the hierarchy being pasted by the real objects from the target asset.
            var externalReferences = new HashSet <Guid>(ExternalReferenceCollector.GetExternalReferences(definition, rootNode).Select(x => x.Id));
            var visitor            = new ObjectReferencePathGenerator(definition)
            {
                ShouldOutputReference = x => externalReferences.Contains(x)
            };

            visitor.Visit(rootNode);

            FixupObjectReferences.FixupReferences(tempAsset, visitor.Result, referenceableObjects, true);

            data = hierarchy;

            return(true);
        }