Json.NET
Code Coverage Statistics for Source File

Newtonsoft.Json\Linq\JContainer.cs

Symbol Coverage: 96.77% (390 of 403)

Branch Coverage: 92.92% (223 of 240)

Cyclomatic Complexity Avg: 2.03 Max:21

Code Lines: 376


L V Source
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
using System.Linq;
29
using System.Threading;
30
using Newtonsoft.Json.Utilities;
31
using System.Collections;
32
using System.Diagnostics;
33
using System.Globalization;
34
using System.ComponentModel;
35
using System.Collections.Specialized;
36
#if !SILVERLIGHT
37
using Newtonsoft.Json.Linq.ComponentModel;
38
#endif
39

  
40
namespace Newtonsoft.Json.Linq
41
{
42
  /// <summary>
43
  /// Represents a token that can contain other tokens.
44
  /// </summary>
45
  public abstract class JContainer : JToken, IList<JToken>
46
#if !SILVERLIGHT
47
    , ITypedList, IBindingList
48
#else
49
    , IList, INotifyCollectionChanged
50
#endif
51
  {
52
#if !SILVERLIGHT
53
    /// <summary>
54
    /// Occurs when the list changes or an item in the list changes.
55
    /// </summary>
56
    public event ListChangedEventHandler ListChanged;
57

  
58
    /// <summary>
59
    /// Occurs before an item is added to the collection.
60
    /// </summary>
61
    public event AddingNewEventHandler AddingNew;
62
#else
63
    /// <summary>
64
    /// Occurs when the items list of the collection has changed, or the collection is reset.
65
    /// </summary>
66
    public event NotifyCollectionChangedEventHandler CollectionChanged;
67
#endif
68

  
69
    private JToken _content;
70
    private object _syncRoot;
71
    private bool _busy;
72

  
73
    internal JToken Content
74
    {
75
 4740
      get { return _content; }
76
 706
      set { _content = value; }
77
    }
78

  
79
 1196
    internal JContainer()
80
    {
81
 1196
    }
82

  
83
 12
    internal JContainer(JContainer other)
84
    {
85
 12
      ValidationUtils.ArgumentNotNull(other, "c");
86

  
87
 12
      JToken content = other.Last;
88
 12
      if (content != null)
89
      {
90
        do
91
        {
92
 16
          content = content._next;
93
 16
          Add(content.CloneToken());
94
        }
95
 16
        while (content != other.Last);
96
      }
97
 12
    }
98

  
99
    internal void CheckReentrancy()
100
    {
101
 2058
      if (_busy)
102
 0
        throw new InvalidOperationException("Cannot change {0} during a collection change event.".FormatWith(CultureInfo.InvariantCulture, GetType()));
103
 2058
    }
104

  
105
 #if !SILVERLIGHT
106
    /// <summary>
107
    /// Raises the <see cref="AddingNew"/> event.
108
    /// </summary>
109
    /// <param name="e">The <see cref="AddingNewEventArgs"/> instance containing the event data.</param>
110
    protected virtual void OnAddingNew(AddingNewEventArgs e)
111
    {
112
 2
      AddingNewEventHandler handler = AddingNew;
113
 2
      if (handler != null)
114
 1
        handler(this, e);
115
 2
    }
116

  
117
    /// <summary>
118
    /// Raises the <see cref="ListChanged"/> event.
119
    /// </summary>
120
    /// <param name="e">The <see cref="ListChangedEventArgs"/> instance containing the event data.</param>
121
    protected virtual void OnListChanged(ListChangedEventArgs e)
122
    {
123
 68
      ListChangedEventHandler handler = ListChanged;
124

  
125
 68
      if (handler != null)
126
      {
127
 5
        _busy = true;
128
        try
129
        {
130
 5
          handler(this, e);
131
        }
132
        finally
133
        {
134
 5
          _busy = false;
135
        }
136
      }
137
 68
    }
138
#else
139
    /// <summary>
140
    /// Raises the <see cref="CollectionChanged"/> event.
141
    /// </summary>
142
    /// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
143
    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
144
    {
145
      NotifyCollectionChangedEventHandler handler = CollectionChanged;
146

  
147
      if (handler != null)
148
      {
149
        _busy = true;
150
        try
151
        {
152
          handler(this, e);
153
        }
154
        finally
155
        {
156
          _busy = false;
157
        }
158
      }
159
    }
160
#endif
161

  
162
    /// <summary>
163
    /// Gets a value indicating whether this token has childen tokens.
164
    /// </summary>
165
    /// <value>
166
    /// 	<c>true</c> if this token has child values; otherwise, <c>false</c>.
167
    /// </value>
168
    public override bool HasValues
169
    {
170
 1
      get { return (_content != null); }
171
    }
172

  
173
    internal bool ContentsEqual(JContainer container)
174
    {
175
 38
      JToken t1 = First;
176
 38
      JToken t2 = container.First;
177

  
178
 38
      if (t1 == t2)
179
 5
        return true;
180

  
181
      do
182
      {
183
 95
        if (t1 == null && t2 == null)
184
 33
          return true;
185

  
186
 62
        if (t1 != null && t2 != null && t1.DeepEquals(t2))
187
        {
188
 62
          t1 = (t1 != Last) ? t1.Next : null;
189
 62
          t2 = (t2 != container.Last) ? t2.Next : null;
190
        }
191
        else
192
        {
193
 0
          return false;
194
        }
195
      }
196
 62
      while (true);
197
 38
    }
198

  
199
    /// <summary>
200
    /// Get the first child token of this token.
201
    /// </summary>
202
    /// <value>
203
    /// A <see cref="JToken"/> containing the first child token of the <see cref="JToken"/>.
204
    /// </value>
205
    public override JToken First
206
    {
207
      get
208
      {
209
 6517
        if (Last == null)
210
 421
          return null;
211

  
212
 6096
        return Last._next;
213
 6517
      }
214
    }
215

  
216
    /// <summary>
217
    /// Get the last child token of this token.
218
    /// </summary>
219
    /// <value>
220
    /// A <see cref="JToken"/> containing the last child token of the <see cref="JToken"/>.
221
    /// </value>
222
    public override JToken Last
223
    {
224
      [DebuggerStepThrough]
225
 14532
      get { return _content; }
226
    }
227

  
228
    /// <summary>
229
    /// Returns a collection of the child tokens of this token, in document order.
230
    /// </summary>
231
    /// <returns>
232
    /// An <see cref="IEnumerable{T}"/> of <see cref="JToken"/> containing the child tokens of this <see cref="JToken"/>, in document order.
233
    /// </returns>
234
    public override JEnumerable<JToken> Children()
235
    {
236
 1999
      return new JEnumerable<JToken>(ChildrenInternal());
237
 1999
    }
238

  
239
    private IEnumerable<JToken> ChildrenInternal()
240
    {
241
 1994
      JToken first = First;
242
 1994
      JToken current = first;
243
 1994
      if (current == null)
244
 407
        yield break;
245

  
246
      do
247
      {
248
 4153
        yield return current;
249
      }
250
 3895
      while ((current = current.Next) != null);
251
    }
252

  
253
    /// <summary>
254
    /// Returns a collection of the child values of this token, in document order.
255
    /// </summary>
256
    /// <typeparam name="T">The type to convert the values to.</typeparam>
257
    /// <returns>
258
    /// A <see cref="IEnumerable{T}"/> containing the child values of this <see cref="JToken"/>, in document order.
259
    /// </returns>
260
    public override IEnumerable<T> Values<T>()
261
    {
262
 1
      return Children().Convert<JToken, T>();
263
 1
    }
264

  
265
    /// <summary>
266
    /// Returns a collection of the descendant tokens for this token in document order.
267
    /// </summary>
268
    /// <returns>An <see cref="IEnumerable{JToken}"/> containing the descendant tokens of the <see cref="JToken"/>.</returns>
269
    public IEnumerable<JToken> Descendants()
270
    {
271
 54
      foreach (JToken o in Children())
272
      {
273
 99
        yield return o;
274
 99
        JContainer c = o as JContainer;
275
 99
        if (c != null)
276
        {
277
 52
          foreach (JToken d in c.Descendants())
278
          {
279
 298
            yield return d;
280
          }
281
        }
282
      }
283
    }
284

  
285
    internal bool IsMultiContent(object content)
286
    {
287
 2111
      return (content is IEnumerable && !(content is string) && !(content is JToken) && !(content is byte[]));
288
 2111
    }
289

  
290
    internal virtual void AddItem(bool isLast, JToken previous, JToken item)
291
    {
292
 1272
      CheckReentrancy();
293

  
294
 1272
      ValidateToken(item, null);
295

  
296
 1264
      item = EnsureParentToken(item);
297

  
298
 1264
      JToken next = (previous != null) ? previous._next : item;
299

  
300
 1264
      item.Parent = this;
301
 1264
      item.Next = next;
302

  
303
 1264
      if (previous != null)
304
 814
        previous.Next = item;
305

  
306
 1264
      if (isLast || previous == null)
307
 1251
        _content = item;
308

  
309
#if !SILVERLIGHT
310
 1264
      if (ListChanged != null)
311
 1
        OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, IndexOfItem(item)));
312
#else
313
      if (CollectionChanged != null)
314
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, IndexOfItem(item)));
315
#endif
316
 1264
    }
