1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qqmlimport_p.h"
44 #include <QtCore/qdebug.h>
45 #include <QtCore/qdir.h>
46 #include <QtQml/qqmlfile.h>
47 #include <QtCore/qfileinfo.h>
48 #include <QtCore/qpluginloader.h>
49 #include <QtCore/qlibraryinfo.h>
50 #include <QtCore/qreadwritelock.h>
51 #include <QtQml/qqmlextensioninterface.h>
52 #include <private/qqmlglobal_p.h>
53 #include <private/qqmltypenamecache_p.h>
54 #include <private/qqmlengine_p.h>
55 #include <private/qfieldlist_p.h>
59 DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
60 DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
62 static const QLatin1Char Dot('.');
63 static const QLatin1Char Slash('/');
64 static const QLatin1Char Backslash('\\');
65 static const QLatin1Char Colon(':');
66 static const QLatin1String Slash_qmldir("/qmldir");
67 static const QLatin1String String_qmldir("qmldir");
68 static const QString dotqml_string(QLatin1String(".qml"));
72 QString resolveLocalUrl(const QString &url, const QString &relative)
74 if (relative.contains(Colon)) {
75 // contains a host name
76 return QUrl(url).resolved(QUrl(relative)).toString();
77 } else if (relative.isEmpty()) {
79 } else if (relative.at(0) == Slash || !url.contains(Slash)) {
82 QString base(url.left(url.lastIndexOf(Slash) + 1));
84 if (relative == QLatin1String("."))
87 base.append(relative);
89 // Remove any relative directory elements in the path
90 int length = base.length();
92 while ((index = base.indexOf(QLatin1String("/."), index)) != -1) {
93 if ((length > (index + 2)) && (base.at(index + 2) == Dot) &&
94 (length == (index + 3) || (base.at(index + 3) == Slash))) {
95 // Either "/../" or "/..<END>"
96 int previous = base.lastIndexOf(Slash, index - 1);
100 int removeLength = (index - previous) + 3;
101 base.remove(previous + 1, removeLength);
102 length -= removeLength;
104 } else if ((length == (index + 2)) || (base.at(index + 2) == Slash)) {
105 // Either "/./" or "/.<END>"
106 base.remove(index, 2);
117 bool isPathAbsolute(const QString &path)
119 #if defined(Q_OS_UNIX)
120 return (path.at(0) == Slash);
123 return fi.isAbsolute();
129 typedef QMap<QString, QString> StringStringMap;
130 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
132 class QQmlImportNamespace
135 QQmlImportNamespace() : nextNamespace(0) {}
136 ~QQmlImportNamespace() { qDeleteAll(imports); }
144 QQmlDirComponents qmlDirComponents;
145 QQmlDirScripts qmlDirScripts;
147 bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir,
148 QQmlImportNamespace *nameSpace, QList<QQmlError> *errors);
150 static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin);
152 bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type,
153 int *vmajor, int *vminor,
154 QQmlType** type_return, QString* url_return,
155 QString *base = 0, bool *typeRecursionDetected = 0) const;
157 QList<Import *> imports;
159 Import *findImport(const QString &uri);
161 bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type,
162 int *vmajor, int *vminor,
163 QQmlType** type_return, QString* url_return,
164 QString *base = 0, QList<QQmlError> *errors = 0);
166 // Prefix when used as a qualified import. Otherwise empty.
167 QHashedString prefix;
169 // Used by QQmlImportsPrivate::qualifiedSets
170 QQmlImportNamespace *nextNamespace;
173 class QQmlImportsPrivate
176 QQmlImportsPrivate(QQmlTypeLoader *loader);
177 ~QQmlImportsPrivate();
179 QQmlImportNamespace *importNamespace(const QString &prefix) const;
181 bool addLibraryImport(const QString& uri, const QString &prefix,
182 int vmaj, int vmin, const QString &qmldirIdentifier, const QString &qmldirUrl, bool incomplete,
183 QQmlImportDatabase *database,
184 QList<QQmlError> *errors);
186 bool addFileImport(const QString &uri, const QString &prefix,
188 bool isImplicitImport, bool incomplete, QQmlImportDatabase *database,
189 QList<QQmlError> *errors);
191 bool updateQmldirContent(const QString &uri, const QString &prefix,
192 const QString &qmldirIdentifier, const QString& qmldirUrl,
193 QQmlImportDatabase *database,
194 QList<QQmlError> *errors);
196 bool resolveType(const QHashedStringRef &type, int *vmajor, int *vminor,
197 QQmlType** type_return, QString* url_return,
198 QList<QQmlError> *errors);
204 mutable QQmlImportNamespace unqualifiedset;
206 QQmlImportNamespace *findQualifiedNamespace(const QHashedStringRef &) const;
207 mutable QFieldList<QQmlImportNamespace, &QQmlImportNamespace::nextNamespace> qualifiedSets;
209 QQmlTypeLoader *typeLoader;
211 static inline QString tr(const char *str) {
212 return QQmlImportDatabase::tr(str);
215 static bool locateQmldir(const QString &uri, int vmaj, int vmin,
216 QQmlImportDatabase *database,
217 QString *outQmldirFilePath, QString *outUrl);
219 static bool validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin,
220 QList<QQmlError> *errors);
222 bool importExtension(const QString &absoluteFilePath, const QString &uri,
223 QQmlImportDatabase *database,
224 const QQmlTypeLoader::QmldirContent *qmldir,
225 QList<QQmlError> *errors);
227 bool getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
228 const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors);
230 QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database);
232 QQmlImportNamespace::Import *addImportToNamespace(QQmlImportNamespace *nameSpace,
233 const QString &uri, const QString &url,
234 int vmaj, int vmin, QQmlScript::Import::Type type,
235 QList<QQmlError> *errors);
240 \brief The QQmlImports class encapsulates one QML document's import statements.
243 QQmlImports::QQmlImports(const QQmlImports ©)
250 QQmlImports::operator =(const QQmlImports ©)
259 QQmlImports::QQmlImports(QQmlTypeLoader *typeLoader)
260 : d(new QQmlImportsPrivate(typeLoader))
264 QQmlImports::~QQmlImports()
271 Sets the base URL to be used for all relative file imports added.
273 void QQmlImports::setBaseUrl(const QUrl& url, const QString &urlString)
277 if (urlString.isEmpty()) {
278 d->base = url.toString();
280 //Q_ASSERT(url.toString() == urlString);
286 Returns the base URL to be used for all relative file imports added.
288 QUrl QQmlImports::baseUrl() const
293 void QQmlImports::populateCache(QQmlTypeNameCache *cache, QQmlEngine *engine) const
295 const QQmlImportNamespace &set = d->unqualifiedset;
297 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
298 const QQmlImportNamespace::Import *import = set.imports.at(ii);
299 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion);
301 cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import->minversion));
304 for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(ns)) {
306 const QQmlImportNamespace &set = *ns;
308 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
309 const QQmlImportNamespace::Import *import = set.imports.at(ii);
310 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion);
312 QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[set.prefix];
313 typeimport.modules.append(QQmlTypeModuleVersion(module, import->minversion));
316 QQmlMetaType::ModuleApi moduleApi = QQmlMetaType::moduleApi(import->uri, import->majversion,
318 if (moduleApi.script || moduleApi.qobject) {
319 QQmlTypeNameCache::Import &import = cache->m_namedImports[set.prefix];
320 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
321 import.moduleApi = ep->moduleApiInstance(moduleApi);
327 QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
329 QList<QQmlImports::ScriptReference> scripts;
331 const QQmlImportNamespace &set = d->unqualifiedset;
333 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
334 const QQmlImportNamespace::Import *import = set.imports.at(ii);
336 foreach (const QQmlDirParser::Script &script, import->qmlDirScripts) {
338 ref.nameSpace = script.nameSpace;
339 ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
344 for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(ns)) {
345 const QQmlImportNamespace &set = *ns;
347 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
348 const QQmlImportNamespace::Import *import = set.imports.at(ii);
350 foreach (const QQmlDirParser::Script &script, import->qmlDirScripts) {
352 ref.nameSpace = script.nameSpace;
353 ref.qualifier = set.prefix;
354 ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
364 Form a complete path to a qmldir file, from a base URL, a module URI and version specification.
366 QString QQmlImports::completeQmldirPath(const QString &uri, const QString &base, int vmaj, int vmin,
367 ImportVersion version)
370 url.replace(Dot, Slash);
373 if (!dir.endsWith(Slash) && !dir.endsWith(Backslash))
377 if (version == QQmlImports::FullyVersioned) {
378 // extension with fully encoded version number (eg. MyModule.3.2)
379 dir += QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin);
380 } else if (version == QQmlImports::PartiallyVersioned) {
381 // extension with encoded version major (eg. MyModule.3)
382 dir += QString(QLatin1String(".%1")).arg(vmaj);
383 } // else extension without version number (eg. MyModule)
385 return dir + Slash_qmldir;
391 The given (namespace qualified) \a type is resolved to either
393 \li a QQmlImportNamespace stored at \a ns_return,
394 \li a QQmlType stored at \a type_return, or
395 \li a component located at \a url_return.
398 If any return pointer is 0, the corresponding search is not done.
400 \sa addFileImport(), addLibraryImport
402 bool QQmlImports::resolveType(const QHashedStringRef &type,
403 QQmlType** type_return, QString* url_return, int *vmaj, int *vmin,
404 QQmlImportNamespace** ns_return, QList<QQmlError> *errors) const
406 QQmlImportNamespace* ns = d->findQualifiedNamespace(type);
412 if (type_return || url_return) {
413 if (d->resolveType(type,vmaj,vmin,type_return,url_return, errors)) {
414 if (qmlImportTrace()) {
415 #define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \
416 << ')' << "::resolveType: " << type.toString() << " => "
418 if (type_return && *type_return && url_return && !url_return->isEmpty())
419 RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << ' ' << *url_return << " TYPE/URL";
420 if (type_return && *type_return)
421 RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << " TYPE";
422 if (url_return && !url_return->isEmpty())
423 RESOLVE_TYPE_DEBUG << *url_return << " URL";
425 #undef RESOLVE_TYPE_DEBUG
433 bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors)
435 Q_ASSERT(resolvedUrl.endsWith(Slash));
438 qmlDirComponents = qmldir->components();
440 const QQmlDirScripts &scripts = qmldir->scripts();
441 if (!scripts.isEmpty()) {
442 // Verify that we haven't imported these scripts already
443 for (QList<QQmlImportNamespace::Import *>::const_iterator it = nameSpace->imports.constBegin();
444 it != nameSpace->imports.constEnd(); ++it) {
445 if ((*it != this) && ((*it)->uri == uri)) {
447 error.setDescription(QQmlImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg((*it)->url));
448 errors->prepend(error);
453 qmlDirScripts = getVersionedScripts(scripts, majversion, minversion);
459 QQmlDirScripts QQmlImportNamespace::Import::getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin)
461 QMap<QString, QQmlDirParser::Script> versioned;
463 for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
464 sit != qmldirscripts.constEnd(); ++sit) {
465 // Only include scripts that match our requested version
466 if (((vmaj == -1) || (sit->majorVersion == vmaj)) &&
467 ((vmin == -1) || (sit->minorVersion <= vmin))) {
468 // Load the highest version that matches
469 QMap<QString, QQmlDirParser::Script>::iterator vit = versioned.find(sit->nameSpace);
470 if (vit == versioned.end() || (vit->minorVersion < sit->minorVersion)) {
471 versioned.insert(sit->nameSpace, *sit);
476 return versioned.values();
482 Searching \e only in the namespace \a ns (previously returned in a call to
483 resolveType(), \a type is found and returned to either
484 a QQmlType stored at \a type_return, or
485 a component located at \a url_return.
487 If either return pointer is 0, the corresponding search is not done.
489 bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QHashedStringRef &type,
490 QQmlType** type_return, QString* url_return,
491 int *vmaj, int *vmin) const
493 return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return,url_return);
496 bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader,
497 const QHashedStringRef& type, int *vmajor, int *vminor,
498 QQmlType** type_return, QString* url_return,
499 QString *base, bool *typeRecursionDetected) const
501 if (majversion >= 0 && minversion >= 0) {
502 QQmlType *t = QQmlMetaType::qmlType(type, uri, majversion, minversion);
504 if (vmajor) *vmajor = majversion;
505 if (vminor) *vminor = minversion;
512 QQmlDirComponents::ConstIterator it = qmlDirComponents.find(type), end = qmlDirComponents.end();
514 QString componentUrl;
515 QQmlDirComponents::ConstIterator candidate = end;
516 for ( ; it != end && it.key() == type; ++it) {
517 const QQmlDirParser::Component &c = *it;
519 // importing version -1 means import ALL versions
520 if ((majversion == -1) ||
521 (c.majorVersion == majversion && c.minorVersion <= minversion)) {
522 // Is this better than the previous candidate?
523 if ((candidate == end) ||
524 (c.majorVersion > candidate->majorVersion) ||
525 ((c.majorVersion == candidate->majorVersion) && (c.minorVersion > candidate->minorVersion))) {
526 componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
527 if (c.internal && base) {
528 if (resolveLocalUrl(*base, c.fileName) != componentUrl)
529 continue; // failed attempt to access an internal type
531 if (base && (*base == componentUrl)) {
532 if (typeRecursionDetected)
533 *typeRecursionDetected = true;
534 continue; // no recursion
537 // This is our best candidate so far
543 if (candidate != end) {
545 *url_return = componentUrl;
548 } else if (!isLibrary) {
549 QString qmlUrl = url + QString::fromRawData(type.constData(), type.length()) + dotqml_string;
553 if (QQmlFile::isBundle(qmlUrl)) {
554 exists = QQmlFile::bundleFileExists(qmlUrl, typeLoader->engine());
556 exists = !typeLoader->absoluteFilePath(QQmlFile::urlToLocalFileOrQrc(qmlUrl)).isEmpty();
560 if (base && (*base == qmlUrl)) { // no recursion
561 if (typeRecursionDetected)
562 *typeRecursionDetected = true;
565 *url_return = qmlUrl;
574 bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, int *vminor,
575 QQmlType** type_return, QString* url_return,
576 QList<QQmlError> *errors)
578 QQmlImportNamespace *s = 0;
579 int dot = type.indexOf(Dot);
581 QHashedStringRef namespaceName(type.constData(), dot);
582 s = findQualifiedNamespace(namespaceName);
586 error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName.toString()));
587 errors->prepend(error);
591 int ndot = type.indexOf(Dot,dot+1);
595 error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
596 errors->prepend(error);
603 QHashedStringRef unqualifiedtype = dot < 0 ? type : QHashedStringRef(type.constData()+dot+1, type.length()-dot-1);
605 if (s->resolveType(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
607 if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && url_return && s != &unqualifiedset) {
608 // qualified, and only 1 url
609 *url_return = resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml"));
617 QQmlImportNamespace::Import *QQmlImportNamespace::findImport(const QString &uri)
619 for (QList<Import *>::iterator it = imports.begin(), end = imports.end(); it != end; ++it) {
620 if ((*it)->uri == uri)
627 bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type,
628 int *vmajor, int *vminor, QQmlType** type_return,
629 QString* url_return, QString *base, QList<QQmlError> *errors)
631 bool typeRecursionDetected = false;
632 for (int i=0; i<imports.count(); ++i) {
633 const Import *import = imports.at(i);
634 if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, url_return,
635 base, &typeRecursionDetected)) {
636 if (qmlCheckTypes()) {
637 // check for type clashes
638 for (int j = i+1; j<imports.count(); ++j) {
639 const Import *import2 = imports.at(j);
640 if (import2->resolveType(typeLoader, type, vmajor, vminor, 0, 0, base)) {
642 QString u1 = import->url;
643 QString u2 = import2->url;
646 int dot = b.lastIndexOf(Dot);
649 QString l = b.left(dot);
650 if (u1.startsWith(b))
651 u1 = u1.mid(b.count());
653 u1 = QQmlImportDatabase::tr("local directory");
654 if (u2.startsWith(b))
655 u2 = u2.mid(b.count());
657 u2 = QQmlImportDatabase::tr("local directory");
663 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
665 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
667 .arg(import->majversion).arg(import->minversion)
668 .arg(import2->majversion).arg(import2->minversion));
670 errors->prepend(error);
681 if (typeRecursionDetected)
682 error.setDescription(QQmlImportDatabase::tr("is instantiated recursively"));
684 error.setDescription(QQmlImportDatabase::tr("is not a type"));
685 errors->prepend(error);
690 QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader)
691 : ref(1), typeLoader(loader) {
694 QQmlImportsPrivate::~QQmlImportsPrivate()
696 while (QQmlImportNamespace *ns = qualifiedSets.takeFirst())
700 QQmlImportNamespace *QQmlImportsPrivate::findQualifiedNamespace(const QHashedStringRef &prefix) const
702 for (QQmlImportNamespace *ns = qualifiedSets.first(); ns; ns = qualifiedSets.next(ns)) {
703 if (prefix == ns->prefix)
710 Import an extension defined by a qmldir file.
712 \a qmldirFilePath is either a raw file path, or a bundle url.
714 bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
716 QQmlImportDatabase *database,
717 const QQmlTypeLoader::QmldirContent *qmldir,
718 QList<QQmlError> *errors)
722 if (qmlImportTrace())
723 qDebug().nospace() << "QQmlImports(" << qPrintable(base) << ")::importExtension: "
724 << "loaded " << qmldirFilePath;
726 if (!database->qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(qmldirFilePath)) {
728 QString qmldirPath = qmldirFilePath;
730 int slash = qmldirPath.lastIndexOf(Slash);
732 qmldirPath.truncate(slash);
734 foreach (const QQmlDirParser::Plugin &plugin, qmldir->plugins()) {
735 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath,
736 plugin.path, plugin.name);
737 if (!resolvedFilePath.isEmpty()) {
738 if (!database->importPlugin(resolvedFilePath, uri, qmldir->typeNamespace(), errors)) {
740 // XXX TODO: should we leave the import plugin error alone?
741 // Here, we pop it off the top and coalesce it into this error's message.
742 // The reason is that the lower level may add url and line/column numbering information.
743 QQmlError poppedError = errors->takeFirst();
745 error.setDescription(tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
746 error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
747 errors->prepend(error);
754 error.setDescription(tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
755 error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
756 errors->prepend(error);
762 database->qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(qmldirFilePath);
768 bool QQmlImportsPrivate::getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
769 const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors)
774 *qmldir = typeLoader->qmldirContent(qmldirIdentifier, uri);
776 // Ensure that parsing was successful
777 if ((*qmldir)->hasError()) {
780 if (QQmlFile::isBundle(qmldirIdentifier))
781 url = QUrl(qmldirIdentifier);
783 url = QUrl::fromLocalFile(qmldirIdentifier);
785 const QList<QQmlError> qmldirErrors = (*qmldir)->errors(uri);
786 for (int i = 0; i < qmldirErrors.size(); ++i) {
787 QQmlError error = qmldirErrors.at(i);
789 errors->append(error);
798 QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
800 struct I { static bool greaterThan(const QString &s1, const QString &s2) {
804 QString dir = dir_arg;
805 if (dir.endsWith(Slash) || dir.endsWith(Backslash))
808 QStringList paths = database->fileImportPath;
809 qSort(paths.begin(), paths.end(), I::greaterThan); // Ensure subdirs preceed their parents.
811 QString stableRelativePath = dir;
812 foreach(const QString &path, paths) {
813 if (dir.startsWith(path)) {
814 stableRelativePath = dir.mid(path.length()+1);
819 stableRelativePath.replace(Backslash, Slash);
821 // remove optional versioning in dot notation from uri
822 int lastSlash = stableRelativePath.lastIndexOf(Slash);
823 if (lastSlash >= 0) {
824 int versionDot = stableRelativePath.indexOf(Dot, lastSlash);
826 stableRelativePath = stableRelativePath.left(versionDot);
829 stableRelativePath.replace(Slash, Dot);
831 return stableRelativePath;
835 Locates the qmldir file for \a uri version \a vmaj.vmin. Returns true if found,
836 and fills in outQmldirFilePath and outQmldirUrl appropriately. Otherwise returns
839 bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database,
840 QString *outQmldirFilePath, QString *outQmldirPathUrl)
842 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
846 QQmlImportDatabase::QmldirCache *cacheHead = 0;
848 QQmlImportDatabase::QmldirCache **cachePtr = database->qmldirCache.value(uri);
850 cacheHead = *cachePtr;
851 QQmlImportDatabase::QmldirCache *cache = cacheHead;
853 if (cache->versionMajor == vmaj && cache->versionMinor == vmin) {
854 *outQmldirFilePath = cache->qmldirFilePath;
855 *outQmldirPathUrl = cache->qmldirPathUrl;
856 return !cache->qmldirFilePath.isEmpty();
863 QQmlTypeLoader &typeLoader = QQmlEnginePrivate::get(database->engine)->typeLoader;
865 QStringList localImportPaths = database->importPathList(QQmlImportDatabase::Local);
867 // Search local import paths for a matching version
868 for (int version = QQmlImports::FullyVersioned; version <= QQmlImports::Unversioned; ++version) {
869 foreach (const QString &path, localImportPaths) {
870 QString qmldirPath = QQmlImports::completeQmldirPath(uri, path, vmaj, vmin, static_cast<QQmlImports::ImportVersion>(version));
872 QString absoluteFilePath = typeLoader.absoluteFilePath(qmldirPath);
873 if (!absoluteFilePath.isEmpty()) {
875 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
876 if (absolutePath.at(0) == Colon)
877 url = QLatin1String("qrc://") + absolutePath.mid(1);
879 url = QUrl::fromLocalFile(absolutePath).toString();
881 QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
882 cache->versionMajor = vmaj;
883 cache->versionMinor = vmin;
884 cache->qmldirFilePath = absoluteFilePath;
885 cache->qmldirPathUrl = url;
886 cache->next = cacheHead;
887 database->qmldirCache.insert(uri, cache);
889 *outQmldirFilePath = absoluteFilePath;
890 *outQmldirPathUrl = url;
897 QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
898 cache->versionMajor = vmaj;
899 cache->versionMinor = vmin;
900 cache->next = cacheHead;
901 database->qmldirCache.insert(uri, cache);
906 bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin,
907 QList<QQmlError> *errors)
909 int lowest_min = INT_MAX;
910 int highest_min = INT_MIN;
912 typedef QQmlDirComponents::const_iterator ConstIterator;
913 const QQmlDirComponents &components = qmldir->components();
915 ConstIterator cend = components.constEnd();
916 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
917 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
918 if ((cit2->typeName == cit->typeName) &&
919 (cit2->majorVersion == cit->majorVersion) &&
920 (cit2->minorVersion == cit->minorVersion)) {
921 // This entry clashes with a predecessor
923 error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
924 .arg(cit->typeName).arg(cit->majorVersion).arg(cit->minorVersion).arg(uri));
925 errors->prepend(error);
930 if (cit->majorVersion == vmaj) {
931 lowest_min = qMin(lowest_min, cit->minorVersion);
932 highest_min = qMax(highest_min, cit->minorVersion);
936 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
937 const QQmlDirScripts &scripts = qmldir->scripts();
939 SConstIterator send = scripts.constEnd();
940 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
941 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
942 if ((sit2->nameSpace == sit->nameSpace) &&
943 (sit2->majorVersion == sit->majorVersion) &&
944 (sit2->minorVersion == sit->minorVersion)) {
945 // This entry clashes with a predecessor
947 error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
948 .arg(sit->nameSpace).arg(sit->majorVersion).arg(sit->minorVersion).arg(uri));
949 errors->prepend(error);
954 if (sit->majorVersion == vmaj) {
955 lowest_min = qMin(lowest_min, sit->minorVersion);
956 highest_min = qMax(highest_min, sit->minorVersion);
960 if (lowest_min > vmin || highest_min < vmin) {
962 error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
963 errors->prepend(error);
970 QQmlImportNamespace *QQmlImportsPrivate::importNamespace(const QString &prefix) const
972 QQmlImportNamespace *nameSpace = 0;
974 if (prefix.isEmpty()) {
975 nameSpace = &unqualifiedset;
977 nameSpace = findQualifiedNamespace(prefix);
980 nameSpace = new QQmlImportNamespace;
981 nameSpace->prefix = prefix;
982 qualifiedSets.append(nameSpace);
989 QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace,
990 const QString &uri, const QString &url, int vmaj, int vmin,
991 QQmlScript::Import::Type type,
992 QList<QQmlError> *errors)
996 Q_ASSERT(url.isEmpty() || url.endsWith(Slash));
998 QQmlImportNamespace::Import *import = new QQmlImportNamespace::Import;
1001 import->majversion = vmaj;
1002 import->minversion = vmin;
1003 import->isLibrary = (type == QQmlScript::Import::Library);
1005 nameSpace->imports.prepend(import);
1009 bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &prefix,
1010 int vmaj, int vmin, const QString &qmldirIdentifier, const QString &qmldirUrl, bool incomplete,
1011 QQmlImportDatabase *database,
1012 QList<QQmlError> *errors)
1017 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1018 Q_ASSERT(nameSpace);
1020 QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, uri, qmldirUrl, vmaj, vmin, QQmlScript::Import::Library, errors);
1024 const QQmlTypeLoader::QmldirContent *qmldir = 0;
1026 if (!qmldirIdentifier.isEmpty()) {
1027 if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
1031 if (!importExtension(qmldir->pluginLocation(), uri, database, qmldir, errors))
1034 if (!inserted->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors))
1039 // Ensure that we are actually providing something
1040 if ((vmaj < 0) || (vmin < 0) || !QQmlMetaType::isModule(uri, vmaj, vmin)) {
1041 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1043 if (QQmlMetaType::isAnyModule(uri))
1044 error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
1046 error.setDescription(tr("module \"%1\" is not installed").arg(uri));
1047 errors->prepend(error);
1049 } else if ((vmaj >= 0) && (vmin >= 0) && qmldir) {
1050 // Verify that the qmldir content is valid for this version
1051 if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors))
1060 bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix,
1062 bool isImplicitImport, bool incomplete, QQmlImportDatabase *database,
1063 QList<QQmlError> *errors)
1067 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1068 Q_ASSERT(nameSpace);
1070 // The uri for this import. For library imports this is the same as uri
1071 // specified by the user, but it may be different in the case of file imports.
1072 QString importUri = uri;
1074 QString qmldirPath = importUri;
1075 if (importUri.endsWith(Slash))
1076 qmldirPath += String_qmldir;
1078 qmldirPath += Slash_qmldir;
1080 QString qmldirUrl = resolveLocalUrl(base, qmldirPath);
1082 QString qmldirIdentifier;
1084 if (QQmlFile::isBundle(qmldirUrl)) {
1086 QString dir = resolveLocalUrl(base, importUri);
1087 Q_ASSERT(QQmlFile::isBundle(dir));
1088 if (!QQmlFile::bundleDirectoryExists(dir, typeLoader->engine())) {
1089 if (!isImplicitImport) {
1091 error.setDescription(tr("\"%1\": no such directory").arg(uri));
1092 error.setUrl(QUrl(qmldirUrl));
1093 errors->prepend(error);
1098 // Transforms the (possible relative) uri into our best guess relative to the
1100 importUri = resolvedUri(dir, database);
1101 if (importUri.endsWith(Slash))
1104 if (QQmlFile::bundleFileExists(qmldirUrl, typeLoader->engine()))
1105 qmldirIdentifier = qmldirUrl;
1107 } else if (QQmlFile::isLocalFile(qmldirUrl)) {
1109 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1110 Q_ASSERT(!localFileOrQrc.isEmpty());
1112 QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, importUri));
1113 if (!typeLoader->directoryExists(dir)) {
1114 if (!isImplicitImport) {
1116 error.setDescription(tr("\"%1\": no such directory").arg(uri));
1117 error.setUrl(QUrl(qmldirUrl));
1118 errors->prepend(error);
1123 // Transforms the (possible relative) uri into our best guess relative to the
1125 importUri = resolvedUri(dir, database);
1126 if (importUri.endsWith(Slash))
1129 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty())
1130 qmldirIdentifier = localFileOrQrc;
1132 } else if (nameSpace->prefix.isEmpty() && !incomplete) {
1134 if (!isImplicitImport) {
1136 error.setDescription(tr("import \"%1\" has no qmldir and no namespace").arg(importUri));
1137 error.setUrl(QUrl(qmldirUrl));
1138 errors->prepend(error);
1145 // The url for the path containing files for this import
1146 QString url = resolveLocalUrl(base, uri);
1147 if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1150 QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QQmlScript::Import::File, errors);
1153 if (!incomplete && !qmldirIdentifier.isEmpty()) {
1154 const QQmlTypeLoader::QmldirContent *qmldir = 0;
1155 if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors))
1159 if (!importExtension(qmldir->pluginLocation(), importUri, database, qmldir, errors))
1162 if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors))
1170 bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString &prefix,
1171 const QString &qmldirIdentifier, const QString& qmldirUrl,
1172 QQmlImportDatabase *database, QList<QQmlError> *errors)
1174 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1175 Q_ASSERT(nameSpace);
1177 if (QQmlImportNamespace::Import *import = nameSpace->findImport(uri)) {
1178 const QQmlTypeLoader::QmldirContent *qmldir = 0;
1179 if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
1183 if (!importExtension(qmldir->pluginLocation(), uri, database, qmldir, errors))
1186 if (import->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) {
1187 // Ensure that we are actually providing something
1188 int vmaj = import->majversion;
1189 int vmin = import->minversion;
1191 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1192 // The implicit import qmldir can be empty
1193 if (uri != QLatin1String(".")) {
1195 if (QQmlMetaType::isAnyModule(uri))
1196 error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
1198 error.setDescription(tr("module \"%1\" is not installed").arg(uri));
1199 errors->prepend(error);
1202 } else if ((vmaj >= 0) && (vmin >= 0)) {
1203 // Verify that the qmldir content is valid for this version
1204 if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors))
1212 if (errors->isEmpty()) {
1214 error.setDescription(QQmlTypeLoader::tr("Cannot update qmldir content for '%1'").arg(uri));
1215 errors->prepend(error);
1224 Adds an implicit "." file import. This is equivalent to calling addFileImport(), but error
1225 messages related to the path or qmldir file not existing are suppressed.
1227 bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlError> *errors)
1231 if (qmlImportTrace())
1232 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString())
1233 << ")::addImplicitImport";
1235 bool incomplete = !isLocal(baseUrl());
1236 return d->addFileImport(QLatin1String("."), QString(), -1, -1, true, incomplete, importDb, errors);
1242 Adds information to \a imports such that subsequent calls to resolveType()
1243 will resolve types qualified by \a prefix by considering types found at the given \a uri.
1245 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
1246 added via addImportPath() (if importType is LibraryImport).
1248 The \a prefix may be empty, in which case the import location is considered for
1251 The base URL must already have been set with Import::setBaseUrl().
1253 Optionally, the url the import resolved to can be returned by providing the url parameter.
1254 Not all imports will result in an output url being generated, in which case the url will
1255 be set to an empty string.
1257 Returns true on success, and false on failure. In case of failure, the errors array will
1258 filled appropriately.
1260 bool QQmlImports::addFileImport(QQmlImportDatabase *importDb,
1261 const QString& uri, const QString& prefix, int vmaj, int vmin,
1262 bool incomplete, QList<QQmlError> *errors)
1267 if (qmlImportTrace())
1268 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::addFileImport: "
1269 << uri << ' ' << vmaj << '.' << vmin << " as " << prefix;
1271 return d->addFileImport(uri, prefix, vmaj, vmin, false, incomplete, importDb, errors);
1274 bool QQmlImports::addLibraryImport(QQmlImportDatabase *importDb,
1275 const QString &uri, const QString &prefix, int vmaj, int vmin,
1276 const QString &qmldirIdentifier, const QString& qmldirUrl, bool incomplete, QList<QQmlError> *errors)
1281 if (qmlImportTrace())
1282 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::addLibraryImport: "
1283 << uri << ' ' << vmaj << '.' << vmin << " as " << prefix;
1285 return d->addLibraryImport(uri, prefix, vmaj, vmin, qmldirIdentifier, qmldirUrl, incomplete, importDb, errors);
1288 bool QQmlImports::updateQmldirContent(QQmlImportDatabase *importDb,
1289 const QString &uri, const QString &prefix,
1290 const QString &qmldirIdentifier, const QString& qmldirUrl, QList<QQmlError> *errors)
1295 if (qmlImportTrace())
1296 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::updateQmldirContent: "
1297 << uri << " to " << qmldirUrl << " as " << prefix;
1299 return d->updateQmldirContent(uri, prefix, qmldirIdentifier, qmldirUrl, importDb, errors);
1302 bool QQmlImports::locateQmldir(QQmlImportDatabase *importDb,
1303 const QString& uri, int vmaj, int vmin,
1304 QString *qmldirFilePath, QString *url)
1306 return d->locateQmldir(uri, vmaj, vmin, importDb, qmldirFilePath, url);
1309 bool QQmlImports::isLocal(const QString &url)
1311 return QQmlFile::isBundle(url) || !QQmlFile::urlToLocalFileOrQrc(url).isEmpty();
1314 bool QQmlImports::isLocal(const QUrl &url)
1316 return QQmlFile::isBundle(url) || !QQmlFile::urlToLocalFileOrQrc(url).isEmpty();
1321 \class QQmlImportDatabase
1322 \brief The QQmlImportDatabase class manages the QML imports for a QQmlEngine.
1325 QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
1328 filePluginPath << QLatin1String(".");
1330 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
1332 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
1333 addImportPath(installImportsPath);
1336 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
1337 if (!envImportPath.isEmpty()) {
1338 #if defined(Q_OS_WIN)
1339 QLatin1Char pathSep(';');
1341 QLatin1Char pathSep(':');
1343 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
1344 for (int ii = paths.count() - 1; ii >= 0; --ii)
1345 addImportPath(paths.at(ii));
1348 addImportPath(QCoreApplication::applicationDirPath());
1351 QQmlImportDatabase::~QQmlImportDatabase()
1353 qmldirCache.clear();
1359 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
1360 The \a prefix must contain the dot.
1362 \a qmldirPath is the location of the qmldir file.
1364 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1365 const QString &qmldirPath,
1366 const QString &qmldirPluginPath,
1367 const QString &baseName, const QStringList &suffixes,
1368 const QString &prefix)
1370 QStringList searchPaths = filePluginPath;
1371 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
1372 if (!qmldirPluginPathIsRelative)
1373 searchPaths.prepend(qmldirPluginPath);
1375 foreach (const QString &pluginPath, searchPaths) {
1377 QString resolvedPath;
1378 if (pluginPath == QLatin1String(".")) {
1379 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
1380 resolvedPath = QDir::cleanPath(qmldirPath + Slash + qmldirPluginPath);
1382 resolvedPath = qmldirPath;
1384 if (QDir::isRelativePath(pluginPath))
1385 resolvedPath = QDir::cleanPath(qmldirPath + Slash + pluginPath);
1387 resolvedPath = pluginPath;
1390 // hack for resources, should probably go away
1391 if (resolvedPath.startsWith(Colon))
1392 resolvedPath = QCoreApplication::applicationDirPath();
1394 if (!resolvedPath.endsWith(Slash))
1395 resolvedPath += Slash;
1397 foreach (const QString &suffix, suffixes) {
1398 QString pluginFileName = prefix;
1400 pluginFileName += baseName;
1401 pluginFileName += suffix;
1403 QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
1404 if (!absolutePath.isEmpty())
1405 return absolutePath;
1409 if (qmlImportTrace())
1410 qDebug() << "QQmlImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
1411 << "in" << qmldirPath;
1419 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
1422 \header \li Platform \li Valid suffixes
1423 \row \li Windows \li \c .dll
1424 \row \li Unix/Linux \li \c .so
1425 \row \li AIX \li \c .a
1426 \row \li HP-UX \li \c .sl, \c .so (HP-UXi)
1427 \row \li Mac OS X \li \c .dylib, \c .bundle, \c .so
1430 Version number on unix are ignored.
1432 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1433 const QString &qmldirPath, const QString &qmldirPluginPath,
1434 const QString &baseName)
1436 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
1437 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1440 << QLatin1String("d.dll") // try a qmake-style debug build first
1442 << QLatin1String(".dll"));
1445 # if defined(Q_OS_DARWIN)
1447 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1450 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
1451 << QLatin1String(".dylib")
1453 << QLatin1String(".dylib")
1454 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
1456 << QLatin1String(".so")
1457 << QLatin1String(".bundle"),
1458 QLatin1String("lib"));
1459 # else // Generic Unix
1460 QStringList validSuffixList;
1462 # if defined(Q_OS_HPUX)
1464 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
1465 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
1466 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
1468 validSuffixList << QLatin1String(".sl");
1470 validSuffixList << QLatin1String(".so");
1472 # elif defined(Q_OS_AIX)
1473 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
1474 # elif defined(Q_OS_UNIX)
1475 validSuffixList << QLatin1String(".so");
1478 // Examples of valid library names:
1481 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
1490 QStringList QQmlImportDatabase::pluginPathList() const
1492 return filePluginPath;
1498 void QQmlImportDatabase::setPluginPathList(const QStringList &paths)
1500 if (qmlImportTrace())
1501 qDebug().nospace() << "QQmlImportDatabase::setPluginPathList: " << paths;
1503 filePluginPath = paths;
1509 void QQmlImportDatabase::addPluginPath(const QString& path)
1511 if (qmlImportTrace())
1512 qDebug().nospace() << "QQmlImportDatabase::addPluginPath: " << path;
1514 QUrl url = QUrl(path);
1515 if (url.isRelative() || url.scheme() == QLatin1String("file")
1516 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1517 QDir dir = QDir(path);
1518 filePluginPath.prepend(dir.canonicalPath());
1520 filePluginPath.prepend(path);
1527 void QQmlImportDatabase::addImportPath(const QString& path)
1529 if (qmlImportTrace())
1530 qDebug().nospace() << "QQmlImportDatabase::addImportPath: " << path;
1535 QUrl url = QUrl(path);
1538 if (url.scheme() == QLatin1String("file")) {
1539 cPath = QQmlFile::urlToLocalFileOrQrc(url);
1540 } else if (path.startsWith(QLatin1Char(':'))) {
1541 // qrc directory, e.g. :/foo
1542 // need to convert to a qrc url, e.g. qrc:/foo
1543 cPath = QStringLiteral("qrc") + path;
1544 cPath.replace(Backslash, Slash);
1545 } else if (url.isRelative() ||
1546 (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1547 QDir dir = QDir(path);
1548 cPath = dir.canonicalPath();
1551 cPath.replace(Backslash, Slash);
1554 if (!cPath.isEmpty()
1555 && !fileImportPath.contains(cPath))
1556 fileImportPath.prepend(cPath);
1562 QStringList QQmlImportDatabase::importPathList(PathType type) const
1564 if (type == LocalOrRemote)
1565 return fileImportPath;
1568 foreach (const QString &path, fileImportPath) {
1569 bool localPath = isPathAbsolute(path) || QQmlFile::isLocalFile(path);
1570 if (localPath == (type == Local))
1580 void QQmlImportDatabase::setImportPathList(const QStringList &paths)
1582 if (qmlImportTrace())
1583 qDebug().nospace() << "QQmlImportDatabase::setImportPathList: " << paths;
1585 fileImportPath = paths;
1587 // Our existing cached paths may have been invalidated
1588 qmldirCache.clear();
1594 bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &uri, const QString &typeNamespace, QList<QQmlError> *errors)
1596 if (qmlImportTrace())
1597 qDebug().nospace() << "QQmlImportDatabase::importPlugin: " << uri << " from " << filePath;
1599 #ifndef QT_NO_LIBRARY
1600 QFileInfo fileInfo(filePath);
1601 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1603 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1604 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1606 if (typesRegistered) {
1607 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1608 "QQmlImportDatabase::importPlugin",
1609 "Internal error: Plugin imported previously with different uri");
1612 if (!engineInitialized || !typesRegistered) {
1613 if (!QQml_isFileCaseCorrect(absoluteFilePath)) {
1616 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1617 errors->prepend(error);
1621 QPluginLoader loader(absoluteFilePath);
1623 if (!loader.load()) {
1626 error.setDescription(loader.errorString());
1627 errors->prepend(error);
1632 QObject *instance = loader.instance();
1633 if (QQmlTypesExtensionInterface *iface = qobject_cast<QQmlExtensionInterface *>(instance)) {
1635 const QByteArray bytes = uri.toUtf8();
1636 const char *moduleId = bytes.constData();
1637 if (!typesRegistered) {
1639 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1641 QStringList registrationFailures;
1644 QWriteLocker lock(QQmlMetaType::typeRegistrationLock());
1646 if (!typeNamespace.isEmpty()) {
1647 // This is a 'strict' module
1648 if (typeNamespace != uri) {
1649 // The namespace for type registrations must match the URI for locating the module
1651 error.setDescription(tr("Module namespace '%1' does not match import URI '%2'").arg(typeNamespace).arg(uri));
1652 errors->prepend(error);
1656 if (QQmlMetaType::namespaceContainsRegistrations(typeNamespace)) {
1657 // Other modules have already installed to this namespace
1659 error.setDescription(tr("Namespace '%1' has already been used for type registration").arg(typeNamespace));
1660 errors->prepend(error);
1663 QQmlMetaType::protectNamespace(typeNamespace);
1666 // This is not a stict module - provide a warning
1667 qWarning().nospace() << qPrintable(tr("Module '%1' does not contain a module directive - it cannot be protected from external registrations.").arg(uri));
1670 QQmlMetaType::setTypeRegistrationNamespace(typeNamespace);
1672 iface->registerTypes(moduleId);
1674 registrationFailures = QQmlMetaType::typeRegistrationFailures();
1675 QQmlMetaType::setTypeRegistrationNamespace(QString());
1678 if (!registrationFailures.isEmpty()) {
1679 foreach (const QString &failure, registrationFailures) {
1681 error.setDescription(failure);
1682 errors->prepend(error);
1687 if (!engineInitialized) {
1688 // things on the engine (eg. adding new global objects) have to be done for every
1690 // XXX protect against double initialization
1691 initializedPlugins.insert(absoluteFilePath);
1693 QQmlExtensionInterface *eiface =
1694 qobject_cast<QQmlExtensionInterface *>(instance);
1696 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1697 ep->typeLoader.initializeEngine(eiface, moduleId);
1703 error.setDescription(loader.errorString());
1704 errors->prepend(error);