1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <Eris/ClassDispatcher.h>
#include <Eris/TypeInfo.h>
#include <Eris/Wait.h>
#include <Eris/Connection.h>
#include <Eris/Log.h>
#include <Eris/Exceptions.h>
#include <sigc++/bind.h>
#include <sigc++/object_slot.h>
#include <cassert>
namespace Eris
{
ClassDispatcher::ClassDispatcher(const std::string& nm, Connection *conn) :
Dispatcher(nm), _conn(conn)
{
}
ClassDispatcher::~ClassDispatcher()
{
for (ClassDispatcherList::iterator I=_subs.begin(); I !=_subs.end(); ++I)
I->sub->decRef();
}
bool ClassDispatcher::dispatch(DispatchContextDeque &dq)
{
TypeInfoPtr mty = _conn->getTypeService()->getTypeForAtlas(dq.front());
if (!mty->isBound()) {
Eris::log(LOG_VERBOSE, "waiting for bind of %s", mty->getName().c_str());
new WaitForSignal(mty->getBoundSignal(), dq.back(), _conn);
// set dispatched, to avoid warnings about not hitting a leaf
Atlas::Message::Element::MapType &o = dq.back().asMap();
o["__DISPATCHED__"] = "1";
}
for (ClassDispatcherList::iterator I=_subs.begin(); I !=_subs.end(); ++I)
if (mty->safeIsA(I->type)) {
// off we go
return I->sub->dispatch(dq);
}
return false;
}
Dispatcher* ClassDispatcher::addSubdispatch(Dispatcher *d, const std::string clnm)
{
assert(d);
TypeInfo* ty = _conn->getTypeService()->getTypeByName(clnm);
_Class cl = {d,ty};
d->addRef();
if (!ty->isBound()) {
/* rather cautious logic here : we are going to push_front the unbound type,
so it will get tested, but only hit exact matches. This is safe, since they would have
fired anyway (assuming the types are unique). Once the thing is bound, we need
to re-order of course (and sharpish!) */
_subs.push_front(cl);
SigC::Slot0<void> sl(SigC::bind(SigC::slot(*this, &ClassDispatcher::boundType), ty));
ty->getBoundSignal().connect(sl);
return d;
}
boundInsert(cl);
return d;
}
void ClassDispatcher::rmvSubdispatch(Dispatcher *d)
{
assert(d);
std::string nm(d->getName());
for (ClassDispatcherList::iterator I=_subs.begin(); I !=_subs.end(); ++I) {
if (I->sub == d) {
_subs.erase(I);
d->decRef();
return;
}
if (I->sub->_name[0] == '_') {
// anonymous, check it
Dispatcher *ds = I->sub->getSubdispatch(nm);
if (ds) {
I->sub->rmvSubdispatch(d);
// clean up empty anonymous nodes automatically
if (I->sub->empty()) {
I->sub->decRef();
_subs.erase(I);
}
return;
}
}
}
Eris::log(LOG_ERROR, "Unknown dispatcher %s in ClassDispatcher:rmvSubdispatcher",
d->getName().c_str());
}
Dispatcher* ClassDispatcher::getSubdispatch(const std::string &nm)
{
for (ClassDispatcherList::iterator I=_subs.begin(); I !=_subs.end(); ++I) {
if (I->sub->getName() == nm)
return I->sub;
if (I->sub->_name[0] == '_') {
Dispatcher *ds = I->sub->getSubdispatch(nm);
if (ds)
return ds;
}
}
return NULL;
}
void ClassDispatcher::purge()
{
throw InvalidOperation("called purge() on ClassDispatcher " + _name);
}
void ClassDispatcher::boundType(TypeInfo *ty)
{
_Class cl = {NULL,NULL};
// remove it (from near the front, hopefully)
for (ClassDispatcherList::iterator I=_subs.begin(); I !=_subs.end(); ++I)
if (I->type == ty) {
cl = *I;
_subs.erase(I);
break;
}
if (cl.sub == NULL) {
Eris::log(LOG_ERROR, "Couldn't find type %s in dispatcher %s doing full bind",
ty->getName().c_str(), _name.c_str());
throw InvalidOperation("Missing type doing full bind in class dispatcher");
}
Eris::log(LOG_DEBUG, "reordering class dispatcher %s node %s",
_name.c_str(), cl.type->getName().c_str());
boundInsert(cl);
}
void ClassDispatcher::boundInsert(const _Class &cl)
{
assert(cl.type->isBound());
for (ClassDispatcherList::iterator I=_subs.begin(); I !=_subs.end(); ++I)
if (cl.type->isA(I->type)) {
_subs.insert(I, cl); // insert before, and get out
return;
}
_subs.push_back(cl);
}
Dispatcher* ClassDispatcher::newAnonymous(Connection *conn)
{
return new ClassDispatcher("_class", conn);
}
} // of namespace Eris
|