317

  
318
    internal JToken EnsureParentToken(JToken item)
319
    {
320
 1996
      if (item.Parent != null)
321
      {
322
 4
        item = item.CloneToken();
323
      }
324
      else
325
      {
326
        // check whether attempting to add a token to itself
327
 1992
        JContainer parent = this;
328
 5187
        while (parent.Parent != null)
329
        {
330
 3195
          parent = parent.Parent;
331
        }
332
 1992
        if (item == parent)
333
        {
334
 1
          item = item.CloneToken();
335
        }
336
      }
337
 1996
      return item;
338
 1996
    }
339

  
340
    internal void AddInternal(bool isLast, JToken previous, object content)
341
    {
342
 1895
      if (IsMultiContent(content))
343
      {
344
 131
        IEnumerable enumerable = (IEnumerable) content;
345

  
346
 131
        JToken multiPrevious = previous;
347
 131
        foreach (object c in enumerable)
348
        {
349
 366
          AddInternal(isLast, multiPrevious, c);
350
 366
          multiPrevious = (multiPrevious != null) ? multiPrevious._next : Last;
351
        }
352
      }
353
      else
354
      {
355
 1764
        JToken item = CreateFromContent(content);
356

  
357
 1764
        AddItem(isLast, previous, item);
358
      }
359
 1885
    }
