summaryrefslogtreecommitdiff
path: root/unity-shared
diff options
Diffstat (limited to 'unity-shared')
-rw-r--r--unity-shared/CMakeLists.txt1
-rw-r--r--unity-shared/DebugDBusInterface.cpp295
-rw-r--r--unity-shared/DebugDBusInterface.h2
-rw-r--r--unity-shared/XPathQueryPart.cpp186
-rw-r--r--unity-shared/XPathQueryPart.h45
5 files changed, 359 insertions, 170 deletions
diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt
index 3f02801d9..ead1ec981 100644
--- a/unity-shared/CMakeLists.txt
+++ b/unity-shared/CMakeLists.txt
@@ -64,6 +64,7 @@ set (UNITY_SHARED_SOURCES
VScrollBarOverlayWindow.cpp
WindowButtons.cpp
WindowManager.cpp
+ XPathQueryPart.cpp
)
if(ENABLE_X_SUPPORT)
diff --git a/unity-shared/DebugDBusInterface.cpp b/unity-shared/DebugDBusInterface.cpp
index c95534297..7aedca53a 100644
--- a/unity-shared/DebugDBusInterface.cpp
+++ b/unity-shared/DebugDBusInterface.cpp
@@ -28,12 +28,10 @@
#include <boost/bind.hpp>
#include <NuxCore/Logger.h>
#include <NuxCore/LoggingWriter.h>
-#include <xpathselect/node.h>
-#include <xpathselect/xpathselect.h>
-#include <dlfcn.h>
#include "DebugDBusInterface.h"
#include "Introspectable.h"
+#include "XPathQueryPart.h"
namespace unity
{
@@ -47,149 +45,9 @@ namespace
namespace local
{
std::ofstream output_file;
-
- class IntrospectableAdapter: public xpathselect::Node
- {
- public:
- typedef std::shared_ptr<IntrospectableAdapter> Ptr;
- IntrospectableAdapter(Introspectable* node)
- : node_(node)
- {}
-
- std::string GetName() const
- {
- return node_->GetName();
- }
-
- bool MatchProperty(const std::string& name, const std::string& value) const
- {
- bool matches = false;
-
- GVariantBuilder child_builder;
- g_variant_builder_init(&child_builder, G_VARIANT_TYPE("a{sv}"));
- g_variant_builder_add(&child_builder, "{sv}", "id", g_variant_new_uint64(node_->GetIntrospectionId()));
- node_->AddProperties(&child_builder);
- GVariant* prop_dict = g_variant_builder_end(&child_builder);
- GVariant *prop_value = g_variant_lookup_value(prop_dict, name.c_str(), NULL);
-
- if (prop_value != NULL)
- {
- GVariantClass prop_val_type = g_variant_classify(prop_value);
- // it'd be nice to be able to do all this with one method. However, the booleans need
- // special treatment, and I can't figure out how to group all the integer types together
- // without resorting to template functions.... and we all know what happens when you
- // start doing that...
- switch (prop_val_type)
- {
- case G_VARIANT_CLASS_STRING:
- {
- const gchar* prop_val = g_variant_get_string(prop_value, NULL);
- if (g_strcmp0(prop_val, value.c_str()) == 0)
- {
- matches = true;
- }
- }
- break;
- case G_VARIANT_CLASS_BOOLEAN:
- {
- std::string value = boost::to_upper_copy(value);
- bool p = value == "TRUE" ||
- value == "ON" ||
- value == "YES" ||
- value == "1";
- matches = (g_variant_get_boolean(prop_value) == p);
- }
- break;
- case G_VARIANT_CLASS_BYTE:
- {
- // It would be nice if I could do all the integer types together, but I couldn't see how...
- std::stringstream stream(value);
- int val; // changing this to guchar causes problems.
- stream >> val;
- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
- val == g_variant_get_byte(prop_value);
- }
- break;
- case G_VARIANT_CLASS_INT16:
- {
- std::stringstream stream(value);
- gint16 val;
- stream >> val;
- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
- val == g_variant_get_int16(prop_value);
- }
- break;
- case G_VARIANT_CLASS_UINT16:
- {
- std::stringstream stream(value);
- guint16 val;
- stream >> val;
- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
- val == g_variant_get_uint16(prop_value);
- }
- break;
- case G_VARIANT_CLASS_INT32:
- {
- std::stringstream stream(value);
- gint32 val;
- stream >> val;
- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
- val == g_variant_get_int32(prop_value);
- }
- break;
- case G_VARIANT_CLASS_UINT32:
- {
- std::stringstream stream(value);
- guint32 val;
- stream >> val;
- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
- val == g_variant_get_uint32(prop_value);
- }
- break;
- case G_VARIANT_CLASS_INT64:
- {
- std::stringstream stream(value);
- gint64 val;
- stream >> val;
- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
- val == g_variant_get_int64(prop_value);
- }
- break;
- case G_VARIANT_CLASS_UINT64:
- {
- std::stringstream stream(value);
- guint64 val;
- stream >> val;
- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
- val == g_variant_get_uint64(prop_value);
- }
- break;
- default:
- LOG_WARNING(logger) << "Unable to match against property of unknown type.";
- };
- }
- g_variant_unref(prop_value);
- g_variant_unref(prop_dict);
- return matches;
- }
-
- std::vector<xpathselect::Node::Ptr> Children() const
- {
- std::vector<xpathselect::Node::Ptr> children;
- for(auto child: node_->GetIntrospectableChildren())
- {
- children.push_back(std::make_shared<IntrospectableAdapter>(child));
- }
- return children;
-
- }
-
- Introspectable* node_;
- };
}
}
-bool TryLoadXPathImplementation();
GVariant* GetState(std::string const& query);
void StartLogToFile(std::string const& file_path);
void ResetLogging();
@@ -363,40 +221,20 @@ DebugDBusInterface::HandleDBusMethodCall(GDBusConnection* connection,
}
}
+
GVariant* GetState(std::string const& query)
{
+ // process the XPath query:
+ std::list<Introspectable*> parts = GetIntrospectableNodesFromQuery(query, _parent_introspectable);
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE("a(sv)"));
-
- // try load the xpathselect library:
- void* driver = dlopen("libxpathselect.so", RTLD_LAZY);
- if (driver)
+ if (parts.empty())
{
- typedef decltype(&xpathselect::SelectNodes) entry_t;
- // clear errors:
- dlerror();
- entry_t entry_point = (entry_t) dlsym(driver, "SelectNodes");
- const char* err = dlerror();
- if (err)
- {
- LOG_ERROR(logger) << "Unable to load entry point in libxpathselect: " << err;
- }
- else
- {
- // process the XPath query:
- local::IntrospectableAdapter::Ptr root_node = std::make_shared<local::IntrospectableAdapter>(_parent_introspectable);
- auto nodes = entry_point(root_node, query);
- for (auto n : nodes)
- {
- auto p = std::static_pointer_cast<local::IntrospectableAdapter>(n);
- if (p)
- g_variant_builder_add(&builder, "(sv)", p->node_->GetName().c_str(), p->node_->Introspect());
- }
- }
+ LOG_WARNING(logger) << "Query '" << query << "' Did not match anything.";
}
- else
+ for (Introspectable *node : parts)
{
- LOG_WARNING(logger) << "Cannot complete introspection request because libxpathselect is not installed.";
+ g_variant_builder_add(&builder, "(sv)", node->GetName().c_str(), node->Introspect());
}
return g_variant_new("(a(sv))", &builder);
@@ -436,5 +274,122 @@ void LogMessage(std::string const& severity,
}
}
+/*
+ * Do a breadth-first search of the introspection tree and find all nodes that match the
+ * query.
+ */
+std::list<Introspectable*> GetIntrospectableNodesFromQuery(std::string const& query, Introspectable* tree_root)
+{
+ std::list<Introspectable*> start_points;
+ std::string sanitised_query;
+ // Allow user to be lazy when specifying root node.
+ if (query == "" || query == "/")
+ {
+ sanitised_query = "/" + tree_root->GetName();
+ }
+ else
+ {
+ sanitised_query = query;
+ }
+ // split query into parts
+ std::list<XPathQueryPart> query_parts;
+
+ {
+ std::list<std::string> query_strings;
+ boost::algorithm::split(query_strings, sanitised_query, boost::algorithm::is_any_of("/"));
+ // Boost's split() implementation does not match it's documentation! According to the
+ // docs, it's not supposed to add empty strings, but it does, which is a PITA. This
+ // next line removes them:
+ query_strings.erase( std::remove_if( query_strings.begin(),
+ query_strings.end(),
+ boost::bind( &std::string::empty, _1 ) ),
+ query_strings.end());
+ for (auto part : query_strings)
+ {
+ query_parts.push_back(XPathQueryPart(part));
+ }
+ }
+
+ // absolute or relative query string?
+ if (sanitised_query.at(0) == '/' && sanitised_query.at(1) != '/')
+ {
+ // absolute query - start point is tree root node.
+ if (query_parts.front().Matches(tree_root))
+ {
+ start_points.push_back(tree_root);
+ }
+ }
+ else
+ {
+ // relative - need to do a depth first tree search for all nodes that match the
+ // first node in the query.
+
+ // warn about malformed queries (all queries must start with '/')
+ if (sanitised_query.at(0) != '/')
+ {
+ LOG_WARNING(logger) << "Malformed relative introspection query: '" << query << "'.";
+ }
+
+ // non-recursive BFS traversal to find starting points:
+ std::queue<Introspectable*> queue;
+ queue.push(tree_root);
+ while (!queue.empty())
+ {
+ Introspectable *node = queue.front();
+ queue.pop();
+ if (query_parts.front().Matches(node))
+ {
+ // found one. We keep going deeper, as there may be another node beneath this one
+ // with the same node name.
+ start_points.push_back(node);
+ }
+ // Add all children of current node to queue.
+ for (Introspectable* child : node->GetIntrospectableChildren())
+ {
+ queue.push(child);
+ }
+ }
+ }
+
+ // now we have the tree start points, process them:
+ query_parts.pop_front();
+ typedef std::pair<Introspectable*, std::list<XPathQueryPart>::iterator> node_match_pair;
+
+ std::queue<node_match_pair> traverse_queue;
+ for (Introspectable *node : start_points)
+ {
+ traverse_queue.push(node_match_pair(node, query_parts.begin()));
+ }
+ start_points.clear();
+
+ while (!traverse_queue.empty())
+ {
+ node_match_pair p = traverse_queue.front();
+ traverse_queue.pop();
+
+ Introspectable *node = p.first;
+ auto query_it = p.second;
+
+ if (query_it == query_parts.end())
+ {
+ // found a match:
+ start_points.push_back(node);
+ }
+ else
+ {
+ // push all children of current node to start of queue, advance search iterator, and loop again.
+ for (Introspectable* child : node->GetIntrospectableChildren())
+ {
+ if (query_it->Matches(child))
+ {
+ auto it_copy(query_it);
+ ++it_copy;
+ traverse_queue.push(node_match_pair(child, it_copy));
+ }
+ }
+ }
+ }
+ return start_points;
+}
}
}
diff --git a/unity-shared/DebugDBusInterface.h b/unity-shared/DebugDBusInterface.h
index 95c9c8098..3c11a5a0b 100644
--- a/unity-shared/DebugDBusInterface.h
+++ b/unity-shared/DebugDBusInterface.h
@@ -31,6 +31,8 @@ extern const std::string DBUS_BUS_NAME;
namespace debug
{
class Introspectable;
+std::list<Introspectable*> GetIntrospectableNodesFromQuery(std::string const& query, Introspectable *tree_root);
+
class DebugDBusInterface
{
public:
diff --git a/unity-shared/XPathQueryPart.cpp b/unity-shared/XPathQueryPart.cpp
new file mode 100644
index 000000000..3608a50b4
--- /dev/null
+++ b/unity-shared/XPathQueryPart.cpp
@@ -0,0 +1,186 @@
+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
+/*
+ * Copyright (C) 2010 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Thomi Richards <thomi.richards@canonical.com>
+ */
+
+#include <sstream>
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/bind.hpp>
+#include <NuxCore/Logger.h>
+#include "XPathQueryPart.h"
+#include "Introspectable.h"
+
+namespace unity
+{
+
+namespace debug
+{
+DECLARE_LOGGER(logger, "unity.debug.xpath");
+
+// Stores a part of an XPath query.
+XPathQueryPart::XPathQueryPart(std::string const& query_part)
+{
+ std::vector<std::string> part_pieces;
+ boost::algorithm::split(part_pieces, query_part, boost::algorithm::is_any_of("[]="));
+ // Boost's split() implementation does not match it's documentation! According to the
+ // docs, it's not supposed to add empty strings, but it does, which is a PITA. This
+ // next line removes them:
+ part_pieces.erase( std::remove_if( part_pieces.begin(),
+ part_pieces.end(),
+ boost::bind( &std::string::empty, _1 ) ),
+ part_pieces.end());
+ if (part_pieces.size() == 1)
+ {
+ node_name_ = part_pieces.at(0);
+ }
+ else if (part_pieces.size() == 3)
+ {
+ node_name_ = part_pieces.at(0);
+ param_name_ = part_pieces.at(1);
+ param_value_ = part_pieces.at(2);
+ }
+ else
+ {
+ LOG_WARNING(logger) << "Malformed query part: " << query_part;
+ // assume it's just a node name:
+ node_name_ = query_part;
+ }
+}
+
+bool XPathQueryPart::Matches(Introspectable* node) const
+{
+ bool matches = false;
+ if (param_name_ == "")
+ {
+ matches = (node_name_ == "*" || node->GetName() == node_name_);
+ }
+ else
+ {
+ GVariantBuilder child_builder;
+ g_variant_builder_init(&child_builder, G_VARIANT_TYPE("a{sv}"));
+ g_variant_builder_add(&child_builder, "{sv}", "id", g_variant_new_uint64(node->GetIntrospectionId()));
+ node->AddProperties(&child_builder);
+ GVariant* prop_dict = g_variant_builder_end(&child_builder);
+ GVariant *prop_value = g_variant_lookup_value(prop_dict, param_name_.c_str(), NULL);
+
+ if (prop_value != NULL)
+ {
+ GVariantClass prop_val_type = g_variant_classify(prop_value);
+ // it'd be nice to be able to do all this with one method. However, the booleans need
+ // special treatment, and I can't figure out how to group all the integer types together
+ // without resorting to template functions.... and we all know what happens when you
+ // start doing that...
+ switch (prop_val_type)
+ {
+ case G_VARIANT_CLASS_STRING:
+ {
+ const gchar* prop_val = g_variant_get_string(prop_value, NULL);
+ if (g_strcmp0(prop_val, param_value_.c_str()) == 0)
+ {
+ matches = true;
+ }
+ }
+ break;
+ case G_VARIANT_CLASS_BOOLEAN:
+ {
+ std::string value = boost::to_upper_copy(param_value_);
+ bool p = value == "TRUE" ||
+ value == "ON" ||
+ value == "YES" ||
+ value == "1";
+ matches = (g_variant_get_boolean(prop_value) == p);
+ }
+ break;
+ case G_VARIANT_CLASS_BYTE:
+ {
+ // It would be nice if I could do all the integer types together, but I couldn't see how...
+ std::stringstream stream(param_value_);
+ int val; // changing this to guchar causes problems.
+ stream >> val;
+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
+ val == g_variant_get_byte(prop_value);
+ }
+ break;
+ case G_VARIANT_CLASS_INT16:
+ {
+ std::stringstream stream(param_value_);
+ gint16 val;
+ stream >> val;
+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
+ val == g_variant_get_int16(prop_value);
+ }
+ break;
+ case G_VARIANT_CLASS_UINT16:
+ {
+ std::stringstream stream(param_value_);
+ guint16 val;
+ stream >> val;
+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
+ val == g_variant_get_uint16(prop_value);
+ }
+ break;
+ case G_VARIANT_CLASS_INT32:
+ {
+ std::stringstream stream(param_value_);
+ gint32 val;
+ stream >> val;
+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
+ val == g_variant_get_int32(prop_value);
+ }
+ break;
+ case G_VARIANT_CLASS_UINT32:
+ {
+ std::stringstream stream(param_value_);
+ guint32 val;
+ stream >> val;
+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
+ val == g_variant_get_uint32(prop_value);
+ }
+ break;
+ case G_VARIANT_CLASS_INT64:
+ {
+ std::stringstream stream(param_value_);
+ gint64 val;
+ stream >> val;
+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
+ val == g_variant_get_int64(prop_value);
+ }
+ break;
+ case G_VARIANT_CLASS_UINT64:
+ {
+ std::stringstream stream(param_value_);
+ guint64 val;
+ stream >> val;
+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
+ val == g_variant_get_uint64(prop_value);
+ }
+ break;
+ default:
+ LOG_WARNING(logger) << "Unable to match against property of unknown type.";
+ };
+ }
+ g_variant_unref(prop_value);
+ g_variant_unref(prop_dict);
+ }
+
+ return matches;
+}
+
+}
+}
diff --git a/unity-shared/XPathQueryPart.h b/unity-shared/XPathQueryPart.h
new file mode 100644
index 000000000..b5beb3f4f
--- /dev/null
+++ b/unity-shared/XPathQueryPart.h
@@ -0,0 +1,45 @@
+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
+/*
+ * Copyright (C) 2010 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Thomi Richards <thomi.richards@canonical.com>
+ */
+
+#include <string>
+
+#ifndef _XPATH_QUERY_PART
+#define _XPATH_QUERY_PART
+
+namespace unity
+{
+namespace debug
+{
+ class Introspectable;
+ // Stores a part of an XPath query.
+ class XPathQueryPart
+ {
+ public:
+ XPathQueryPart(std::string const& query_part);
+ bool Matches(Introspectable* node) const;
+ private:
+ std::string node_name_;
+ std::string param_name_;
+ std::string param_value_;
+ };
+
+}
+}
+
+#endif