9e59868be4062505b8be517df69818fb738bdb46
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Interaction / Model / ModelItemDictionary.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="ModelItemDictionary.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Activities.Presentation.Model {
8
9     using System.Activities.Presentation.Internal.Properties;
10     using System.Activities.Presentation;
11     using System.Runtime;
12
13     using System;
14     using System.Collections;
15     using System.Collections.Generic;
16     using System.Collections.Specialized;
17     using System.Diagnostics.CodeAnalysis;
18     using System.Globalization;
19     using System.Windows;
20
21     /// <summary>
22     /// ModelItemDictionary derives from ModelItem and implements support for a 
23     /// dictionary of key/value pairs.  Both the keys and values are items. 
24     /// 
25     /// ModelItemDictionary defines an attached property "Key", which is adds 
26     /// to all items contained in the dictionary.  The data type of the Key 
27     /// property is "ModelItem" and it is marked as non-browsable and 
28     /// non-serializable.
29     ///
30     /// In addition to the Key property, ModelItemDictionary also returns an 
31     /// Item property from its properties collection just like ModelItemCollection.  
32     /// ModelItemDictionary reuses the ModelProperty defined on ModelItemCollection.  
33     /// The value returned is an enumeration of the values in the dictionary.  
34     /// The Source property of all items in the dictionary refers to this Item 
35     /// property.
36     /// </summary>
37     public abstract class ModelItemDictionary : ModelItem, IDictionary<ModelItem, ModelItem>, IDictionary, INotifyCollectionChanged {
38
39         /// <summary>
40         /// Creates a new ModelItemDictionary.
41         /// </summary>
42         protected ModelItemDictionary() { }
43
44         /// <summary>
45         /// Returns the item at the given key.  Sets the item at the given 
46         /// key to the given value.  If there is no item for the given key 
47         /// this returns null because null is not a valid item.
48         /// </summary>
49         /// <param name="key"></param>
50         /// <returns></returns>
51         /// <exception cref="InvalidOperationException">if the dictionary is read only and you set a new value.</exception>
52         /// <exception cref="KeyNotFoundException">if the given key is not in the dictionary.</exception>
53         [SuppressMessage("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers")]
54         public abstract ModelItem this[ModelItem key] { get; set; }
55
56         /// <summary>
57         /// Returns the item at the given key.  Sets the item at the given 
58         /// key to the given value.  If there is no item for the given key 
59         /// this returns null because null is not a valid item.
60         /// </summary>
61         /// <param name="key"></param>
62         /// <returns></returns>
63         /// <exception cref="InvalidOperationException">if the dictionary is read only and you set a new value.</exception>
64         /// <exception cref="KeyNotFoundException">if the given key is not in the dictionary.</exception>
65         [SuppressMessage("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers")]
66         public abstract ModelItem this[object key] { get; set; }
67
68         /// <summary>
69         /// Returns the count of items in the dictionary.
70         /// </summary>
71         public abstract int Count { get; }
72
73         /// <summary>
74         /// Returns true if the dictionary is a fixed size.  
75         /// The default implementation returns true if the
76         /// dictionary is read only.
77         /// </summary>
78         protected virtual bool IsFixedSize {
79             get { return IsReadOnly; }
80         }
81
82         /// <summary>
83         /// Returns true if the dictionary cannot be modified.
84         /// </summary>
85         public abstract bool IsReadOnly { get; }
86
87         /// <summary>
88         /// Protected access to ICollection.IsSynchronized.
89         /// </summary>
90         protected virtual bool IsSynchronized {
91             get { return false; }
92         }
93
94         /// <summary>
95         /// Returns the keys of the collection.  The keys are guaranteed to be in 
96         /// the same order as the values.  The resulting collection is read-only.
97         /// </summary>
98         [Fx.Tag.KnownXamlExternalAttribute]
99         public abstract ICollection<ModelItem> Keys { get; }
100
101         /// <summary>
102         /// Protected access to the SyncRoot object used to synchronize
103         /// this collection.  The default value returns "this".
104         /// </summary>
105         protected virtual object SyncRoot {
106             get { return this; }
107         }
108
109         /// <summary>
110         /// Returns the values of the collection.  The values are guaranteed to be 
111         /// in the same order as the keys.  The resulting collection is read-only.
112         /// </summary>
113         [Fx.Tag.KnownXamlExternalAttribute]
114         public abstract ICollection<ModelItem> Values { get; }
115
116         /// <summary>
117         /// This event is raised when the contents of this collection change.
118         /// </summary>
119         public abstract event NotifyCollectionChangedEventHandler CollectionChanged;
120
121         /// <summary>
122         /// Adds the item to the dictionary under the given key. 
123         /// </summary>
124         /// <param name="key"></param>
125         /// <param name="value"></param>
126         /// <exception cref="InvalidOperationException">if the dictionary is read only.</exception>
127         public abstract void Add(ModelItem key, ModelItem value);
128
129         /// <summary>
130         /// Adds the value to the dictionary under the given key.  This
131         /// will wrap the key and value in an item.  It returns the item
132         /// representing the key.
133         /// </summary>
134         /// <param name="key"></param>
135         /// <param name="value"></param>
136         /// <returns>an item representing the key in the dictionary</returns>
137         /// <exception cref="InvalidOperationException">if the dictionary is read only.</exception>
138         public abstract ModelItem Add(object key, object value);
139
140         /// <summary>
141         /// Clears the contents of the dictionary.
142         /// </summary>
143         /// <exception cref="InvalidOperationException">if the dictionary is read only.</exception>
144         public abstract void Clear();
145
146         /// <summary>
147         /// Copies into the given array.
148         /// </summary>
149         /// <param name="array"></param>
150         /// <param name="arrayIndex"></param>
151         protected virtual void CopyTo(KeyValuePair<ModelItem, ModelItem>[] array, int arrayIndex) {
152             foreach (KeyValuePair<ModelItem, ModelItem> kv in this) {
153                 array[arrayIndex++] = kv;
154             }
155         }
156
157         /// <summary>
158         /// Returns true if the dictionary contains the given key value pair.
159         /// </summary>
160         protected virtual bool Contains(KeyValuePair<ModelItem, ModelItem> item) {
161             ModelItem value;
162             return TryGetValue(item.Key, out value) && value == item.Value;
163         }
164
165         /// <summary>
166         /// Returns true if the dictionary contains the given key.
167         /// </summary>
168         /// <param name="key"></param>
169         /// <returns></returns>
170         public abstract bool ContainsKey(ModelItem key);
171
172         /// <summary>
173         /// Returns true if the dictionary contains the given key.
174         /// </summary>
175         /// <param name="key"></param>
176         /// <returns></returns>
177         public abstract bool ContainsKey(object key);
178
179         //
180         // Helper method that verifies that objects can be upcast to
181         // the correct type.
182         //
183         private static ModelItem ConvertType(object value) {
184             try {
185                 return (ModelItem)value;
186             }
187             catch (InvalidCastException) {
188                 throw FxTrace.Exception.AsError(new ArgumentException(
189                     string.Format(CultureInfo.CurrentCulture,
190                         Resources.Error_ArgIncorrectType,
191                         "value", typeof(ModelItem).FullName)));
192             }
193         }
194
195         /// <summary>
196         /// Returns an enumerator for the items in the dictionary.
197         /// </summary>
198         /// <returns></returns>
199         [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
200         public abstract IEnumerator<KeyValuePair<ModelItem, ModelItem>> GetEnumerator();
201
202         /// <summary>
203         /// Removes the item from the dictionary.  This does nothing if the item 
204         /// does not exist in the collection.
205         /// </summary>
206         /// <param name="key"></param>
207         /// <exception cref="InvalidOperationException">if the dictionary is read only.</exception>
208         public abstract bool Remove(ModelItem key);
209
210         /// <summary>
211         /// Removes the item from the dictionary.  This does nothing if the item 
212         /// does not exist in the collection.
213         /// </summary>
214         /// <param name="key"></param>
215         /// <exception cref="InvalidOperationException">if the dictionary is read only.</exception>
216         public abstract bool Remove(object key);
217
218         /// <summary>
219         /// Retrieves the value for the given key, or returns false if the 
220         /// value can\92t be found.
221         /// </summary>
222         /// <param name="key"></param>
223         /// <param name="value"></param>
224         /// <returns></returns>
225         public abstract bool TryGetValue(ModelItem key, out ModelItem value);
226
227         /// <summary>
228         /// Retrieves the value for the given key, or returns false if the 
229         /// value can\92t be found.
230         /// </summary>
231         /// <param name="key"></param>
232         /// <param name="value"></param>
233         /// <returns></returns>
234         public abstract bool TryGetValue(object key, out ModelItem value);
235
236         /// <summary>
237         /// ModelItemDictionary provides an attached property "Key", which is adds to 
238         /// all items contained in the dictionary.  The data type of the Key 
239         /// property is "ModelItem".
240         /// </summary>
241         public static readonly DependencyProperty KeyProperty = DependencyProperty.RegisterAttachedReadOnly(
242             "Key",
243             typeof(ModelItem),
244             typeof(ModelItemDictionary), null).DependencyProperty;
245
246         // IDictionary API is synthesized on top of the generic API.
247
248         #region IDictionary Members
249
250         /// <summary>
251         /// IDictionary Implementation maps back to public API.
252         /// </summary>
253         void IDictionary.Add(object key, object value) {
254             Add(key, value);
255         }
256
257         /// <summary>
258         /// IDictionary Implementation maps back to public API.
259         /// </summary>
260         void IDictionary.Clear() {
261             Clear();
262         }
263
264         /// <summary>
265         /// IDictionary Implementation maps back to public API.
266         /// </summary>
267         [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")]
268         bool IDictionary.Contains(object key) {
269             return ContainsKey(key);
270         }
271
272         /// <summary>
273         /// IDictionary Implementation maps back to public API.
274         /// </summary>
275         IDictionaryEnumerator IDictionary.GetEnumerator() {
276             return new DictionaryEnumerator(GetEnumerator());
277         }
278
279         /// <summary>
280         /// IDictionary Implementation maps back to public API.
281         /// </summary>
282         bool IDictionary.IsFixedSize {
283             get { return IsFixedSize; }
284         }
285
286         /// <summary>
287         /// IDictionary Implementation maps back to public API.
288         /// </summary>
289         bool IDictionary.IsReadOnly {
290             get { return IsReadOnly; }
291         }
292
293         /// <summary>
294         /// IDictionary Implementation maps back to public API.
295         /// </summary>
296         ICollection IDictionary.Keys
297         {
298             get {
299                 object[] keys = new object[Count];
300                 int idx = 0;
301                 foreach (KeyValuePair<ModelItem, ModelItem> kv in this) {
302                     keys[idx++] = kv.Key;
303                 }
304                 return keys;
305             }
306         }
307
308         /// <summary>
309         /// IDictionary Implementation maps back to public API.
310         /// </summary>
311         void IDictionary.Remove(object key) {
312             Remove(key);
313         }
314
315         /// <summary>
316         /// IDictionary Implementation maps back to public API.
317         /// </summary>
318         ICollection IDictionary.Values
319         {
320             get {
321                 object[] values = new object[Count];
322                 int idx = 0;
323                 foreach (KeyValuePair<ModelItem, ModelItem> kv in this) {
324                     values[idx++] = kv.Value;
325                 }
326                 return values;
327             }
328         }
329
330         /// <summary>
331         /// IDictionary Implementation maps back to public API.
332         /// </summary>
333         object IDictionary.this[object key] {
334             get { return this[ConvertType(key)]; }
335             set { this[ConvertType(key)] = ConvertType(value); }
336         }
337
338         #endregion
339
340         #region ICollection Members
341
342         /// <summary>
343         /// ICollection Implementation maps back to public API.
344         /// </summary>
345         void ICollection.CopyTo(Array array, int index) {
346
347             if (Count > 0) {
348                 int len = array.GetLength(0);
349                 if (index >= len) {
350                     throw FxTrace.Exception.AsError(new ArgumentException(
351                         string.Format(CultureInfo.CurrentCulture,
352                         Resources.Error_InvalidArrayIndex, index)));
353                 }
354
355                 KeyValuePair<ModelItem, ModelItem>[] typedArray = new KeyValuePair<ModelItem, ModelItem>[len];
356
357                 CopyTo(typedArray, index);
358
359                 for (; index < typedArray.Length; index++) {
360                     array.SetValue(typedArray[index], index);
361                 }
362             }
363         }
364
365         /// <summary>
366         /// ICollection Implementation maps back to public API.
367         /// </summary>
368         int ICollection.Count {
369             get { return Count; }
370         }
371
372         /// <summary>
373         /// ICollection Implementation maps back to public API.
374         /// </summary>
375         bool ICollection.IsSynchronized {
376             get { return IsSynchronized; }
377         }
378
379         /// <summary>
380         /// ICollection Implementation maps back to public API.
381         /// </summary>
382         object ICollection.SyncRoot {
383             get { return SyncRoot; }
384         }
385
386         #endregion
387
388         #region IEnumerable Members
389
390         /// <summary>
391         /// IEnumerator Implementation maps back to public API.
392         /// </summary>
393         IEnumerator IEnumerable.GetEnumerator() {
394             foreach (KeyValuePair<ModelItem, ModelItem> kv in this) {
395                 yield return kv;
396             }
397         }
398
399         #endregion
400
401         #region ICollection<KeyValuePair<ModelItem,ModelItem>> Members
402
403         void ICollection<KeyValuePair<ModelItem, ModelItem>>.Add(KeyValuePair<ModelItem, ModelItem> item) {
404             Add(item.Key, item.Value);
405         }
406
407         bool ICollection<KeyValuePair<ModelItem, ModelItem>>.Contains(KeyValuePair<ModelItem, ModelItem> item) {
408             return Contains(item);
409         }
410
411         void ICollection<KeyValuePair<ModelItem, ModelItem>>.CopyTo(KeyValuePair<ModelItem, ModelItem>[] array, int arrayIndex) {
412             if (arrayIndex >= array.Length) {
413                 throw FxTrace.Exception.AsError(new ArgumentException(
414                     string.Format(CultureInfo.CurrentCulture,
415                     Resources.Error_InvalidArrayIndex, arrayIndex)));
416             }
417
418             CopyTo(array, arrayIndex);
419         }
420
421         bool ICollection<KeyValuePair<ModelItem, ModelItem>>.Remove(KeyValuePair<ModelItem, ModelItem> item) {
422             ModelItem value;
423             if (TryGetValue(item.Key, out value) && value == item.Value) {
424                 return Remove(item.Key);
425             }
426
427             return false;
428         }
429
430         #endregion
431
432         //
433         // This is a simple struct that implements a dictionary enumerator,
434         // since this isn't supported in the iterator pattern.
435         //
436         private struct DictionaryEnumerator : IDictionaryEnumerator {
437
438             private IEnumerator<KeyValuePair<ModelItem, ModelItem>> _real;
439
440             internal DictionaryEnumerator(IEnumerator<KeyValuePair<ModelItem, ModelItem>> real) {
441                 _real = real;
442             }
443
444             #region IDictionaryEnumerator Members
445
446             public DictionaryEntry Entry {
447                 get { return new DictionaryEntry(_real.Current.Key, _real.Current.Value); }
448             }
449
450             public object Key {
451                 get { return _real.Current.Key; }
452             }
453
454             public object Value {
455                 get { return _real.Current.Value; }
456             }
457
458             #endregion
459
460             #region IEnumerator Members
461
462             public object Current {
463                 get { return Entry; }
464             }
465
466             public bool MoveNext() {
467                 return _real.MoveNext();
468             }
469
470             public void Reset() {
471                 _real.Reset();
472             }
473
474             #endregion
475         }
476     }
477 }