360

  
361
    internal int IndexOfItem(JToken item)
362
    {
363
 51
      int index = 0;
364
 51
      foreach (JToken token in Children())
365
      {
366
 73
        if (token == item)
367
 35
          return index;
368

  
369
 38
        index++;
370
      }
371

  
372
 16
      return -1;
373
 51
    }
374

  
375
    internal virtual void InsertItem(int index, JToken item)
376
    {
377
 8
      if (index == 0)
378
      {
379
 3
        AddFirst(item);
380
      }
381
      else
382
      {
383
 5
        JToken token = GetItem(index);
384
 3
        AddInternal(false, token.Previous, item);
385
      }
386
 6
    }
387

  
388
    internal virtual void RemoveItemAt(int index)
389
    {
390
 6
      if (index < 0)
391
 1
        throw new ArgumentOutOfRangeException("index", "index is less than 0.");
392

  
393
 5
      CheckReentrancy();
394

  
395
 5
      int currentIndex = 0;
396
 5
      foreach (JToken token in Children())
397
      {
398
 5
        if (index == currentIndex)
399
        {
400
 4
          token.Remove();
401

  
402
#if !SILVERLIGHT
403
 4
          OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
404
#else
405
          OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, token, index));
406
#endif
407

  
408
 4
          return;
409
        }
