All files
[pithos-ms-client] / trunk / Libraries / Json40r2 / Source / Src / Newtonsoft.Json / Linq / JContainer.cs
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 #if !(SILVERLIGHT || NET20 || NET35)
52     , INotifyCollectionChanged
53 #endif
54   {
55 #if !SILVERLIGHT
56     /// <summary>
57     /// Occurs when the list changes or an item in the list changes.
58     /// </summary>
59     public event ListChangedEventHandler ListChanged;
60
61     /// <summary>
62     /// Occurs before an item is added to the collection.
63     /// </summary>
64     public event AddingNewEventHandler AddingNew;
65 #endif
66 #if SILVERLIGHT || !(NET20 || NET35)
67     /// <summary>
68     /// Occurs when the items list of the collection has changed, or the collection is reset.
69     /// </summary>
70     public event NotifyCollectionChangedEventHandler CollectionChanged;
71 #endif
72
73     private JToken _content;
74     private object _syncRoot;
75     private bool _busy;
76
77     internal JToken Content
78     {
79       get { return _content; }
80       set { _content = value; }
81     }
82
83     internal JContainer()
84     {
85     }
86
87     internal JContainer(JContainer other)
88     {
89       ValidationUtils.ArgumentNotNull(other, "c");
90
91       JToken content = other.Last;
92       if (content != null)
93       {
94         do
95         {
96           content = content._next;
97           Add(content.CloneToken());
98         }
99         while (content != other.Last);
100       }
101     }
102
103     internal void CheckReentrancy()
104     {
105       if (_busy)
106         throw new InvalidOperationException("Cannot change {0} during a collection change event.".FormatWith(CultureInfo.InvariantCulture, GetType()));
107     }
108
109  #if !SILVERLIGHT
110     /// <summary>
111     /// Raises the <see cref="AddingNew"/> event.
112     /// </summary>
113     /// <param name="e">The <see cref="AddingNewEventArgs"/> instance containing the event data.</param>
114     protected virtual void OnAddingNew(AddingNewEventArgs e)
115     {
116       AddingNewEventHandler handler = AddingNew;
117       if (handler != null)
118         handler(this, e);
119     }
120
121     /// <summary>
122     /// Raises the <see cref="ListChanged"/> event.
123     /// </summary>
124     /// <param name="e">The <see cref="ListChangedEventArgs"/> instance containing the event data.</param>
125     protected virtual void OnListChanged(ListChangedEventArgs e)
126     {
127       ListChangedEventHandler handler = ListChanged;
128
129       if (handler != null)
130       {
131         _busy = true;
132         try
133         {
134           handler(this, e);
135         }
136         finally
137         {
138           _busy = false;
139         }
140       }
141     }
142 #endif
143 #if SILVERLIGHT || !(NET20 || NET35)
144     /// <summary>
145     /// Raises the <see cref="CollectionChanged"/> event.
146     /// </summary>
147     /// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
148     protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
149     {
150       NotifyCollectionChangedEventHandler handler = CollectionChanged;
151
152       if (handler != null)
153       {
154         _busy = true;
155         try
156         {
157           handler(this, e);
158         }
159         finally
160         {
161           _busy = false;
162         }
163       }
164     }
165 #endif
166
167     /// <summary>
168     /// Gets a value indicating whether this token has childen tokens.
169     /// </summary>
170     /// <value>
171     ///         <c>true</c> if this token has child values; otherwise, <c>false</c>.
172     /// </value>
173     public override bool HasValues
174     {
175       get { return (_content != null); }
176     }
177
178     internal bool ContentsEqual(JContainer container)
179     {
180       JToken t1 = First;
181       JToken t2 = container.First;
182
183       if (t1 == t2)
184         return true;
185
186       do
187       {
188         if (t1 == null && t2 == null)
189           return true;
190
191         if (t1 != null && t2 != null && t1.DeepEquals(t2))
192         {
193           t1 = (t1 != Last) ? t1.Next : null;
194           t2 = (t2 != container.Last) ? t2.Next : null;
195         }
196         else
197         {
198           return false;
199         }
200       }
201       while (true);
202     }
203
204     /// <summary>
205     /// Get the first child token of this token.
206     /// </summary>
207     /// <value>
208     /// A <see cref="JToken"/> containing the first child token of the <see cref="JToken"/>.
209     /// </value>
210     public override JToken First
211     {
212       get
213       {
214         if (Last == null)
215           return null;
216
217         return Last._next;
218       }
219     }
220
221     /// <summary>
222     /// Get the last child token of this token.
223     /// </summary>
224     /// <value>
225     /// A <see cref="JToken"/> containing the last child token of the <see cref="JToken"/>.
226     /// </value>
227     public override JToken Last
228     {
229       [DebuggerStepThrough]
230       get { return _content; }
231     }
232
233     /// <summary>
234     /// Returns a collection of the child tokens of this token, in document order.
235     /// </summary>
236     /// <returns>
237     /// An <see cref="IEnumerable{T}"/> of <see cref="JToken"/> containing the child tokens of this <see cref="JToken"/>, in document order.
238     /// </returns>
239     public override JEnumerable<JToken> Children()
240     {
241       return new JEnumerable<JToken>(ChildrenInternal());
242     }
243
244     internal IEnumerable<JToken> ChildrenInternal()
245     {
246       JToken first = First;
247       JToken current = first;
248       if (current == null)
249         yield break;
250
251       do
252       {
253         yield return current;
254       }
255       while ((current = current.Next) != null);
256     }
257
258     /// <summary>
259     /// Returns a collection of the child values of this token, in document order.
260     /// </summary>
261     /// <typeparam name="T">The type to convert the values to.</typeparam>
262     /// <returns>
263     /// A <see cref="IEnumerable{T}"/> containing the child values of this <see cref="JToken"/>, in document order.
264     /// </returns>
265     public override IEnumerable<T> Values<T>()
266     {
267       return Children().Convert<JToken, T>();
268     }
269
270     /// <summary>
271     /// Returns a collection of the descendant tokens for this token in document order.
272     /// </summary>
273     /// <returns>An <see cref="IEnumerable{JToken}"/> containing the descendant tokens of the <see cref="JToken"/>.</returns>
274     public IEnumerable<JToken> Descendants()
275     {
276       foreach (JToken o in Children())
277       {
278         yield return o;
279         JContainer c = o as JContainer;
280         if (c != null)
281         {
282           foreach (JToken d in c.Descendants())
283           {
284             yield return d;
285           }
286         }
287       }
288     }
289
290     internal bool IsMultiContent(object content)
291     {
292       return (content is IEnumerable && !(content is string) && !(content is JToken) && !(content is byte[]));
293     }
294
295     internal virtual void AddItem(bool isLast, JToken previous, JToken item)
296     {
297       CheckReentrancy();
298
299       ValidateToken(item, null);
300
301       item = EnsureParentToken(item);
302
303       JToken next = (previous != null) ? previous._next : item;
304
305       item.Parent = this;
306       item.Next = next;
307
308       if (previous != null)
309         previous.Next = item;
310
311       if (isLast || previous == null)
312         _content = item;
313
314 #if !SILVERLIGHT
315       if (ListChanged != null)
316         OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, IndexOfItem(item)));
317 #endif
318 #if SILVERLIGHT || !(NET20 || NET35)
319       if (CollectionChanged != null)
320         OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, IndexOfItem(item)));
321 #endif
322     }
323
324     internal JToken EnsureParentToken(JToken item)
325     {
326       if (item.Parent != null)
327       {
328         item = item.CloneToken();
329       }
330       else
331       {
332         // check whether attempting to add a token to itself
333         JContainer parent = this;
334         while (parent.Parent != null)
335         {
336           parent = parent.Parent;
337         }
338         if (item == parent)
339         {
340           item = item.CloneToken();
341         }
342       }
343       return item;
344     }
345
346     internal void AddInternal(bool isLast, JToken previous, object content)
347     {
348       if (IsMultiContent(content))
349       {
350         IEnumerable enumerable = (IEnumerable) content;
351
352         JToken multiPrevious = previous;
353         foreach (object c in enumerable)
354         {
355           AddInternal(isLast, multiPrevious, c);
356           multiPrevious = (multiPrevious != null) ? multiPrevious._next : Last;
357         }
358       }
359       else
360       {
361         JToken item = CreateFromContent(content);
362
363         AddItem(isLast, previous, item);
364       }
365     }
366
367     internal int IndexOfItem(JToken item)
368     {
369       int index = 0;
370       foreach (JToken token in Children())
371       {
372         if (token == item)
373           return index;
374
375         index++;
376       }
377
378       return -1;
379     }
380
381     internal virtual void InsertItem(int index, JToken item)
382     {
383       if (index == 0)
384       {
385         AddFirst(item);
386       }
387       else
388       {
389         JToken token = GetItem(index);
390         AddInternal(false, token.Previous, item);
391       }
392     }
393
394     internal virtual void RemoveItemAt(int index)
395     {
396       if (index < 0)
397         throw new ArgumentOutOfRangeException("index", "index is less than 0.");
398
399       CheckReentrancy();
400
401       int currentIndex = 0;
402       foreach (JToken token in Children())
403       {
404         if (index == currentIndex)
405         {
406           token.Remove();
407
408 #if !SILVERLIGHT
409           OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
410 #endif
411 #if SILVERLIGHT || !(NET20 || NET35)
412           OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, token, index));
413 #endif
414
415           return;
416         }
417
418         currentIndex++;
419       }
420
421       throw new ArgumentOutOfRangeException("index", "index is equal to or greater than Count.");
422     }
423
424     internal virtual bool RemoveItem(JToken item)
425     {
426       if (item == null || item.Parent != this)
427         return false;
428
429       CheckReentrancy();
430
431       JToken content = _content;
432
433       int itemIndex = 0;
434       while (content._next != item)
435       {
436         itemIndex++;
437         content = content._next;
438       }
439       if (content == item)
440       {
441         // token is containers last child
442         _content = null;
443       }
444       else
445       {
446         if (_content == item)
447         {
448           _content = content;
449         }
450         content._next = item._next;
451       }
452       item.Parent = null;
453       item.Next = null;
454
455 #if !SILVERLIGHT
456       OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, itemIndex));
457 #endif
458 #if SILVERLIGHT || !(NET20 || NET35)
459       OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, itemIndex));
460 #endif
461
462       return true;
463     }
464
465     internal virtual JToken GetItem(int index)
466     {
467       return Children().ElementAt(index);
468     }
469
470     internal virtual void SetItem(int index, JToken item)
471     {
472       CheckReentrancy();
473
474       JToken token = GetItem(index);
475       token.Replace(item);
476
477 #if !SILVERLIGHT
478       OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));
479 #endif
480 #if SILVERLIGHT || !(NET20 || NET35)
481       OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, token, index));
482 #endif
483     }
484
485     internal virtual void ClearItems()
486     {
487       CheckReentrancy();
488
489       while (_content != null)
490       {
491         JToken o = _content;
492
493         JToken next = o._next;
494         if (o != _content || next != o._next)
495           throw new InvalidOperationException("This operation was corrupted by external code.");
496
497         if (next != o)
498           o._next = next._next;
499         else
500           _content = null;
501
502         next.Parent = null;
503         next._next = null;
504       }
505
506 #if !SILVERLIGHT
507       OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
508 #endif
509 #if SILVERLIGHT || !(NET20 || NET35)
510       OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
511 #endif
512     }
513
514     internal virtual void ReplaceItem(JToken existing, JToken replacement)
515     {
516       if (existing == null || existing.Parent != this)
517         return;
518
519       if (IsTokenUnchanged(existing, replacement))
520         return;
521
522       CheckReentrancy();
523
524       replacement = EnsureParentToken(replacement);
525
526       ValidateToken(replacement, existing);
527
528       JToken content = _content;
529
530       int itemIndex = 0;
531       while (content._next != existing)
532       {
533         itemIndex++;
534         content = content._next;
535       }
536
537       if (content == existing)
538       {
539         // token is containers last child
540         _content = replacement;
541         replacement._next = replacement;
542       }
543       else
544       {
545         if (_content == existing)
546         {
547           _content = replacement;
548         }
549         content._next = replacement;
550         replacement._next = existing._next;
551       }
552
553       replacement.Parent = this;
554
555       existing.Parent = null;
556       existing.Next = null;
557
558 #if !SILVERLIGHT
559       OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, itemIndex));
560 #endif
561 #if SILVERLIGHT || !(NET20 || NET35)
562       OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, replacement, existing, itemIndex));
563 #endif
564     }
565
566     internal virtual bool ContainsItem(JToken item)
567     {
568       return (IndexOfItem(item) != -1);
569     }
570
571     internal virtual void CopyItemsTo(Array array, int arrayIndex)
572     {
573       if (array == null)
574         throw new ArgumentNullException("array");
575       if (arrayIndex < 0)
576         throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0.");
577       if (arrayIndex >= array.Length)
578         throw new ArgumentException("arrayIndex is equal to or greater than the length of array.");
579       if (CountItems() > array.Length - arrayIndex)
580         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.");
581
582       int index = 0;
583       foreach (JToken token in Children())
584       {
585         array.SetValue(token, arrayIndex + index);
586         index++;
587       }
588     }
589
590     internal virtual int CountItems()
591     {
592       return Children().Count();
593     }
594
595     internal static bool IsTokenUnchanged(JToken currentValue, JToken newValue)
596     {
597       JValue v1 = currentValue as JValue;
598       if (v1 != null)
599       {
600         // null will get turned into a JValue of type null
601         if (v1.Type == JTokenType.Null && newValue == null)
602           return true;
603
604         return v1.Equals(newValue);
605       }
606
607       return false;
608     }
609
610     internal virtual void ValidateToken(JToken o, JToken existing)
611     {
612       ValidationUtils.ArgumentNotNull(o, "o");
613
614       if (o.Type == JTokenType.Property)
615         throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType()));
616     }
617
618     /// <summary>
619     /// Adds the specified content as children of this <see cref="JToken"/>.
620     /// </summary>
621     /// <param name="content">The content to be added.</param>
622     public void Add(object content)
623     {
624       AddInternal(true, Last, content);
625     }
626
627     /// <summary>
628     /// Adds the specified content as the first children of this <see cref="JToken"/>.
629     /// </summary>
630     /// <param name="content">The content to be added.</param>
631     public void AddFirst(object content)
632     {
633       AddInternal(false, Last, content);
634     }
635
636     internal JToken CreateFromContent(object content)
637     {
638       if (content is JToken)
639         return (JToken)content;
640       
641       return new JValue(content);
642     }
643
644     /// <summary>
645     /// Creates an <see cref="JsonWriter"/> that can be used to add tokens to the <see cref="JToken"/>.
646     /// </summary>
647     /// <returns>An <see cref="JsonWriter"/> that is ready to have content written to it.</returns>
648     public JsonWriter CreateWriter()
649     {
650       return new JTokenWriter(this);
651     }
652
653     /// <summary>
654     /// Replaces the children nodes of this token with the specified content.
655     /// </summary>
656     /// <param name="content">The content.</param>
657     public void ReplaceAll(object content)
658     {
659       ClearItems();
660       Add(content);
661     }
662
663     /// <summary>
664     /// Removes the child nodes from this token.
665     /// </summary>
666     public void RemoveAll()
667     {
668       ClearItems();
669     }
670
671     internal void ReadTokenFrom(JsonReader r)
672     {
673       int startDepth = r.Depth;
674
675       if (!r.Read())
676         throw new Exception("Error reading {0} from JsonReader.".FormatWith(CultureInfo.InvariantCulture, GetType().Name));
677
678       ReadContentFrom(r);
679
680       int endDepth = r.Depth;
681
682       if (endDepth > startDepth)
683         throw new Exception("Unexpected end of content while loading {0}.".FormatWith(CultureInfo.InvariantCulture, GetType().Name));
684     }
685
686     internal void ReadContentFrom(JsonReader r)
687     {
688       ValidationUtils.ArgumentNotNull(r, "r");
689       IJsonLineInfo lineInfo = r as IJsonLineInfo;
690
691       JContainer parent = this;
692
693       do
694       {
695         if (parent is JProperty && ((JProperty)parent).Value != null)
696         {
697           if (parent == this)
698             return;
699
700           parent = parent.Parent;
701         }
702
703         switch (r.TokenType)
704         {
705           case JsonToken.None:
706             // new reader. move to actual content
707             break;
708           case JsonToken.StartArray:
709             JArray a = new JArray();
710             a.SetLineInfo(lineInfo);
711             parent.Add(a);
712             parent = a;
713             break;
714
715           case JsonToken.EndArray:
716             if (parent == this)
717               return;
718
719             parent = parent.Parent;
720             break;
721           case JsonToken.StartObject:
722             JObject o = new JObject();
723             o.SetLineInfo(lineInfo);
724             parent.Add(o);
725             parent = o;
726             break;
727           case JsonToken.EndObject:
728             if (parent == this)
729               return;
730
731             parent = parent.Parent;
732             break;
733           case JsonToken.StartConstructor:
734             JConstructor constructor = new JConstructor(r.Value.ToString());
735             constructor.SetLineInfo(constructor);
736             parent.Add(constructor);
737             parent = constructor;
738             break;
739           case JsonToken.EndConstructor:
740             if (parent == this)
741               return;
742
743             parent = parent.Parent;
744             break;
745           case JsonToken.String:
746           case JsonToken.Integer:
747           case JsonToken.Float:
748           case JsonToken.Date:
749           case JsonToken.Boolean:
750           case JsonToken.Bytes:
751             JValue v = new JValue(r.Value);
752             v.SetLineInfo(lineInfo);
753             parent.Add(v);
754             break;
755           case JsonToken.Comment:
756             v = JValue.CreateComment(r.Value.ToString());
757             v.SetLineInfo(lineInfo);
758             parent.Add(v);
759             break;
760           case JsonToken.Null:
761             v = new JValue(null, JTokenType.Null);
762             v.SetLineInfo(lineInfo);
763             parent.Add(v);
764             break;
765           case JsonToken.Undefined:
766             v = new JValue(null, JTokenType.Undefined);
767             v.SetLineInfo(lineInfo);
768             parent.Add(v);
769             break;
770           case JsonToken.PropertyName:
771             string propertyName = r.Value.ToString();
772             JProperty property = new JProperty(propertyName);
773             property.SetLineInfo(lineInfo);
774             JObject parentObject = (JObject) parent;
775             // handle multiple properties with the same name in JSON
776             JProperty existingPropertyWithName = parentObject.Property(propertyName);
777             if (existingPropertyWithName == null)
778               parent.Add(property);
779             else
780               existingPropertyWithName.Replace(property);
781             parent = property;
782             break;
783           default:
784             throw new InvalidOperationException("The JsonReader should not be on a token of type {0}.".FormatWith(CultureInfo.InvariantCulture, r.TokenType));
785         }
786       }
787       while (r.Read());
788     }
789
790     internal int ContentsHashCode()
791     {
792       int hashCode = 0;
793       foreach (JToken item in Children())
794       {
795         hashCode ^= item.GetDeepHashCode();
796       }
797       return hashCode;
798     }
799
800 #if !SILVERLIGHT
801     string ITypedList.GetListName(PropertyDescriptor[] listAccessors)
802     {
803       return string.Empty;
804     }
805
806     PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
807     {
808       ICustomTypeDescriptor d = First as ICustomTypeDescriptor;
809       if (d != null)
810         return d.GetProperties();
811
812       return null;
813     }
814 #endif
815
816     #region IList<JToken> Members
817
818     int IList<JToken>.IndexOf(JToken item)
819     {
820       return IndexOfItem(item);
821     }
822
823     void IList<JToken>.Insert(int index, JToken item)
824     {
825       InsertItem(index, item);
826     }
827
828     void IList<JToken>.RemoveAt(int index)
829     {
830       RemoveItemAt(index);
831     }
832
833     JToken IList<JToken>.this[int index]
834     {
835       get { return GetItem(index); }
836       set { SetItem(index, value); }
837     }
838
839     #endregion
840
841     #region ICollection<JToken> Members
842
843     void ICollection<JToken>.Add(JToken item)
844     {
845       Add(item);
846     }
847
848     void ICollection<JToken>.Clear()
849     {
850       ClearItems();
851     }
852
853     bool ICollection<JToken>.Contains(JToken item)
854     {
855       return ContainsItem(item);
856     }
857
858     void ICollection<JToken>.CopyTo(JToken[] array, int arrayIndex)
859     {
860       CopyItemsTo(array, arrayIndex);
861     }
862
863     int ICollection<JToken>.Count
864     {
865       get { return CountItems(); }
866     }
867
868     bool ICollection<JToken>.IsReadOnly
869     {
870       get { return false; }
871     }
872
873     bool ICollection<JToken>.Remove(JToken item)
874     {
875       return RemoveItem(item);
876     }
877
878     #endregion
879
880     private JToken EnsureValue(object value)
881     {
882       if (value == null)
883         return null;
884
885       if (value is JToken)
886         return (JToken) value;
887
888       throw new ArgumentException("Argument is not a JToken.");
889     }
890
891     #region IList Members
892
893     int IList.Add(object value)
894     {
895       Add(EnsureValue(value));
896       return CountItems() - 1;
897     }
898
899     void IList.Clear()
900     {
901       ClearItems();
902     }
903
904     bool IList.Contains(object value)
905     {
906       return ContainsItem(EnsureValue(value));
907     }
908
909     int IList.IndexOf(object value)
910     {
911       return IndexOfItem(EnsureValue(value));
912     }
913
914     void IList.Insert(int index, object value)
915     {
916       InsertItem(index, EnsureValue(value));
917     }
918
919     bool IList.IsFixedSize
920     {
921       get { return false; }
922     }
923
924     bool IList.IsReadOnly
925     {
926       get { return false; }
927     }
928
929     void IList.Remove(object value)
930     {
931       RemoveItem(EnsureValue(value));
932     }
933
934     void IList.RemoveAt(int index)
935     {
936       RemoveItemAt(index);
937     }
938
939     object IList.this[int index]
940     {
941       get { return GetItem(index); }
942       set { SetItem(index, EnsureValue(value)); }
943     }
944
945     #endregion
946
947     #region ICollection Members
948
949     void ICollection.CopyTo(Array array, int index)
950     {
951       CopyItemsTo(array, index);
952     }
953
954     int ICollection.Count
955     {
956       get { return CountItems(); }
957     }
958
959     bool ICollection.IsSynchronized
960     {
961       get { return false; }
962     }
963
964     object ICollection.SyncRoot
965     {
966       get
967       {
968         if (_syncRoot == null)
969           Interlocked.CompareExchange(ref _syncRoot, new object(), null);
970
971         return _syncRoot;
972       }
973
974     }
975
976     #endregion
977
978     #region IBindingList Members
979
980 #if !SILVERLIGHT
981     void IBindingList.AddIndex(PropertyDescriptor property)
982     {
983     }
984
985     object IBindingList.AddNew()
986     {
987       AddingNewEventArgs args = new AddingNewEventArgs();
988       OnAddingNew(args);
989
990       if (args.NewObject == null)
991         throw new Exception("Could not determine new value to add to '{0}'.".FormatWith(CultureInfo.InvariantCulture, GetType()));
992
993       if (!(args.NewObject is JToken))
994         throw new Exception("New item to be added to collection must be compatible with {0}.".FormatWith(CultureInfo.InvariantCulture, typeof (JToken)));
995
996       JToken newItem = (JToken)args.NewObject;
997       Add(newItem);
998
999       return newItem;
1000     }
1001
1002     bool IBindingList.AllowEdit
1003     {
1004       get { return true; }
1005     }
1006
1007     bool IBindingList.AllowNew
1008     {
1009       get { return true; }
1010     }
1011
1012     bool IBindingList.AllowRemove
1013     {
1014       get { return true; }
1015     }
1016
1017     void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)
1018     {
1019       throw new NotSupportedException();
1020     }
1021
1022     int IBindingList.Find(PropertyDescriptor property, object key)
1023     {
1024       throw new NotSupportedException();
1025     }
1026
1027     bool IBindingList.IsSorted
1028     {
1029       get { return false; }
1030     }
1031
1032     void IBindingList.RemoveIndex(PropertyDescriptor property)
1033     {
1034     }
1035
1036     void IBindingList.RemoveSort()
1037     {
1038       throw new NotSupportedException();
1039     }
1040
1041     ListSortDirection IBindingList.SortDirection
1042     {
1043       get { return ListSortDirection.Ascending; }
1044     }
1045
1046     PropertyDescriptor IBindingList.SortProperty
1047     {
1048       get { return null; }
1049     }
1050
1051     bool IBindingList.SupportsChangeNotification
1052     {
1053       get { return true; }
1054     }
1055
1056     bool IBindingList.SupportsSearching
1057     {
1058       get { return false; }
1059     }
1060
1061     bool IBindingList.SupportsSorting
1062     {
1063       get { return false; }
1064     }
1065 #endif
1066
1067     #endregion
1068   }
1069 }