1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 
9 [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2112:SecuredTypesShouldNotExposeFields", Scope = "type", Target = "System.ComponentModel.EventDescriptorCollection")]
10 
11 namespace System.ComponentModel
12 {
13     /// <summary>
14     ///    <para>
15     ///       Represents a collection of events.
16     ///    </para>
17     /// </summary>
18     public class EventDescriptorCollection : ICollection, IList
19     {
20         private EventDescriptor[] _events;
21         private string[] _namedSort;
22         private readonly IComparer _comparer;
23         private bool _eventsOwned;
24         private bool _needSort;
25         private readonly bool _readOnly;
26 
27         /// <summary>
28         /// An empty AttributeCollection that can used instead of creating a new one with no items.
29         /// </summary>
30         public static readonly EventDescriptorCollection Empty = new EventDescriptorCollection(null, true);
31 
32         /// <summary>
33         ///    <para>
34         ///       Initializes a new instance of the <see cref='System.ComponentModel.EventDescriptorCollection'/> class.
35         ///    </para>
36         /// </summary>
EventDescriptorCollection(EventDescriptor[] events)37         public EventDescriptorCollection(EventDescriptor[] events)
38         {
39             if (events == null)
40             {
41                 _events = Array.Empty<EventDescriptor>();
42             }
43             else
44             {
45                 _events = events;
46                 Count = events.Length;
47             }
48             _eventsOwned = true;
49         }
50 
51         /// <summary>
52         ///     Initializes a new instance of an event descriptor collection, and allows you to mark the
53         ///     collection as read-only so it cannot be modified.
54         /// </summary>
EventDescriptorCollection(EventDescriptor[] events, bool readOnly)55         public EventDescriptorCollection(EventDescriptor[] events, bool readOnly) : this(events)
56         {
57             _readOnly = readOnly;
58         }
59 
EventDescriptorCollection(EventDescriptor[] events, int eventCount, string[] namedSort, IComparer comparer)60         private EventDescriptorCollection(EventDescriptor[] events, int eventCount, string[] namedSort, IComparer comparer)
61         {
62             _eventsOwned = false;
63             if (namedSort != null)
64             {
65                 _namedSort = (string[])namedSort.Clone();
66             }
67             _comparer = comparer;
68             _events = events;
69             Count = eventCount;
70             _needSort = true;
71         }
72 
73         /// <summary>
74         ///    <para>
75         ///       Gets the number
76         ///       of event descriptors in the collection.
77         ///    </para>
78         /// </summary>
79         public int Count { get; private set; }
80 
81         /// <summary>
82         ///    <para>Gets the event with the specified index
83         ///       number.</para>
84         /// </summary>
85         public virtual EventDescriptor this[int index]
86         {
87             get
88             {
89                 if (index >= Count)
90                 {
91                     throw new IndexOutOfRangeException();
92                 }
93                 EnsureEventsOwned();
94                 return _events[index];
95             }
96         }
97 
98         /// <summary>
99         ///    <para>
100         ///       Gets the event with the specified name.
101         ///    </para>
102         /// </summary>
103         public virtual EventDescriptor this[string name] => Find(name, false);
104 
105         /// <summary>
106         ///    <para>[To be supplied.]</para>
107         /// </summary>
Add(EventDescriptor value)108         public int Add(EventDescriptor value)
109         {
110             if (_readOnly)
111             {
112                 throw new NotSupportedException();
113             }
114 
115             EnsureSize(Count + 1);
116             _events[Count++] = value;
117             return Count - 1;
118         }
119 
120         /// <summary>
121         ///    <para>[To be supplied.]</para>
122         /// </summary>
Clear()123         public void Clear()
124         {
125             if (_readOnly)
126             {
127                 throw new NotSupportedException();
128             }
129 
130             Count = 0;
131         }
132 
133         /// <summary>
134         ///    <para>[To be supplied.]</para>
135         /// </summary>
Contains(EventDescriptor value)136         public bool Contains(EventDescriptor value)
137         {
138             return IndexOf(value) >= 0;
139         }
140 
141         /// <internalonly/>
ICollection.CopyTo(Array array, int index)142         void ICollection.CopyTo(Array array, int index)
143         {
144             EnsureEventsOwned();
145             Array.Copy(_events, 0, array, index, Count);
146         }
147 
EnsureEventsOwned()148         private void EnsureEventsOwned()
149         {
150             if (!_eventsOwned)
151             {
152                 _eventsOwned = true;
153                 if (_events != null)
154                 {
155                     EventDescriptor[] newEvents = new EventDescriptor[Count];
156                     Array.Copy(_events, 0, newEvents, 0, Count);
157                     _events = newEvents;
158                 }
159             }
160 
161             if (_needSort)
162             {
163                 _needSort = false;
164                 InternalSort(_namedSort);
165             }
166         }
167 
EnsureSize(int sizeNeeded)168         private void EnsureSize(int sizeNeeded)
169         {
170             if (sizeNeeded <= _events.Length)
171             {
172                 return;
173             }
174 
175             if (_events.Length == 0)
176             {
177                 Count = 0;
178                 _events = new EventDescriptor[sizeNeeded];
179                 return;
180             }
181 
182             EnsureEventsOwned();
183 
184             int newSize = Math.Max(sizeNeeded, _events.Length * 2);
185             EventDescriptor[] newEvents = new EventDescriptor[newSize];
186             Array.Copy(_events, 0, newEvents, 0, Count);
187             _events = newEvents;
188         }
189 
190         /// <summary>
191         ///    <para>
192         ///       Gets the description of the event with the specified
193         ///       name
194         ///       in the collection.
195         ///    </para>
196         /// </summary>
Find(string name, bool ignoreCase)197         public virtual EventDescriptor Find(string name, bool ignoreCase)
198         {
199             EventDescriptor p = null;
200 
201             if (ignoreCase)
202             {
203                 for (int i = 0; i < Count; i++)
204                 {
205                     if (String.Equals(_events[i].Name, name, StringComparison.OrdinalIgnoreCase))
206                     {
207                         p = _events[i];
208                         break;
209                     }
210                 }
211             }
212             else
213             {
214                 for (int i = 0; i < Count; i++)
215                 {
216                     if (String.Equals(_events[i].Name, name, StringComparison.Ordinal))
217                     {
218                         p = _events[i];
219                         break;
220                     }
221                 }
222             }
223 
224             return p;
225         }
226 
227         /// <summary>
228         ///    <para>[To be supplied.]</para>
229         /// </summary>
IndexOf(EventDescriptor value)230         public int IndexOf(EventDescriptor value)
231         {
232             return Array.IndexOf(_events, value, 0, Count);
233         }
234 
235         /// <summary>
236         ///    <para>[To be supplied.]</para>
237         /// </summary>
Insert(int index, EventDescriptor value)238         public void Insert(int index, EventDescriptor value)
239         {
240             if (_readOnly)
241             {
242                 throw new NotSupportedException();
243             }
244 
245             EnsureSize(Count + 1);
246             if (index < Count)
247             {
248                 Array.Copy(_events, index, _events, index + 1, Count - index);
249             }
250             _events[index] = value;
251             Count++;
252         }
253 
254         /// <summary>
255         ///    <para>[To be supplied.]</para>
256         /// </summary>
Remove(EventDescriptor value)257         public void Remove(EventDescriptor value)
258         {
259             if (_readOnly)
260             {
261                 throw new NotSupportedException();
262             }
263 
264             int index = IndexOf(value);
265 
266             if (index != -1)
267             {
268                 RemoveAt(index);
269             }
270         }
271 
272         /// <summary>
273         ///    <para>[To be supplied.]</para>
274         /// </summary>
RemoveAt(int index)275         public void RemoveAt(int index)
276         {
277             if (_readOnly)
278             {
279                 throw new NotSupportedException();
280             }
281 
282             if (index < Count - 1)
283             {
284                 Array.Copy(_events, index + 1, _events, index, Count - index - 1);
285             }
286             _events[Count - 1] = null;
287             Count--;
288         }
289 
290         /// <summary>
291         ///    <para>
292         ///       Gets an enumerator for this <see cref='System.ComponentModel.EventDescriptorCollection'/>.
293         ///    </para>
294         /// </summary>
GetEnumerator()295         public IEnumerator GetEnumerator()
296         {
297             // we can only return an enumerator on the events we actually have...
298             if (_events.Length == Count)
299             {
300                 return _events.GetEnumerator();
301             }
302             else
303             {
304                 return new ArraySubsetEnumerator(_events, Count);
305             }
306         }
307 
308         /// <summary>
309         ///    <para>
310         ///       Sorts the members of this EventDescriptorCollection, using the default sort for this collection,
311         ///       which is usually alphabetical.
312         ///    </para>
313         /// </summary>
Sort()314         public virtual EventDescriptorCollection Sort()
315         {
316             return new EventDescriptorCollection(_events, Count, _namedSort, _comparer);
317         }
318 
319 
320         /// <summary>
321         ///    <para>
322         ///       Sorts the members of this EventDescriptorCollection.  Any specified NamedSort arguments will
323         ///       be applied first, followed by sort using the specified IComparer.
324         ///    </para>
325         /// </summary>
Sort(string[] names)326         public virtual EventDescriptorCollection Sort(string[] names)
327         {
328             return new EventDescriptorCollection(_events, Count, names, _comparer);
329         }
330 
331         /// <summary>
332         ///    <para>
333         ///       Sorts the members of this EventDescriptorCollection.  Any specified NamedSort arguments will
334         ///       be applied first, followed by sort using the specified IComparer.
335         ///    </para>
336         /// </summary>
Sort(string[] names, IComparer comparer)337         public virtual EventDescriptorCollection Sort(string[] names, IComparer comparer)
338         {
339             return new EventDescriptorCollection(_events, Count, names, comparer);
340         }
341 
342         /// <summary>
343         ///    <para>
344         ///       Sorts the members of this EventDescriptorCollection, using the specified IComparer to compare,
345         ///       the EventDescriptors contained in the collection.
346         ///    </para>
347         /// </summary>
Sort(IComparer comparer)348         public virtual EventDescriptorCollection Sort(IComparer comparer)
349         {
350             return new EventDescriptorCollection(_events, Count, _namedSort, comparer);
351         }
352 
353         /// <summary>
354         ///    <para>
355         ///       Sorts the members of this EventDescriptorCollection.  Any specified NamedSort arguments will
356         ///       be applied first, followed by sort using the specified IComparer.
357         ///    </para>
358         /// </summary>
InternalSort(string[] names)359         protected void InternalSort(string[] names)
360         {
361             if (_events.Length == 0)
362             {
363                 return;
364             }
365 
366             InternalSort(_comparer);
367 
368             if (names != null && names.Length > 0)
369             {
370                 List<EventDescriptor> eventList = new List<EventDescriptor>(_events);
371                 int foundCount = 0;
372                 int eventCount = _events.Length;
373 
374                 for (int i = 0; i < names.Length; i++)
375                 {
376                     for (int j = 0; j < eventCount; j++)
377                     {
378                         EventDescriptor currentEvent = eventList[j];
379 
380                         // Found a matching event.  Here, we add it to our array.  We also
381                         // mark it as null in our array list so we don't add it twice later.
382                         //
383                         if (currentEvent != null && currentEvent.Name.Equals(names[i]))
384                         {
385                             _events[foundCount++] = currentEvent;
386                             eventList[j] = null;
387                             break;
388                         }
389                     }
390                 }
391 
392                 // At this point we have filled in the first "foundCount" number of propeties, one for each
393                 // name in our name array.  If a name didn't match, then it is ignored.  Next, we must fill
394                 // in the rest of the properties.  We now have a sparse array containing the remainder, so
395                 // it's easy.
396                 //
397                 for (int i = 0; i < eventCount; i++)
398                 {
399                     if (eventList[i] != null)
400                     {
401                         _events[foundCount++] = eventList[i];
402                     }
403                 }
404 
405                 Debug.Assert(foundCount == eventCount, "We did not completely fill our event array");
406             }
407         }
408 
409         /// <summary>
410         ///    <para>
411         ///       Sorts the members of this EventDescriptorCollection using the specified IComparer.
412         ///    </para>
413         /// </summary>
InternalSort(IComparer sorter)414         protected void InternalSort(IComparer sorter)
415         {
416             if (sorter == null)
417             {
418                 TypeDescriptor.SortDescriptorArray(this);
419             }
420             else
421             {
422                 Array.Sort(_events, sorter);
423             }
424         }
425 
426         /// <internalonly/>
427         bool ICollection.IsSynchronized => false;
428 
429         /// <internalonly/>
430         object ICollection.SyncRoot => null;
431 
432         int ICollection.Count => Count;
433 
IEnumerable.GetEnumerator()434         IEnumerator IEnumerable.GetEnumerator()
435         {
436             return GetEnumerator();
437         }
438 
439         /// <internalonly/>
440         object IList.this[int index]
441         {
442             get
443             {
444                 return this[index];
445             }
446             set
447             {
448                 if (_readOnly)
449                 {
450                     throw new NotSupportedException();
451                 }
452 
453                 if (index >= Count)
454                 {
455                     throw new IndexOutOfRangeException();
456                 }
457                 EnsureEventsOwned();
458                 _events[index] = (EventDescriptor)value;
459             }
460         }
461 
462         /// <internalonly/>
IList.Add(object value)463         int IList.Add(object value)
464         {
465             return Add((EventDescriptor)value);
466         }
467 
468         /// <internalonly/>
IList.Contains(object value)469         bool IList.Contains(object value)
470         {
471             return Contains((EventDescriptor)value);
472         }
473 
IList.Clear()474         void IList.Clear()
475         {
476             Clear();
477         }
478 
479         /// <internalonly/>
IList.IndexOf(object value)480         int IList.IndexOf(object value)
481         {
482             return IndexOf((EventDescriptor)value);
483         }
484 
485         /// <internalonly/>
IList.Insert(int index, object value)486         void IList.Insert(int index, object value)
487         {
488             Insert(index, (EventDescriptor)value);
489         }
490 
491         /// <internalonly/>
IList.Remove(object value)492         void IList.Remove(object value)
493         {
494             Remove((EventDescriptor)value);
495         }
496 
IList.RemoveAt(int index)497         void IList.RemoveAt(int index)
498         {
499             RemoveAt(index);
500         }
501 
502         /// <internalonly/>
503         bool IList.IsReadOnly => _readOnly;
504 
505         /// <internalonly/>
506         bool IList.IsFixedSize => _readOnly;
507 
508         private class ArraySubsetEnumerator : IEnumerator
509         {
510             private readonly Array _array;
511             private readonly int _total;
512             private int _current;
513 
ArraySubsetEnumerator(Array array, int count)514             public ArraySubsetEnumerator(Array array, int count)
515             {
516                 Debug.Assert(count == 0 || array != null, "if array is null, count should be 0");
517                 Debug.Assert(array == null || count <= array.Length, "Trying to enumerate more than the array contains");
518 
519                 _array = array;
520                 _total = count;
521                 _current = -1;
522             }
523 
MoveNext()524             public bool MoveNext()
525             {
526                 if (_current < _total - 1)
527                 {
528                     _current++;
529                     return true;
530                 }
531                 else
532                 {
533                     return false;
534                 }
535             }
536 
Reset()537             public void Reset()
538             {
539                 _current = -1;
540             }
541 
542             public object Current
543             {
544                 get
545                 {
546                     if (_current == -1)
547                     {
548                         throw new InvalidOperationException();
549                     }
550                     else
551                     {
552                         return _array.GetValue(_current);
553                     }
554                 }
555             }
556         }
557     }
558 }
559 
560