objectlist.bmx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. SuperStrict
  2. Rem
  3. bbdoc: Data structures/Array-backed Object Lists
  4. End Rem
  5. Module BRL.ObjectList
  6. ModuleInfo "Version: 1.01"
  7. ModuleInfo "Author: Bruce A Henderson"
  8. ModuleInfo "License: zlib/libpng"
  9. ModuleInfo "History: 1.01"
  10. ModuleInfo "History: Implemented Swap()."
  11. ModuleInfo "History: 1.00"
  12. ModuleInfo "History: Initial Release."
  13. Rem
  14. bbdoc: Array-backed Object List
  15. End Rem
  16. Type TObjectList
  17. Field version:Int
  18. Field data:Object[16]
  19. Field size:Int
  20. Field dirty:Int
  21. Method _ensureCapacity(newSize:Int)
  22. If newSize >= data.length Then
  23. data = data[.. newSize * 3 / 2 + 1]
  24. End If
  25. End Method
  26. Rem
  27. bbdoc: Clears the list.
  28. about: Removes all objects from list.
  29. End Rem
  30. Method Clear()
  31. For Local i:Int = 0 Until size
  32. data[i] = Null
  33. Next
  34. size = 0
  35. version :+ 1
  36. dirty = False
  37. End Method
  38. Rem
  39. bbdoc: Checks if the list is empty.
  40. returns: #True if the list is empty, else #False
  41. End Rem
  42. Method IsEmpty:Int()
  43. Return size = 0
  44. End Method
  45. Rem
  46. bbdoc: Adds an object to the start of the list
  47. End Rem
  48. Method AddFirst(value:Object)
  49. Assert value Else "Can't insert Null object into list"
  50. Compact()
  51. If size Then
  52. _ensureCapacity(size + 1)
  53. ArrayCopy(data, 0, data, 1, size)
  54. End If
  55. data[0] = value
  56. size :+ 1
  57. version :+ 1
  58. End Method
  59. Rem
  60. bbdoc: Adds an object to the end of the list
  61. End Rem
  62. Method AddLast(value:Object)
  63. Assert value Else "Can't insert Null object into list"
  64. Compact()
  65. _ensureCapacity(size + 1)
  66. data[size] = value
  67. size :+ 1
  68. version :+ 1
  69. End Method
  70. Rem
  71. bbdoc: Checks if the list contains a value
  72. returns: #True if the list contains @obj, else #False
  73. End Rem
  74. Method Contains:Int(obj:Object)
  75. For Local i:Int = 0 Until size
  76. If data[i] = obj Then
  77. Return True
  78. End If
  79. Next
  80. End Method
  81. Rem
  82. bbdoc: Returns the first object in the list
  83. about: Returns #Null if the list is empty.
  84. End Rem
  85. Method First:Object()
  86. If size Then
  87. Compact()
  88. Return data[0]
  89. End If
  90. End Method
  91. Rem
  92. bbdoc: Returns the last object in the list
  93. about: Returns #Null if the list is empty.
  94. End Rem
  95. Method Last:Object()
  96. If size Then
  97. Compact()
  98. Return data[size - 1]
  99. End If
  100. End Method
  101. Rem
  102. bbdoc: Removes and returns the first object in the list.
  103. about: Returns #Null if the list is empty.
  104. End Rem
  105. Method RemoveFirst:Object()
  106. If size Then
  107. Compact()
  108. If size Then
  109. Local value:Object = data[0]
  110. ArrayCopy(data, 1, data, 0, size - 1)
  111. size :- 1
  112. data[size] = Null
  113. version :+ 1
  114. Return value
  115. End If
  116. End If
  117. End Method
  118. Rem
  119. bbdoc: Removes and returns the last object in the list.
  120. about: Returns #Null if the list is empty.
  121. End Rem
  122. Method RemoveLast:Object()
  123. If size Then
  124. Compact()
  125. If size Then
  126. Local value:Object = data[size - 1]
  127. size :- 1
  128. data[size] = Null
  129. version :+ 1
  130. Return value
  131. End If
  132. End If
  133. End Method
  134. Rem
  135. bbdoc: Returns the object at the given index.
  136. about: Throws an exception if the index is out of range (must be 0..list.Count()-1 inclusive).
  137. End Rem
  138. Method ValueAtIndex:Object(index:Int)
  139. Compact()
  140. Assert index>=0 Else "Object index must be positive"
  141. If index >= size Then RuntimeError "List index out of range"
  142. Return data[index]
  143. End Method
  144. Rem
  145. bbdoc: Counts the list length
  146. returns: The numbers of objects in the list.
  147. End Rem
  148. Method Count:Int()
  149. Compact()
  150. Return size
  151. End Method
  152. Rem
  153. bbdoc: Removes an object from the list.
  154. about: Remove scans the list for the specified value and removes it.
  155. By default, only the first found object is removed. Enabling @removeAll will result in all instances of @value being removed from the list.
  156. By default, the list is compacted on each remove. This can be inefficient if removing several objects from a list. Disabling @compactOnRemove
  157. will skip compaction until either #Compact() is called, or the current enumerator completes, or a different list method is called.
  158. This mechanism allows for removal of elements during an enumeration.
  159. End Rem
  160. Method Remove:Int(value:Object, removeAll:Int = False, compactOnRemove:Int = True)
  161. If size Then
  162. Local modified:Int
  163. For Local i:Int = 0 Until size
  164. If data[i] = value Then
  165. data[i] = Null
  166. modified = True
  167. If Not removeAll Then
  168. Exit
  169. End If
  170. End If
  171. Next
  172. If modified Then
  173. dirty = True
  174. If compactOnRemove Then
  175. Compact()
  176. End If
  177. version :+ 1
  178. End If
  179. Return modified
  180. End If
  181. End Method
  182. Rem
  183. bbdoc: Compacts the list.
  184. about: Use with #Remove() and @compactOnRemove = #False.
  185. End Rem
  186. Method Compact()
  187. If dirty Then
  188. Local offset:Int
  189. For Local i:Int = 0 Until size
  190. Local value:Object = data[i]
  191. If value Then
  192. data[offset] = value
  193. offset :+ 1
  194. End If
  195. Next
  196. size = offset
  197. dirty = False
  198. version :+ 1
  199. End If
  200. End Method
  201. Rem
  202. bbdoc: Swaps content of two lists while keeping list references intact.
  203. End Rem
  204. Method Swap(list:TObjectList)
  205. If Not list Then
  206. Return
  207. End If
  208. Local tmpVersion:Int = list.version
  209. Local tmpData:Object[] = list.data
  210. Local tmpSize:int = list.size
  211. Local tmpDirty:int = list.dirty
  212. list.version = self.version
  213. list.data = self.data
  214. list.size = self.size
  215. list.dirty = self.dirty
  216. self.version = tmpVersion
  217. self.data = tmpData
  218. self.size = tmpSize
  219. self.dirty = tmpDirty
  220. End Method
  221. Rem
  222. bbdoc: Creates an identical copy of the list.
  223. End Rem
  224. Method Copy:TObjectList()
  225. Compact()
  226. Local list:TObjectList = New TObjectList()
  227. For Local i:Int = 0 Until size
  228. list.AddLast(data[i])
  229. Next
  230. Return list
  231. End Method
  232. Rem
  233. bbdoc: Reverses the order of the list.
  234. End Rem
  235. Method Reverse()
  236. Compact()
  237. If size Then
  238. Local leftOffset:Int
  239. Local rightOffset:Int = size - 1
  240. While leftOffset < rightOffset
  241. Local temp:Object = data[leftOffset]
  242. data[leftOffset] = data[rightOffset]
  243. data[rightOffset] = temp
  244. leftOffset :+ 1
  245. rightOffset :- 1
  246. Wend
  247. End If
  248. End Method
  249. Rem
  250. bbdoc: Creates a new list that is the reversed version of this list.
  251. End Rem
  252. Method Reversed:TObjectList()
  253. Compact()
  254. Local list:TObjectList = New TObjectList()
  255. Local i:Int = size - 1
  256. While i >= 0
  257. list.AddLast(data[i])
  258. i :- 1
  259. Wend
  260. Return list
  261. End Method
  262. Method _removeAt(index:Int)
  263. data[index] = Null
  264. dirty = True
  265. version :+ 1
  266. End Method
  267. Method ObjectEnumerator:TObjectListEnumerator()
  268. Local enumeration:TObjectListEnumerator=New TObjectListEnumerator
  269. enumeration.list = Self
  270. Return enumeration
  271. End Method
  272. Method ReverseEnumerator:TObjectListReverseEnumerator()
  273. Local enumeration:TObjectListReverseEnumerator = New TObjectListReverseEnumerator
  274. enumeration.list = Self
  275. enumeration.index = size - 1
  276. Return enumeration
  277. End Method
  278. Rem
  279. bbdoc: Converts the list to an array
  280. returns: An array of objects
  281. End Rem
  282. Method ToArray:Object[]()
  283. Compact()
  284. Local arr:Object[] = New Object[size]
  285. If size Then
  286. ArrayCopy data, 0, arr, 0, size
  287. End If
  288. Return arr
  289. End Method
  290. Rem
  291. bbdoc: Creates a list from an array
  292. returns: A new object list
  293. End Rem
  294. Function FromArray:TObjectList(arr:Object[])
  295. Local list:TObjectList = New TObjectList
  296. For Local i:Int = 0 Until arr.length
  297. list.AddLast arr[i]
  298. Next
  299. Return list
  300. End Function
  301. Rem
  302. bbdoc: Sort the list in either ascending (default) or decending order.
  303. about: User types should implement a Compare method in order to be sorted.
  304. End Rem
  305. Method Sort(ascending:Int=True, compareFunc:Int( o1:Object,o2:Object )=_CompareObjects)
  306. If size < 2 Then
  307. Return
  308. End If
  309. Local ccsgn:Int = -1
  310. If ascending Then
  311. ccsgn = 1
  312. End If
  313. Compact()
  314. _sort(data, 0, size - 1, compareFunc, ccsgn)
  315. End Method
  316. Function _sort(data:Object[], low:Int, high:Int, compareFunc:Int( o1:Object,o2:Object ), ccsgn:Int)
  317. If low < high Then
  318. Local index:Int = _partition(data, low, high, compareFunc, ccsgn)
  319. _sort(data, low, index - 1, compareFunc, ccsgn)
  320. _sort(data, index + 1, high, compareFunc, ccsgn)
  321. End If
  322. End Function
  323. Function _partition:Int(data:Object[], low:Int, high:Int, compareFunc:Int( o1:Object,o2:Object ), ccsgn:Int)
  324. Local pivot:Object = data[high]
  325. Local index:Int = low - 1
  326. For Local n:Int = low Until high
  327. If compareFunc(data[n], pivot) * ccsgn < 0 Then
  328. index :+ 1
  329. Local tmp:Object = data[index]
  330. data[index] = data[n]
  331. data[n] = tmp
  332. End If
  333. Next
  334. Local tmp:Object = data[index + 1]
  335. data[index + 1] = data[high]
  336. data[high] = tmp
  337. Return index + 1
  338. End Function
  339. Function _CompareObjects:Int( o1:Object,o2:Object )
  340. Return o1.Compare( o2 )
  341. End Function
  342. End Type
  343. Rem
  344. bbdoc: Enumerator Object used by #TObjectList in order to implement #Eachin support.
  345. End Rem
  346. Type TObjectListEnumerator
  347. Field list:TObjectList
  348. Field index:Int = 0
  349. Field lastVersion:Int
  350. Method HasNext:Int()
  351. Local result:Int = index < list.size
  352. ' reached the end of the iteration
  353. If Not result Then
  354. list.Compact()
  355. End If
  356. Return result
  357. End Method
  358. Method NextObject:Object()
  359. Local value:Object = list.data[index]
  360. index :+ 1
  361. lastVersion = list.version
  362. Return value
  363. End Method
  364. Method Remove()
  365. list._removeAt(index - 1)
  366. lastVersion = list.version
  367. End Method
  368. Method Delete()
  369. If lastVersion = list.version Then
  370. list.Compact()
  371. End If
  372. End Method
  373. End Type
  374. Rem
  375. bbdoc: Enumerator Object used by #TObjectList in order to implement #Eachin support.
  376. about: This enumerator traverses the list in reverse (last to first).
  377. End Rem
  378. Type TObjectListReverseEnumerator
  379. Field list:TObjectList
  380. Field index:Int
  381. Field lastVersion:Int
  382. Method HasNext:Int()
  383. Local result:Int = index >= 0
  384. ' reached the end of the iteration
  385. If Not result Then
  386. list.Compact()
  387. End If
  388. Return result
  389. End Method
  390. Method NextObject:Object()
  391. Local value:Object = list.data[index]
  392. index :- 1
  393. lastVersion = list.version
  394. Return value
  395. End Method
  396. Method Remove()
  397. list._removeAt(index + 1)
  398. lastVersion = list.version
  399. End Method
  400. Method Delete()
  401. If lastVersion = list.version Then
  402. list.Compact()
  403. End If
  404. End Method
  405. Method ObjectEnumerator:TObjectListReverseEnumerator()
  406. Return Self
  407. End Method
  408. End Type