Beispiel #1
0
        public AssetCompositeHierarchyData <UIElementDesign, UIElement> CreateElementInstance(UIAssetBase targetContainer, [NotNull] string targetLocation, Guid elementId, out Guid instanceId)
        {
            // TODO: make a common base method in AssetCompositeHierarchy - the beginning of the method is similar to CreatePrefabInstance
            var idRemapping = new Dictionary <Guid, Guid>();
            var instance    = (UILibraryAsset)CreateDerivedAsset(targetLocation, out idRemapping);

            var rootElementId = idRemapping[elementId];

            if (instance.Hierarchy.RootParts.All(x => x.Id != rootElementId))
            {
                throw new ArgumentException(@"The given id cannot be found in the root parts of this library.", nameof(elementId));
            }

            instanceId = instance.Hierarchy.Parts.Values.FirstOrDefault()?.Base?.InstanceId ?? Guid.NewGuid();

            var result = new AssetCompositeHierarchyData <UIElementDesign, UIElement>();

            result.RootParts.Add(instance.Hierarchy.Parts[rootElementId].UIElement);
            result.Parts.Add(instance.Hierarchy.Parts[rootElementId]);
            foreach (var element in this.EnumerateChildPartDesigns(instance.Hierarchy.Parts[rootElementId], instance.Hierarchy, true))
            {
                result.Parts.Add(element);
            }
            return(result);
        }
        /// <summary>
        /// Remaps the parts identifier.
        /// </summary>
        /// <typeparam name="TAssetPartDesign"></typeparam>
        /// <typeparam name="TAssetPart">The underlying type of part.</typeparam>
        /// <param name="hierarchy">The hierarchy of parts.</param>
        /// <param name="idRemapping">The identifier remapping.</param>
        public static void RemapPartsId <TAssetPartDesign, TAssetPart>(AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart> hierarchy, IDictionary <Guid, Guid> idRemapping)
            where TAssetPartDesign : class, IAssetPartDesign <TAssetPart>
            where TAssetPart : class, IIdentifiable
        {
            Guid newId;

            // Remap parts in asset2 with new Id
            for (var i = 0; i < hierarchy.RootPartIds.Count; ++i)
            {
                if (idRemapping.TryGetValue(hierarchy.RootPartIds[i], out newId))
                {
                    hierarchy.RootPartIds[i] = newId;
                }
            }

            foreach (var part in hierarchy.Parts)
            {
                if (idRemapping.TryGetValue(part.Part.Id, out newId))
                {
                    part.Part.Id = newId;
                }
            }

            // Sort again the hierarchy (since the Ids changed)
            hierarchy.Parts.Sort();
        }
Beispiel #3
0
        /// <summary>
        /// Creates a instance of the given control that can be added to another <see cref="UIAssetBase"/>.
        /// </summary>
        /// <param name="targetContainer">The container in which the instance will be added.</param>
        /// <param name="targetLocation">The location of this asset.</param>
        /// <param name="elementId">The id of the element to instantiate.</param>
        /// <param name="instanceId">The identifier of the created instance.</param>
        /// <returns>An <see cref="AssetCompositeHierarchyData{UIElementDesign, UIElement}"/> containing the cloned elements of </returns>
        /// <remarks>This method will update the <see cref="Asset.BaseParts"/> property of the <see paramref="targetContainer"/>.</remarks>
        public AssetCompositeHierarchyData <UIElementDesign, UIElement> CreateElementInstance(UIAssetBase targetContainer, string targetLocation, Guid elementId, out Guid instanceId)
        {
            // TODO: make a common base method in AssetCompositeHierarchy - the beginning of the method is similar to CreatePrefabInstance
            var idRemapping = new Dictionary <Guid, Guid>();
            var instance    = (UILibraryAsset)CreateChildAsset(targetLocation, idRemapping);

            var rootElementId = idRemapping[elementId];

            if (!instance.Hierarchy.RootPartIds.Contains(rootElementId))
            {
                throw new ArgumentException(@"The given id cannot be found in the root parts of this library.", nameof(elementId));
            }

            targetContainer.AddBasePart(instance.Base);
            instanceId = Guid.NewGuid();
            foreach (var elementEntry in instance.Hierarchy.Parts)
            {
                elementEntry.BasePartInstanceId = instanceId;
            }

            var result = new AssetCompositeHierarchyData <UIElementDesign, UIElement>();

            result.RootPartIds.Add(rootElementId);
            result.Parts.Add(instance.Hierarchy.Parts[rootElementId]);
            foreach (var element in EnumerateChildParts(instance.Hierarchy.Parts[rootElementId], instance.Hierarchy, true))
            {
                result.Parts.Add(element);
            }
            return(result);
        }
