Make WvStreams compile with gcc 4.4.
[wvstreams.git] / uniconf / unitransactiongen.cc
blobad3e25d05a4bb44f9466e8137917bcaed08ebb32
1 #include "unitransactiongen.h"
2 #include "uniconftree.h"
3 #include "unilistiter.h"
4 #include "wvmoniker.h"
6 static IUniConfGen *creator(WvStringParm s, IObject *_obj)
8 IUniConfGen *base = wvcreate<IUniConfGen>(s, _obj);
9 if (base)
10 return new UniTransactionGen(base);
11 else
12 return NULL;
15 static WvMoniker<IUniConfGen> moniker("transaction", creator);
17 /* This enum is a field of UniConfChangeTree. It indicates the type of
18 change represented by a node in a UniConfChangeTree. */
19 enum changeMode
21 /* This indicates that "newvalue" is valid and that
22 its value should be written to the underlying generator at commit
23 time. This tree *might* have children, which must be applied.
24 "newvalue" will be a non-null pointer to a non-null WvString. */
25 NEWVALUE,
26 /* This indicates that "newtree" is valid (but possibly NULL) and that
27 the underlying generator's corresponding subtree should be made
28 identical at commit time. This tree will *not* have children (though
29 newtree might). */
30 NEWTREE,
31 /* This indicates that "was_null_or_empty" is valid and that the key
32 in the underlying generator should be created at commit time if it
33 does not already exist at commit time. This tree *will* have
34 children, which must be applied, and at least one of which will
35 be non-BLANK. "was_null_or_empty" will be the return value of the
36 WvString negation operation on the last known value of the
37 corresponding key in the underlying generator; it is necessary
38 in order to filter callbacks in certain cases. */
39 NEWNODE,
40 /* This indicates that none of the fields are valid and that
41 nothing should be done for this tree. This tree *will* have children,
42 which must be applied, but they will all have mode of NEWTREE with
43 newtree == NULL. */
44 BLANK
47 class UniConfChangeTree : public UniConfTree<UniConfChangeTree>
49 public:
50 changeMode mode;
52 // This used to be a union, but it was causing memory errors that were
53 // extremely difficult to track down. Some of this code might serve no
54 // purpose without this being a union, but I'd rather have it still work
55 // and not leak than break it. -- mrwise
56 WvString newvalue;
57 UniConfValueTree *newtree;
58 bool was_null_or_empty;
60 // Constructs a tree and links it to a parent.
61 UniConfChangeTree(UniConfChangeTree *parent, const UniConfKey &key)
62 : UniConfTree<UniConfChangeTree>(parent, key), newtree(0) {}
64 // Destroys a tree and everything it owns.
65 ~UniConfChangeTree()
67 if (newtree)
68 delete newtree;
72 // Constructed by UniTransactionGen::iterator() to iterate over a section that
73 // is to be completely replaced by a particular UniConfValueTree.
74 class GenStyleValueTreeIter : public UniConfGen::Iter
76 public:
77 GenStyleValueTreeIter(UniConfValueTree *node)
78 : i(*node)
80 // printf("GenStyleValueTreeIter\n");
83 ~GenStyleValueTreeIter()
85 // printf("~GenStyleValueTreeIter\n");
88 void rewind() { i.rewind(); }
89 bool next() { return i.next(); }
90 UniConfKey key() const { return i->key(); }
91 WvString value() const { return i->value(); }
93 private:
94 UniConfValueTree::Iter i;
97 // Constructed by UniTransactionGen::iterator() to iterate over a section that
98 // is being modified but not replaced. We iterate first over all of the values
99 // that we're changing, except those we're deleting, and second over all
100 // existing values not iterated over in the first stage, except those we're
101 // deleting.
102 class GenStyleChangeTreeIter : public UniConfGen::Iter
104 public:
105 GenStyleChangeTreeIter(UniConfChangeTree *_root,
106 const UniConfKey &_section,
107 IUniConfGen *_base)
108 : root(_root), section(_section), base(_base),
109 doing_i1(true), i1(*root), i2(base->iterator(section))
111 // printf("GenStyleChangeTreeIter(%s)\n", WvString(section).cstr());
114 ~GenStyleChangeTreeIter()
116 // printf("~GenStyleChangeTreeIter(%s)\n", WvString(section).cstr());
117 if (i2) delete i2;
120 void rewind()
122 i1.rewind();
123 doing_i1 = true;
126 bool next()
128 if (doing_i1)
130 for (;;)
132 if (i1.next())
134 if (i1->mode == NEWVALUE ||
135 i1->mode == NEWNODE ||
136 (i1->mode == NEWTREE && i1->newtree))
137 return true;
139 else
140 break;
142 doing_i1 = false;
143 if (i2) i2->rewind();
145 if (i2)
147 for (;;)
149 if (i2->next())
151 UniConfChangeTree *node = root->findchild(i2->key());
152 if (!node || node->mode == BLANK)
153 return true;
155 else
156 break;
159 return false;
162 UniConfKey key() const
164 if (doing_i1)
165 return i1->key();
166 else if (i2)
167 return i2->key();
168 else
169 return UniConfKey();
172 WvString value() const
174 if (doing_i1)
176 if (i1->mode == NEWVALUE)
177 return i1->newvalue;
178 else if (i1->mode == NEWTREE)
179 return i1->newtree->value();
180 else // i.e. i1->mode == NEWNODE
182 WvString value(base->get(UniConfKey(section, i1->key())));
183 return !value ? WvString::empty : value;
186 else
188 return i2->value();
192 private:
193 UniConfChangeTree *root;
194 UniConfKey section;
195 IUniConfGen *base;
197 bool doing_i1;
198 UniConfChangeTree::Iter i1;
199 UniConfGen::Iter *i2;
202 UniTransactionGen::UniTransactionGen(IUniConfGen *_base)
203 : root(NULL), base(_base)
205 base->add_callback(this, wv::bind(&UniTransactionGen::gencallback, this,
206 _1, _2));
209 UniTransactionGen::~UniTransactionGen()
211 base->del_callback(this);
212 WVRELEASE(base);
213 WVDELETE(root);
216 WvString UniTransactionGen::get(const UniConfKey &key)
218 UniConfChangeTree *node = root;
219 for (int seg = 0;; node = node->findchild(key.segment(seg++)))
221 if (!node)
222 // If we couldn't find the next node, then we aren't
223 // changing the requested key, and so the value is whatever
224 // it currently is.
225 return base->get(key);
226 else if (node->mode == NEWTREE)
228 // Else if the next node has mode of NEWTREE, then we're changing
229 // the requested key to whatever its value is in the stored
230 // tree.
231 if (node->newtree)
233 UniConfValueTree *subnode = node->newtree->find(
234 key.last(key.numsegments() - seg));
235 if (subnode)
236 return subnode->value();
238 return WvString::null;
240 else if (seg == key.numsegments())
242 // Else if this is the last node, then figure out what the node
243 // would do and return the appropriate value. (The node's mode
244 // will be either NEWVALUE, NEWNODE, or BLANK.)
245 if (node->mode == NEWVALUE)
246 return node->newvalue;
247 WvString value(base->get(key.first(seg)));
248 return (node->mode == NEWNODE && !value) ? WvString::empty : value;
253 void UniTransactionGen::set(const UniConfKey &key, WvStringParm value)
255 hold_delta();
256 root = set_change(root, key, 0, value);
257 unhold_delta();
260 void UniTransactionGen::setv(const UniConfPairList &pairs)
262 hold_delta();
263 UniConfPairList::Iter i(pairs);
264 for (i.rewind(); i.next(); )
265 root = set_change(root, i->key(), 0, i->value());
266 unhold_delta();
269 void UniTransactionGen::commit()
271 if (root)
273 // Apply our changes to the inner generator. We can't optimise
274 // away callbacks at this point, because we may get notified of
275 // changes caused by our changes.
276 hold_delta();
277 apply_changes(root, UniConfKey());
279 // make sure the inner generator also commits
280 base->commit();
282 // save deleting the root till now so we can hide any
283 // redundant notifications caused by the base->commit()
284 delete root;
285 root = NULL;
286 unhold_delta();
289 // no need to base->commit() if we know we haven't changed anything!
292 bool UniTransactionGen::refresh()
294 if (root)
296 hold_delta();
297 cancel_changes(root, UniConfKey());
298 delete root;
299 root = NULL;
300 unhold_delta();
302 // no need to base->commit() here, since the inner generator never
303 // saw any changes
306 // must always base->refresh(), even if we didn't change anything
307 return base->refresh();
310 UniConfGen::Iter *UniTransactionGen::iterator(const UniConfKey &key)
312 UniConfChangeTree *node = root;
313 for (int seg = 0;; node = node->findchild(key.segment(seg++)))
315 if (!node)
316 // If we couldn't find the next node, then we aren't changing the
317 // children of the requested key, so they're whatever they
318 // currently are.
319 return base->iterator(key);
320 else if (node->mode == NEWTREE)
322 // Else if the next node has mode of NEWTREE, then we're changing
323 // the children of the requested key to whatever they are in the
324 // stored tree.
325 if (node->newtree)
327 UniConfValueTree *subnode = node->newtree->find(
328 key.last(key.numsegments() - seg));
329 if (subnode)
331 UniConfGen::Iter *i = new GenStyleValueTreeIter(subnode);
332 UniListIter *i2 = new UniListIter(this);
333 i2->autofill(i);
334 delete i;
335 return i2;
338 return new UniConfGen::NullIter();
340 else if (seg == key.numsegments())
342 // Else if this is the last node, then iterate over its direct
343 // children.
344 UniConfGen::Iter *i = new GenStyleChangeTreeIter(node, key, base);
345 UniListIter *i2 = new UniListIter(this);
346 i2->autofill(i);
347 delete i;
348 return i2;
353 void UniTransactionGen::apply_values(UniConfValueTree *newcontents,
354 const UniConfKey &section)
356 base->set(section, newcontents->value());
358 UniConfGen::Iter *j = base->iterator(section);
359 if (j)
361 for (j->rewind(); j->next();)
363 if (newcontents->findchild(j->key()) == NULL)
364 // Delete all children of the current value in the
365 // underlying generator that do not exist in our
366 // replacement tree.
367 base->set(UniConfKey(section, j->key()), WvString::null);
369 delete j;
372 // Repeat for each child in the replacement tree.
373 UniConfValueTree::Iter i(*newcontents);
374 for (i.rewind(); i.next();)
375 apply_values(i.ptr(), UniConfKey(section, i->key()));
378 void UniTransactionGen::apply_changes(UniConfChangeTree *node,
379 const UniConfKey &section)
381 if (node->mode == NEWTREE)
383 // If the current change is a NEWTREE change, then replace the
384 // tree in the underlying generator with the stored one.
385 if (node->newtree == NULL)
386 base->set(section, WvString::null);
387 else
388 apply_values(node->newtree, section);
389 // Since such changes have no children, return immediately.
390 return;
392 else if (node->mode == NEWVALUE)
394 // Else if the current change is a NEWVALUE change, ...
395 base->set(section, node->newvalue);
397 else if (node->mode == NEWNODE)
399 // Else if the current change is a NEWNODE change, ...
400 if (!base->exists(section))
401 // ... and the current value in the underlying generator doesn't
402 // exist, then create it.
403 base->set(section, WvString::empty);
404 // Note: This *is* necessary. We can't ignore this change and have
405 // the underlying generator handle it, because it's possible that
406 // this NEWNODE was the result of a set() which was later deleted.
409 // Repeat for each child in the change tree.
410 UniConfChangeTree::Iter i(*node);
411 for (i.rewind(); i.next();)
412 apply_changes(i.ptr(), UniConfKey(section, i->key()));
415 struct my_userdata
417 UniConfValueTree *node;
418 const UniConfKey &key;
421 void UniTransactionGen::deletion_visitor(const UniConfValueTree *node,
422 void *userdata)
424 my_userdata *data = (my_userdata *)userdata;
425 delta(UniConfKey(data->key, node->fullkey(data->node)), WvString::null);
428 // Mirror image of apply_values() that issues all of the callbacks associated
429 // with discarding a replacement value tree.
430 void UniTransactionGen::cancel_values(UniConfValueTree *newcontents,
431 const UniConfKey &section)
433 WvString value(base->get(section));
434 if (!newcontents || newcontents->value() != value)
435 delta(section, value);
437 if (newcontents)
439 UniConfValueTree::Iter i(*newcontents);
440 for (i.rewind(); i.next();)
442 UniConfKey subkey(section, i->key());
443 if (!base->exists(subkey))
445 my_userdata data = { i.ptr(), subkey };
446 i->visit(wv::bind(&UniTransactionGen::deletion_visitor, this,
447 _1, _2),
448 (void*)&data, false, true);
453 UniConfGen::Iter *i = base->iterator(section);
454 if (i)
456 for (i->rewind(); i->next();)
457 cancel_values(newcontents ?
458 newcontents->findchild(i->key()) : NULL,
459 UniConfKey(section, i->key()));
460 delete i;
464 // Mirror image of apply_changes() that issues all of the callbacks associated
465 // with discarding a change tree.
466 void UniTransactionGen::cancel_changes(UniConfChangeTree *node,
467 const UniConfKey &section)
469 if (node->mode == NEWTREE)
471 if (!base->exists(section))
473 if (node->newtree != NULL)
475 my_userdata data = { node->newtree, section };
476 node->newtree->visit(
477 wv::bind(&UniTransactionGen::deletion_visitor, this,
478 _1, _2),
479 (void *)&data, false, true);
482 else
483 cancel_values(node->newtree, section);
484 return;
487 WvString value;
488 if (node->mode != BLANK)
489 value = base->get(section);
491 if (node->mode == NEWVALUE &&
492 !value.isnull() &&
493 value != node->newvalue)
494 delta(section, value);
496 UniConfChangeTree::Iter i(*node);
497 for (i.rewind(); i.next();)
498 cancel_changes(i.ptr(), UniConfKey(section, i->key()));
500 if (node->mode != BLANK && value.isnull())
501 delta(section, WvString::null);
504 void UniTransactionGen::gencallback(const UniConfKey &key,
505 WvStringParm value)
507 UniConfChangeTree *node = root;
508 for (int seg = 0;; node = node->findchild(key.segment(seg++)))
510 if (!node)
511 // If we couldn't find the next node, then we aren't changing
512 // the changed key or any of its children, and so a callback
513 // should be made.
514 break;
515 else if (node->mode == NEWTREE)
516 // Else if the next node has mode of NEWTREE, then we're changing
517 // the changed key and all of its children to whatever their
518 // values are in the stored tree, and so the callback should be
519 // ignored.
520 return;
521 else if (seg == key.numsegments())
523 // Else if this is the last node, then figure out what we
524 // should do.
525 if (node->mode == NEWVALUE)
526 // If we're replacing this key's value, then we should
527 // ignore the callback.
528 return;
529 else if (node->mode == NEWNODE)
531 // Else if we want to create this key, then use its
532 // was_null_or_empty flag to figure out if we need
533 // to issue a callback, and update it if necessary.
534 if (node->was_null_or_empty && !value)
535 return;
536 node->was_null_or_empty = !value;
537 if (value.isnull())
539 delta(key, WvString::empty);
540 return;
542 break;
544 else // i.e. node->mode == BLANK
545 // Else if we're doing nothing to this key, then a
546 // callback should be made.
547 break;
551 // Make a normal callback.
552 delta(key, value);
555 // Create and return a UniConfValueTree containing the value 'value' for
556 // the key given by the segments of 'key' at and after position 'seg', with
557 // parent 'parent' and key given by the segment of 'key' at position seg-1
558 // (which is the "root" key if seg == 0). Issue callbacks as necessary using
559 // all the segments of 'key'.
560 UniConfValueTree *UniTransactionGen::create_value(UniConfValueTree *parent,
561 const UniConfKey &key,
562 int seg,
563 WvStringParm value)
565 UniConfValueTree *tree = 0;
566 for (; seg != key.numsegments();)
568 // Create any needed intermediate nodes, each with value equal to
569 // the empty string.
570 parent = new UniConfValueTree(parent,
571 key.segment(seg-1),
572 WvString::empty);
573 delta(key.first(seg++), WvString::empty);
574 if (!tree)
575 tree = parent;
577 // Create the last node with the specified value.
578 parent = new UniConfValueTree(parent,
579 key.segment(seg-1),
580 value);
581 delta(key, value);
582 if (!tree)
583 tree = parent;
584 return tree;
587 void UniTransactionGen::deletion_simulator(const UniConfKey &key)
589 UniConfGen::Iter *i = base->iterator(key);
590 if (i)
592 for (i->rewind(); i->next();)
593 deletion_simulator(UniConfKey(key, i->key()));
594 delete i;
596 delta(key, WvString::null);
599 // Like create_value(), but make a UniConfChangeTree containing a *change*
600 // to value 'value'.
601 UniConfChangeTree *UniTransactionGen::create_change(UniConfChangeTree *parent,
602 const UniConfKey &key,
603 int seg,
604 WvStringParm value)
606 // if the key has a trailing slash, this should be a no-op: we don't
607 // want this to have any effect
608 if ((key.hastrailingslash()) && !value.isnull())
609 return parent;
611 UniConfChangeTree *tree = 0;
612 for (; seg != key.numsegments(); seg++)
614 parent = new UniConfChangeTree(parent, key.segment(seg-1));
615 if (value.isnull())
616 // We don't do anything for intermediate nodes when deleting, ...
617 parent->mode = BLANK;
618 else
620 // ... but when set()'ing a non-null value, we want them to exist.
621 parent->mode = NEWNODE;
622 UniConfKey nodekey(key.first(seg));
623 WvString curr = base->get(nodekey);
624 parent->was_null_or_empty = !curr;
625 if (curr.isnull())
626 delta(nodekey, WvString::empty);
628 if (!tree)
629 tree = parent;
631 parent = new UniConfChangeTree(parent, key.segment(seg-1));
632 // Create the last node with the specified change.
633 if (value.isnull())
635 parent->mode = NEWTREE;
636 parent->newtree = 0;
637 if (base->exists(key))
638 deletion_simulator(key);
640 else
642 parent->mode = NEWVALUE;
643 parent->newvalue = WvString(value);
644 if (base->get(key) != value)
645 delta(key, value);
647 if (!tree)
648 tree = parent;
649 return tree;
652 // Modify an existing UniConfValueTree to incorporate the set() of a
653 // particular value for a particular key. Return a possibly altered
654 // pointer to the root of the tree. 'seg' and 'key' are used like they
655 // are in create_value(), and callbacks are made similarly.
656 UniConfValueTree *UniTransactionGen::set_value(UniConfValueTree *node,
657 const UniConfKey &key,
658 int seg,
659 WvStringParm value)
661 // printf("set_value('%s', %d)\n", WvString(key).cstr(), value.isnull());
662 if (value.isnull())
664 // Delete the key if it exists.
665 if (node)
667 UniConfValueTree *subnode = node->find(
668 key.last(key.numsegments() - seg));
669 if (subnode)
671 hold_delta();
672 my_userdata data = { subnode, key };
673 subnode->visit(wv::bind(&UniTransactionGen::deletion_visitor,
674 this, _1, _2),
675 (void *)&data, false, true);
676 // printf("DELETE SUBNODE!\n");
677 delete subnode;
678 unhold_delta();
679 return subnode == node ? NULL : node;
681 else
682 return node;
684 else
685 return NULL;
687 else
689 // Switch to create_value() if we ever can't find the next node.
690 if (!node)
691 return create_value(NULL, key, seg, value);
693 UniConfValueTree *subnode = node;
694 for (; seg != key.numsegments();)
696 UniConfKey segment(key.segment(seg++));
697 UniConfValueTree *child = subnode->findchild(segment);
698 // Switch to create_value() if we ever can't find the next node.
699 if (!child)
701 create_value(subnode, key, seg, value);
702 return node;
704 else
705 subnode = child;
707 // The node already existed and we've found it; set it.
708 if (value != subnode->value())
710 subnode->setvalue(value);
711 delta(key, value);
713 return node;
717 void UniTransactionGen::deletion_simulator2(const UniConfKey &key)
719 UniConfGen::Iter *i = this->iterator(key);
720 if (i)
722 for (i->rewind(); i->next();)
723 deletion_simulator2(UniConfKey(key, i->key()));
724 delete i;
726 delta(key, WvString::null);
729 // Like set_value(), but, again, for UniConfChangeTrees instead.
730 UniConfChangeTree *UniTransactionGen::set_change(UniConfChangeTree *node,
731 const UniConfKey &key,
732 int seg,
733 WvStringParm value)
735 // printf("set_change(key=%s,mode=%d) = '%s'\n",
736 // WvString(key).cstr(), node ? node->mode : 999, value.cstr());
738 // Switch to create_change() if we ever can't find the next node,
739 // and switch to set_value() if we ever find a NEWTREE.
740 if (!node)
741 return create_change(NULL, key, seg, value);
742 else if (node->mode == NEWTREE)
744 node->newtree = set_value(node->newtree, key, seg, value);
745 return node;
748 UniConfChangeTree *subnode = node;
749 for (; seg != key.numsegments();)
751 if (subnode->mode == BLANK && !value.isnull())
753 // If we're setting a non-null value and we weren't previously
754 // doing anything to this node, then now we want to create it.
755 subnode->mode = NEWNODE;
756 UniConfKey nodekey(key.first(seg));
757 WvString curr = base->get(nodekey);
758 subnode->was_null_or_empty = !curr;
759 if (curr.isnull())
760 delta(nodekey, WvString::empty);
763 UniConfKey segment(key.segment(seg++));
764 UniConfChangeTree *next = subnode->findchild(segment);
765 // Switch to create_change() if we ever can't find the next node,
766 // and switch to set_value() if we ever find a NEWTREE.
767 if (!next)
769 create_change(subnode, key, seg, value);
770 return node;
772 else if (next->mode == NEWTREE)
774 next->newtree = set_value(next->newtree,
775 key, seg, value);
776 return node;
778 else
779 subnode = next;
781 // The node already existed, didn't have mode of NEWTREE, and we've
782 // found it; change it.
783 if (value.isnull())
785 if (subnode->mode != BLANK || base->exists(key))
786 deletion_simulator2(key);
787 subnode->zap();
788 subnode->mode = NEWTREE;
789 subnode->newtree = 0;
791 else if (subnode->mode == NEWVALUE)
793 if (subnode->newvalue != value)
795 subnode->newvalue = value;
796 delta(key, value);
799 else if (subnode->mode == BLANK)
801 if (base->get(key) != value)
802 delta(key, value);
803 subnode->mode = NEWVALUE;
804 subnode->newvalue = WvString(value);
806 else // i.e. subnode->mode == NEWNODE
808 WvString currval(base->get(key));
809 if ((!currval != !value) && (currval != value))
810 delta(key, value);
811 subnode->mode = NEWVALUE;
812 subnode->newvalue = WvString(value);
814 return node;
817 // We'll say we're okay whenever the underlying generator is.
818 bool UniTransactionGen::isok()
820 return base->isok();
823 void UniTransactionGen::flush_buffers()