410

  
411
 1
        currentIndex++;
412
      }
413

  
414
 1
      throw new ArgumentOutOfRangeException("index", "index is equal to or greater than Count.");
415
 4
    }
416

  
417
    internal virtual bool RemoveItem(JToken item)
418
    {
419
 25
      if (item == null || item.Parent != this)
420
 7
        return false;
421

  
422
 18
      CheckReentrancy();
423

  
424
 18
      JToken content = _content;
425

  
426
 18
      int itemIndex = 0;
427
 21
      while (content._next != item)
428
      {
429
 3
        itemIndex++;
430
 3
        content = content._next;
431
      }
432
 18
      if (content == item)
433
      {
434
        // token is containers last child
435
 8
        _content = null;
436
      }
437
      else
438
      {
439
 10
        if (_content == item)
440
        {
441
 2
          _content = content;
442
        }
443
 10
        content._next = item._next;
444
      }
445
 18
      item.Parent = null;
446
 18
      item.Next = null;
447

  
448
#if !SILVERLIGHT
449
 18
      OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, itemIndex));
450
#else
451
      OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, itemIndex));
452
#endif
453

  
454
 18
      return true;
455
 25
    }
456

  
457
    internal virtual JToken GetItem(int index)
458
    {
459
 159
      return Children().ElementAt(index);
460
 157
    }
461

  
462
    internal virtual void SetItem(int index, JToken item)
463
    {
464
 11
      CheckReentrancy();
465

  
466
 11
      JToken token = GetItem(index);
467
 11
      token.Replace(item);
468

  
469
#if !SILVERLIGHT
470
 8
      OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));
471
#else
472
      OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, token, index));
473
#endif
474
 8
    }
475

  
476
    internal virtual void ClearItems()
477
    {
478
 6
      CheckReentrancy();
479

  
480
 17
      while (_content != null)
481
      {
482
 11
        JToken o = _content;
483

  
484
 11
        JToken next = o._next;
485
 11
        if (o != _content || next != o._next)
486
 0
          throw new InvalidOperationException("This operation was corrupted by external code.");
487

  
488
 11
        if (next != o)
489
 5
          o._next = next._next;
490
        else
491
 6
          _content = null;
492

  
493
 11
        next.Parent = null;
494
 11
        next._next = null;
495
      }
496

  
497
#if !SILVERLIGHT
498
 6
      OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
499
#else
500
      OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
501
#endif
502
 6
    }
503

  
504
    internal virtual void ReplaceItem(JToken existing, JToken replacement)
505
    {
506
 26
      if (existing == null || existing.Parent != this)
507
 0
        return;
508

  
509
 26
      if (IsTokenUnchanged(existing, replacement))
510
 0
        return;
511

  
512
 26
      CheckReentrancy();
513

  
514
 26
      replacement = EnsureParentToken(replacement);
515

  
516
 26
      ValidateToken(replacement, existing);
517

  
518
 23
      JToken content = _content;
519

  
520
 23
      int itemIndex = 0;
521
 29
      while (content._next != existing)
522
      {
523
 6
        itemIndex++;
524
 6
        content = content._next;
525
      }
526

  
527
 23
      if (content == existing)
528
      {
529
        // token is containers last child
530
 13
        _content = replacement;
531
 13
        replacement._next = replacement;
532
      }
533
      else
534
      {
535
 10
        if (_content == existing)
536
        {
537
 2
          _content = replacement;
538
        }
539
 10
        content._next = replacement;
540
 10
        replacement._next = existing._next;
541
      }
542

  
543
 23
      replacement.Parent = this;
544

  
545
 23
      existing.Parent = null;
546
 23
      existing.Next = null;
547

  
548
#if !SILVERLIGHT
549
 23
      OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, itemIndex));