Beispiel #4
0
        /// <summary>
        /// Clones a sub-hierarchy of this asset.
        /// </summary>
        /// <param name="sourceRootId">The id of the root of the sub-hierarchy to clone</param>
        /// <param name="cleanReference">If true, any reference to a part external to the cloned hierarchy will be set to null.</param>
        /// <param name="idRemapping">A dictionary containing the mapping of ids from the source parts to the new parts.</param>
        /// <returns>A <see cref="AssetCompositeHierarchyData{UIElementDesign, UIElement}"/> corresponding to the cloned parts.</returns>
        public override AssetCompositeHierarchyData <UIElementDesign, UIElement> CloneSubHierarchy(Guid sourceRootId, bool cleanReference, out Dictionary <Guid, Guid> idRemapping)
        {
            if (!Hierarchy.Parts.ContainsKey(sourceRootId))
            {
                throw new ArgumentException(@"The source root part must be an part of this asset.", nameof(sourceRootId));
            }

            // Note: Instead of copying the whole asset (with its potentially big hierarchy),
            // we first copy the asset only (without the hierarchy), then the sub-hierarchy to extract.
            var subTreeRoot      = Hierarchy.Parts[sourceRootId];
            var subTreeHierarchy = new AssetCompositeHierarchyData <UIElementDesign, UIElement> {
                Parts = { subTreeRoot }, RootPartIds = { sourceRootId }
            };

            foreach (var subTreeDesign in EnumerateChildParts(subTreeRoot, Hierarchy, true))
            {
                subTreeHierarchy.Parts.Add(Hierarchy.Parts[subTreeDesign.UIElement.Id]);
            }

            // clone the parts of the sub-tree
            var clonedHierarchy = (AssetCompositeHierarchyData <UIElementDesign, UIElement>)AssetCloner.Clone(subTreeHierarchy);
            //clonedHierarchy.Parts[sourceRootEntity].UIElement.Parent = null;

            //if (cleanReference)
            //{
            //    // set to null reference outside of the sub-tree
            //    var tempAsset = new PrefabAsset { Hierarchy = clonedHierarchy };
            //    tempAsset.FixupPartReferences();
            //}

            // temporary nullify the hierarchy to avoid to clone it
            var sourceHierarchy = Hierarchy;

            Hierarchy = null;

            // revert the source hierarchy
            Hierarchy = sourceHierarchy;

            // Generate part mapping
            idRemapping = new Dictionary <Guid, Guid>();
            foreach (var partDesign in clonedHierarchy.Parts)
            {
                // Generate new Id
                var newPartId = Guid.NewGuid();

                // Update mappings
                idRemapping.Add(partDesign.UIElement.Id, newPartId);

                // Update part with new id
                partDesign.UIElement.Id = newPartId;
            }

            // Rewrite part references
            // Should we nullify invalid references?
            AssetPartsAnalysis.RemapPartsId(clonedHierarchy, idRemapping);

            return(clonedHierarchy);
        }
Beispiel #5
0
        /// <inheritdoc/>
        protected override void ClearPartReferences(AssetCompositeHierarchyData <UIElementDesign, UIElement> clonedHierarchy)
        {
            // set to null reference outside of the sub-tree
            var tempAsset = new UILibraryAsset {
                Hierarchy = clonedHierarchy
            };

            tempAsset.FixupPartReferences();
        }
Beispiel #6
0
        /// <inheritdoc/>
        protected override void ClearPartReferences(AssetCompositeHierarchyData <EntityDesign, Entity> clonedHierarchy)
        {
            // set to null reference outside of the sub-tree
            var tempAsset = new PrefabAsset {
                Hierarchy = clonedHierarchy
            };

            tempAsset.FixupPartReferences();
        }
