src/Elastic.Apm/Libraries/Newtonsoft.Json/Linq/JContainer.cs (687 lines of code) (raw):
#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Threading;
using Elastic.Apm.Libraries.Newtonsoft.Json.Utilities;
#if HAVE_INOTIFY_COLLECTION_CHANGED
using System.Collections.Specialized;
#endif
#if !HAVE_LINQ
#else
#endif
#nullable enable
namespace Elastic.Apm.Libraries.Newtonsoft.Json.Linq
{
/// <summary>
/// Represents a token that can contain other tokens.
/// </summary>
internal abstract partial class JContainer
: JToken, IList<JToken>
#if HAVE_COMPONENT_MODEL
, ITypedList, IBindingList
#endif
, IList
#if HAVE_INOTIFY_COLLECTION_CHANGED
, INotifyCollectionChanged
#endif
{
#if HAVE_COMPONENT_MODEL
internal ListChangedEventHandler? _listChanged;
internal AddingNewEventHandler? _addingNew;
/// <summary>
/// Occurs when the list changes or an item in the list changes.
/// </summary>
public event ListChangedEventHandler ListChanged
{
add => _listChanged += value;
remove => _listChanged -= value;
}
/// <summary>
/// Occurs before an item is added to the collection.
/// </summary>
public event AddingNewEventHandler AddingNew
{
add => _addingNew += value;
remove => _addingNew -= value;
}
#endif
#if HAVE_INOTIFY_COLLECTION_CHANGED
internal NotifyCollectionChangedEventHandler? _collectionChanged;
/// <summary>
/// Occurs when the items list of the collection has changed, or the collection is reset.
/// </summary>
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add { _collectionChanged += value; }
remove { _collectionChanged -= value; }
}
#endif
/// <summary>
/// Gets the container's children tokens.
/// </summary>
/// <value>The container's children tokens.</value>
protected abstract IList<JToken> ChildrenTokens { get; }
private object? _syncRoot;
#if (HAVE_COMPONENT_MODEL || HAVE_INOTIFY_COLLECTION_CHANGED)
private bool _busy;
#endif
internal JContainer() { }
internal JContainer(JContainer other)
: this()
{
ValidationUtils.ArgumentNotNull(other, nameof(other));
var i = 0;
foreach (var child in other)
{
TryAddInternal(i, child, false);
i++;
}
CopyAnnotations(this, other);
}
internal void CheckReentrancy()
{
#if (HAVE_COMPONENT_MODEL || HAVE_INOTIFY_COLLECTION_CHANGED)
if (_busy)
{
throw new InvalidOperationException("Cannot change {0} during a collection change event.".FormatWith(CultureInfo.InvariantCulture, GetType()));
}
#endif
}
internal virtual IList<JToken> CreateChildrenCollection() => new List<JToken>();
#if HAVE_COMPONENT_MODEL
/// <summary>
/// Raises the <see cref="AddingNew"/> event.
/// </summary>
/// <param name="e">The <see cref="AddingNewEventArgs"/> instance containing the event data.</param>
protected virtual void OnAddingNew(AddingNewEventArgs e)
{
_addingNew?.Invoke(this, e);
}
/// <summary>
/// Raises the <see cref="ListChanged"/> event.
/// </summary>
/// <param name="e">The <see cref="ListChangedEventArgs"/> instance containing the event data.</param>
protected virtual void OnListChanged(ListChangedEventArgs e)
{
ListChangedEventHandler? handler = _listChanged;
if (handler != null)
{
_busy = true;
try
{
handler(this, e);
}
finally
{
_busy = false;
}
}
}
#endif
#if HAVE_INOTIFY_COLLECTION_CHANGED
/// <summary>
/// Raises the <see cref="CollectionChanged"/> event.
/// </summary>
/// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler? handler = _collectionChanged;
if (handler != null)
{
_busy = true;
try
{
handler(this, e);
}
finally
{
_busy = false;
}
}
}
#endif
/// <summary>
/// Gets a value indicating whether this token has child tokens.
/// </summary>
/// <value>
/// <c>true</c> if this token has child values; otherwise, <c>false</c>.
/// </value>
public override bool HasValues => ChildrenTokens.Count > 0;
internal bool ContentsEqual(JContainer container)
{
if (container == this) return true;
var t1 = ChildrenTokens;
var t2 = container.ChildrenTokens;
if (t1.Count != t2.Count) return false;
for (var i = 0; i < t1.Count; i++)
{
if (!t1[i].DeepEquals(t2[i])) return false;
}
return true;
}
/// <summary>
/// Get the first child token of this token.
/// </summary>
/// <value>
/// A <see cref="JToken" /> containing the first child token of the <see cref="JToken" />.
/// </value>
public override JToken? First
{
get
{
var children = ChildrenTokens;
return children.Count > 0 ? children[0] : null;
}
}
/// <summary>
/// Get the last child token of this token.
/// </summary>
/// <value>
/// A <see cref="JToken" /> containing the last child token of the <see cref="JToken" />.
/// </value>
public override JToken? Last
{
get
{
var children = ChildrenTokens;
var count = children.Count;
return count > 0 ? children[count - 1] : null;
}
}
/// <summary>
/// Returns a collection of the child tokens of this token, in document order.
/// </summary>
/// <returns>
/// An <see cref="IEnumerable{T}" /> of <see cref="JToken" /> containing the child tokens of this <see cref="JToken" />, in
/// document order.
/// </returns>
public override JEnumerable<JToken> Children() => new(ChildrenTokens);
/// <summary>
/// Returns a collection of the child values of this token, in document order.
/// </summary>
/// <typeparam name="T">The type to convert the values to.</typeparam>
/// <returns>
/// A <see cref="IEnumerable{T}" /> containing the child values of this <see cref="JToken" />, in document order.
/// </returns>
public override IEnumerable<T?> Values<T>() where T : default => ChildrenTokens.Convert<JToken, T>();
/// <summary>
/// Returns a collection of the descendant tokens for this token in document order.
/// </summary>
/// <returns>
/// An <see cref="IEnumerable{T}" /> of <see cref="JToken" /> containing the descendant tokens of the
/// <see cref="JToken" />.
/// </returns>
public IEnumerable<JToken> Descendants() => GetDescendants(false);
/// <summary>
/// Returns a collection of the tokens that contain this token, and all descendant tokens of this token, in document order.
/// </summary>
/// <returns>
/// An <see cref="IEnumerable{T}" /> of <see cref="JToken" /> containing this token, and all the descendant tokens
/// of the <see cref="JToken" />.
/// </returns>
public IEnumerable<JToken> DescendantsAndSelf() => GetDescendants(true);
internal IEnumerable<JToken> GetDescendants(bool self)
{
if (self) yield return this;
foreach (var o in ChildrenTokens)
{
yield return o;
if (o is JContainer c)
{
foreach (var d in c.Descendants()) yield return d;
}
}
}
internal bool IsMultiContent([NotNullWhen(true)] object? content) =>
content is IEnumerable && !(content is string) && !(content is JToken) && !(content is byte[]);
internal JToken EnsureParentToken(JToken? item, bool skipParentCheck)
{
if (item == null) return JValue.CreateNull();
if (skipParentCheck) return item;
// to avoid a token having multiple parents or creating a recursive loop, create a copy if...
// the item already has a parent
// the item is being added to itself
// the item is being added to the root parent of itself
if (item.Parent != null || item == this || item.HasValues && Root == item) item = item.CloneToken();
return item;
}
internal abstract int IndexOfItem(JToken? item);
internal virtual bool InsertItem(int index, JToken? item, bool skipParentCheck)
{
var children = ChildrenTokens;
if (index > children.Count) throw new ArgumentOutOfRangeException(nameof(index), "Index must be within the bounds of the List.");
CheckReentrancy();
item = EnsureParentToken(item, skipParentCheck);
var previous = index == 0 ? null : children[index - 1];
// haven't inserted new token yet so next token is still at the inserting index
var next = index == children.Count ? null : children[index];
ValidateToken(item, null);
item.Parent = this;
item.Previous = previous;
if (previous != null) previous.Next = item;
item.Next = next;
if (next != null) next.Previous = item;
children.Insert(index, item);
#if HAVE_COMPONENT_MODEL
if (_listChanged != null)
{
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, index));
}
#endif
#if HAVE_INOTIFY_COLLECTION_CHANGED
if (_collectionChanged != null)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
#endif
return true;
}
internal virtual void RemoveItemAt(int index)
{
var children = ChildrenTokens;
if (index < 0) throw new ArgumentOutOfRangeException(nameof(index), "Index is less than 0.");
if (index >= children.Count) throw new ArgumentOutOfRangeException(nameof(index), "Index is equal to or greater than Count.");
CheckReentrancy();
var item = children[index];
var previous = index == 0 ? null : children[index - 1];
var next = index == children.Count - 1 ? null : children[index + 1];
if (previous != null) previous.Next = next;
if (next != null) next.Previous = previous;
item.Parent = null;
item.Previous = null;
item.Next = null;
children.RemoveAt(index);
#if HAVE_COMPONENT_MODEL
if (_listChanged != null)
{
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
}
#endif
#if HAVE_INOTIFY_COLLECTION_CHANGED
if (_collectionChanged != null)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
}
#endif
}
internal virtual bool RemoveItem(JToken? item)
{
if (item != null)
{
var index = IndexOfItem(item);
if (index >= 0)
{
RemoveItemAt(index);
return true;
}
}
return false;
}
internal virtual JToken GetItem(int index) => ChildrenTokens[index];
internal virtual void SetItem(int index, JToken? item)
{
var children = ChildrenTokens;
if (index < 0) throw new ArgumentOutOfRangeException(nameof(index), "Index is less than 0.");
if (index >= children.Count) throw new ArgumentOutOfRangeException(nameof(index), "Index is equal to or greater than Count.");
var existing = children[index];
if (IsTokenUnchanged(existing, item)) return;
CheckReentrancy();
item = EnsureParentToken(item, false);
ValidateToken(item, existing);
var previous = index == 0 ? null : children[index - 1];
var next = index == children.Count - 1 ? null : children[index + 1];
item.Parent = this;
item.Previous = previous;
if (previous != null) previous.Next = item;
item.Next = next;
if (next != null) next.Previous = item;
children[index] = item;
existing.Parent = null;
existing.Previous = null;
existing.Next = null;
#if HAVE_COMPONENT_MODEL
if (_listChanged != null)
{
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));
}
#endif
#if HAVE_INOTIFY_COLLECTION_CHANGED
if (_collectionChanged != null)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, existing, index));
}
#endif
}
internal virtual void ClearItems()
{
CheckReentrancy();
var children = ChildrenTokens;
foreach (var item in children)
{
item.Parent = null;
item.Previous = null;
item.Next = null;
}
children.Clear();
#if HAVE_COMPONENT_MODEL
if (_listChanged != null)
{
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
#endif
#if HAVE_INOTIFY_COLLECTION_CHANGED
if (_collectionChanged != null)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
#endif
}
internal virtual void ReplaceItem(JToken existing, JToken replacement)
{
if (existing == null || existing.Parent != this) return;
var index = IndexOfItem(existing);
SetItem(index, replacement);
}
internal virtual bool ContainsItem(JToken? item) => IndexOfItem(item) != -1;
internal virtual void CopyItemsTo(Array array, int arrayIndex)
{
if (array == null) throw new ArgumentNullException(nameof(array));
if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex), "arrayIndex is less than 0.");
if (arrayIndex >= array.Length && arrayIndex != 0)
throw new ArgumentException("arrayIndex is equal to or greater than the length of array.");
if (Count > array.Length - arrayIndex)
throw new ArgumentException(
"The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array.");
var index = 0;
foreach (var token in ChildrenTokens)
{
array.SetValue(token, arrayIndex + index);
index++;
}
}
internal static bool IsTokenUnchanged(JToken currentValue, JToken? newValue)
{
if (currentValue is JValue v1)
{
if (newValue == null)
{
// null will get turned into a JValue of type null
return v1.Type == JTokenType.Null;
}
return v1.Equals(newValue);
}
return false;
}
internal virtual void ValidateToken(JToken o, JToken? existing)
{
ValidationUtils.ArgumentNotNull(o, nameof(o));
if (o.Type == JTokenType.Property)
throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType()));
}
/// <summary>
/// Adds the specified content as children of this <see cref="JToken" />.
/// </summary>
/// <param name="content">The content to be added.</param>
public virtual void Add(object? content) => TryAddInternal(ChildrenTokens.Count, content, false);
internal bool TryAdd(object? content) => TryAddInternal(ChildrenTokens.Count, content, false);
internal void AddAndSkipParentCheck(JToken token) => TryAddInternal(ChildrenTokens.Count, token, true);
/// <summary>
/// Adds the specified content as the first children of this <see cref="JToken" />.
/// </summary>
/// <param name="content">The content to be added.</param>
public void AddFirst(object? content) => TryAddInternal(0, content, false);
internal bool TryAddInternal(int index, object? content, bool skipParentCheck)
{
if (IsMultiContent(content))
{
var enumerable = (IEnumerable)content;
var multiIndex = index;
foreach (var c in enumerable)
{
TryAddInternal(multiIndex, c, skipParentCheck);
multiIndex++;
}
return true;
}
var item = CreateFromContent(content);
return InsertItem(index, item, skipParentCheck);
}
internal static JToken CreateFromContent(object? content)
{
if (content is JToken token) return token;
return new JValue(content);
}
/// <summary>
/// Creates a <see cref="JsonWriter" /> that can be used to add tokens to the <see cref="JToken" />.
/// </summary>
/// <returns>A <see cref="JsonWriter" /> that is ready to have content written to it.</returns>
public JsonWriter CreateWriter() => new JTokenWriter(this);
/// <summary>
/// Replaces the child nodes of this token with the specified content.
/// </summary>
/// <param name="content">The content.</param>
public void ReplaceAll(object content)
{
ClearItems();
Add(content);
}
/// <summary>
/// Removes the child nodes from this token.
/// </summary>
public void RemoveAll() => ClearItems();
internal abstract void MergeItem(object content, JsonMergeSettings? settings);
/// <summary>
/// Merge the specified content into this <see cref="JToken" />.
/// </summary>
/// <param name="content">The content to be merged.</param>
public void Merge(object content) => MergeItem(content, null);
/// <summary>
/// Merge the specified content into this <see cref="JToken" /> using <see cref="JsonMergeSettings" />.
/// </summary>
/// <param name="content">The content to be merged.</param>
/// <param name="settings">The <see cref="JsonMergeSettings" /> used to merge the content.</param>
public void Merge(object content, JsonMergeSettings? settings) => MergeItem(content, settings);
internal void ReadTokenFrom(JsonReader reader, JsonLoadSettings? options)
{
var startDepth = reader.Depth;
if (!reader.Read())
throw JsonReaderException.Create(reader,
"Error reading {0} from JsonReader.".FormatWith(CultureInfo.InvariantCulture, GetType().Name));
ReadContentFrom(reader, options);
var endDepth = reader.Depth;
if (endDepth > startDepth)
throw JsonReaderException.Create(reader,
"Unexpected end of content while loading {0}.".FormatWith(CultureInfo.InvariantCulture, GetType().Name));
}
internal void ReadContentFrom(JsonReader r, JsonLoadSettings? settings)
{
ValidationUtils.ArgumentNotNull(r, nameof(r));
var lineInfo = r as IJsonLineInfo;
var parent = this;
do
{
if (parent is JProperty p && p.Value != null)
{
if (parent == this) return;
parent = parent.Parent;
}
MiscellaneousUtils.Assert(parent != null);
switch (r.TokenType)
{
case JsonToken.None:
// new reader. move to actual content
break;
case JsonToken.StartArray:
var a = new JArray();
a.SetLineInfo(lineInfo, settings);
parent.Add(a);
parent = a;
break;
case JsonToken.EndArray:
if (parent == this) return;
parent = parent.Parent;
break;
case JsonToken.StartObject:
var o = new JObject();
o.SetLineInfo(lineInfo, settings);
parent.Add(o);
parent = o;
break;
case JsonToken.EndObject:
if (parent == this) return;
parent = parent.Parent;
break;
case JsonToken.StartConstructor:
var constructor = new JConstructor(r.Value!.ToString());
constructor.SetLineInfo(lineInfo, settings);
parent.Add(constructor);
parent = constructor;
break;
case JsonToken.EndConstructor:
if (parent == this) return;
parent = parent.Parent;
break;
case JsonToken.String:
case JsonToken.Integer:
case JsonToken.Float:
case JsonToken.Date:
case JsonToken.Boolean:
case JsonToken.Bytes:
var v = new JValue(r.Value);
v.SetLineInfo(lineInfo, settings);
parent.Add(v);
break;
case JsonToken.Comment:
if (settings != null && settings.CommentHandling == CommentHandling.Load)
{
v = JValue.CreateComment(r.Value!.ToString());
v.SetLineInfo(lineInfo, settings);
parent.Add(v);
}
break;
case JsonToken.Null:
v = JValue.CreateNull();
v.SetLineInfo(lineInfo, settings);
parent.Add(v);
break;
case JsonToken.Undefined:
v = JValue.CreateUndefined();
v.SetLineInfo(lineInfo, settings);
parent.Add(v);
break;
case JsonToken.PropertyName:
var property = ReadProperty(r, settings, lineInfo, parent);
if (property != null)
parent = property;
else
r.Skip();
break;
default:
throw new InvalidOperationException(
"The JsonReader should not be on a token of type {0}.".FormatWith(CultureInfo.InvariantCulture, r.TokenType));
}
} while (r.Read());
}
private static JProperty? ReadProperty(JsonReader r, JsonLoadSettings? settings, IJsonLineInfo? lineInfo, JContainer parent)
{
var duplicatePropertyNameHandling = settings?.DuplicatePropertyNameHandling ?? DuplicatePropertyNameHandling.Replace;
var parentObject = (JObject)parent;
var propertyName = r.Value!.ToString();
var existingPropertyWithName = parentObject.Property(propertyName, StringComparison.Ordinal);
if (existingPropertyWithName != null)
{
if (duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Ignore)
return null;
if (duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Error)
throw JsonReaderException.Create(r,
"Property with the name '{0}' already exists in the current JSON object.".FormatWith(CultureInfo.InvariantCulture,
propertyName));
}
var property = new JProperty(propertyName);
property.SetLineInfo(lineInfo, settings);
// handle multiple properties with the same name in JSON
if (existingPropertyWithName == null)
parent.Add(property);
else
existingPropertyWithName.Replace(property);
return property;
}
internal int ContentsHashCode()
{
var hashCode = 0;
foreach (var item in ChildrenTokens) hashCode ^= item.GetDeepHashCode();
return hashCode;
}
#if HAVE_COMPONENT_MODEL
string ITypedList.GetListName(PropertyDescriptor[] listAccessors)
{
return string.Empty;
}
PropertyDescriptorCollection? ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
{
ICustomTypeDescriptor? d = First as ICustomTypeDescriptor;
return d?.GetProperties();
}
#endif
#region IList<JToken> Members
int IList<JToken>.IndexOf(JToken item) => IndexOfItem(item);
void IList<JToken>.Insert(int index, JToken item) => InsertItem(index, item, false);
void IList<JToken>.RemoveAt(int index) => RemoveItemAt(index);
JToken IList<JToken>.this[int index]
{
get => GetItem(index);
set => SetItem(index, value);
}
#endregion
#region ICollection<JToken> Members
void ICollection<JToken>.Add(JToken item) => Add(item);
void ICollection<JToken>.Clear() => ClearItems();
bool ICollection<JToken>.Contains(JToken item) => ContainsItem(item);
void ICollection<JToken>.CopyTo(JToken[] array, int arrayIndex) => CopyItemsTo(array, arrayIndex);
bool ICollection<JToken>.IsReadOnly => false;
bool ICollection<JToken>.Remove(JToken item) => RemoveItem(item);
#endregion
private JToken? EnsureValue(object value)
{
if (value == null) return null;
if (value is JToken token) return token;
throw new ArgumentException("Argument is not a JToken.");
}
#region IList Members
int IList.Add(object value)
{
Add(EnsureValue(value));
return Count - 1;
}
void IList.Clear() => ClearItems();
bool IList.Contains(object value) => ContainsItem(EnsureValue(value));
int IList.IndexOf(object value) => IndexOfItem(EnsureValue(value));
void IList.Insert(int index, object value) => InsertItem(index, EnsureValue(value), false);
bool IList.IsFixedSize => false;
bool IList.IsReadOnly => false;
void IList.Remove(object value) => RemoveItem(EnsureValue(value));
void IList.RemoveAt(int index) => RemoveItemAt(index);
object IList.this[int index]
{
get => GetItem(index);
set => SetItem(index, EnsureValue(value));
}
#endregion
#region ICollection Members
void ICollection.CopyTo(Array array, int index) => CopyItemsTo(array, index);
/// <summary>
/// Gets the count of child JSON tokens.
/// </summary>
/// <value>The count of child JSON tokens.</value>
public int Count => ChildrenTokens.Count;
bool ICollection.IsSynchronized => false;
object ICollection.SyncRoot
{
get
{
if (_syncRoot == null) Interlocked.CompareExchange(ref _syncRoot, new object(), null);
return _syncRoot;
}
}
#endregion
#region IBindingList Members
#if HAVE_COMPONENT_MODEL
void IBindingList.AddIndex(PropertyDescriptor property)
{
}
object IBindingList.AddNew()
{
AddingNewEventArgs args = new AddingNewEventArgs();
OnAddingNew(args);
if (args.NewObject == null)
{
throw new JsonException("Could not determine new value to add to '{0}'.".FormatWith(CultureInfo.InvariantCulture, GetType()));
}
if (!(args.NewObject is JToken newItem))
{
throw new JsonException("New item to be added to collection must be compatible with {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JToken)));
}
Add(newItem);
return newItem;
}
bool IBindingList.AllowEdit => true;
bool IBindingList.AllowNew => true;
bool IBindingList.AllowRemove => true;
void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)
{
throw new NotSupportedException();
}
int IBindingList.Find(PropertyDescriptor property, object key)
{
throw new NotSupportedException();
}
bool IBindingList.IsSorted => false;
void IBindingList.RemoveIndex(PropertyDescriptor property)
{
}
void IBindingList.RemoveSort()
{
throw new NotSupportedException();
}
ListSortDirection IBindingList.SortDirection => ListSortDirection.Ascending;
PropertyDescriptor? IBindingList.SortProperty => null;
bool IBindingList.SupportsChangeNotification => true;
bool IBindingList.SupportsSearching => false;
bool IBindingList.SupportsSorting => false;
#endif
#endregion
internal static void MergeEnumerableContent(JContainer target, IEnumerable content, JsonMergeSettings? settings)
{
switch (settings?.MergeArrayHandling ?? MergeArrayHandling.Concat)
{
case MergeArrayHandling.Concat:
foreach (JToken item in content) target.Add(item);
break;
case MergeArrayHandling.Union:
#if HAVE_HASH_SET
HashSet<JToken> items = new HashSet<JToken>(target, EqualityComparer);
foreach (JToken item in content)
{
if (items.Add(item))
{
target.Add(item);
}
}
#else
var items = new Dictionary<JToken, bool>(EqualityComparer);
foreach (var t in target) items[t] = true;
foreach (JToken item in content)
{
if (!items.ContainsKey(item))
{
items[item] = true;
target.Add(item);
}
}
#endif
break;
case MergeArrayHandling.Replace:
if (target == content) break;
target.ClearItems();
foreach (JToken item in content) target.Add(item);
break;
case MergeArrayHandling.Merge:
var i = 0;
foreach (var targetItem in content)
{
if (i < target.Count)
{
var sourceItem = target[i];
if (sourceItem is JContainer existingContainer)
existingContainer.Merge(targetItem, settings);
else
{
if (targetItem != null)
{
var contentValue = CreateFromContent(targetItem);
if (contentValue.Type != JTokenType.Null) target[i] = contentValue;
}
}
}
else
target.Add(targetItem);
i++;
}
break;
default:
throw new ArgumentOutOfRangeException(nameof(settings), "Unexpected merge array handling when merging JSON.");
}
}
}
}