550
#else
551
      OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, replacement, existing, itemIndex));
552
#endif
553
 23
    }
554

  
555
    internal virtual bool ContainsItem(JToken item)
556
    {
557
 20
      return (IndexOfItem(item) != -1);
558
 20
    }
559

  
560
    internal virtual void CopyItemsTo(Array array, int arrayIndex)
561
    {
562
 7
      if (array == null)
563
 1
        throw new ArgumentNullException("array");
564
 6
      if (arrayIndex < 0)
565
 1
        throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0.");
566
 5
      if (arrayIndex >= array.Length)
567
 1
        throw new ArgumentException("arrayIndex is equal to or greater than the length of array.");
568
 4
      if (CountItems() > array.Length - arrayIndex)
569
 1
        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.");
570

  
571
 3
      int index = 0;
572
 3
      foreach (JToken token in Children())
573
      {
574
 7
        array.SetValue(token, arrayIndex + index);
575
 7
        index++;
576
      }
577
 3
    }
578

  
579
    internal virtual int CountItems()
580
    {
581
 271
      return Children().Count();
582
 271
    }
583

  
584
    internal static bool IsTokenUnchanged(JToken currentValue, JToken newValue)
585
    {
586
 40
      JValue v1 = currentValue as JValue;
587
 40
      if (v1 != null)
588
      {
589
        // null will get turned into a JValue of type null
590
 28
        if (v1.Type == JTokenType.Null && newValue == null)
591
 0
          return true;
592

  
593
 28
        return v1.Equals(newValue);
594
      }
595

  
596
 12
      return false;
597
 40
    }
598

  
599
    internal virtual void ValidateToken(JToken o, JToken existing)
600
    {
601
 607
      ValidationUtils.ArgumentNotNull(o, "o");
602

  
603
 607
      if (o.Type == JTokenType.Property)
604
 1
        throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType()));
605
 606
    }
606

  
607
    /// <summary>
608
    /// Adds the specified content as children of this <see cref="JToken"/>.
609
    /// </summary>
610
    /// <param name="content">The content to be added.</param>
611
    public void Add(object content)
612
    {
613
 1512
      AddInternal(true, Last, content);
614
 1502
    }
615

  
616
    /// <summary>
617
    /// Adds the specified content as the first children of this <see cref="JToken"/>.
618
    /// </summary>
619
    /// <param name="content">The content to be added.</param>
620
    public void AddFirst(object content)
621
    {
622
 9
      AddInternal(false, Last, content);
623
 9
    }
624

  
625
    internal JToken CreateFromContent(object content)
626
    {
627
 1977
      if (content is JToken)
628
 1602
        return (JToken)content;
629
      
630
 375
      return new JValue(content);
631
 1977
    }
632

  
633
    /// <summary>
634
    /// Creates an <see cref="JsonWriter"/> that can be used to add tokens to the <see cref="JToken"/>.
635
    /// </summary>
636
    /// <returns>An <see cref="JsonWriter"/> that is ready to have content written to it.</returns>
637
    public JsonWriter CreateWriter()
638
    {
639
 1
      return new JTokenWriter(this);
640
 1
    }
641

  
642
    /// <summary>
643
    /// Replaces the children nodes of this token with the specified content.
644
    /// </summary>
645
    /// <param name="content">The content.</param>
646
    public void ReplaceAll(object content)
647
    {
648
 1
      ClearItems();
649
 1
      Add(content);
650
 1
    }
651

  
652
    /// <summary>
653
    /// Removes the child nodes from this token.
654
    /// </summary>
655
    public void RemoveAll()
656
    {
657
 2
      ClearItems();
658
 2
    }
659

  
660
    internal void ReadContentFrom(JsonReader r)
