1 #include "unitransactiongen.h"
2 #include "uniconftree.h"
3 #include "unilistiter.h"
6 static IUniConfGen
*creator(WvStringParm s
, IObject
*_obj
)
8 IUniConfGen
*base
= wvcreate
<IUniConfGen
>(s
, _obj
);
10 return new UniTransactionGen(base
);
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. */
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. */
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
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. */
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
47 class UniConfChangeTree
: public UniConfTree
<UniConfChangeTree
>
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
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.
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
77 GenStyleValueTreeIter(UniConfValueTree
*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(); }
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
102 class GenStyleChangeTreeIter
: public UniConfGen::Iter
105 GenStyleChangeTreeIter(UniConfChangeTree
*_root
,
106 const UniConfKey
&_section
,
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());
134 if (i1
->mode
== NEWVALUE
||
135 i1
->mode
== NEWNODE
||
136 (i1
->mode
== NEWTREE
&& i1
->newtree
))
143 if (i2
) i2
->rewind();
151 UniConfChangeTree
*node
= root
->findchild(i2
->key());
152 if (!node
|| node
->mode
== BLANK
)
162 UniConfKey
key() const
172 WvString
value() const
176 if (i1
->mode
== 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
;
193 UniConfChangeTree
*root
;
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,
209 UniTransactionGen::~UniTransactionGen()
211 base
->del_callback(this);
216 WvString
UniTransactionGen::get(const UniConfKey
&key
)
218 UniConfChangeTree
*node
= root
;
219 for (int seg
= 0;; node
= node
->findchild(key
.segment(seg
++)))
222 // If we couldn't find the next node, then we aren't
223 // changing the requested key, and so the value is whatever
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
233 UniConfValueTree
*subnode
= node
->newtree
->find(
234 key
.last(key
.numsegments() - seg
));
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
)
256 root
= set_change(root
, key
, 0, value
);
260 void UniTransactionGen::setv(const UniConfPairList
&pairs
)
263 UniConfPairList::Iter
i(pairs
);
264 for (i
.rewind(); i
.next(); )
265 root
= set_change(root
, i
->key(), 0, i
->value());
269 void UniTransactionGen::commit()
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.
277 apply_changes(root
, UniConfKey());
279 // make sure the inner generator also commits
282 // save deleting the root till now so we can hide any
283 // redundant notifications caused by the base->commit()
289 // no need to base->commit() if we know we haven't changed anything!
292 bool UniTransactionGen::refresh()
297 cancel_changes(root
, UniConfKey());
302 // no need to base->commit() here, since the inner generator never
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
++)))
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
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
327 UniConfValueTree
*subnode
= node
->newtree
->find(
328 key
.last(key
.numsegments() - seg
));
331 UniConfGen::Iter
*i
= new GenStyleValueTreeIter(subnode
);
332 UniListIter
*i2
= new UniListIter(this);
338 return new UniConfGen::NullIter();
340 else if (seg
== key
.numsegments())
342 // Else if this is the last node, then iterate over its direct
344 UniConfGen::Iter
*i
= new GenStyleChangeTreeIter(node
, key
, base
);
345 UniListIter
*i2
= new UniListIter(this);
353 void UniTransactionGen::apply_values(UniConfValueTree
*newcontents
,
354 const UniConfKey
§ion
)
356 base
->set(section
, newcontents
->value());
358 UniConfGen::Iter
*j
= base
->iterator(section
);
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
367 base
->set(UniConfKey(section
, j
->key()), WvString::null
);
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
§ion
)
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
);
388 apply_values(node
->newtree
, section
);
389 // Since such changes have no children, return immediately.
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()));
417 UniConfValueTree
*node
;
418 const UniConfKey
&key
;
421 void UniTransactionGen::deletion_visitor(const UniConfValueTree
*node
,
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
§ion
)
433 WvString
value(base
->get(section
));
434 if (!newcontents
|| newcontents
->value() != value
)
435 delta(section
, value
);
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,
448 (void*)&data
, false, true);
453 UniConfGen::Iter
*i
= base
->iterator(section
);
456 for (i
->rewind(); i
->next();)
457 cancel_values(newcontents
?
458 newcontents
->findchild(i
->key()) : NULL
,
459 UniConfKey(section
, i
->key()));
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
§ion
)
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,
479 (void *)&data
, false, true);
483 cancel_values(node
->newtree
, section
);
488 if (node
->mode
!= BLANK
)
489 value
= base
->get(section
);
491 if (node
->mode
== NEWVALUE
&&
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
,
507 UniConfChangeTree
*node
= root
;
508 for (int seg
= 0;; node
= node
->findchild(key
.segment(seg
++)))
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
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
521 else if (seg
== key
.numsegments())
523 // Else if this is the last node, then figure out what we
525 if (node
->mode
== NEWVALUE
)
526 // If we're replacing this key's value, then we should
527 // ignore the callback.
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
)
536 node
->was_null_or_empty
= !value
;
539 delta(key
, WvString::empty
);
544 else // i.e. node->mode == BLANK
545 // Else if we're doing nothing to this key, then a
546 // callback should be made.
551 // Make a normal callback.
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
,
565 UniConfValueTree
*tree
= 0;
566 for (; seg
!= key
.numsegments();)
568 // Create any needed intermediate nodes, each with value equal to
570 parent
= new UniConfValueTree(parent
,
573 delta(key
.first(seg
++), WvString::empty
);
577 // Create the last node with the specified value.
578 parent
= new UniConfValueTree(parent
,
587 void UniTransactionGen::deletion_simulator(const UniConfKey
&key
)
589 UniConfGen::Iter
*i
= base
->iterator(key
);
592 for (i
->rewind(); i
->next();)
593 deletion_simulator(UniConfKey(key
, i
->key()));
596 delta(key
, WvString::null
);
599 // Like create_value(), but make a UniConfChangeTree containing a *change*
601 UniConfChangeTree
*UniTransactionGen::create_change(UniConfChangeTree
*parent
,
602 const UniConfKey
&key
,
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())
611 UniConfChangeTree
*tree
= 0;
612 for (; seg
!= key
.numsegments(); seg
++)
614 parent
= new UniConfChangeTree(parent
, key
.segment(seg
-1));
616 // We don't do anything for intermediate nodes when deleting, ...
617 parent
->mode
= BLANK
;
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
;
626 delta(nodekey
, WvString::empty
);
631 parent
= new UniConfChangeTree(parent
, key
.segment(seg
-1));
632 // Create the last node with the specified change.
635 parent
->mode
= NEWTREE
;
637 if (base
->exists(key
))
638 deletion_simulator(key
);
642 parent
->mode
= NEWVALUE
;
643 parent
->newvalue
= WvString(value
);
644 if (base
->get(key
) != value
)
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
,
661 // printf("set_value('%s', %d)\n", WvString(key).cstr(), value.isnull());
664 // Delete the key if it exists.
667 UniConfValueTree
*subnode
= node
->find(
668 key
.last(key
.numsegments() - seg
));
672 my_userdata data
= { subnode
, key
};
673 subnode
->visit(wv::bind(&UniTransactionGen::deletion_visitor
,
675 (void *)&data
, false, true);
676 // printf("DELETE SUBNODE!\n");
679 return subnode
== node
? NULL
: node
;
689 // Switch to create_value() if we ever can't find the next 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.
701 create_value(subnode
, key
, seg
, value
);
707 // The node already existed and we've found it; set it.
708 if (value
!= subnode
->value())
710 subnode
->setvalue(value
);
717 void UniTransactionGen::deletion_simulator2(const UniConfKey
&key
)
719 UniConfGen::Iter
*i
= this->iterator(key
);
722 for (i
->rewind(); i
->next();)
723 deletion_simulator2(UniConfKey(key
, i
->key()));
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
,
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.
741 return create_change(NULL
, key
, seg
, value
);
742 else if (node
->mode
== NEWTREE
)
744 node
->newtree
= set_value(node
->newtree
, key
, seg
, value
);
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
;
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.
769 create_change(subnode
, key
, seg
, value
);
772 else if (next
->mode
== NEWTREE
)
774 next
->newtree
= set_value(next
->newtree
,
781 // The node already existed, didn't have mode of NEWTREE, and we've
782 // found it; change it.
785 if (subnode
->mode
!= BLANK
|| base
->exists(key
))
786 deletion_simulator2(key
);
788 subnode
->mode
= NEWTREE
;
789 subnode
->newtree
= 0;
791 else if (subnode
->mode
== NEWVALUE
)
793 if (subnode
->newvalue
!= value
)
795 subnode
->newvalue
= value
;
799 else if (subnode
->mode
== BLANK
)
801 if (base
->get(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
))
811 subnode
->mode
= NEWVALUE
;
812 subnode
->newvalue
= WvString(value
);
817 // We'll say we're okay whenever the underlying generator is.
818 bool UniTransactionGen::isok()
823 void UniTransactionGen::flush_buffers()