WvStreams
unitransactiongen.cc
00001 #include "unitransactiongen.h"
00002 #include "uniconftree.h"
00003 #include "unilistiter.h"
00004 #include "wvmoniker.h"
00005 
00006 static IUniConfGen *creator(WvStringParm s, IObject *_obj)
00007 {
00008     IUniConfGen *base = wvcreate<IUniConfGen>(s, _obj);
00009     if (base)
00010         return new UniTransactionGen(base);
00011     else
00012         return NULL;
00013 }
00014 
00015 static WvMoniker<IUniConfGen> moniker("transaction", creator);
00016 
00017 /* This enum is a field of UniConfChangeTree. It indicates the type of
00018    change represented by a node in a UniConfChangeTree. */
00019 enum changeMode
00020 {
00021     /* This indicates that "newvalue" is valid and that
00022        its value should be written to the underlying generator at commit
00023        time. This tree *might* have children, which must be applied.
00024        "newvalue" will be a non-null pointer to a non-null WvString. */
00025     NEWVALUE,
00026     /* This indicates that "newtree" is valid (but possibly NULL) and that
00027        the underlying generator's corresponding subtree should be made
00028        identical at commit time. This tree will *not* have children (though
00029        newtree might). */
00030     NEWTREE,
00031     /* This indicates that "was_null_or_empty" is valid and that the key
00032        in the underlying generator should be created at commit time if it
00033        does not already exist at commit time. This tree *will* have
00034        children, which must be applied, and at least one of which will
00035        be non-BLANK. "was_null_or_empty" will be the return value of the
00036        WvString negation operation on the last known value of the
00037        corresponding key in the underlying generator; it is necessary
00038        in order to filter callbacks in certain cases. */
00039     NEWNODE,
00040     /* This indicates that none of the fields are valid and that
00041        nothing should be done for this tree. This tree *will* have children,
00042        which must be applied, but they will all have mode of NEWTREE with
00043        newtree == NULL. */
00044     BLANK
00045 };
00046 
00047 class UniConfChangeTree : public UniConfTree<UniConfChangeTree>
00048 {
00049 public:
00050     changeMode mode;
00051 
00052     // This used to be a union, but it was causing memory errors that were 
00053     // extremely difficult to track down.  Some of this code might serve no
00054     // purpose without this being a union, but I'd rather have it still work
00055     // and not leak than break it. -- mrwise
00056     WvString newvalue;
00057     UniConfValueTree *newtree;
00058     bool was_null_or_empty;
00059 
00060     // Constructs a tree and links it to a parent.
00061     UniConfChangeTree(UniConfChangeTree *parent, const UniConfKey &key)
00062         : UniConfTree<UniConfChangeTree>(parent, key), newtree(0) {}
00063 
00064     // Destroys a tree and everything it owns.
00065     ~UniConfChangeTree()
00066     {
00067         if (newtree)
00068             delete newtree;
00069     }
00070 };
00071 
00072 // Constructed by UniTransactionGen::iterator() to iterate over a section that
00073 // is to be completely replaced by a particular UniConfValueTree.
00074 class GenStyleValueTreeIter : public UniConfGen::Iter
00075 {
00076 public:
00077     GenStyleValueTreeIter(UniConfValueTree *node)
00078         : i(*node)
00079     {
00080         // printf("GenStyleValueTreeIter\n");
00081     }
00082     
00083     ~GenStyleValueTreeIter()
00084     {
00085         // printf("~GenStyleValueTreeIter\n");
00086     }
00087 
00088     void rewind() { i.rewind(); }
00089     bool next() { return i.next(); }
00090     UniConfKey key() const { return i->key(); }
00091     WvString value() const { return i->value(); }
00092 
00093 private:
00094     UniConfValueTree::Iter i;
00095 };
00096 
00097 // Constructed by UniTransactionGen::iterator() to iterate over a section that
00098 // is being modified but not replaced. We iterate first over all of the values
00099 // that we're changing, except those we're deleting, and second over all
00100 // existing values not iterated over in the first stage, except those we're
00101 // deleting.
00102 class GenStyleChangeTreeIter : public UniConfGen::Iter
00103 {
00104 public:
00105     GenStyleChangeTreeIter(UniConfChangeTree *_root,
00106                            const UniConfKey &_section,
00107                            IUniConfGen *_base)
00108         : root(_root), section(_section), base(_base),
00109           doing_i1(true), i1(*root), i2(base->iterator(section))
00110     {
00111         // printf("GenStyleChangeTreeIter(%s)\n", WvString(section).cstr());
00112     }
00113 
00114     ~GenStyleChangeTreeIter()
00115     {
00116         // printf("~GenStyleChangeTreeIter(%s)\n", WvString(section).cstr());
00117         if (i2) delete i2;
00118     }
00119 
00120     void rewind()
00121     {
00122         i1.rewind();
00123         doing_i1 = true;
00124     }
00125 
00126     bool next()
00127     {
00128         if (doing_i1)
00129         {
00130             for (;;)
00131             {
00132                 if (i1.next())
00133                 {
00134                     if (i1->mode == NEWVALUE ||
00135                         i1->mode == NEWNODE ||
00136                         (i1->mode == NEWTREE && i1->newtree))
00137                         return true;
00138                 }
00139                 else
00140                     break;
00141             }
00142             doing_i1 = false;
00143             if (i2) i2->rewind();
00144         }
00145         if (i2)
00146         {
00147             for (;;)
00148             {
00149                 if (i2->next())
00150                 {
00151                     UniConfChangeTree *node = root->findchild(i2->key());
00152                     if (!node || node->mode == BLANK)
00153                         return true;
00154                 }
00155                 else
00156                     break;
00157             }
00158         }
00159         return false;
00160     }
00161 
00162     UniConfKey key() const
00163     {
00164         if (doing_i1)
00165             return i1->key();
00166         else if (i2)
00167             return i2->key();
00168         else
00169             return UniConfKey();
00170     }
00171 
00172     WvString value() const
00173     {
00174         if (doing_i1)
00175         {
00176             if (i1->mode == NEWVALUE)
00177                 return i1->newvalue;
00178             else if (i1->mode == NEWTREE)
00179                 return i1->newtree->value();
00180             else // i.e. i1->mode == NEWNODE
00181             {
00182                 WvString value(base->get(UniConfKey(section, i1->key())));
00183                 return !value ? WvString::empty : value;
00184             }
00185         }
00186         else
00187         {
00188             return i2->value();
00189         }
00190     }
00191 
00192 private:
00193     UniConfChangeTree *root;
00194     UniConfKey section;
00195     IUniConfGen *base;
00196 
00197     bool doing_i1;
00198     UniConfChangeTree::Iter i1;
00199     UniConfGen::Iter *i2;
00200 };
00201 
00202 UniTransactionGen::UniTransactionGen(IUniConfGen *_base)
00203     : root(NULL), base(_base)
00204 {
00205     base->add_callback(this, wv::bind(&UniTransactionGen::gencallback, this,
00206                                       _1, _2));
00207 }
00208 
00209 UniTransactionGen::~UniTransactionGen()
00210 {
00211     base->del_callback(this);
00212     WVRELEASE(base);
00213     WVDELETE(root);
00214 }
00215 
00216 WvString UniTransactionGen::get(const UniConfKey &key)
00217 {
00218     UniConfChangeTree *node = root;
00219     for (int seg = 0;; node = node->findchild(key.segment(seg++)))
00220     {
00221         if (!node)
00222             // If we couldn't find the next node, then we aren't
00223             // changing the requested key, and so the value is whatever
00224             // it currently is.
00225             return base->get(key);
00226         else if (node->mode == NEWTREE)
00227         {
00228             // Else if the next node has mode of NEWTREE, then we're changing
00229             // the requested key to whatever its value is in the stored
00230             // tree.
00231             if (node->newtree)
00232             {
00233                 UniConfValueTree *subnode = node->newtree->find(
00234                     key.last(key.numsegments() - seg));
00235                 if (subnode)
00236                     return subnode->value();
00237             }
00238             return WvString::null;
00239         }
00240         else if (seg == key.numsegments())
00241         {
00242             // Else if this is the last node, then figure out what the node
00243             // would do and return the appropriate value. (The node's mode
00244             // will be either NEWVALUE, NEWNODE, or BLANK.)
00245             if (node->mode == NEWVALUE)
00246                 return node->newvalue;
00247             WvString value(base->get(key.first(seg)));
00248             return (node->mode == NEWNODE && !value) ? WvString::empty : value;
00249         }
00250     }
00251 }
00252 
00253 void UniTransactionGen::set(const UniConfKey &key, WvStringParm value)
00254 {
00255     hold_delta();
00256     root = set_change(root, key, 0, value);
00257     unhold_delta();
00258 }
00259 
00260 void UniTransactionGen::setv(const UniConfPairList &pairs)
00261 {
00262     hold_delta();
00263     UniConfPairList::Iter i(pairs);
00264     for (i.rewind(); i.next(); )
00265         root = set_change(root, i->key(), 0, i->value());
00266     unhold_delta();
00267 }
00268 
00269 void UniTransactionGen::commit()
00270 {
00271     if (root)
00272     {
00273         // Apply our changes to the inner generator.  We can't optimise
00274         // away callbacks at this point, because we may get notified of
00275         // changes caused by our changes.
00276         hold_delta();
00277         apply_changes(root, UniConfKey());
00278 
00279         // make sure the inner generator also commits
00280         base->commit();
00281 
00282         // save deleting the root till now so we can hide any
00283         // redundant notifications caused by the base->commit()
00284         delete root;
00285         root = NULL;
00286         unhold_delta();
00287     }
00288     
00289     // no need to base->commit() if we know we haven't changed anything!
00290 }
00291 
00292 bool UniTransactionGen::refresh()
00293 {
00294     if (root)
00295     {
00296         hold_delta();
00297         cancel_changes(root, UniConfKey());
00298         delete root;
00299         root = NULL;
00300         unhold_delta();
00301         
00302         // no need to base->commit() here, since the inner generator never
00303         // saw any changes
00304     }
00305     
00306     // must always base->refresh(), even if we didn't change anything
00307     return base->refresh();
00308 }
00309 
00310 UniConfGen::Iter *UniTransactionGen::iterator(const UniConfKey &key)
00311 {
00312     UniConfChangeTree *node = root;
00313     for (int seg = 0;; node = node->findchild(key.segment(seg++)))
00314     {
00315         if (!node)
00316             // If we couldn't find the next node, then we aren't changing the
00317             // children of the requested key, so they're whatever they
00318             // currently are.
00319             return base->iterator(key);
00320         else if (node->mode == NEWTREE)
00321         {
00322             // Else if the next node has mode of NEWTREE, then we're changing
00323             // the children of the requested key to whatever they are in the
00324             // stored tree.
00325             if (node->newtree)
00326             {
00327                 UniConfValueTree *subnode = node->newtree->find(
00328                     key.last(key.numsegments() - seg));
00329                 if (subnode)
00330                 {
00331                     UniConfGen::Iter *i = new GenStyleValueTreeIter(subnode);
00332                     UniListIter *i2 = new UniListIter(this);
00333                     i2->autofill(i);
00334                     delete i;
00335                     return i2;
00336                 }
00337             }
00338             return new UniConfGen::NullIter();
00339         }
00340         else if (seg == key.numsegments())
00341         {
00342             // Else if this is the last node, then iterate over its direct
00343             // children.
00344             UniConfGen::Iter *i = new GenStyleChangeTreeIter(node, key, base);
00345             UniListIter *i2 = new UniListIter(this);
00346             i2->autofill(i);
00347             delete i;
00348             return i2;
00349         }
00350     }
00351 }
00352 
00353 void UniTransactionGen::apply_values(UniConfValueTree *newcontents,
00354                                      const UniConfKey &section)
00355 {
00356     base->set(section, newcontents->value());
00357 
00358     UniConfGen::Iter *j = base->iterator(section);
00359     if (j)
00360     {
00361         for (j->rewind(); j->next();)
00362         {
00363             if (newcontents->findchild(j->key()) == NULL)
00364                 // Delete all children of the current value in the
00365                 // underlying generator that do not exist in our
00366                 // replacement tree.
00367                 base->set(UniConfKey(section, j->key()), WvString::null);
00368         }
00369         delete j;
00370     }
00371 
00372     // Repeat for each child in the replacement tree.
00373     UniConfValueTree::Iter i(*newcontents);
00374     for (i.rewind(); i.next();)
00375         apply_values(i.ptr(), UniConfKey(section, i->key()));
00376 }
00377 
00378 void UniTransactionGen::apply_changes(UniConfChangeTree *node,
00379                                       const UniConfKey &section)
00380 {
00381     if (node->mode == NEWTREE)
00382     {
00383         // If the current change is a NEWTREE change, then replace the
00384         // tree in the underlying generator with the stored one.
00385         if (node->newtree == NULL)
00386             base->set(section, WvString::null);
00387         else
00388             apply_values(node->newtree, section);
00389         // Since such changes have no children, return immediately.
00390         return;
00391     }
00392     else if (node->mode == NEWVALUE)
00393     {
00394         // Else if the current change is a NEWVALUE change, ...
00395         base->set(section, node->newvalue);
00396     }
00397     else if (node->mode == NEWNODE)
00398     {
00399         // Else if the current change is a NEWNODE change, ...
00400         if (!base->exists(section))
00401             // ... and the current value in the underlying generator doesn't
00402             // exist, then create it.
00403             base->set(section, WvString::empty);
00404         // Note: This *is* necessary. We can't ignore this change and have
00405         // the underlying generator handle it, because it's possible that
00406         // this NEWNODE was the result of a set() which was later deleted.
00407     }
00408     
00409     // Repeat for each child in the change tree.
00410     UniConfChangeTree::Iter i(*node);
00411     for (i.rewind(); i.next();)
00412         apply_changes(i.ptr(), UniConfKey(section, i->key()));
00413 }
00414 
00415 struct my_userdata
00416 {
00417     UniConfValueTree *node;
00418     const UniConfKey &key;
00419 };
00420 
00421 void UniTransactionGen::deletion_visitor(const UniConfValueTree *node,
00422                                          void *userdata)
00423 {
00424     my_userdata *data = (my_userdata *)userdata;
00425     delta(UniConfKey(data->key, node->fullkey(data->node)), WvString::null);
00426 }
00427 
00428 // Mirror image of apply_values() that issues all of the callbacks associated
00429 // with discarding a replacement value tree.
00430 void UniTransactionGen::cancel_values(UniConfValueTree *newcontents,
00431                                       const UniConfKey &section)
00432 {
00433     WvString value(base->get(section));
00434     if (!newcontents || newcontents->value() != value)
00435         delta(section, value);
00436     
00437     if (newcontents)
00438     {
00439         UniConfValueTree::Iter i(*newcontents);
00440         for (i.rewind(); i.next();)
00441         {
00442             UniConfKey subkey(section, i->key());
00443             if (!base->exists(subkey))
00444             {
00445                 my_userdata data = { i.ptr(), subkey };
00446                 i->visit(wv::bind(&UniTransactionGen::deletion_visitor, this,
00447                                   _1, _2),
00448                          (void*)&data, false, true);
00449             }
00450         }
00451     }
00452 
00453     UniConfGen::Iter *i = base->iterator(section);
00454     if (i)
00455     {
00456         for (i->rewind(); i->next();)
00457             cancel_values(newcontents ?
00458                           newcontents->findchild(i->key()) : NULL,
00459                           UniConfKey(section, i->key()));
00460         delete i;
00461     }
00462 }
00463 
00464 // Mirror image of apply_changes() that issues all of the callbacks associated
00465 // with discarding a change tree.
00466 void UniTransactionGen::cancel_changes(UniConfChangeTree *node,
00467                                        const UniConfKey &section)
00468 {
00469     if (node->mode == NEWTREE)
00470     {
00471         if (!base->exists(section))
00472         {
00473             if (node->newtree != NULL)
00474             {
00475                 my_userdata data = { node->newtree, section };
00476                 node->newtree->visit(
00477                     wv::bind(&UniTransactionGen::deletion_visitor, this,
00478                              _1, _2),
00479                     (void *)&data, false, true);
00480             }
00481         }
00482         else
00483             cancel_values(node->newtree, section);
00484         return;
00485     }
00486 
00487     WvString value;
00488     if (node->mode != BLANK)
00489         value = base->get(section);
00490 
00491     if (node->mode == NEWVALUE &&
00492         !value.isnull() &&
00493         value != node->newvalue)
00494         delta(section, value);
00495 
00496     UniConfChangeTree::Iter i(*node);
00497     for (i.rewind(); i.next();)
00498         cancel_changes(i.ptr(), UniConfKey(section, i->key()));
00499 
00500     if (node->mode != BLANK && value.isnull())
00501         delta(section, WvString::null);
00502 }
00503 
00504 void UniTransactionGen::gencallback(const UniConfKey &key,
00505                                     WvStringParm value)
00506 {
00507     UniConfChangeTree *node = root;
00508     for (int seg = 0;; node = node->findchild(key.segment(seg++)))
00509     {
00510         if (!node)
00511             // If we couldn't find the next node, then we aren't changing
00512             // the changed key or any of its children, and so a callback
00513             // should be made.
00514             break;
00515         else if (node->mode == NEWTREE)
00516             // Else if the next node has mode of NEWTREE, then we're changing
00517             // the changed key and all of its children to whatever their
00518             // values are in the stored tree, and so the callback should be
00519             // ignored.
00520             return;
00521         else if (seg == key.numsegments())
00522         {
00523             // Else if this is the last node, then figure out what we 
00524             // should do.
00525             if (node->mode == NEWVALUE)
00526                 // If we're replacing this key's value, then we should
00527                 // ignore the callback.
00528                 return;
00529             else if (node->mode == NEWNODE)
00530             {
00531                 // Else if we want to create this key, then use its
00532                 // was_null_or_empty flag to figure out if we need
00533                 // to issue a callback, and update it if necessary.
00534                 if (node->was_null_or_empty && !value)
00535                     return;
00536                 node->was_null_or_empty = !value;
00537                 if (value.isnull())
00538                 {
00539                     delta(key, WvString::empty);
00540                     return;
00541                 }
00542                 break;
00543             }
00544             else // i.e. node->mode == BLANK
00545                 // Else if we're doing nothing to this key, then a
00546                 // callback should be made.
00547                 break;
00548         }
00549     }
00550     
00551     // Make a normal callback.
00552     delta(key, value);
00553 }
00554 
00555 // Create and return a UniConfValueTree containing the value 'value' for
00556 // the key given by the segments of 'key' at and after position 'seg', with
00557 // parent 'parent' and key given by the segment of 'key' at position seg-1
00558 // (which is the "root" key if seg == 0). Issue callbacks as necessary using
00559 // all the segments of 'key'.
00560 UniConfValueTree *UniTransactionGen::create_value(UniConfValueTree *parent,
00561                                                   const UniConfKey &key,
00562                                                   int seg,
00563                                                   WvStringParm value)
00564 {
00565     UniConfValueTree *tree = 0;
00566     for (; seg != key.numsegments();)
00567     {
00568         // Create any needed intermediate nodes, each with value equal to
00569         // the empty string.
00570         parent = new UniConfValueTree(parent,
00571                                       key.segment(seg-1),
00572                                       WvString::empty);
00573         delta(key.first(seg++), WvString::empty);
00574         if (!tree)
00575             tree = parent;
00576     }
00577     // Create the last node with the specified value.
00578     parent = new UniConfValueTree(parent,
00579                                   key.segment(seg-1),
00580                                   value);
00581     delta(key, value);
00582     if (!tree)
00583         tree = parent;
00584     return tree;
00585 }
00586 
00587 void UniTransactionGen::deletion_simulator(const UniConfKey &key)
00588 {
00589     UniConfGen::Iter *i = base->iterator(key);
00590     if (i)
00591     {
00592         for (i->rewind(); i->next();)
00593             deletion_simulator(UniConfKey(key, i->key()));
00594         delete i;
00595     }
00596     delta(key, WvString::null);
00597 }
00598 
00599 // Like create_value(), but make a UniConfChangeTree containing a *change*
00600 // to value 'value'.
00601 UniConfChangeTree *UniTransactionGen::create_change(UniConfChangeTree *parent,
00602                                                     const UniConfKey &key,
00603                                                     int seg,
00604                                                     WvStringParm value)
00605 {
00606     // if the key has a trailing slash, this should be a no-op: we don't
00607     // want this to have any effect
00608     if ((key.hastrailingslash()) && !value.isnull())
00609         return parent;
00610 
00611     UniConfChangeTree *tree = 0;
00612     for (; seg != key.numsegments(); seg++)
00613     {
00614         parent = new UniConfChangeTree(parent, key.segment(seg-1));
00615         if (value.isnull())
00616             // We don't do anything for intermediate nodes when deleting, ...
00617             parent->mode = BLANK;
00618         else
00619         {
00620             // ... but when set()'ing a non-null value, we want them to exist.
00621             parent->mode = NEWNODE;
00622             UniConfKey nodekey(key.first(seg));
00623             WvString curr = base->get(nodekey);
00624             parent->was_null_or_empty = !curr;
00625             if (curr.isnull())
00626                 delta(nodekey, WvString::empty);
00627         }
00628         if (!tree)
00629             tree = parent;
00630     }
00631     parent = new UniConfChangeTree(parent, key.segment(seg-1));
00632     // Create the last node with the specified change.
00633     if (value.isnull())
00634     {
00635         parent->mode = NEWTREE;
00636         parent->newtree = 0;
00637         if (base->exists(key))
00638             deletion_simulator(key);
00639     }
00640     else
00641     {
00642         parent->mode = NEWVALUE;
00643         parent->newvalue = WvString(value);
00644         if (base->get(key) != value)
00645             delta(key, value);
00646     }
00647     if (!tree)
00648         tree = parent;
00649     return tree;
00650 }
00651 
00652 // Modify an existing UniConfValueTree to incorporate the set() of a
00653 // particular value for a particular key. Return a possibly altered
00654 // pointer to the root of the tree. 'seg' and 'key' are used like they
00655 // are in create_value(), and callbacks are made similarly.
00656 UniConfValueTree *UniTransactionGen::set_value(UniConfValueTree *node,
00657                                                const UniConfKey &key,
00658                                                int seg,
00659                                                WvStringParm value)
00660 {
00661     // printf("set_value('%s', %d)\n", WvString(key).cstr(), value.isnull());
00662     if (value.isnull())
00663     {
00664         // Delete the key if it exists.
00665         if (node)
00666         {
00667             UniConfValueTree *subnode = node->find(
00668                 key.last(key.numsegments() - seg));
00669             if (subnode)
00670             {
00671                 hold_delta();
00672                 my_userdata data = { subnode, key };
00673                 subnode->visit(wv::bind(&UniTransactionGen::deletion_visitor,
00674                                         this, _1, _2),
00675                                (void *)&data, false, true);
00676                 // printf("DELETE SUBNODE!\n");
00677                 delete subnode;
00678                 unhold_delta();
00679                 return subnode == node ? NULL : node;
00680             }
00681             else
00682                 return node;
00683         }
00684         else
00685             return NULL;
00686     }
00687     else
00688     {
00689         // Switch to create_value() if we ever can't find the next node.
00690         if (!node)
00691             return create_value(NULL, key, seg, value);
00692         
00693         UniConfValueTree *subnode = node;
00694         for (; seg != key.numsegments();)
00695         {
00696             UniConfKey segment(key.segment(seg++));
00697             UniConfValueTree *child = subnode->findchild(segment);
00698             // Switch to create_value() if we ever can't find the next node.
00699             if (!child)
00700             {
00701                 create_value(subnode, key, seg, value);
00702                 return node;
00703             }
00704             else
00705                 subnode = child;
00706         }
00707         // The node already existed and we've found it; set it.
00708         if (value != subnode->value())
00709         {
00710             subnode->setvalue(value);
00711             delta(key, value);
00712         }
00713         return node;
00714     }
00715 }
00716 
00717 void UniTransactionGen::deletion_simulator2(const UniConfKey &key)
00718 {
00719     UniConfGen::Iter *i = this->iterator(key);
00720     if (i)
00721     {
00722         for (i->rewind(); i->next();)
00723             deletion_simulator2(UniConfKey(key, i->key()));
00724         delete i;
00725     }
00726     delta(key, WvString::null);
00727 }
00728 
00729 // Like set_value(), but, again, for UniConfChangeTrees instead.
00730 UniConfChangeTree *UniTransactionGen::set_change(UniConfChangeTree *node,
00731                                                  const UniConfKey &key,
00732                                                  int seg,
00733                                                  WvStringParm value)
00734 {
00735     // printf("set_change(key=%s,mode=%d) = '%s'\n",
00736     //        WvString(key).cstr(), node ? node->mode : 999, value.cstr());
00737     
00738     // Switch to create_change() if we ever can't find the next node,
00739     // and switch to set_value() if we ever find a NEWTREE.
00740     if (!node)
00741         return create_change(NULL, key, seg, value);
00742     else if (node->mode == NEWTREE)
00743     {
00744         node->newtree = set_value(node->newtree, key, seg, value);
00745         return node;
00746     }
00747     
00748     UniConfChangeTree *subnode = node;
00749     for (; seg != key.numsegments();)
00750     {
00751         if (subnode->mode == BLANK && !value.isnull())
00752         {
00753             // If we're setting a non-null value and we weren't previously
00754             // doing anything to this node, then now we want to create it.
00755             subnode->mode = NEWNODE;
00756             UniConfKey nodekey(key.first(seg));
00757             WvString curr = base->get(nodekey);
00758             subnode->was_null_or_empty = !curr;
00759             if (curr.isnull())
00760                 delta(nodekey, WvString::empty);
00761         }
00762         
00763         UniConfKey segment(key.segment(seg++));
00764         UniConfChangeTree *next = subnode->findchild(segment);
00765         // Switch to create_change() if we ever can't find the next node,
00766         // and switch to set_value() if we ever find a NEWTREE.
00767         if (!next)
00768         {
00769             create_change(subnode, key, seg, value);
00770             return node;
00771         }
00772         else if (next->mode == NEWTREE)
00773         {
00774             next->newtree = set_value(next->newtree,
00775                                       key, seg, value);
00776             return node;
00777         }
00778         else
00779             subnode = next;
00780     }
00781     // The node already existed, didn't have mode of NEWTREE, and we've
00782     // found it; change it.
00783     if (value.isnull())
00784     {
00785         if (subnode->mode != BLANK || base->exists(key))
00786             deletion_simulator2(key);
00787         subnode->zap();
00788         subnode->mode = NEWTREE;
00789         subnode->newtree = 0;
00790     }
00791     else if (subnode->mode == NEWVALUE)
00792     {
00793         if (subnode->newvalue != value)
00794         {
00795             subnode->newvalue = value;
00796             delta(key, value);
00797         }
00798     }
00799     else if (subnode->mode == BLANK)
00800     {
00801         if (base->get(key) != value)
00802             delta(key, value);      
00803         subnode->mode = NEWVALUE;
00804         subnode->newvalue = WvString(value);
00805     }
00806     else // i.e. subnode->mode == NEWNODE
00807     {
00808         WvString currval(base->get(key));
00809         if ((!currval != !value) && (currval != value))
00810             delta(key, value);
00811         subnode->mode = NEWVALUE;
00812         subnode->newvalue = WvString(value);
00813     }
00814     return node;
00815 }
00816 
00817 // We'll say we're okay whenever the underlying generator is.
00818 bool UniTransactionGen::isok()
00819 {
00820     return base->isok();
00821 }
00822 
00823 void UniTransactionGen::flush_buffers()
00824 {
00825 }