WARNING: USE THIS SOFTWARE AT YOUR OWN RISK! THIS IS EXPERIMENTAL SOFTWARE NOT INTENDED FOR PRODUCTION USE! Zuble is currently an early stage prototype. As such Zuble is minimally tested and inherently unstable. It is provided for experimental, development, and demonstration purposes only. Zuble QML Types   |  Zuble C++ Classes   |  Zuble Overview
Zuble  0.1
Zuble Framework C++/QML extension API
ZblLogMapData.cpp
Go to the documentation of this file.
1 #include "ZblLogMapData.h"
2 
3 namespace Zbl
4 {
5 
7 
8 bool ZblLogMapData::isValidMapType(int mapType)
9 {
10  MarkType marktype = MarkType(mapType);
11 
12  switch(marktype)
13  {
14  case Invalid:
15  case Selection:
16  case Bookmark:
17  case Searchmap:
18  return true;
19  default:
20  return false;
21  }
22 }
23 
24 
25 bool ZblLogMapData::insertMark(qint64 recordID,
26  qint64 lastRecordID,
27  qint64 seekPosition)
28 {
29  zDebug() << "Insert mark, recordID: " << recordID
30  << ", lastRecordID: " << lastRecordID
31  << ", seekPosition: " << seekPosition;
32 
33  if(lastRecordID == -1)
34  lastRecordID = recordID;
35  else if(recordID > lastRecordID)
36  {
37  zDebug() << "Warning: lastRecordID should be less than or equal to "
38  "recordID. Swapping values.";
39 
40  qint64 temp = recordID;
41  recordID = lastRecordID;
42  lastRecordID = temp;
43  }
44 
45  if(m_map.isEmpty())
46  {
47  zDebug() << "Creating mark at record ID: " << recordID;
48 
49  m_map.insert(recordID, MarkNode(recordID, lastRecordID, seekPosition));
50 
51  //emit selectionUpdated();
52 
53  return true;
54  }
55 
56  bool retval = false;
57 
58  QMap<qint64, MarkNode>::iterator it = m_map.lowerBound(recordID);
59 
60  if(it != m_map.end() && it.key() == recordID)
61  {
62  // a mark exists at the target location
63 
64  zDebug() << "A mark already exists at record ID: " << recordID;
65 
66  if(it->m_lastID < lastRecordID)
67  {
68  // existing mark too small, stretch mark to encompass new size
69 
70  zDebug() << "Stretching mark at record ID: " << recordID
71  << ", lastID: " << lastRecordID;
72 
73  it->m_lastID = lastRecordID;
74 
75  mergeMarkNodes(it);
76 
77  //emit selectionUpdated();
78 
79  retval = true;
80  }
81 
82  return retval;
83  }
84 
85  // Get here if either at end of map or no mark exist at target. Index
86  // points to mark after target or end of map so back up to preceeding mark
87  // and check it. If no intersection with new mark then create a new mark,
88  // otherwise new mark starts within previous mark so check end of new mark
89  // and grow previous mark if necessary. If a change was made to the map
90  // scan subsequent marks to ensure non-overlapping mark structure is
91  // maintained.
92 
93  if(it == m_map.begin())
94  {
95  // new mark does not intersect previous mark
96  // so create new mark
97 
98  zDebug() << "Creating mark at record ID: " << recordID;
99 
100  it = m_map.insert(recordID, MarkNode(recordID, lastRecordID, seekPosition));
101 
102  mergeMarkNodes(it);
103 
104  //emit selectionUpdated();
105 
106  return true;
107  }
108 
109  it--;
110 
111  qint64 recID = it->m_recordID;
112  qint64 lastID = it->m_lastID;
113 
114  if(recordID > lastID+1)
115  {
116  // new mark does not intersect previous mark
117  // so create new mark
118 
119  zDebug() << "Creating mark at record ID: " << recordID;
120 
121  it = m_map.insert(recordID, MarkNode(recordID, lastRecordID, seekPosition));
122 
123  mergeMarkNodes(it);
124 
125  //emit selectionUpdated();
126 
127  retval = true;
128  }
129  else if(lastRecordID > lastID)
130  {
131  // new mark overflows previous, stretch previous mark
132  // to encompass new mark
133 
134  zDebug() << "Stretching mark at record ID: " << recID
135  << ", lastID: " << lastRecordID;
136 
137  it->m_lastID = lastRecordID;
138 
139  mergeMarkNodes(it);
140 
141  //emit selectionUpdated();
142 
143  retval = true;
144  }
145  else
146  {
147  zDebug() << "Existing mark subsumes new mark at record ID: "
148  << recID;
149  }
150 
151  return retval;
152 
153 }
154 
156 {
157  if(!m_map.isEmpty())
158  {
159  m_map.clear();
160 
161  //emit selectionUpdated();
162  return true;
163  }
164  return false;
165 }
166 
167 void ZblLogMapData::mergeMarkNodes(QMap<qint64, MarkNode>::iterator it)
168 {
169  zDebug() << "Merging nodes at record ID: " << it->m_recordID;
170 
171  QMap<qint64, MarkNode>::iterator next = it+1;
172 
173  while(next != m_map.end())
174  {
175  if(it->m_lastID < next->m_recordID)
176  break;
177 
178  if(next->m_lastID > it->m_lastID)
179  {
180  zDebug() << "Stretching mark at record ID: " << it->m_recordID
181  << ", lastID: " << next->m_lastID;
182 
183  it->m_lastID = next->m_lastID;
184  }
185 
186  zDebug() << "Removing superfluous mark at record ID: "
187  << next->m_recordID;
188 
189  next = m_map.erase(next);
190  }
191 }
192 
193 bool ZblLogMapData::hasMark(qint64 recordID) const
194 {
195  if(m_map.isEmpty())
196  return false;
197 
198  QMap<qint64, MarkNode>::const_iterator it = m_map.lowerBound(recordID);
199 
200  if(it == m_map.begin())
201  return (recordID >= it->m_recordID && recordID <= it->m_lastID);
202 
203  if(it == m_map.end() || recordID != it->m_recordID)
204  {
205  it--;
206  return recordID <= it->m_lastID;
207  }
208 
209  return true;
210 }
211 
212 qint64 ZblLogMapData::findNextMark(qint64 startID, bool forward) const
213 {
214  QMap<qint64, MarkNode>::const_iterator it;
215 
216  if( m_map.isEmpty())
217  return -1;
218 
219  if(startID == 0 && !forward)
220  return -1;
221 
222  if(forward)
223  {
224  if(startID == -1)
225  it = m_map.begin();
226  else
227  startID++;
228 
229  it = m_map.lowerBound(startID);
230 
231  if(it == m_map.end())
232  return -1;
233  else
234  return it.key();
235  }
236  else
237  {
238  if(startID == -1)
239  it = m_map.end();
240  else
241  it = m_map.lowerBound(startID);
242 
243  if(--it == m_map.begin())
244  return -1;
245  else
246  return it.key();
247  }
248 
249  return -1;
250 }
251 
252 
253 bool ZblLogMapData::removeMark(qint64 recordID, qint64 lastRecordID)
254 {
255  zDebug() << "Remove mark, recordID: " << recordID
256  << ", lastRecordID: " << lastRecordID;
257 
258  if(m_map.isEmpty())
259  {
260  zDebug() << "Warning: removeMark called for empty log map.";
261  return false;
262  }
263 
264  bool retval = false;
265 
266  if(lastRecordID == -1)
267  lastRecordID = recordID;
268  else if(recordID > lastRecordID)
269  {
270  zDebug() << "Warning: lastRecordID should be less than or equal to "
271  "recordID. Swapping values.";
272 
273  qint64 temp = recordID;
274  recordID = lastRecordID;
275  lastRecordID = temp;
276  }
277 
278  QMap<qint64, MarkNode>::iterator it = m_map.lowerBound(recordID);
279 
280  if(it == m_map.end())
281  {
282  // no mark at target location, check previous
283  it--;
284 
285  if(it->m_recordID > lastRecordID)
286  retval = false; // no overlap of previous
287  else if(lastRecordID >= it->m_lastID)
288  retval = truncateRange(it, lastRecordID-1); // top overlapps previous
289  else
290  retval = splitRange(it, recordID-1, lastRecordID+1); // entire range overlaps previous
291  }
292  else if(it.key() == recordID)
293  {
294  // a mark exists at the target location
295 
296  zDebug() << "A mark exists at record ID: " << recordID;
297 
298  if(it->m_lastID == lastRecordID)
299  {
300  // existing mark matches removed mark
301 
302  zDebug() << "Removing mark at record ID: " << recordID
303  << ", lastID: " << lastRecordID;
304 
305  m_map.remove(it.key());
306 
307  retval = true;
308  }
309  else if(it->m_lastID < lastRecordID)
310  {
311  // existing mark smaller than remove mark
312 
313  retval = removeRange(it, recordID, lastRecordID);
314  }
315  else
316  {
317  // existing mark larger than remove mark,
318  // remove existing and insert tail
319 
320  retval = decapitateRange(it, lastRecordID+1);
321  }
322 
323  }
324  else
325  {
326  // no mark exists at target location
327 
328  // check subsequent mark for overlap
329 
330  if(it->m_recordID <= lastRecordID)
331  {
332  retval = decapitateRange(it, lastRecordID+1);
333  }
334  else
335  {
336  // check previous mark for overlap
337 
338  if(it == m_map.begin())
339  {
340  retval = false; // no previous mark
341  }
342  else
343  {
344  it--;
345 
346  if(it->m_lastID < recordID)
347  retval = false; // no overlap of previous
348 
349  else if(lastRecordID >= it->m_lastID)
350  retval = truncateRange(it, lastRecordID-1); // top overlapps previous
351  else
352  retval = splitRange(it, recordID-1, lastRecordID+1); // entire range overlaps previous
353  }
354  }
355  }
356 
357  //if(retval)
358  // emit selectionUpdated();
359 
360  return retval;
361 }
362 
363 bool ZblLogMapData::removeRange(QMap<qint64, MarkNode>::iterator it, qint64 recordID, qint64 lastRecordID)
364 {
365  // loop removing marks that fall within the range
366 
367  bool markRemoved = false;
368 
369  while(it->m_recordID <= lastRecordID && it->m_lastID <= lastRecordID)
370  {
371  m_map.remove(it.key());
372  markRemoved = true;
373  it++;
374  if(it == m_map.end())
375  return markRemoved;
376  }
377 
378  // remove top of mark that overlaps the range, if any
379 
380  if(it->m_recordID <= lastRecordID)
381  {
382  if(decapitateRange(it, lastRecordID+1))
383  markRemoved = true;
384  }
385 
386  return markRemoved;
387 }
388 
389 bool ZblLogMapData::decapitateRange(QMap<qint64, MarkNode>::iterator it, qint64 newStartID)
390 {
391  if(newStartID == it->m_recordID)
392  return false;
393 
394  if(newStartID < it->m_recordID || newStartID > it->m_lastID)
395  {
396  zWarning() << "newStartID parameter exceeds range of iterator item.";
397  return false;
398  }
399 
400  qint64 lastExtantID = it->m_lastID;
401 
402  m_map.remove(it.key());
403 
404  insertMark(newStartID,lastExtantID,-1);
405 
406  return true;
407 }
408 
409 bool ZblLogMapData::truncateRange(QMap<qint64, MarkNode>::iterator it, qint64 newEndID)
410 {
411  if(newEndID < it->m_recordID || newEndID > it->m_lastID)
412  {
413  zWarning() << "newEndID parameter exceeds range of iterator item.";
414  return false;
415  }
416 
417  it->m_lastID = newEndID;
418 
419  return true;
420 }
421 
422 bool ZblLogMapData::splitRange(QMap<qint64, MarkNode>::iterator it, qint64 endHeadID, qint64 startTailID)
423 {
424  if(endHeadID < it->m_recordID || endHeadID > it->m_lastID)
425  {
426  zWarning() << "endHeadID parameter exceeds range of iterator item.";
427  return false;
428  }
429 
430  if(startTailID < it->m_recordID || startTailID > it->m_lastID)
431  {
432  zWarning() << "startTailID parameter exceeds range of iterator item.";
433  return false;
434  }
435 
436  qint64 lastExtantID = it->m_lastID;
437 
438  //m_map.remove(it.key());
439 
440  truncateRange(it, endHeadID);
441 
442  insertMark(startTailID,lastExtantID,-1);
443 
444  return true;
445 }
446 
448 {
449  ZblLogLinkList links;
450 
451  QMap<qint64, MarkNode>::const_iterator it = m_map.begin();
452 
453  for(qint64 index=0; it != m_map.end(); it++)
454  {
455  qint64 linkID = it->getFirstID();
456  qint64 linkPos = it->getSeekPos();
457  zDebug() << "link key = " << it.key() << ", ID = " << linkID
458  << ", seek = " << linkPos;
459 
460  links.addLink(linkID, linkPos);
461  }
462  return links;
463 }
464 
465 
466 
467 
469 {
470  return m_markType;
471 }
472 
473 
474 } // Zbl
MarkType
Types of log maps: Invalid, Selection, Search, Bookmark.
Definition: ZblLogMapData.h:35
void mergeMarkNodes(QMap< qint64, MarkNode >::iterator it)
bool splitRange(QMap< qint64, MarkNode >::iterator it, qint64 endHeadID, qint64 startTailID)
bool insertMark(qint64 recordID, qint64 lastRecordID, qint64 seekPosition)
Creates a new log mark in the database.
MarkType m_markType
The type of mark map: invalid, selection, bookmark, searchmark.
bool truncateRange(QMap< qint64, MarkNode >::iterator it, qint64 newEndID)
ZblLogLinkList getLogLinkList() const
Obtain a ZblLogLinkList object containing expanded log links.
bool clear()
Removes all marks from the log map.
Definition: ZAndGate.cpp:6
A mark node is a compressed set of contiguous log record links.
bool decapitateRange(QMap< qint64, MarkNode >::iterator it, qint64 newStartID)
MarkType getMapType() const
returns this log map&#39;s mark type
#define ZBL_DEFINE_LOGGED_OBJECT(class_name)
Definition: zglobal.h:99
#define zWarning()
Definition: zglobal.h:111
#define zDebug()
Definition: zglobal.h:113
bool removeRange(QMap< qint64, MarkNode >::iterator it, qint64 recordID, qint64 lastRecordID)
bool removeMark(qint64 recordID, qint64 lastRecordID)
Removes the specified mark from the log map.
ZBL_DECLARE_LOGGED_OBJECT QMap< qint64, MarkNode > m_map
A lookup table for mark nodes. It maps firstRecordID to MarkNode object.
qint64 findNextMark(qint64 startID, bool forward) const
Search forward or backward for the next mark from a specified starting position.
bool hasMark(qint64 recordID) const
Determine if the log map contains a mark for the specified log record.
The implicitly shared data object encapsulated by ZblLogMap objects.
Definition: ZblLogMapData.h:25