motion-yapper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (348) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.lock +63 -0
  6. data/README.md +16 -0
  7. data/Rakefile +20 -0
  8. data/app/app_delegate.rb +5 -0
  9. data/lib/yapper.rb +30 -0
  10. data/lib/yapper/attachment.rb +48 -0
  11. data/lib/yapper/bson.rb +20 -0
  12. data/lib/yapper/config.rb +18 -0
  13. data/lib/yapper/db.rb +151 -0
  14. data/lib/yapper/document.rb +54 -0
  15. data/lib/yapper/document/attachment.rb +26 -0
  16. data/lib/yapper/document/callbacks.rb +86 -0
  17. data/lib/yapper/document/persistance.rb +171 -0
  18. data/lib/yapper/document/relation.rb +84 -0
  19. data/lib/yapper/document/selection.rb +100 -0
  20. data/lib/yapper/extensions.rb +80 -0
  21. data/lib/yapper/log.rb +5 -0
  22. data/lib/yapper/sync.rb +134 -0
  23. data/lib/yapper/sync/data.rb +12 -0
  24. data/lib/yapper/sync/event.rb +194 -0
  25. data/lib/yapper/sync/queue.rb +164 -0
  26. data/lib/yapper/timestamps.rb +16 -0
  27. data/lib/yapper/version.rb +3 -0
  28. data/lib/yapper/yapper.rb +2 -0
  29. data/spec/helpers/time_helper.rb +3 -0
  30. data/spec/integration/all_spec.rb +40 -0
  31. data/spec/integration/callback_spec.rb +87 -0
  32. data/spec/integration/db_spec.rb +40 -0
  33. data/spec/integration/find_spec.rb +51 -0
  34. data/spec/integration/persistance_spec.rb +118 -0
  35. data/spec/integration/relation_spec.rb +174 -0
  36. data/spec/integration/sync_spec.rb +42 -0
  37. data/spec/integration/timestamps_spec.rb +34 -0
  38. data/spec/integration/types_spec.rb +23 -0
  39. data/spec/integration/when_spec.rb +29 -0
  40. data/spec/integration/where_spec.rb +132 -0
  41. data/vendor/Podfile.lock +24 -0
  42. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPClient.h +641 -0
  43. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPClient.m +1396 -0
  44. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h +133 -0
  45. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m +327 -0
  46. data/vendor/Pods/AFNetworking/AFNetworking/AFImageRequestOperation.h +113 -0
  47. data/vendor/Pods/AFNetworking/AFNetworking/AFImageRequestOperation.m +321 -0
  48. data/vendor/Pods/AFNetworking/AFNetworking/AFJSONRequestOperation.h +71 -0
  49. data/vendor/Pods/AFNetworking/AFNetworking/AFJSONRequestOperation.m +150 -0
  50. data/vendor/Pods/AFNetworking/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
  51. data/vendor/Pods/AFNetworking/AFNetworking/AFNetworkActivityIndicatorManager.m +157 -0
  52. data/vendor/Pods/AFNetworking/AFNetworking/AFNetworking.h +43 -0
  53. data/vendor/Pods/AFNetworking/AFNetworking/AFPropertyListRequestOperation.h +68 -0
  54. data/vendor/Pods/AFNetworking/AFNetworking/AFPropertyListRequestOperation.m +143 -0
  55. data/vendor/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h +370 -0
  56. data/vendor/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m +848 -0
  57. data/vendor/Pods/AFNetworking/AFNetworking/AFXMLRequestOperation.h +89 -0
  58. data/vendor/Pods/AFNetworking/AFNetworking/AFXMLRequestOperation.m +167 -0
  59. data/vendor/Pods/AFNetworking/AFNetworking/UIImageView+AFNetworking.h +78 -0
  60. data/vendor/Pods/AFNetworking/AFNetworking/UIImageView+AFNetworking.m +191 -0
  61. data/vendor/Pods/AFNetworking/LICENSE +19 -0
  62. data/vendor/Pods/AFNetworking/README.md +208 -0
  63. data/vendor/Pods/BuildHeaders/AFNetworking/AFHTTPClient.h +641 -0
  64. data/vendor/Pods/BuildHeaders/AFNetworking/AFHTTPRequestOperation.h +133 -0
  65. data/vendor/Pods/BuildHeaders/AFNetworking/AFImageRequestOperation.h +113 -0
  66. data/vendor/Pods/BuildHeaders/AFNetworking/AFJSONRequestOperation.h +71 -0
  67. data/vendor/Pods/BuildHeaders/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
  68. data/vendor/Pods/BuildHeaders/AFNetworking/AFNetworking.h +43 -0
  69. data/vendor/Pods/BuildHeaders/AFNetworking/AFPropertyListRequestOperation.h +68 -0
  70. data/vendor/Pods/BuildHeaders/AFNetworking/AFURLConnectionOperation.h +370 -0
  71. data/vendor/Pods/BuildHeaders/AFNetworking/AFXMLRequestOperation.h +89 -0
  72. data/vendor/Pods/BuildHeaders/AFNetworking/UIImageView+AFNetworking.h +78 -0
  73. data/vendor/Pods/BuildHeaders/CocoaLumberjack/ContextFilterLogFormatter.h +65 -0
  74. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDASLLogger.h +41 -0
  75. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDAbstractDatabaseLogger.h +102 -0
  76. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDFileLogger.h +334 -0
  77. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDLog.h +601 -0
  78. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDTTYLogger.h +167 -0
  79. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DispatchQueueLogFormatter.h +116 -0
  80. data/vendor/Pods/BuildHeaders/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
  81. data/vendor/Pods/BuildHeaders/Reachability/Reachability.h +109 -0
  82. data/vendor/Pods/BuildHeaders/YapDatabase/YapCache.h +90 -0
  83. data/vendor/Pods/BuildHeaders/YapDatabase/YapCollectionKey.h +20 -0
  84. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabase.h +547 -0
  85. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseConnection.h +447 -0
  86. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseConnectionState.h +29 -0
  87. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseDefaults.h +37 -0
  88. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtension.h +15 -0
  89. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionConnection.h +11 -0
  90. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionPrivate.h +440 -0
  91. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionTransaction.h +11 -0
  92. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredView.h +108 -0
  93. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewConnection.h +12 -0
  94. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewPrivate.h +19 -0
  95. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewTransaction.h +39 -0
  96. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearch.h +89 -0
  97. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchConnection.h +32 -0
  98. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchPrivate.h +69 -0
  99. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
  100. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchTransaction.h +68 -0
  101. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseLogging.h +158 -0
  102. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseManager.h +17 -0
  103. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabasePrivate.h +424 -0
  104. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseQuery.h +42 -0
  105. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndex.h +100 -0
  106. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexConnection.h +33 -0
  107. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexPrivate.h +73 -0
  108. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexSetup.h +33 -0
  109. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexTransaction.h +58 -0
  110. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseStatement.h +13 -0
  111. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseString.h +121 -0
  112. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseTransaction.h +541 -0
  113. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseView.h +186 -0
  114. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewChange.h +272 -0
  115. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewChangePrivate.h +94 -0
  116. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewConnection.h +115 -0
  117. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewMappings.h +825 -0
  118. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewMappingsPrivate.h +72 -0
  119. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewOptions.h +56 -0
  120. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPage.h +36 -0
  121. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPageMetadata.h +27 -0
  122. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPrivate.h +153 -0
  123. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewRangeOptions.h +330 -0
  124. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewRangeOptionsPrivate.h +17 -0
  125. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewTransaction.h +447 -0
  126. data/vendor/Pods/BuildHeaders/YapDatabase/YapMemoryTable.h +74 -0
  127. data/vendor/Pods/BuildHeaders/YapDatabase/YapNull.h +17 -0
  128. data/vendor/Pods/BuildHeaders/YapDatabase/YapSet.h +41 -0
  129. data/vendor/Pods/BuildHeaders/YapDatabase/YapTouch.h +15 -0
  130. data/vendor/Pods/CocoaLumberjack/LICENSE.txt +18 -0
  131. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.h +41 -0
  132. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.m +99 -0
  133. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.h +102 -0
  134. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.m +727 -0
  135. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.h +334 -0
  136. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.m +1353 -0
  137. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.h +601 -0
  138. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.m +1083 -0
  139. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.h +167 -0
  140. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.m +1479 -0
  141. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/ContextFilterLogFormatter.h +65 -0
  142. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/ContextFilterLogFormatter.m +191 -0
  143. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DispatchQueueLogFormatter.h +116 -0
  144. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DispatchQueueLogFormatter.m +251 -0
  145. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/README.txt +7 -0
  146. data/vendor/Pods/CocoaLumberjack/README.markdown +37 -0
  147. data/vendor/Pods/Headers/AFNetworking/AFHTTPClient.h +641 -0
  148. data/vendor/Pods/Headers/AFNetworking/AFHTTPRequestOperation.h +133 -0
  149. data/vendor/Pods/Headers/AFNetworking/AFImageRequestOperation.h +113 -0
  150. data/vendor/Pods/Headers/AFNetworking/AFJSONRequestOperation.h +71 -0
  151. data/vendor/Pods/Headers/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
  152. data/vendor/Pods/Headers/AFNetworking/AFNetworking.h +43 -0
  153. data/vendor/Pods/Headers/AFNetworking/AFPropertyListRequestOperation.h +68 -0
  154. data/vendor/Pods/Headers/AFNetworking/AFURLConnectionOperation.h +370 -0
  155. data/vendor/Pods/Headers/AFNetworking/AFXMLRequestOperation.h +89 -0
  156. data/vendor/Pods/Headers/AFNetworking/UIImageView+AFNetworking.h +78 -0
  157. data/vendor/Pods/Headers/CocoaLumberjack/ContextFilterLogFormatter.h +65 -0
  158. data/vendor/Pods/Headers/CocoaLumberjack/DDASLLogger.h +41 -0
  159. data/vendor/Pods/Headers/CocoaLumberjack/DDAbstractDatabaseLogger.h +102 -0
  160. data/vendor/Pods/Headers/CocoaLumberjack/DDFileLogger.h +334 -0
  161. data/vendor/Pods/Headers/CocoaLumberjack/DDLog.h +601 -0
  162. data/vendor/Pods/Headers/CocoaLumberjack/DDTTYLogger.h +167 -0
  163. data/vendor/Pods/Headers/CocoaLumberjack/DispatchQueueLogFormatter.h +116 -0
  164. data/vendor/Pods/Headers/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
  165. data/vendor/Pods/Headers/Reachability/Reachability.h +109 -0
  166. data/vendor/Pods/Headers/YapDatabase/YapCache.h +90 -0
  167. data/vendor/Pods/Headers/YapDatabase/YapCollectionKey.h +20 -0
  168. data/vendor/Pods/Headers/YapDatabase/YapDatabase.h +547 -0
  169. data/vendor/Pods/Headers/YapDatabase/YapDatabaseConnection.h +447 -0
  170. data/vendor/Pods/Headers/YapDatabase/YapDatabaseConnectionState.h +29 -0
  171. data/vendor/Pods/Headers/YapDatabase/YapDatabaseDefaults.h +37 -0
  172. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtension.h +15 -0
  173. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionConnection.h +11 -0
  174. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionPrivate.h +440 -0
  175. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionTransaction.h +11 -0
  176. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredView.h +108 -0
  177. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewConnection.h +12 -0
  178. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewPrivate.h +19 -0
  179. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewTransaction.h +39 -0
  180. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearch.h +89 -0
  181. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchConnection.h +32 -0
  182. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchPrivate.h +69 -0
  183. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
  184. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchTransaction.h +68 -0
  185. data/vendor/Pods/Headers/YapDatabase/YapDatabaseLogging.h +158 -0
  186. data/vendor/Pods/Headers/YapDatabase/YapDatabaseManager.h +17 -0
  187. data/vendor/Pods/Headers/YapDatabase/YapDatabasePrivate.h +424 -0
  188. data/vendor/Pods/Headers/YapDatabase/YapDatabaseQuery.h +42 -0
  189. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndex.h +100 -0
  190. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexConnection.h +33 -0
  191. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexPrivate.h +73 -0
  192. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexSetup.h +33 -0
  193. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexTransaction.h +58 -0
  194. data/vendor/Pods/Headers/YapDatabase/YapDatabaseStatement.h +13 -0
  195. data/vendor/Pods/Headers/YapDatabase/YapDatabaseString.h +121 -0
  196. data/vendor/Pods/Headers/YapDatabase/YapDatabaseTransaction.h +541 -0
  197. data/vendor/Pods/Headers/YapDatabase/YapDatabaseView.h +186 -0
  198. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewChange.h +272 -0
  199. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewChangePrivate.h +94 -0
  200. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewConnection.h +115 -0
  201. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewMappings.h +825 -0
  202. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewMappingsPrivate.h +72 -0
  203. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewOptions.h +56 -0
  204. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPage.h +36 -0
  205. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPageMetadata.h +27 -0
  206. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPrivate.h +153 -0
  207. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewRangeOptions.h +330 -0
  208. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewRangeOptionsPrivate.h +17 -0
  209. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewTransaction.h +447 -0
  210. data/vendor/Pods/Headers/YapDatabase/YapMemoryTable.h +74 -0
  211. data/vendor/Pods/Headers/YapDatabase/YapNull.h +17 -0
  212. data/vendor/Pods/Headers/YapDatabase/YapSet.h +41 -0
  213. data/vendor/Pods/Headers/YapDatabase/YapTouch.h +15 -0
  214. data/vendor/Pods/Headers/____Pods-AFNetworking-prefix.h +17 -0
  215. data/vendor/Pods/Headers/____Pods-CocoaLumberjack-prefix.h +5 -0
  216. data/vendor/Pods/Headers/____Pods-NSData+MD5Digest-prefix.h +5 -0
  217. data/vendor/Pods/Headers/____Pods-Reachability-prefix.h +5 -0
  218. data/vendor/Pods/Headers/____Pods-YapDatabase-prefix.h +5 -0
  219. data/vendor/Pods/Headers/____Pods-environment.h +38 -0
  220. data/vendor/Pods/Manifest.lock +24 -0
  221. data/vendor/Pods/NSData+MD5Digest/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
  222. data/vendor/Pods/NSData+MD5Digest/NSData+MD5Digest/NSData+MD5Digest.m +39 -0
  223. data/vendor/Pods/NSData+MD5Digest/README.md +11 -0
  224. data/vendor/Pods/Pods-AFNetworking-Private.xcconfig +5 -0
  225. data/vendor/Pods/Pods-AFNetworking-dummy.m +5 -0
  226. data/vendor/Pods/Pods-AFNetworking-prefix.pch +17 -0
  227. data/vendor/Pods/Pods-AFNetworking.xcconfig +1 -0
  228. data/vendor/Pods/Pods-Acknowledgements.markdown +96 -0
  229. data/vendor/Pods/Pods-Acknowledgements.plist +142 -0
  230. data/vendor/Pods/Pods-CocoaLumberjack-Private.xcconfig +5 -0
  231. data/vendor/Pods/Pods-CocoaLumberjack-dummy.m +5 -0
  232. data/vendor/Pods/Pods-CocoaLumberjack-prefix.pch +5 -0
  233. data/vendor/Pods/Pods-CocoaLumberjack.xcconfig +0 -0
  234. data/vendor/Pods/Pods-NSData+MD5Digest-Private.xcconfig +5 -0
  235. data/vendor/Pods/Pods-NSData+MD5Digest-dummy.m +5 -0
  236. data/vendor/Pods/Pods-NSData+MD5Digest-prefix.pch +5 -0
  237. data/vendor/Pods/Pods-NSData+MD5Digest.xcconfig +0 -0
  238. data/vendor/Pods/Pods-Reachability-Private.xcconfig +5 -0
  239. data/vendor/Pods/Pods-Reachability-dummy.m +5 -0
  240. data/vendor/Pods/Pods-Reachability-prefix.pch +5 -0
  241. data/vendor/Pods/Pods-Reachability.xcconfig +1 -0
  242. data/vendor/Pods/Pods-YapDatabase-Private.xcconfig +5 -0
  243. data/vendor/Pods/Pods-YapDatabase-dummy.m +5 -0
  244. data/vendor/Pods/Pods-YapDatabase-prefix.pch +5 -0
  245. data/vendor/Pods/Pods-YapDatabase.xcconfig +1 -0
  246. data/vendor/Pods/Pods-dummy.m +5 -0
  247. data/vendor/Pods/Pods-environment.h +38 -0
  248. data/vendor/Pods/Pods-resources.sh +68 -0
  249. data/vendor/Pods/Pods.bridgesupport +5096 -0
  250. data/vendor/Pods/Pods.xcconfig +5 -0
  251. data/vendor/Pods/Pods.xcodeproj/project.pbxproj +5536 -0
  252. data/vendor/Pods/Reachability/LICENCE.txt +24 -0
  253. data/vendor/Pods/Reachability/README.md +65 -0
  254. data/vendor/Pods/Reachability/Reachability.h +109 -0
  255. data/vendor/Pods/Reachability/Reachability.m +527 -0
  256. data/vendor/Pods/YapDatabase/LICENSE.txt +18 -0
  257. data/vendor/Pods/YapDatabase/README.md +30 -0
  258. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/Internal/YapDatabaseFilteredViewPrivate.h +19 -0
  259. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredView.h +108 -0
  260. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredView.m +175 -0
  261. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewConnection.h +12 -0
  262. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewConnection.m +41 -0
  263. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewTransaction.h +39 -0
  264. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewTransaction.m +966 -0
  265. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/Internal/YapDatabaseFullTextSearchPrivate.h +69 -0
  266. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearch.h +89 -0
  267. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearch.m +146 -0
  268. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchConnection.h +32 -0
  269. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchConnection.m +298 -0
  270. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
  271. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchSnippetOptions.m +95 -0
  272. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchTransaction.h +68 -0
  273. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchTransaction.m +1352 -0
  274. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/Internal/YapDatabaseExtensionPrivate.h +440 -0
  275. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtension.h +15 -0
  276. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtension.m +58 -0
  277. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionConnection.h +11 -0
  278. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionConnection.m +46 -0
  279. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionTransaction.h +11 -0
  280. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionTransaction.m +180 -0
  281. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/Internal/YapDatabaseSecondaryIndexPrivate.h +73 -0
  282. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndex.h +100 -0
  283. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndex.m +149 -0
  284. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexConnection.h +33 -0
  285. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexConnection.m +330 -0
  286. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexSetup.h +33 -0
  287. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexSetup.m +184 -0
  288. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexTransaction.h +58 -0
  289. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexTransaction.m +1166 -0
  290. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewChangePrivate.h +94 -0
  291. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewMappingsPrivate.h +72 -0
  292. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPage.h +36 -0
  293. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPage.mm +296 -0
  294. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPageMetadata.h +27 -0
  295. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPageMetadata.m +28 -0
  296. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPrivate.h +153 -0
  297. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewRangeOptionsPrivate.h +17 -0
  298. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewChange.h +272 -0
  299. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewChange.m +2494 -0
  300. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewMappings.h +825 -0
  301. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewMappings.m +1567 -0
  302. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewRangeOptions.h +330 -0
  303. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewRangeOptions.m +141 -0
  304. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseView.h +186 -0
  305. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseView.m +191 -0
  306. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewConnection.h +115 -0
  307. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewConnection.m +897 -0
  308. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewOptions.h +56 -0
  309. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewOptions.m +27 -0
  310. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.h +447 -0
  311. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.m +4505 -0
  312. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapCache.h +90 -0
  313. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapCache.m +453 -0
  314. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseConnectionState.h +29 -0
  315. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseConnectionState.m +48 -0
  316. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseDefaults.h +37 -0
  317. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseDefaults.m +83 -0
  318. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseLogging.h +158 -0
  319. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseLogging.m +73 -0
  320. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseManager.h +17 -0
  321. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseManager.m +56 -0
  322. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabasePrivate.h +424 -0
  323. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseStatement.h +13 -0
  324. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseStatement.m +26 -0
  325. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseString.h +121 -0
  326. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapMemoryTable.h +74 -0
  327. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapMemoryTable.m +603 -0
  328. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapNull.h +17 -0
  329. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapNull.m +31 -0
  330. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapTouch.h +15 -0
  331. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapTouch.m +31 -0
  332. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapCollectionKey.h +20 -0
  333. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapCollectionKey.m +194 -0
  334. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapDatabaseQuery.h +42 -0
  335. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapDatabaseQuery.m +96 -0
  336. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapSet.h +41 -0
  337. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapSet.m +82 -0
  338. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabase.h +547 -0
  339. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabase.m +2022 -0
  340. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseConnection.h +447 -0
  341. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseConnection.m +3874 -0
  342. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseTransaction.h +541 -0
  343. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseTransaction.m +5282 -0
  344. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.bridgesupport +16 -0
  345. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.h +13 -0
  346. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.m +20 -0
  347. data/yapper.gemspec +24 -0
  348. metadata +458 -0
