Exemplo n.º 1
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);
        }
Exemplo n.º 2
0
        public EntityViewModel Duplicate()
        {
            var flags           = SubHierarchyCloneFlags.GenerateNewIdsForIdentifiableObjects;
            var clonedHierarchy = EntityHierarchyPropertyGraph.CloneSubHierarchies(Asset.Session.AssetNodeContainer, Asset.Asset, AssetSideEntity.Id.Yield(), flags, out Dictionary <Guid, Guid> idRemapping);

            AssetPartsAnalysis.GenerateNewBaseInstanceIds(clonedHierarchy);

            var addedRoot = clonedHierarchy.Parts[clonedHierarchy.RootParts.Single().Id];

            addedRoot.Folder = (Parent as EntityFolderViewModel)?.Path;

            // rename the entity to avoid having the same names
            if (Parent == null)
            {
                throw new InvalidOperationException($"{nameof(Parent)} cannot be null");
            }
            addedRoot.Entity.Name = EntityFactory.ComputeNewName(Parent, addedRoot.Entity.Name);
            Asset.AssetHierarchyPropertyGraph.AddPartToAsset(clonedHierarchy.Parts, addedRoot, (Parent.Owner as EntityViewModel)?.AssetSideEntity, Parent.Owner.IndexOfEntity(this) + 1);
            var cloneId = addedRoot.Entity.Id;

            // The view model should already exist at that point
            var partId    = new AbsoluteId(Asset.Id, cloneId);
            var viewModel = (EntityViewModel)Editor.FindPartViewModel(partId);

            // TODO: Offset a bit (by 1 scene unit horizontally?) the cloned entity so it appears distincly from the source entity
            return(viewModel);
        }
Exemplo n.º 3
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);
        }
        /// <summary>
        /// Clones a sub-hierarchy of this asset.
        /// </summary>
        /// <param name="sourceRootIds">The ids that are the roots of the sub-hierarchies 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="generateNewIdsForIdentifiableObjects">If true, the cloned objects that implement <see cref="IIdentifiable"/> will have new ids.</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 AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart> CloneSubHierarchies(IEnumerable <Guid> sourceRootIds, bool cleanReference, bool generateNewIdsForIdentifiableObjects, bool generateNewBaseInstanceIds, out Dictionary <Guid, Guid> idRemapping)
        {
            // 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 subTreeHierarchy = new AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart>();

            foreach (var rootId in sourceRootIds)
            {
                if (!Hierarchy.Parts.ContainsKey(rootId))
                {
                    throw new ArgumentException(@"The source root parts must be parts of this asset.", nameof(sourceRootIds));
                }

                subTreeHierarchy.RootPartIds.Add(rootId);

                subTreeHierarchy.Parts.Add(Hierarchy.Parts[rootId]);
                foreach (var subTreePart in EnumerateChildParts(Hierarchy.Parts[rootId].Part, true))
                {
                    subTreeHierarchy.Parts.Add(Hierarchy.Parts[subTreePart.Id]);
                }
            }
            // clone the parts of the sub-tree
            var clonedHierarchy = AssetCloner.Clone(subTreeHierarchy, generateNewIdsForIdentifiableObjects ? AssetClonerFlags.GenerateNewIdsForIdentifiableObjects : AssetClonerFlags.None, out idRemapping);

            // Remap ids from the root id collection to the new ids generated during cloning
            AssetPartsAnalysis.RemapPartsId(clonedHierarchy, idRemapping);

            foreach (var rootEntity in clonedHierarchy.RootPartIds)
            {
                PostClonePart(clonedHierarchy.Parts[rootEntity].Part);
            }
            if (cleanReference)
            {
                // set to null reference outside of the sub-tree
                var tempAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(GetType());
                tempAsset.Hierarchy = clonedHierarchy;
                tempAsset.FixupPartReferences();
            }
            else
            {
                // restore initial ids for reference outside of the subtree, so they can be fixed up later.
                var tempAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(GetType());
                tempAsset.Hierarchy = clonedHierarchy;
                var visitor = new AssetCompositePartReferenceCollector();
                visitor.VisitAsset(tempAsset);
                var references        = visitor.Result;
                var revertedIdMapping = idRemapping.ToDictionary(x => x.Value, x => x.Key);
                foreach (var referencedPart in references.Select(x => x.AssetPart).OfType <IIdentifiable>())
                {
                    var realPart = tempAsset.ResolvePartReference(referencedPart);
                    if (realPart == null)
                    {
                        referencedPart.Id = revertedIdMapping[referencedPart.Id];
                    }
                }
            }

            if (generateNewBaseInstanceIds)
            {
                AssetPartsAnalysis.GenerateNewBaseInstanceIds(clonedHierarchy);
            }

            return(clonedHierarchy);
        }