WvStreams
uniconfroot.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * Defines the root management class for UniConf.  To create any kind of
00006  * UniConf tree, you'll need one of these.
00007  */
00008 #include "uniconfroot.h"
00009 #include "wvlinkerhack.h"
00010 
00011 WV_LINK_TO(UniGenHack);
00012 
00013 
00014 UniConfRoot::UniConfRoot():
00015     UniConf(this),
00016     watchroot(NULL)
00017 {
00018     mounts.add_callback(this, wv::bind(&UniConfRoot::gen_callback, this,
00019                                        _1, _2));
00020 }
00021 
00022 
00023 UniConfRoot::UniConfRoot(WvStringParm moniker, bool refresh):
00024     UniConf(this),
00025     watchroot(NULL)
00026 {
00027     mounts.mount("/", moniker, refresh);
00028     mounts.add_callback(this, wv::bind(&UniConfRoot::gen_callback, this,
00029                                        _1, _2));
00030 }
00031 
00032 
00033 UniConfRoot::UniConfRoot(UniConfGen *gen, bool refresh):
00034     UniConf(this),
00035     watchroot(NULL)
00036 {
00037     mounts.mountgen("/", gen, refresh);
00038     mounts.add_callback(this, wv::bind(&UniConfRoot::gen_callback, this,
00039                                        _1, _2));
00040 }
00041 
00042 
00043 // make sure the given subtree of callback information is empty
00044 static bool watchout(UniWatchInfoTree *t)
00045 {
00046     bool fail = false;
00047     
00048     UniWatchInfoTree::Iter i(*t);
00049     for (i.rewind(); i.next(); )
00050     {
00051         UniWatchInfoTree *w = i.ptr();
00052         
00053         if (w->haschildren())
00054             if (watchout(w))
00055                 fail = true;
00056         
00057         if (!w->watches.isempty())
00058         {
00059             fail = true;
00060             if (1)
00061                 fprintf(stderr, "Remaining watch: '%s' (%d)\n",
00062                         w->fullkey().printable().cstr(), w->watches.count());
00063         }
00064     }
00065     
00066     return fail;
00067 }
00068 
00069 
00070 UniConfRoot::~UniConfRoot()
00071 {
00072     // first, unmount everything.  Some of the mounts might have waiting
00073     // callbacks.  (I hope not, but... things like UniUnwrapGen might get
00074     // confusing.)
00075     mounts.zap();
00076     
00077     // if the list of callbacks is non-empty, someone is either very buggy
00078     // (they disappeared without deleting their callback, so they could cause
00079     // crashes), or they're not getting what they expected (we disappeared
00080     // before they did, so they won't be getting their callback).
00081     assert(!watchout(&watchroot));
00082     
00083     mounts.del_callback(this);
00084 }
00085 
00086 
00087 void UniConfRoot::add_callback(void *cookie, const UniConfKey &key,
00088                                const UniConfCallback &callback, bool recurse)
00089 {
00090     UniWatchInfo *w = new UniWatchInfo(cookie, recurse, callback);
00091 
00092     UniWatchInfoTree *node = &watchroot;
00093     
00094     UniConfKey::Iter i(key);
00095     for (i.rewind(); i.next(); )
00096     {
00097         UniWatchInfoTree *prev = node;
00098         node = node->findchild(i());
00099         if (!node)
00100             node = new UniWatchInfoTree(prev, i());
00101     }
00102     node->watches.append(w, true);
00103 }
00104 
00105 
00106 void UniConfRoot::del_callback(void *cookie, const UniConfKey &key,
00107                                bool recurse)
00108 {
00109     UniWatchInfoTree *node = watchroot.find(key);
00110     if (node)
00111     {
00112         UniWatchInfoList::Iter i(node->watches);
00113         for (i.rewind(); i.next(); )
00114         {
00115             // remove the watch if it matches
00116             if (i->cookie == cookie && i->recurse == recurse)
00117             {
00118                 i.xunlink();
00119                 break;
00120             }
00121         }
00122         // prune the branch if needed
00123         prune(node);
00124     }
00125 }
00126 
00127 
00128 void UniConfRoot::add_setbool(const UniConfKey &key, bool *flag, bool recurse)
00129 {
00130     add_callback(flag, key, wv::bind(&UniConfRoot::setbool_callback, flag,
00131                                      _1, _2),
00132                  recurse);
00133 }
00134 
00135 
00136 void UniConfRoot::del_setbool(const UniConfKey &key, bool *flag, bool recurse)
00137 {
00138     del_callback(flag, key, recurse);
00139 }
00140 
00141 
00142 void UniConfRoot::check(UniWatchInfoTree *node,
00143                         const UniConfKey &key, int segleft)
00144 {
00145     UniWatchInfoList::Iter i(node->watches);
00146     for (i.rewind(); i.next(); )
00147     {
00148         if (!i->recursive() && segleft > 0)
00149             continue;
00150 
00151         i->notify(UniConf(this, key.removelast(segleft)), key.last(segleft));
00152     }
00153 }
00154 
00155 
00156 void UniConfRoot::deletioncheck(UniWatchInfoTree *node, const UniConfKey &key)
00157 {
00158     UniWatchInfoTree::Iter i(*node);
00159     for (i.rewind(); i.next(); )
00160     {
00161         UniWatchInfoTree *w = i.ptr();
00162         UniConfKey subkey(key, w->key());
00163         
00164         // pretend that we wiped out just this key
00165         check(w, subkey, 0);
00166         deletioncheck(w, subkey);
00167     }
00168 }
00169 
00170 
00171 void UniConfRoot::prune(UniWatchInfoTree *node)
00172 {
00173     while (node != & watchroot && ! node->isessential())
00174     {
00175         UniWatchInfoTree *next = node->parent();
00176         delete node;
00177         node = next;
00178     }
00179 }
00180 
00181 
00182 void UniConfRoot::gen_callback(const UniConfKey &key, WvStringParm value)
00183 {
00184     hold_delta();
00185     UniWatchInfoTree *node = & watchroot;
00186     int segs = key.numsegments();
00187 
00188     // check root node
00189     check(node, key, segs);
00190 
00191     // look for watches on key and its ancestors
00192     for (int s = 0; s < segs; )
00193     {
00194         node = node->findchild(key.segment(s));
00195         s++;
00196         if (!node)
00197             goto done; // no descendents so we can stop
00198         check(node, key, segs - s);
00199     }
00200 
00201     // look for watches on descendents of key if node was deleted
00202     if (value.isnull())
00203         deletioncheck(node, key);
00204     
00205 done:
00206     unhold_delta();
00207 }