dRonin  adbada4
dRonin GCS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
pluginspec.cpp
Go to the documentation of this file.
1 
13 /*
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful, but
20  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  * for more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with this program; if not, see <http://www.gnu.org/licenses/>
26  */
27 
28 #include "pluginspec.h"
29 #include "pluginspec.h"
30 #include "pluginspec_p.h"
31 #include "iplugin.h"
32 #include "iplugin_p.h"
33 #include "pluginmanager.h"
34 
35 #include <QtCore/QFile>
36 #include <QtCore/QFileInfo>
37 #include <QtCore/QXmlStreamReader>
38 #include <QtCore/QRegExp>
39 #include <QtCore/QCoreApplication>
40 #include <QtDebug>
41 
42 #ifdef Q_OS_LINUX
43 // Using the patched version breaks on Fedora 10, KDE4.2.2/Qt4.5.
44 # define USE_UNPATCHED_QPLUGINLOADER 1
45 #else
46 # define USE_UNPATCHED_QPLUGINLOADER 1
47 #endif
48 
49 #if USE_UNPATCHED_QPLUGINLOADER
50 
51 # include <QtCore/QPluginLoader>
52  typedef QT_PREPEND_NAMESPACE(QPluginLoader) PluginLoader;
53 
54 #else
55 
56 # include "patchedpluginloader.cpp"
57  typedef PatchedPluginLoader PluginLoader;
58 
59 #endif
60 
123 using namespace ExtensionSystem;
124 using namespace ExtensionSystem::Internal;
125 
131 {
132  return name == other.name && version == other.version;
133 }
134 
139 PluginSpec::PluginSpec()
140  : d(new PluginSpecPrivate(this))
141 {
142 }
143 
149 {
150  delete d;
151  d = nullptr;
152 }
153 
158 QString PluginSpec::name() const
159 {
160  return d->name;
161 }
162 
167 QString PluginSpec::version() const
168 {
169  return d->version;
170 }
171 
177 {
178  return d->compatVersion;
179 }
180 
185 QString PluginSpec::vendor() const
186 {
187  return d->vendor;
188 }
189 
194 QString PluginSpec::copyright() const
195 {
196  return d->copyright;
197 }
198 
203 QString PluginSpec::license() const
204 {
205  return d->license;
206 }
207 
212 QString PluginSpec::description() const
213 {
214  return d->description;
215 }
216 
221 QString PluginSpec::url() const
222 {
223  return d->url;
224 }
225 
231 {
232  return d->dependencies;
233 }
234 
241 {
242  return d->argumentDescriptions;
243 }
244 
250 QString PluginSpec::location() const
251 {
252  return d->location;
253 }
254 
260 QString PluginSpec::filePath() const
261 {
262  return d->filePath;
263 }
264 
270 QStringList PluginSpec::arguments() const
271 {
272  return d->arguments;
273 }
274 
280 void PluginSpec::setArguments(const QStringList &arguments)
281 {
282  d->arguments = arguments;
283 }
284 
290 void PluginSpec::addArgument(const QString &argument)
291 {
292  d->arguments.push_back(argument);
293 }
294 
295 
302 {
303  return d->state;
304 }
305 
311 {
312  return d->hasError;
313 }
314 
319 QString PluginSpec::errorString() const
320 {
321  return d->errorString;
322 }
323 
331 bool PluginSpec::provides(const QString &pluginName, const QString &version) const
332 {
333  return d->provides(pluginName, version);
334 }
335 
342 {
343  return d->plugin;
344 }
345 
354 {
355  return d->dependencySpecs;
356 }
357 
358 //==========PluginSpecPrivate==================
359 
360 namespace {
361  const char * const PLUGIN = "plugin";
362  const char * const PLUGIN_NAME = "name";
363  const char * const PLUGIN_VERSION = "version";
364  const char * const PLUGIN_COMPATVERSION = "compatVersion";
365  const char * const VENDOR = "vendor";
366  const char * const COPYRIGHT = "copyright";
367  const char * const LICENSE = "license";
368  const char * const DESCRIPTION = "description";
369  const char * const URL = "url";
370  const char * const DEPENDENCYLIST = "dependencyList";
371  const char * const DEPENDENCY = "dependency";
372  const char * const DEPENDENCY_NAME = "name";
373  const char * const DEPENDENCY_VERSION = "version";
374  const char * const ARGUMENTLIST = "argumentList";
375  const char * const ARGUMENT = "argument";
376  const char * const ARGUMENT_NAME = "name";
377  const char * const ARGUMENT_PARAMETER = "parameter";
378 }
383 PluginSpecPrivate::PluginSpecPrivate(PluginSpec *spec)
384  : plugin(nullptr),
385  state(PluginSpec::Invalid),
386  hasError(false),
387  q(spec)
388 {
389 }
390 
395 bool PluginSpecPrivate::read(const QString &fileName)
396 {
397  name
398  = version
399  = compatVersion
400  = vendor
401  = copyright
402  = license
403  = description
404  = url
405  = location
406  = "";
408  hasError = false;
409  errorString = "";
410  dependencies.clear();
411  QFile file(fileName);
412  if (!file.exists())
413  return reportError(tr("File does not exist: %1").arg(file.fileName()));
414  if (!file.open(QIODevice::ReadOnly))
415  return reportError(tr("Could not open file for read: %1").arg(file.fileName()));
416  QFileInfo fileInfo(file);
417  location = fileInfo.absolutePath();
418  filePath = fileInfo.absoluteFilePath();
419  QXmlStreamReader reader(&file);
420  while (!reader.atEnd()) {
421  reader.readNext();
422  switch (reader.tokenType()) {
423  case QXmlStreamReader::StartElement:
424  readPluginSpec(reader);
425  break;
426  default:
427  break;
428  }
429  }
430  if (reader.hasError())
431  return reportError(tr("Error parsing file %1: %2, at line %3, column %4")
432  .arg(file.fileName())
433  .arg(reader.errorString())
434  .arg(reader.lineNumber())
435  .arg(reader.columnNumber()));
437  return true;
438 }
439 
444 bool PluginSpecPrivate::reportError(const QString &err)
445 {
446  errorString = err;
447  hasError = true;
448  return false;
449 }
450 
451 static inline QString msgAttributeMissing(const char *elt, const char *attribute)
452 {
453  return QCoreApplication::translate("PluginSpec", "'%1' misses attribute '%2'").arg(QLatin1String(elt), QLatin1String(attribute));
454 }
455 
456 static inline QString msgInvalidFormat(const char *content)
457 {
458  return QCoreApplication::translate("PluginSpec", "'%1' has invalid format").arg(content);
459 }
460 
461 static inline QString msgInvalidElement(const QString &name)
462 {
463  return QCoreApplication::translate("PluginSpec", "Invalid element '%1'").arg(name);
464 }
465 
466 static inline QString msgUnexpectedClosing(const QString &name)
467 {
468  return QCoreApplication::translate("PluginSpec", "Unexpected closing element '%1'").arg(name);
469 }
470 
471 static inline QString msgUnexpectedToken()
472 {
473  return QCoreApplication::translate("PluginSpec", "Unexpected token");
474 }
475 
480 void PluginSpecPrivate::readPluginSpec(QXmlStreamReader &reader)
481 {
482  QString element = reader.name().toString();
483  if (element != QString(PLUGIN)) {
484  reader.raiseError(QCoreApplication::translate("PluginSpec", "Expected element '%1' as top level element").arg(PLUGIN));
485  return;
486  }
487  name = reader.attributes().value(PLUGIN_NAME).toString();
488  if (name.isEmpty()) {
489  reader.raiseError(msgAttributeMissing(PLUGIN, PLUGIN_NAME));
490  return;
491  }
492  version = reader.attributes().value(PLUGIN_VERSION).toString();
493  if (version.isEmpty()) {
494  reader.raiseError(msgAttributeMissing(PLUGIN, PLUGIN_VERSION));
495  return;
496  }
497  if (!isValidVersion(version)) {
498  reader.raiseError(msgInvalidFormat(PLUGIN_VERSION));
499  return;
500  }
501  compatVersion = reader.attributes().value(PLUGIN_COMPATVERSION).toString();
502  if (!compatVersion.isEmpty() && !isValidVersion(compatVersion)) {
503  reader.raiseError(msgInvalidFormat(PLUGIN_COMPATVERSION));
504  return;
505  } else if (compatVersion.isEmpty()) {
507  }
508  while (!reader.atEnd()) {
509  reader.readNext();
510  switch (reader.tokenType()) {
511  case QXmlStreamReader::StartElement:
512  element = reader.name().toString();
513  if (element == VENDOR)
514  vendor = reader.readElementText().trimmed();
515  else if (element == COPYRIGHT)
516  copyright = reader.readElementText().trimmed();
517  else if (element == LICENSE)
518  license = reader.readElementText().trimmed();
519  else if (element == DESCRIPTION)
520  description = reader.readElementText().trimmed();
521  else if (element == URL)
522  url = reader.readElementText().trimmed();
523  else if (element == DEPENDENCYLIST)
524  readDependencies(reader);
525  else if (element == ARGUMENTLIST)
526  readArgumentDescriptions(reader);
527  else
528  reader.raiseError(msgInvalidElement(name));
529  break;
530  case QXmlStreamReader::EndDocument:
531  case QXmlStreamReader::Comment:
532  case QXmlStreamReader::EndElement:
533  case QXmlStreamReader::Characters:
534  break;
535  default:
536  reader.raiseError(msgUnexpectedToken());
537  break;
538  }
539  }
540 }
541 
547 void PluginSpecPrivate::readArgumentDescriptions(QXmlStreamReader &reader)
548 {
549  QString element;
550  while (!reader.atEnd()) {
551  reader.readNext();
552  switch (reader.tokenType()) {
553  case QXmlStreamReader::StartElement:
554  element = reader.name().toString();
555  if (element == ARGUMENT) {
556  readArgumentDescription(reader);
557  } else {
558  reader.raiseError(msgInvalidElement(name));
559  }
560  break;
561  case QXmlStreamReader::Comment:
562  case QXmlStreamReader::Characters:
563  break;
564  case QXmlStreamReader::EndElement:
565  element = reader.name().toString();
566  if (element == ARGUMENTLIST)
567  return;
568  reader.raiseError(msgUnexpectedClosing(element));
569  break;
570  default:
571  reader.raiseError(msgUnexpectedToken());
572  break;
573  }
574  }
575 }
576 
581 void PluginSpecPrivate::readArgumentDescription(QXmlStreamReader &reader)
582 {
584  arg.name = reader.attributes().value(ARGUMENT_NAME).toString();
585  if (arg.name.isEmpty()) {
586  reader.raiseError(msgAttributeMissing(ARGUMENT, ARGUMENT_NAME));
587  return;
588  }
589  arg.parameter = reader.attributes().value(ARGUMENT_PARAMETER).toString();
590  arg.description = reader.readElementText();
591  if (reader.tokenType() != QXmlStreamReader::EndElement)
592  reader.raiseError(msgUnexpectedToken());
593  argumentDescriptions.push_back(arg);
594 }
595 
600 void PluginSpecPrivate::readDependencies(QXmlStreamReader &reader)
601 {
602  QString element;
603  while (!reader.atEnd()) {
604  reader.readNext();
605  switch (reader.tokenType()) {
606  case QXmlStreamReader::StartElement:
607  element = reader.name().toString();
608  if (element == DEPENDENCY) {
609  readDependencyEntry(reader);
610  } else {
611  reader.raiseError(msgInvalidElement(name));
612  }
613  break;
614  case QXmlStreamReader::Comment:
615  case QXmlStreamReader::Characters:
616  break;
617  case QXmlStreamReader::EndElement:
618  element = reader.name().toString();
619  if (element == DEPENDENCYLIST)
620  return;
621  reader.raiseError(msgUnexpectedClosing(element));
622  break;
623  default:
624  reader.raiseError(msgUnexpectedToken());
625  break;
626  }
627  }
628 }
629 
634 void PluginSpecPrivate::readDependencyEntry(QXmlStreamReader &reader)
635 {
636  PluginDependency dep;
637  dep.name = reader.attributes().value(DEPENDENCY_NAME).toString();
638  if (dep.name.isEmpty()) {
639  reader.raiseError(msgAttributeMissing(DEPENDENCY, DEPENDENCY_NAME));
640  return;
641  }
642  dep.version = reader.attributes().value(DEPENDENCY_VERSION).toString();
643  if (!dep.version.isEmpty() && !isValidVersion(dep.version)) {
644  reader.raiseError(msgInvalidFormat(DEPENDENCY_VERSION));
645  return;
646  }
647  dependencies.append(dep);
648  reader.readNext();
649  if (reader.tokenType() != QXmlStreamReader::EndElement)
650  reader.raiseError(msgUnexpectedToken());
651 }
652 
657 bool PluginSpecPrivate::provides(const QString &pluginName, const QString &pluginVersion) const
658 {
659  if (QString::compare(pluginName, name, Qt::CaseInsensitive) != 0)
660  return false;
661  return (versionCompare(version, pluginVersion) >= 0) && (versionCompare(compatVersion, pluginVersion) <= 0);
662 }
663 
668 QRegExp &PluginSpecPrivate::versionRegExp()
669 {
670  static QRegExp reg("([0-9]+)(?:[.]([0-9]+))?(?:[.]([0-9]+))?(?:_([0-9]+))?");
671  return reg;
672 }
677 bool PluginSpecPrivate::isValidVersion(const QString &version)
678 {
679  return versionRegExp().exactMatch(version);
680 }
681 
686 int PluginSpecPrivate::versionCompare(const QString &version1, const QString &version2)
687 {
688  QRegExp reg1 = versionRegExp();
689  QRegExp reg2 = versionRegExp();
690  if (!reg1.exactMatch(version1))
691  return 0;
692  if (!reg2.exactMatch(version2))
693  return 0;
694  int number1;
695  int number2;
696  for (int i = 0; i < 4; ++i) {
697  number1 = reg1.cap(i+1).toInt();
698  number2 = reg2.cap(i+1).toInt();
699  if (number1 < number2)
700  return -1;
701  if (number1 > number2)
702  return 1;
703  }
704  return 0;
705 }
706 
712 {
713  if (hasError)
714  return false;
716  state = PluginSpec::Read; // Go back, so we just re-resolve the dependencies.
717  if (state != PluginSpec::Read) {
718  errorString = QCoreApplication::translate("PluginSpec", "Resolving dependencies failed because state != Read");
719  hasError = true;
720  return false;
721  }
722  QList<PluginSpec *> resolvedDependencies;
723  foreach (const PluginDependency &dependency, dependencies) {
724  PluginSpec *found = nullptr;
725  foreach (PluginSpec *spec, specs) {
726  if (spec->provides(dependency.name, dependency.version)) {
727  found = spec;
728  break;
729  }
730  }
731  if (!found) {
732  hasError = true;
733  if (!errorString.isEmpty())
734  errorString.append("\n");
735  errorString.append(QCoreApplication::translate("PluginSpec", "Could not resolve dependency '%1(%2)'")
736  .arg(dependency.name).arg(dependency.version));
737  continue;
738  }
739  resolvedDependencies.append(found);
740  }
741  if (hasError)
742  return false;
743  dependencySpecs = resolvedDependencies;
745  return true;
746 }
747 
753 {
754  if (hasError)
755  return false;
756  if (state != PluginSpec::Resolved) {
757  if (state == PluginSpec::Loaded)
758  return true;
759  errorString = QCoreApplication::translate("PluginSpec", "Loading the library failed because state != Resolved");
760  hasError = true;
761  return false;
762  }
763 #ifdef QT_NO_DEBUG
764 
765 #ifdef Q_OS_WIN
766  QString libName = QString("%1/%2.dll").arg(location).arg(name);
767 #elif defined(Q_OS_MAC)
768  QString libName = QString("%1/lib%2.dylib").arg(location).arg(name);
769 #else
770  QString libName = QString("%1/lib%2.so").arg(location).arg(name);
771 #endif
772 
773 #else //Q_NO_DEBUG
774 
775 #ifdef Q_OS_WIN
776  QString libName = QString("%1/%2d.dll").arg(location).arg(name);
777 #elif defined(Q_OS_MAC)
778  QString libName = QString("%1/lib%2_debug.dylib").arg(location).arg(name);
779 #else
780  QString libName = QString("%1/lib%2.so").arg(location).arg(name);
781 #endif
782 
783 #endif
784 
785  PluginLoader loader(libName);
786  if (!loader.load()) {
787  hasError = true;
788  errorString = libName + QString::fromLatin1(": ") + loader.errorString();
789  return false;
790  }
791  IPlugin *pluginObject = qobject_cast<IPlugin*>(loader.instance());
792  if (!pluginObject) {
793  hasError = true;
794  errorString = QCoreApplication::translate("PluginSpec", "Plugin is not valid (does not derive from IPlugin)");
795  loader.unload();
796  return false;
797  }
799  plugin = pluginObject;
800  plugin->d->pluginSpec = q;
801  return true;
802 }
803 
809 {
810  if (hasError)
811  return false;
812  if (state != PluginSpec::Loaded) {
814  return true;
815  errorString = QCoreApplication::translate("PluginSpec", "Initializing the plugin failed because state != Loaded");
816  hasError = true;
817  return false;
818  }
819  if (!plugin) {
820  errorString = QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to initialize");
821  hasError = true;
822  return false;
823  }
824  QString err;
825  if (!plugin->initialize(arguments, &err)) {
826  errorString = QCoreApplication::translate("PluginSpec", "Plugin initialization failed: %1").arg(err);
827  hasError = true;
828  return false;
829  }
831  return true;
832 }
833 
839 {
840  if (hasError)
841  return false;
843  if (state == PluginSpec::Running)
844  return true;
845  errorString = QCoreApplication::translate("PluginSpec", "Cannot perform extensionsInitialized because state != Initialized");
846  hasError = true;
847  return false;
848  }
849  if (!plugin) {
850  errorString = QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to perform extensionsInitialized");
851  hasError = true;
852  return false;
853  }
856  return true;
857 }
858 
864 {
865  if (!plugin)
866  return;
867  plugin->shutdown();
869 }
870 
876 {
877  if (!plugin)
878  return;
879  delete plugin;
880  plugin = nullptr;
882 }
883 
QString license() const
Definition: pluginspec.cpp:203
virtual void extensionsInitialized()=0
QString copyright() const
Definition: pluginspec.cpp:194
PatchedPluginLoader PluginLoader
Definition: pluginspec.cpp:57
typedef QT_PREPEND_NAMESPACE(QPluginLoader) PluginLoader
PluginSpec::PluginArgumentDescriptions argumentDescriptions
Definition: pluginspec_p.h:75
IPlugin * plugin() const
Definition: pluginspec.cpp:341
for i
Definition: OPPlots.m:140
QString filePath() const
Definition: pluginspec.cpp:260
bool provides(const QString &pluginName, const QString &version) const
Definition: pluginspec.cpp:657
void setArguments(const QStringList &arguments)
Definition: pluginspec.cpp:280
static bool isValidVersion(const QString &version)
Definition: pluginspec.cpp:677
QString errorString() const
Definition: pluginspec.cpp:319
QList< PluginDependency > dependencies
Definition: pluginspec_p.h:68
bool read(const QString &fileName)
Definition: pluginspec.cpp:395
bool operator==(const PluginDependency &other)
Definition: pluginspec.cpp:130
Parse log file
QList< PluginDependency > dependencies() const
Definition: pluginspec.cpp:230
bool provides(const QString &pluginName, const QString &version) const
Definition: pluginspec.cpp:331
static int versionCompare(const QString &version1, const QString &version2)
Definition: pluginspec.cpp:686
QStringList arguments() const
Definition: pluginspec.cpp:270
QString compatVersion() const
Definition: pluginspec.cpp:176
virtual bool initialize(const QStringList &arguments, QString *errorString)=0
void addArgument(const QString &argument)
Definition: pluginspec.cpp:290
bool resolveDependencies(const QList< PluginSpec * > &specs)
Definition: pluginspec.cpp:711
PluginArgumentDescriptions argumentDescriptions() const
Definition: pluginspec.cpp:240
Contains the information of the plugins xml description file and information about the plugin's curre...
Definition: pluginspec.h:63
Struct that contains the name and required compatible version number of a plugin's dependency...
Definition: pluginspec.h:49
QString location() const
Definition: pluginspec.cpp:250
Definition: icore.h:39
QList< PluginSpec * > dependencySpecs() const
Definition: pluginspec.cpp:353
QString description() const
Definition: pluginspec.cpp:212
Base class for all plugins.
Definition: iplugin.h:45
QString version() const
Definition: pluginspec.cpp:167
virtual void shutdown()
Definition: iplugin.h:55