protected override void ExecuteSync(IContentNode content, Index index, object parameter) { var value = content.Retrieve(index); var collectionDescriptor = (CollectionDescriptor)TypeDescriptorFactory.Default.Find(value.GetType()); object itemToAdd; // First, check if parameter is an AbstractNodeEntry var abstractNodeEntry = parameter as AbstractNodeEntry; if (abstractNodeEntry != null) { itemToAdd = abstractNodeEntry.GenerateValue(null); } // Otherwise, assume it's an object else { var elementType = collectionDescriptor.ElementType; itemToAdd = parameter ?? (IsReferenceType(elementType) ? null : ObjectFactoryRegistry.NewInstance(elementType)); } if (index.IsEmpty) { content.Add(itemToAdd); } else { // Handle collections in collections // TODO: this is not working on the observable node side var collectionNode = content.ItemReferences[index].TargetNode; collectionNode.Add(itemToAdd); } }
protected override ObjectReference FindTargetReference(IContentNode sourceNode, IContentNode targetNode, ObjectReference sourceReference) { if (sourceReference.Index.IsEmpty) { return(targetNode.TargetReference); } // Special case for objects that are identifiable: the object must be linked to the base only if it has the same id if (sourceReference.ObjectValue != null) { if (sourceReference.Index.IsEmpty) { return(targetNode.TargetReference); } var sourceAssetNode = (IAssetNode)sourceNode; if ((sourceAssetNode as AssetMemberNode)?.IsNonIdentifiableCollectionContent ?? false) { return(null); } // Enumerable reference: we look for an object with the same id var targetReference = targetNode.ItemReferences; var sourceIds = CollectionItemIdHelper.GetCollectionItemIds(sourceNode.Retrieve()); var targetIds = CollectionItemIdHelper.GetCollectionItemIds(targetNode.Retrieve()); var itemId = sourceIds[sourceReference.Index.Value]; var targetKey = targetIds.GetKey(itemId); return(targetReference.FirstOrDefault(x => Equals(x.Index.Value, targetKey))); } // Not identifiable - default applies return(base.FindTargetReference(sourceNode, targetNode, sourceReference)); }
internal void Refresh(IContentNode ownerNode, NodeContainer nodeContainer, Index index) { var objectValue = ownerNode.Retrieve(index); var boxedTarget = TargetNode as BoxedContent; if (boxedTarget != null && objectValue?.GetType() == TargetNode.Type) { // If we are boxing a struct, and the targeted type didn't change, we reuse the same nodes and just overwrite the struct value. boxedTarget.UpdateFromOwner(objectValue); // But we still need to refresh inner references! foreach (var member in TargetNode.Members.Where(x => x.IsReference)) { nodeContainer?.UpdateReferences(member); } } else if (TargetNode?.Value != objectValue) { // This call will recursively update the references. var target = SetTarget(objectValue, nodeContainer); if (target != null) { var boxedContent = target as BoxedContent; boxedContent?.SetOwnerContent(ownerNode, index); } } // This reference is not orphan anymore. orphanObject = null; }
/// <summary> /// Tests the validity of a node that is a member of an object. /// </summary> /// <param name="containerNode">The node of the container of this member.</param> /// <param name="memberNode">The memeber node to validate.</param> /// <param name="container">The value represented by the container node.</param> /// <param name="member">The value of the member represented by the member node.</param> /// <param name="memberName">The name of the member to validate.</param> /// <param name="isReference">Indicate whether the member node is expected to contain a reference to the value it represents.</param> public static void TestMemberNode(IContentNode containerNode, IContentNode memberNode, object container, object member, string memberName, bool isReference) { if (containerNode == null) { throw new ArgumentNullException(nameof(containerNode)); } if (memberNode == null) { throw new ArgumentNullException(nameof(memberNode)); } if (container == null) { throw new ArgumentNullException(nameof(container)); } // Check that the content is of the expected type. Assert.AreEqual(typeof(MemberContent), memberNode.GetType()); // A node with a MemberContent should have the same name that the member in the container. Assert.AreEqual(memberName, ((IMemberNode)memberNode).Name); // A node with a MemberContent should have its container as parent. Assert.AreEqual(containerNode, ((IMemberNode)memberNode).Parent); // A node with a MemberContent should have the member value as value of its content. Assert.AreEqual(member, memberNode.Retrieve()); // A node with a primitive MemberContent should not contain a reference. Assert.AreEqual(isReference, memberNode.IsReference); }
/// <summary> /// Tests the validity of a node that is an object that is a collection /// </summary> /// <param name="node">The node to validate.</param> /// <param name="obj">The value represented by this node.</param> /// <param name="isReference">Indicate whether the node is expected to contain an enumerable reference to the collection items.</param> public static void TestCollectionObjectContentNode(IContentNode node, object obj, bool isReference) { if (node == null) { throw new ArgumentNullException(nameof(node)); } if (obj == null) { throw new ArgumentNullException(nameof(obj)); } // Check that the content is of the expected type. Assert.IsInstanceOf <ObjectContent>(node); // A node with an ObjectContent should have the related object as value of its content. Assert.AreEqual(obj, node.Retrieve()); if (isReference) { // A node with an ObjectContent representing a collection of reference types should contain an enumerable reference. Assert.AreEqual(true, node.IsReference); Assert.Null(node.TargetReference); Assert.NotNull(node.ItemReferences); } else { // A node with an ObjectContent representing a collection of primitive or struct types should not contain a refernce. Assert.AreEqual(false, node.IsReference); } // A node with an ObjectContent representing a collection should not have any child. Assert.AreEqual(0, ((IObjectNode)node).Members.Count); }
protected static bool IsIndexExisting(IContentNode node, Index index) { if (node.IsReference) { var reference = node.ItemReferences; if (reference?.HasIndex(index) ?? false) { return(true); } } else { var value = node.Retrieve(); var collectionDescriptor = node.Descriptor as CollectionDescriptor; if (collectionDescriptor != null && index.IsInt && index.Int >= 0 && index.Int < collectionDescriptor.GetCollectionCount(value)) { return(true); } var dictionaryDescriptor = node.Descriptor as DictionaryDescriptor; if (dictionaryDescriptor != null && dictionaryDescriptor.KeyType.IsInstanceOfType(index.Value) && dictionaryDescriptor.ContainsKey(value, index.Value)) { return(true); } } return(false); }
private static void AssertCollection(IContentNode content, params string[] items) { for (var i = 0; i < items.Length; i++) { var item = items[i]; Assert.AreEqual(item, content.Retrieve(new Index(i))); } }
protected override void ExecuteSync(IContentNode content, Index index, object parameter) { var indices = (Tuple <int, int>)parameter; var sourceIndex = new Index(indices.Item1); var targetIndex = new Index(indices.Item2); var value = content.Retrieve(sourceIndex); content.Remove(value, sourceIndex); content.Add(value, targetIndex); }
protected override void ExecuteSync(IContentNode content, Index index, object parameter) { var oldName = index; var renamedObject = content.Retrieve(oldName); content.Remove(renamedObject, oldName); var newName = AddPrimitiveKeyCommand.GenerateStringKey(content.Value, content.Descriptor, (string)parameter); content.Add(renamedObject, newName); }
/// <summary> /// Sets the value of the model content associated to this <see cref="GraphNodeViewModel"/>. The value is actually modified only if the new value is different from the previous value. /// </summary> /// <returns><c>True</c> if the value has been modified, <c>false</c> otherwise.</returns> protected virtual bool SetModelContentValue(IContentNode node, object newValue) { var oldValue = node.Retrieve(Index); if (!Equals(oldValue, newValue)) { node.Update(newValue, Index); return(true); } return(false); }
protected override void ExecuteSync(IContentNode content, Index index, object parameter) { var value = content.Retrieve(index); var dictionaryDescriptor = (DictionaryDescriptor)TypeDescriptorFactory.Default.Find(value.GetType()); var newKey = dictionaryDescriptor.KeyType != typeof(string) ? new Index(Activator.CreateInstance(dictionaryDescriptor.KeyType)) : GenerateStringKey(value, dictionaryDescriptor, parameter as string); object newItem = null; if (!IsReferenceType(dictionaryDescriptor.ValueType)) { newItem = CreateInstance(dictionaryDescriptor.ValueType); } content.Add(newItem, newKey); }
private static void VerifyListenerEvent(MemberNodeChangeEventArgs e, IContentNode contentOwner, ContentChangeType type, Index index, object oldValue, object newValue, bool changeApplied) { Assert.NotNull(e); Assert.NotNull(contentOwner); Assert.AreEqual(type, e.ChangeType); Assert.AreEqual(contentOwner, e.Member); Assert.AreEqual(index, e.Index); Assert.AreEqual(newValue, e.NewValue); Assert.AreEqual(oldValue, e.OldValue); if (type == ContentChangeType.ValueChange) { Assert.AreEqual(changeApplied ? newValue : oldValue, contentOwner.Retrieve(index)); } }
public override Task Execute(IContentNode content, Index index, object parameter) { var hasIndex = content.Indices == null || content.Indices.Contains(index); var currentValue = hasIndex ? content.Retrieve(index) : (content.Type.IsValueType ? Activator.CreateInstance(content.Type) : null); var newValue = ChangeValue(currentValue, parameter); if (hasIndex) { if (!Equals(newValue, currentValue)) { content.Update(newValue, index); } } else { content.Add(newValue, index); } return(Task.FromResult(0)); }
/// <summary> /// Tests the validity of a node that is an object that is not a collection /// </summary> /// <param name="node">The node to validate.</param> /// <param name="obj">The value represented by this node.</param> /// <param name="childCount">The number of members expected in the node.</param> public static void TestNonCollectionObjectNode(IContentNode node, object obj, int childCount) { if (node == null) { throw new ArgumentNullException(nameof(node)); } if (obj == null) { throw new ArgumentNullException(nameof(obj)); } // Check that the content is of the expected type. Assert.IsInstanceOf <ObjectContent>(node); // A node with an ObjectContent should have the related object as value of its content. Assert.AreEqual(obj, node.Retrieve()); // A node with an ObjectContent should not contain a reference if it does not represent a collection. Assert.AreEqual(false, node.IsReference); // Check that we have the expected number of children. Assert.AreEqual(childCount, ((IObjectNode)node).Members.Count); }
protected override void ExecuteSync(IContentNode content, Index index, object parameter) { var item = content.Retrieve(index); content.Remove(item, index); }
public void Refresh(IContentNode ownerNode, NodeContainer nodeContainer) { var newObjectValue = ownerNode.Value; if (!(newObjectValue is IEnumerable)) { throw new ArgumentException(@"The object is not an IEnumerable", nameof(newObjectValue)); } ObjectValue = newObjectValue; var newReferences = new HybridDictionary <Index, ObjectReference>(); if (IsDictionary) { foreach (var item in (IEnumerable)ObjectValue) { var key = GetKey(item); var value = (ObjectReference)Reference.CreateReference(GetValue(item), ElementType, key); newReferences.Add(key, value); } } else { var i = 0; foreach (var item in (IEnumerable)ObjectValue) { var key = new Index(i); var value = (ObjectReference)Reference.CreateReference(item, ElementType, key); newReferences.Add(key, value); ++i; } } // The reference need to be updated if it has never been initialized, if the number of items is different, or if any index or any value is different. var needUpdate = items == null || newReferences.Count != items.Count || !AreItemsEqual(items, newReferences); if (needUpdate) { // We create a mapping values of the old list of references to their corresponding target node. We use a list because we can have multiple times the same target in items. var oldReferenceMapping = new List <KeyValuePair <object, ObjectReference> >(); if (items != null) { var existingIndices = ContentNode.GetIndices(ownerNode).ToList(); foreach (var item in items) { var boxedTarget = item.Value.TargetNode as BoxedContent; // For collection of struct, we need to update the target nodes first so equity comparer will work. Careful tho, we need to skip removed items! if (boxedTarget != null && existingIndices.Contains(item.Key)) { // If we are boxing a struct, we reuse the same nodes if they are type-compatible and just overwrite the struct value. var value = ownerNode.Retrieve(item.Key); if (value?.GetType() == item.Value.TargetNode?.Type) { boxedTarget.UpdateFromOwner(ownerNode.Retrieve(item.Key)); } } if (item.Value.ObjectValue != null) { oldReferenceMapping.Add(new KeyValuePair <object, ObjectReference>(item.Value.ObjectValue, item.Value)); } } } foreach (var newReference in newReferences) { if (newReference.Value.ObjectValue != null) { var found = false; var i = 0; foreach (var item in oldReferenceMapping) { if (Equals(newReference.Value.ObjectValue, item.Key)) { // If this value was already present in the old list of reference, just use the same target node in the new list. newReference.Value.SetTarget(item.Value.TargetNode); // Remove consumed existing reference so if there is a second entry with the same "key", it will be the other reference that will be used. oldReferenceMapping.RemoveAt(i); found = true; break; } ++i; } if (!found) { // Otherwise, do a full update that will properly initialize the new reference. newReference.Value.Refresh(ownerNode, nodeContainer, newReference.Key); } } } items = newReferences; // Remark: this works because both KeyCollection and List implements IReadOnlyCollection. Any internal change to HybridDictionary might break this! Indices = (IReadOnlyCollection <Index>)newReferences.Keys; } }
/// <summary> /// Retrieve the value of the model content associated to this <see cref="GraphNodeViewModel"/>. /// </summary> /// <returns>The value of the model content associated to this <see cref="GraphNodeViewModel"/>.</returns> protected object GetModelContentValue() { return(SourceNode.Retrieve(Index)); }