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