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 
17 using Atlas::Objects::Root;
18 using namespace Atlas::Objects::Operation;
19 
20 namespace Eris {
21 
23 
24 TypeInfo::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 
35 TypeInfo::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 
48 bool 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 
62 bool 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 
96 void 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 
171 bool 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 
179 bool TypeInfo::operator<(const TypeInfo &x) const
180 {
181  return m_name < x.m_name;
182 }
183 
184 void 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 
204 void 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 
226 void 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 
245 void 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 
259 const 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 
277 void 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 
288 void 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 
305 void 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
Eris::TypeInfo::setProperty
void setProperty(const std::string &propertyName, const Atlas::Message::Element &element)
Sets a property.
Definition: TypeInfo.cpp:277
Eris::TypeService::sendRequest
void sendRequest(const std::string &id)
Definition: TypeService.cpp:155
Eris::TypeService::BoundType
sigc::signal< void, TypeInfo * > BoundType
Definition: TypeService.h:44
Eris::TypeInfo::isBound
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
Eris::TypeInfo::isA
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
Eris::TypeInfo::resolveChildren
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
Eris::TypeInfo::getName
const std::string & getName() const
the unique type name (matches the Atlas type)
Definition: TypeInfo.h:217
Eris::TypeService
Definition: TypeService.h:23
Eris::TypeInfo::getChildren
const std::set< TypeInfo * > & getChildren() const
Gets the currently resolved child TypeInfo instances.
Definition: TypeInfo.h:226
Eris::TypeInfo::getParent
const TypeInfo * getParent() const
Gets the currently resolved parent TypeInfo instances.
Definition: TypeInfo.h:231
Eris::TypeInfo::operator==
bool operator==(const TypeInfo &x) const
efficent comparisom of types (uses type ids if possible)
Definition: TypeInfo.cpp:171
Eris::TypeInfo::PropertyChanges
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
Eris::error
Definition: LogStream.h:65
Eris::TypeService::getTypeByName
TypeInfo * getTypeByName(const std::string &tynm)
Definition: TypeService.cpp:61
Eris::TypeInfo::getProperty
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
Eris::TypeInfo::hasUnresolvedChildren
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
Eris
Definition: Account.cpp:33
Eris::TypeInfo::processTypeData
void processTypeData(const Atlas::Objects::Root &atype)
process the INFO data
Definition: TypeInfo.cpp:96
Eris::TypeInfo::TypeInfo
TypeInfo(std::string id, TypeService &)
forward constructor, when data is not available
Definition: TypeInfo.cpp:24
Eris::TypeInfo
The representation of an Atlas type (i.e a class or operation definition). This class supports effice...
Definition: TypeInfo.h:32
Eris::TypeInfo::operator<
bool operator<(const TypeInfo &x) const
efficent ordering of type (uses type ids if possible)
Definition: TypeInfo.cpp:179
Eris::TypeInfo::refresh
void refresh()
Request update to the type info from the server.
Definition: TypeInfo.cpp:300
Eris::TypeService::findTypeByName
TypeInfo * findTypeByName(const std::string &tynm)
Definition: TypeService.cpp:51
Eris::warning
Definition: LogStream.h:55
Eris::TypeInfo::Bound
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
Eris::TypeInfo::onPropertyChanges
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