PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/Scripts/Engines/AI/NeedDriven/SortableList.cs

https://bitbucket.org/Kel/crepuscule
C# | 539 lines | 289 code | 42 blank | 208 comment | 87 complexity | 8fc7dc2dcf1eeb7250b2bee0286a714e MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Collections;
  6. namespace Server
  7. {
  8. /// <summary>
  9. /// The SortableList allows to maintain a list sorted as long as needed.
  10. /// If no IComparer interface has been provided at construction, then the list expects the Objects to implement IComparer.
  11. /// If the list is not sorted it behaves like an ordinary list.
  12. /// When sorted, the list's "Add" method will put new objects at the right place.
  13. /// As well the "Contains" and "IndexOf" methods will perform a binary search.
  14. /// </summary>
  15. public class SortableList : IList, ICloneable
  16. {
  17. private ArrayList _List;
  18. private IComparer _Comparer = null;
  19. private bool _UseObjectsComparison;
  20. /// <summary>
  21. /// Default constructor.
  22. /// Since no IComparer is provided here, added objects must implement the IComparer interface.
  23. /// </summary>
  24. public SortableList()
  25. { InitProperties(null, 0); }
  26. /// <summary>
  27. /// Constructor.
  28. /// Since no IComparer is provided, added objects must implement the IComparer interface.
  29. /// </summary>
  30. /// <param name="Capacity">Capacity of the list (<see cref="ArrayList.Capacity">ArrayList.Capacity</see>)</param>
  31. public SortableList(int Capacity)
  32. { InitProperties(null, Capacity); }
  33. /// <summary>
  34. /// Constructor.
  35. /// </summary>
  36. /// <param name="Comparer">Will be used to compare added elements for sort and search operations.</param>
  37. public SortableList(IComparer Comparer)
  38. { InitProperties(Comparer, 0); }
  39. /// <summary>
  40. /// Constructor.
  41. /// </summary>
  42. /// <param name="Comparer">Will be used to compare added elements for sort and search operations.</param>
  43. /// <param name="Capacity">Capacity of the list (<see cref="ArrayList.Capacity">ArrayList.Capacity</see>)</param>
  44. public SortableList(IComparer Comparer, int Capacity)
  45. { InitProperties(Comparer, Capacity); }
  46. /// <summary>
  47. /// 'Get only' property that indicates if the list is sorted.
  48. /// </summary>
  49. public bool IsSorted { get { return _IsSorted; } }
  50. private bool _IsSorted;
  51. /// <summary>
  52. /// Get : Indicates if the list must be kept sorted from now on.
  53. /// Set : Tells the list if it must stay sorted or not. Impossible to set to true if the list is not sorted.
  54. /// <see cref="KeepSorted">KeepSorted</see>==true implies that <see cref="IsSorted">IsSorted</see>==true
  55. /// </summary>
  56. /// <exception cref="InvalidOperationException">Cannot be set to true if the list is not sorted yet.</exception>
  57. public bool KeepSorted
  58. {
  59. set
  60. {
  61. if (value == true && !_IsSorted) throw new InvalidOperationException("The SortableList can only be kept sorted if it is sorted.");
  62. _KeepSorted = value;
  63. }
  64. get { return _KeepSorted; }
  65. }
  66. private bool _KeepSorted;
  67. /// <summary>
  68. /// If set to true, it will not be possible to add an object to the list if its value is already in the list.
  69. /// </summary>
  70. public bool AddDuplicates { set { _AddDuplicates = value; } get { return _AddDuplicates; } }
  71. private bool _AddDuplicates;
  72. /// <summary>
  73. /// IList implementation.
  74. /// Gets - or sets - object's value at a specified index.
  75. /// The set operation is impossible if the <see cref="KeepSorted">KeepSorted</see> property is set to true.
  76. /// </summary>
  77. /// <exception cref="ArgumentOutOfRangeException">Index is less than zero or Index is greater than Count.</exception>
  78. /// <exception cref="InvalidOperationException">[] operator cannot be used to set a value if KeepSorted property is set to true.</exception>
  79. public object this[int Index]
  80. {
  81. get
  82. {
  83. if (Index >= _List.Count || Index < 0) throw new ArgumentOutOfRangeException("Index is less than zero or Index is greater than Count.");
  84. return _List[Index];
  85. }
  86. set
  87. {
  88. if (_KeepSorted) throw new InvalidOperationException("[] operator cannot be used to set a value if KeepSorted property is set to true.");
  89. if (Index >= _List.Count || Index < 0) throw new ArgumentOutOfRangeException("Index is less than zero or Index is greater than Count.");
  90. if (ObjectIsCompliant(value))
  91. {
  92. object OBefore = Index > 0 ? _List[Index - 1] : null;
  93. object OAfter = Index < Count - 1 ? _List[Index + 1] : null;
  94. if (OBefore != null && _Comparer.Compare(OBefore, value) > 0 || OAfter != null && _Comparer.Compare(value, OAfter) > 0) _IsSorted = false;
  95. _List[Index] = value;
  96. }
  97. }
  98. }
  99. /// <summary>
  100. /// IList implementation.
  101. /// If the <see cref="KeepSorted">KeepSorted</see> property is set to true, the object will be added at the right place.
  102. /// Else it will be added at the end of the list.
  103. /// </summary>
  104. /// <param name="O">The object to add.</param>
  105. /// <returns>The index where the object has been added.</returns>
  106. /// <exception cref="ArgumentException">The SortableList is set to use object's IComparable interface, and the specifed object does not implement this interface.</exception>
  107. public int Add(object O)
  108. {
  109. int Return = -1;
  110. if (ObjectIsCompliant(O))
  111. {
  112. if (_KeepSorted)
  113. {
  114. int Index = IndexOf(O);
  115. int NewIndex = Index >= 0 ? Index : -Index - 1;
  116. if (NewIndex >= Count) _List.Add(O);
  117. else _List.Insert(NewIndex, O);
  118. Return = NewIndex;
  119. }
  120. else
  121. {
  122. _IsSorted = false;
  123. Return = _List.Add(O);
  124. }
  125. }
  126. return Return;
  127. }
  128. /// <summary>
  129. /// IList implementation.
  130. /// Search for a specified object in the list.
  131. /// If the list is sorted, a <see cref="ArrayList.BinarySearch">BinarySearch</see> is performed using IComparer interface.
  132. /// Else the <see cref="Equals">Object.Equals</see> implementation is used.
  133. /// </summary>
  134. /// <param name="O">The object to look for</param>
  135. /// <returns>true if the object is in the list, otherwise false.</returns>
  136. public bool Contains(object O)
  137. {
  138. return _IsSorted ? _List.BinarySearch(O, _Comparer) >= 0 : _List.Contains(O);
  139. }
  140. /// <summary>
  141. /// IList implementation.
  142. /// Returns the index of the specified object in the list.
  143. /// If the list is sorted, a <see cref="ArrayList.BinarySearch">BinarySearch</see> is performed using IComparer interface.
  144. /// Else the <see cref="Equals">Object.Equals</see> implementation of objects is used.
  145. /// </summary>
  146. /// <param name="O">The object to locate.</param>
  147. /// <returns>
  148. /// If the object has been found, a positive integer corresponding to its position.
  149. /// If the objects has not been found, a negative integer which is the bitwise complement of the index of the next element.
  150. /// </returns>
  151. public int IndexOf(object O)
  152. {
  153. int Result = -1;
  154. if (_IsSorted)
  155. {
  156. Result = _List.BinarySearch(O, _Comparer);
  157. while (Result > 0 && _List[Result - 1].Equals(O)) Result--; // We want to point at the FIRST occurence
  158. }
  159. else Result = _List.IndexOf(O);
  160. return Result;
  161. }
  162. /// <summary>
  163. /// IList implementation.
  164. /// Idem <see cref="ArrayList">ArrayList</see>
  165. /// </summary>
  166. public bool IsFixedSize { get { return _List.IsFixedSize; } }
  167. /// <summary>
  168. /// IList implementation.
  169. /// Idem <see cref="ArrayList">ArrayList</see>
  170. /// </summary>
  171. public bool IsReadOnly { get { return _List.IsReadOnly; } }
  172. /// <summary>
  173. /// IList implementation.
  174. /// Idem <see cref="ArrayList">ArrayList</see>
  175. /// </summary>
  176. public void Clear() { _List.Clear(); }
  177. /// <summary>
  178. /// IList implementation.
  179. /// Inserts an objects at a specified index.
  180. /// Cannot be used if the list has its KeepSorted property set to true.
  181. /// </summary>
  182. /// <param name="Index">The index before which the object must be added.</param>
  183. /// <param name="O">The object to add.</param>
  184. /// <exception cref="ArgumentException">The SortableList is set to use object's IComparable interface, and the specifed object does not implement this interface.</exception>
  185. /// <exception cref="ArgumentOutOfRangeException">Index is less than zero or Index is greater than Count.</exception>
  186. /// <exception cref="InvalidOperationException">If the object is added at the specify index, the list will not be sorted any more and the <see cref="KeepSorted"/> property is set to true.</exception>
  187. public void Insert(int Index, object O)
  188. {
  189. if (_KeepSorted) throw new InvalidOperationException("Insert method cannot be called if KeepSorted property is set to true.");
  190. if (Index >= _List.Count || Index < 0) throw new ArgumentOutOfRangeException("Index is less than zero or Index is greater than Count.");
  191. if (ObjectIsCompliant(O))
  192. {
  193. object OBefore = Index > 0 ? _List[Index - 1] : null;
  194. object OAfter = _List[Index];
  195. if (OBefore != null && _Comparer.Compare(OBefore, O) > 0 || OAfter != null && _Comparer.Compare(O, OAfter) > 0) _IsSorted = false;
  196. _List.Insert(Index, O);
  197. }
  198. }
  199. /// <summary>
  200. /// IList implementation.
  201. /// Idem <see cref="ArrayList">ArrayList</see>
  202. /// </summary>
  203. /// <param name="Value">The object whose value must be removed if found in the list.</param>
  204. public void Remove(object Value) { _List.Remove(Value); }
  205. /// <summary>
  206. /// IList implementation.
  207. /// Idem <see cref="ArrayList">ArrayList</see>
  208. /// </summary>
  209. /// <param name="Index">Index of object to remove.</param>
  210. public void RemoveAt(int Index) { _List.RemoveAt(Index); }
  211. /// <summary>
  212. /// IList.ICollection implementation.
  213. /// Idem <see cref="ArrayList">ArrayList</see>
  214. /// </summary>
  215. /// <param name="array"></param>
  216. /// <param name="arrayIndex"></param>
  217. public void CopyTo(Array array, int arrayIndex) { _List.CopyTo(array, arrayIndex); }
  218. /// <summary>
  219. /// IList.ICollection implementation.
  220. /// Idem <see cref="ArrayList">ArrayList</see>
  221. /// </summary>
  222. public int Count { get { return _List.Count; } }
  223. /// <summary>
  224. /// IList.ICollection implementation.
  225. /// Idem <see cref="ArrayList">ArrayList</see>
  226. /// </summary>
  227. public bool IsSynchronized { get { return _List.IsSynchronized; } }
  228. /// <summary>
  229. /// IList.ICollection implementation.
  230. /// Idem <see cref="ArrayList">ArrayList</see>
  231. /// </summary>
  232. public object SyncRoot { get { return _List.SyncRoot; } }
  233. /// <summary>
  234. /// IList.IEnumerable implementation.
  235. /// Idem <see cref="ArrayList">ArrayList</see>
  236. /// </summary>
  237. /// <returns>Enumerator on the list.</returns>
  238. public IEnumerator GetEnumerator()
  239. { return _List.GetEnumerator(); }
  240. /// <summary>
  241. /// ICloneable implementation.
  242. /// Idem <see cref="ArrayList">ArrayList</see>
  243. /// </summary>
  244. /// <returns>Cloned object.</returns>
  245. public object Clone()
  246. {
  247. SortableList Clone = new SortableList(_Comparer, _List.Capacity);
  248. Clone._List = (ArrayList)_List.Clone();
  249. Clone._AddDuplicates = _AddDuplicates;
  250. Clone._IsSorted = _IsSorted;
  251. Clone._KeepSorted = _KeepSorted;
  252. return Clone;
  253. }
  254. /// <summary>
  255. /// Idem IndexOf(object), but starting at a specified position in the list
  256. /// </summary>
  257. /// <param name="O">The object to locate.</param>
  258. /// <param name="Start">The index for start position.</param>
  259. /// <returns></returns>
  260. public int IndexOf(object O, int Start)
  261. {
  262. int Result = -1;
  263. if (_IsSorted)
  264. {
  265. Result = _List.BinarySearch(Start, _List.Count - Start, O, _Comparer);
  266. while (Result > Start && _List[Result - 1].Equals(O)) Result--; // We want to point at the first occurence
  267. }
  268. else Result = _List.IndexOf(O, Start);
  269. return Result;
  270. }
  271. /// <summary>
  272. /// Defines an equality for two objects
  273. /// </summary>
  274. public delegate bool Equality(object O1, object O2);
  275. /// <summary>
  276. /// Idem IndexOf(object), but with a specified equality function
  277. /// </summary>
  278. /// <param name="O">The object to locate.</param>
  279. /// <param name="AreEqual">Equality function to use for the search.</param>
  280. /// <returns></returns>
  281. public int IndexOf(object O, Equality AreEqual)
  282. {
  283. for (int i = 0; i < _List.Count; i++)
  284. if (AreEqual(_List[i], O)) return i;
  285. return -1;
  286. }
  287. /// <summary>
  288. /// Idem IndexOf(object), but with a start index and a specified equality function
  289. /// </summary>
  290. /// <param name="O">The object to locate.</param>
  291. /// <param name="Start">The index for start position.</param>
  292. /// <param name="AreEqual">Equality function to use for the search.</param>
  293. /// <returns></returns>
  294. public int IndexOf(object O, int Start, Equality AreEqual)
  295. {
  296. if (Start < 0 || Start >= _List.Count) throw new ArgumentException("Start index must belong to [0; Count-1].");
  297. for (int i = Start; i < _List.Count; i++)
  298. if (AreEqual(_List[i], O)) return i;
  299. return -1;
  300. }
  301. /// <summary>
  302. /// Idem <see cref="ArrayList">ArrayList</see>
  303. /// </summary>
  304. public int Capacity { get { return _List.Capacity; } set { _List.Capacity = value; } }
  305. /// <summary>
  306. /// Object.ToString() override.
  307. /// Build a string to represent the list.
  308. /// </summary>
  309. /// <returns>The string refecting the list.</returns>
  310. public override string ToString()
  311. {
  312. string OutString = "{";
  313. for (int i = 0; i < _List.Count; i++)
  314. OutString += _List[i].ToString() + (i != _List.Count - 1 ? "; " : "}");
  315. return OutString;
  316. }
  317. /// <summary>
  318. /// Object.Equals() override.
  319. /// </summary>
  320. /// <returns>true if object is equal to this, otherwise false.</returns>
  321. public override bool Equals(object O)
  322. {
  323. SortableList SL = (SortableList)O;
  324. if (SL.Count != Count) return false;
  325. for (int i = 0; i < Count; i++)
  326. if (!SL[i].Equals(this[i])) return false;
  327. return true;
  328. }
  329. /// <summary>
  330. /// Object.GetHashCode() override.
  331. /// </summary>
  332. /// <returns>Hash code for this.</returns>
  333. public override int GetHashCode() { return _List.GetHashCode(); }
  334. /// <summary>
  335. /// Sorts the elements in the list using <see cref="ArrayList.Sort">ArrayList.Sort</see>.
  336. /// Does nothing if the list is already sorted.
  337. /// </summary>
  338. public void Sort()
  339. {
  340. if (_IsSorted) return;
  341. _List.Sort(_Comparer);
  342. _IsSorted = true;
  343. }
  344. /// <summary>
  345. /// If the <see cref="KeepSorted">KeepSorted</see> property is set to true, the object will be added at the right place.
  346. /// Else it will be appended to the list.
  347. /// </summary>
  348. /// <param name="C">The object to add.</param>
  349. /// <returns>The index where the object has been added.</returns>
  350. /// <exception cref="ArgumentException">The SortableList is set to use object's IComparable interface, and the specifed object does not implement this interface.</exception>
  351. public void AddRange(ICollection C)
  352. {
  353. if (_KeepSorted) foreach (object O in C) Add(O);
  354. else _List.AddRange(C);
  355. }
  356. /// <summary>
  357. /// Inserts a collection of objects at a specified index.
  358. /// Should not be used if the list is the KeepSorted property is set to true.
  359. /// </summary>
  360. /// <param name="Index">The index before which the objects must be added.</param>
  361. /// <param name="C">The object to add.</param>
  362. /// <exception cref="ArgumentException">The SortableList is set to use objects's IComparable interface, and the specifed object does not implement this interface.</exception>
  363. /// <exception cref="ArgumentOutOfRangeException">Index is less than zero or Index is greater than Count.</exception>
  364. /// <exception cref="InvalidOperationException">If the object is added at the specify index, the list will not be sorted any more and the <see cref="KeepSorted"/> property is set to true.</exception>
  365. public void InsertRange(int Index, ICollection C)
  366. {
  367. if (_KeepSorted) foreach (object O in C) Insert(Index++, O);
  368. else _List.InsertRange(Index, C);
  369. }
  370. /// <summary>
  371. /// Limits the number of occurrences of a specified value.
  372. /// Same values are equals according to the Equals() method of objects in the list.
  373. /// The first occurrences encountered are kept.
  374. /// </summary>
  375. /// <param name="Value">Value whose occurrences number must be limited.</param>
  376. /// <param name="NbValuesToKeep">Number of occurrences to keep</param>
  377. public void LimitNbOccurrences(object Value, int NbValuesToKeep)
  378. {
  379. if (Value == null) throw new ArgumentNullException("Value");
  380. int Pos = 0;
  381. while ((Pos = IndexOf(Value, Pos)) >= 0)
  382. {
  383. if (NbValuesToKeep <= 0) _List.RemoveAt(Pos);
  384. else { Pos++; NbValuesToKeep--; }
  385. if (_IsSorted && _Comparer.Compare(_List[Pos], Value) > 0) break; // No need to follow
  386. }
  387. }
  388. /// <summary>
  389. /// Removes all duplicates in the list.
  390. /// Each value encountered will have only one representant.
  391. /// </summary>
  392. public void RemoveDuplicates()
  393. {
  394. int PosIt;
  395. if (_IsSorted)
  396. {
  397. PosIt = 0;
  398. while (PosIt < Count - 1)
  399. {
  400. if (_Comparer.Compare(this[PosIt], this[PosIt + 1]) == 0) RemoveAt(PosIt);
  401. else PosIt++;
  402. }
  403. }
  404. else
  405. {
  406. int Left = 0;
  407. while (Left >= 0)
  408. {
  409. PosIt = Left + 1;
  410. while (PosIt > 0)
  411. {
  412. if (Left != PosIt && _Comparer.Compare(this[Left], this[PosIt]) == 0) RemoveAt(PosIt);
  413. else PosIt++;
  414. }
  415. Left++;
  416. }
  417. }
  418. }
  419. /// <summary>
  420. /// Returns the object of the list whose value is minimum
  421. /// </summary>
  422. /// <returns>The minimum object in the list</returns>
  423. public int IndexOfMin()
  424. {
  425. int RetInt = -1;
  426. if (_List.Count > 0)
  427. {
  428. RetInt = 0;
  429. object RetObj = _List[0];
  430. if (!_IsSorted)
  431. {
  432. for (int i = 1; i < _List.Count; i++)
  433. if (_Comparer.Compare(RetObj, _List[i]) > 0)
  434. {
  435. RetObj = _List[i];
  436. RetInt = i;
  437. }
  438. }
  439. }
  440. return RetInt;
  441. }
  442. /// <summary>
  443. /// Returns the object of the list whose value is maximum
  444. /// </summary>
  445. /// <returns>The maximum object in the list</returns>
  446. public int IndexOfMax()
  447. {
  448. int RetInt = -1;
  449. if (_List.Count > 0)
  450. {
  451. RetInt = _List.Count - 1;
  452. object RetObj = _List[_List.Count - 1];
  453. if (!_IsSorted)
  454. {
  455. for (int i = _List.Count - 2; i >= 0; i--)
  456. if (_Comparer.Compare(RetObj, _List[i]) < 0)
  457. {
  458. RetObj = _List[i];
  459. RetInt = i;
  460. }
  461. }
  462. }
  463. return RetInt;
  464. }
  465. private bool ObjectIsCompliant(object O)
  466. {
  467. if (_UseObjectsComparison && !(O is IComparable)) throw new ArgumentException("The SortableList is set to use the IComparable interface of objects, and the object to add does not implement the IComparable interface.");
  468. if (!_AddDuplicates && Contains(O)) return false;
  469. return true;
  470. }
  471. private class Comparison : IComparer
  472. {
  473. public int Compare(object O1, object O2)
  474. {
  475. IComparable C = O1 as IComparable;
  476. return C.CompareTo(O2);
  477. }
  478. }
  479. private void InitProperties(IComparer Comparer, int Capacity)
  480. {
  481. if (Comparer != null)
  482. {
  483. _Comparer = Comparer;
  484. _UseObjectsComparison = false;
  485. }
  486. else
  487. {
  488. _Comparer = new Comparison();
  489. _UseObjectsComparison = true;
  490. }
  491. _List = Capacity > 0 ? new ArrayList(Capacity) : new ArrayList();
  492. _IsSorted = true;
  493. _KeepSorted = true;
  494. _AddDuplicates = true;
  495. }
  496. }
  497. }