summaryrefslogtreecommitdiff
path: root/unity-shared
diff options
authorTim Penhey <tim.penhey@canonical.com>2012-10-24 09:54:53 +0200
committerTim Penhey <tim.penhey@canonical.com>2012-10-24 09:54:53 +0200
commit6766a1fd2988191974edb42b51508c34035adf04 (patch)
tree35badac9cc1ca2839c97021397b0413c184cb971 /unity-shared
parent4abcf1c6a8ba87c8aa8bd31e4882580d3b16b329 (diff)
Use the range based for, and remove compiz header. Move the debug files into unity-shared.
(bzr r2865.2.8)
Diffstat (limited to 'unity-shared')
-rw-r--r--unity-shared/CMakeLists.txt2
-rw-r--r--unity-shared/DebugDBusInterface.cpp393
-rw-r--r--unity-shared/DebugDBusInterface.h67
-rw-r--r--unity-shared/XPathQueryPart.cpp189
-rw-r--r--unity-shared/XPathQueryPart.h45
5 files changed, 696 insertions, 0 deletions
diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt
index 28dcc84e8..576d89482 100644
--- a/unity-shared/CMakeLists.txt
+++ b/unity-shared/CMakeLists.txt
@@ -32,6 +32,7 @@ set (UNITY_SHARED_SOURCES
CoverArt.cpp
BackgroundEffectHelper.cpp
DashStyle.cpp
+ DebugDBusInterface.cpp
DefaultThumbnailProvider.cpp
FontSettings.cpp
IMTextEntry.cpp
@@ -64,6 +65,7 @@ set (UNITY_SHARED_SOURCES
UnityWindowView.cpp
UserThumbnailProvider.cpp
WindowManager.cpp
+ XPathQueryPart.cpp
)
if(UNITY_ENABLE_X_ORG_SUPPORT)
diff --git a/unity-shared/DebugDBusInterface.cpp b/unity-shared/DebugDBusInterface.cpp
new file mode 100644
index 000000000..f086eac4a
--- /dev/null
+++ b/unity-shared/DebugDBusInterface.cpp
@@ -0,0 +1,393 @@
+// -*- 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: Alex Launi <alex.launi@canonical.com>
+ */
+
+#include <queue>
+#include <fstream>
+#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 <NuxCore/LoggingWriter.h>
+
+#include "DebugDBusInterface.h"
+#include "Introspectable.h"
+#include "XPathQueryPart.h"
+
+namespace unity
+{
+const std::string DBUS_BUS_NAME = "com.canonical.Unity";
+
+namespace debug
+{
+namespace
+{
+nux::logging::Logger logger("unity.debug.DebugDBusInterface");
+
+namespace local
+{
+ std::ofstream output_file;
+}
+}
+
+GVariant* GetState(std::string const& query);
+void StartLogToFile(std::string const& file_path);
+void ResetLogging();
+void SetLogSeverity(std::string const& log_component,
+ std::string const& severity);
+void LogMessage(std::string const& severity,
+ std::string const& message);
+
+const char* DebugDBusInterface::DBUS_DEBUG_OBJECT_PATH = "/com/canonical/Unity/Debug";
+
+const gchar DebugDBusInterface::introspection_xml[] =
+ " <node>"
+ " <interface name='com.canonical.Autopilot.Introspection'>"
+ ""
+ " <method name='GetState'>"
+ " <arg type='s' name='piece' direction='in' />"
+ " <arg type='a(sv)' name='state' direction='out' />"
+ " </method>"
+ ""
+ " </interface>"
+ ""
+ " <interface name='com.canonical.Unity.Debug.Logging'>"
+ ""
+ " <method name='StartLogToFile'>"
+ " <arg type='s' name='file_path' direction='in' />"
+ " </method>"
+ ""
+ " <method name='ResetLogging'>"
+ " </method>"
+ ""
+ " <method name='SetLogSeverity'>"
+ " <arg type='s' name='log_component' direction='in' />"
+ " <arg type='s' name='severity' direction='in' />"
+ " </method>"
+ ""
+ " <method name='LogMessage'>"
+ " <arg type='s' name='severity' direction='in' />"
+ " <arg type='s' name='message' direction='in' />"
+ " </method>"
+ ""
+ " </interface>"
+ " </node>";
+
+GDBusInterfaceVTable DebugDBusInterface::interface_vtable =
+{
+ DebugDBusInterface::HandleDBusMethodCall,
+ NULL,
+ NULL
+};
+
+static Introspectable* _parent_introspectable;
+
+DebugDBusInterface::DebugDBusInterface(Introspectable* parent)
+{
+ _parent_introspectable = parent;
+ _owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
+ unity::DBUS_BUS_NAME.c_str(),
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ &DebugDBusInterface::OnBusAcquired,
+ &DebugDBusInterface::OnNameAcquired,
+ &DebugDBusInterface::OnNameLost,
+ this,
+ NULL);
+}
+
+DebugDBusInterface::~DebugDBusInterface()
+{
+ g_bus_unown_name(_owner_id);
+}
+
+void
+DebugDBusInterface::OnBusAcquired(GDBusConnection* connection, const gchar* name, gpointer data)
+{
+ int i = 0;
+ GError* error;
+
+ GDBusNodeInfo* introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
+ if (!introspection_data)
+ {
+ LOG_WARNING(logger) << "No dbus introspection data could be loaded. State introspection will not work";
+ return;
+ }
+
+ while (introspection_data->interfaces[i] != NULL)
+ {
+ error = NULL;
+ g_dbus_connection_register_object(connection,
+ DebugDBusInterface::DBUS_DEBUG_OBJECT_PATH,
+ introspection_data->interfaces[i],
+ &interface_vtable,
+ data,
+ NULL,
+ &error);
+ if (error != NULL)
+ {
+ g_warning("Could not register debug interface onto d-bus");
+ g_error_free(error);
+ }
+ i++;
+ }
+ g_dbus_node_info_unref(introspection_data);
+}
+
+void
+DebugDBusInterface::OnNameAcquired(GDBusConnection* connection, const gchar* name, gpointer data)
+{
+}
+
+void
+DebugDBusInterface::OnNameLost(GDBusConnection* connection, const gchar* name, gpointer data)
+{
+}
+
+void
+DebugDBusInterface::HandleDBusMethodCall(GDBusConnection* connection,
+ const gchar* sender,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* method_name,
+ GVariant* parameters,
+ GDBusMethodInvocation* invocation,
+ gpointer user_data)
+{
+ if (g_strcmp0(method_name, "GetState") == 0)
+ {
+ GVariant* ret;
+ const gchar* input;
+ g_variant_get(parameters, "(&s)", &input);
+
+ ret = GetState(input);
+ // GetState returns a floating variant and
+ // g_dbus_method_invocation_return_value ref sinks it
+ g_dbus_method_invocation_return_value(invocation, ret);
+ }
+ else if (g_strcmp0(method_name, "StartLogToFile") == 0)
+ {
+ const gchar* log_path;
+ g_variant_get(parameters, "(&s)", &log_path);
+
+ StartLogToFile(log_path);
+ g_dbus_method_invocation_return_value(invocation, NULL);
+ }
+ else if (g_strcmp0(method_name, "ResetLogging") == 0)
+ {
+ ResetLogging();
+ g_dbus_method_invocation_return_value(invocation, NULL);
+ }
+ else if (g_strcmp0(method_name, "SetLogSeverity") == 0)
+ {
+ const gchar* component;
+ const gchar* severity;
+ g_variant_get(parameters, "(&s&s)", &component, &severity);
+
+ SetLogSeverity(component, severity);
+ g_dbus_method_invocation_return_value(invocation, NULL);
+ }
+ else if (g_strcmp0(method_name, "LogMessage") == 0)
+ {
+ const gchar* severity;
+ const gchar* message;
+ g_variant_get(parameters, "(&s&s)", &severity, &message);
+
+ LogMessage(severity, message);
+ g_dbus_method_invocation_return_value(invocation, NULL);
+ }
+ else
+ {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ unity::DBUS_BUS_NAME.c_str(),
+ "Failed to find method");
+ }
+}
+
+
+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)"));
+ if (parts.empty())
+ {
+ LOG_WARNING(logger) << "Query '" << query << "' Did not match anything.";
+ }
+ for (Introspectable *node : parts)
+ {
+ g_variant_builder_add(&builder, "(sv)", node->GetName().c_str(), node->Introspect());
+ }
+
+ return g_variant_new("(a(sv))", &builder);
+}
+
+void StartLogToFile(std::string const& file_path)
+{
+ if (local::output_file.is_open())
+ local::output_file.close();
+ local::output_file.open(file_path);
+ nux::logging::Writer::Instance().SetOutputStream(local::output_file);
+}
+
+void ResetLogging()
+{
+ if (local::output_file.is_open())
+ local::output_file.close();
+ nux::logging::Writer::Instance().SetOutputStream(std::cout);
+ nux::logging::reset_logging();
+}
+
+void SetLogSeverity(std::string const& log_component,
+ std::string const& severity)
+{
+ nux::logging::Logger(log_component).SetLogLevel(nux::logging::get_logging_level(severity));
+}
+
+void LogMessage(std::string const& severity,
+ std::string const& message)
+{
+ nux::logging::Level level = nux::logging::get_logging_level(severity);
+ if (logger.GetEffectiveLogLevel() <= level)
+ {
+ nux::logging::LogStream(level, logger.module(), __FILE__, __LINE__).stream()
+ << message;
+ }
+}
+
+/*
+ * 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
new file mode 100644
index 000000000..3c11a5a0b
--- /dev/null
+++ b/unity-shared/DebugDBusInterface.h
@@ -0,0 +1,67 @@
+// -*- 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: Alex Launi <alex.launi@canonical.com>
+ */
+
+#include <gio/gio.h>
+
+#ifndef _DEBUG_DBUS_INTERFACE_H
+#define _DEBUG_DBUS_INTERFACE_H
+
+class CompScreen;
+
+namespace unity
+{
+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:
+ DebugDBusInterface(Introspectable* introspectable);
+ ~DebugDBusInterface();
+
+private:
+ /* methods */
+ static void OnBusAcquired(GDBusConnection* connection, const gchar* name, gpointer data);
+ static void OnNameAcquired(GDBusConnection* connection, const gchar* name, gpointer data);
+ static void OnNameLost(GDBusConnection* connection, const gchar* name, gpointer data);
+ static void HandleDBusMethodCall(GDBusConnection* connection,
+ const gchar* sender,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* method_name,
+ GVariant* parameters,
+ GDBusMethodInvocation* invocation,
+ gpointer user_data);
+ static const char* DBUS_DEBUG_OBJECT_PATH;
+ static const gchar introspection_xml[];
+ static GDBusInterfaceVTable interface_vtable;
+
+ static GVariant* BuildFakeReturn();
+
+ /* members */
+ guint _owner_id;
+};
+}
+}
+
+#endif /* _DEBUG_DBUS_INTERFACE_H */
diff --git a/unity-shared/XPathQueryPart.cpp b/unity-shared/XPathQueryPart.cpp
new file mode 100644
index 000000000..a42a65c34
--- /dev/null
+++ b/unity-shared/XPathQueryPart.cpp
@@ -0,0 +1,189 @@
+// -*- 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
+{
+namespace
+{
+ nux::logging::Logger logger("unity.debug.DebugDBusInterface");
+}
+
+// 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