|
| 1 | +/*! |
| 2 | +@class QtDataSync::EventCursor |
| 3 | + |
| 4 | +The EventCursor class provides a simple interface to walk through all the changes that have been |
| 5 | +done on the internal data set. Unlike the DataStore, which presents the actual data but does give |
| 6 | +no guarantees on completeness of change events, this class can be used to deterministically |
| 7 | +handle all changes in chronological order. |
| 8 | + |
| 9 | +@note By default, a datasync instance does not create an eventlog. Enable it via the |
| 10 | +Setup::eventLoggingMode property |
| 11 | + |
| 12 | +@attention This class does **not** hold any data itself, only what was changed, when it happend |
| 13 | +and whether it was changed or deleted. This means if the same dataset is changed multiple times |
| 14 | +in a row, you will get as many change events as it was changed, but you can only ever read the |
| 15 | +most recent dataset from the current datastore. |
| 16 | + |
| 17 | +@sa DataStore, Setup::eventLoggingMode, EventCursor::skipObsolete |
| 18 | +*/ |
| 19 | + |
| 20 | +/*! |
| 21 | +@property QtDataSync::EventCursor::valid |
| 22 | + |
| 23 | +@default{`false`} |
| 24 | + |
| 25 | +This can be used to check if an eventcursor returned from one of the static functions is actually |
| 26 | +valid. |
| 27 | + |
| 28 | +@note A once valid eventcursor will only ever advance forward to another valid position and thus |
| 29 | +cannot become invalid |
| 30 | + |
| 31 | +@accessors{ |
| 32 | + @readAc{isValid()} |
| 33 | + @constantAc |
| 34 | +} |
| 35 | + |
| 36 | +@sa EventCursor::first, EventCursor::last, EventCursor::create, EventCursor::load |
| 37 | +*/ |
| 38 | + |
| 39 | +/*! |
| 40 | +@property QtDataSync::EventCursor::setupName |
| 41 | + |
| 42 | +@default{`QtDataSync::DefaultSetup`} |
| 43 | + |
| 44 | +The setup name is the name that was passed to the Setup::create method to create the datasync |
| 45 | +instance this cursor is refering to. |
| 46 | + |
| 47 | +@accessors{ |
| 48 | + @readAc{setupName()} |
| 49 | + @constantAc |
| 50 | +} |
| 51 | + |
| 52 | +@sa QtDataSync::DefaultSetup, Setup::create |
| 53 | +*/ |
| 54 | + |
| 55 | +/*! |
| 56 | +@property QtDataSync::EventCursor::index |
| 57 | + |
| 58 | +@default{`0`} |
| 59 | + |
| 60 | +This index is unqiue and can uniquely identify an event. They are automatically generate and |
| 61 | +are strictly ascending, meaning that newer events will always have a higher index compared to an |
| 62 | +older event. |
| 63 | + |
| 64 | +@accessors{ |
| 65 | + @readAc{index()} |
| 66 | + @notifyAc{indexChanged()} |
| 67 | +} |
| 68 | + |
| 69 | +@sa EventCursor::create, EventCursor::key |
| 70 | +*/ |
| 71 | + |
| 72 | +/*! |
| 73 | +@property QtDataSync::EventCursor::key |
| 74 | + |
| 75 | +@default{<i>empty</i>} |
| 76 | + |
| 77 | +This key contains both, the name of the class and the id of the dataset that was changed. |
| 78 | + |
| 79 | +@accessors{ |
| 80 | + @readAc{key()} |
| 81 | + @notifyAc{keyChanged()} |
| 82 | +} |
| 83 | + |
| 84 | +@sa QtDataSync::ObjectKey, EventCursor::wasRemoved |
| 85 | +*/ |
| 86 | + |
| 87 | +/*! |
| 88 | +@property QtDataSync::EventCursor::wasRemoved |
| 89 | + |
| 90 | +@default{`false`} |
| 91 | + |
| 92 | +If a change event created a new dataset or modified an existing one, than this property is set to |
| 93 | +false. If an existing dataset was deleted it is set to true. |
| 94 | + |
| 95 | +@accessors{ |
| 96 | + @readAc{wasRemoved()} |
| 97 | + @notifyAc{wasRemovedChanged()} |
| 98 | +} |
| 99 | + |
| 100 | +@sa EventCursor::key |
| 101 | +*/ |
| 102 | + |
| 103 | +/*! |
| 104 | +@property QtDataSync::EventCursor::timestamp |
| 105 | + |
| 106 | +@default{<i>invalid</i>} |
| 107 | + |
| 108 | +Timestamps are generated internally based on the current local time and then converted to UTC |
| 109 | +the moment a dataset was changed. This property loads the UTC variant and converts it back to the |
| 110 | +current local time, which means this property **always** returns the time converter to the |
| 111 | +current local time. If you need UTC timestamps, convert it back. The timestamp contains the date |
| 112 | +and time with milliseconds precision. |
| 113 | + |
| 114 | +@accessors{ |
| 115 | + @readAc{timestamp()} |
| 116 | + @notifyAc{timestampChanged()} |
| 117 | +} |
| 118 | + |
| 119 | +@sa EventCursor::key, EventCursor::index |
| 120 | +*/ |
| 121 | + |
| 122 | +/*! |
| 123 | +@property QtDataSync::EventCursor::skipObsolete |
| 124 | + |
| 125 | +@default{`true`} |
| 126 | + |
| 127 | +If skipping obsolete entries is enabled, the cursor will automatically skip multiple change |
| 128 | +events on the same dataset and only consider the most recent ones. If set to false, all entries |
| 129 | +are used instead. |
| 130 | + |
| 131 | +A simple example. Consider the following chain of events: |
| 132 | +@code |
| 133 | + 001 [MyType:Id1] change <- cursor positioned here |
| 134 | + 002 [MyType:Id2] change |
| 135 | + 003 [MyType:Id1] delete |
| 136 | + 004 [MyType:Id3] delete |
| 137 | + 005 [MyType:Id1] change |
| 138 | +@endcode |
| 139 | + |
| 140 | +If this property is set to true, calling next() will move it to `002`. Calling next again will |
| 141 | +make it skip `003` and move to `004` instead, because there is a newer change event for the same |
| 142 | +dataset (`005`). Calling next one more time then moves to `005`. If this property had been |
| 143 | +disabled, entry `003` would not have been skipped, event though the change that was done has |
| 144 | +already been succeeded by a newer one. |
| 145 | + |
| 146 | +@note This property *only** affects the next(), hasNext() and autoScanLog() methods. It is |
| 147 | +still possible to create a cursor on an obsolete event via create() |
| 148 | + |
| 149 | +@accessors{ |
| 150 | + @readAc{skipObsolete()} |
| 151 | + @writeAc{setSkipObsolete()} |
| 152 | + @notifyAc{skipObsoleteChanged()} |
| 153 | +} |
| 154 | + |
| 155 | +@sa EventCursor::next, EventCursor::hasNext, EventCursor::autoScanLog, EventCursor::create, |
| 156 | +EventCursor::index |
| 157 | +*/ |
| 158 | + |
| 159 | +/*! |
| 160 | +@fn QtDataSync::EventCursor::isEventLogActive |
| 161 | + |
| 162 | +@param setupName The name of the setup to check for an active log |
| 163 | + |
| 164 | +Eventlogging must be explicitly enabled when creating a setup. In case you do not know at the |
| 165 | +point of using the cursor whether the log is actually available, you can use this method to |
| 166 | +check |
| 167 | + |
| 168 | +@attention In case the log is not enabled, all creation methods to obtain a cursor will fail and |
| 169 | +instead return invalid cursors |
| 170 | + |
| 171 | +@sa Setup::eventLoggingMode, EventCursor::valid |
| 172 | +*/ |
| 173 | + |
| 174 | +/*! |
| 175 | +@fn QtDataSync::EventCursor::first(QObject*) |
| 176 | + |
| 177 | +@param parent The parent object to set as parent of the event cursor |
| 178 | +@returns A valid eventcursor, positioned on the oldest available change, or an invalid cursor |
| 179 | +if no changes exist |
| 180 | +@throws EventCursorException Thrown if access to the underlying database failed |
| 181 | + |
| 182 | +If no setup name is specified, the cursor is created for the default setup name |
| 183 | + |
| 184 | +@sa EventCursor::last, EventCursor::create, EventCursor::load, EventCursor::valid, |
| 185 | +EventCursor::isEventLogActive |
| 186 | +*/ |
| 187 | + |
| 188 | +/*! |
| 189 | +@fn QtDataSync::EventCursor::first(const QString &, QObject*) |
| 190 | +@param setupName The name of the setup to create the cursor for |
| 191 | +@copydetails EventCursor::first(QObject*) |
| 192 | +*/ |
| 193 | + |
| 194 | +/*! |
| 195 | +@fn QtDataSync::EventCursor::last(QObject*) |
| 196 | + |
| 197 | +@param parent The parent object to set as parent of the event cursor |
| 198 | +@returns A valid eventcursor, positioned on the newest available change, or an invalid cursor |
| 199 | +if no changes exist |
| 200 | +@throws EventCursorException Thrown if access to the underlying database failed |
| 201 | + |
| 202 | +If no setup name is specified, the cursor is created for the default setup name |
| 203 | + |
| 204 | +@sa EventCursor::first, EventCursor::create, EventCursor::load, EventCursor::valid, |
| 205 | +EventCursor::isEventLogActive |
| 206 | +*/ |
| 207 | + |
| 208 | +/*! |
| 209 | +@fn QtDataSync::EventCursor::last(const QString &, QObject*) |
| 210 | +@param setupName The name of the setup to create the cursor for |
| 211 | +@copydetails EventCursor::last(QObject*) |
| 212 | +*/ |
| 213 | + |
| 214 | +/*! |
| 215 | +@fn QtDataSync::EventCursor::create(quint64, QObject*) |
| 216 | + |
| 217 | +@param index The index of the change event to position the cursor on |
| 218 | +@param parent The parent object to set as parent of the event cursor |
| 219 | +@returns A valid eventcursor, positioned on the given index, or an invalid cursor |
| 220 | +if no changes exist for that index |
| 221 | +@throws EventCursorException Thrown if access to the underlying database failed |
| 222 | + |
| 223 | +If no setup name is specified, the cursor is created for the default setup name |
| 224 | + |
| 225 | +@sa EventCursor::first, EventCursor::last, EventCursor::load, EventCursor::valid, |
| 226 | +EventCursor::isEventLogActive |
| 227 | +*/ |
| 228 | + |
| 229 | +/*! |
| 230 | +@fn QtDataSync::EventCursor::create(quint64, const QString &, QObject*) |
| 231 | +@param setupName The name of the setup to create the cursor for |
| 232 | +@copydetails EventCursor::create(quint64, QObject*) |
| 233 | +*/ |
| 234 | + |
| 235 | +/*! |
| 236 | +@fn QtDataSync::EventCursor::load(const QByteArray &, QObject*) |
| 237 | + |
| 238 | +@param data The data to create the cursor from |
| 239 | +@param parent The parent object to set as parent of the event cursor |
| 240 | +@returns A valid eventcursor, positioned on the index part of the data, or an invalid cursor |
| 241 | +if no changes exist for that index |
| 242 | +@throws EventCursorException Thrown if access to the underlying database failed |
| 243 | + |
| 244 | +If no setup name is specified, the cursor is created for the default setup name. The following |
| 245 | +properties are restored from the data: |
| 246 | + |
| 247 | +- EventCursor::index |
| 248 | +- EventCursor::skipObsolete |
| 249 | + |
| 250 | +Use save() to save the data of an existing cursor so you can later load it again using this |
| 251 | +method. |
| 252 | + |
| 253 | +@note The index is only set in case the returned cursor is valid. For an invalid cursor, only the |
| 254 | +skipObsolete property will be set. |
| 255 | + |
| 256 | +@sa EventCursor::first, EventCursor::last, EventCursor::create, EventCursor::valid, |
| 257 | +EventCursor::isEventLogActive, EventCursor::save, EventCursor::skipObsolete |
| 258 | +*/ |
| 259 | + |
| 260 | +/*! |
| 261 | +@fn QtDataSync::EventCursor::load(const QByteArray &, const QString &, QObject*) |
| 262 | +@param setupName The name of the setup to create the cursor for |
| 263 | +@copydetails EventCursor::load(const QByteArray &, QObject*) |
| 264 | +*/ |
| 265 | + |
| 266 | +/*! |
| 267 | +@fn QtDataSync::EventCursor::save |
| 268 | + |
| 269 | +@returns Encoded data of the current cursor that can be used to recreate it. |
| 270 | + |
| 271 | +The following properties are encoded into the data: |
| 272 | + |
| 273 | +- EventCursor::index |
| 274 | +- EventCursor::skipObsolete |
| 275 | + |
| 276 | +@sa EventCursor::load |
| 277 | +*/ |
| 278 | + |
| 279 | +/*! |
| 280 | +@fn QtDataSync::EventCursor::hasNext |
| 281 | + |
| 282 | +@returns True if there is at least one newer change event currently available, false if not |
| 283 | +@throws EventCursorException Thrown if access to the underlying database failed |
| 284 | + |
| 285 | +This method does not change the position or general state of the current cursor. This method |
| 286 | +honors the value of skipObsolete |
| 287 | + |
| 288 | +@sa EventCursor::next, EventCursor::valid, EventCursor::skipObsolete |
| 289 | +*/ |
| 290 | + |
| 291 | +/*! |
| 292 | +@fn QtDataSync::EventCursor::next |
| 293 | + |
| 294 | +@returns True if the cursor was able to advance to a new change event, false if not |
| 295 | +@throws EventCursorException Thrown if access to the underlying database failed |
| 296 | + |
| 297 | +In case there was a new change event, after calling this method the cursor is now positioned on |
| 298 | +that newer record and in case it was previously valid now still is valid and true is returned. If |
| 299 | +however there was no new change event to advance to, the cursor stayes **unchanged** and false is |
| 300 | +returned. This means the cursor is **not** set to be invalid if this method fails, only the |
| 301 | +return value or the index can be used to check if the next operation actually did anything. |
| 302 | + |
| 303 | +This method honors the value of skipObsolete |
| 304 | + |
| 305 | +@sa EventCursor::hasNext, EventCursor::autoScanLog, EventCursor::skipObsolete |
| 306 | +*/ |
| 307 | + |
| 308 | +/*! |
| 309 | +@fn QtDataSync::EventCursor::autoScanLog() |
| 310 | + |
| 311 | +@throws EventCursorException Thrown if access to the underlying database failed |
| 312 | + |
| 313 | +This method basically just calles next() in a loop to walk through all existing change events |
| 314 | +until it reaches the current one. After that happend, the cursor will now react on changes to |
| 315 | +the events in the database and automatically advance forward as soon as a new event was logged |
| 316 | +there. |
| 317 | + |
| 318 | +This method honors the value of skipObsolete |
| 319 | + |
| 320 | +@sa EventCursor::hasNext, EventCursor::next, EventCursor::skipObsolete, EventCursor::indexChanged |
| 321 | +*/ |
| 322 | + |
| 323 | +/*! |
| 324 | +@fn QtDataSync::EventCursor::autoScanLog(std::function<bool(const EventCursor *)>, bool) |
| 325 | + |
| 326 | +@param function The callback to be called for each event visited |
| 327 | +@param scanCurrent Specify if the event currently pointed should be passed to the function before |
| 328 | +advancing to the next one |
| 329 | +@throws EventCursorException Thrown if access to the underlying database failed |
| 330 | + |
| 331 | +This method basically just calles next() in a loop to walk through all existing change events |
| 332 | +until it reaches the current one. After that happend, the cursor will now react on changes to |
| 333 | +the events in the database and automatically advance forward as soon as a new event was logged |
| 334 | +there. |
| 335 | + |
| 336 | +For every event visited that way, the function callback is called once with this cursor passed |
| 337 | +to it. The method can return true to continue the loop or false to end it. |
| 338 | + |
| 339 | +This method honors the value of skipObsolete |
| 340 | + |
| 341 | +@sa EventCursor::hasNext, EventCursor::next, EventCursor::skipObsolete, EventCursor::indexChanged |
| 342 | +*/ |
| 343 | + |
| 344 | +/*! |
| 345 | +@fn QtDataSync::EventCursor::autoScanLog(QObject *, std::function<bool(const EventCursor *)>, bool) |
| 346 | +@param scope The object scope to limit the callback to. Only call as long as the scope still exists |
| 347 | +@copydetails QtDataSync::EventCursor::autoScanLog(std::function<bool(const EventCursor *)>, bool) |
| 348 | +*/ |
| 349 | + |
| 350 | +/*! |
| 351 | +@fn QtDataSync::EventCursor::clearEventLog |
| 352 | + |
| 353 | +@param offset The number of events to keep before the current one |
| 354 | +@throws EventCursorException Thrown if access to the underlying database failed |
| 355 | + |
| 356 | +When called, all events that are older than the one currently pointed to are deleted. You can |
| 357 | +additionally keep entries by specifing an offset. If the offset is for example 5, there will be |
| 358 | +5 entries left that are older than the current one after the cleanup. |
| 359 | + |
| 360 | +@note This method does **not** the value of skipObsolete |
| 361 | + |
| 362 | +@sa EventCursor::index |
| 363 | +*/ |
| 364 | + |
| 365 | +/*! |
| 366 | +@fn QtDataSync::EventCursor::eventLogChanged |
| 367 | + |
| 368 | +This signal does **not** mean that the event the current cursor is pointing to has changed. It |
| 369 | +only notifies you that there are new events that have been added to the underlying event log. |
| 370 | +This signal is used by autoScanLog() to automatically advance through new events. |
| 371 | + |
| 372 | +@sa EventCursor::autoScanLog, EventCursor::indexChanged |
| 373 | +*/ |
0 commit comments