661
    {
662
 73
      ValidationUtils.ArgumentNotNull(r, "r");
663
 73
      IJsonLineInfo lineInfo = r as IJsonLineInfo;
664

  
665
 73
      JContainer parent = this;
666

  
667
      do
668
      {
669
 1025
        if (parent is JProperty && ((JProperty)parent).Value != null)
670
        {
671
 345
          if (parent == this)
672
 3
            return;
673

  
674
 342
          parent = parent.Parent;
675
        }
676

  
677
 1022
        switch (r.TokenType)
678
        {
679
          case JsonToken.None:
680
            // new reader. move to actual content
681
 0
            break;
682
          case JsonToken.StartArray:
683
 39
            JArray a = new JArray();
684
 39
            a.SetLineInfo(lineInfo);
685
 39
            parent.Add(a);
686
 39
            parent = a;
687
 39
            break;
688

  
689
          case JsonToken.EndArray:
690
 51
            if (parent == this)
691
 12
              return;
692

  
693
 39
            parent = parent.Parent;
694
 39
            break;
695
          case JsonToken.StartObject:
696
 61
            JObject o = new JObject();
697
 61
            o.SetLineInfo(lineInfo);
698
 61
            parent.Add(o);
699
 61
            parent = o;
700
 61
            break;
701
          case JsonToken.EndObject:
702
 115
            if (parent == this)
703
 54
              return;
704

  
705
 61
            parent = parent.Parent;
706
 61
            break;
707
          case JsonToken.StartConstructor:
708
 4
            JConstructor constructor = new JConstructor(r.Value.ToString());
709
 4
            constructor.SetLineInfo(constructor);
710
 4
            parent.Add(constructor);
711
 4
            parent = constructor;
712
 4
            break;
713
          case JsonToken.EndConstructor:
714
 6
            if (parent == this)
715
 2
              return;
716

  
717
 4
            parent = parent.Parent;
718
 4
            break;
719
          case JsonToken.String:
720
          case JsonToken.Integer:
721
          case JsonToken.Float:
722
          case JsonToken.Date:
723
          case JsonToken.Boolean:
724
          case JsonToken.Bytes:
725
 364
            JValue v = new JValue(r.Value);
726
 364
            v.SetLineInfo(lineInfo);
727
 364
            parent.Add(v);
728
 364
            break;
729
          case JsonToken.Comment:
730
 0
            v = JValue.CreateComment(r.Value.ToString());
731
 0
            v.SetLineInfo(lineInfo);
732
 0
            parent.Add(v);
733
 0
            break;
734
          case JsonToken.Null:
735
 37
            v = new JValue(null, JTokenType.Null);
736
 37
            v.SetLineInfo(lineInfo);
737
 37
            parent.Add(v);
738
 37
            break;
739
          case JsonToken.Undefined:
740
 1
            v = new JValue(null, JTokenType.Undefined);
741
 1
            v.SetLineInfo(lineInfo);
742
 1
            parent.Add(v);
743
 1
            break;
744
          case JsonToken.PropertyName:
745
 344
            string propertyName = r.Value.ToString();
746
 344
            JProperty property = new JProperty(propertyName);
747
 344
            property.SetLineInfo(lineInfo);
748
 344
            JObject parentObject = (JObject) parent;
749
            // handle multiple properties with the same name in JSON
750
 344
            JProperty existingPropertyWithName = parentObject.Property(propertyName);
751
 344
            if (existingPropertyWithName == null)
752
 343
              parent.Add(property);
753
            else
754
 1
              existingPropertyWithName.Replace(property);
755
 344
            parent = property;
756
 344
            break;
757
          default:
758
 0
            throw new InvalidOperationException("The JsonReader should not be on a token of type {0}.".FormatWith(CultureInfo.InvariantCulture, r.TokenType));
759
        }
760
      }
761
 954
      while (r.Read());
762
 72
    }
763

  
764
    internal int ContentsHashCode()
