From e1c7dd6d09fc1f1c85c10567de4d1cddeb1a2c56 Mon Sep 17 00:00:00 2001 From: Thomi Richards Date: Thu, 24 Jan 2013 17:53:37 +1300 Subject: Load XPath implementation from libxpathselect, if it is present. (bzr r3057.1.3) --- unity-shared/CMakeLists.txt | 1 - unity-shared/DebugDBusInterface.cpp | 295 +++++++++++++++++++++--------------- unity-shared/DebugDBusInterface.h | 2 - unity-shared/XPathQueryPart.cpp | 186 ----------------------- unity-shared/XPathQueryPart.h | 45 ------ 5 files changed, 170 insertions(+), 359 deletions(-) delete mode 100644 unity-shared/XPathQueryPart.cpp delete mode 100644 unity-shared/XPathQueryPart.h (limited to 'unity-shared') diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt index 261a7fac7..5e5090390 100644 --- a/unity-shared/CMakeLists.txt +++ b/unity-shared/CMakeLists.txt @@ -62,7 +62,6 @@ set (UNITY_SHARED_SOURCES UserThumbnailProvider.cpp VScrollBarOverlayWindow.cpp WindowManager.cpp - XPathQueryPart.cpp ) if(ENABLE_X_SUPPORT) diff --git a/unity-shared/DebugDBusInterface.cpp b/unity-shared/DebugDBusInterface.cpp index 7aedca53a..c95534297 100644 --- a/unity-shared/DebugDBusInterface.cpp +++ b/unity-shared/DebugDBusInterface.cpp @@ -28,10 +28,12 @@ #include #include #include +#include +#include +#include #include "DebugDBusInterface.h" #include "Introspectable.h" -#include "XPathQueryPart.h" namespace unity { @@ -45,9 +47,149 @@ namespace namespace local { std::ofstream output_file; + + class IntrospectableAdapter: public xpathselect::Node + { + public: + typedef std::shared_ptr 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 Children() const + { + std::vector children; + for(auto child: node_->GetIntrospectableChildren()) + { + children.push_back(std::make_shared(child)); + } + return children; + + } + + Introspectable* node_; + }; } } +bool TryLoadXPathImplementation(); GVariant* GetState(std::string const& query); void StartLogToFile(std::string const& file_path); void ResetLogging(); @@ -221,20 +363,40 @@ DebugDBusInterface::HandleDBusMethodCall(GDBusConnection* connection, } } - GVariant* GetState(std::string const& query) { - // process the XPath query: - std::list parts = GetIntrospectableNodesFromQuery(query, _parent_introspectable); GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE("a(sv)")); - if (parts.empty()) + + // try load the xpathselect library: + void* driver = dlopen("libxpathselect.so", RTLD_LAZY); + if (driver) { - LOG_WARNING(logger) << "Query '" << query << "' Did not match anything."; + 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(_parent_introspectable); + auto nodes = entry_point(root_node, query); + for (auto n : nodes) + { + auto p = std::static_pointer_cast(n); + if (p) + g_variant_builder_add(&builder, "(sv)", p->node_->GetName().c_str(), p->node_->Introspect()); + } + } } - for (Introspectable *node : parts) + else { - g_variant_builder_add(&builder, "(sv)", node->GetName().c_str(), node->Introspect()); + LOG_WARNING(logger) << "Cannot complete introspection request because libxpathselect is not installed."; } return g_variant_new("(a(sv))", &builder); @@ -274,122 +436,5 @@ 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 GetIntrospectableNodesFromQuery(std::string const& query, Introspectable* tree_root) -{ - std::list 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 query_parts; - - { - std::list 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 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::iterator> node_match_pair; - - std::queue 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 3c11a5a0b..95c9c8098 100644 --- a/unity-shared/DebugDBusInterface.h +++ b/unity-shared/DebugDBusInterface.h @@ -31,8 +31,6 @@ extern const std::string DBUS_BUS_NAME; namespace debug { class Introspectable; -std::list GetIntrospectableNodesFromQuery(std::string const& query, Introspectable *tree_root); - class DebugDBusInterface { public: diff --git a/unity-shared/XPathQueryPart.cpp b/unity-shared/XPathQueryPart.cpp deleted file mode 100644 index 3608a50b4..000000000 --- a/unity-shared/XPathQueryPart.cpp +++ /dev/null @@ -1,186 +0,0 @@ -// -*- 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 . - * - * Authored by: Thomi Richards - */ - -#include -#include -#include -#include -#include -#include -#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 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 deleted file mode 100644 index b5beb3f4f..000000000 --- a/unity-shared/XPathQueryPart.h +++ /dev/null @@ -1,45 +0,0 @@ -// -*- 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 . - * - * Authored by: Thomi Richards - */ - -#include - -#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 -- cgit v1.2.3