Beispiel #7
0
            public EntityEntry(EntityDesign entityDesign, AssetCompositeHierarchyData <EntityDesign, Entity> hierarchy, int order)
            {
                if (entityDesign == null)
                {
                    throw new ArgumentNullException(nameof(entityDesign));
                }
                Order        = order;
                Hierarchy    = hierarchy;
                EntityDesign = entityDesign;

                // Remove children from transform component in order to process them later
                PushChildren();
            }
Beispiel #8
0
        /// <summary>
        /// Helper method to dump this hierarchy to a text output
        /// </summary>
        /// <param name="hierarchy"></param>
        /// <param name="writer"></param>
        /// <returns><c>true</c> if the dump was sucessful, <c>false</c> otherwise</returns>
        public static bool DumpTo(this AssetCompositeHierarchyData <EntityDesign, Entity> hierarchy, TextWriter writer)
        {
            bool result = true;

            writer.WriteLine("***************");
            writer.WriteLine($"RootEntities [{hierarchy.RootPartIds.Count}]");
            writer.WriteLine("===============");
            foreach (var id in hierarchy.RootPartIds)
            {
                if (!hierarchy.Parts.ContainsKey(id))
                {
                    result = false;
                }
                writer.WriteLine(hierarchy.Parts.ContainsKey(id) ? $"{id} => {hierarchy.Parts[id].Entity}" : $"{id} => ERROR - Entity not found in [Entities]");
            }

            writer.WriteLine("***************");
            writer.WriteLine($"Entities [{hierarchy.Parts.Count}]");
            writer.WriteLine("===============");
            for (int i = 0; i < hierarchy.Parts.Count; i++)
            {
                var entityEntry = hierarchy.Parts[i];

                writer.Write($"{entityEntry.Entity.Id} => {entityEntry.Entity}");

                if (entityEntry.BaseId != null)
                {
                    writer.Write($" Base: {entityEntry.BaseId}");
                }

                if (entityEntry.BasePartInstanceId != null)
                {
                    writer.Write($" BasePartInstanceId: {entityEntry.BasePartInstanceId}");
                }
                writer.WriteLine();

                foreach (var child in entityEntry.Entity.Transform.Children)
                {
                    writer.Write($"  - {child.Entity.Id} => {child.Entity.Name}");
                    if (!hierarchy.Parts.ContainsKey(child.Entity.Id))
                    {
                        writer.Write(" <= ERROR, Entity not found in [Entities]");
                        result = false;
                    }
                    writer.WriteLine();
                }
            }
            return(result);
        }
Beispiel #9
0
        /// <summary>
        /// When a part is added to the base asset, it could be the result of a move (remove + add).
        /// In that case, remove the new <paramref name="clonedPart"/> and replace it with the <paramref name="existingPart"/>.
        /// </summary>
        /// <param name="baseHierarchy">The cloned base hierarchy.</param>
        /// <param name="clonedPart">The cloned part to replace.</param>
        /// <param name="existingPart">The existing part to restore.</param>
        /// <seealso cref="PartAddedInBaseAsset"/>
        /// <remarks>
        /// Inheriting instance can override this method to perform additional operations.
        /// </remarks>
        protected virtual void ReuseExistingPart([NotNull] AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart> baseHierarchy, [NotNull] TAssetPartDesign clonedPart, [NotNull] TAssetPartDesign existingPart)
        {
            // Replace the cloned part by the one to restore in the list of root if needed
            if (baseHierarchy.RootParts.Remove(clonedPart.Part))
            {
                baseHierarchy.RootParts.Add(existingPart.Part);
            }

            // Replace the cloned part by the one to restore in the list of parts
            if (!baseHierarchy.Parts.Remove(clonedPart.Part.Id))
            {
                throw new InvalidOperationException("The new part should be in the baseHierarchy.");
            }
            baseHierarchy.Parts.Add(existingPart);
        }
        /// <summary>
        /// Assigns new unique identifiers for base part <see cref="BasePart.InstanceId"/> in the given <paramref name="hierarchy"/>.
        /// </summary>
        /// <typeparam name="TAssetPartDesign"></typeparam>
        /// <typeparam name="TAssetPart">The underlying type of part.</typeparam>
        /// <param name="hierarchy">The hierarchy which part groups should have new identifiers.</param>
        public static void GenerateNewBaseInstanceIds <TAssetPartDesign, TAssetPart>(AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart> hierarchy)
            where TAssetPartDesign : class, IAssetPartDesign <TAssetPart>
            where TAssetPart : class, IIdentifiable
        {
            var baseInstanceMapping = new Dictionary <Guid, Guid>();

            foreach (var part in hierarchy.Parts.Where(x => x.Base != null))
            {
                Guid newInstanceId;
                if (!baseInstanceMapping.TryGetValue(part.Base.InstanceId, out newInstanceId))
                {
                    newInstanceId = Guid.NewGuid();
                    baseInstanceMapping.Add(part.Base.InstanceId, newInstanceId);
                }
                part.Base = new BasePart(part.Base.BasePartAsset, part.Base.BasePartId, newInstanceId);
            }
        }