765
    {
766
 12
      int hashCode = 0;
767
 8
      foreach (JToken item in Children())
768
      {
769
 8
        hashCode ^= item.GetDeepHashCode();
770
      }
771
 12
      return hashCode;
772
 12
    }
773

  
774
#if !SILVERLIGHT
775
    string ITypedList.GetListName(PropertyDescriptor[] listAccessors)
776
    {
777
 1
      return string.Empty;
778
 1
    }
779

  
780
    PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
781
    {
782
 2
      JObject o = First as JObject;
783
 2
      if (o != null)
784
      {
785
        // explicitly use constructor because compact framework has no provider
786
 1
        JTypeDescriptor descriptor = new JTypeDescriptor(o);
787
 1
        return descriptor.GetProperties();
788
      }
789

  
790
 1
      return null;
791
 2
    }
792
#endif
793

  
794
    #region IList<JToken> Members
795

  
796
    int IList<JToken>.IndexOf(JToken item)
797
    {
798
 2
      return IndexOfItem(item);
799
 2
    }
800

  
801
    void IList<JToken>.Insert(int index, JToken item)
802
    {
803
 1
      InsertItem(index, item);
804
 1
    }
805

  
806
    void IList<JToken>.RemoveAt(int index)
807
    {
808
 1
      RemoveItemAt(index);
809
 1
    }
810

  
811
    JToken IList<JToken>.this[int index]
812
    {
813
 8
      get { return GetItem(index); }
814
 3
      set { SetItem(index, value); }
815
    }
816

  
817
    #endregion
818

  
819
    #region ICollection<JToken> Members
820

  
821
    void ICollection<JToken>.Add(JToken item)
822
    {
823
 5
      Add(item);
824
 1
    }
825

  
826
    void ICollection<JToken>.Clear()
827
    {
828
 1
      ClearItems();
829
 1
    }
830

  
831
    bool ICollection<JToken>.Contains(JToken item)
832
    {
833
 7
      return ContainsItem(item);
834
 7
    }
835

  
836
    void ICollection<JToken>.CopyTo(JToken[] array, int arrayIndex)
837
    {
838
 1
      CopyItemsTo(array, arrayIndex);
839
 1
    }
840

  
841
    int ICollection<JToken>.Count
842
    {
843
 9
      get { return CountItems(); }
844
    }
845

  
846
    bool ICollection<JToken>.IsReadOnly
847
    {
848
 1
      get { return false; }
849
    }
850

  
851
    bool ICollection<JToken>.Remove(JToken item)
852
    {
853
 5
      return RemoveItem(item);
854
 5
    }
855

  
856
    #endregion
857

  
858
    private JToken EnsureValue(object value)
859
    {
860
 23
      if (value == null)
861
 1
        return null;
862

  
863
 22
      if (value is JToken)
864
 21
        return (JToken) value;
865

  
866
 1
      throw new ArgumentException("Argument is not a JToken.");
867
 22
    }
868

  
869
    #region IList Members
870

  
871
    int IList.Add(object value)
872
    {
873
 5
      Add(EnsureValue(value));
874
 1
      return CountItems() - 1;
875
 1
    }
876

  
877
    void IList.Clear()
878
    {
879
 2
      ClearItems();
880
 1
    }
881

  
882
    bool IList.Contains(object value)
883
    {
884
 5
      return ContainsItem(EnsureValue(value));
885
 5
    }
886

  
887
    int IList.IndexOf(object value)
888
    {
889
 2
      return IndexOfItem(EnsureValue(value));
890
 2
    }
891

  
892
    void IList.Insert(int index, object value)
893
    {
894
 1
      InsertItem(index, EnsureValue(value));
895
 1
    }
896

  
897
    bool IList.IsFixedSize
898
    {
899
 1
      get { return false; }
900
    }
901

  
902
    bool IList.IsReadOnly
903
    {
904
 1
      get { return false; }
905
    }
906

  
907
    void IList.Remove(object value)