@@ -0,0 +1,2494 @@
1
+ #import "YapDatabaseViewChange.h"
2
+ #import "YapDatabaseViewChangePrivate.h"
3
+ #import "YapDatabaseViewMappingsPrivate.h"
4
+
5
+
6
+ @implementation YapDatabaseViewSectionChange {
7
+
8
+ /* From YapDatabaseViewChangePrivate.h :
9
+
10
+ @public
11
+
12
+ NSString *group; // immutable
13
+ BOOL isReset; // immutable
14
+
15
+ YapDatabaseViewChangeType type; // mutable during consolidation
16
+
17
+ NSUInteger originalSection; // mutable during pre-processing
18
+ NSUInteger finalSection; // mutable during pre-processing
19
+ */
20
+ }
21
+
22
+ @synthesize type = type;
23
+ @synthesize group = group;
24
+
25
+ + (YapDatabaseViewSectionChange *)insertGroup:(NSString *)group
26
+ {
27
+ YapDatabaseViewSectionChange *op = [[YapDatabaseViewSectionChange alloc] init];
28
+ op->type = YapDatabaseViewChangeInsert;
29
+ op->group = group;
30
+ op->isReset = NO;
31
+ op->originalSection = op->finalSection = NSNotFound;
32
+
33
+ return op;
34
+ }
35
+
36
+ + (YapDatabaseViewSectionChange *)deleteGroup:(NSString *)group
37
+ {
38
+ YapDatabaseViewSectionChange *op = [[YapDatabaseViewSectionChange alloc] init];
39
+ op->type = YapDatabaseViewChangeDelete;
40
+ op->group = group;
41
+ op->isReset = NO;
42
+ op->originalSection = op->finalSection = NSNotFound;
43
+
44
+ return op;
45
+ }
46
+
47
+ + (YapDatabaseViewSectionChange *)resetGroup:(NSString *)group
48
+ {
49
+ YapDatabaseViewSectionChange *op = [[YapDatabaseViewSectionChange alloc] init];
50
+ op->type = YapDatabaseViewChangeDelete;
51
+ op->group = group;
52
+ op->isReset = YES;
53
+ op->originalSection = op->finalSection = NSNotFound;
54
+
55
+ return op;
56
+ }
57
+
58
+ - (id)copyWithZone:(NSZone *)zone
59
+ {
60
+ YapDatabaseViewSectionChange *op = [[YapDatabaseViewSectionChange alloc] init];
61
+ op->type = type;
62
+ op->group = group;
63
+ op->isReset = isReset;
64
+ op->originalSection = originalSection;
65
+ op->finalSection = finalSection;
66
+
67
+ return op;
68
+ }
69
+
70
+ - (NSUInteger)index
71
+ {
72
+ if (type == YapDatabaseViewChangeInsert)
73
+ return finalSection;
74
+ else
75
+ return originalSection;
76
+ }
77
+
78
+ - (NSString *)description
79
+ {
80
+ if (type == YapDatabaseViewChangeInsert)
81
+ {
82
+ if (finalSection == NSNotFound)
83
+ {
84
+ // Internal style (for debugging the processAndConsolidateOperations method)
85
+ return [NSString stringWithFormat:
86
+ @"<YapDatabaseViewSectionChange: Insert group(%@)", group];
87
+ }
88
+ else
89
+ {
90
+ // External style (for debugging UITableView & UICollectionView updates)
91
+ return [NSString stringWithFormat:
92
+ @"<YapDatabaseViewSectionChange: Insert section(%lu) group(%@)>",
93
+ (unsigned long)finalSection, group];
94
+ }
95
+ }
96
+ else // if (type == YapDatabaseViewChangeDelete)
97
+ {
98
+ if (originalSection == NSNotFound)
99
+ {
100
+ // Internal style (for debugging the processAndConsolidateOperations method)
101
+ return [NSString stringWithFormat:
102
+ @"<YapDatabaseViewSectionChange: Delete group(%@)", group];
103
+ }
104
+ else
105
+ {
106
+ // External style (for debugging UITableView & UICollectionView updates)
107
+ return [NSString stringWithFormat:
108
+ @"<YapDatabaseViewSectionChange: Delete section(%lu) group(%@)>",
109
+ (unsigned long)originalSection, group];
110
+ }
111
+ }
112
+ }
113
+
114
+ @end
115
+
116
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
117
+ #pragma mark -
118
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
119
+
120
+ @implementation YapDatabaseViewRowChange {
121
+
122
+ /* From YapDatabaseViewChangePrivate.h :
123
+
124
+ @public
125
+
126
+ id key; // immutable
127
+
128
+ NSString *originalGroup; // immutable
129
+ NSString *finalGroup; // mutable during consolidation
130
+
131
+ YapDatabaseViewChangeType type; // mutable during consolidation
132
+ int columns; // mutable during consolidation
133
+
134
+ NSUInteger opOriginalIndex; // immutable
135
+ NSUInteger opFinalIndex; // immutable
136
+
137
+ NSUInteger originalIndex; // mutable during processing
138
+ NSUInteger finalIndex; // mutable during processing
139
+
140
+ NSUInteger originalSection; // mutable during pre-processing
141
+ NSUInteger finalSection; // mutable during pre-processing
142
+ */
143
+ }
144
+
145
+ @synthesize type = type;
146
+ @synthesize changes = changes;
147
+ @synthesize originalGroup = originalGroup;
148
+ @synthesize finalGroup = finalGroup;
149
+ @synthesize originalIndex = originalIndex;
150
+ @synthesize finalIndex = finalIndex;
151
+ @synthesize originalSection = originalSection;
152
+ @synthesize finalSection = finalSection;
153
+
154
+ - (NSIndexPath *)indexPath
155
+ {
156
+ if (type == YapDatabaseViewChangeInsert) {
157
+ return nil; // <-- You should be using newIndexPath
158
+ }
159
+ else
160
+ {
161
+ #if TARGET_OS_IPHONE
162
+ return [NSIndexPath indexPathForRow:originalIndex inSection:originalSection];
163
+ #else
164
+ NSUInteger indexes[] = {originalSection, originalIndex};
165
+ return [NSIndexPath indexPathWithIndexes:indexes length:2];
166
+ #endif
167
+ }
168
+ }
169
+
170
+ - (NSIndexPath *)newIndexPath
171
+ {
172
+ if (type == YapDatabaseViewChangeDelete || type == YapDatabaseViewChangeUpdate) {
173
+ return nil; // <-- You should be using indexPath
174
+ }
175
+ else {
176
+ #if TARGET_OS_IPHONE
177
+ return [NSIndexPath indexPathForRow:finalIndex inSection:finalSection];
178
+ #else
179
+ NSUInteger indexes[] = {finalSection, finalIndex};
180
+ return [NSIndexPath indexPathWithIndexes:indexes length:2];
181
+ #endif
182
+ }
183
+ }
184
+
185
+ - (id)copyWithZone:(NSZone *)zone
186
+ {
187
+ YapDatabaseViewRowChange *op = [[YapDatabaseViewRowChange alloc] init];
188
+ op->key = key;
189
+ op->originalGroup = originalGroup;
190
+ op->finalGroup = finalGroup;
191
+ op->type = type;
192
+ op->changes = changes;
193
+ op->opOriginalIndex = opOriginalIndex;
194
+ op->opFinalIndex = opFinalIndex;
195
+ op->originalIndex = originalIndex;
196
+ op->finalIndex = finalIndex;
197
+ op->originalSection = originalSection;
198
+ op->finalSection = finalSection;
199
+
200
+ return op;
201
+ }
202
+
203
+ + (YapDatabaseViewRowChange *)insertKey:(id)key inGroup:(NSString *)group atIndex:(NSUInteger)index
204
+ {
205
+ YapDatabaseViewRowChange *op = [[YapDatabaseViewRowChange alloc] init];
206
+ op->type = YapDatabaseViewChangeInsert;
207
+ op->key = key;
208
+ op->changes = YapDatabaseViewChangedObject | YapDatabaseViewChangedMetadata;
209
+
210
+ op->originalGroup = nil; // invalid in insert type
211
+ op->originalIndex = op->opOriginalIndex = NSNotFound; // invalid in insert type
212
+
213
+ op->finalGroup = group;
214
+ op->finalIndex = op->opFinalIndex = index;
215
+
216
+ op->originalSection = op->finalSection = NSNotFound;
217
+
218
+ return op;
219
+ }
220
+
221
+ + (YapDatabaseViewRowChange *)deleteKey:(id)key inGroup:(NSString *)group atIndex:(NSUInteger)index
222
+ {
223
+ YapDatabaseViewRowChange *op = [[YapDatabaseViewRowChange alloc] init];
224
+ op->type = YapDatabaseViewChangeDelete;
225
+ op->key = key;
226
+ op->changes = YapDatabaseViewChangedObject | YapDatabaseViewChangedMetadata;
227
+
228
+ op->originalGroup = group;
229
+ op->originalIndex = op->opOriginalIndex = index;
230
+
231
+ op->finalGroup = nil; // invalid in delete type
232
+ op->finalIndex = op->opFinalIndex = NSNotFound; // invalid in delete type
233
+
234
+ op->originalSection = op->finalSection = NSNotFound;
235
+
236
+ return op;
237
+ }
238
+
239
+ + (YapDatabaseViewRowChange *)updateKey:(id)key changes:(int)flags inGroup:(NSString *)group atIndex:(NSUInteger)index
240
+ {
241
+ YapDatabaseViewRowChange *op = [[YapDatabaseViewRowChange alloc] init];
242
+ op->type = YapDatabaseViewChangeUpdate;
243
+ op->key = key;
244
+ op->changes = flags;
245
+
246
+ op->originalGroup = group;
247
+ op->originalIndex = op->opOriginalIndex = index;
248
+
249
+ op->finalGroup = group;
250
+ op->finalIndex = op->opFinalIndex = index;
251
+
252
+ op->originalSection = op->finalSection = NSNotFound;
253
+
254
+ return op;
255
+ }
256
+
257
+ - (NSString *)description
258
+ {
259
+ if (type == YapDatabaseViewChangeInsert)
260
+ {
261
+ if (finalSection == NSNotFound)
262
+ {
263
+ // Internal style (for debugging the processAndConsolidateOperations method)
264
+ return [NSString stringWithFormat:
265
+ @"<YapDatabaseViewRowChange: Insert pre(~ -> %lu) post(~ -> %lu) group(%@) key(%@)",
266
+ (unsigned long)opFinalIndex, (unsigned long)finalIndex, finalGroup, key];
267
+ }
268
+ else
269
+ {
270
+ // External style (for debugging UITableView & UICollectionView updates)
271
+ return [NSString stringWithFormat:
272
+ @"<YapDatabaseViewRowChange: Insert indexPath(nil) newIndexPath(%lu, %lu) group(%@) key(%@)>",
273
+ (unsigned long)finalSection, (unsigned long)finalIndex, finalGroup, key];
274
+ }
275
+ }
276
+ else if (type == YapDatabaseViewChangeDelete)
277
+ {
278
+ if (originalSection == NSNotFound)
279
+ {
280
+ // Internal style (for debugging the processAndConsolidateOperations method)
281
+ return [NSString stringWithFormat:
282
+ @"<YapDatabaseViewRowChange: Delete pre(%lu -> ~) post(%lu -> ~) group(%@) key(%@)",
283
+ (unsigned long)opOriginalIndex, (unsigned long)originalIndex, originalGroup, key];
284
+ }
285
+ else
286
+ {
287
+ // External style (for debugging UITableView & UICollectionView updates)
288
+ return [NSString stringWithFormat:
289
+ @"<YapDatabaseViewRowChange: Delete indexPath(%lu, %lu) newIndexPath(nil) group(%@) key(%@)>",
290
+ (unsigned long)originalSection, (unsigned long)originalIndex, originalGroup, key];
291
+ }
292
+ }
293
+ else if (type == YapDatabaseViewChangeMove)
294
+ {
295
+ if (originalSection == NSNotFound && finalSection == NSNotFound)
296
+ {
297
+ // Internal style (for debugging the processAndConsolidateOperations method)
298
+ return [NSString stringWithFormat:
299
+ @"<YapDatabaseViewRowChange: Move pre(%lu -> %lu) post(%lu -> %lu) group(%@ -> %@) key(%@)",
300
+ (unsigned long)opOriginalIndex, (unsigned long)opFinalIndex,
301
+ (unsigned long)originalIndex, (unsigned long)finalIndex,
302
+ originalGroup, finalGroup, key];
303
+ }
304
+ else
305
+ {
306
+ // External style (for debugging UITableView & UICollectionView updates)
307
+ return [NSString stringWithFormat:
308
+ @"<YapDatabaseViewRowChange: Move indexPath(%lu, %lu) newIndexPath(%lu, %lu) group(%@ -> %@) key(%@)",
309
+ (unsigned long)originalSection, (unsigned long)originalIndex,
310
+ (unsigned long)finalSection, (unsigned long)finalIndex,
311
+ originalGroup, finalGroup, key];
312
+ }
313
+ }
314
+ else // if (type == YapDatabaseViewChangeUpdate)
315
+ {
316
+ if (originalSection == NSNotFound && finalSection == NSNotFound)
317
+ {
318
+ // Internal style (for debugging the processAndConsolidateOperations method)
319
+ return [NSString stringWithFormat:
320
+ @"<YapDatabaseViewRowChange: Update pre(%lu) post(%lu -> %lu) group(%@) key(%@)",
321
+ (unsigned long)opOriginalIndex,
322
+ (unsigned long)originalIndex, (unsigned long)finalIndex, originalGroup, key];
323
+ }
324
+ else
325
+ {
326
+ // External style (for debugging UITableView & UICollectionView updates)
327
+ return [NSString stringWithFormat:
328
+ @"<YapDatabaseViewRowChange: Update indexPath(%lu, %lu) group(%@) key(%@)",
329
+ (unsigned long)originalSection, (unsigned long)originalIndex, originalGroup, key];
330
+ }
331
+ }
332
+ }
333
+
334
+ @end
335
+
336
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
337
+ #pragma mark -
338
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
339
+
340
+ @implementation YapDatabaseViewChange
341
+
342
+ /**
343
+ * During a read-write transaction, every modification to the view results in one or more
344
+ * YapDatabaseViewSectionChange or YapDatabaseViewRowChange objects being appended to an internal array.
345
+ *
346
+ * At the end of the read-write transaction we have a big list of changes that have occurred.
347
+ *
348
+ * This method takes the original list and pre-processes it:
349
+ *
350
+ * - splits the array in sectionChanges and rowChanges
351
+ * - removes any items from the changes array that don't concern us (group not in mappings)
352
+ * - injects extra changes if there are cell dependencies configured in the mappings
353
+ **/
354
+ + (void)preProcessChanges:(NSArray *)changes
355
+ withOriginalMappings:(YapDatabaseViewMappings *)originalMappings
356
+ finalMappings:(YapDatabaseViewMappings *)finalMappings
357
+ andGetSectionChanges:(NSMutableArray **)sectionChangesPtr
358
+ rowChanges:(NSMutableArray **)rowChangesPtr
359
+ {
360
+
361
+ // We remove any items from the changes array that don't concern us.
362
+ // For example, if mappings only contain groupA & groupB,
363
+ // then we can ignore changes in groupC.
364
+ //
365
+ // We also may need to inject extra changes.
366
+ // The user may specify, as a configuration option within mappings,
367
+ // the the drawing of cells has an dependency upon neighboring cells.
368
+
369
+ NSMutableArray *sectionChanges = [NSMutableArray arrayWithCapacity:1];
370
+ NSMutableArray *rowChanges = [NSMutableArray arrayWithCapacity:[changes count]];
371
+
372
+ NSSet *groups = [NSSet setWithArray:[originalMappings allGroups]];
373
+
374
+ NSMutableDictionary *counts = [originalMappings counts];
375
+ NSDictionary *dependencies = [originalMappings dependencies];
376
+
377
+ for (id change in changes)
378
+ {
379
+ if ([change isKindOfClass:[YapDatabaseViewSectionChange class]])
380
+ {
381
+ __unsafe_unretained YapDatabaseViewSectionChange *immutableSectionChange =
382
+ (YapDatabaseViewSectionChange *)change;
383
+
384
+ if (immutableSectionChange->type == YapDatabaseViewChangeDelete)
385
+ {
386
+ if ([groups containsObject:immutableSectionChange->group])
387
+ {
388
+ YapDatabaseViewSectionChange *sectionChange = [immutableSectionChange copy];
389
+ [sectionChanges addObject:sectionChange];
390
+
391
+ if (sectionChange->isReset)
392
+ {
393
+ // Special case processing.
394
+ //
395
+ // Most of the time, groups are deleted because the last key within the group was removed.
396
+ // Thus a regular section delete is accompanied by all the corresponding row deletes.
397
+ // But if the user invokes:
398
+ // - removeAllObjectsInAllCollections
399
+ //
400
+ // then we get a section delete that isn't accompanied by the corresponding row deletes.
401
+ // This operation is flagged via isReset.
402
+ //
403
+ // So here's what we do in this situation:
404
+ // - remove any previous row changes within the group
405
+ // - manually inject all the proper row deletes
406
+
407
+ __unsafe_unretained YapDatabaseViewRowChange *rowChange;
408
+
409
+ for (NSUInteger i = [rowChanges count]; i > 0; i--)
410
+ {
411
+ rowChange = [rowChanges objectAtIndex:(i-1)];
412
+
413
+ if (rowChange->type == YapDatabaseViewChangeDelete)
414
+ {
415
+ if ([rowChange->originalGroup isEqualToString:sectionChange->group])
416
+ {
417
+ [rowChanges removeObjectAtIndex:(i-1)];
418
+ }
419
+ }
420
+ else // YapDatabaseViewChangeInsert || YapDatabaseViewChangeUpdate
421
+ {
422
+ if ([rowChange->finalGroup isEqualToString:sectionChange->group])
423
+ {
424
+ [rowChanges removeObjectAtIndex:(i-1)];
425
+ }
426
+ }
427
+ }
428
+
429
+ YapDatabaseViewRangePosition rangePosition =
430
+ [originalMappings rangePositionForGroup:sectionChange->group];
431
+
432
+ NSUInteger prevRowOffset = rangePosition.offsetFromBeginning;
433
+ NSUInteger prevRowCount = rangePosition.length;
434
+
435
+ while (prevRowCount > 0)
436
+ {
437
+ YapDatabaseViewRowChange *rowChange =
438
+ [YapDatabaseViewRowChange deleteKey:nil
439
+ inGroup:sectionChange->group
440
+ atIndex:(prevRowOffset+prevRowCount-1)];
441
+
442
+ [rowChanges addObject:rowChange];
443
+ prevRowCount--;
444
+ }
445
+
446
+ [counts setObject:@(0) forKey:sectionChange->group];
447
+ }
448
+ }
449
+ }
450
+ else if (immutableSectionChange->type == YapDatabaseViewChangeInsert)
451
+ {
452
+ if ([groups containsObject:immutableSectionChange->group])
453
+ {
454
+ YapDatabaseViewSectionChange *sectionChange = [immutableSectionChange copy];
455
+ [sectionChanges addObject:sectionChange];
456
+ }
457
+ }
458
+ }
459
+ else
460
+ {
461
+ __unsafe_unretained YapDatabaseViewRowChange *immutableRowChange = (YapDatabaseViewRowChange *)change;
462
+
463
+ __unsafe_unretained NSString *group = nil;
464
+ NSUInteger groupCount = 0;
465
+ NSUInteger groupIndex = 0;
466
+ BOOL wasDelete = 0;
467
+
468
+ if (immutableRowChange->type == YapDatabaseViewChangeDelete)
469
+ {
470
+ if ([groups containsObject:immutableRowChange->originalGroup])
471
+ {
472
+ YapDatabaseViewRowChange *rowChange = [immutableRowChange copy];
473
+ [rowChanges addObject:rowChange];
474
+
475
+ group = rowChange->originalGroup;
476
+ groupIndex = rowChange->originalIndex;
477
+
478
+ groupCount = [[counts objectForKey:group] unsignedIntegerValue];
479
+ groupCount--;
480
+ [counts setObject:@(groupCount) forKey:group];
481
+
482
+ wasDelete = YES;
483
+ }
484
+ }
485
+ else if (immutableRowChange->type == YapDatabaseViewChangeInsert)
486
+ {
487
+ if ([groups containsObject:immutableRowChange->finalGroup])
488
+ {
489
+ YapDatabaseViewRowChange *rowChange = [immutableRowChange copy];
490
+ [rowChanges addObject:rowChange];
491
+
492
+ group = rowChange->finalGroup;
493
+ groupIndex = rowChange->finalIndex;
494
+
495
+ groupCount = [[counts objectForKey:group] unsignedIntegerValue];
496
+ groupCount++;
497
+ [counts setObject:@(groupCount) forKey:group];
498
+ }
499
+ }
500
+ else if (immutableRowChange->type == YapDatabaseViewChangeUpdate)
501
+ {
502
+ if ([groups containsObject:immutableRowChange->originalGroup])
503
+ {
504
+ YapDatabaseViewRowChange *rowChange = [immutableRowChange copy];
505
+ [rowChanges addObject:rowChange];
506
+
507
+ group = rowChange->originalGroup;
508
+ groupIndex = rowChange->originalIndex;
509
+
510
+ groupCount = [[counts objectForKey:group] unsignedIntegerValue];
511
+ }
512
+ }
513
+
514
+ // Handle cell drawing dependencies.
515
+
516
+ NSSet *dependenciesForGroup = [dependencies objectForKey:group];
517
+ for (NSNumber *offsetNum in dependenciesForGroup)
518
+ {
519
+ NSInteger offset = [offsetNum integerValue] * -1;
520
+ NSUInteger dependencyIndex = groupCount;
521
+
522
+ if (offset > 0)
523
+ {
524
+ dependencyIndex = groupIndex + offset;
525
+ if (wasDelete)
526
+ dependencyIndex--;
527
+ }
528
+ else if ((offset < 0) && (-1*offset <= groupIndex))
529
+ {
530
+ dependencyIndex = groupIndex + offset;
531
+ }
532
+
533
+ if (dependencyIndex < groupCount)
534
+ {
535
+ int changes = YapDatabaseViewChangedDependency;
536
+
537
+ YapDatabaseViewRowChange *rowChange =
538
+ [YapDatabaseViewRowChange updateKey:nil changes:changes inGroup:group atIndex:dependencyIndex];
539
+
540
+ [rowChanges addObject:rowChange];
541
+ }
542
+ }
543
+
544
+ }
545
+ }
546
+
547
+ if (sectionChangesPtr) *sectionChangesPtr = sectionChanges;
548
+ if (rowChangesPtr) *rowChangesPtr = rowChanges;
549
+ }
550
+
551
+
552
+ /**
553
+ * During a read-write transaction, every modification to the view results in one or more
554
+ * YapDatabaseViewSectionChange or YapDatabaseViewRowChange objects being appended to an internal array.
555
+ *
556
+ * At the end of the read-write transaction we have a big list of changes that have occurred.
557
+ * But each change represents the change state at the moment the change took place.
558
+ * And we ultimately need to get the change state to reflect the original and/or final position of the change.
559
+ *
560
+ * This method takes a list of YapDatabaseViewRowChange objects and processes them to
561
+ * properly calculate and set the original and/or final index of each row change.
562
+ **/
563
+ + (void)processRowChanges:(NSMutableArray *)changes
564
+ {
565
+ // Each YapDatabaseViewRowChange object is one of:
566
+ //
567
+ // - Row insert
568
+ // - Row delete
569
+ // - Row update
570
+ //
571
+ // If a row is moved, then it is represented in the given array as a delete followed by an insert.
572
+ //
573
+ // Each YapDatabaseViewRowChange represents the change state AT THE MOMENT THE CHANGE TOOK PLACE.
574
+ // This is critically important to understand.
575
+ //
576
+ // For example, imagine the following array:
577
+ // @[
578
+ // <RowInsert group=fruit index=0 key=apple>
579
+ // <RowDelete group=fruit index=7 key=carrot>
580
+ // <RowInsert group=fruit index=2 key=blueberry>
581
+ // <RowInsert group=fruit index=2 key=banana>
582
+ // ]
583
+ //
584
+ // What was the original index of 'carrot'?
585
+ // What is the final index of 'blueberry'?
586
+ //
587
+ // The original index of 'carrot' was 6, because the earlier insert of 'apple' pushed its index upward.
588
+ // The final index of 'blueberry' is 3, because a later insert of 'banana' pushed its index upward.
589
+ //
590
+ // As you might guess, there are a number of edge cases to watch out for.
591
+ // Ultimately, the algorithm is fairly straight-forward, but the code gets a little verbose.
592
+ //
593
+ // There are are crap ton of unit tests for this code...
594
+ //
595
+ // Please see the UNIT TESTS for a bunch of examples that may shed additional light on the algorithm:
596
+ // TestViewChangeLogic.m
597
+
598
+ NSUInteger i;
599
+ NSUInteger j;
600
+
601
+ // First we enumerate the items BACKWARDS,
602
+ // and update the ORIGINAL values.
603
+
604
+ NSUInteger count = [changes count];
605
+
606
+ for (i = count; i > 0; i--)
607
+ {
608
+ YapDatabaseViewRowChange *change = [changes objectAtIndex:(i-1)];
609
+
610
+ if (change->type == YapDatabaseViewChangeDelete)
611
+ {
612
+ // A DELETE operation may affect the ORIGINAL index value of operations that occurred AFTER it,
613
+ // IF the later operation occurs at a greater or equal index value. ( +1 )
614
+
615
+ for (j = i; j < count; j++)
616
+ {
617
+ YapDatabaseViewRowChange *laterChange = [changes objectAtIndex:j];
618
+
619
+ if (laterChange->type == YapDatabaseViewChangeDelete ||
620
+ laterChange->type == YapDatabaseViewChangeUpdate)
621
+ {
622
+ if (laterChange->originalIndex >= change->opOriginalIndex &&
623
+ [laterChange->originalGroup isEqualToString:change->originalGroup])
624
+ {
625
+ laterChange->originalIndex += 1;
626
+ }
627
+ }
628
+ }
629
+ }
630
+ else if (change->type == YapDatabaseViewChangeInsert)
631
+ {
632
+ // An INSERT operation may affect the ORIGINAL index value of operations that occurred AFTER it,
633
+ // IF the later operation occurs at a greater or equal index value. ( -1 )
634
+
635
+ for (j = i; j < count; j++)
636
+ {
637
+ YapDatabaseViewRowChange *laterChange = [changes objectAtIndex:j];
638
+
639
+ if (laterChange->type == YapDatabaseViewChangeDelete ||
640
+ laterChange->type == YapDatabaseViewChangeUpdate)
641
+ {
642
+ if (laterChange->originalIndex >= change->opFinalIndex &&
643
+ [laterChange->originalGroup isEqualToString:change->finalGroup])
644
+ {
645
+ laterChange->originalIndex -= 1;
646
+ }
647
+ }
648
+ }
649
+ }
650
+ }
651
+
652
+ // Next we enumerate the items FORWARDS,
653
+ // and update the FINAL values.
654
+
655
+ for (i = 1; i < count; i++)
656
+ {
657
+ YapDatabaseViewRowChange *change = [changes objectAtIndex:i];
658
+
659
+ if (change->type == YapDatabaseViewChangeDelete)
660
+ {
661
+ // A DELETE operation may affect the FINAL index value of operations that occurred BEFORE it,
662
+ // IF the earlier operation occurs at a greater (but not equal) index value. ( -1 )
663
+
664
+ for (j = i; j > 0; j--)
665
+ {
666
+ YapDatabaseViewRowChange *earlierChange = [changes objectAtIndex:(j-1)];
667
+
668
+ if (earlierChange->type == YapDatabaseViewChangeInsert ||
669
+ earlierChange->type == YapDatabaseViewChangeUpdate )
670
+ {
671
+ if (earlierChange->finalIndex > change->opOriginalIndex &&
672
+ [earlierChange->finalGroup isEqualToString:change->originalGroup])
673
+ {
674
+ earlierChange->finalIndex -= 1;
675
+ }
676
+ }
677
+ }
678
+ }
679
+ else if (change->type == YapDatabaseViewChangeInsert)
680
+ {
681
+ // An INSERT operation may affect the FINAL index value of operations that occurred BEFORE it,
682
+ // IF the earlier operation occurs at a greater index value ( +1 )
683
+
684
+ for (j = i; j > 0; j--)
685
+ {
686
+ YapDatabaseViewRowChange *earlierChange = [changes objectAtIndex:(j-1)];
687
+
688
+ if (earlierChange->type == YapDatabaseViewChangeInsert ||
689
+ earlierChange->type == YapDatabaseViewChangeUpdate)
690
+ {
691
+ if (earlierChange->finalIndex >= change->opFinalIndex &&
692
+ [earlierChange->finalGroup isEqualToString:change->finalGroup])
693
+ {
694
+ earlierChange->finalIndex += 1;
695
+ }
696
+ }
697
+ }
698
+ }
699
+ }
700
+ }
701
+
702
+ /**
703
+ * This method consolidates multiple changes to the same row into a single change that reflects
704
+ * the original and final position of each changed row.
705
+ **/
706
+ + (void)consolidateRowChanges:(NSMutableArray *)changes
707
+ {
708
+ NSUInteger i;
709
+ NSUInteger j;
710
+
711
+ NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
712
+
713
+ i = 0;
714
+ while (i < [changes count])
715
+ {
716
+ YapDatabaseViewRowChange *firstChangeForKey = [changes objectAtIndex:i];
717
+ YapDatabaseViewRowChange *mostRecentChangeForKey = firstChangeForKey;
718
+
719
+ // Find later operations with the same key
720
+
721
+ for (j = i+1; j < [changes count]; j++)
722
+ {
723
+ YapDatabaseViewRowChange *laterChange = [changes objectAtIndex:j];
724
+ BOOL changesAreForSameKey = NO;
725
+
726
+ if (firstChangeForKey->key && laterChange->key)
727
+ {
728
+ // Compare keys
729
+
730
+ if ([laterChange->key isEqual:firstChangeForKey->key])
731
+ changesAreForSameKey = YES;
732
+ }
733
+ else
734
+ {
735
+ // Compare indexes & groups
736
+ //
737
+ // This technique is used if one of the keys is nil,
738
+ // and applies to situations where one of the changes is an Update with a nil key,
739
+ // which was injected during pre-processing due to cell drawing dependencies.
740
+
741
+ if (mostRecentChangeForKey->type == YapDatabaseViewChangeUpdate)
742
+ {
743
+ if (laterChange->type == YapDatabaseViewChangeUpdate ||
744
+ laterChange->type == YapDatabaseViewChangeDelete)
745
+ {
746
+ if (mostRecentChangeForKey->originalIndex == laterChange->originalIndex &&
747
+ [mostRecentChangeForKey->originalGroup isEqualToString:laterChange->originalGroup]) {
748
+ changesAreForSameKey = YES;
749
+ }
750
+ }
751
+ }
752
+ else if (mostRecentChangeForKey->type == YapDatabaseViewChangeInsert)
753
+ {
754
+ if (laterChange->type == YapDatabaseViewChangeUpdate)
755
+ {
756
+ if (mostRecentChangeForKey->originalIndex == laterChange->originalIndex &&
757
+ [mostRecentChangeForKey->originalGroup isEqualToString:laterChange->originalGroup]) {
758
+ changesAreForSameKey = YES;
759
+ }
760
+ }
761
+ }
762
+
763
+ if (changesAreForSameKey)
764
+ {
765
+ if (mostRecentChangeForKey->key == nil)
766
+ mostRecentChangeForKey->key = laterChange->key;
767
+ else
768
+ laterChange->key = mostRecentChangeForKey->key;
769
+ }
770
+ }
771
+
772
+ if (changesAreForSameKey)
773
+ {
774
+ firstChangeForKey->changes |= laterChange->changes;
775
+ [indexSet addIndex:j];
776
+
777
+ mostRecentChangeForKey = laterChange;
778
+ }
779
+ }
780
+
781
+ if ([indexSet count] == 0)
782
+ {
783
+ i++; // continue;
784
+ }
785
+ else
786
+ {
787
+ YapDatabaseViewRowChange *lastChangeForKey = [changes objectAtIndex:[indexSet lastIndex]];
788
+
789
+ if (firstChangeForKey->type == YapDatabaseViewChangeDelete)
790
+ {
791
+ if (lastChangeForKey->type == YapDatabaseViewChangeDelete)
792
+ {
793
+ // Delete + Insert + ... + Delete
794
+ //
795
+ // All operations except the first are no-ops
796
+
797
+ [changes removeObjectsAtIndexes:indexSet];
798
+ i++;
799
+ }
800
+ else if (lastChangeForKey->type == YapDatabaseViewChangeInsert)
801
+ {
802
+ // Delete + Insert = Move
803
+ //
804
+ // This is always a move operation.
805
+ // Even if the final location hasn't ultimately changed, we still want to treat it as a move.
806
+ // Only a true update, where the index never budged, can be emitted as an update.
807
+ //
808
+ // If we attempt to consolidate this into an update,
809
+ // then the tableView/collectionView will offset the update's index
810
+ // based on insertions & deletions at smaller indexes,
811
+ // and may ultimately update the wrong cell.
812
+
813
+ firstChangeForKey->type = YapDatabaseViewChangeMove;
814
+ firstChangeForKey->finalIndex = lastChangeForKey->finalIndex;
815
+ firstChangeForKey->finalGroup = lastChangeForKey->finalGroup;
816
+
817
+ [changes removeObjectsAtIndexes:indexSet];
818
+ i++;
819
+ }
820
+ else if (lastChangeForKey->type == YapDatabaseViewChangeUpdate)
821
+ {
822
+ // Delete + Insert + ... + Update = Move
823
+ //
824
+ // This is always a move operation.
825
+ // Even if the final location hasn't ultimately changed, we still want to treat it as a move.
826
+ // Only a true update, where the index never budged, can be emitted as an update.
827
+ //
828
+ // If we attempt to consolidate this into an update,
829
+ // then the tableView/collectionView will offset the update's index
830
+ // based on insertions & deletions at smaller indexes,
831
+ // and may ultimately update the wrong cell.
832
+
833
+ firstChangeForKey->type = YapDatabaseViewChangeMove;
834
+ firstChangeForKey->finalIndex = lastChangeForKey->finalIndex;
835
+ firstChangeForKey->finalGroup = lastChangeForKey->finalGroup;
836
+
837
+ [changes removeObjectsAtIndexes:indexSet];
838
+ i++;
839
+ }
840
+ }
841
+ else if (firstChangeForKey->type == YapDatabaseViewChangeInsert)
842
+ {
843
+ if (lastChangeForKey->type == YapDatabaseViewChangeDelete)
844
+ {
845
+ // Insert + Delete
846
+ //
847
+ // All operations are no-ops (& i remains the same)
848
+
849
+ [changes removeObjectsAtIndexes:indexSet];
850
+ [changes removeObjectAtIndex:i];
851
+
852
+ }
853
+ else if (lastChangeForKey->type == YapDatabaseViewChangeInsert)
854
+ {
855
+ // Insert + Delete + ... + Insert
856
+ //
857
+ // All operations except the last are no-ops.
858
+
859
+ firstChangeForKey->finalIndex = lastChangeForKey->finalIndex;
860
+ firstChangeForKey->finalGroup = lastChangeForKey->finalGroup;
861
+
862
+ [changes removeObjectsAtIndexes:indexSet];
863
+ i++;
864
+ }
865
+ else // if (lastChangeForKey->type == YapDatabaseViewChangeUpdate)
866
+ {
867
+ // Insert + Update
868
+ //
869
+ // This is still an insert, but the final location may have changed.
870
+
871
+ firstChangeForKey->finalIndex = lastChangeForKey->finalIndex;
872
+ firstChangeForKey->finalGroup = lastChangeForKey->finalGroup;
873
+
874
+ [changes removeObjectsAtIndexes:indexSet];
875
+ i++;
876
+ }
877
+ }
878
+ else if (firstChangeForKey->type == YapDatabaseViewChangeUpdate)
879
+ {
880
+ if (lastChangeForKey->type == YapDatabaseViewChangeDelete)
881
+ {
882
+ // Update + Delete
883
+ //
884
+ // This is ultimately a Delete.
885
+ // We need to be sure to use the original original index.
886
+
887
+ firstChangeForKey->type = YapDatabaseViewChangeDelete;
888
+
889
+ [changes removeObjectsAtIndexes:indexSet];
890
+ i++;
891
+ }
892
+ else if (lastChangeForKey->type == YapDatabaseViewChangeInsert)
893
+ {
894
+ // Update + Delete + ... + Insert = Move
895
+ //
896
+ // This is always a move operation.
897
+ // Even if the final location hasn't ultimately changed, we still want to treat it as a move.
898
+ // Only a true update, where the index never budged, can be emitted as an update.
899
+ //
900
+ // If we attempt to consolidate this into an update,
901
+ // then the tableView/collectionView will offset the update's index
902
+ // based on insertions & deletions at smaller indexes,
903
+ // and may ultimately update the wrong cell.
904
+
905
+ firstChangeForKey->type = YapDatabaseViewChangeMove;
906
+ firstChangeForKey->finalIndex = lastChangeForKey->finalIndex;
907
+ firstChangeForKey->finalGroup = lastChangeForKey->finalGroup;
908
+
909
+ [changes removeObjectsAtIndexes:indexSet];
910
+ i++;
911
+ }
912
+ else // if (lastChangeForKey->type == YapDatabaseViewChangeUpdate)
913
+ {
914
+ // Update + ... + Update
915
+ //
916
+ // This is either an Update or a Move.
917
+ // Only a true update, where the index never budged, can be emitted as an update.
918
+ //
919
+ // So we scan all the changes, and if every single one is an update, then we can emit an update.
920
+
921
+ __block BOOL isTrueUpdate = YES;
922
+
923
+ [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
924
+
925
+ YapDatabaseViewRowChange *changeForKey = [changes objectAtIndex:idx];
926
+
927
+ if (changeForKey->type != YapDatabaseViewChangeUpdate)
928
+ {
929
+ isTrueUpdate = NO;
930
+ *stop = YES;
931
+ }
932
+ }];
933
+
934
+ if (isTrueUpdate)
935
+ {
936
+ // = Update
937
+
938
+ [changes removeObjectsAtIndexes:indexSet];
939
+ i++;
940
+ }
941
+ else
942
+ {
943
+ // = Move
944
+ //
945
+ // The final location comes from the last update
946
+
947
+ firstChangeForKey->type = YapDatabaseViewChangeMove;
948
+ firstChangeForKey->finalIndex = lastChangeForKey->finalIndex;
949
+ firstChangeForKey->finalGroup = lastChangeForKey->finalGroup;
950
+
951
+ [changes removeObjectsAtIndexes:indexSet];
952
+ i++;
953
+ }
954
+ }
955
+ }
956
+
957
+ [indexSet removeAllIndexes];
958
+
959
+ } // ([indexSet count] > 0)
960
+
961
+ } // while (i < [changes count])
962
+ }
963
+
964
+ /**
965
+ * This method consolidates multiple changes to the same section into a single change.
966
+ **/
967
+ + (void)consolidateSectionChanges:(NSMutableArray *)changes
968
+ {
969
+ NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
970
+
971
+ NSUInteger i = 0;
972
+ while (i < [changes count])
973
+ {
974
+ YapDatabaseViewSectionChange *firstSectionChangeForGroup = [changes objectAtIndex:i];
975
+
976
+ // Find later operations with the same group
977
+
978
+ for (NSUInteger j = i+1; j < [changes count]; j++)
979
+ {
980
+ YapDatabaseViewSectionChange *laterSectionChange = [changes objectAtIndex:j];
981
+
982
+ if ([laterSectionChange->group isEqualToString:firstSectionChangeForGroup->group])
983
+ {
984
+ [indexSet addIndex:j];
985
+ }
986
+ }
987
+
988
+ if ([indexSet count] == 0)
989
+ {
990
+ i++;
991
+ }
992
+ else
993
+ {
994
+ YapDatabaseViewSectionChange *lastSectionChangeForGroup = [changes objectAtIndex:[indexSet lastIndex]];
995
+
996
+ if (firstSectionChangeForGroup->type == YapDatabaseViewChangeDelete)
997
+ {
998
+ if (lastSectionChangeForGroup->type == YapDatabaseViewChangeDelete)
999
+ {
1000
+ // Delete + Insert + ... + Delete
1001
+ //
1002
+ // All operations except the first are no-ops
1003
+
1004
+ [changes removeObjectsAtIndexes:indexSet];
1005
+ i++;
1006
+ }
1007
+ else // if (lastSectionChangeForGroup->type == YapDatabaseViewChangeInsert)
1008
+ {
1009
+ // Delete + Insert
1010
+ //
1011
+ // All operations are no-ops (& i remains the same)
1012
+
1013
+ [changes removeObjectsAtIndexes:indexSet];
1014
+ [changes removeObjectAtIndex:i];
1015
+ }
1016
+ }
1017
+ else if (firstSectionChangeForGroup->type == YapDatabaseViewChangeInsert)
1018
+ {
1019
+ if (lastSectionChangeForGroup->type == YapDatabaseViewChangeDelete)
1020
+ {
1021
+ // Insert + Delete
1022
+ //
1023
+ // All operations are no-ops (& i remains the same)
1024
+
1025
+ [changes removeObjectsAtIndexes:indexSet];
1026
+ [changes removeObjectAtIndex:i];
1027
+ }
1028
+ else if (lastSectionChangeForGroup->type == YapDatabaseViewChangeInsert)
1029
+ {
1030
+ // Insert + Delete + ... + Insert
1031
+ //
1032
+ // All operations except the first are no-ops.
1033
+
1034
+ [changes removeObjectsAtIndexes:indexSet];
1035
+ i++;
1036
+ }
1037
+ }
1038
+ }
1039
+ }
1040
+ }
1041
+
1042
+ /**
1043
+ * This method applies the given mappings to the processed list of row changes.
1044
+ * Based upon the configuration of the mappings, it will
1045
+ *
1046
+ * - filter items that are excluded based on per-group range configurations
1047
+ * - alter indexes based on per-group range configurations
1048
+ * - reverse indexes based on per-group reversal settings
1049
+ **/
1050
+ + (void)postProcessAndFilterRowChanges:(NSMutableArray *)rowChanges
1051
+ withOriginalMappings:(YapDatabaseViewMappings *)originalMappings
1052
+ finalMappings:(YapDatabaseViewMappings *)finalMappings
1053
+ {
1054
+
1055
+ // The user may have various range options set for each section/group.
1056
+ // For example:
1057
+ //
1058
+ // The user has a hard range on group "fiction" in the "bookSalesRank" view in order to display the top 20.
1059
+ // So any items outside of that range must be filtered.
1060
+
1061
+ NSDictionary *rangeOptions = [finalMappings rangeOptions];
1062
+ BOOL rangeOptionsChanged = YES;
1063
+
1064
+ // Note: The rangeOptions are the same between originalMappings & finalMappings.
1065
+
1066
+ for (NSString *group in rangeOptions)
1067
+ {
1068
+ YapDatabaseViewRangeOptions *rangeOpts = [rangeOptions objectForKey:group];
1069
+
1070
+ NSUInteger originalGroupCount = [originalMappings fullCountForGroup:group];
1071
+ NSUInteger finalGroupCount = [finalMappings fullCountForGroup:group];
1072
+
1073
+ YapDatabaseViewPin pin = rangeOpts.pin;
1074
+
1075
+ //
1076
+ // STEP 1 : Calculate the originalRange & finalRange
1077
+ //
1078
+
1079
+ NSUInteger originalRangeLength = rangeOpts.length;
1080
+ NSUInteger originalRangeOffset = rangeOpts.offset;
1081
+
1082
+ NSUInteger originalRangeMin;
1083
+ NSUInteger originalRangeMax;
1084
+
1085
+ if (pin == YapDatabaseViewBeginning)
1086
+ {
1087
+ originalRangeMin = originalRangeOffset;
1088
+ originalRangeMax = originalRangeOffset + originalRangeLength;
1089
+ }
1090
+ else // if (pin == YapDatabaseViewEnd)
1091
+ {
1092
+ if (originalRangeOffset < originalGroupCount) {
1093
+ originalRangeMax = originalGroupCount - originalRangeOffset;
1094
+ originalRangeMin = originalRangeMax - originalRangeLength;
1095
+ }
1096
+ else {
1097
+ originalRangeMax = 0;
1098
+ originalRangeMin = 0;
1099
+ }
1100
+ }
1101
+
1102
+ NSUInteger finalRangeLength;
1103
+ NSUInteger finalRangeOffset;
1104
+
1105
+ NSUInteger finalRangeMin;
1106
+ NSUInteger finalRangeMax;
1107
+
1108
+ NSUInteger flexibleRangeNonPinSideDeleteDiff = 0;
1109
+ NSUInteger flexibleRangePinSideInsertDiff = 0;
1110
+ NSUInteger flexibleRangeNonPinSideInsertDiff = 0;
1111
+
1112
+ if (rangeOpts.isFixedRange)
1113
+ {
1114
+ // FIXED Range:
1115
+ //
1116
+ // - The length is fixed. It only shrinks if we run out of keys.
1117
+ // - The offset never changes.
1118
+ //
1119
+ // If pinned to the BEGINNING:
1120
+ // The offset represents how far the beginning of the range is from the beginning of the group.
1121
+ //
1122
+ // Group : <---------------------------------------->
1123
+ // Range : <--------->
1124
+ // Offset: <----------------> (pin == YapDatabaseViewBeginning)
1125
+ //
1126
+ // If pinned to the END:
1127
+ // The offset represents how far the end of the range is from the end of the group.
1128
+ //
1129
+ // Group : <---------------------------------------->
1130
+ // Range : <--------->
1131
+ // Offset: <-------------> (pin == YapDatabaseViewEnd)
1132
+
1133
+ finalRangeOffset = originalRangeOffset;
1134
+
1135
+ NSUInteger maxFinalRangeLength;
1136
+ if (finalGroupCount > finalRangeOffset)
1137
+ maxFinalRangeLength = finalGroupCount - finalRangeOffset;
1138
+ else
1139
+ maxFinalRangeLength = 0;
1140
+
1141
+ finalRangeLength = MIN(rangeOpts.maxLength, maxFinalRangeLength);
1142
+
1143
+ if (pin == YapDatabaseViewBeginning)
1144
+ {
1145
+ finalRangeMin = finalRangeOffset;
1146
+ finalRangeMax = finalRangeOffset + finalRangeLength;
1147
+ }
1148
+ else // if (pin == YapDatabaseViewEnd)
1149
+ {
1150
+ if (finalRangeOffset < finalGroupCount) {
1151
+ finalRangeMax = finalGroupCount - finalRangeOffset;
1152
+ finalRangeMin = finalRangeMax - finalRangeLength;
1153
+ }
1154
+ else {
1155
+ finalRangeMax = 0;
1156
+ finalRangeMin = 0;
1157
+ }
1158
+ }
1159
+ }
1160
+ else // if (rangeOpts.isFlexibleRange)
1161
+ {
1162
+ // FLEXIBLE Range:
1163
+ //
1164
+ // The length changes as items are inserted and deleted with the range boundary.
1165
+ // The offset changes as items are inserted and deleted between the range and its pinned end.
1166
+
1167
+ finalRangeMin = originalRangeMin;
1168
+ finalRangeMax = originalRangeMax;
1169
+
1170
+ BOOL finalRangeWasEmpty = ((finalRangeMax - finalRangeMin) == 0);
1171
+
1172
+ YapDatabaseViewGrowOptions growOptions = rangeOpts.growOptions;
1173
+
1174
+ for (YapDatabaseViewRowChange *rowChange in rowChanges)
1175
+ {
1176
+ if (rowChange->type == YapDatabaseViewChangeDelete || rowChange->type == YapDatabaseViewChangeMove)
1177
+ {
1178
+ if ([rowChange->originalGroup isEqualToString:group])
1179
+ {
1180
+ // A DELETE operation can:
1181
+ // - decrement the location of the final range
1182
+ // - decrease the length of the final range
1183
+
1184
+ if (rowChange->opOriginalIndex < finalRangeMin)
1185
+ {
1186
+ finalRangeMin -= 1;
1187
+ }
1188
+ if (rowChange->opOriginalIndex < finalRangeMax)
1189
+ {
1190
+ finalRangeMax -= 1;
1191
+ }
1192
+ }
1193
+
1194
+ }// fi (rowChange->type == YapDatabaseViewChangeDelete || rowChange->type == YapDatabaseViewChangeMove)
1195
+
1196
+ if (rowChange->type == YapDatabaseViewChangeInsert || rowChange->type == YapDatabaseViewChangeMove)
1197
+ {
1198
+ if ([rowChange->finalGroup isEqualToString:group])
1199
+ {
1200
+ // An INSERT operation can:
1201
+ // - increment the location of the final range
1202
+ // - increase the length of the final range
1203
+ //
1204
+ // How do we know if an inserted item should be included in the range?
1205
+ // This is based upon the growOptions.
1206
+ //
1207
+ // Please see the documentation in the header file for the growOptions property.
1208
+ // It has an extended discussion.
1209
+
1210
+ if (pin == YapDatabaseViewBeginning)
1211
+ {
1212
+ if ((growOptions & YapDatabaseViewGrowPinSide) || finalRangeWasEmpty)
1213
+ {
1214
+ if (rowChange->opFinalIndex < finalRangeMin)
1215
+ finalRangeMin++;
1216
+ }
1217
+ else
1218
+ {
1219
+ if (rowChange->opFinalIndex <= finalRangeMin)
1220
+ finalRangeMin++;
1221
+ }
1222
+
1223
+ if ((growOptions & YapDatabaseViewGrowNonPinSide) || finalRangeWasEmpty)
1224
+ {
1225
+ if (rowChange->opFinalIndex <= finalRangeMax)
1226
+ finalRangeMax++;
1227
+ }
1228
+ else
1229
+ {
1230
+ if (rowChange->opFinalIndex < finalRangeMax)
1231
+ finalRangeMax++;
1232
+ }
1233
+ }
1234
+ else // if (pin == YapDatabaseViewEnd)
1235
+ {
1236
+ if ((growOptions & YapDatabaseViewGrowNonPinSide) || finalRangeWasEmpty)
1237
+ {
1238
+ if (rowChange->opFinalIndex < finalRangeMin)
1239
+ finalRangeMin++;
1240
+ }
1241
+ else
1242
+ {
1243
+ if (rowChange->opFinalIndex <= finalRangeMin)
1244
+ finalRangeMin++;
1245
+ }
1246
+
1247
+ if ((growOptions & YapDatabaseViewGrowPinSide) || finalRangeWasEmpty)
1248
+ {
1249
+ if (rowChange->opFinalIndex <= finalRangeMax)
1250
+ finalRangeMax++;
1251
+ }
1252
+ else
1253
+ {
1254
+ if (rowChange->opFinalIndex < finalRangeMax)
1255
+ finalRangeMax++;
1256
+ }
1257
+ }
1258
+ }
1259
+
1260
+ }// fi (rowChange->type == YapDatabaseViewChangeInsert || rowChange->type == YapDatabaseViewChangeMove)
1261
+
1262
+ // If the range ever becomes empty,
1263
+ // then we need to effectively set the growOptions to YapDatabaseViewGrowOnBothSides.
1264
+ //
1265
+ // With an empty range, there is no different between PinSide and NonPinSide.
1266
+ //
1267
+ // Notice that this flag, once set, remains set.
1268
+
1269
+ finalRangeWasEmpty = finalRangeWasEmpty || ((finalRangeMax - finalRangeMin) == 0);
1270
+
1271
+ } // end for (YapDatabaseViewRowChange *rowChange in rowChanges)
1272
+
1273
+
1274
+ // Adjust if we exceed max length, or drop below min length
1275
+
1276
+ finalRangeLength = finalRangeMax - finalRangeMin;
1277
+
1278
+ if (finalRangeLength > rangeOpts.maxLength)
1279
+ {
1280
+ NSUInteger diff = finalRangeLength - rangeOpts.maxLength;
1281
+ flexibleRangeNonPinSideDeleteDiff = diff;
1282
+
1283
+ if (pin == YapDatabaseViewBeginning)
1284
+ {
1285
+ // Prune from non-pin-side (end)
1286
+ finalRangeMax -= diff;
1287
+ }
1288
+ else // if (pin == YapDatabaseViewEnd)
1289
+ {
1290
+ // Prune from non-pin-side (beginning)
1291
+ finalRangeMin += diff;
1292
+ }
1293
+
1294
+ }
1295
+ else if ((finalRangeLength < rangeOpts.minLength) && (finalRangeLength < finalGroupCount))
1296
+ {
1297
+ NSUInteger diff = rangeOpts.minLength - finalRangeLength;
1298
+
1299
+ if (pin == YapDatabaseViewBeginning)
1300
+ {
1301
+ // Append from pin-side first (beginning) (if possible)
1302
+ if (finalRangeMin > 0)
1303
+ {
1304
+ NSUInteger pinSideDiff = MIN(diff, finalRangeMin);
1305
+ flexibleRangePinSideInsertDiff = pinSideDiff;
1306
+
1307
+ finalRangeMin -= pinSideDiff;
1308
+ diff -= pinSideDiff;
1309
+ }
1310
+
1311
+ // Append from non-pin-side second (end) (if possible) (if still needed)
1312
+ if ((finalRangeMax < finalGroupCount) && (diff > 0))
1313
+ {
1314
+ NSUInteger nonPinSideDiff = MIN(diff, (finalGroupCount - finalRangeMax));
1315
+ flexibleRangeNonPinSideInsertDiff = nonPinSideDiff;
1316
+
1317
+ finalRangeMax += nonPinSideDiff;
1318
+ //diff -= nonPinSideDiff;
1319
+ }
1320
+ }
1321
+ else // if (pin == YapDatabaseViewEnd)
1322
+ {
1323
+ // Append from pin-side first (end) (if possible)
1324
+ if (finalRangeMax < finalGroupCount)
1325
+ {
1326
+ NSUInteger pinSideDiff = MIN(diff, (finalGroupCount - finalRangeMax));
1327
+ flexibleRangePinSideInsertDiff = pinSideDiff;
1328
+
1329
+ finalRangeMax += pinSideDiff;
1330
+ diff -= pinSideDiff;
1331
+ }
1332
+
1333
+ // Append from non-pin-side second (beginning) (if possible) (if still needed)
1334
+ if ((finalRangeMin > 0) && (diff > 0))
1335
+ {
1336
+ NSUInteger nonPinSideDiff = MIN(diff, finalRangeMin);
1337
+ flexibleRangeNonPinSideInsertDiff = nonPinSideDiff;
1338
+
1339
+ finalRangeMin -= nonPinSideDiff;
1340
+ //diff -= nonPinSideDiff;
1341
+ }
1342
+ }
1343
+ }
1344
+
1345
+ // Set finalLength & finalOffset
1346
+
1347
+ finalRangeLength = finalRangeMax - finalRangeMin;
1348
+
1349
+ if (pin == YapDatabaseViewBeginning)
1350
+ finalRangeOffset = finalRangeMin;
1351
+ else
1352
+ finalRangeOffset = finalGroupCount - finalRangeMax;
1353
+
1354
+ } // END if (rangeOpts.isFlexibleRange)
1355
+
1356
+ //
1357
+ // STEP 2 : Filter items that are outside the range, and "map" items that are inside the range.
1358
+ //
1359
+ // By "map" we mean update the index to match the range, not the entire view.
1360
+ // For example, if there is a hard range to display only the last 20 items in the view,
1361
+ // then the index of the last item should be 20 (range.length), not 436 (group.length).
1362
+
1363
+ NSUInteger deleteCount = 0;
1364
+ NSUInteger insertCount = 0;
1365
+
1366
+ NSUInteger i = 0;
1367
+ while (i < [rowChanges count])
1368
+ {
1369
+ YapDatabaseViewRowChange *rowChange = [rowChanges objectAtIndex:i];
1370
+
1371
+ if (rowChange->type == YapDatabaseViewChangeDelete)
1372
+ {
1373
+ if ([rowChange->originalGroup isEqualToString:group])
1374
+ {
1375
+ if (rowChange->originalIndex >= originalRangeMin &&
1376
+ rowChange->originalIndex < originalRangeMax)
1377
+ {
1378
+ // Include in changeset
1379
+ i++;
1380
+ deleteCount++;
1381
+
1382
+ // Update index to match range
1383
+ rowChange->originalIndex -= originalRangeMin;
1384
+ }
1385
+ else
1386
+ {
1387
+ // Exclude from changeset
1388
+ [rowChanges removeObjectAtIndex:i];
1389
+ }
1390
+ }
1391
+ }
1392
+ else if (rowChange->type == YapDatabaseViewChangeInsert)
1393
+ {
1394
+ if ([rowChange->finalGroup isEqualToString:group])
1395
+ {
1396
+ if (rowChange->finalIndex >= finalRangeMin &&
1397
+ rowChange->finalIndex < finalRangeMax)
1398
+ {
1399
+ // Include in changeset
1400
+ i++;
1401
+ insertCount++;
1402
+
1403
+ // Update index to match range
1404
+ rowChange->finalIndex -= finalRangeMin;
1405
+ }
1406
+ else
1407
+ {
1408
+ // Exclude from changeset
1409
+ [rowChanges removeObjectAtIndex:i];
1410
+ }
1411
+ }
1412
+ }
1413
+ else if (rowChange->type == YapDatabaseViewChangeUpdate)
1414
+ {
1415
+ if ([rowChange->originalGroup isEqualToString:group])
1416
+ {
1417
+ if (rowChange->originalIndex >= originalRangeMin &&
1418
+ rowChange->originalIndex < originalRangeMax)
1419
+ {
1420
+ // Include in changeset
1421
+ i++;
1422
+
1423
+ // Update index to match range
1424
+ rowChange->originalIndex -= originalRangeMin;
1425
+ rowChange->finalIndex -= finalRangeMin;
1426
+ }
1427
+ else
1428
+ {
1429
+ // Exclude from changeset
1430
+ [rowChanges removeObjectAtIndex:i];
1431
+ }
1432
+ }
1433
+ }
1434
+ else if (rowChange->type == YapDatabaseViewChangeMove)
1435
+ {
1436
+ // A move is both a delete and an insert.
1437
+ // Sometimes both operations apply to this group.
1438
+ // Sometimes only one.
1439
+ // Sometimes neither.
1440
+
1441
+ BOOL filterDelete = NO;
1442
+ BOOL filterInsert = NO;
1443
+
1444
+ if ([rowChange->originalGroup isEqualToString:group])
1445
+ {
1446
+ if (rowChange->originalIndex >= originalRangeMin &&
1447
+ rowChange->originalIndex < originalRangeMax)
1448
+ {
1449
+ // Include (delete operation) in changeset
1450
+
1451
+ // Update index to match range
1452
+ rowChange->originalIndex -= originalRangeMin;
1453
+ }
1454
+ else
1455
+ {
1456
+ // Exclude (delete operation) from changeset
1457
+ filterDelete = YES;
1458
+ }
1459
+ }
1460
+
1461
+ if ([rowChange->finalGroup isEqualToString:group])
1462
+ {
1463
+ if (rowChange->finalIndex >= finalRangeMin &&
1464
+ rowChange->finalIndex < finalRangeMax)
1465
+ {
1466
+ // Include (insert operation) in changeset
1467
+
1468
+ // Update index to match range
1469
+ rowChange->finalIndex -= finalRangeMin;
1470
+ }
1471
+ else
1472
+ {
1473
+ // Exclude (insert operation) from changeset
1474
+ filterInsert = YES;
1475
+ }
1476
+ }
1477
+
1478
+ if (filterDelete && filterInsert)
1479
+ {
1480
+ // Exclude from changeset
1481
+ [rowChanges removeObjectAtIndex:i];
1482
+ }
1483
+ else if (filterDelete && !filterInsert)
1484
+ {
1485
+ // Move -> Insert
1486
+ rowChange->type = YapDatabaseViewChangeInsert;
1487
+ i++;
1488
+ insertCount++;
1489
+ }
1490
+ else if (!filterDelete && filterInsert)
1491
+ {
1492
+ // Move -> Delete
1493
+ rowChange->type = YapDatabaseViewChangeDelete;
1494
+ i++;
1495
+ deleteCount++;
1496
+ }
1497
+ else
1498
+ {
1499
+ // Move
1500
+ i++;
1501
+ insertCount++;
1502
+ deleteCount++;
1503
+ }
1504
+ }
1505
+ }
1506
+
1507
+ //
1508
+ // STEP 3: Fix the final counts by manually adding any needed insertions or deletions
1509
+ //
1510
+
1511
+ NSUInteger numberOfInsertOperationsToManuallyAdd = 0;
1512
+ NSUInteger numberOfDeleteOperationsToManuallyAdd = 0;
1513
+
1514
+ if (rangeOpts.isFixedRange)
1515
+ {
1516
+ // FIXED Range:
1517
+ //
1518
+ // For fixed ranges, we need to ensure the changeset reflects the proper count.
1519
+ // For example:
1520
+ //
1521
+ // The fixed range has a lenth of 20.
1522
+ // The only changes were 2 insertions.
1523
+ // Thus, we need to add 2 delete changes to balance the length.
1524
+ //
1525
+ // These represent the items that got pushed out of the range.
1526
+
1527
+ NSUInteger length = originalRangeLength;
1528
+ length += insertCount;
1529
+ length -= deleteCount;
1530
+
1531
+ if (length > finalRangeLength)
1532
+ {
1533
+ // Need to add DELETE operations.
1534
+
1535
+ numberOfDeleteOperationsToManuallyAdd = length - finalRangeLength;
1536
+ }
1537
+ else if (length < finalRangeLength)
1538
+ {
1539
+ // Need to add INSERT operations.
1540
+
1541
+ numberOfInsertOperationsToManuallyAdd = finalRangeLength - length;
1542
+ }
1543
+ }
1544
+ else if (flexibleRangeNonPinSideDeleteDiff != 0)
1545
+ {
1546
+ // FLEXIBLE Range:
1547
+ //
1548
+ // The range naturally expanded to the point where it exceeded the max length.
1549
+ // We may need to manually add DELETE operations.
1550
+
1551
+ NSInteger expectedDiff = finalRangeLength - originalRangeLength;
1552
+ NSInteger actualDiff = insertCount - deleteCount;
1553
+
1554
+ // Example 1: typical length going above max
1555
+ //
1556
+ // originalRangeLength = 5, finalRangeLength = 10, max = 10, insertCount = 10, deleteCount = 0
1557
+ // expectedDiff = 5
1558
+ // actualDiff = 10
1559
+
1560
+ // Example 2: user changed max value on us without changing length
1561
+ // (not technically supporeted, but not overly difficult to handle)
1562
+ //
1563
+ // originalRangeLength = 10, finalRangeLength = 5, max = 5, insertCount = 1, deleteCount = 0
1564
+ // expectedDiff = -5
1565
+ // actualDiff = 1
1566
+
1567
+ if (actualDiff > expectedDiff)
1568
+ {
1569
+ numberOfDeleteOperationsToManuallyAdd = actualDiff - expectedDiff;
1570
+ }
1571
+ }
1572
+ else if ((flexibleRangePinSideInsertDiff > 0) || (flexibleRangeNonPinSideInsertDiff > 0))
1573
+ {
1574
+ // FLEXIBLE Range:
1575
+ //
1576
+ // The range naturally shrunk to the point where it was smaller than the min length.
1577
+ // We may need to manually add INSERT operations.
1578
+ //
1579
+ // Note: The originalRangeLength may actually be smaller than the finalRangeLength.
1580
+
1581
+ NSInteger expectedDiff = finalRangeLength - originalRangeLength;
1582
+ NSInteger actualDiff = insertCount - deleteCount;
1583
+
1584
+ // Example 1: database near empty to begin with
1585
+ //
1586
+ // originalRangeLength = 1, finalRangeLength = 5, min = 5, insertCount = 5, deleteCount = 1
1587
+ // expectedDiff = 4
1588
+ // actualDiff = 4
1589
+
1590
+ // Example 2: typical length dropping below min
1591
+ //
1592
+ // originalRangeLength = 5, finalRangeLength = 5, min = 5, insertCount = 0, deleteCount = 2
1593
+ // expectedDiff = 0
1594
+ // actualDiff = -2
1595
+
1596
+ // Example 3: typical length dropping below min
1597
+ //
1598
+ // originalRangeLength = 10, finalRangeLength = 5, min = 5, insertCount = 0, deleteCount = 8
1599
+ // expectedDiff = -5
1600
+ // actualDiff = -8
1601
+
1602
+ // Example 4: user changed min value on us without changing length
1603
+ // (not technically supported, but not overly difficult to handle)
1604
+ //
1605
+ // originalRangeLength = 2, finalRangeLength = 5, min = 5, insertCount = 2, deleteCount = 0
1606
+ // expectedDiff = 3
1607
+ // actualDiff = 2
1608
+
1609
+ if (actualDiff < expectedDiff)
1610
+ {
1611
+ numberOfInsertOperationsToManuallyAdd = expectedDiff - actualDiff;
1612
+ }
1613
+ }
1614
+
1615
+ if (numberOfDeleteOperationsToManuallyAdd > 0)
1616
+ {
1617
+ // Manually add DELETE operations.
1618
+ //
1619
+ // These operations represent the objects that got pushed out of the final range,
1620
+ // even though the items themselves didn't change.
1621
+ //
1622
+ // They are to go at the end opposite the pin.
1623
+
1624
+ NSUInteger count = 0;
1625
+
1626
+ NSUInteger index;
1627
+ if (pin == YapDatabaseViewBeginning)
1628
+ index = originalRangeLength - 1;
1629
+ else
1630
+ index = 0;
1631
+
1632
+ while (count < numberOfDeleteOperationsToManuallyAdd)
1633
+ {
1634
+ // We need to be careful not to step on existing rowChanges.
1635
+ // If there is an existing delete for this index, we need to continue onto the next index.
1636
+ // If there is an existing move from this index, we need to continue onto the next index.
1637
+ // If there is an existing update for this index, we need to change it to a delete.
1638
+
1639
+ BOOL found = NO;
1640
+
1641
+ for (YapDatabaseViewRowChange *rowChange in rowChanges)
1642
+ {
1643
+ if (rowChange->type == YapDatabaseViewChangeDelete ||
1644
+ rowChange->type == YapDatabaseViewChangeMove)
1645
+ {
1646
+ if (rowChange->originalIndex == index && [rowChange->originalGroup isEqualToString:group])
1647
+ {
1648
+ found = YES;
1649
+ break;
1650
+ }
1651
+ }
1652
+ else if (rowChange->type == YapDatabaseViewChangeUpdate)
1653
+ {
1654
+ if (rowChange->originalIndex == index && [rowChange->originalGroup isEqualToString:group])
1655
+ {
1656
+ rowChange->type = YapDatabaseViewChangeDelete;
1657
+
1658
+ count++;
1659
+ found = YES;
1660
+ break;
1661
+ }
1662
+ }
1663
+ }
1664
+
1665
+ if (!found)
1666
+ {
1667
+ YapDatabaseViewRowChange *rowChange =
1668
+ [YapDatabaseViewRowChange deleteKey:nil inGroup:group atIndex:index];
1669
+
1670
+ [rowChanges addObject:rowChange];
1671
+ count++;
1672
+ }
1673
+
1674
+ if (pin == YapDatabaseViewBeginning)
1675
+ index--;
1676
+ else
1677
+ index++;
1678
+ }
1679
+ }
1680
+
1681
+ if ((numberOfInsertOperationsToManuallyAdd > 0) && (flexibleRangePinSideInsertDiff > 0))
1682
+ {
1683
+ // Manually add INSERT operations.
1684
+ //
1685
+ // These operations represent the objects that got pulled into the final range,
1686
+ // even though the items themselves didn't change.
1687
+ //
1688
+ // They are to go pin side.
1689
+ //
1690
+ // Note: This code path is only taken if using a flexibleRange
1691
+
1692
+ NSUInteger i = 0;
1693
+ NSUInteger count = 0;
1694
+
1695
+ NSUInteger index;
1696
+ if (pin == YapDatabaseViewBeginning)
1697
+ index = finalRangeMin;
1698
+ else
1699
+ index = finalRangeLength - 1;
1700
+
1701
+ while ((count < numberOfInsertOperationsToManuallyAdd) && (i < flexibleRangePinSideInsertDiff))
1702
+ {
1703
+ // We need to be careful not to step on existing rowChanges.
1704
+ // If there is an existing insert for this index, we need to continue onto the next index.
1705
+ // If there is an existing move to this index, we need to continue onto the next index.
1706
+ // If there is an existing update for this index, we need to change it to an insert.
1707
+ //
1708
+ // Note: I don't think the update scenario is actually possible.
1709
+
1710
+ BOOL found = NO;
1711
+
1712
+ for (YapDatabaseViewRowChange *rowChange in rowChanges)
1713
+ {
1714
+ if (rowChange->type == YapDatabaseViewChangeInsert ||
1715
+ rowChange->type == YapDatabaseViewChangeMove)
1716
+ {
1717
+ if (rowChange->finalIndex == index && [rowChange->finalGroup isEqualToString:group])
1718
+ {
1719
+ found = YES;
1720
+ break;
1721
+ }
1722
+ }
1723
+ else if (rowChange->type == YapDatabaseViewChangeUpdate)
1724
+ {
1725
+ if (rowChange->finalIndex == index && [rowChange->finalGroup isEqualToString:group])
1726
+ {
1727
+ rowChange->type = YapDatabaseViewChangeInsert;
1728
+
1729
+ count++;
1730
+ found = YES;
1731
+ break;
1732
+ }
1733
+ }
1734
+ }
1735
+
1736
+ if (!found)
1737
+ {
1738
+ YapDatabaseViewRowChange *rowChange =
1739
+ [YapDatabaseViewRowChange insertKey:nil inGroup:group atIndex:index];
1740
+
1741
+ [rowChanges addObject:rowChange];
1742
+ count++;
1743
+ }
1744
+
1745
+ if (pin == YapDatabaseViewBeginning)
1746
+ index++;
1747
+ else
1748
+ index--;
1749
+
1750
+ i++;
1751
+ }
1752
+
1753
+ numberOfInsertOperationsToManuallyAdd -= count;
1754
+ }
1755
+
1756
+ if (numberOfInsertOperationsToManuallyAdd > 0)
1757
+ {
1758
+ // Manually add INSERT operations.
1759
+ //
1760
+ // These operations represent the objects that got pulled into the final range,
1761
+ // even though the items themselves didn't change.
1762
+ //
1763
+ // They are to go at the end opposite the pin.
1764
+
1765
+ NSUInteger count = 0;
1766
+
1767
+ NSUInteger index;
1768
+ if (pin == YapDatabaseViewBeginning)
1769
+ index = finalRangeLength - 1;
1770
+ else
1771
+ index = 0;
1772
+
1773
+ while (count < numberOfInsertOperationsToManuallyAdd)
1774
+ {
1775
+ // We need to be careful not to step on existing rowChanges.
1776
+ // If there is an existing insert for this index, we need to continue onto the next index.
1777
+ // If there is an existing move to this index, we need to continue onto the next index.
1778
+ // If there is an existing update for this index, we need to change it to an insert.
1779
+ //
1780
+ // Note: I don't think the update scenario is actually possible.
1781
+
1782
+ BOOL found = NO;
1783
+
1784
+ for (YapDatabaseViewRowChange *rowChange in rowChanges)
1785
+ {
1786
+ if (rowChange->type == YapDatabaseViewChangeInsert ||
1787
+ rowChange->type == YapDatabaseViewChangeMove)
1788
+ {
1789
+ if (rowChange->finalIndex == index && [rowChange->finalGroup isEqualToString:group])
1790
+ {
1791
+ found = YES;
1792
+ break;
1793
+ }
1794
+ }
1795
+ else if (rowChange->type == YapDatabaseViewChangeUpdate)
1796
+ {
1797
+ if (rowChange->finalIndex == index && [rowChange->finalGroup isEqualToString:group])
1798
+ {
1799
+ rowChange->type = YapDatabaseViewChangeInsert;
1800
+
1801
+ count++;
1802
+ found = YES;
1803
+ break;
1804
+ }
1805
+ }
1806
+ }
1807
+
1808
+ if (!found)
1809
+ {
1810
+ YapDatabaseViewRowChange *rowChange =
1811
+ [YapDatabaseViewRowChange insertKey:nil inGroup:group atIndex:index];
1812
+
1813
+ [rowChanges addObject:rowChange];
1814
+ count++;
1815
+ }
1816
+
1817
+ if (pin == YapDatabaseViewBeginning)
1818
+ index--;
1819
+ else
1820
+ index++;
1821
+ }
1822
+ }
1823
+
1824
+ //
1825
+ // STEP 4.A : Update finalMappings if needed (by updating rangeOpts.length & rangeOpts.offset)
1826
+
1827
+ if ((originalRangeLength != finalRangeLength) || (originalRangeOffset != finalRangeOffset))
1828
+ {
1829
+ [finalMappings updateRangeOptionsForGroup:group withNewLength:finalRangeLength newOffset:finalRangeOffset];
1830
+ rangeOptionsChanged = YES;
1831
+ }
1832
+
1833
+ } // for (NSString *group in rangeOptions)
1834
+
1835
+ // Step 4.B : Update finalMappings if needed (by updating visibleGroups)
1836
+
1837
+ if (rangeOptionsChanged)
1838
+ {
1839
+ [finalMappings updateVisibility];
1840
+ }
1841
+
1842
+ //
1843
+ // STEP 5 : Set the originalSection & finalSection
1844
+ // STEP 6 : Reverse indexes for reversed groups
1845
+ //
1846
+
1847
+ NSSet *reverse = [originalMappings reverse];
1848
+
1849
+ for (YapDatabaseViewRowChange *rowChange in rowChanges)
1850
+ {
1851
+ // Set the originalSection & finalSection
1852
+
1853
+ if (rowChange->type == YapDatabaseViewChangeDelete)
1854
+ {
1855
+ rowChange->originalSection = [originalMappings sectionForGroup:rowChange->originalGroup];
1856
+ }
1857
+ else if (rowChange->type == YapDatabaseViewChangeInsert)
1858
+ {
1859
+ rowChange->finalSection = [finalMappings sectionForGroup:rowChange->finalGroup];
1860
+ }
1861
+ else if (rowChange->type == YapDatabaseViewChangeUpdate)
1862
+ {
1863
+ rowChange->originalSection = [originalMappings sectionForGroup:rowChange->originalGroup];
1864
+ rowChange->finalSection = [finalMappings sectionForGroup:rowChange->finalGroup];
1865
+
1866
+ if (rowChange->originalSection != rowChange->finalSection)
1867
+ {
1868
+ // Turn the update into a move.
1869
+ // If we don't do so, UITableView seems to do the wrong thing.
1870
+
1871
+ rowChange->type = YapDatabaseViewChangeMove;
1872
+ }
1873
+ }
1874
+ else // if (rowChange->type == YapDatabaseViewChangeMove)
1875
+ {
1876
+ rowChange->originalSection = [originalMappings sectionForGroup:rowChange->originalGroup];
1877
+ rowChange->finalSection = [finalMappings sectionForGroup:rowChange->finalGroup];
1878
+ }
1879
+
1880
+ // Reverse indexes for reversed groups.
1881
+ //
1882
+ // For example, if the group contains 4 items, the indexes get reversed like so:
1883
+ // 0 -> 3
1884
+ // 1 -> 2
1885
+ // 2 -> 1
1886
+ // 3 -> 0
1887
+ //
1888
+ // Basically, we find the midpoint, and then move each index to the other side of the midpoint,
1889
+ // but we keep its distance from the midpoint the same.
1890
+
1891
+ if ([reverse containsObject:rowChange->originalGroup])
1892
+ {
1893
+ NSUInteger count = [originalMappings visibleCountForGroup:rowChange->originalGroup];
1894
+ double mid = (count - 1) / 2.0;
1895
+
1896
+ rowChange->originalIndex = (NSUInteger)(mid - (rowChange->originalIndex - mid));
1897
+ }
1898
+
1899
+ if ([reverse containsObject:rowChange->finalGroup])
1900
+ {
1901
+ NSUInteger count = [finalMappings visibleCountForGroup:rowChange->finalGroup];
1902
+ double mid = (count - 1) / 2.0;
1903
+
1904
+ rowChange->finalIndex = (NSUInteger)(mid - (rowChange->finalIndex - mid));
1905
+ }
1906
+ }
1907
+
1908
+ //
1909
+ // STEP 7 : Handle group consolidation
1910
+ //
1911
+
1912
+ BOOL oldIsUsingConsolidatedGroup = [originalMappings isUsingConsolidatedGroup];
1913
+ BOOL newIsUsingConsolidatedGroup = [finalMappings isUsingConsolidatedGroup];
1914
+
1915
+ if (oldIsUsingConsolidatedGroup && newIsUsingConsolidatedGroup)
1916
+ {
1917
+ //
1918
+ // The groups were previously consolidated, and still are.
1919
+ //
1920
+
1921
+ NSString *consolidatedGroupName = [originalMappings consolidatedGroupName];
1922
+
1923
+ // Step 1
1924
+ //
1925
+ // - calculate original & final offset for each group
1926
+
1927
+ NSArray *allGroups = [originalMappings allGroups];
1928
+
1929
+ NSMutableDictionary *originalOffsets = [NSMutableDictionary dictionaryWithCapacity:[allGroups count]];
1930
+ NSMutableDictionary *finalOffsets = [NSMutableDictionary dictionaryWithCapacity:[allGroups count]];
1931
+
1932
+ NSUInteger originalOffset = 0;
1933
+ NSUInteger finalOffset = 0;
1934
+
1935
+ for (NSString *group in allGroups)
1936
+ {
1937
+ [originalOffsets setObject:@(originalOffset) forKey:group];
1938
+ [finalOffsets setObject:@(finalOffset) forKey:group];
1939
+
1940
+ originalOffset += [originalMappings visibleCountForGroup:group];
1941
+ finalOffset += [finalMappings visibleCountForGroup:group];
1942
+ }
1943
+
1944
+ // Step 2
1945
+ //
1946
+ // - change section to zero, and group to consolidatedGroupName for each change
1947
+ // - increment original & final row index for each change
1948
+
1949
+ for (YapDatabaseViewRowChange *rowChange in rowChanges)
1950
+ {
1951
+ if (rowChange->type == YapDatabaseViewChangeDelete)
1952
+ {
1953
+ NSUInteger oOffset = [[originalOffsets objectForKey:rowChange->originalGroup] unsignedIntegerValue];
1954
+
1955
+ rowChange->originalGroup = consolidatedGroupName;
1956
+ rowChange->originalSection = 0;
1957
+ rowChange->originalIndex += oOffset;
1958
+ }
1959
+ else if (rowChange->type == YapDatabaseViewChangeInsert)
1960
+ {
1961
+ NSUInteger fOffset = [[finalOffsets objectForKey:rowChange->finalGroup] unsignedIntegerValue];
1962
+
1963
+ rowChange->finalGroup = consolidatedGroupName;
1964
+ rowChange->finalSection = 0;
1965
+ rowChange->finalIndex += fOffset;
1966
+ }
1967
+ else if (rowChange->type == YapDatabaseViewChangeMove)
1968
+ {
1969
+ NSUInteger oOffset = [[originalOffsets objectForKey:rowChange->originalGroup] unsignedIntegerValue];
1970
+ NSUInteger fOffset = [[finalOffsets objectForKey:rowChange->finalGroup] unsignedIntegerValue];
1971
+
1972
+ rowChange->originalGroup = consolidatedGroupName;
1973
+ rowChange->originalSection = 0;
1974
+ rowChange->originalIndex += oOffset;
1975
+
1976
+ rowChange->finalGroup = consolidatedGroupName;
1977
+ rowChange->finalSection = 0;
1978
+ rowChange->finalIndex += fOffset;
1979
+ }
1980
+ else // if (rowChange->type == YapDatabaseViewChangeUpdate)
1981
+ {
1982
+ NSUInteger oOffset = [[originalOffsets objectForKey:rowChange->originalGroup] unsignedIntegerValue];
1983
+ NSUInteger fOffset = [[finalOffsets objectForKey:rowChange->finalGroup] unsignedIntegerValue];
1984
+
1985
+ rowChange->originalGroup = consolidatedGroupName;
1986
+ rowChange->originalSection = 0;
1987
+ rowChange->originalIndex += oOffset;
1988
+
1989
+ rowChange->finalGroup = consolidatedGroupName;
1990
+ rowChange->finalSection = 0;
1991
+ rowChange->finalIndex += fOffset;
1992
+
1993
+ if (rowChange->originalIndex != rowChange->finalIndex)
1994
+ {
1995
+ rowChange->type = YapDatabaseViewChangeMove;
1996
+ }
1997
+ }
1998
+ }
1999
+ }
2000
+ else if (!oldIsUsingConsolidatedGroup && newIsUsingConsolidatedGroup)
2001
+ {
2002
+ //
2003
+ // Switching from groups to consolidated group
2004
+ //
2005
+
2006
+ NSString *consolidatedGroupName = [originalMappings consolidatedGroupName];
2007
+
2008
+ // Step 1
2009
+ //
2010
+ // - calculate the offsets for each group within the consolidated group
2011
+
2012
+ NSArray *allGroups = [originalMappings allGroups];
2013
+
2014
+ NSMutableDictionary *finalOffsets = [NSMutableDictionary dictionaryWithCapacity:[allGroups count]];
2015
+ NSUInteger finalOffset = 0;
2016
+
2017
+ for (NSString *group in allGroups)
2018
+ {
2019
+ [finalOffsets setObject:@(finalOffset) forKey:group];
2020
+
2021
+ finalOffset += [finalMappings visibleCountForGroup:group];
2022
+ }
2023
+
2024
+ // Step 2
2025
+ //
2026
+ // - enumerate over every single row that was visible in the originalMappings
2027
+ // - check to see if its already represented in the existing array of rowChanges
2028
+ // - if not, then inject a move operation for it
2029
+
2030
+ NSUInteger beginningChangeCount = [rowChanges count];
2031
+
2032
+ for (NSString *group in [originalMappings visibleGroups])
2033
+ {
2034
+ NSUInteger originalSection = [originalMappings sectionForGroup:group];
2035
+ NSUInteger finalSection = [finalMappings sectionForGroup:group];
2036
+
2037
+ NSUInteger originalGroupCount = [originalMappings visibleCountForGroup:group];
2038
+
2039
+ for (NSUInteger originalIndex = 0; originalIndex < originalGroupCount; originalIndex++)
2040
+ {
2041
+ BOOL found = NO;
2042
+ NSUInteger finalIndex = originalIndex;
2043
+
2044
+ for (NSUInteger i = 0; i < beginningChangeCount; i++)
2045
+ {
2046
+ YapDatabaseViewRowChange *rowChange = [rowChanges objectAtIndex:i];
2047
+
2048
+ if (rowChange->type != YapDatabaseViewChangeInsert &&
2049
+ rowChange->originalSection == originalSection &&
2050
+ rowChange->originalIndex == originalIndex)
2051
+ {
2052
+ found = YES;
2053
+ break;
2054
+ }
2055
+
2056
+ if (rowChange->type == YapDatabaseViewChangeDelete ||
2057
+ rowChange->type == YapDatabaseViewChangeMove )
2058
+ {
2059
+ if (rowChange->originalSection == originalSection)
2060
+ {
2061
+ if (rowChange->originalIndex < finalIndex)
2062
+ {
2063
+ // A row was deleted below our row.
2064
+ // So its finalIndex gets decremented.
2065
+ finalIndex--;
2066
+ }
2067
+ }
2068
+ }
2069
+
2070
+ if (rowChange->type == YapDatabaseViewChangeInsert ||
2071
+ rowChange->type == YapDatabaseViewChangeMove )
2072
+ {
2073
+ if (rowChange->finalSection == finalSection)
2074
+ {
2075
+ if (rowChange->finalIndex <= finalIndex)
2076
+ {
2077
+ // A row was inserted below our row.
2078
+ // So its finalIndex gets incremented.
2079
+ finalIndex++;
2080
+ }
2081
+ }
2082
+ }
2083
+ }
2084
+
2085
+ if (!found)
2086
+ {
2087
+ YapDatabaseViewRowChange *op = [[YapDatabaseViewRowChange alloc] init];
2088
+ op->type = YapDatabaseViewChangeMove;
2089
+ op->key = nil;
2090
+ op->changes = 0;
2091
+
2092
+ op->originalGroup = group;
2093
+ op->originalSection = originalSection;
2094
+ op->originalIndex = originalIndex;
2095
+
2096
+ NSUInteger finalGroupOffset = [[finalOffsets objectForKey:group] unsignedIntegerValue];
2097
+
2098
+ op->finalGroup = consolidatedGroupName;
2099
+ op->finalSection = 0;
2100
+ op->finalIndex = finalGroupOffset + finalIndex;
2101
+
2102
+ [rowChanges addObject:op];
2103
+ }
2104
+ }
2105
+ }
2106
+
2107
+ // Step 3
2108
+ //
2109
+ // - update the finalGroup/finalSection/finalIndex for every rowChange
2110
+ // (excluding the one's we injected)
2111
+
2112
+ for (NSUInteger i = 0; i < beginningChangeCount; i++)
2113
+ {
2114
+ YapDatabaseViewRowChange *rowChange = [rowChanges objectAtIndex:i];
2115
+
2116
+ if (rowChange->type == YapDatabaseViewChangeInsert)
2117
+ {
2118
+ NSUInteger fOffset = [[finalOffsets objectForKey:rowChange->finalGroup] unsignedIntegerValue];
2119
+
2120
+ rowChange->finalGroup = consolidatedGroupName;
2121
+ rowChange->finalSection = 0;
2122
+ rowChange->finalIndex += fOffset;
2123
+ }
2124
+ else if (rowChange->type == YapDatabaseViewChangeMove)
2125
+ {
2126
+ NSUInteger fOffset = [[finalOffsets objectForKey:rowChange->finalGroup] unsignedIntegerValue];
2127
+
2128
+ rowChange->finalGroup = consolidatedGroupName;
2129
+ rowChange->finalSection = 0;
2130
+ rowChange->finalIndex += fOffset;
2131
+ }
2132
+ else if (rowChange->type == YapDatabaseViewChangeUpdate)
2133
+ {
2134
+ rowChange->type = YapDatabaseViewChangeMove;
2135
+
2136
+ NSUInteger fOffset = [[finalOffsets objectForKey:rowChange->finalGroup] unsignedIntegerValue];
2137
+
2138
+ rowChange->finalGroup = consolidatedGroupName;
2139
+ rowChange->finalSection = 0;
2140
+ rowChange->finalIndex += fOffset;
2141
+ }
2142
+ }
2143
+ }
2144
+ else if (oldIsUsingConsolidatedGroup && !newIsUsingConsolidatedGroup)
2145
+ {
2146
+ //
2147
+ // Switching from consolidated group to groups
2148
+ //
2149
+
2150
+ NSString *consolidatedGroupName = [originalMappings consolidatedGroupName];
2151
+
2152
+ // Step 1
2153
+ //
2154
+ // - calculate the offsets for each group within the consolidated group
2155
+
2156
+ NSArray *allGroups = [originalMappings allGroups];
2157
+
2158
+ NSMutableDictionary *originalOffsets = [NSMutableDictionary dictionaryWithCapacity:[allGroups count]];
2159
+
2160
+ NSUInteger originalOffset = 0;
2161
+
2162
+ for (NSString *group in allGroups)
2163
+ {
2164
+ [originalOffsets setObject:@(originalOffset) forKey:group];
2165
+
2166
+ originalOffset += [originalMappings visibleCountForGroup:group];
2167
+ }
2168
+
2169
+ // Step 2
2170
+ //
2171
+ // - enumerate over every single row that was visible in the originalMappings
2172
+ // - check to see if its already represented in the existing array of rowChanges
2173
+ // - if not, then inject a move operation for it
2174
+
2175
+ NSUInteger beginningChangeCount = [rowChanges count];
2176
+
2177
+ for (NSString *group in [originalMappings visibleGroups])
2178
+ {
2179
+ NSUInteger originalSection = [originalMappings sectionForGroup:group];
2180
+ NSUInteger finalSection = [finalMappings sectionForGroup:group];
2181
+
2182
+ NSUInteger originalGroupCount = [originalMappings visibleCountForGroup:group];
2183
+
2184
+ for (NSUInteger originalIndex = 0; originalIndex < originalGroupCount; originalIndex++)
2185
+ {
2186
+ BOOL found = NO;
2187
+ NSUInteger finalIndex = originalIndex;
2188
+
2189
+ for (NSUInteger i = 0; i < beginningChangeCount; i++)
2190
+ {
2191
+ YapDatabaseViewRowChange *rowChange = [rowChanges objectAtIndex:i];
2192
+
2193
+ if (rowChange->type != YapDatabaseViewChangeInsert &&
2194
+ rowChange->originalSection == originalSection &&
2195
+ rowChange->originalIndex == originalIndex)
2196
+ {
2197
+ found = YES;
2198
+ break;
2199
+ }
2200
+
2201
+ if (rowChange->type == YapDatabaseViewChangeDelete ||
2202
+ rowChange->type == YapDatabaseViewChangeMove )
2203
+ {
2204
+ if (rowChange->originalSection == originalSection)
2205
+ {
2206
+ if (rowChange->originalIndex < finalIndex)
2207
+ {
2208
+ // A row was deleted below our row.
2209
+ // So its finalIndex gets decremented.
2210
+ finalIndex--;
2211
+ }
2212
+ }
2213
+ }
2214
+
2215
+ if (rowChange->type == YapDatabaseViewChangeInsert ||
2216
+ rowChange->type == YapDatabaseViewChangeMove )
2217
+ {
2218
+ if (rowChange->finalSection == finalSection)
2219
+ {
2220
+ if (rowChange->finalIndex <= finalIndex)
2221
+ {
2222
+ // A row was inserted below our row.
2223
+ // So its finalIndex gets incremented.
2224
+ finalIndex++;
2225
+ }
2226
+ }
2227
+ }
2228
+ }
2229
+
2230
+ if (!found)
2231
+ {
2232
+ YapDatabaseViewRowChange *op = [[YapDatabaseViewRowChange alloc] init];
2233
+ op->type = YapDatabaseViewChangeMove;
2234
+ op->key = nil;
2235
+ op->changes = 0;
2236
+
2237
+ NSUInteger originalGroupOffset = [[originalOffsets objectForKey:group] unsignedIntegerValue];
2238
+
2239
+ op->originalGroup = consolidatedGroupName;
2240
+ op->originalSection = 0;
2241
+ op->originalIndex = originalGroupOffset + originalIndex;
2242
+
2243
+ op->finalGroup = group;
2244
+ op->finalSection = finalSection;
2245
+ op->finalIndex = finalIndex;
2246
+
2247
+ [rowChanges addObject:op];
2248
+ }
2249
+ }
2250
+ }
2251
+
2252
+ // Step 3
2253
+ //
2254
+ // - update the originalGroup/originalSection/originalIndex for every rowChange
2255
+ // (excluding the one's we injected)
2256
+
2257
+ for (NSUInteger i = 0; i < beginningChangeCount; i++)
2258
+ {
2259
+ YapDatabaseViewRowChange *rowChange = [rowChanges objectAtIndex:i];
2260
+
2261
+ if (rowChange->type == YapDatabaseViewChangeDelete)
2262
+ {
2263
+ NSUInteger oOffset = [[originalOffsets objectForKey:rowChange->originalGroup] unsignedIntegerValue];
2264
+
2265
+ rowChange->originalGroup = consolidatedGroupName;
2266
+ rowChange->originalSection = 0;
2267
+ rowChange->originalIndex += oOffset;
2268
+ }
2269
+ else if (rowChange->type == YapDatabaseViewChangeMove)
2270
+ {
2271
+ NSUInteger oOffset = [[originalOffsets objectForKey:rowChange->originalGroup] unsignedIntegerValue];
2272
+
2273
+ rowChange->originalGroup = consolidatedGroupName;
2274
+ rowChange->originalSection = 0;
2275
+ rowChange->originalIndex += oOffset;
2276
+ }
2277
+ else if (rowChange->type == YapDatabaseViewChangeUpdate)
2278
+ {
2279
+ rowChange->type = YapDatabaseViewChangeMove;
2280
+
2281
+ NSUInteger oOffset = [[originalOffsets objectForKey:rowChange->originalGroup] unsignedIntegerValue];
2282
+
2283
+ rowChange->originalGroup = consolidatedGroupName;
2284
+ rowChange->originalSection = 0;
2285
+ rowChange->originalIndex += oOffset;
2286
+ }
2287
+ }
2288
+ }
2289
+ }
2290
+
2291
+ /**
2292
+ * This method applies the given mappings to the processed list of section changes.
2293
+ * It will filter the sectionChanges array to properly represent the configuration of the mappings.
2294
+ **/
2295
+ + (void)postProcessAndFilterSectionChanges:(NSMutableArray *)sectionChanges
2296
+ withOriginalMappings:(YapDatabaseViewMappings *)originalMappings
2297
+ finalMappings:(YapDatabaseViewMappings *)finalMappings
2298
+ {
2299
+ //
2300
+ // STEP 1 : Handle dynamic sections
2301
+ //
2302
+
2303
+ NSUInteger i = 0;
2304
+ while (i < [sectionChanges count])
2305
+ {
2306
+ YapDatabaseViewSectionChange *sectionChange = [sectionChanges objectAtIndex:i];
2307
+
2308
+ if (sectionChange->type == YapDatabaseViewChangeDelete)
2309
+ {
2310
+ // Although a group was deleted, the user may be allowing empty sections.
2311
+ // If so, we shouldn't emit a removeSection change.
2312
+
2313
+ NSUInteger originalSection = [originalMappings sectionForGroup:sectionChange->group];
2314
+ NSUInteger finalSection = [finalMappings sectionForGroup:sectionChange->group];
2315
+
2316
+ if ((originalSection != NSNotFound) && (finalSection == NSNotFound))
2317
+ {
2318
+ // Emit
2319
+ sectionChange->originalSection = originalSection;
2320
+ i++;
2321
+ }
2322
+ else
2323
+ {
2324
+ // Don't emit
2325
+ [sectionChanges removeObjectAtIndex:i];
2326
+ }
2327
+ }
2328
+ else // if (sectionChange->type == YapDatabaseViewChangeInsert)
2329
+ {
2330
+ // Although a group was inserted, the user may have been allowing empty sections.
2331
+ // If so, we shouldn't emit an insertSection change.
2332
+
2333
+ NSUInteger originalSection = [originalMappings sectionForGroup:sectionChange->group];
2334
+ NSUInteger finalSection = [finalMappings sectionForGroup:sectionChange->group];
2335
+
2336
+ if ((originalSection == NSNotFound) && (finalSection != NSNotFound))
2337
+ {
2338
+ // Emit
2339
+ sectionChange->finalSection = finalSection;
2340
+ i++;
2341
+ }
2342
+ else
2343
+ {
2344
+ // Don't emit
2345
+ [sectionChanges removeObjectAtIndex:i];
2346
+ }
2347
+ }
2348
+ }
2349
+
2350
+ //
2351
+ // STEP 2 : Handle group consolidation
2352
+ //
2353
+
2354
+ BOOL oldIsUsingConsolidatedGroup = [originalMappings isUsingConsolidatedGroup];
2355
+ BOOL newIsUsingConsolidatedGroup = [finalMappings isUsingConsolidatedGroup];
2356
+
2357
+ if (oldIsUsingConsolidatedGroup && newIsUsingConsolidatedGroup)
2358
+ {
2359
+ //
2360
+ // The groups were previously consolidated, and still are.
2361
+ //
2362
+
2363
+ // - clear section changes (everything stays within consolidated group)
2364
+
2365
+ [sectionChanges removeAllObjects];
2366
+ }
2367
+ else if (!oldIsUsingConsolidatedGroup && newIsUsingConsolidatedGroup)
2368
+ {
2369
+ //
2370
+ // Switching from groups to consolidated group
2371
+ //
2372
+
2373
+ // - clear section changes (so we can set them manually)
2374
+
2375
+ [sectionChanges removeAllObjects];
2376
+
2377
+ // - insert section delete for all original sections
2378
+
2379
+ NSUInteger originalSection = 0;
2380
+ for (NSString *originalGroup in [originalMappings visibleGroups])
2381
+ {
2382
+ YapDatabaseViewSectionChange *deleteOp = [YapDatabaseViewSectionChange deleteGroup:originalGroup];
2383
+ deleteOp->originalSection = originalSection;
2384
+
2385
+ [sectionChanges addObject:deleteOp];
2386
+ originalSection++;
2387
+ }
2388
+
2389
+ // - insert section insert for consolidated group
2390
+
2391
+ NSString *consolidatedGroupName = [originalMappings consolidatedGroupName];
2392
+
2393
+ YapDatabaseViewSectionChange *insertOp = [YapDatabaseViewSectionChange insertGroup:consolidatedGroupName];
2394
+ insertOp->finalSection = 0;
2395
+
2396
+ [sectionChanges addObject:insertOp];
2397
+ }
2398
+ else if (oldIsUsingConsolidatedGroup && !newIsUsingConsolidatedGroup)
2399
+ {
2400
+ //
2401
+ // Switching from consolidated group to groups
2402
+ //
2403
+
2404
+ // - clear section changes (so we can set them manually)
2405
+
2406
+ [sectionChanges removeAllObjects];
2407
+
2408
+ // - insert section delete for consolidatedGroup
2409
+
2410
+ NSString *consolidatedGroupName = [originalMappings consolidatedGroupName];
2411
+
2412
+ YapDatabaseViewSectionChange *deleteOp = [YapDatabaseViewSectionChange deleteGroup:consolidatedGroupName];
2413
+ deleteOp->originalSection = 0;
2414
+
2415
+ [sectionChanges addObject:deleteOp];
2416
+
2417
+ // - insert section insert for all final sections
2418
+
2419
+ NSUInteger finalSection = 0;
2420
+ for (NSString *finalGroup in [finalMappings visibleGroups])
2421
+ {
2422
+ YapDatabaseViewSectionChange *insertOp = [YapDatabaseViewSectionChange insertGroup:finalGroup];
2423
+ insertOp->finalSection = finalSection;
2424
+
2425
+ [sectionChanges addObject:insertOp];
2426
+ finalSection++;
2427
+ }
2428
+ }
2429
+ }
2430
+
2431
+ + (void)getSectionChanges:(NSArray **)sectionChangesPtr
2432
+ rowChanges:(NSArray **)rowChangesPtr
2433
+ withOriginalMappings:(YapDatabaseViewMappings *)originalMappings
2434
+ finalMappings:(YapDatabaseViewMappings *)finalMappings
2435
+ fromChanges:(NSArray *)changes
2436
+ {
2437
+ NSMutableArray *sectionChanges = nil;
2438
+ NSMutableArray *rowChanges = nil;
2439
+
2440
+ [originalMappings setAutoConsolidatingDisabled:YES]; // disable during processing
2441
+ [finalMappings setAutoConsolidatingDisabled:YES]; // disable during processing
2442
+
2443
+ // PRE-PROCESSING
2444
+ //
2445
+ // Remove any items from the changes array that don't concern us.
2446
+
2447
+ [self preProcessChanges:changes
2448
+ withOriginalMappings:originalMappings
2449
+ finalMappings:finalMappings
2450
+ andGetSectionChanges:&sectionChanges
2451
+ rowChanges:&rowChanges];
2452
+
2453
+ //
2454
+ // PROCESSING
2455
+ //
2456
+ // This is where the magic happens.
2457
+ // Calculates original and final index of every change.
2458
+
2459
+ [self processRowChanges:rowChanges];
2460
+
2461
+ // CONSOLIDATION
2462
+ //
2463
+ // Merge multiple changes to same row into a single change.
2464
+ // Merge multiple changes to a group into a zero or one change.
2465
+
2466
+ [self consolidateRowChanges:rowChanges];
2467
+
2468
+ [self consolidateSectionChanges:sectionChanges];
2469
+
2470
+ //
2471
+ // POST-PROCESSING
2472
+ //
2473
+ // This is where we apply the mappings to filter & alter the changeset.
2474
+
2475
+ [self postProcessAndFilterRowChanges:rowChanges
2476
+ withOriginalMappings:originalMappings
2477
+ finalMappings:finalMappings];
2478
+
2479
+ [self postProcessAndFilterSectionChanges:sectionChanges
2480
+ withOriginalMappings:originalMappings
2481
+ finalMappings:finalMappings];
2482
+
2483
+ //
2484
+ // DONE
2485
+ //
2486
+
2487
+ [originalMappings setAutoConsolidatingDisabled:NO];
2488
+ [finalMappings setAutoConsolidatingDisabled:NO];
2489
+
2490
+ if (sectionChangesPtr) *sectionChangesPtr = sectionChanges;
2491
+ if (rowChangesPtr) *rowChangesPtr = rowChanges;
2492
+ }
2493
+
2494
+ @end