Beispiel #11
0
        private void MapEntities <T>(AssetCompositeHierarchyData <EntityDesign, Entity> hierarchyData, Dictionary <GroupPartKey, T> entities, Guid?instancePartIdArg = null) where T : EntityEntry
        {
            if (hierarchyData == null)
            {
                return;
            }

            // Important, if the hierarchy is coming from a part, we need to clone it entirely
            // and associate correctly the instancePartId with each entities that it is composed of.
            if (instancePartIdArg.HasValue)
            {
                hierarchyData = (AssetCompositeHierarchyData <EntityDesign, Entity>)AssetCloner.Clone(hierarchyData);
            }


            foreach (var entityDesign in hierarchyData.Parts)
            {
                if (instancePartIdArg.HasValue)
                {
                    entityDesign.BasePartInstanceId = instancePartIdArg;
                }

                var key = new GroupPartKey(instancePartIdArg, entityDesign.Entity.Id);

                if (entities.ContainsKey(key))
                {
                    continue;
                }

                EntityEntry remap;
                if (typeof(T) == typeof(NewEntityEntry))
                {
                    remap = new NewEntityEntry(entityDesign, hierarchyData, entities.Count);
                }
                else if (typeof(T) == typeof(BaseEntityEntry))
                {
                    remap = new BaseEntityEntry(entityDesign, hierarchyData, entities.Count);
                }
                else
                {
                    throw new ArgumentException($"Invalid type [{typeof(T)}]. Expecting concrete type NewEntityEntry or BaseEntityEntry");
                }

                entities[key] = (T)remap;
            }
        }
        /// <summary>
        /// Creates a instance of the given control that can be added to another <see cref="UIAssetBase"/>.
        /// </summary>
        /// <param name="targetContainer">The container in which the instance will be added.</param>
        /// <param name="targetLocation">The location of this asset.</param>
        /// <param name="elementId">The id of the element to instantiate.</param>
        /// <param name="instanceId">The identifier of the created instance.</param>
        /// <returns>An <see cref="AssetCompositeHierarchyData{UIElementDesign, UIElement}"/> containing the cloned elements of </returns>
        /// <remarks>This method will update the <see cref="Asset.BaseParts"/> property of the <see paramref="targetContainer"/>.</remarks>
        public AssetCompositeHierarchyData<UIElementDesign, UIElement> CreateElementInstance(UIAssetBase targetContainer, string targetLocation, Guid elementId, out Guid instanceId)
        {
            // TODO: make a common base method in AssetCompositeHierarchy - the beginning of the method is similar to CreatePrefabInstance
            var idRemapping = new Dictionary<Guid, Guid>();
            var instance = (UILibraryAsset)CreateDerivedAsset(targetLocation, idRemapping);

            var rootElementId = idRemapping[elementId];
            if (!instance.Hierarchy.RootPartIds.Contains(rootElementId))
                throw new ArgumentException(@"The given id cannot be found in the root parts of this library.", nameof(elementId));

            instanceId = instance.Hierarchy.Parts.FirstOrDefault()?.Base.InstanceId ?? Guid.NewGuid();

            var result = new AssetCompositeHierarchyData<UIElementDesign, UIElement>();
            result.RootPartIds.Add(rootElementId);
            result.Parts.Add(instance.Hierarchy.Parts[rootElementId]);
            foreach (var element in EnumerateChildParts(instance.Hierarchy.Parts[rootElementId], instance.Hierarchy, true))
            {
                result.Parts.Add(element);
            }
            return result;
        }
