ros_pluginlib_plugin_provider.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011, Dirk Thomas, TU Darmstadt
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following
13  * disclaimer in the documentation and/or other materials provided
14  * with the distribution.
15  * * Neither the name of the TU Darmstadt nor the names of its
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #ifndef qt_gui_cpp__RosPluginlibPluginProvider_H
34 #define qt_gui_cpp__RosPluginlibPluginProvider_H
35 
36 #include "plugin.h"
37 #include "plugin_context.h"
38 #include "plugin_descriptor.h"
39 #include "plugin_provider.h"
40 
41 // while this header uses boost shared pointers
42 // Shiboken2 isn't able to parse it correctly atm
43 //#include <boost/shared_ptr.hpp>
44 
46 #include <tinyxml.h>
47 
48 #include <QCoreApplication>
49 #include <QEvent>
50 #include <QList>
51 #include <QMap>
52 #include <QObject>
53 #include <QString>
54 
55 #include <fstream>
56 #include <string>
57 #include <vector>
58 
59 namespace qt_gui_cpp
60 {
61 
62 template<typename T>
64  : public QObject
65  , public PluginProvider
66 {
67 
68 public:
69 
70  static RosPluginlibPluginProvider<T>* create_instance(const QString& export_tag, const QString& base_class_type)
71  {
72  return new RosPluginlibPluginProvider<T>(export_tag, base_class_type);
73  }
74 
75  RosPluginlibPluginProvider(const QString& export_tag, const QString& base_class_type)
76  : QObject()
77  , PluginProvider()
78  , export_tag_(export_tag)
79  , base_class_type_(base_class_type)
80  , class_loader_(0)
81  {
82  unload_libraries_event_ = QEvent::registerEventType();
83  }
84 
86  {
87  if (class_loader_)
88  {
89  delete class_loader_;
90  }
91  }
92 
93  virtual QMap<QString, QString> discover(QObject* discovery_data)
94  {
95  return PluginProvider::discover(discovery_data);
96  }
97 
98  virtual QList<PluginDescriptor*> discover_descriptors(QObject* discovery_data)
99  {
100  if (class_loader_)
101  {
102  delete class_loader_;
103  }
104 
105  Settings discovery_settings(discovery_data);
106  QString key = "qt_gui_cpp.RosPluginlibPluginProvider/" + export_tag_ + " " + base_class_type_;
107  bool is_cached = discovery_settings.contains(key);
108 
109  std::vector<std::string> plugin_xml_paths;
110  // reuse plugin paths from cache if available
111  if (is_cached)
112  {
113  QStringList paths = discovery_settings.value(key).toStringList();
114  for (QStringList::const_iterator it = paths.begin(); it != paths.end(); it++)
115  {
116  plugin_xml_paths.push_back(it->toStdString());
117  }
118  }
119  else
120  {
121  qDebug("RosPluginlibPluginProvider::discover_descriptors() crawling for plugins of type '%s' and base class '%s'", export_tag_.toStdString().c_str(), base_class_type_.toStdString().c_str());
122  }
123  class_loader_ = new pluginlib::ClassLoader<T>(export_tag_.toStdString(), base_class_type_.toStdString(), std::string("plugin"), plugin_xml_paths);
124 
125  if (!is_cached)
126  {
127  // save discovered paths
128  std::vector<std::string> paths = class_loader_->getPluginXmlPaths();
129  QStringList qpaths;
130  for (std::vector<std::string>::const_iterator it = paths.begin(); it != paths.end(); it++)
131  {
132  qpaths.push_back(it->c_str());
133  }
134  discovery_settings.setValue(key, qpaths);
135  }
136 
137  QList<PluginDescriptor*> descriptors;
138 
139  std::vector<std::string> classes = class_loader_->getDeclaredClasses();
140  for (std::vector<std::string>::iterator it = classes.begin(); it != classes.end(); it++)
141  {
142  std::string lookup_name = *it;
143 
144  std::string name = class_loader_->getName(lookup_name);
145  std::string plugin_xml = class_loader_->getPluginManifestPath(lookup_name);
146  boost::filesystem::path p(plugin_xml);
147 #if BOOST_FILESYSTEM_VERSION >= 3
148  std::string plugin_path = p.parent_path().string();
149 #else
150  std::string plugin_path = p.parent_path();
151 #endif
152 
153  QMap<QString, QString> attributes;
154  attributes["class_name"] = name.c_str();
155  attributes["class_type"] = class_loader_->getClassType(lookup_name).c_str();
156  attributes["class_base_class_type"] = class_loader_->getBaseClassType().c_str();
157  attributes["package_name"] = class_loader_->getClassPackage(lookup_name).c_str();
158  attributes["plugin_path"] = plugin_path.c_str();
159 
160  // check if plugin is available
161  //std::string library_path = class_loader_->getClassLibraryPath(lookup_name);
162  //attributes["not_available"] = !std::ifstream(library_path.c_str()) ? QString("library ").append(lookup_name.c_str()).append(" not found (may be it must be built?)") : "";
163  attributes["not_available"] = "";
164 
165  PluginDescriptor* plugin_descriptor = new PluginDescriptor(lookup_name.c_str(), attributes);
166  QString label = name.c_str();
167  QString statustip = class_loader_->getClassDescription(lookup_name).c_str();
168  QString icon;
169  QString icontype;
170  parseManifest(lookup_name, plugin_path, label, statustip, icon, icontype, plugin_descriptor);
171  plugin_descriptor->setActionAttributes(label, statustip, icon, icontype);
172 
173  // add plugin descriptor
174  descriptors.append(plugin_descriptor);
175  }
176  return descriptors;
177  }
178 
179  virtual void* load(const QString& plugin_id, PluginContext* plugin_context)
180  {
181  return load_explicit_type(plugin_id, plugin_context);
182  }
183 
184  virtual Plugin* load_plugin(const QString& plugin_id, PluginContext* plugin_context)
185  {
186  T* instance = load_explicit_type(plugin_id, plugin_context);
187  if (instance == 0)
188  {
189  return 0;
190  }
191  Plugin* plugin = dynamic_cast<Plugin*>(instance);
192  if (plugin == 0)
193  {
194  // TODO: garbage instance
195  qWarning("RosPluginlibPluginProvider::load_plugin() called on non-plugin plugin provider");
196  return 0;
197  }
198  return plugin;
199  }
200 
201  virtual T* load_explicit_type(const QString& plugin_id, PluginContext* plugin_context)
202  {
203  std::string lookup_name = plugin_id.toStdString();
204 
205  if (!class_loader_->isClassAvailable(lookup_name))
206  {
207  qWarning("RosPluginlibPluginProvider::load_explicit_type(%s) class not available", lookup_name.c_str());
208  return 0;
209  }
210 
211  boost::shared_ptr<T> instance;
212  try
213  {
214  instance = create_plugin(lookup_name, plugin_context);
215  }
217  {
218  qWarning("RosPluginlibPluginProvider::load_explicit_type(%s) could not load library (%s)", lookup_name.c_str(), e.what());
219  return 0;
220  }
222  {
223  qWarning("RosPluginlibPluginProvider::load_explicit_type(%s) failed creating instance (%s)", lookup_name.c_str(), e.what());
224  return 0;
225  }
226 
227  if (!instance)
228  {
229  qWarning("RosPluginlibPluginProvider::load_explicit_type(%s) failed creating instance", lookup_name.c_str());
230  return 0;
231  }
232 
233  // pass context to plugin
234  Plugin* plugin = dynamic_cast<Plugin*>(&*instance);
235  if (plugin)
236  {
237  try
238  {
239  init_plugin(plugin_id, plugin_context, plugin);
240  }
241  catch (std::exception& e)
242  {
243  // TODO: garbage instance
244  qWarning("RosPluginlibPluginProvider::load_explicit_type(%s) failed initializing plugin (%s)", lookup_name.c_str(), e.what());
245  return 0;
246  }
247  }
248 
249  //qDebug("RosPluginlibPluginProvider::load_explicit_type(%s) succeeded", lookup_name.c_str());
250  instances_[&*instance] = instance;
251 
252  return &*instance;
253  }
254 
255  virtual void unload(void* instance)
256  {
257  if (!instances_.contains(instance))
258  {
259  qCritical("RosPluginlibPluginProvider::unload() instance not found");
260  return;
261  }
262 
263  boost::shared_ptr<T> pointer = instances_.take(instance);
264  libraries_to_unload_.append(pointer);
265 
266  QCoreApplication::postEvent(this, new QEvent(static_cast<QEvent::Type>(unload_libraries_event_)));
267  }
268 
269  bool event(QEvent* e)
270  {
271  if (e->type() == unload_libraries_event_)
272  {
273  libraries_to_unload_.clear();
274  return true;
275  }
276  return QObject::event(e);
277  }
278 
279 protected:
280 
281  virtual boost::shared_ptr<T> create_plugin(const std::string& lookup_name, PluginContext* /*plugin_context*/ = 0)
282  {
283  return class_loader_->createInstance(lookup_name);
284  }
285 
286  virtual void init_plugin(const QString& /*plugin_id*/, PluginContext* plugin_context, Plugin* plugin)
287  {
288  plugin->initPlugin(*plugin_context);
289  }
290 
291 private:
292 
293  bool parseManifest(const std::string& lookup_name, const std::string& plugin_path, QString& label, QString& statustip, QString& icon, QString& icontype, PluginDescriptor* plugin_descriptor)
294  {
295  //qDebug("RosPluginlibPluginProvider::parseManifest()");
296 
297  std::string manifest_path = class_loader_->getPluginManifestPath(lookup_name);
298  //qDebug("RosPluginlibPluginProvider::parseManifest() manifest_path \"%s\"", manifest_path.c_str());
299  TiXmlDocument doc;
300  bool loaded = doc.LoadFile(manifest_path);
301  if (!loaded)
302  {
303  if (doc.ErrorRow() > 0)
304  {
305  qWarning("RosPluginlibPluginProvider::parseManifest() could not load manifest \"%s\" (%s [line %d, column %d])", manifest_path.c_str(), doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol());
306  }
307  else
308  {
309  qWarning("RosPluginlibPluginProvider::parseManifest() could not load manifest \"%s\" (%s)", manifest_path.c_str(), doc.ErrorDesc());
310  }
311  return false;
312  }
313 
314  // search library-tag with specific path-attribute
315  std::string class_type = class_loader_->getClassType(lookup_name);
316  TiXmlElement* library_element = doc.FirstChildElement("library");
317  while (library_element)
318  {
319  // search class-tag with specific type- and base_class_type-attribute
320  TiXmlElement* class_element = library_element->FirstChildElement("class");
321  while (class_element)
322  {
323  if (class_type.compare(class_element->Attribute("type")) == 0 && base_class_type_.compare(class_element->Attribute("base_class_type")) == 0)
324  {
325  TiXmlElement* qtgui_element = class_element->FirstChildElement("qtgui");
326  if (qtgui_element)
327  {
328  // extract meta information
329  parseActionAttributes(qtgui_element, plugin_path, label, statustip, icon, icontype);
330 
331  // extract grouping information
332  TiXmlElement* group_element = qtgui_element->FirstChildElement("group");
333  while (group_element)
334  {
335  QString group_label;
336  QString group_statustip;
337  QString group_icon;
338  QString group_icontype;
339  parseActionAttributes(group_element, plugin_path, group_label, group_statustip, group_icon, group_icontype);
340  plugin_descriptor->addGroupAttributes(group_label, group_statustip, group_icon, group_icontype);
341 
342  group_element = group_element->NextSiblingElement("group");
343  }
344  }
345  return true;
346  }
347  class_element = class_element->NextSiblingElement("class");
348  }
349  break;
350 
351  library_element = library_element->NextSiblingElement("library");
352  }
353 
354  qWarning("RosPluginlibPluginProvider::parseManifest() could not handle manifest \"%s\"", manifest_path.c_str());
355  return false;
356  }
357 
358  void parseActionAttributes(TiXmlElement* element, const std::string& plugin_path, QString& label, QString& statustip, QString& icon, QString& icontype)
359  {
360  TiXmlElement* child_element;
361  if ((child_element = element->FirstChildElement("label")) != 0)
362  {
363  label = child_element->GetText();
364  }
365  if ((child_element = element->FirstChildElement("icon")) != 0)
366  {
367  icontype = child_element->Attribute("type");
368  if (icontype == "file")
369  {
370  // prepend base path
371  icon = plugin_path.c_str();
372  icon += "/";
373  icon += child_element->GetText();
374  }
375  else
376  {
377  icon = child_element->GetText();
378  }
379  }
380  if ((child_element = element->FirstChildElement("statustip")) != 0)
381  {
382  statustip = child_element->GetText();
383  }
384  }
385 
387  {
388  }
389 
390  QString export_tag_;
391 
393 
395 
397 
398  QMap<void*, boost::shared_ptr<T> > instances_;
399 
400  QList<boost::shared_ptr<T> > libraries_to_unload_;
401 
402 };
403 
404 } // namespace
405 
406 #endif // qt_gui_cpp__RosPluginlibPluginProvider_H
virtual void initPlugin(PluginContext &)
Definition: plugin.h:69
virtual void * load(const QString &plugin_id, PluginContext *plugin_context)
virtual T * load_explicit_type(const QString &plugin_id, PluginContext *plugin_context)
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: settings.cpp:95
virtual QMap< QString, QString > discover(QObject *discovery_data)
virtual boost::shared_ptr< T > create_plugin(const std::string &lookup_name, PluginContext *=0)
virtual Plugin * load_plugin(const QString &plugin_id, PluginContext *plugin_context)
virtual void init_plugin(const QString &, PluginContext *plugin_context, Plugin *plugin)
static RosPluginlibPluginProvider< T > * create_instance(const QString &export_tag, const QString &base_class_type)
bool contains(const QString &key) const
Definition: settings.cpp:75
QList< boost::shared_ptr< T > > libraries_to_unload_
bool parseManifest(const std::string &lookup_name, const std::string &plugin_path, QString &label, QString &statustip, QString &icon, QString &icontype, PluginDescriptor *plugin_descriptor)
void setValue(const QString &key, const QVariant &value)
Definition: settings.cpp:89
virtual QMap< QString, QString > discover(QObject *discovery_data)
void parseActionAttributes(TiXmlElement *element, const std::string &plugin_path, QString &label, QString &statustip, QString &icon, QString &icontype)
QMap< void *, boost::shared_ptr< T > > instances_
std::vector< std::string > getPluginXmlPaths()
virtual QList< PluginDescriptor * > discover_descriptors(QObject *discovery_data)
void addGroupAttributes(const QString &label, const QString &statustip=QString(), const QString &icon=QString(), const QString &icontype=QString())
RosPluginlibPluginProvider(const QString &export_tag, const QString &base_class_type)
void setActionAttributes(const QString &label, const QString &statustip=QString(), const QString &icon=QString(), const QString &icontype=QString())


qt_gui_cpp
Author(s): Dirk Thomas
autogenerated on Tue Apr 13 2021 03:03:14