908
    {
909
 6
      RemoveItem(EnsureValue(value));
910
 5
    }
911

  
912
    void IList.RemoveAt(int index)
913
    {
914
 1
      RemoveItemAt(index);
915
 1
    }
916

  
917
    object IList.this[int index]
918
    {
919
 15
      get { return GetItem(index); }
920
 2
      set { SetItem(index, EnsureValue(value)); }
921
    }
922

  
923
    #endregion
924

  
925
    #region ICollection Members
926

  
927
    void ICollection.CopyTo(Array array, int index)
928
    {
929
 1
      CopyItemsTo(array, index);
930
 1
    }
931

  
932
    int ICollection.Count
933
    {
934
 211
      get { return CountItems(); }
935
    }
936

  
937
    bool ICollection.IsSynchronized
938
    {
939
 1
      get { return false; }
940
    }
941

  
942
    object ICollection.SyncRoot
943
    {
944
      get
945
      {
946
 1
        if (_syncRoot == null)
947
 1
          Interlocked.CompareExchange(ref _syncRoot, new object(), null);
948

  
949
 1
        return _syncRoot;
950
 1
      }
951

  
952
    }
953

  
954
    #endregion
955

  
956
    #region IBindingList Members
957

  
958
#if !SILVERLIGHT
959
    void IBindingList.AddIndex(PropertyDescriptor property)
960
    {
961
 1
    }
962

  
963
    object IBindingList.AddNew()
964
    {
965
 2
      AddingNewEventArgs args = new AddingNewEventArgs();
966
 2
      OnAddingNew(args);
967

  
968
 2
      if (args.NewObject == null)
969
 1
        throw new Exception("Could not determine new value to add to '{0}'.".FormatWith(CultureInfo.InvariantCulture, GetType()));
970

  
971
 1
      if (!(args.NewObject is JToken))
972
 0
        throw new Exception("New item to be added to collection must be compatible with {0}.".FormatWith(CultureInfo.InvariantCulture, typeof (JToken)));
973

  
974
 1
      JToken newItem = (JToken)args.NewObject;
975
 1
      Add(newItem);
976

  
977
 1
      return newItem;
978
 1
    }
979

  
980
    bool IBindingList.AllowEdit
981
    {
982
 1
      get { return true; }
983
    }
984

  
985
    bool IBindingList.AllowNew
986
    {
987
 1
      get { return true; }
988
    }
989

  
990
    bool IBindingList.AllowRemove
991
    {
992
 1
      get { return true; }
993
    }
994

  
995
    void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)
996
    {
997
 1
      throw new NotSupportedException();
998
    }
999

  
1000
    int IBindingList.Find(PropertyDescriptor property, object key)
1001
    {
1002
 1
      throw new NotSupportedException();
1003
    }
1004

  
1005
    bool IBindingList.IsSorted
1006
    {
1007
 1
      get { return false; }
1008
    }
1009

  
1010
    void IBindingList.RemoveIndex(PropertyDescriptor property)
1011
    {
1012
 1
    }
1013

  
1014
    void IBindingList.RemoveSort()
1015
    {
1016
 1
      throw new NotSupportedException();
1017
    }
1018

  
1019
    ListSortDirection IBindingList.SortDirection
1020
    {
1021
 1
      get { return ListSortDirection.Ascending; }
1022
    }
1023

  
1024
    PropertyDescriptor IBindingList.SortProperty
1025
    {
1026
 1
      get { return null; }
1027
    }
1028

  
1029
    bool IBindingList.SupportsChangeNotification
1030
    {
1031
 1
      get { return true; }
1032
    }
1033

  
1034
    bool IBindingList.SupportsSearching
1035
    {
1036
 1
      get { return false; }
1037
    }
1038

  
1039
    bool IBindingList.SupportsSorting
1040
    {
1041
 1
      get { return false; }
1042
    }
1043
#endif
1044

  
1045
    #endregion
1046
  }
1047
}