public override ObjectReference FindTargetReference(IGraphNode sourceNode, IGraphNode targetNode, ObjectReference sourceReference) { // Not identifiable - default applies if (sourceReference.Index.IsEmpty || sourceReference.ObjectValue == null) { return(base.FindTargetReference(sourceNode, targetNode, sourceReference)); } // Special case for objects that are identifiable: the object must be linked to the base only if it has the same id var sourceAssetNode = (AssetObjectNode)sourceNode; var targetAssetNode = (AssetObjectNode)targetNode; if (!CollectionItemIdHelper.HasCollectionItemIds(sourceAssetNode.Retrieve())) { return(null); } // Enumerable reference: we look for an object with the same id var targetReference = targetAssetNode.ItemReferences; var sourceIds = CollectionItemIdHelper.GetCollectionItemIds(sourceNode.Retrieve()); var targetIds = CollectionItemIdHelper.GetCollectionItemIds(targetNode.Retrieve()); var itemId = sourceIds[sourceReference.Index.Value]; var targetKey = targetIds.GetKey(itemId); foreach (var targetRef in targetReference) { if (Equals(targetRef.Index.Value, targetKey)) { return(targetRef); } } return(null); }
private bool ShouldGenerateItemIdCollection(object collection) { // Do not generate for value types (collections that are struct) or null if (collection?.GetType().IsValueType != false) { return(false); } // Do not generate if within a type that doesn't use identifiable collections if (inNonIdentifiableType > 0) { return(false); } // Do not generate if item id collection already exists if (CollectionItemIdHelper.HasCollectionItemIds(collection)) { return(false); } // Do not generate if the collection has been flagged to not be identifiable if (nonIdentifiableCollection?.Contains(collection) == true) { return(false); } return(true); }
/// <inheritdoc/> protected override void VisitObjectNode([NotNull] IAssetObjectNode objectNode, int inNonIdentifiableType) { if (!objectNode.IsReference) { return; } foreach (var index in ((IAssetObjectNodeInternal)objectNode).Indices) { if (!propertyGraphDefinition.IsTargetItemObjectReference(objectNode, index, objectNode.Retrieve(index))) { continue; } var itemPath = ConvertPath(CurrentPath, inNonIdentifiableType); if (CollectionItemIdHelper.HasCollectionItemIds(objectNode.Retrieve())) { var itemId = objectNode.IndexToId(index); itemPath.PushItemId(itemId); } else { itemPath.PushIndex(index.Value); } var value = objectNode.Retrieve(index) as IIdentifiable; if (value == null) { throw new InvalidOperationException("IsObjectReference returned true for an object that is not IIdentifiable"); } var id = value.Id; if (ShouldOutputReference?.Invoke(id) ?? true) { Result.Set(itemPath, id); } } }
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); } } }
public static YamlAssetPath ConvertPath([NotNull] GraphNodePath path, int inNonIdentifiableType = 0) { if (path == null) { throw new ArgumentNullException(nameof(path)); } var currentNode = (IAssetNode)path.RootNode; var result = new YamlAssetPath(); var i = 0; foreach (var item in path.Path) { switch (item.Type) { case GraphNodePath.ElementType.Member: { var member = item.Name; result.PushMember(member); var objectNode = currentNode as IObjectNode; if (objectNode == null) { throw new InvalidOperationException($"An IObjectNode was expected when processing the path [{path}]"); } currentNode = (IAssetNode)objectNode.TryGetChild(member); break; } case GraphNodePath.ElementType.Target: { if (i < path.Path.Count - 1) { var targetingMemberNode = currentNode as IMemberNode; if (targetingMemberNode == null) { throw new InvalidOperationException($"An IMemberNode was expected when processing the path [{path}]"); } currentNode = (IAssetNode)targetingMemberNode.Target; } break; } case GraphNodePath.ElementType.Index: { var index = item.Index; var objectNode = currentNode as AssetObjectNode; if (objectNode == null) { throw new InvalidOperationException($"An IObjectNode was expected when processing the path [{path}]"); } if (inNonIdentifiableType > 0 || !CollectionItemIdHelper.HasCollectionItemIds(objectNode.Retrieve())) { result.PushIndex(index.Value); } else { var id = objectNode.IndexToId(index); // Create a new id if we don't have any so far if (id == ItemId.Empty) { id = ItemId.New(); } result.PushItemId(id); } if (i < path.Path.Count - 1) { currentNode = (IAssetNode)objectNode.IndexedTarget(index); } break; } default: throw new ArgumentOutOfRangeException(); } ++i; } return(result); }