diff options
| author | Brandon Schaefer <brandon.schaefer@canonical.com> | 2013-02-01 12:30:27 -0800 |
|---|---|---|
| committer | Brandon Schaefer <brandon.schaefer@canonical.com> | 2013-02-01 12:30:27 -0800 |
| commit | a74a32ec8b7e987e5c7c09b92553a9b436fb7738 (patch) | |
| tree | 52e20c44d1927a049485ab69a73fcbcd0dc4da4d /unity-shared | |
| parent | 94b610ee744f4791da0cb2bfa5a8b655db0c6a87 (diff) | |
* Cherry pick xpathselect changes, causing problems with AP tests
(bzr r3099.1.1)
Diffstat (limited to 'unity-shared')
| -rw-r--r-- | unity-shared/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | unity-shared/DebugDBusInterface.cpp | 295 | ||||
| -rw-r--r-- | unity-shared/DebugDBusInterface.h | 2 | ||||
| -rw-r--r-- | unity-shared/XPathQueryPart.cpp | 186 | ||||
| -rw-r--r-- | unity-shared/XPathQueryPart.h | 45 |
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 |
