/// <inheritdoc/> public void Refresh(object newObjectValue) { if (!(newObjectValue is IEnumerable)) { throw new ArgumentException(@"The object is not an IEnumerable", nameof(newObjectValue)); } ObjectValue = newObjectValue; references.Clear(); references.AddRange( IsDictionary ? ((IEnumerable)ObjectValue).Cast <object>().Select(x => (ObjectReference)Reference.CreateReference(GetValue(x), elementType, GetKey(x))) : ((IEnumerable)ObjectValue).Cast <object>().Select((x, i) => (ObjectReference)Reference.CreateReference(x, elementType, new Index(i)))); indices.Clear(); foreach (var reference in references) { indices.Add(reference.Index); } }
public void Refresh(IGraphNode ownerNode, NodeContainer nodeContainer, NodeFactoryDelegate nodeFactory) { var newObjectValue = ownerNode.Content.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) { oldReferenceMapping.AddRange(items.Values.Where(x => x.ObjectValue != null && !(x.TargetNode?.Content is BoxedContent)).Select(x => new KeyValuePair <object, ObjectReference>(x.ObjectValue, x))); } 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, nodeFactory, 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; } }
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; } }
public void Refresh(IGraphNode ownerNode, NodeContainer nodeContainer, NodeFactoryDelegate nodeFactory) { var newObjectValue = ownerNode.Content.Value; if (!(newObjectValue is IEnumerable)) { throw new ArgumentException(@"The object is not an IEnumerable", nameof(newObjectValue)); } ObjectValue = newObjectValue; // First, let's build a new list of uninitialized references var newReferences = new List <ObjectReference>(IsDictionary ? ((IEnumerable)ObjectValue).Cast <object>().Select(x => (ObjectReference)Reference.CreateReference(GetValue(x), elementType, GetKey(x))) : ((IEnumerable)ObjectValue).Cast <object>().Select((x, i) => (ObjectReference)Reference.CreateReference(x, elementType, new Index(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 = references == null || newReferences.Count != references.Count || newReferences.Zip(references).Any(x => !x.Item1.Index.Equals(x.Item2.Index) || !Equals(x.Item1.ObjectValue, x.Item2.ObjectValue)); if (needUpdate) { // We create a dictionary that maps values of the old list of references to their corresponding target node. var dictionary = references?.Where(x => x.ObjectValue != null && !(x.TargetNode?.Content is BoxedContent)).ToDictionary(x => x.ObjectValue) ?? new Dictionary <object, ObjectReference>(); foreach (var newReference in newReferences) { if (newReference.ObjectValue != null) { ObjectReference oldReference; if (dictionary.TryGetValue(newReference.ObjectValue, out oldReference)) { // If this value was already present in the old list of reference, just use the same target node in the new list. newReference.SetTarget(oldReference.TargetNode); } else { // Otherwise, do a full update that will properly initialize the new reference. newReference.Refresh(ownerNode, nodeContainer, nodeFactory, newReference.Index); } } } references = newReferences; indices = newReferences.Select(x => x.Index).ToList(); } }