/* * Worldvisions Weaver Software: * Copyright (C) 1997-2002 Net Integration Technologies, Inc. * * Defines the root management class for UniConf. To create any kind of * UniConf tree, you'll need one of these. */ #include "uniconfroot.h" #include "wvlinkerhack.h" WV_LINK_TO(UniGenHack); UniConfRoot::UniConfRoot(): UniConf(this), watchroot(NULL) { mounts.add_callback(this, wv::bind(&UniConfRoot::gen_callback, this, _1, _2)); } UniConfRoot::UniConfRoot(WvStringParm moniker, bool refresh): UniConf(this), watchroot(NULL) { mounts.mount("/", moniker, refresh); mounts.add_callback(this, wv::bind(&UniConfRoot::gen_callback, this, _1, _2)); } UniConfRoot::UniConfRoot(UniConfGen *gen, bool refresh): UniConf(this), watchroot(NULL) { mounts.mountgen("/", gen, refresh); mounts.add_callback(this, wv::bind(&UniConfRoot::gen_callback, this, _1, _2)); } // make sure the given subtree of callback information is empty static bool watchout(UniWatchInfoTree *t) { bool fail = false; UniWatchInfoTree::Iter i(*t); for (i.rewind(); i.next(); ) { UniWatchInfoTree *w = i.ptr(); if (w->haschildren()) if (watchout(w)) fail = true; if (!w->watches.isempty()) { fail = true; if (1) fprintf(stderr, "Remaining watch: '%s' (%zd)\n", w->fullkey().printable().cstr(), w->watches.count()); } } return fail; } UniConfRoot::~UniConfRoot() { // first, unmount everything. Some of the mounts might have waiting // callbacks. (I hope not, but... things like UniUnwrapGen might get // confusing.) mounts.zap(); // if the list of callbacks is non-empty, someone is either very buggy // (they disappeared without deleting their callback, so they could cause // crashes), or they're not getting what they expected (we disappeared // before they did, so they won't be getting their callback). assert(!watchout(&watchroot)); mounts.del_callback(this); } void UniConfRoot::add_callback(void *cookie, const UniConfKey &key, const UniConfCallback &callback, bool recurse) { UniWatchInfo *w = new UniWatchInfo(cookie, recurse, callback); UniWatchInfoTree *node = &watchroot; UniConfKey::Iter i(key); for (i.rewind(); i.next(); ) { UniWatchInfoTree *prev = node; node = node->findchild(i()); if (!node) node = new UniWatchInfoTree(prev, i()); } node->watches.append(w, true); } void UniConfRoot::del_callback(void *cookie, const UniConfKey &key, bool recurse) { UniWatchInfoTree *node = watchroot.find(key); if (node) { UniWatchInfoList::Iter i(node->watches); for (i.rewind(); i.next(); ) { // remove the watch if it matches if (i->cookie == cookie && i->recurse == recurse) { i.xunlink(); break; } } // prune the branch if needed prune(node); } } void UniConfRoot::add_setbool(const UniConfKey &key, bool *flag, bool recurse) { add_callback(flag, key, wv::bind(&UniConfRoot::setbool_callback, flag, _1, _2), recurse); } void UniConfRoot::del_setbool(const UniConfKey &key, bool *flag, bool recurse) { del_callback(flag, key, recurse); } void UniConfRoot::check(UniWatchInfoTree *node, const UniConfKey &key, int segleft) { UniWatchInfoList::Iter i(node->watches); for (i.rewind(); i.next(); ) { if (!i->recursive() && segleft > 0) continue; i->notify(UniConf(this, key.removelast(segleft)), key.last(segleft)); } } void UniConfRoot::deletioncheck(UniWatchInfoTree *node, const UniConfKey &key) { UniWatchInfoTree::Iter i(*node); for (i.rewind(); i.next(); ) { UniWatchInfoTree *w = i.ptr(); UniConfKey subkey(key, w->key()); // pretend that we wiped out just this key check(w, subkey, 0); deletioncheck(w, subkey); } } void UniConfRoot::prune(UniWatchInfoTree *node) { while (node != & watchroot && ! node->isessential()) { UniWatchInfoTree *next = node->parent(); delete node; node = next; } } void UniConfRoot::gen_callback(const UniConfKey &key, WvStringParm value) { hold_delta(); UniWatchInfoTree *node = & watchroot; int segs = key.numsegments(); // check root node check(node, key, segs); // look for watches on key and its ancestors for (int s = 0; s < segs; ) { node = node->findchild(key.segment(s)); s++; if (!node) goto done; // no descendents so we can stop check(node, key, segs - s); } // look for watches on descendents of key if node was deleted if (value.isnull()) deletioncheck(node, key); done: unhold_delta(); }