Beispiel #13
0
        /// <summary>
        /// Remaps the entities identifier.
        /// </summary>
        /// <param name="entityHierarchy">The entity hierarchy.</param>
        /// <param name="idRemapping">The identifier remapping.</param>
        public static void RemapEntitiesId(AssetCompositeHierarchyData <EntityDesign, Entity> entityHierarchy, Dictionary <Guid, Guid> idRemapping)
        {
            Guid newId;

            // Remap entities in asset2 with new Id
            for (int i = 0; i < entityHierarchy.RootPartIds.Count; ++i)
            {
                if (idRemapping.TryGetValue(entityHierarchy.RootPartIds[i], out newId))
                {
                    entityHierarchy.RootPartIds[i] = newId;
                }
            }

            foreach (var entity in entityHierarchy.Parts)
            {
                if (idRemapping.TryGetValue(entity.Entity.Id, out newId))
                {
                    entity.Entity.Id = newId;
                }
            }

            // Sort again the EntityCollection (since ID changed)
            entityHierarchy.Parts.Sort();
        }
Beispiel #14
0
 public NewEntityEntry(EntityDesign entityDesign, AssetCompositeHierarchyData <EntityDesign, Entity> hierarchy, int order) : base(entityDesign, hierarchy, order)
 {
 }
Beispiel #15
0
        /// <summary>
        /// Clones a sub-hierarchy of this asset.
        /// </summary>
        /// <param name="sourceRootEntity">The entity that is the root of the sub-hierarchy to clone</param>
        /// <param name="cleanReference">If true, any reference to an entity external to the cloned hierarchy will be set to null.</param>
        /// <param name="entityMapping">A dictionary containing the mapping of ids from the source entites to the new entities.</param>
        /// <returns>A <see cref="AssetCompositeHierarchyData{EntityDesign, Entity}"/> corresponding to the cloned entities.</returns>
        public AssetCompositeHierarchyData <EntityDesign, Entity> CloneSubHierarchy(Guid sourceRootEntity, bool cleanReference, out Dictionary <Guid, Guid> entityMapping)
        {
            if (!Hierarchy.Parts.ContainsKey(sourceRootEntity))
            {
                throw new ArgumentException(@"The source root entity must be an entity of this asset.", nameof(sourceRootEntity));
            }

            // Note: Instead of copying the whole asset (with its potentially big hierarchy),
            // we first copy the asset only (without the hierarchy), then the sub-hierarchy to extract.
            var subTreeRoot      = Hierarchy.Parts[sourceRootEntity];
            var subTreeHierarchy = new AssetCompositeHierarchyData <EntityDesign, Entity> {
                Parts = { subTreeRoot }, RootPartIds = { sourceRootEntity }
            };

            foreach (var subTreeEntity in EnumerateChildParts(subTreeRoot, true))
            {
                subTreeHierarchy.Parts.Add(Hierarchy.Parts[subTreeEntity.Entity.Id]);
            }

            // clone the entities of the sub-tree
            var clonedHierarchy = (AssetCompositeHierarchyData <EntityDesign, Entity>)AssetCloner.Clone(subTreeHierarchy);

            clonedHierarchy.Parts[sourceRootEntity].Entity.Transform.Parent = null;

            if (cleanReference)
            {
                // set to null reference outside of the sub-tree
                var tempAsset = new PrefabAsset {
                    Hierarchy = clonedHierarchy
                };
                tempAsset.FixupPartReferences();
            }

            // temporary nullify the hierarchy to avoid to clone it
            var sourceHierarchy = Hierarchy;

            Hierarchy = null;

            // revert the source hierarchy
            Hierarchy = sourceHierarchy;

            // Generate entity mapping
            entityMapping = new Dictionary <Guid, Guid>();
            foreach (var entityDesign in clonedHierarchy.Parts)
            {
                // Generate new Id
                var newEntityId = Guid.NewGuid();

                // Update mappings
                entityMapping.Add(entityDesign.Entity.Id, newEntityId);

                // Update entity with new id
                entityDesign.Entity.Id = newEntityId;
            }

            // Rewrite entity references
            // Should we nullify invalid references?
            EntityAnalysis.RemapEntitiesId(clonedHierarchy, entityMapping);

            return(clonedHierarchy);
        }
