eris 1.4.0
A WorldForge client library.
TypeInfo.cpp
1#include <utility>
2
3#ifdef HAVE_CONFIG_H
4 #include "config.h"
5#endif
6
7#include "TypeInfo.h"
8#include "Log.h"
9#include "Exceptions.h"
10#include "TypeService.h"
11
12#include <Atlas/Objects/Operation.h>
13
14#include <cassert>
15#include <algorithm>
16
17using Atlas::Objects::Root;
18using namespace Atlas::Objects::Operation;
19
20namespace Eris {
21
23
24TypeInfo::TypeInfo(std::string id, TypeService &ts) :
25 m_parent(nullptr),
26 m_bound(false),
27 m_name(std::move(id)),
28 m_typeService(ts)
29{
30 if (m_name == "root") {
31 m_bound = true; // root node is always bound
32 }
33}
34
35TypeInfo::TypeInfo(const Root &atype, TypeService &ts) :
36 m_parent(nullptr),
37 m_bound(false),
38 m_name(atype->getId()),
39 m_typeService(ts)
40{
41 if (m_name == "root") {
42 m_bound = true; // root node is always bound
43 }
44
45 processTypeData(atype);
46}
47
48bool TypeInfo::isA(TypeInfo* tp) const
49{
50 if (!m_bound) {
51 warning() << "calling isA on unbound type " << m_name;
52 }
53
54 // uber fast short-circuit for type equality
55 if (tp == this) {
56 return true;
57 }
58
59 return m_ancestors.find(tp) != m_ancestors.end(); // non-authorative if not bound
60}
61
62bool TypeInfo::isA(const std::string& typeName) const
63{
64 if (!m_bound) {
65 warning() << "calling isA on unbound type " << m_name;
66 }
67 if (m_name == typeName) {
68 return true;
69 }
70
71 auto I = std::find_if(m_ancestors.begin(), m_ancestors.end(), [&](const TypeInfo* typeInfo){ return typeInfo->m_name == typeName;});
72 return I != m_ancestors.end();
73}
74
75
77{
78 return !m_unresolvedChildren.empty();
79}
80
82{
83 if (m_unresolvedChildren.empty()) {
84 error() << "Type " << m_name << " has no unresolved children";
85 return;
86 }
87
88 auto uchildren = m_unresolvedChildren;
89 for (const auto& child : uchildren) {
90 addChild(m_typeService.getTypeByName(child));
91 }
92
93 assert(m_unresolvedChildren.empty());
94}
95
96void TypeInfo::processTypeData(const Root &atype)
97{
98
99 if (atype->getId() != m_name) {
100 error() << "mis-targeted INFO operation for " << atype->getId() << " arrived at " << m_name;
101 return;
102 }
103
104
105 if (atype->hasAttr("children"))
106 {
107 const Atlas::Message::Element childElem(atype->getAttr("children"));
108 if (!childElem.isList()) {
109 warning() << "'children' element is not of list type when processing entity type " << m_name << ".";
110 } else {
111 const Atlas::Message::ListType & children(childElem.asList());
112
113 for (const auto& childElement : children) {
114 if (childElement.isString()) {
115 TypeInfo* child = m_typeService.findTypeByName(childElement.String());
116 // if the child was already known, don't add to unresolved
117 if (child && m_children.find(child) != m_children.end()) {
118 continue;
119 }
120
121 m_unresolvedChildren.insert(childElement.String());
122 }
123 }
124 }
125 }
126
127
128 //No need to signal changes for "entities" since it's only used for creation of new entities
129 Atlas::Message::Element entitiesElement;
130 if (atype->copyAttr("entities", entitiesElement) == 0) {
131 if (entitiesElement.isList()) {
132 m_entities = std::move(entitiesElement.List());
133 }
134 }
135
136
137 //Don't allow parent and obj type to be changed for already bound types.
138 if (!m_bound) {
139 setParent(m_typeService.getTypeByName(atype->getParent()));
140 m_objType = atype->getObjtype();
141
142 extractDefaultProperties(atype);
143
144 validateBind();
145 } else {
146 //For already bound types we'll extract the properties and check if any changed.
147
148 auto oldProperties = std::move(m_properties);
149
150 extractDefaultProperties(atype);
151
152 for (auto& entry : m_properties) {
153 auto oldEntryI = oldProperties.find(entry.first);
154 if (oldEntryI == oldProperties.end() || oldEntryI->second != entry.second) {
155 PropertyChanges.emit(entry.first, entry.second);
156 }
157
158 if (oldEntryI != oldProperties.end()) {
159 oldProperties.erase(oldEntryI);
160 }
161 }
162
163 //If there are any old properties left they have been removed from the type, we should signal with an empty element.
164 for (auto& entry : oldProperties) {
165 PropertyChanges.emit(entry.first, Atlas::Message::Element());
166 }
167
168 }
169}
170
171bool TypeInfo::operator==(const TypeInfo &x) const
172{
173 if (&m_typeService != &x.m_typeService)
174 warning() << "comparing TypeInfos from different type services, bad";
175
176 return (m_name == x.m_name);
177}
178
179bool TypeInfo::operator<(const TypeInfo &x) const
180{
181 return m_name < x.m_name;
182}
183
184void TypeInfo::setParent(TypeInfo* tp)
185{
186 if (m_parent)
187 {
188 // it's critical we bail fast here to avoid infinite mutual recursion with addChild
189 return;
190 }
191
192 if (m_ancestors.count(tp)) {
193 error() << "Adding " << tp->m_name << " as parent of " << m_name << ", but already marked as ancestor";
194 }
195
196 // update the gear
197 m_parent = tp;
198 addAncestor(tp);
199
200 // note this will never recurse deep because of the fast exiting up top
201 tp->addChild(this);
202}
203
204void TypeInfo::addChild(TypeInfo* tp)
205{
206 assert(tp);
207 if (tp == this) {
208 error() << "Attempt to add " << getName() << " as a child if itself";
209 return;
210 }
211 if (tp->getName() == this->getName()) {
212 error() << "Attempt to add " << getName() << " as child to identical parent ";
213 return;
214 }
215
216 if (m_children.count(tp)) {
217 return;
218 }
219 m_unresolvedChildren.erase(tp->getName());
220
221 m_children.insert(tp);
222 // again this will not recurse due to the termination code
223 tp->setParent(this);
224}
225
226void TypeInfo::addAncestor(TypeInfo* tp)
227{
228 // someone has reported getting into a loop here (i.e a circular inheritance
229 // graph). To try and catch that, I'm putting this assert in. If / when you
230 // hit it, get in touch with James.
231 assert(m_children.count(tp) == 0);
232 assert(m_ancestors.count(tp) == 0);
233
234 m_ancestors.insert(tp);
235
236 auto& parentAncestors = tp->m_ancestors;
237 m_ancestors.insert(parentAncestors.begin(), parentAncestors.end());
238
239 // tell all our children!
240 for (auto child : m_children) {
241 child->addAncestor(tp);
242 }
243}
244
245void TypeInfo::extractDefaultProperties(const Atlas::Objects::Root& atype)
246{
248 if (atype->hasAttr("properties")) {
249 auto propertiesElement = atype->getAttr("properties");
250 if (!propertiesElement.isMap()) {
251 warning() << "'properties' element is not of map type when processing entity type " << m_name << ".";
252 } else {
253 m_properties = propertiesElement.Map();
254 }
255 }
256}
257
258
259const Atlas::Message::Element* TypeInfo::getProperty(const std::string& propertyName) const
260{
262 auto A = m_properties.find(propertyName);
263 if (A != m_properties.end()) {
264 return &(A->second);
265 } else {
267 if (getParent()) {
268 const Atlas::Message::Element* element(getParent()->getProperty(propertyName));
269 if (element) {
270 return element;
271 }
272 }
273 }
274 return nullptr;
275}
276
277void TypeInfo::setProperty(const std::string& propertyName, const Atlas::Message::Element& element)
278{
279 onPropertyChanges(propertyName, element);
280 auto I = m_properties.find(propertyName);
281 if (I == m_properties.end()) {
282 m_properties.insert(Atlas::Message::MapType::value_type(propertyName, element));
283 } else {
284 I->second = element;
285 }
286}
287
288void TypeInfo::onPropertyChanges(const std::string& propertyName, const Atlas::Message::Element& element)
289{
290 PropertyChanges.emit(propertyName, element);
292 for (auto child : getChildren()) {
293 Atlas::Message::MapType::const_iterator J = child->m_properties.find(propertyName);
294 if (J == child->m_properties.end()) {
295 child->onPropertyChanges(propertyName, element);
296 }
297 }
298}
299
301 m_typeService.sendRequest(m_name);
302}
303
304
305void TypeInfo::validateBind()
306{
307 if (m_bound) return;
308
309 // check all our parents
310 if (m_parent) {
311 if (!m_parent->isBound()) return;
312 }
313
314 m_bound = true;
315
316 Bound.emit();
317 m_typeService.BoundType.emit(this);
318
319 for (auto child : m_children) {
320 child->validateBind();
321 }
322}
323
324} // of namespace Eris
The representation of an Atlas type (i.e a class or operation definition). This class supports effice...
Definition: TypeInfo.h:33
bool operator<(const TypeInfo &x) const
efficent ordering of type (uses type ids if possible)
Definition: TypeInfo.cpp:179
bool isA(TypeInfo *ti) const
Test whether this type inherits (directly or indirectly) from the specific class. If this type is not...
Definition: TypeInfo.cpp:48
const std::string & getName() const
the unique type name (matches the Atlas type)
Definition: TypeInfo.h:217
bool operator==(const TypeInfo &x) const
efficent comparisom of types (uses type ids if possible)
Definition: TypeInfo.cpp:171
void processTypeData(const Atlas::Objects::Root &atype)
process the INFO data
Definition: TypeInfo.cpp:96
void refresh()
Request update to the type info from the server.
Definition: TypeInfo.cpp:300
const Atlas::Message::Element * getProperty(const std::string &propertyName) const
Gets the value of the named property. This method will search through both this instance and all of i...
Definition: TypeInfo.cpp:259
void setProperty(const std::string &propertyName, const Atlas::Message::Element &element)
Sets a property.
Definition: TypeInfo.cpp:277
sigc::signal< void, const std::string &, const Atlas::Message::Element & > PropertyChanges
Emitted before an property changes. The first parameter is the name of the property,...
Definition: TypeInfo.h:114
const TypeInfo * getParent() const
Gets the currently resolved parent TypeInfo instances.
Definition: TypeInfo.h:231
sigc::signal< void > Bound
Emitted when the type is bound, i.e there is an unbroken graph of TypeInfo instances through every an...
Definition: TypeInfo.h:152
TypeInfo(std::string id, TypeService &)
forward constructor, when data is not available
Definition: TypeInfo.cpp:24
bool isBound() const
Check the bound flag for this node; if false then recursivley check parents until an authorative is f...
Definition: TypeInfo.h:212
const std::set< TypeInfo * > & getChildren() const
Gets the currently resolved child TypeInfo instances.
Definition: TypeInfo.h:226
bool hasUnresolvedChildren() const
Test if there are child types of the type, which have not yet been retrieved from the server.
Definition: TypeInfo.cpp:76
void onPropertyChanges(const std::string &propertyName, const Atlas::Message::Element &element)
Called before the PropertyChanges signal is emitted. This call is made before an property is changed....
Definition: TypeInfo.cpp:288
void resolveChildren()
Retrive all child types from the server. This will log an error and do nothing if no unresolved child...
Definition: TypeInfo.cpp:81
void sendRequest(const std::string &id)
TypeInfo * findTypeByName(const std::string &tynm)
Definition: TypeService.cpp:51
sigc::signal< void, TypeInfo * > BoundType
Definition: TypeService.h:44
TypeInfo * getTypeByName(const std::string &tynm)
Definition: TypeService.cpp:61
Definition: Account.cpp:33