Warning, file /sdk/dferry/client/introspection.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 #include "introspection.h"
0002 
0003 #include "arguments.h"
0004 #include "stringtools.h"
0005 
0006 #include <tinyxml2.h>
0007 
0008 #include <cassert>
0009 #include <cstring> // for strcmp...
0010 #include <memory>
0011 
0012 namespace tx2 = tinyxml2;
0013 
0014 // strcmp()'s return value is easy to misinterpret, especially for equality...
0015 static bool strequal(const char *c1, const char *c2)
0016 {
0017     return !strcmp(c1, c2);
0018 }
0019 
0020 // This function exists to avoid making the many copies that a naive recursive implementation would make.
0021 // That it makes it possible to reserve() is just a nice extra.
0022 static void introspectionNodePathHelper(const IntrospectionNode *node, uint32 length, std::string *out)
0023 {
0024     if (node->parent) {
0025         introspectionNodePathHelper(node->parent, length + 1 + node->name.length(), out);
0026         *out += '/';
0027         *out += node->name;
0028     } else {
0029         // root node; reserve just enough room in output string before we start filling it in
0030         out->reserve(length);
0031     }
0032 }
0033 
0034 std::string IntrospectionNode::path() const
0035 {
0036     if (!parent) {
0037         return std::string("/");
0038     }
0039     std::string ret;
0040     introspectionNodePathHelper(this, 0, &ret);
0041     return ret;
0042 }
0043 
0044 IntrospectionTree::IntrospectionTree()
0045    : m_rootNode(new IntrospectionNode)
0046 {
0047     m_rootNode->parent = nullptr;
0048     // name stays empty for the root
0049 }
0050 
0051 IntrospectionTree::~IntrospectionTree()
0052 {
0053     delete m_rootNode;
0054     m_rootNode = nullptr;
0055 }
0056 
0057 bool IntrospectionTree::mergeXml(const char *xmlData, const char *_path)
0058 {
0059     // TODO use path to replace an empty root node name in the XML document
0060     // TODO is it OK to add interfaces to the root node? if so, review/test the code to ensure that it works!
0061     // TODO what about adding interfaces to other existing nodes? (this is not taken care of in current code!)
0062     // TODO check the path names (absolute / relative according to spec)
0063     // If things turn out to be complicated, it might be better to first create a detached tree from the XML
0064     // document, then check if it can be merged without conflicts, then merge it. That requires not rollback.
0065     tx2::XMLDocument doc;
0066     if (doc.Parse(xmlData) != tx2::XML_SUCCESS) {
0067         return false;
0068     }
0069     tx2::XMLElement *el = doc.RootElement();
0070     if (!el || !strequal(el->Name(), "node")) {
0071         return false;
0072     }
0073     std::string path = _path;
0074     const tx2::XMLAttribute *attr = el->FirstAttribute();
0075     if (attr && strequal(attr->Name(), "name")) {
0076         std::string intrinsicPath = attr->Value();
0077         if (!path.empty() && path != intrinsicPath) {
0078             return false;
0079         }
0080         path = intrinsicPath;
0081     }
0082 
0083     // ### Should we do this? if (path.empty()) { path = "/"; }
0084 
0085     std::string leafName;
0086     IntrospectionNode *parent = findOrCreateParent(path.c_str(), &leafName);
0087     if (!parent || parent->children.count(leafName)) {
0088         // the second condition makes sure that we don't overwrite existing nodes
0089         return false;
0090     }
0091 
0092     if (!addNode(parent, el)) {
0093         // TODO undo creation of parent nodes by findOrCreateParent() here
0094 
0095         return false;
0096     }
0097     return true;
0098 }
0099 
0100 IntrospectionNode *IntrospectionTree::findOrCreateParent(const char *path, std::string *leafName)
0101 {
0102     cstring csPath(path);
0103     if (!Arguments::isObjectPathValid(csPath)) {
0104         return nullptr;
0105     }
0106     std::string strPath(path, csPath.length); // prevent another strlen()
0107     std::vector<std::string> elements = split(strPath, '/', false);
0108 
0109     IntrospectionNode *node = m_rootNode;
0110 
0111     // the leaf node is to be created later, hence we omit the last path element
0112     for (size_t i = 0; i + 1 < elements.size(); i++) {
0113         std::map<std::string, IntrospectionNode *>::iterator it = node->children.find(elements[i]);
0114         if (it != node->children.end()) {
0115             node = it->second;
0116         } else {
0117             IntrospectionNode *newNode = new IntrospectionNode;
0118             newNode->name = elements[i];
0119             node->children[elements[i]] = newNode;
0120             node = newNode;
0121         }
0122     }
0123     if (leafName) {
0124         if (elements.empty()) {
0125             leafName->clear();
0126         } else {
0127             *leafName = elements.back();
0128         }
0129     }
0130     return node;
0131 }
0132 
0133 // careful with this one when not calling it from findOrCreateParent() or mergeXml() - if applied to a node
0134 // that was created earlier, it might delete children that shouldn't be deleted.
0135 void IntrospectionTree::pruneBranch(IntrospectionNode *node)
0136 {
0137     IntrospectionNode *const parent = node->parent;
0138     // remove the node itself, including any children
0139     removeNode(node);
0140 
0141     // remove all now empty parents (except the root node)
0142     for (node = parent; node != m_rootNode; ) {
0143         assert(node->parent->children.count(node->name) == 1);
0144         if (node->children.size() == 1 && node->interfaces.empty()) {
0145             // we must have deleted that child while ascending; just remove the current node
0146             IntrospectionNode *exNode = node;
0147             node = node->parent;
0148             delete exNode;
0149         } else {
0150             // this node has other children, so we are going to keep it and update its children map
0151             // (we want to do this for the root node, too, so break here and do it after the loop)
0152             break;
0153         }
0154     }
0155 }
0156 
0157 void IntrospectionTree::removeNode(IntrospectionNode *node)
0158 {
0159     std::map<std::string, IntrospectionNode *>::iterator it = node->children.begin();
0160     for (; it != node->children.end(); ++it) {
0161         removeNode(it->second);
0162     }
0163     delete node;
0164 }
0165 
0166 bool IntrospectionTree::addNode(IntrospectionNode *parent, const tx2::XMLElement *el)
0167 {
0168     const tx2::XMLAttribute *attr = el->FirstAttribute();
0169     if (!attr || !strequal(attr->Name(), "name") || attr->Next()) {
0170         return false;
0171     }
0172 
0173     // const bool isRootOfDocument = el == el->GetDocument()->RootElement();
0174 
0175     std::unique_ptr<IntrospectionNode> node(new IntrospectionNode);
0176     node->parent = parent;
0177     node->name = attr->Value();
0178 
0179     for (const tx2::XMLElement *child = el->FirstChildElement(); child; child = child->NextSiblingElement()) {
0180         if (strequal(child->Name(), "node")) {
0181             if (!addNode(node.get(), child)) {
0182                 return false;
0183             }
0184         } else if (strequal(child->Name(), "interface")) {
0185             if (!addInterface(node.get(), child)) {
0186                 return false;
0187             }
0188         }
0189     }
0190     parent->children.emplace(node->name, node.get());
0191     node.release();
0192     return true;
0193 }
0194 
0195 bool IntrospectionTree::addInterface(IntrospectionNode *node, const tx2::XMLElement *el)
0196 {
0197     const tx2::XMLAttribute *attr = el->FirstAttribute();
0198     if (!attr || !strequal(attr->Name(), "name") || attr->Next()) {
0199         return false;
0200     }
0201 
0202     Interface iface;
0203     iface.name = attr->Value();
0204 
0205     for (const tx2::XMLElement *child = el->FirstChildElement(); child; child = child->NextSiblingElement()) {
0206         if (strequal(child->Name(), "method")) {
0207             if (!addMethod(&iface, child, Message::MethodCallMessage)) {
0208                 return false;
0209             }
0210         } else if (strequal(child->Name(), "signal")) {
0211             if (!addMethod(&iface, child, Message::SignalMessage)) {
0212                 return false;
0213             }
0214         } else if (strequal(child->Name(), "property")) {
0215             if (!addProperty(&iface, child)) {
0216                 return false;
0217             }
0218         } else {
0219             return false;
0220         }
0221     }
0222     node->interfaces[iface.name] = iface;
0223     return true;
0224 }
0225 
0226 bool IntrospectionTree::addMethod(Interface *iface, const tx2::XMLElement *el, Message::Type messageType)
0227 {
0228     const tx2::XMLAttribute *attr = el->FirstAttribute();
0229     if (!attr || !strequal(attr->Name(), "name") || attr->Next()) {
0230         return false;
0231     }
0232 
0233     Method method;
0234     method.type = messageType;
0235     method.name = attr->Value();
0236 
0237     for (const tx2::XMLElement *child = el->FirstChildElement(); child; child = child->NextSiblingElement()) {
0238         if (strequal(child->Name(), "arg")) {
0239             if (!addArgument(&method, child, messageType)) {
0240                 return false;
0241             }
0242         } else if (strequal(child->Name(), "annotation")) {
0243             // annotations are allowed, but we don't use them
0244             continue;
0245         } else {
0246             return false;
0247         }
0248     }
0249     iface->methods[method.name] = method;
0250     return true;
0251 }
0252 
0253 bool IntrospectionTree::addArgument(Method *method, const tx2::XMLElement *el, Message::Type messageType)
0254 {
0255     Argument arg;
0256     arg.isDirectionOut = messageType == Message::SignalMessage;
0257     for (const tx2::XMLAttribute *attr = el->FirstAttribute(); attr; attr = attr->Next()) {
0258         if (strequal(attr->Name(), "name")) {
0259             arg.name = attr->Value();
0260         } else if (strequal(attr->Name(), "type")) {
0261             arg.type = attr->Value();
0262             // TODO validate (single complete type!)
0263         } else if (strequal(attr->Name(), "direction")) {
0264             if (strequal(attr->Value(), "in")) {
0265                 if (messageType == Message::SignalMessage) {
0266                     return false;
0267                 }
0268                 arg.isDirectionOut = false;
0269             } else if (strequal(attr->Value(), "out")) {
0270                 arg.isDirectionOut = true;
0271             } else {
0272                 return false;
0273             }
0274         } else {
0275             return false;
0276         }
0277     }
0278     if (arg.type.empty()) {
0279         return false;
0280     }
0281     method->arguments.push_back(arg);
0282     return true;
0283 }
0284 
0285 bool IntrospectionTree::addProperty(Interface *iface, const tx2::XMLElement *el)
0286 {
0287     Property prop;
0288     prop.access = Property::Invalid;
0289     for (const tx2::XMLAttribute *attr = el->FirstAttribute(); attr; attr = attr->Next()) {
0290         if (strequal(attr->Name(), "name")) {
0291             // TODO validate
0292             prop.name = attr->Value();
0293         } else if (strequal(attr->Name(), "type")) {
0294             // TODO validate - don't forget to check it's a single complete type
0295             prop.type = attr->Value();
0296         } else if (strequal(attr->Name(), "access")) {
0297             if (strequal(attr->Value(), "readwrite")) {
0298                 prop.access = Property::ReadWrite;
0299             } else if (strequal(attr->Value(), "read")) {
0300                 prop.access = Property::Read;
0301             } else if (strequal(attr->Value(), "write")) {
0302                 prop.access = Property::Write;
0303             } else {
0304                 return false;
0305             }
0306         } else {
0307             return false;
0308         }
0309     }
0310     if (prop.name.empty() || prop.type.empty() || prop.access == Property::Invalid) {
0311         return false;
0312     }
0313     iface->properties[prop.name] = prop;
0314     return true;
0315 }