OSDN Git Service

2482ea54d37d0cfc18e69c64ee56b2d4af240e2b
[qt-creator-jp/qt-creator-jp.git] / src / libs / extensionsystem / pluginspec.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "pluginspec.h"
35
36 #include "pluginspec_p.h"
37 #include "iplugin.h"
38 #include "iplugin_p.h"
39 #include "pluginmanager.h"
40
41 #include <QtCore/QDir>
42 #include <QtCore/QFile>
43 #include <QtCore/QFileInfo>
44 #include <QtCore/QXmlStreamReader>
45 #include <QtCore/QRegExp>
46 #include <QtCore/QCoreApplication>
47 #include <QtDebug>
48
49 #ifdef Q_OS_LINUX
50 // Using the patched version breaks on Fedora 10, KDE4.2.2/Qt4.5.
51 #   define USE_UNPATCHED_QPLUGINLOADER 1
52 #else
53 #   define USE_UNPATCHED_QPLUGINLOADER 1
54 #endif
55
56 #if USE_UNPATCHED_QPLUGINLOADER
57
58 #   include <QtCore/QPluginLoader>
59     typedef QT_PREPEND_NAMESPACE(QPluginLoader) PluginLoader;
60
61 #else
62
63 #   include "patchedpluginloader.cpp"
64     typedef PatchedPluginLoader PluginLoader;
65
66 #endif
67
68 /*!
69     \class ExtensionSystem::PluginDependency
70     \brief Struct that contains the name and required compatible version number of a plugin's dependency.
71
72     This reflects the data of a dependency tag in the plugin's xml description file.
73     The name and version are used to resolve the dependency, i.e. a plugin with the given name and
74     plugin \c {compatibility version <= dependency version <= plugin version} is searched for.
75
76     See also ExtensionSystem::IPlugin for more information about plugin dependencies and
77     version matching.
78 */
79
80 /*!
81     \variable ExtensionSystem::PluginDependency::name
82     String identifier of the plugin.
83 */
84
85 /*!
86     \variable ExtensionSystem::PluginDependency::version
87     Version string that a plugin must match to fill this dependency.
88 */
89
90 /*!
91     \variable ExtensionSystem::PluginDependency::type
92     Defines whether the dependency is required or optional.
93     \sa ExtensionSystem::PluginDependency::Type
94 */
95
96 /*!
97     \enum ExtensionSystem::PluginDependency::Type
98     Whether the dependency is required or optional.
99     \value Required
100            Dependency needs to be there.
101     \value Optional
102            Dependency is not necessarily needed. You need to make sure that
103            the plugin is able to load without this dependency installed, so
104            for example you may not link to the dependency's library.
105 */
106
107 /*!
108     \class ExtensionSystem::PluginSpec
109     \brief Contains the information of the plugins xml description file and
110     information about the plugin's current state.
111
112     The plugin spec is also filled with more information as the plugin
113     goes through its loading process (see PluginSpec::State).
114     If an error occurs, the plugin spec is the place to look for the
115     error details.
116 */
117
118 /*!
119     \enum ExtensionSystem::PluginSpec::State
120
121     The plugin goes through several steps while being loaded.
122     The state gives a hint on what went wrong in case of an error.
123
124     \value  Invalid
125             Starting point: Even the xml description file was not read.
126     \value  Read
127             The xml description file has been successfully read, and its
128             information is available via the PluginSpec.
129     \value  Resolved
130             The dependencies given in the description file have been
131             successfully found, and are available via the dependencySpecs() method.
132     \value  Loaded
133             The plugin's library is loaded and the plugin instance created
134             (available through plugin()).
135     \value  Initialized
136             The plugin instance's IPlugin::initialize() method has been called
137             and returned a success value.
138     \value  Running
139             The plugin's dependencies are successfully initialized and
140             extensionsInitialized has been called. The loading process is
141             complete.
142     \value Stopped
143             The plugin has been shut down, i.e. the plugin's IPlugin::aboutToShutdown() method has been called.
144     \value Deleted
145             The plugin instance has been deleted.
146 */
147
148 using namespace ExtensionSystem;
149 using namespace ExtensionSystem::Internal;
150
151 /*!
152     \fn uint qHash(const ExtensionSystem::PluginDependency &value)
153     \internal
154 */
155 uint ExtensionSystem::qHash(const ExtensionSystem::PluginDependency &value)
156 {
157     return qHash(value.name);
158 }
159
160 /*!
161     \fn bool PluginDependency::operator==(const PluginDependency &other)
162     \internal
163 */
164 bool PluginDependency::operator==(const PluginDependency &other) const
165 {
166     return name == other.name && version == other.version && type == other.type;
167 }
168
169 /*!
170     \fn PluginSpec::PluginSpec()
171     \internal
172 */
173 PluginSpec::PluginSpec()
174     : d(new PluginSpecPrivate(this))
175 {
176 }
177
178 /*!
179     \fn PluginSpec::~PluginSpec()
180     \internal
181 */
182 PluginSpec::~PluginSpec()
183 {
184     delete d;
185     d = 0;
186 }
187
188 /*!
189     \fn QString PluginSpec::name() const
190     The plugin name. This is valid after the PluginSpec::Read state is reached.
191 */
192 QString PluginSpec::name() const
193 {
194     return d->name;
195 }
196
197 /*!
198     \fn QString PluginSpec::version() const
199     The plugin version. This is valid after the PluginSpec::Read state is reached.
200 */
201 QString PluginSpec::version() const
202 {
203     return d->version;
204 }
205
206 /*!
207     \fn QString PluginSpec::compatVersion() const
208     The plugin compatibility version. This is valid after the PluginSpec::Read state is reached.
209 */
210 QString PluginSpec::compatVersion() const
211 {
212     return d->compatVersion;
213 }
214
215 /*!
216     \fn QString PluginSpec::vendor() const
217     The plugin vendor. This is valid after the PluginSpec::Read state is reached.
218 */
219 QString PluginSpec::vendor() const
220 {
221     return d->vendor;
222 }
223
224 /*!
225     \fn QString PluginSpec::copyright() const
226     The plugin copyright. This is valid after the PluginSpec::Read state is reached.
227 */
228 QString PluginSpec::copyright() const
229 {
230     return d->copyright;
231 }
232
233 /*!
234     \fn QString PluginSpec::license() const
235     The plugin license. This is valid after the PluginSpec::Read state is reached.
236 */
237 QString PluginSpec::license() const
238 {
239     return d->license;
240 }
241
242 /*!
243     \fn QString PluginSpec::description() const
244     The plugin description. This is valid after the PluginSpec::Read state is reached.
245 */
246 QString PluginSpec::description() const
247 {
248     return d->description;
249 }
250
251 /*!
252     \fn QString PluginSpec::url() const
253     The plugin url where you can find more information about the plugin. This is valid after the PluginSpec::Read state is reached.
254 */
255 QString PluginSpec::url() const
256 {
257     return d->url;
258 }
259
260 /*!
261     \fn QString PluginSpec::category() const
262     The category that the plugin belongs to. Categories are groups of plugins which allow for keeping them together in the UI.
263     Returns an empty string if the plugin does not belong to a category.
264 */
265 QString PluginSpec::category() const
266 {
267     return d->category;
268 }
269
270 /*!
271     \fn bool PluginSpec::isExperimental() const
272     Returns if the plugin has its experimental flag set.
273 */
274 bool PluginSpec::isExperimental() const
275 {
276     return d->experimental;
277 }
278
279 /*!
280     \fn bool PluginSpec::isEnabled() const
281     Returns if the plugin is loaded at startup. True by default - the user can change it from the Plugin settings.
282 */
283 bool PluginSpec::isEnabled() const
284 {
285     return d->enabled;
286 }
287
288 /*!
289     \fn bool PluginSpec::isDisabledIndirectly() const
290     Returns true if loading was not done due to user unselecting this plugin or its dependencies,
291     or if command-line parameter -noload was used.
292 */
293 bool PluginSpec::isDisabledIndirectly() const
294 {
295     return d->disabledIndirectly;
296 }
297
298 /*!
299     \fn QList<PluginDependency> PluginSpec::dependencies() const
300     The plugin dependencies. This is valid after the PluginSpec::Read state is reached.
301 */
302 QList<PluginDependency> PluginSpec::dependencies() const
303 {
304     return d->dependencies;
305 }
306
307 /*!
308     \fn PluginSpec::PluginArgumentDescriptions PluginSpec::argumentDescriptions() const
309     Returns a list of descriptions of command line arguments the plugin processes.
310 */
311
312 PluginSpec::PluginArgumentDescriptions PluginSpec::argumentDescriptions() const
313 {
314     return d->argumentDescriptions;
315 }
316
317 /*!
318     \fn QString PluginSpec::location() const
319     The absolute path to the directory containing the plugin xml description file
320     this PluginSpec corresponds to.
321 */
322 QString PluginSpec::location() const
323 {
324     return d->location;
325 }
326
327 /*!
328     \fn QString PluginSpec::filePath() const
329     The absolute path to the plugin xml description file (including the file name)
330     this PluginSpec corresponds to.
331 */
332 QString PluginSpec::filePath() const
333 {
334     return d->filePath;
335 }
336
337 /*!
338     \fn QStringList PluginSpec::arguments() const
339     Command line arguments specific to that plugin. Set at startup
340 */
341
342 QStringList PluginSpec::arguments() const
343 {
344     return d->arguments;
345 }
346
347 /*!
348     \fn void PluginSpec::setArguments(const QStringList &arguments)
349     Set the command line arguments specific to that plugin to \a arguments.
350 */
351
352 void PluginSpec::setArguments(const QStringList &arguments)
353 {
354     d->arguments = arguments;
355 }
356
357 /*!
358     \fn PluginSpec::addArgument(const QString &argument)
359     Adds \a argument to the command line arguments specific to that plugin.
360 */
361
362 void PluginSpec::addArgument(const QString &argument)
363 {
364     d->arguments.push_back(argument);
365 }
366
367
368 /*!
369     \fn PluginSpec::State PluginSpec::state() const
370     The state in which the plugin currently is.
371     See the description of the PluginSpec::State enum for details.
372 */
373 PluginSpec::State PluginSpec::state() const
374 {
375     return d->state;
376 }
377
378 /*!
379     \fn bool PluginSpec::hasError() const
380     Returns whether an error occurred while reading/starting the plugin.
381 */
382 bool PluginSpec::hasError() const
383 {
384     return d->hasError;
385 }
386
387 /*!
388     \fn QString PluginSpec::errorString() const
389     Detailed, possibly multi-line, error description in case of an error.
390 */
391 QString PluginSpec::errorString() const
392 {
393     return d->errorString;
394 }
395
396 /*!
397     \fn bool PluginSpec::provides(const QString &pluginName, const QString &version) const
398     Returns if this plugin can be used to fill in a dependency of the given
399     \a pluginName and \a version.
400
401         \sa PluginSpec::dependencies()
402 */
403 bool PluginSpec::provides(const QString &pluginName, const QString &version) const
404 {
405     return d->provides(pluginName, version);
406 }
407
408 /*!
409     \fn IPlugin *PluginSpec::plugin() const
410     The corresponding IPlugin instance, if the plugin library has already been successfully loaded,
411     i.e. the PluginSpec::Loaded state is reached.
412 */
413 IPlugin *PluginSpec::plugin() const
414 {
415     return d->plugin;
416 }
417
418 /*!
419     \fn QList<PluginSpec *> PluginSpec::dependencySpecs() const
420     Returns the list of dependencies, already resolved to existing plugin specs.
421     Valid if PluginSpec::Resolved state is reached.
422
423     \sa PluginSpec::dependencies()
424 */
425 QHash<PluginDependency, PluginSpec *> PluginSpec::dependencySpecs() const
426 {
427     return d->dependencySpecs;
428 }
429
430 //==========PluginSpecPrivate==================
431
432 namespace {
433     const char * const PLUGIN = "plugin";
434     const char * const PLUGIN_NAME = "name";
435     const char * const PLUGIN_VERSION = "version";
436     const char * const PLUGIN_COMPATVERSION = "compatVersion";
437     const char * const PLUGIN_EXPERIMENTAL = "experimental";
438     const char * const VENDOR = "vendor";
439     const char * const COPYRIGHT = "copyright";
440     const char * const LICENSE = "license";
441     const char * const DESCRIPTION = "description";
442     const char * const URL = "url";
443     const char * const CATEGORY = "category";
444     const char * const DEPENDENCYLIST = "dependencyList";
445     const char * const DEPENDENCY = "dependency";
446     const char * const DEPENDENCY_NAME = "name";
447     const char * const DEPENDENCY_VERSION = "version";
448     const char * const DEPENDENCY_TYPE = "type";
449     const char * const DEPENDENCY_TYPE_SOFT = "optional";
450     const char * const DEPENDENCY_TYPE_HARD = "required";
451     const char * const ARGUMENTLIST = "argumentList";
452     const char * const ARGUMENT = "argument";
453     const char * const ARGUMENT_NAME = "name";
454     const char * const ARGUMENT_PARAMETER = "parameter";
455 }
456 /*!
457     \fn PluginSpecPrivate::PluginSpecPrivate(PluginSpec *spec)
458     \internal
459 */
460 PluginSpecPrivate::PluginSpecPrivate(PluginSpec *spec)
461     :
462     enabled(true),
463     disabledIndirectly(false),
464     plugin(0),
465     state(PluginSpec::Invalid),
466     hasError(false),
467     q(spec)
468 {
469 }
470
471 /*!
472     \fn bool PluginSpecPrivate::read(const QString &fileName)
473     \internal
474 */
475 bool PluginSpecPrivate::read(const QString &fileName)
476 {
477     name
478         = version
479         = compatVersion
480         = vendor
481         = copyright
482         = license
483         = description
484         = url
485         = category
486         = location
487         = "";
488     state = PluginSpec::Invalid;
489     hasError = false;
490     errorString = "";
491     dependencies.clear();
492     QFile file(fileName);
493     if (!file.exists())
494         return reportError(tr("File does not exist: %1").arg(file.fileName()));
495     if (!file.open(QIODevice::ReadOnly))
496         return reportError(tr("Could not open file for read: %1").arg(file.fileName()));
497     QFileInfo fileInfo(file);
498     location = fileInfo.absolutePath();
499     filePath = fileInfo.absoluteFilePath();
500     QXmlStreamReader reader(&file);
501     while (!reader.atEnd()) {
502         reader.readNext();
503         switch (reader.tokenType()) {
504         case QXmlStreamReader::StartElement:
505             readPluginSpec(reader);
506             break;
507         default:
508             break;
509         }
510     }
511     if (reader.hasError())
512         return reportError(tr("Error parsing file %1: %2, at line %3, column %4")
513                 .arg(file.fileName())
514                 .arg(reader.errorString())
515                 .arg(reader.lineNumber())
516                 .arg(reader.columnNumber()));
517     state = PluginSpec::Read;
518     return true;
519 }
520
521 void PluginSpec::setEnabled(bool value)
522 {
523     d->enabled = value;
524 }
525
526 void PluginSpec::setDisabledIndirectly(bool value)
527 {
528     d->disabledIndirectly = value;
529 }
530
531 /*!
532     \fn bool PluginSpecPrivate::reportError(const QString &err)
533     \internal
534 */
535 bool PluginSpecPrivate::reportError(const QString &err)
536 {
537     errorString = err;
538     hasError = true;
539     return false;
540 }
541
542 static inline QString msgAttributeMissing(const char *elt, const char *attribute)
543 {
544     return QCoreApplication::translate("PluginSpec", "'%1' misses attribute '%2'").arg(QLatin1String(elt), QLatin1String(attribute));
545 }
546
547 static inline QString msgInvalidFormat(const char *content)
548 {
549     return QCoreApplication::translate("PluginSpec", "'%1' has invalid format").arg(content);
550 }
551
552 static inline QString msgInvalidElement(const QString &name)
553 {
554     return QCoreApplication::translate("PluginSpec", "Invalid element '%1'").arg(name);
555 }
556
557 static inline QString msgUnexpectedClosing(const QString &name)
558 {
559     return QCoreApplication::translate("PluginSpec", "Unexpected closing element '%1'").arg(name);
560 }
561
562 static inline QString msgUnexpectedToken()
563 {
564     return QCoreApplication::translate("PluginSpec", "Unexpected token");
565 }
566
567 /*!
568     \fn void PluginSpecPrivate::readPluginSpec(QXmlStreamReader &reader)
569     \internal
570 */
571 void PluginSpecPrivate::readPluginSpec(QXmlStreamReader &reader)
572 {
573     QString element = reader.name().toString();
574     if (element != QString(PLUGIN)) {
575         reader.raiseError(QCoreApplication::translate("PluginSpec", "Expected element '%1' as top level element").arg(PLUGIN));
576         return;
577     }
578     name = reader.attributes().value(PLUGIN_NAME).toString();
579     if (name.isEmpty()) {
580         reader.raiseError(msgAttributeMissing(PLUGIN, PLUGIN_NAME));
581         return;
582     }
583     version = reader.attributes().value(PLUGIN_VERSION).toString();
584     if (version.isEmpty()) {
585         reader.raiseError(msgAttributeMissing(PLUGIN, PLUGIN_VERSION));
586         return;
587     }
588     if (!isValidVersion(version)) {
589         reader.raiseError(msgInvalidFormat(PLUGIN_VERSION));
590         return;
591     }
592     compatVersion = reader.attributes().value(PLUGIN_COMPATVERSION).toString();
593     if (!compatVersion.isEmpty() && !isValidVersion(compatVersion)) {
594         reader.raiseError(msgInvalidFormat(PLUGIN_COMPATVERSION));
595         return;
596     } else if (compatVersion.isEmpty()) {
597         compatVersion = version;
598     }
599     QString experimentalString = reader.attributes().value(PLUGIN_EXPERIMENTAL).toString();
600     experimental = (experimentalString.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0);
601     if (!experimentalString.isEmpty() && !experimental
602             && experimentalString.compare(QLatin1String("false"), Qt::CaseInsensitive) != 0) {
603         reader.raiseError(msgInvalidFormat(PLUGIN_EXPERIMENTAL));
604         return;
605     }
606     enabled = !experimental;
607     while (!reader.atEnd()) {
608         reader.readNext();
609         switch (reader.tokenType()) {
610         case QXmlStreamReader::StartElement:
611             element = reader.name().toString();
612             if (element == VENDOR)
613                 vendor = reader.readElementText().trimmed();
614             else if (element == COPYRIGHT)
615                 copyright = reader.readElementText().trimmed();
616             else if (element == LICENSE)
617                 license = reader.readElementText().trimmed();
618             else if (element == DESCRIPTION)
619                 description = reader.readElementText().trimmed();
620             else if (element == URL)
621                 url = reader.readElementText().trimmed();
622             else if (element == CATEGORY)
623                 category = reader.readElementText().trimmed();
624             else if (element == DEPENDENCYLIST)
625                 readDependencies(reader);
626             else if (element == ARGUMENTLIST)
627                 readArgumentDescriptions(reader);
628             else
629                 reader.raiseError(msgInvalidElement(name));
630             break;
631         case QXmlStreamReader::EndDocument:
632         case QXmlStreamReader::Comment:
633         case QXmlStreamReader::EndElement:
634         case QXmlStreamReader::Characters:
635             break;
636         default:
637             reader.raiseError(msgUnexpectedToken());
638             break;
639         }
640     }
641 }
642
643 /*!
644     \fn void PluginSpecPrivate::readArgumentDescriptions(QXmlStreamReader &reader)
645     \internal
646 */
647
648 void PluginSpecPrivate::readArgumentDescriptions(QXmlStreamReader &reader)
649 {
650     QString element;
651     while (!reader.atEnd()) {
652         reader.readNext();
653         switch (reader.tokenType()) {
654         case QXmlStreamReader::StartElement:
655             element = reader.name().toString();
656             if (element == ARGUMENT) {
657                 readArgumentDescription(reader);
658             } else {
659                 reader.raiseError(msgInvalidElement(name));
660             }
661             break;
662         case QXmlStreamReader::Comment:
663         case QXmlStreamReader::Characters:
664             break;
665         case QXmlStreamReader::EndElement:
666             element = reader.name().toString();
667             if (element == ARGUMENTLIST)
668                 return;
669             reader.raiseError(msgUnexpectedClosing(element));
670             break;
671         default:
672             reader.raiseError(msgUnexpectedToken());
673             break;
674         }
675     }
676 }
677
678 /*!
679     \fn void PluginSpecPrivate::readArgumentDescription(QXmlStreamReader &reader)
680     \internal
681 */
682 void PluginSpecPrivate::readArgumentDescription(QXmlStreamReader &reader)
683 {
684     PluginArgumentDescription arg;
685     arg.name = reader.attributes().value(ARGUMENT_NAME).toString();
686     if (arg.name.isEmpty()) {
687         reader.raiseError(msgAttributeMissing(ARGUMENT, ARGUMENT_NAME));
688         return;
689     }
690     arg.parameter = reader.attributes().value(ARGUMENT_PARAMETER).toString();
691     arg.description = reader.readElementText();
692     if (reader.tokenType() != QXmlStreamReader::EndElement)
693         reader.raiseError(msgUnexpectedToken());
694     argumentDescriptions.push_back(arg);
695 }
696
697 /*!
698     \fn void PluginSpecPrivate::readDependencies(QXmlStreamReader &reader)
699     \internal
700 */
701 void PluginSpecPrivate::readDependencies(QXmlStreamReader &reader)
702 {
703     QString element;
704     while (!reader.atEnd()) {
705         reader.readNext();
706         switch (reader.tokenType()) {
707         case QXmlStreamReader::StartElement:
708             element = reader.name().toString();
709             if (element == DEPENDENCY) {
710                 readDependencyEntry(reader);
711             } else {
712                 reader.raiseError(msgInvalidElement(name));
713             }
714             break;
715         case QXmlStreamReader::Comment:
716         case QXmlStreamReader::Characters:
717             break;
718         case QXmlStreamReader::EndElement:
719             element = reader.name().toString();
720             if (element == DEPENDENCYLIST)
721                 return;
722             reader.raiseError(msgUnexpectedClosing(element));
723             break;
724         default:
725             reader.raiseError(msgUnexpectedToken());
726             break;
727         }
728     }
729 }
730
731 /*!
732     \fn void PluginSpecPrivate::readDependencyEntry(QXmlStreamReader &reader)
733     \internal
734 */
735 void PluginSpecPrivate::readDependencyEntry(QXmlStreamReader &reader)
736 {
737     PluginDependency dep;
738     dep.name = reader.attributes().value(DEPENDENCY_NAME).toString();
739     if (dep.name.isEmpty()) {
740         reader.raiseError(msgAttributeMissing(DEPENDENCY, DEPENDENCY_NAME));
741         return;
742     }
743     dep.version = reader.attributes().value(DEPENDENCY_VERSION).toString();
744     if (!dep.version.isEmpty() && !isValidVersion(dep.version)) {
745         reader.raiseError(msgInvalidFormat(DEPENDENCY_VERSION));
746         return;
747     }
748     dep.type = PluginDependency::Required;
749     if (reader.attributes().hasAttribute(DEPENDENCY_TYPE)) {
750         QString typeValue = reader.attributes().value(DEPENDENCY_TYPE).toString();
751         if (typeValue == QLatin1String(DEPENDENCY_TYPE_HARD)) {
752             dep.type = PluginDependency::Required;
753         } else if (typeValue == QLatin1String(DEPENDENCY_TYPE_SOFT)) {
754             dep.type = PluginDependency::Optional;
755         } else {
756             reader.raiseError(msgInvalidFormat(DEPENDENCY_TYPE));
757             return;
758         }
759     }
760     dependencies.append(dep);
761     reader.readNext();
762     if (reader.tokenType() != QXmlStreamReader::EndElement)
763         reader.raiseError(msgUnexpectedToken());
764 }
765
766 /*!
767     \fn bool PluginSpecPrivate::provides(const QString &pluginName, const QString &pluginVersion) const
768     \internal
769 */
770 bool PluginSpecPrivate::provides(const QString &pluginName, const QString &pluginVersion) const
771 {
772     if (QString::compare(pluginName, name, Qt::CaseInsensitive) != 0)
773         return false;
774     return (versionCompare(version, pluginVersion) >= 0) && (versionCompare(compatVersion, pluginVersion) <= 0);
775 }
776
777 /*!
778     \fn QRegExp &PluginSpecPrivate::versionRegExp()
779     \internal
780 */
781 QRegExp &PluginSpecPrivate::versionRegExp()
782 {
783     static QRegExp reg("([0-9]+)(?:[.]([0-9]+))?(?:[.]([0-9]+))?(?:_([0-9]+))?");
784     return reg;
785 }
786 /*!
787     \fn bool PluginSpecPrivate::isValidVersion(const QString &version)
788     \internal
789 */
790 bool PluginSpecPrivate::isValidVersion(const QString &version)
791 {
792     return versionRegExp().exactMatch(version);
793 }
794
795 /*!
796     \fn int PluginSpecPrivate::versionCompare(const QString &version1, const QString &version2)
797     \internal
798 */
799 int PluginSpecPrivate::versionCompare(const QString &version1, const QString &version2)
800 {
801     QRegExp reg1 = versionRegExp();
802     QRegExp reg2 = versionRegExp();
803     if (!reg1.exactMatch(version1))
804         return 0;
805     if (!reg2.exactMatch(version2))
806         return 0;
807     int number1;
808     int number2;
809     for (int i = 0; i < 4; ++i) {
810         number1 = reg1.cap(i+1).toInt();
811         number2 = reg2.cap(i+1).toInt();
812         if (number1 < number2)
813             return -1;
814         if (number1 > number2)
815             return 1;
816     }
817     return 0;
818 }
819
820 /*!
821     \fn bool PluginSpecPrivate::resolveDependencies(const QList<PluginSpec *> &specs)
822     \internal
823 */
824 bool PluginSpecPrivate::resolveDependencies(const QList<PluginSpec *> &specs)
825 {
826     if (hasError)
827         return false;
828     if (state == PluginSpec::Resolved)
829         state = PluginSpec::Read; // Go back, so we just re-resolve the dependencies.
830     if (state != PluginSpec::Read) {
831         errorString = QCoreApplication::translate("PluginSpec", "Resolving dependencies failed because state != Read");
832         hasError = true;
833         return false;
834     }
835     QHash<PluginDependency, PluginSpec *> resolvedDependencies;
836     foreach (const PluginDependency &dependency, dependencies) {
837         PluginSpec *found = 0;
838
839         foreach (PluginSpec *spec, specs) {
840             if (spec->provides(dependency.name, dependency.version)) {
841                 found = spec;
842                 break;
843             }
844         }
845         if (!found) {
846             if (dependency.type == PluginDependency::Required) {
847                 hasError = true;
848                 if (!errorString.isEmpty())
849                     errorString.append(QLatin1Char('\n'));
850                 errorString.append(QCoreApplication::translate("PluginSpec", "Could not resolve dependency '%1(%2)'")
851                     .arg(dependency.name).arg(dependency.version));
852             }
853             continue;
854         }
855         resolvedDependencies.insert(dependency, found);
856     }
857     if (hasError)
858         return false;
859
860     dependencySpecs = resolvedDependencies;
861
862     state = PluginSpec::Resolved;
863
864     return true;
865 }
866
867 void PluginSpecPrivate::disableIndirectlyIfDependencyDisabled()
868 {
869     if (!enabled)
870         return;
871
872     if (disabledIndirectly)
873         return;
874
875     QHashIterator<PluginDependency, PluginSpec *> it(dependencySpecs);
876     while (it.hasNext()) {
877         it.next();
878         if (it.key().type == PluginDependency::Optional)
879             continue;
880         PluginSpec *dependencySpec = it.value();
881         if (dependencySpec->isDisabledIndirectly() || !dependencySpec->isEnabled()) {
882             disabledIndirectly = true;
883             break;
884         }
885     }
886 }
887
888 /*!
889     \fn bool PluginSpecPrivate::loadLibrary()
890     \internal
891 */
892 bool PluginSpecPrivate::loadLibrary()
893 {
894     if (hasError)
895         return false;
896     if (state != PluginSpec::Resolved) {
897         if (state == PluginSpec::Loaded)
898             return true;
899         errorString = QCoreApplication::translate("PluginSpec", "Loading the library failed because state != Resolved");
900         hasError = true;
901         return false;
902     }
903 #ifdef QT_NO_DEBUG
904
905 #ifdef Q_OS_WIN
906     QString libName = QString("%1/%2.dll").arg(location).arg(name);
907 #elif defined(Q_OS_MAC)
908     QString libName = QString("%1/lib%2.dylib").arg(location).arg(name);
909 #else
910     QString libName = QString("%1/lib%2.so").arg(location).arg(name);
911 #endif
912
913 #else //Q_NO_DEBUG
914
915 #ifdef Q_OS_WIN
916     QString libName = QString("%1/%2d.dll").arg(location).arg(name);
917 #elif defined(Q_OS_MAC)
918     QString libName = QString("%1/lib%2_debug.dylib").arg(location).arg(name);
919 #else
920     QString libName = QString("%1/lib%2.so").arg(location).arg(name);
921 #endif
922
923 #endif
924
925     PluginLoader loader(libName);
926     if (!loader.load()) {
927         hasError = true;
928         errorString = QDir::toNativeSeparators(libName)
929             + QString::fromLatin1(": ") + loader.errorString();
930         return false;
931     }
932     IPlugin *pluginObject = qobject_cast<IPlugin*>(loader.instance());
933     if (!pluginObject) {
934         hasError = true;
935         errorString = QCoreApplication::translate("PluginSpec", "Plugin is not valid (does not derive from IPlugin)");
936         loader.unload();
937         return false;
938     }
939     state = PluginSpec::Loaded;
940     plugin = pluginObject;
941     plugin->d->pluginSpec = q;
942     return true;
943 }
944
945 /*!
946     \fn bool PluginSpecPrivate::initializePlugin()
947     \internal
948 */
949 bool PluginSpecPrivate::initializePlugin()
950 {
951     if (hasError)
952         return false;
953     if (state != PluginSpec::Loaded) {
954         if (state == PluginSpec::Initialized)
955             return true;
956         errorString = QCoreApplication::translate("PluginSpec", "Initializing the plugin failed because state != Loaded");
957         hasError = true;
958         return false;
959     }
960     if (!plugin) {
961         errorString = QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to initialize");
962         hasError = true;
963         return false;
964     }
965     QString err;
966     if (!plugin->initialize(arguments, &err)) {
967         errorString = QCoreApplication::translate("PluginSpec", "Plugin initialization failed: %1").arg(err);
968         hasError = true;
969         return false;
970     }
971     state = PluginSpec::Initialized;
972     return true;
973 }
974
975 /*!
976     \fn bool PluginSpecPrivate::initializeExtensions()
977     \internal
978 */
979 bool PluginSpecPrivate::initializeExtensions()
980 {
981     if (hasError)
982         return false;
983     if (state != PluginSpec::Initialized) {
984         if (state == PluginSpec::Running)
985             return true;
986         errorString = QCoreApplication::translate("PluginSpec", "Cannot perform extensionsInitialized because state != Initialized");
987         hasError = true;
988         return false;
989     }
990     if (!plugin) {
991         errorString = QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to perform extensionsInitialized");
992         hasError = true;
993         return false;
994     }
995     plugin->extensionsInitialized();
996     state = PluginSpec::Running;
997     return true;
998 }
999
1000 /*!
1001     \fn bool PluginSpecPrivate::stop()
1002     \internal
1003 */
1004 IPlugin::ShutdownFlag PluginSpecPrivate::stop()
1005 {
1006     if (!plugin)
1007         return IPlugin::SynchronousShutdown;
1008     state = PluginSpec::Stopped;
1009     return plugin->aboutToShutdown();
1010 }
1011
1012 /*!
1013     \fn bool PluginSpecPrivate::kill()
1014     \internal
1015 */
1016 void PluginSpecPrivate::kill()
1017 {
1018     if (!plugin)
1019         return;
1020     delete plugin;
1021     plugin = 0;
1022     state = PluginSpec::Deleted;
1023 }