#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <Eris/TypeInfo.h>
#include <Eris/Log.h>
#include <Eris/Exceptions.h>
#include <Atlas/Objects/Root.h>
#include <Atlas/Objects/Operation.h>
#include <Atlas/Objects/Entity.h>
#include <Atlas/Objects/objectFactory.h>
#include <cassert>
using Atlas::Objects::Root;
using namespace Atlas::Objects::Operation;
namespace Eris {
////////////////////////////////////////////////////////////////////////////////////////////////////////
TypeInfo::TypeInfo(const std::string &id, TypeService *ts) :
m_bound(false),
m_name(id),
m_atlasClassNo(0),
m_moveCount(0),
m_typeService(ts)
{
if (m_name == "root")
m_bound = true; // root node is always bound
}
TypeInfo::TypeInfo(const Root &atype, TypeService *ts) :
m_bound(false),
m_name(atype->getId()),
m_moveCount(0),
m_typeService(ts)
{
if (m_name == "root") m_bound = true; // root node is always bound
processTypeData(atype);
}
bool TypeInfo::isA(TypeInfoPtr tp)
{
if (!m_bound) warning() << "calling isA on unbound type " << m_name;
// uber fast short-circuit for type equality
if (tp == this) return true;
return m_ancestors.count(tp); // non-authorative if not bound
}
bool TypeInfo::hasUnresolvedChildren() const
{
return !m_unresolvedChildren.empty();
}
void TypeInfo::resolveChildren()
{
if (m_unresolvedChildren.empty()) {
error() << "Type " << m_name << " has no unresolved children";
return;
}
StringSet uchildren(m_unresolvedChildren);
for (StringSet::const_iterator it = uchildren.begin(); it != uchildren.end(); ++it) {
addChild(m_typeService->getTypeByName(*it));
}
assert(m_unresolvedChildren.empty());
}
#pragma mark -
void TypeInfo::processTypeData(const Root &atype)
{
if (atype->getId() != m_name) {
error() << "mis-targeted INFO operation for " << atype->getId() << " arrived at " << m_name;
return;
}
const StringList& parents(atype->getParents());
for (StringList::const_iterator P = parents.begin(); P != parents.end(); ++P)
addParent(m_typeService->getTypeByName(*P));
if (atype->hasAttr("children"))
{
const Atlas::Message::Element childElem(atype->getAttr("children"));
const Atlas::Message::ListType & children(childElem.asList());
for (Atlas::Message::ListType::const_iterator C = children.begin(); C != children.end(); ++C) {
TypeInfo* child = m_typeService->findTypeByName(C->asString());
// if the child was already known, don't add to unresolved
if (child && m_children.count(child)) continue;
m_unresolvedChildren.insert(C->asString());
}
}
validateBind();
}
bool TypeInfo::operator==(const TypeInfo &x) const
{
if (m_typeService != x.m_typeService)
warning() << "comparing TypeInfos from different type services, bad";
return (m_name == x.m_name);
}
bool TypeInfo::operator<(const TypeInfo &x) const
{
return m_name < x.m_name;
}
void TypeInfo::addParent(TypeInfoPtr tp)
{
if (m_parents.count(tp))
{
// it's critcial we bail fast here to avoid infitite mutual recursion with addChild
return;
}
if (m_ancestors.count(tp))
error() << "Adding " << tp->m_name << " as parent of " << m_name << ", but already marked as ancestor";
// update the gear
m_parents.insert(tp);
addAncestor(tp);
// note this will never recurse deep becuase of the fast exiting up top
tp->addChild(this);
}
void TypeInfo::addChild(TypeInfoPtr tp)
{
if (tp == this) {
error() << "Attempt to add " << getName() << " as a child if itself";
return;
}
if (tp->getName() == this->getName()) {
error() << "Attempt to add " << getName() << " as child to identical parent ";
return;
}
if (m_children.count(tp)) return;
m_unresolvedChildren.erase(tp->getName());
m_children.insert(tp);
// again this will not recurse due to the termination code
tp->addParent(this);
}
void TypeInfo::addAncestor(TypeInfoPtr tp)
{
// someone has reported getting into a loop here (i.e a circular inheritance
// graph). To try and catch that, I'm putting this assert in. If / when you
// hit it, get in touch with James.
assert(m_children.count(tp) == 0);
assert(m_ancestors.count(tp) == 0);
m_ancestors.insert(tp);
const TypeInfoSet& parentAncestors = tp->m_ancestors;
m_ancestors.insert(parentAncestors.begin(), parentAncestors.end());
// tell all our childen!
for (TypeInfoSet::iterator C=m_children.begin(); C!=m_children.end();++C) {
(*C)->addAncestor(tp);
}
}
void TypeInfo::validateBind()
{
if (m_bound) return;
// check all our parents
for (TypeInfoSet::iterator P=m_parents.begin(); P!=m_parents.end();++P) {
if (!(*P)->isBound()) return;
}
m_bound = true;
Bound.emit(this);
m_typeService->BoundType.emit(this);
for (TypeInfoSet::iterator C=m_children.begin(); C!=m_children.end();++C) {
(*C)->validateBind();
}
}
} // of namespace Eris
syntax highlighted by Code2HTML, v. 0.9.1