Beispiel #16
0
        /// <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);
        }
 /// <inheritdoc />
 protected override void ReuseExistingPart(AssetCompositeHierarchyData <EntityDesign, Entity> baseHierarchy, EntityDesign clonedPart, EntityDesign existingPart)
 {
     // Update the folder information
     existingPart.Folder = clonedPart.Folder;
     base.ReuseExistingPart(baseHierarchy, clonedPart, existingPart);
 }
Beispiel #18
0
 public abstract void ReplaceRootElement([NotNull] PanelViewModel sourcePanel, [NotNull] AssetCompositeHierarchyData <UIElementDesign, UIElement> hierarchy, Guid targetPanelId);
Beispiel #19
0
        /// <summary>
        /// Prepare the merge by computing internal dictionaries
        /// </summary>
        private void PrepareMerge()
        {
            // Process NewAsset
            MapEntities(newAsset.Hierarchy, newEntities);

            // Process BaseAsset
            MapEntities(baseAsset?.Hierarchy, baseEntities);
            if (newAsset.BaseParts != null)
            {
                foreach (var partItem in newAsset.GetBasePartInstanceIds())
                {
                    foreach (var groupPartId in partItem.Value)
                    {
                        // Because we have a groupPartId, it will clone the hierarchy so that
                        // we can safely merge instances later with NewEntity
                        MapEntities(partItem.Key.Hierarchy, baseEntities, groupPartId);
                    }
                }
            }

            // Process NewBaseAsset
            MapEntities(newBaseAsset?.Hierarchy, newBaseEntities);
            if (newBaseParts != null)
            {
                foreach (var partItem in newAsset.GetBasePartInstanceIds(newBaseParts))
                {
                    foreach (var groupPartId in partItem.Value)
                    {
                        // Because we have a groupPartId, it will clone the hierarchy so that
                        // we can safely merge instances later with NewEntity
                        MapEntities(partItem.Key.Hierarchy, newBaseEntities, groupPartId);
                    }
                }
            }

            // Compute Entities Added by newbase (not present in base)
            foreach (var entityFromNewBase in newBaseEntities)
            {
                var entityId = entityFromNewBase.Value.EntityDesign.Entity.Id;
                // For PartInstanceId key, we take it from the entityFromNewBase.Key
                var basePartInstanceId = entityFromNewBase.Key.PartInstanceId;
                var key = new GroupPartKey(basePartInstanceId, entityId);

                if (!baseEntities.ContainsKey(key))
                {
                    var tempHiearchy = new AssetCompositeHierarchyData <EntityDesign, Entity>();
                    tempHiearchy.Parts.Add(entityFromNewBase.Value.EntityDesign);

                    // The new entity added by the newbase
                    // Because we are cloning the entity, we need to restore children temporarely in order to clone them as well
                    entityFromNewBase.Value.PopChildren();
                    var newEntityDesign = ((AssetCompositeHierarchyData <EntityDesign, Entity>)AssetCloner.Clone(tempHiearchy)).Parts[0];
                    entityFromNewBase.Value.PushChildren();

                    var newId = Guid.NewGuid();
                    newEntityDesign.Entity.Id          = newId;
                    newEntityDesign.BaseId             = entityId;
                    newEntityDesign.BasePartInstanceId = basePartInstanceId;

                    // Because we are going to modify the NewBase we need to clone it
                    // We tag this entry as special, as we will have to process its children differently later in the merge hierarchy
                    var item = new NewEntityEntry(newEntityDesign, null, newEntities.Count)
                    {
                        IsNewBase = true
                    };

                    // Add this to the list of entities from newAsset
                    // Specific to the newEntities, GroupPartKey.PartInstanceId is always Guid.Empty
                    newEntities.Add(new GroupPartKey(null, newId), item);

                    // If the entity is coming from a part and is from root Entities, we need to add it to the rootEntities by default
                    if (basePartInstanceId.HasValue && entityFromNewBase.Value.Hierarchy.RootPartIds.Contains(entityId))
                    {
                        rootEntitiesToAdd.Add(newId);
                    }
                }
            }

            // Compute Entities Removed in newbase (present in base)
            foreach (var entityFromBase in baseEntities)
            {
                var entityId = entityFromBase.Value.EntityDesign.Entity.Id;
                var key      = new GroupPartKey(entityFromBase.Key.PartInstanceId, entityId);

                if (!newBaseEntities.ContainsKey(key))
                {
                    entitiesRemovedInNewBase.Add(entityId);
                }
            }

            // Compute the GUID -> index in list for RootEntities for Base and NewBase
            var baseRootEntities = new Dictionary <Guid, int>();

            if (baseAsset != null)
            {
                for (int i = 0; i < baseAsset.Hierarchy.RootPartIds.Count; i++)
                {
                    var id = baseAsset.Hierarchy.RootPartIds[i];
                    baseRootEntities.Add(id, i);
                }
            }
            var newBaseRootEntities = new Dictionary <Guid, int>();

            if (newBaseAsset != null)
            {
                for (int i = 0; i < newBaseAsset.Hierarchy.RootPartIds.Count; i++)
                {
                    var id = newBaseAsset.Hierarchy.RootPartIds[i];
                    newBaseRootEntities.Add(id, i);
                }
            }

            // Associate all NewEntity entries to their base
            foreach (var newEntityEntry in newEntities.ToList()) // use ToList so we can modify the dictionary while iterating
            {
                // Skip entities that don't have a base, as we don't have to do anything in the merge
                var entityDesign = newEntityEntry.Value.EntityDesign;
                if (!entityDesign.BaseId.HasValue)
                {
                    continue;
                }

                // Else we will associate entries
                var newEntity = entityDesign.Entity;
                var baseId    = entityDesign.BaseId.Value;
                var baseKey   = new GroupPartKey(entityDesign.BasePartInstanceId, baseId);

                BaseEntityEntry baseRemap;
                BaseEntityEntry newBaseRemap;
                baseEntities.TryGetValue(baseKey, out baseRemap);
                newBaseEntities.TryGetValue(baseKey, out newBaseRemap);

                // Link between base and new entity
                if (baseRemap != null)
                {
                    baseRemap.NewEntity = newEntityEntry.Value;
                }

                if (newBaseRemap != null)
                {
                    newBaseRemap.NewEntity = newEntityEntry.Value;
                }

                // Link the new entity to its base
                newEntityEntry.Value.Base    = baseRemap;
                newEntityEntry.Value.NewBase = newBaseRemap;

                if (entitiesRemovedInNewBase.Contains(baseId))
                {
                    entitiesToRemoveFromNew.Add(newEntity.Id);

                    // Else the entity has been removed
                    newEntities.Remove(newEntityEntry.Key);
                }
                else
                {
                    // Remap ids in the RootEntities for base
                    int index;
                    if (baseAsset != null && baseRootEntities.TryGetValue(baseId, out index))
                    {
                        baseRootEntities.Remove(baseId);
                        baseAsset.Hierarchy.RootPartIds[index] = newEntity.Id;
                    }

                    // Remap ids in the RootEntities for newBase
                    if (newBaseAsset != null && newBaseRootEntities.TryGetValue(baseId, out index))
                    {
                        newBaseRootEntities.Remove(baseId);
                        newBaseAsset.Hierarchy.RootPartIds[index] = newEntity.Id;
                    }
                }
            }

            // Uncomment the following code to dump the content of the different entities and how
            // they are remapped
            if (Debug)
            {
                System.Diagnostics.Debug.WriteLine(string.Empty);
                System.Diagnostics.Debug.WriteLine("**********************************");
                System.Diagnostics.Debug.WriteLine($"Merge Entity [{debugLocation}] => {newAsset.Id} Base: {baseAsset?.Id}");
                System.Diagnostics.Debug.WriteLine("==================================");
                Dump("\n**********\nBase\n==========", baseEntities);
                Dump("\n**********\nNewBase\n==========", newBaseEntities);
                Dump("\n**********\nNew\n==========", newEntities);
            }
        }