File indexing completed on 2024-05-12 17:15:20

0001 /*
0002    Copyright (C) 2013 Andreas Hartmetz <ahartmetz@gmail.com>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LGPL.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 
0019    Alternatively, this file is available under the Mozilla Public License
0020    Version 1.1.  You may obtain a copy of the License at
0021    http://www.mozilla.org/MPL/
0022 */
0023 
0024 #include "introspection.h"
0025 
0026 #include "../testutil.h"
0027 
0028 #include <tinyxml2.h>
0029 
0030 #include <fstream>
0031 #include <string>
0032 #include <iostream>
0033 
0034 namespace tx2 = tinyxml2;
0035 
0036 // Extract the doctype declaration to insert into the re-synthesized XML. It could just be
0037 // omitted from the reference results, but I'd rather test with completely "real" data.
0038 
0039 static void addDoctypeDecl(tx2::XMLDocument *doc)
0040 {
0041     tx2::XMLDocument d;
0042     d.LoadFile(TEST_DATADIR "/introspect1.xml");
0043     tx2::XMLNode *doctypeDecl = d.FirstChild()->ShallowClone(doc);
0044     doc->InsertFirstChild(doctypeDecl);
0045 }
0046 
0047 static std::string readFile(const char *filename)
0048 {
0049     // Dear STL, you must be kidding...
0050     std::ifstream ifs(filename);
0051     return std::string((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
0052 }
0053 
0054 ///////////////////////////////////////////////////////////////////////////
0055 // Functions to turn introspection data structure back into XML
0056 
0057 // the first argument can't be a XMLElement because an XMLDocument isn't an XMLElement
0058 static void xmlizeNode(tx2::XMLNode *el, const IntrospectionNode *node);
0059 static void xmlizeInterface(tx2::XMLElement *el, const Interface *iface);
0060 static void xmlizeMethod(tx2::XMLElement *el, const Method *method);
0061 static void xmlizeArgument(tx2::XMLElement *el, const Argument *arg, bool isSignal);
0062 static void xmlizeProperty(tx2::XMLElement *el, const Property *property);
0063 
0064 static tx2::XMLElement *addElement(tx2::XMLNode *xmlNode, const char *type, const std::string &name)
0065 {
0066     tx2::XMLElement *el = xmlNode->GetDocument()->NewElement(type);
0067     el->SetAttribute("name", name.c_str());
0068     xmlNode->InsertEndChild(el);
0069     return el;
0070 }
0071 
0072 static void xmlizeNode(tx2::XMLNode *el, const IntrospectionNode *node)
0073 {
0074     tx2::XMLElement *nodeEl = addElement(el, "node", node->name);
0075 
0076     std::map<std::string, Interface>::const_iterator iIt = node->interfaces.begin();
0077     for (; iIt != node->interfaces.end(); ++iIt) {
0078         xmlizeInterface(nodeEl, &iIt->second);
0079     }
0080 
0081     std::map<std::string, IntrospectionNode *>::const_iterator nIt = node->children.begin();
0082     for (; nIt != node->children.end(); ++nIt) {
0083         xmlizeNode(nodeEl, nIt->second);
0084     }
0085 
0086 }
0087 
0088 static void xmlizeInterface(tx2::XMLElement *el, const Interface *iface)
0089 {
0090     tx2::XMLElement *ifaceEl = addElement(el, "interface", iface->name);
0091 
0092     std::map<std::string, Method>::const_iterator mIt = iface->methods.begin();
0093     for (; mIt != iface->methods.end(); ++mIt) {
0094         xmlizeMethod(ifaceEl, &mIt->second);
0095     }
0096 
0097     std::map<std::string, Property>::const_iterator pIt = iface->properties.begin();
0098     for (; pIt != iface->properties.end(); ++pIt) {
0099         xmlizeProperty(ifaceEl, &pIt->second);
0100     }
0101 }
0102 
0103 static void xmlizeMethod(tx2::XMLElement *el, const Method *method)
0104 {
0105     bool isSignal = method->type == Message::SignalMessage;
0106     tx2::XMLElement *methodEl = addElement(el, isSignal ? "signal" : "method", method->name);
0107 
0108     for (const Argument &arg : method->arguments) {
0109         xmlizeArgument(methodEl, &arg, isSignal);
0110     }
0111 }
0112 
0113 static void xmlizeArgument(tx2::XMLElement *el, const Argument *arg, bool /* TODO! isSignal */)
0114 {
0115     tx2::XMLElement *argEl = addElement(el, "arg", arg->name);
0116     if (!arg->name.empty()) {
0117         argEl->SetAttribute("name", arg->name.c_str());
0118     }
0119     argEl->SetAttribute("type", arg->type.c_str());
0120     argEl->SetAttribute("direction", arg->isDirectionOut ? "out" : "in");
0121 }
0122 
0123 static void xmlizeProperty(tx2::XMLElement *el, const Property *property)
0124 {
0125     tx2::XMLElement *propEl = addElement(el, "property", property->name);
0126     propEl->SetAttribute("type", property->type.c_str());
0127     propEl->SetAttribute("type", property->type.c_str());
0128 
0129     const char *access = property->access == Property::Read ? "read" :
0130                          property->access == Property::Write ? "write" : "readwrite";
0131     propEl->SetAttribute("access", access);
0132 }
0133 
0134 
0135 
0136 ///////////////////////////////////////////////////////////////////////////
0137 
0138 static void testBasicRoundtrip()
0139 {
0140     const char *filename = TEST_DATADIR "/introspect1.xml";
0141     IntrospectionTree tree;
0142     TEST(tree.mergeXml(readFile(filename).c_str(), ""));
0143 
0144     tx2::XMLDocument doc;
0145     addDoctypeDecl(&doc);
0146     xmlizeNode(&doc, tree.m_rootNode);
0147 
0148     {
0149         tx2::XMLDocument normalized;
0150         normalized.LoadFile(TEST_DATADIR "/introspect1.xml");
0151         tx2::XMLPrinter printer;
0152         normalized.Print(&printer);
0153         std::cout << printer.CStr() << '\n';
0154     }
0155 
0156     tx2::XMLPrinter printer;
0157     doc.Print(&printer);
0158     std::cout << printer.CStr() << '\n';
0159 }
0160 
0161 int main(int, char *[])
0162 {
0163     testBasicRoundtrip();
0164     std::cout << "Passed!\n";
0165 }