public AssetPropertyGraph(AssetPropertyGraphContainer container, AssetItem assetItem, ILogger logger) { if (container == null) { throw new ArgumentNullException(nameof(container)); } if (assetItem == null) { throw new ArgumentNullException(nameof(assetItem)); } Container = container; AssetCollectionItemIdHelper.GenerateMissingItemIds(assetItem.Asset); CollectionItemIdsAnalysis.FixupItemIds(assetItem, logger); Asset = assetItem.Asset; RootNode = (AssetObjectNode)Container.NodeContainer.GetOrCreateNode(assetItem.Asset); ApplyOverrides(RootNode, assetItem.Overrides); nodeListener = new GraphNodeChangeListener(RootNode, ShouldListenToTargetNode); nodeListener.Changing += AssetContentChanging; nodeListener.Changed += AssetContentChanged; baseLinker = new AssetToBaseNodeLinker(this) { LinkAction = LinkBaseNode }; }
public void PrepareForSave(ILogger logger, AssetItem assetItem) { if (assetItem.Asset != Asset) { throw new ArgumentException($@"The given {nameof(AssetItem)} does not match the asset associated with this instance", nameof(assetItem)); } AssetCollectionItemIdHelper.GenerateMissingItemIds(assetItem.Asset); CollectionItemIdsAnalysis.FixupItemIds(assetItem, logger); assetItem.Overrides = GenerateOverridesForSerialization(RootNode); }
private static Dictionary <AssetViewModel, List <ItemToReload> > PrepareAssemblyReloading(SessionViewModel session, UnloadingVisitor visitor, IUndoRedoService actionService) { var assetItemsToReload = new Dictionary <AssetViewModel, List <ItemToReload> >(); // Serialize old objects from unloaded assemblies to Yaml streams foreach (var asset in session.LocalPackages.SelectMany(x => x.Assets)) { // Asset has no quantum graph (eg. text asset), let's skip it if (asset.PropertyGraph == null) { continue; } // Generate missing ids for safetly AssetCollectionItemIdHelper.GenerateMissingItemIds(asset.Asset); // Serialize objects with types to unload to Yaml // Objects that were already IUnloadable will also be included var rootNode = session.AssetNodeContainer.GetNode(asset.Asset); var itemsToReload = visitor.Run(asset.PropertyGraph); if (itemsToReload.Count > 0) { // We apply changes in opposite visit order so that indices remains valid when we remove objects while iterating for (int index = itemsToReload.Count - 1; index >= 0; index--) { var itemToReload = itemsToReload[index]; // Transform path to Quantum itemToReload.GraphPath = GraphNodePath.From(rootNode, itemToReload.Path, out itemToReload.GraphPathIndex); // Remove (collections) or replace nodes with null (members) so that we can unload assemblies ClearNode(actionService, asset, itemToReload); } // Add to list of items to reload for PostAssemblyReloading assetItemsToReload.Add(asset, new List <ItemToReload>(itemsToReload)); } } return(assetItemsToReload); }
private AssetViewModel CreateAsset(DirectoryBaseViewModel directory, AssetItem assetItem, bool canUndoRedoCreation, LoggerResult loggerResult, bool isLoading) { if (directory == null) { throw new ArgumentNullException(nameof(directory)); } if (assetItem == null) { throw new ArgumentNullException(nameof(assetItem)); } AssetCollectionItemIdHelper.GenerateMissingItemIds(assetItem.Asset); var parameters = new AssetViewModelConstructionParameters(ServiceProvider, directory, Package, assetItem, directory.Session.AssetNodeContainer, canUndoRedoCreation); Session.GraphContainer.InitializeAsset(assetItem, loggerResult); var assetType = assetItem.Asset.GetType(); var assetViewModelType = typeof(AssetViewModel <>); while (assetType != null) { if (Session.AssetViewModelTypes.TryGetValue(assetType, out assetViewModelType)) { break; } assetViewModelType = typeof(AssetViewModel <>); assetType = assetType.BaseType; } if (assetViewModelType.IsGenericType) { assetViewModelType = assetViewModelType.MakeGenericType(assetItem.Asset.GetType()); } var asset = (AssetViewModel)Activator.CreateInstance(assetViewModelType, parameters); if (!isLoading) { asset.Initialize(); } return(asset); }
internal void OnItemChanged(object sender, ItemChangeEventArgs e) { var value = node.Retrieve(); if (!CollectionItemIdHelper.HasCollectionItemIds(value)) { return; } // Make sure that we have item ids everywhere we're supposed to. AssetCollectionItemIdHelper.GenerateMissingItemIds(e.Collection.Retrieve()); // Clear the cached item identifier collection. collectionItemIdentifiers = null; // Create new ids for collection items var baseNode = (AssetObjectNode)BaseNode; var removedId = ItemId.Empty; var isOverriding = baseNode != null && !PropertyGraph.UpdatingPropertyFromBase; var itemIds = CollectionItemIdHelper.GetCollectionItemIds(value); var collectionDescriptor = node.Descriptor as CollectionDescriptor; switch (e.ChangeType) { case ContentChangeType.CollectionUpdate: break; case ContentChangeType.CollectionAdd: { // Compute the id we will add for this item var itemId = restoringId != ItemId.Empty ? restoringId : ItemId.New(); // Add the id to the proper location (insert or add) if (collectionDescriptor != null) { if (e.Index == Index.Empty) { throw new InvalidOperationException("An item has been added to a collection that does not have a predictable Add. Consider using NonIdentifiableCollectionItemsAttribute on this collection."); } itemIds.Insert(e.Index.Int, itemId); } else { itemIds[e.Index.Value] = itemId; } break; } case ContentChangeType.CollectionRemove: { var itemId = itemIds[e.Index.Value]; // update isOverriding, it should be true only if the item being removed exist in the base. isOverriding = isOverriding && baseNode.HasId(itemId); removedId = collectionDescriptor != null?itemIds.DeleteAndShift(e.Index.Int, isOverriding) : itemIds.Delete(e.Index.Value, isOverriding); break; } default: throw new ArgumentOutOfRangeException(); } // Don't update override if propagation from base is disabled. if (PropertyGraph?.Container?.PropagateChangesFromBase == false) { return; } // Mark it as New if it does not come from the base if (!ResettingOverride) { if (e.ChangeType != ContentChangeType.CollectionRemove && isOverriding) { // If it's an add or an updating, there is no scenario where we can be "un-overriding" without ResettingOverride, so we pass true. OverrideItem(true, e.Index); } else if (e.ChangeType == ContentChangeType.CollectionRemove) { // If it's a delete, it could be an item that was previously added as an override, and that should not be marked as "deleted-overridden", so we pass isOverriding OverrideDeletedItem(isOverriding, removedId); } } }
private void ContentChanged(object sender, ContentChangeEventArgs e) { // Make sure that we have item ids everywhere we're supposed to. AssetCollectionItemIdHelper.GenerateMissingItemIds(e.Content.Retrieve()); var node = (AssetNode)e.Content.OwnerNode; if (node.IsNonIdentifiableCollectionContent) { return; } // Create new ids for collection items var baseNode = (AssetNode)BaseContent?.OwnerNode; var isOverriding = !baseNode?.contentUpdating == true; var removedId = ItemId.Empty; switch (e.ChangeType) { case ContentChangeType.ValueChange: break; case ContentChangeType.CollectionAdd: { var collectionDescriptor = e.Content.Descriptor as CollectionDescriptor; var itemIds = CollectionItemIdHelper.GetCollectionItemIds(e.Content.Retrieve()); // Compute the id we will add for this item ItemId itemId; if (baseNode?.contentUpdating == true) { var baseCollection = baseNode.Content.Retrieve(); var baseIds = CollectionItemIdHelper.GetCollectionItemIds(baseCollection); itemId = itemIds.FindMissingId(baseIds); } else { itemId = restoringId != ItemId.Empty ? restoringId : ItemId.New(); } // Add the id to the proper location (insert or add) if (collectionDescriptor != null) { if (e.Index != Index.Empty) { itemIds.Insert(e.Index.Int, itemId); } else { throw new InvalidOperationException("An item has been added to a collection that does not have a predictable Add. Consider using NonIdentifiableCollectionItemsAttribute on this collection."); } } else { itemIds[e.Index.Value] = itemId; } } break; case ContentChangeType.CollectionRemove: { var collectionDescriptor = e.Content.Descriptor as CollectionDescriptor; if (collectionDescriptor != null) { var itemIds = CollectionItemIdHelper.GetCollectionItemIds(e.Content.Retrieve()); removedId = itemIds.DeleteAndShift(e.Index.Int, isOverriding); } else { var itemIds = CollectionItemIdHelper.GetCollectionItemIds(e.Content.Retrieve()); removedId = itemIds.Delete(e.Index.Value, isOverriding); } } break; default: throw new ArgumentOutOfRangeException(); } // Don't update override if propagation from base is disabled. if (PropertyGraph?.Container?.PropagateChangesFromBase == false) { return; } // Mark it as New if it does not come from the base if (!baseNode?.contentUpdating == true && !ResettingOverride) { if (e.ChangeType != ContentChangeType.CollectionRemove) { if (e.Index == Index.Empty) { OverrideContent(!ResettingOverride); } else { OverrideItem(!ResettingOverride, e.Index); } } else { OverrideDeletedItem(true, removedId); } } }
public void TestIdsGeneration() { ShadowObject.Enable = true; CollectionItemIdentifiers ids; var obj1 = new ContainerCollection("Root") { Strings = { "aaa", "bbb", "ccc" }, Objects = { new ContainerCollection("obj1"), new ContainerCollection("obj2") } }; var hashSet = new HashSet <ItemId>(); Assert.False(CollectionItemIdHelper.TryGetCollectionItemIds(obj1.Strings, out ids)); Assert.False(CollectionItemIdHelper.TryGetCollectionItemIds(obj1.Strings, out ids)); Assert.False(CollectionItemIdHelper.TryGetCollectionItemIds(obj1.Objects, out ids)); Assert.False(CollectionItemIdHelper.TryGetCollectionItemIds(obj1.Objects, out ids)); AssetCollectionItemIdHelper.GenerateMissingItemIds(obj1); Assert.True(CollectionItemIdHelper.TryGetCollectionItemIds(obj1.Strings, out ids)); Assert.Equal(3, ids.KeyCount); Assert.Equal(0, ids.DeletedCount); Assert.True(ids.ContainsKey(0)); Assert.True(ids.ContainsKey(1)); Assert.True(ids.ContainsKey(2)); hashSet.Add(ids[0]); hashSet.Add(ids[1]); hashSet.Add(ids[2]); Assert.True(CollectionItemIdHelper.TryGetCollectionItemIds(obj1.Objects, out ids)); Assert.Equal(2, ids.KeyCount); Assert.Equal(0, ids.DeletedCount); Assert.True(ids.ContainsKey(0)); Assert.True(ids.ContainsKey(1)); hashSet.Add(ids[0]); hashSet.Add(ids[1]); Assert.Equal(5, hashSet.Count); var obj2 = new ContainerDictionary("Root") { Strings = { { GuidGenerator.Get(200), "aaa" }, { GuidGenerator.Get(100), "bbb" }, { GuidGenerator.Get(300), "ccc" } }, Objects = { { "key3", new ContainerCollection("obj1") }, { "key4", new ContainerCollection("obj2") } }, }; hashSet.Clear(); Assert.False(CollectionItemIdHelper.TryGetCollectionItemIds(obj2.Strings, out ids)); Assert.False(CollectionItemIdHelper.TryGetCollectionItemIds(obj2.Strings, out ids)); Assert.False(CollectionItemIdHelper.TryGetCollectionItemIds(obj2.Objects, out ids)); Assert.False(CollectionItemIdHelper.TryGetCollectionItemIds(obj2.Objects, out ids)); AssetCollectionItemIdHelper.GenerateMissingItemIds(obj2); Assert.True(CollectionItemIdHelper.TryGetCollectionItemIds(obj2.Strings, out ids)); Assert.Equal(3, ids.KeyCount); Assert.Equal(0, ids.DeletedCount); Assert.True(ids.ContainsKey(GuidGenerator.Get(200))); Assert.True(ids.ContainsKey(GuidGenerator.Get(100))); Assert.True(ids.ContainsKey(GuidGenerator.Get(300))); hashSet.Add(ids[GuidGenerator.Get(200)]); hashSet.Add(ids[GuidGenerator.Get(100)]); hashSet.Add(ids[GuidGenerator.Get(300)]); Assert.True(CollectionItemIdHelper.TryGetCollectionItemIds(obj2.Objects, out ids)); Assert.Equal(2, ids.KeyCount); Assert.Equal(0, ids.DeletedCount); Assert.True(ids.ContainsKey("key3")); Assert.True(ids.ContainsKey("key4")); hashSet.Add(ids["key3"]); hashSet.Add(ids["key4"]); Assert.Equal(5, hashSet.Count); }
public void PrepareForSave(ILogger logger) { AssetCollectionItemIdHelper.GenerateMissingItemIds(AssetItem.Asset); CollectionItemIdsAnalysis.FixupItemIds(AssetItem, logger); AssetItem.Overrides = GenerateOverridesForSerialization(RootNode); }