1 #region License
2 // Copyright (c) 2007 James Newton-King
3 //
4 // Permission is hereby granted, free of charge, to any person
5 // obtaining a copy of this software and associated documentation
6 // files (the "Software"), to deal in the Software without
7 // restriction, including without limitation the rights to use,
8 // copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following
11 // conditions:
12 //
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 // OTHER DEALINGS IN THE SOFTWARE.
24 #endregion
25 
26 using System;
27 using System.Collections.Generic;
28 #if !PORTABLE40
29 using System.Collections.Specialized;
30 #endif
31 using System.Threading;
32 using Newtonsoft.Json.Utilities;
33 using System.Collections;
34 using System.Globalization;
35 using System.ComponentModel;
36 #if NET20
37 using Newtonsoft.Json.Utilities.LinqBridge;
38 #else
39 using System.Linq;
40 
41 #endif
42 
43 namespace Newtonsoft.Json.Linq
44 {
45     /// <summary>
46     /// Represents a token that can contain other tokens.
47     /// </summary>
48     public abstract class JContainer : JToken, IList<JToken>
49 #if !(DOTNET || PORTABLE || PORTABLE40)
50         , ITypedList, IBindingList
51 #endif
52         , IList
53 #if !(NET20 || NET35 || PORTABLE40)
54         , INotifyCollectionChanged
55 #endif
56     {
57 #if !(DOTNET || PORTABLE40 || PORTABLE)
58         internal ListChangedEventHandler _listChanged;
59         internal AddingNewEventHandler _addingNew;
60 
61         /// <summary>
62         /// Occurs when the list changes or an item in the list changes.
63         /// </summary>
64         public event ListChangedEventHandler ListChanged
65         {
66             add { _listChanged += value; }
67             remove { _listChanged -= value; }
68         }
69 
70         /// <summary>
71         /// Occurs before an item is added to the collection.
72         /// </summary>
73         public event AddingNewEventHandler AddingNew
74         {
75             add { _addingNew += value; }
76             remove { _addingNew -= value; }
77         }
78 #endif
79 #if !(NET20 || NET35 || PORTABLE40)
80         internal NotifyCollectionChangedEventHandler _collectionChanged;
81 
82         /// <summary>
83         /// Occurs when the items list of the collection has changed, or the collection is reset.
84         /// </summary>
85         public event NotifyCollectionChangedEventHandler CollectionChanged
86         {
87             add { _collectionChanged += value; }
88             remove { _collectionChanged -= value; }
89         }
90 #endif
91 
92         /// <summary>
93         /// Gets the container's children tokens.
94         /// </summary>
95         /// <value>The container's children tokens.</value>
96         protected abstract IList<JToken> ChildrenTokens { get; }
97 
98         private object _syncRoot;
99 #if !(PORTABLE40)
100         private bool _busy;
101 #endif
102 
JContainer()103         internal JContainer()
104         {
105         }
106 
JContainer(JContainer other)107         internal JContainer(JContainer other)
108             : this()
109         {
110             ValidationUtils.ArgumentNotNull(other, nameof(other));
111 
112             int i = 0;
113             foreach (JToken child in other)
114             {
115                 AddInternal(i, child, false);
116                 i++;
117             }
118         }
119 
CheckReentrancy()120         internal void CheckReentrancy()
121         {
122 #if !(PORTABLE40)
123             if (_busy)
124             {
125                 throw new InvalidOperationException("Cannot change {0} during a collection change event.".FormatWith(CultureInfo.InvariantCulture, GetType()));
126             }
127 #endif
128         }
129 
CreateChildrenCollection()130         internal virtual IList<JToken> CreateChildrenCollection()
131         {
132             return new List<JToken>();
133         }
134 
135 #if !(DOTNET || PORTABLE40 || PORTABLE)
136         /// <summary>
137         /// Raises the <see cref="AddingNew"/> event.
138         /// </summary>
139         /// <param name="e">The <see cref="AddingNewEventArgs"/> instance containing the event data.</param>
OnAddingNew(AddingNewEventArgs e)140         protected virtual void OnAddingNew(AddingNewEventArgs e)
141         {
142             AddingNewEventHandler handler = _addingNew;
143             if (handler != null)
144             {
145                 handler(this, e);
146             }
147         }
148 
149         /// <summary>
150         /// Raises the <see cref="ListChanged"/> event.
151         /// </summary>
152         /// <param name="e">The <see cref="ListChangedEventArgs"/> instance containing the event data.</param>
OnListChanged(ListChangedEventArgs e)153         protected virtual void OnListChanged(ListChangedEventArgs e)
154         {
155             ListChangedEventHandler handler = _listChanged;
156 
157             if (handler != null)
158             {
159                 _busy = true;
160                 try
161                 {
162                     handler(this, e);
163                 }
164                 finally
165                 {
166                     _busy = false;
167                 }
168             }
169         }
170 #endif
171 #if !(NET20 || NET35 || PORTABLE40)
172         /// <summary>
173         /// Raises the <see cref="CollectionChanged"/> event.
174         /// </summary>
175         /// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
OnCollectionChanged(NotifyCollectionChangedEventArgs e)176         protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
177         {
178             NotifyCollectionChangedEventHandler handler = _collectionChanged;
179 
180             if (handler != null)
181             {
182                 _busy = true;
183                 try
184                 {
185                     handler(this, e);
186                 }
187                 finally
188                 {
189                     _busy = false;
190                 }
191             }
192         }
193 #endif
194 
195         /// <summary>
196         /// Gets a value indicating whether this token has child tokens.
197         /// </summary>
198         /// <value>
199         /// 	<c>true</c> if this token has child values; otherwise, <c>false</c>.
200         /// </value>
201         public override bool HasValues
202         {
203             get { return ChildrenTokens.Count > 0; }
204         }
205 
ContentsEqual(JContainer container)206         internal bool ContentsEqual(JContainer container)
207         {
208             if (container == this)
209             {
210                 return true;
211             }
212 
213             IList<JToken> t1 = ChildrenTokens;
214             IList<JToken> t2 = container.ChildrenTokens;
215 
216             if (t1.Count != t2.Count)
217             {
218                 return false;
219             }
220 
221             for (int i = 0; i < t1.Count; i++)
222             {
223                 if (!t1[i].DeepEquals(t2[i]))
224                 {
225                     return false;
226                 }
227             }
228 
229             return true;
230         }
231 
232         /// <summary>
233         /// Get the first child token of this token.
234         /// </summary>
235         /// <value>
236         /// A <see cref="JToken"/> containing the first child token of the <see cref="JToken"/>.
237         /// </value>
238         public override JToken First
239         {
240             get
241             {
242                 IList<JToken> children = ChildrenTokens;
243                 return (children.Count > 0) ? children[0] : null;
244             }
245         }
246 
247         /// <summary>
248         /// Get the last child token of this token.
249         /// </summary>
250         /// <value>
251         /// A <see cref="JToken"/> containing the last child token of the <see cref="JToken"/>.
252         /// </value>
253         public override JToken Last
254         {
255             get
256             {
257                 IList<JToken> children = ChildrenTokens;
258                 int count = children.Count;
259                 return (count > 0) ? children[count - 1] : null;
260             }
261         }
262 
263         /// <summary>
264         /// Returns a collection of the child tokens of this token, in document order.
265         /// </summary>
266         /// <returns>
267         /// An <see cref="IEnumerable{T}"/> of <see cref="JToken"/> containing the child tokens of this <see cref="JToken"/>, in document order.
268         /// </returns>
Children()269         public override JEnumerable<JToken> Children()
270         {
271             return new JEnumerable<JToken>(ChildrenTokens);
272         }
273 
274         /// <summary>
275         /// Returns a collection of the child values of this token, in document order.
276         /// </summary>
277         /// <typeparam name="T">The type to convert the values to.</typeparam>
278         /// <returns>
279         /// A <see cref="IEnumerable{T}"/> containing the child values of this <see cref="JToken"/>, in document order.
280         /// </returns>
Values()281         public override IEnumerable<T> Values<T>()
282         {
283             return ChildrenTokens.Convert<JToken, T>();
284         }
285 
286         /// <summary>
287         /// Returns a collection of the descendant tokens for this token in document order.
288         /// </summary>
289         /// <returns>An <see cref="IEnumerable{JToken}"/> containing the descendant tokens of the <see cref="JToken"/>.</returns>
Descendants()290         public IEnumerable<JToken> Descendants()
291         {
292             return GetDescendants(false);
293         }
294 
295         /// <summary>
296         /// Returns a collection of the tokens that contain this token, and all descendant tokens of this token, in document order.
297         /// </summary>
298         /// <returns>An <see cref="IEnumerable{JToken}"/> containing this token, and all the descendant tokens of the <see cref="JToken"/>.</returns>
DescendantsAndSelf()299         public IEnumerable<JToken> DescendantsAndSelf()
300         {
301             return GetDescendants(true);
302         }
303 
GetDescendants(bool self)304         internal IEnumerable<JToken> GetDescendants(bool self)
305         {
306             if (self)
307             {
308                 yield return this;
309             }
310 
311             foreach (JToken o in ChildrenTokens)
312             {
313                 yield return o;
314                 JContainer c = o as JContainer;
315                 if (c != null)
316                 {
317                     foreach (JToken d in c.Descendants())
318                     {
319                         yield return d;
320                     }
321                 }
322             }
323         }
324 
IsMultiContent(object content)325         internal bool IsMultiContent(object content)
326         {
327             return (content is IEnumerable && !(content is string) && !(content is JToken) && !(content is byte[]));
328         }
329 
EnsureParentToken(JToken item, bool skipParentCheck)330         internal JToken EnsureParentToken(JToken item, bool skipParentCheck)
331         {
332             if (item == null)
333             {
334                 return JValue.CreateNull();
335             }
336 
337             if (skipParentCheck)
338             {
339                 return item;
340             }
341 
342             // to avoid a token having multiple parents or creating a recursive loop, create a copy if...
343             // the item already has a parent
344             // the item is being added to itself
345             // the item is being added to the root parent of itself
346             if (item.Parent != null || item == this || (item.HasValues && Root == item))
347             {
348                 item = item.CloneToken();
349             }
350 
351             return item;
352         }
353 
IndexOfItem(JToken item)354         internal abstract int IndexOfItem(JToken item);
355 
InsertItem(int index, JToken item, bool skipParentCheck)356         internal virtual void InsertItem(int index, JToken item, bool skipParentCheck)
357         {
358             IList<JToken> children = ChildrenTokens;
359 
360             if (index > children.Count)
361             {
362                 throw new ArgumentOutOfRangeException(nameof(index), "Index must be within the bounds of the List.");
363             }
364 
365             CheckReentrancy();
366 
367             item = EnsureParentToken(item, skipParentCheck);
368 
369             JToken previous = (index == 0) ? null : children[index - 1];
370             // haven't inserted new token yet so next token is still at the inserting index
371             JToken next = (index == children.Count) ? null : children[index];
372 
373             ValidateToken(item, null);
374 
375             item.Parent = this;
376 
377             item.Previous = previous;
378             if (previous != null)
379             {
380                 previous.Next = item;
381             }
382 
383             item.Next = next;
384             if (next != null)
385             {
386                 next.Previous = item;
387             }
388 
389             children.Insert(index, item);
390 
391 #if !(DOTNET || PORTABLE40 || PORTABLE)
392             if (_listChanged != null)
393             {
394                 OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, index));
395             }
396 #endif
397 #if !(NET20 || NET35 || PORTABLE40)
398             if (_collectionChanged != null)
399             {
400                 OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
401             }
402 #endif
403         }
404 
RemoveItemAt(int index)405         internal virtual void RemoveItemAt(int index)
406         {
407             IList<JToken> children = ChildrenTokens;
408 
409             if (index < 0)
410             {
411                 throw new ArgumentOutOfRangeException(nameof(index), "Index is less than 0.");
412             }
413             if (index >= children.Count)
414             {
415                 throw new ArgumentOutOfRangeException(nameof(index), "Index is equal to or greater than Count.");
416             }
417 
418             CheckReentrancy();
419 
420             JToken item = children[index];
421             JToken previous = (index == 0) ? null : children[index - 1];
422             JToken next = (index == children.Count - 1) ? null : children[index + 1];
423 
424             if (previous != null)
425             {
426                 previous.Next = next;
427             }
428             if (next != null)
429             {
430                 next.Previous = previous;
431             }
432 
433             item.Parent = null;
434             item.Previous = null;
435             item.Next = null;
436 
437             children.RemoveAt(index);
438 
439 #if !(DOTNET || PORTABLE40 || PORTABLE)
440             if (_listChanged != null)
441             {
442                 OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
443             }
444 #endif
445 #if !(NET20 || NET35 || PORTABLE40)
446             if (_collectionChanged != null)
447             {
448                 OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
449             }
450 #endif
451         }
452 
RemoveItem(JToken item)453         internal virtual bool RemoveItem(JToken item)
454         {
455             int index = IndexOfItem(item);
456             if (index >= 0)
457             {
458                 RemoveItemAt(index);
459                 return true;
460             }
461 
462             return false;
463         }
464 
GetItem(int index)465         internal virtual JToken GetItem(int index)
466         {
467             return ChildrenTokens[index];
468         }
469 
SetItem(int index, JToken item)470         internal virtual void SetItem(int index, JToken item)
471         {
472             IList<JToken> children = ChildrenTokens;
473 
474             if (index < 0)
475             {
476                 throw new ArgumentOutOfRangeException(nameof(index), "Index is less than 0.");
477             }
478             if (index >= children.Count)
479             {
480                 throw new ArgumentOutOfRangeException(nameof(index), "Index is equal to or greater than Count.");
481             }
482 
483             JToken existing = children[index];
484 
485             if (IsTokenUnchanged(existing, item))
486             {
487                 return;
488             }
489 
490             CheckReentrancy();
491 
492             item = EnsureParentToken(item, false);
493 
494             ValidateToken(item, existing);
495 
496             JToken previous = (index == 0) ? null : children[index - 1];
497             JToken next = (index == children.Count - 1) ? null : children[index + 1];
498 
499             item.Parent = this;
500 
501             item.Previous = previous;
502             if (previous != null)
503             {
504                 previous.Next = item;
505             }
506 
507             item.Next = next;
508             if (next != null)
509             {
510                 next.Previous = item;
511             }
512 
513             children[index] = item;
514 
515             existing.Parent = null;
516             existing.Previous = null;
517             existing.Next = null;
518 
519 #if !(DOTNET || PORTABLE || PORTABLE40)
520             if (_listChanged != null)
521             {
522                 OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));
523             }
524 #endif
525 #if !(NET20 || NET35 || PORTABLE40)
526             if (_collectionChanged != null)
527             {
528                 OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, existing, index));
529             }
530 #endif
531         }
532 
ClearItems()533         internal virtual void ClearItems()
534         {
535             CheckReentrancy();
536 
537             IList<JToken> children = ChildrenTokens;
538 
539             foreach (JToken item in children)
540             {
541                 item.Parent = null;
542                 item.Previous = null;
543                 item.Next = null;
544             }
545 
546             children.Clear();
547 
548 #if !(DOTNET || PORTABLE40 || PORTABLE)
549             if (_listChanged != null)
550             {
551                 OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
552             }
553 #endif
554 #if !(NET20 || NET35 || PORTABLE40)
555             if (_collectionChanged != null)
556             {
557                 OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
558             }
559 #endif
560         }
561 
ReplaceItem(JToken existing, JToken replacement)562         internal virtual void ReplaceItem(JToken existing, JToken replacement)
563         {
564             if (existing == null || existing.Parent != this)
565             {
566                 return;
567             }
568 
569             int index = IndexOfItem(existing);
570             SetItem(index, replacement);
571         }
572 
ContainsItem(JToken item)573         internal virtual bool ContainsItem(JToken item)
574         {
575             return (IndexOfItem(item) != -1);
576         }
577 
CopyItemsTo(Array array, int arrayIndex)578         internal virtual void CopyItemsTo(Array array, int arrayIndex)
579         {
580             if (array == null)
581             {
582                 throw new ArgumentNullException(nameof(array));
583             }
584             if (arrayIndex < 0)
585             {
586                 throw new ArgumentOutOfRangeException(nameof(arrayIndex), "arrayIndex is less than 0.");
587             }
588             if (arrayIndex >= array.Length && arrayIndex != 0)
589             {
590                 throw new ArgumentException("arrayIndex is equal to or greater than the length of array.");
591             }
592             if (Count > array.Length - arrayIndex)
593             {
594                 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.");
595             }
596 
597             int index = 0;
598             foreach (JToken token in ChildrenTokens)
599             {
600                 array.SetValue(token, arrayIndex + index);
601                 index++;
602             }
603         }
604 
IsTokenUnchanged(JToken currentValue, JToken newValue)605         internal static bool IsTokenUnchanged(JToken currentValue, JToken newValue)
606         {
607             JValue v1 = currentValue as JValue;
608             if (v1 != null)
609             {
610                 // null will get turned into a JValue of type null
611                 if (v1.Type == JTokenType.Null && newValue == null)
612                 {
613                     return true;
614                 }
615 
616                 return v1.Equals(newValue);
617             }
618 
619             return false;
620         }
621 
ValidateToken(JToken o, JToken existing)622         internal virtual void ValidateToken(JToken o, JToken existing)
623         {
624             ValidationUtils.ArgumentNotNull(o, nameof(o));
625 
626             if (o.Type == JTokenType.Property)
627             {
628                 throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType()));
629             }
630         }
631 
632         /// <summary>
633         /// Adds the specified content as children of this <see cref="JToken"/>.
634         /// </summary>
635         /// <param name="content">The content to be added.</param>
Add(object content)636         public virtual void Add(object content)
637         {
638             AddInternal(ChildrenTokens.Count, content, false);
639         }
640 
AddAndSkipParentCheck(JToken token)641         internal void AddAndSkipParentCheck(JToken token)
642         {
643             AddInternal(ChildrenTokens.Count, token, true);
644         }
645 
646         /// <summary>
647         /// Adds the specified content as the first children of this <see cref="JToken"/>.
648         /// </summary>
649         /// <param name="content">The content to be added.</param>
AddFirst(object content)650         public void AddFirst(object content)
651         {
652             AddInternal(0, content, false);
653         }
654 
AddInternal(int index, object content, bool skipParentCheck)655         internal void AddInternal(int index, object content, bool skipParentCheck)
656         {
657             if (IsMultiContent(content))
658             {
659                 IEnumerable enumerable = (IEnumerable)content;
660 
661                 int multiIndex = index;
662                 foreach (object c in enumerable)
663                 {
664                     AddInternal(multiIndex, c, skipParentCheck);
665                     multiIndex++;
666                 }
667             }
668             else
669             {
670                 JToken item = CreateFromContent(content);
671 
672                 InsertItem(index, item, skipParentCheck);
673             }
674         }
675 
CreateFromContent(object content)676         internal static JToken CreateFromContent(object content)
677         {
678             JToken token = content as JToken;
679             if (token != null)
680             {
681                 return token;
682             }
683 
684             return new JValue(content);
685         }
686 
687         /// <summary>
688         /// Creates an <see cref="JsonWriter"/> that can be used to add tokens to the <see cref="JToken"/>.
689         /// </summary>
690         /// <returns>An <see cref="JsonWriter"/> that is ready to have content written to it.</returns>
CreateWriter()691         public JsonWriter CreateWriter()
692         {
693             return new JTokenWriter(this);
694         }
695 
696         /// <summary>
697         /// Replaces the children nodes of this token with the specified content.
698         /// </summary>
699         /// <param name="content">The content.</param>
ReplaceAll(object content)700         public void ReplaceAll(object content)
701         {
702             ClearItems();
703             Add(content);
704         }
705 
706         /// <summary>
707         /// Removes the child nodes from this token.
708         /// </summary>
RemoveAll()709         public void RemoveAll()
710         {
711             ClearItems();
712         }
713 
MergeItem(object content, JsonMergeSettings settings)714         internal abstract void MergeItem(object content, JsonMergeSettings settings);
715 
716         /// <summary>
717         /// Merge the specified content into this <see cref="JToken"/>.
718         /// </summary>
719         /// <param name="content">The content to be merged.</param>
Merge(object content)720         public void Merge(object content)
721         {
722             MergeItem(content, new JsonMergeSettings());
723         }
724 
725         /// <summary>
726         /// Merge the specified content into this <see cref="JToken"/> using <see cref="JsonMergeSettings"/>.
727         /// </summary>
728         /// <param name="content">The content to be merged.</param>
729         /// <param name="settings">The <see cref="JsonMergeSettings"/> used to merge the content.</param>
Merge(object content, JsonMergeSettings settings)730         public void Merge(object content, JsonMergeSettings settings)
731         {
732             MergeItem(content, settings);
733         }
734 
ReadTokenFrom(JsonReader reader, JsonLoadSettings options)735         internal void ReadTokenFrom(JsonReader reader, JsonLoadSettings options)
736         {
737             int startDepth = reader.Depth;
738 
739             if (!reader.Read())
740             {
741                 throw JsonReaderException.Create(reader, "Error reading {0} from JsonReader.".FormatWith(CultureInfo.InvariantCulture, GetType().Name));
742             }
743 
744             ReadContentFrom(reader, options);
745 
746             int endDepth = reader.Depth;
747 
748             if (endDepth > startDepth)
749             {
750                 throw JsonReaderException.Create(reader, "Unexpected end of content while loading {0}.".FormatWith(CultureInfo.InvariantCulture, GetType().Name));
751             }
752         }
753 
ReadContentFrom(JsonReader r, JsonLoadSettings settings)754         internal void ReadContentFrom(JsonReader r, JsonLoadSettings settings)
755         {
756             ValidationUtils.ArgumentNotNull(r, nameof(r));
757             IJsonLineInfo lineInfo = r as IJsonLineInfo;
758 
759             JContainer parent = this;
760 
761             do
762             {
763                 if ((parent as JProperty)?.Value != null)
764                 {
765                     if (parent == this)
766                     {
767                         return;
768                     }
769 
770                     parent = parent.Parent;
771                 }
772 
773                 switch (r.TokenType)
774                 {
775                     case JsonToken.None:
776                         // new reader. move to actual content
777                         break;
778                     case JsonToken.StartArray:
779                         JArray a = new JArray();
780                         a.SetLineInfo(lineInfo, settings);
781                         parent.Add(a);
782                         parent = a;
783                         break;
784 
785                     case JsonToken.EndArray:
786                         if (parent == this)
787                         {
788                             return;
789                         }
790 
791                         parent = parent.Parent;
792                         break;
793                     case JsonToken.StartObject:
794                         JObject o = new JObject();
795                         o.SetLineInfo(lineInfo, settings);
796                         parent.Add(o);
797                         parent = o;
798                         break;
799                     case JsonToken.EndObject:
800                         if (parent == this)
801                         {
802                             return;
803                         }
804 
805                         parent = parent.Parent;
806                         break;
807                     case JsonToken.StartConstructor:
808                         JConstructor constructor = new JConstructor(r.Value.ToString());
809                         constructor.SetLineInfo(lineInfo, settings);
810                         parent.Add(constructor);
811                         parent = constructor;
812                         break;
813                     case JsonToken.EndConstructor:
814                         if (parent == this)
815                         {
816                             return;
817                         }
818 
819                         parent = parent.Parent;
820                         break;
821                     case JsonToken.String:
822                     case JsonToken.Integer:
823                     case JsonToken.Float:
824                     case JsonToken.Date:
825                     case JsonToken.Boolean:
826                     case JsonToken.Bytes:
827                         JValue v = new JValue(r.Value);
828                         v.SetLineInfo(lineInfo, settings);
829                         parent.Add(v);
830                         break;
831                     case JsonToken.Comment:
832                         if (settings != null && settings.CommentHandling == CommentHandling.Load)
833                         {
834                             v = JValue.CreateComment(r.Value.ToString());
835                             v.SetLineInfo(lineInfo, settings);
836                             parent.Add(v);
837                         }
838                         break;
839                     case JsonToken.Null:
840                         v = JValue.CreateNull();
841                         v.SetLineInfo(lineInfo, settings);
842                         parent.Add(v);
843                         break;
844                     case JsonToken.Undefined:
845                         v = JValue.CreateUndefined();
846                         v.SetLineInfo(lineInfo, settings);
847                         parent.Add(v);
848                         break;
849                     case JsonToken.PropertyName:
850                         string propertyName = r.Value.ToString();
851                         JProperty property = new JProperty(propertyName);
852                         property.SetLineInfo(lineInfo, settings);
853                         JObject parentObject = (JObject)parent;
854                         // handle multiple properties with the same name in JSON
855                         JProperty existingPropertyWithName = parentObject.Property(propertyName);
856                         if (existingPropertyWithName == null)
857                         {
858                             parent.Add(property);
859                         }
860                         else
861                         {
862                             existingPropertyWithName.Replace(property);
863                         }
864                         parent = property;
865                         break;
866                     default:
867                         throw new InvalidOperationException("The JsonReader should not be on a token of type {0}.".FormatWith(CultureInfo.InvariantCulture, r.TokenType));
868                 }
869             } while (r.Read());
870         }
871 
ContentsHashCode()872         internal int ContentsHashCode()
873         {
874             int hashCode = 0;
875             foreach (JToken item in ChildrenTokens)
876             {
877                 hashCode ^= item.GetDeepHashCode();
878             }
879             return hashCode;
880         }
881 
882 #if !(DOTNET || PORTABLE40 || PORTABLE)
ITypedList.GetListName(PropertyDescriptor[] listAccessors)883         string ITypedList.GetListName(PropertyDescriptor[] listAccessors)
884         {
885             return string.Empty;
886         }
887 
ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)888         PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
889         {
890             ICustomTypeDescriptor d = First as ICustomTypeDescriptor;
891             return d?.GetProperties();
892         }
893 #endif
894 
895         #region IList<JToken> Members
IndexOf(JToken item)896         int IList<JToken>.IndexOf(JToken item)
897         {
898             return IndexOfItem(item);
899         }
900 
Insert(int index, JToken item)901         void IList<JToken>.Insert(int index, JToken item)
902         {
903             InsertItem(index, item, false);
904         }
905 
RemoveAt(int index)906         void IList<JToken>.RemoveAt(int index)
907         {
908             RemoveItemAt(index);
909         }
910 
911         JToken IList<JToken>.this[int index]
912         {
913             get { return GetItem(index); }
914             set { SetItem(index, value); }
915         }
916         #endregion
917 
918         #region ICollection<JToken> Members
Add(JToken item)919         void ICollection<JToken>.Add(JToken item)
920         {
921             Add(item);
922         }
923 
Clear()924         void ICollection<JToken>.Clear()
925         {
926             ClearItems();
927         }
928 
Contains(JToken item)929         bool ICollection<JToken>.Contains(JToken item)
930         {
931             return ContainsItem(item);
932         }
933 
CopyTo(JToken[] array, int arrayIndex)934         void ICollection<JToken>.CopyTo(JToken[] array, int arrayIndex)
935         {
936             CopyItemsTo(array, arrayIndex);
937         }
938 
939         bool ICollection<JToken>.IsReadOnly
940         {
941             get { return false; }
942         }
943 
Remove(JToken item)944         bool ICollection<JToken>.Remove(JToken item)
945         {
946             return RemoveItem(item);
947         }
948         #endregion
949 
EnsureValue(object value)950         private JToken EnsureValue(object value)
951         {
952             if (value == null)
953             {
954                 return null;
955             }
956 
957             JToken token = value as JToken;
958             if (token != null)
959             {
960                 return token;
961             }
962 
963             throw new ArgumentException("Argument is not a JToken.");
964         }
965 
966         #region IList Members
IList.Add(object value)967         int IList.Add(object value)
968         {
969             Add(EnsureValue(value));
970             return Count - 1;
971         }
972 
IList.Clear()973         void IList.Clear()
974         {
975             ClearItems();
976         }
977 
IList.Contains(object value)978         bool IList.Contains(object value)
979         {
980             return ContainsItem(EnsureValue(value));
981         }
982 
IList.IndexOf(object value)983         int IList.IndexOf(object value)
984         {
985             return IndexOfItem(EnsureValue(value));
986         }
987 
IList.Insert(int index, object value)988         void IList.Insert(int index, object value)
989         {
990             InsertItem(index, EnsureValue(value), false);
991         }
992 
993         bool IList.IsFixedSize
994         {
995             get { return false; }
996         }
997 
998         bool IList.IsReadOnly
999         {
1000             get { return false; }
1001         }
1002 
IList.Remove(object value)1003         void IList.Remove(object value)
1004         {
1005             RemoveItem(EnsureValue(value));
1006         }
1007 
IList.RemoveAt(int index)1008         void IList.RemoveAt(int index)
1009         {
1010             RemoveItemAt(index);
1011         }
1012 
1013         object IList.this[int index]
1014         {
1015             get { return GetItem(index); }
1016             set { SetItem(index, EnsureValue(value)); }
1017         }
1018         #endregion
1019 
1020         #region ICollection Members
ICollection.CopyTo(Array array, int index)1021         void ICollection.CopyTo(Array array, int index)
1022         {
1023             CopyItemsTo(array, index);
1024         }
1025 
1026         /// <summary>
1027         /// Gets the count of child JSON tokens.
1028         /// </summary>
1029         /// <value>The count of child JSON tokens</value>
1030         public int Count
1031         {
1032             get { return ChildrenTokens.Count; }
1033         }
1034 
1035         bool ICollection.IsSynchronized
1036         {
1037             get { return false; }
1038         }
1039 
1040         object ICollection.SyncRoot
1041         {
1042             get
1043             {
1044                 if (_syncRoot == null)
1045                 {
1046                     Interlocked.CompareExchange(ref _syncRoot, new object(), null);
1047                 }
1048 
1049                 return _syncRoot;
1050             }
1051         }
1052         #endregion
1053 
1054         #region IBindingList Members
1055 #if !(DOTNET || PORTABLE || PORTABLE40)
IBindingList.AddIndex(PropertyDescriptor property)1056         void IBindingList.AddIndex(PropertyDescriptor property)
1057         {
1058         }
1059 
IBindingList.AddNew()1060         object IBindingList.AddNew()
1061         {
1062             AddingNewEventArgs args = new AddingNewEventArgs();
1063             OnAddingNew(args);
1064 
1065             if (args.NewObject == null)
1066             {
1067                 throw new JsonException("Could not determine new value to add to '{0}'.".FormatWith(CultureInfo.InvariantCulture, GetType()));
1068             }
1069 
1070             if (!(args.NewObject is JToken))
1071             {
1072                 throw new JsonException("New item to be added to collection must be compatible with {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JToken)));
1073             }
1074 
1075             JToken newItem = (JToken)args.NewObject;
1076             Add(newItem);
1077 
1078             return newItem;
1079         }
1080 
1081         bool IBindingList.AllowEdit
1082         {
1083             get { return true; }
1084         }
1085 
1086         bool IBindingList.AllowNew
1087         {
1088             get { return true; }
1089         }
1090 
1091         bool IBindingList.AllowRemove
1092         {
1093             get { return true; }
1094         }
1095 
IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)1096         void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)
1097         {
1098             throw new NotSupportedException();
1099         }
1100 
IBindingList.Find(PropertyDescriptor property, object key)1101         int IBindingList.Find(PropertyDescriptor property, object key)
1102         {
1103             throw new NotSupportedException();
1104         }
1105 
1106         bool IBindingList.IsSorted
1107         {
1108             get { return false; }
1109         }
1110 
IBindingList.RemoveIndex(PropertyDescriptor property)1111         void IBindingList.RemoveIndex(PropertyDescriptor property)
1112         {
1113         }
1114 
IBindingList.RemoveSort()1115         void IBindingList.RemoveSort()
1116         {
1117             throw new NotSupportedException();
1118         }
1119 
1120         ListSortDirection IBindingList.SortDirection
1121         {
1122             get { return ListSortDirection.Ascending; }
1123         }
1124 
1125         PropertyDescriptor IBindingList.SortProperty
1126         {
1127             get { return null; }
1128         }
1129 
1130         bool IBindingList.SupportsChangeNotification
1131         {
1132             get { return true; }
1133         }
1134 
1135         bool IBindingList.SupportsSearching
1136         {
1137             get { return false; }
1138         }
1139 
1140         bool IBindingList.SupportsSorting
1141         {
1142             get { return false; }
1143         }
1144 #endif
1145         #endregion
1146 
MergeEnumerableContent(JContainer target, IEnumerable content, JsonMergeSettings settings)1147         internal static void MergeEnumerableContent(JContainer target, IEnumerable content, JsonMergeSettings settings)
1148         {
1149             switch (settings.MergeArrayHandling)
1150             {
1151                 case MergeArrayHandling.Concat:
1152                     foreach (JToken item in content)
1153                     {
1154                         target.Add(item);
1155                     }
1156                     break;
1157                 case MergeArrayHandling.Union:
1158 #if !NET20
1159                     HashSet<JToken> items = new HashSet<JToken>(target, EqualityComparer);
1160 
1161                     foreach (JToken item in content)
1162                     {
1163                         if (items.Add(item))
1164                         {
1165                             target.Add(item);
1166                         }
1167                     }
1168 #else
1169                     Dictionary<JToken, bool> items = new Dictionary<JToken, bool>(EqualityComparer);
1170                     foreach (JToken t in target)
1171                     {
1172                         items[t] = true;
1173                     }
1174 
1175                     foreach (JToken item in content)
1176                     {
1177                         if (!items.ContainsKey(item))
1178                         {
1179                             items[item] = true;
1180                             target.Add(item);
1181                         }
1182                     }
1183 #endif
1184                     break;
1185                 case MergeArrayHandling.Replace:
1186                     target.ClearItems();
1187                     foreach (JToken item in content)
1188                     {
1189                         target.Add(item);
1190                     }
1191                     break;
1192                 case MergeArrayHandling.Merge:
1193                     int i = 0;
1194                     foreach (object targetItem in content)
1195                     {
1196                         if (i < target.Count)
1197                         {
1198                             JToken sourceItem = target[i];
1199 
1200                             JContainer existingContainer = sourceItem as JContainer;
1201                             if (existingContainer != null)
1202                             {
1203                                 existingContainer.Merge(targetItem, settings);
1204                             }
1205                             else
1206                             {
1207                                 if (targetItem != null)
1208                                 {
1209                                     JToken contentValue = CreateFromContent(targetItem);
1210                                     if (contentValue.Type != JTokenType.Null)
1211                                     {
1212                                         target[i] = contentValue;
1213                                     }
1214                                 }
1215                             }
1216                         }
1217                         else
1218                         {
1219                             target.Add(targetItem);
1220                         }
1221 
1222                         i++;
1223                     }
1224                     break;
1225                 default:
1226                     throw new ArgumentOutOfRangeException(nameof(settings), "Unexpected merge array handling when merging JSON.");
1227             }
1228         }
1229     }
1230 }