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) 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));
305 for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(ns)) {
307 const QQmlImportNamespace &set = *ns;
309 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
310 const QQmlImportNamespace::Import *import = set.imports.at(ii);
311 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion);
313 QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[set.prefix];
314 typeimport.modules.append(QQmlTypeModuleVersion(module, import->minversion));
320 QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
322 QList<QQmlImports::ScriptReference> scripts;
324 const QQmlImportNamespace &set = d->unqualifiedset;
326 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
327 const QQmlImportNamespace::Import *import = set.imports.at(ii);
329 foreach (const QQmlDirParser::Script &script, import->qmlDirScripts) {
331 ref.nameSpace = script.nameSpace;
332 ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
337 for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(ns)) {
338 const QQmlImportNamespace &set = *ns;
340 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
341 const QQmlImportNamespace::Import *import = set.imports.at(ii);
343 foreach (const QQmlDirParser::Script &script, import->qmlDirScripts) {
345 ref.nameSpace = script.nameSpace;
346 ref.qualifier = set.prefix;
347 ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
357 Form a complete path to a qmldir file, from a base URL, a module URI and version specification.
359 QString QQmlImports::completeQmldirPath(const QString &uri, const QString &base, int vmaj, int vmin,
360 ImportVersion version)
363 url.replace(Dot, Slash);
366 if (!dir.endsWith(Slash) && !dir.endsWith(Backslash))
370 if (version == QQmlImports::FullyVersioned) {
371 // extension with fully encoded version number (eg. MyModule.3.2)
372 dir += QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin);
373 } else if (version == QQmlImports::PartiallyVersioned) {
374 // extension with encoded version major (eg. MyModule.3)
375 dir += QString(QLatin1String(".%1")).arg(vmaj);
376 } // else extension without version number (eg. MyModule)
378 return dir + Slash_qmldir;
384 The given (namespace qualified) \a type is resolved to either
386 \li a QQmlImportNamespace stored at \a ns_return,
387 \li a QQmlType stored at \a type_return, or
388 \li a component located at \a url_return.
391 If any return pointer is 0, the corresponding search is not done.
393 \sa addFileImport(), addLibraryImport
395 bool QQmlImports::resolveType(const QHashedStringRef &type,
396 QQmlType** type_return, QString* url_return, int *vmaj, int *vmin,
397 QQmlImportNamespace** ns_return, QList<QQmlError> *errors) const
399 QQmlImportNamespace* ns = d->findQualifiedNamespace(type);
405 if (type_return || url_return) {
406 if (d->resolveType(type,vmaj,vmin,type_return,url_return, errors)) {
407 if (qmlImportTrace()) {
408 #define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \
409 << ')' << "::resolveType: " << type.toString() << " => "
411 if (type_return && *type_return && url_return && !url_return->isEmpty())
412 RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << ' ' << *url_return << " TYPE/URL";
413 if (type_return && *type_return)
414 RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << " TYPE";
415 if (url_return && !url_return->isEmpty())
416 RESOLVE_TYPE_DEBUG << *url_return << " URL";
418 #undef RESOLVE_TYPE_DEBUG
426 bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors)
428 Q_ASSERT(resolvedUrl.endsWith(Slash));
431 qmlDirComponents = qmldir->components();
433 const QQmlDirScripts &scripts = qmldir->scripts();
434 if (!scripts.isEmpty()) {
435 // Verify that we haven't imported these scripts already
436 for (QList<QQmlImportNamespace::Import *>::const_iterator it = nameSpace->imports.constBegin();
437 it != nameSpace->imports.constEnd(); ++it) {
438 if ((*it != this) && ((*it)->uri == uri)) {
440 error.setDescription(QQmlImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg((*it)->url));
441 errors->prepend(error);
446 qmlDirScripts = getVersionedScripts(scripts, majversion, minversion);
452 QQmlDirScripts QQmlImportNamespace::Import::getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin)
454 QMap<QString, QQmlDirParser::Script> versioned;
456 for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
457 sit != qmldirscripts.constEnd(); ++sit) {
458 // Only include scripts that match our requested version
459 if (((vmaj == -1) || (sit->majorVersion == vmaj)) &&
460 ((vmin == -1) || (sit->minorVersion <= vmin))) {
461 // Load the highest version that matches
462 QMap<QString, QQmlDirParser::Script>::iterator vit = versioned.find(sit->nameSpace);
463 if (vit == versioned.end() || (vit->minorVersion < sit->minorVersion)) {
464 versioned.insert(sit->nameSpace, *sit);
469 return versioned.values();
475 Searching \e only in the namespace \a ns (previously returned in a call to
476 resolveType(), \a type is found and returned to either
477 a QQmlType stored at \a type_return, or
478 a component located at \a url_return.
480 If either return pointer is 0, the corresponding search is not done.
482 bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QHashedStringRef &type,
483 QQmlType** type_return, QString* url_return,
484 int *vmaj, int *vmin) const
486 return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return,url_return);
489 bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader,
490 const QHashedStringRef& type, int *vmajor, int *vminor,
491 QQmlType** type_return, QString* url_return,
492 QString *base, bool *typeRecursionDetected) const
494 if (majversion >= 0 && minversion >= 0) {
495 QQmlType *t = QQmlMetaType::qmlType(type, uri, majversion, minversion);
497 if (vmajor) *vmajor = majversion;
498 if (vminor) *vminor = minversion;
505 QQmlDirComponents::ConstIterator it = qmlDirComponents.find(type), end = qmlDirComponents.end();
507 QString componentUrl;
508 QQmlDirComponents::ConstIterator candidate = end;
509 for ( ; it != end && it.key() == type; ++it) {
510 const QQmlDirParser::Component &c = *it;
512 // importing version -1 means import ALL versions
513 if ((majversion == -1) ||
514 (c.majorVersion == majversion && c.minorVersion <= minversion)) {
515 // Is this better than the previous candidate?
516 if ((candidate == end) ||
517 (c.majorVersion > candidate->majorVersion) ||
518 ((c.majorVersion == candidate->majorVersion) && (c.minorVersion > candidate->minorVersion))) {
519 componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
520 if (c.internal && base) {
521 if (resolveLocalUrl(*base, c.fileName) != componentUrl)
522 continue; // failed attempt to access an internal type
524 if (base && (*base == componentUrl)) {
525 if (typeRecursionDetected)
526 *typeRecursionDetected = true;
527 continue; // no recursion
530 // This is our best candidate so far
536 if (candidate != end) {
538 *url_return = componentUrl;
541 } else if (!isLibrary) {
542 QString qmlUrl = url + QString::fromRawData(type.constData(), type.length()) + dotqml_string;
546 if (QQmlFile::isBundle(qmlUrl)) {
547 exists = QQmlFile::bundleFileExists(qmlUrl, typeLoader->engine());
549 exists = !typeLoader->absoluteFilePath(QQmlFile::urlToLocalFileOrQrc(qmlUrl)).isEmpty();
553 if (base && (*base == qmlUrl)) { // no recursion
554 if (typeRecursionDetected)
555 *typeRecursionDetected = true;
558 *url_return = qmlUrl;
567 bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, int *vminor,
568 QQmlType** type_return, QString* url_return,
569 QList<QQmlError> *errors)
571 QQmlImportNamespace *s = 0;
572 int dot = type.indexOf(Dot);
574 QHashedStringRef namespaceName(type.constData(), dot);
575 s = findQualifiedNamespace(namespaceName);
579 error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName.toString()));
580 errors->prepend(error);
584 int ndot = type.indexOf(Dot,dot+1);
588 error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
589 errors->prepend(error);
596 QHashedStringRef unqualifiedtype = dot < 0 ? type : QHashedStringRef(type.constData()+dot+1, type.length()-dot-1);
598 if (s->resolveType(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
600 if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && url_return && s != &unqualifiedset) {
601 // qualified, and only 1 url
602 *url_return = resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml"));
610 QQmlImportNamespace::Import *QQmlImportNamespace::findImport(const QString &uri)
612 for (QList<Import *>::iterator it = imports.begin(), end = imports.end(); it != end; ++it) {
613 if ((*it)->uri == uri)
620 bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type,
621 int *vmajor, int *vminor, QQmlType** type_return,
622 QString* url_return, QString *base, QList<QQmlError> *errors)
624 bool typeRecursionDetected = false;
625 for (int i=0; i<imports.count(); ++i) {
626 const Import *import = imports.at(i);
627 if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, url_return,
628 base, &typeRecursionDetected)) {
629 if (qmlCheckTypes()) {
630 // check for type clashes
631 for (int j = i+1; j<imports.count(); ++j) {
632 const Import *import2 = imports.at(j);
633 if (import2->resolveType(typeLoader, type, vmajor, vminor, 0, 0, base)) {
635 QString u1 = import->url;
636 QString u2 = import2->url;
639 int dot = b.lastIndexOf(Dot);
642 QString l = b.left(dot);
643 if (u1.startsWith(b))
644 u1 = u1.mid(b.count());
646 u1 = QQmlImportDatabase::tr("local directory");
647 if (u2.startsWith(b))
648 u2 = u2.mid(b.count());
650 u2 = QQmlImportDatabase::tr("local directory");
656 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
658 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
660 .arg(import->majversion).arg(import->minversion)
661 .arg(import2->majversion).arg(import2->minversion));
663 errors->prepend(error);
674 if (typeRecursionDetected)
675 error.setDescription(QQmlImportDatabase::tr("is instantiated recursively"));
677 error.setDescription(QQmlImportDatabase::tr("is not a type"));
678 errors->prepend(error);
683 QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader)
684 : ref(1), typeLoader(loader) {
687 QQmlImportsPrivate::~QQmlImportsPrivate()
689 while (QQmlImportNamespace *ns = qualifiedSets.takeFirst())
693 QQmlImportNamespace *QQmlImportsPrivate::findQualifiedNamespace(const QHashedStringRef &prefix) const
695 for (QQmlImportNamespace *ns = qualifiedSets.first(); ns; ns = qualifiedSets.next(ns)) {
696 if (prefix == ns->prefix)
703 Import an extension defined by a qmldir file.
705 \a qmldirFilePath is either a raw file path, or a bundle url.
707 bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
709 QQmlImportDatabase *database,
710 const QQmlTypeLoader::QmldirContent *qmldir,
711 QList<QQmlError> *errors)
715 if (qmlImportTrace())
716 qDebug().nospace() << "QQmlImports(" << qPrintable(base) << ")::importExtension: "
717 << "loaded " << qmldirFilePath;
719 if (!database->qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(qmldirFilePath)) {
721 QString qmldirPath = qmldirFilePath;
723 int slash = qmldirPath.lastIndexOf(Slash);
725 qmldirPath.truncate(slash);
727 foreach (const QQmlDirParser::Plugin &plugin, qmldir->plugins()) {
728 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath,
729 plugin.path, plugin.name);
730 if (!resolvedFilePath.isEmpty()) {
731 if (!database->importPlugin(resolvedFilePath, uri, qmldir->typeNamespace(), errors)) {
733 // XXX TODO: should we leave the import plugin error alone?
734 // Here, we pop it off the top and coalesce it into this error's message.
735 // The reason is that the lower level may add url and line/column numbering information.
736 QQmlError poppedError = errors->takeFirst();
738 error.setDescription(tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
739 error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
740 errors->prepend(error);
747 error.setDescription(tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
748 error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
749 errors->prepend(error);
755 database->qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(qmldirFilePath);
761 bool QQmlImportsPrivate::getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
762 const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors)
767 *qmldir = typeLoader->qmldirContent(qmldirIdentifier, uri);
769 // Ensure that parsing was successful
770 if ((*qmldir)->hasError()) {
773 if (QQmlFile::isBundle(qmldirIdentifier))
774 url = QUrl(qmldirIdentifier);
776 url = QUrl::fromLocalFile(qmldirIdentifier);
778 const QList<QQmlError> qmldirErrors = (*qmldir)->errors(uri);
779 for (int i = 0; i < qmldirErrors.size(); ++i) {
780 QQmlError error = qmldirErrors.at(i);
782 errors->append(error);
791 QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
793 struct I { static bool greaterThan(const QString &s1, const QString &s2) {
797 QString dir = dir_arg;
798 if (dir.endsWith(Slash) || dir.endsWith(Backslash))
801 QStringList paths = database->fileImportPath;
802 qSort(paths.begin(), paths.end(), I::greaterThan); // Ensure subdirs preceed their parents.
804 QString stableRelativePath = dir;
805 foreach(const QString &path, paths) {
806 if (dir.startsWith(path)) {
807 stableRelativePath = dir.mid(path.length()+1);
812 stableRelativePath.replace(Backslash, Slash);
814 // remove optional versioning in dot notation from uri
815 int lastSlash = stableRelativePath.lastIndexOf(Slash);
816 if (lastSlash >= 0) {
817 int versionDot = stableRelativePath.indexOf(Dot, lastSlash);
819 stableRelativePath = stableRelativePath.left(versionDot);
822 stableRelativePath.replace(Slash, Dot);
824 return stableRelativePath;
828 Locates the qmldir file for \a uri version \a vmaj.vmin. Returns true if found,
829 and fills in outQmldirFilePath and outQmldirUrl appropriately. Otherwise returns
832 bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database,
833 QString *outQmldirFilePath, QString *outQmldirPathUrl)
835 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
839 QQmlImportDatabase::QmldirCache *cacheHead = 0;
841 QQmlImportDatabase::QmldirCache **cachePtr = database->qmldirCache.value(uri);
843 cacheHead = *cachePtr;
844 QQmlImportDatabase::QmldirCache *cache = cacheHead;
846 if (cache->versionMajor == vmaj && cache->versionMinor == vmin) {
847 *outQmldirFilePath = cache->qmldirFilePath;
848 *outQmldirPathUrl = cache->qmldirPathUrl;
849 return !cache->qmldirFilePath.isEmpty();
856 QQmlTypeLoader &typeLoader = QQmlEnginePrivate::get(database->engine)->typeLoader;
858 QStringList localImportPaths = database->importPathList(QQmlImportDatabase::Local);
860 // Search local import paths for a matching version
861 for (int version = QQmlImports::FullyVersioned; version <= QQmlImports::Unversioned; ++version) {
862 foreach (const QString &path, localImportPaths) {
863 QString qmldirPath = QQmlImports::completeQmldirPath(uri, path, vmaj, vmin, static_cast<QQmlImports::ImportVersion>(version));
865 QString absoluteFilePath = typeLoader.absoluteFilePath(qmldirPath);
866 if (!absoluteFilePath.isEmpty()) {
868 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
869 if (absolutePath.at(0) == Colon)
870 url = QLatin1String("qrc://") + absolutePath.mid(1);
872 url = QUrl::fromLocalFile(absolutePath).toString();
874 QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
875 cache->versionMajor = vmaj;
876 cache->versionMinor = vmin;
877 cache->qmldirFilePath = absoluteFilePath;
878 cache->qmldirPathUrl = url;
879 cache->next = cacheHead;
880 database->qmldirCache.insert(uri, cache);
882 *outQmldirFilePath = absoluteFilePath;
883 *outQmldirPathUrl = url;
890 QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
891 cache->versionMajor = vmaj;
892 cache->versionMinor = vmin;
893 cache->next = cacheHead;
894 database->qmldirCache.insert(uri, cache);
899 bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin,
900 QList<QQmlError> *errors)
902 int lowest_min = INT_MAX;
903 int highest_min = INT_MIN;
905 typedef QQmlDirComponents::const_iterator ConstIterator;
906 const QQmlDirComponents &components = qmldir->components();
908 ConstIterator cend = components.constEnd();
909 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
910 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
911 if ((cit2->typeName == cit->typeName) &&
912 (cit2->majorVersion == cit->majorVersion) &&
913 (cit2->minorVersion == cit->minorVersion)) {
914 // This entry clashes with a predecessor
916 error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
917 .arg(cit->typeName).arg(cit->majorVersion).arg(cit->minorVersion).arg(uri));
918 errors->prepend(error);
923 if (cit->majorVersion == vmaj) {
924 lowest_min = qMin(lowest_min, cit->minorVersion);
925 highest_min = qMax(highest_min, cit->minorVersion);
929 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
930 const QQmlDirScripts &scripts = qmldir->scripts();
932 SConstIterator send = scripts.constEnd();
933 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
934 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
935 if ((sit2->nameSpace == sit->nameSpace) &&
936 (sit2->majorVersion == sit->majorVersion) &&
937 (sit2->minorVersion == sit->minorVersion)) {
938 // This entry clashes with a predecessor
940 error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
941 .arg(sit->nameSpace).arg(sit->majorVersion).arg(sit->minorVersion).arg(uri));
942 errors->prepend(error);
947 if (sit->majorVersion == vmaj) {
948 lowest_min = qMin(lowest_min, sit->minorVersion);
949 highest_min = qMax(highest_min, sit->minorVersion);
953 if (lowest_min > vmin || highest_min < vmin) {
955 error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
956 errors->prepend(error);
963 QQmlImportNamespace *QQmlImportsPrivate::importNamespace(const QString &prefix) const
965 QQmlImportNamespace *nameSpace = 0;
967 if (prefix.isEmpty()) {
968 nameSpace = &unqualifiedset;
970 nameSpace = findQualifiedNamespace(prefix);
973 nameSpace = new QQmlImportNamespace;
974 nameSpace->prefix = prefix;
975 qualifiedSets.append(nameSpace);
982 QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace,
983 const QString &uri, const QString &url, int vmaj, int vmin,
984 QQmlScript::Import::Type type,
985 QList<QQmlError> *errors)
989 Q_ASSERT(url.isEmpty() || url.endsWith(Slash));
991 QQmlImportNamespace::Import *import = new QQmlImportNamespace::Import;
994 import->majversion = vmaj;
995 import->minversion = vmin;
996 import->isLibrary = (type == QQmlScript::Import::Library);
998 nameSpace->imports.prepend(import);
1002 bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &prefix,
1003 int vmaj, int vmin, const QString &qmldirIdentifier, const QString &qmldirUrl, bool incomplete,
1004 QQmlImportDatabase *database,
1005 QList<QQmlError> *errors)
1010 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1011 Q_ASSERT(nameSpace);
1013 QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, uri, qmldirUrl, vmaj, vmin, QQmlScript::Import::Library, errors);
1017 const QQmlTypeLoader::QmldirContent *qmldir = 0;
1019 if (!qmldirIdentifier.isEmpty()) {
1020 if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
1024 if (!importExtension(qmldir->pluginLocation(), uri, database, qmldir, errors))
1027 if (!inserted->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors))
1032 // Ensure that we are actually providing something
1033 if ((vmaj < 0) || (vmin < 0) || !QQmlMetaType::isModule(uri, vmaj, vmin)) {
1034 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1036 if (QQmlMetaType::isAnyModule(uri))
1037 error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
1039 error.setDescription(tr("module \"%1\" is not installed").arg(uri));
1040 errors->prepend(error);
1042 } else if ((vmaj >= 0) && (vmin >= 0) && qmldir) {
1043 // Verify that the qmldir content is valid for this version
1044 if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors))
1053 bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix,
1055 bool isImplicitImport, bool incomplete, QQmlImportDatabase *database,
1056 QList<QQmlError> *errors)
1060 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1061 Q_ASSERT(nameSpace);
1063 // The uri for this import. For library imports this is the same as uri
1064 // specified by the user, but it may be different in the case of file imports.
1065 QString importUri = uri;
1067 QString qmldirPath = importUri;
1068 if (importUri.endsWith(Slash))
1069 qmldirPath += String_qmldir;
1071 qmldirPath += Slash_qmldir;
1073 QString qmldirUrl = resolveLocalUrl(base, qmldirPath);
1075 QString qmldirIdentifier;
1077 if (QQmlFile::isBundle(qmldirUrl)) {
1079 QString dir = resolveLocalUrl(base, importUri);
1080 Q_ASSERT(QQmlFile::isBundle(dir));
1081 if (!QQmlFile::bundleDirectoryExists(dir, typeLoader->engine())) {
1082 if (!isImplicitImport) {
1084 error.setDescription(tr("\"%1\": no such directory").arg(uri));
1085 error.setUrl(QUrl(qmldirUrl));
1086 errors->prepend(error);
1091 // Transforms the (possible relative) uri into our best guess relative to the
1093 importUri = resolvedUri(dir, database);
1094 if (importUri.endsWith(Slash))
1097 if (QQmlFile::bundleFileExists(qmldirUrl, typeLoader->engine()))
1098 qmldirIdentifier = qmldirUrl;
1100 } else if (QQmlFile::isLocalFile(qmldirUrl)) {
1102 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1103 Q_ASSERT(!localFileOrQrc.isEmpty());
1105 QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, importUri));
1106 if (!typeLoader->directoryExists(dir)) {
1107 if (!isImplicitImport) {
1109 error.setDescription(tr("\"%1\": no such directory").arg(uri));
1110 error.setUrl(QUrl(qmldirUrl));
1111 errors->prepend(error);
1116 // Transforms the (possible relative) uri into our best guess relative to the
1118 importUri = resolvedUri(dir, database);
1119 if (importUri.endsWith(Slash))
1122 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty())
1123 qmldirIdentifier = localFileOrQrc;
1125 } else if (nameSpace->prefix.isEmpty() && !incomplete) {
1127 if (!isImplicitImport) {
1129 error.setDescription(tr("import \"%1\" has no qmldir and no namespace").arg(importUri));
1130 error.setUrl(QUrl(qmldirUrl));
1131 errors->prepend(error);
1138 // The url for the path containing files for this import
1139 QString url = resolveLocalUrl(base, uri);
1140 if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1143 QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QQmlScript::Import::File, errors);
1146 if (!incomplete && !qmldirIdentifier.isEmpty()) {
1147 const QQmlTypeLoader::QmldirContent *qmldir = 0;
1148 if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors))
1152 if (!importExtension(qmldir->pluginLocation(), importUri, database, qmldir, errors))
1155 if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors))
1163 bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString &prefix,
1164 const QString &qmldirIdentifier, const QString& qmldirUrl,
1165 QQmlImportDatabase *database, QList<QQmlError> *errors)
1167 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1168 Q_ASSERT(nameSpace);
1170 if (QQmlImportNamespace::Import *import = nameSpace->findImport(uri)) {
1171 const QQmlTypeLoader::QmldirContent *qmldir = 0;
1172 if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
1176 if (!importExtension(qmldir->pluginLocation(), uri, database, qmldir, errors))
1179 if (import->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) {
1180 // Ensure that we are actually providing something
1181 int vmaj = import->majversion;
1182 int vmin = import->minversion;
1184 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1185 // The implicit import qmldir can be empty
1186 if (uri != QLatin1String(".")) {
1188 if (QQmlMetaType::isAnyModule(uri))
1189 error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
1191 error.setDescription(tr("module \"%1\" is not installed").arg(uri));
1192 errors->prepend(error);
1195 } else if ((vmaj >= 0) && (vmin >= 0)) {
1196 // Verify that the qmldir content is valid for this version
1197 if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors))
1205 if (errors->isEmpty()) {
1207 error.setDescription(QQmlTypeLoader::tr("Cannot update qmldir content for '%1'").arg(uri));
1208 errors->prepend(error);
1217 Adds an implicit "." file import. This is equivalent to calling addFileImport(), but error
1218 messages related to the path or qmldir file not existing are suppressed.
1220 bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlError> *errors)
1224 if (qmlImportTrace())
1225 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString())
1226 << ")::addImplicitImport";
1228 bool incomplete = !isLocal(baseUrl());
1229 return d->addFileImport(QLatin1String("."), QString(), -1, -1, true, incomplete, importDb, errors);
1235 Adds information to \a imports such that subsequent calls to resolveType()
1236 will resolve types qualified by \a prefix by considering types found at the given \a uri.
1238 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
1239 added via addImportPath() (if importType is LibraryImport).
1241 The \a prefix may be empty, in which case the import location is considered for
1244 The base URL must already have been set with Import::setBaseUrl().
1246 Optionally, the url the import resolved to can be returned by providing the url parameter.
1247 Not all imports will result in an output url being generated, in which case the url will
1248 be set to an empty string.
1250 Returns true on success, and false on failure. In case of failure, the errors array will
1251 filled appropriately.
1253 bool QQmlImports::addFileImport(QQmlImportDatabase *importDb,
1254 const QString& uri, const QString& prefix, int vmaj, int vmin,
1255 bool incomplete, QList<QQmlError> *errors)
1260 if (qmlImportTrace())
1261 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::addFileImport: "
1262 << uri << ' ' << vmaj << '.' << vmin << " as " << prefix;
1264 return d->addFileImport(uri, prefix, vmaj, vmin, false, incomplete, importDb, errors);
1267 bool QQmlImports::addLibraryImport(QQmlImportDatabase *importDb,
1268 const QString &uri, const QString &prefix, int vmaj, int vmin,
1269 const QString &qmldirIdentifier, const QString& qmldirUrl, bool incomplete, QList<QQmlError> *errors)
1274 if (qmlImportTrace())
1275 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::addLibraryImport: "
1276 << uri << ' ' << vmaj << '.' << vmin << " as " << prefix;
1278 return d->addLibraryImport(uri, prefix, vmaj, vmin, qmldirIdentifier, qmldirUrl, incomplete, importDb, errors);
1281 bool QQmlImports::updateQmldirContent(QQmlImportDatabase *importDb,
1282 const QString &uri, const QString &prefix,
1283 const QString &qmldirIdentifier, const QString& qmldirUrl, QList<QQmlError> *errors)
1288 if (qmlImportTrace())
1289 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::updateQmldirContent: "
1290 << uri << " to " << qmldirUrl << " as " << prefix;
1292 return d->updateQmldirContent(uri, prefix, qmldirIdentifier, qmldirUrl, importDb, errors);
1295 bool QQmlImports::locateQmldir(QQmlImportDatabase *importDb,
1296 const QString& uri, int vmaj, int vmin,
1297 QString *qmldirFilePath, QString *url)
1299 return d->locateQmldir(uri, vmaj, vmin, importDb, qmldirFilePath, url);
1302 bool QQmlImports::isLocal(const QString &url)
1304 return QQmlFile::isBundle(url) || !QQmlFile::urlToLocalFileOrQrc(url).isEmpty();
1307 bool QQmlImports::isLocal(const QUrl &url)
1309 return QQmlFile::isBundle(url) || !QQmlFile::urlToLocalFileOrQrc(url).isEmpty();
1314 \class QQmlImportDatabase
1315 \brief The QQmlImportDatabase class manages the QML imports for a QQmlEngine.
1318 QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
1321 filePluginPath << QLatin1String(".");
1323 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
1325 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
1326 addImportPath(installImportsPath);
1329 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
1330 if (!envImportPath.isEmpty()) {
1331 #if defined(Q_OS_WIN)
1332 QLatin1Char pathSep(';');
1334 QLatin1Char pathSep(':');
1336 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
1337 for (int ii = paths.count() - 1; ii >= 0; --ii)
1338 addImportPath(paths.at(ii));
1341 addImportPath(QCoreApplication::applicationDirPath());
1344 QQmlImportDatabase::~QQmlImportDatabase()
1346 qmldirCache.clear();
1352 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
1353 The \a prefix must contain the dot.
1355 \a qmldirPath is the location of the qmldir file.
1357 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1358 const QString &qmldirPath,
1359 const QString &qmldirPluginPath,
1360 const QString &baseName, const QStringList &suffixes,
1361 const QString &prefix)
1363 QStringList searchPaths = filePluginPath;
1364 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
1365 if (!qmldirPluginPathIsRelative)
1366 searchPaths.prepend(qmldirPluginPath);
1368 foreach (const QString &pluginPath, searchPaths) {
1370 QString resolvedPath;
1371 if (pluginPath == QLatin1String(".")) {
1372 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
1373 resolvedPath = QDir::cleanPath(qmldirPath + Slash + qmldirPluginPath);
1375 resolvedPath = qmldirPath;
1377 if (QDir::isRelativePath(pluginPath))
1378 resolvedPath = QDir::cleanPath(qmldirPath + Slash + pluginPath);
1380 resolvedPath = pluginPath;
1383 // hack for resources, should probably go away
1384 if (resolvedPath.startsWith(Colon))
1385 resolvedPath = QCoreApplication::applicationDirPath();
1387 if (!resolvedPath.endsWith(Slash))
1388 resolvedPath += Slash;
1390 foreach (const QString &suffix, suffixes) {
1391 QString pluginFileName = prefix;
1393 pluginFileName += baseName;
1394 pluginFileName += suffix;
1396 QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
1397 if (!absolutePath.isEmpty())
1398 return absolutePath;
1402 if (qmlImportTrace())
1403 qDebug() << "QQmlImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
1404 << "in" << qmldirPath;
1412 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
1415 \header \li Platform \li Valid suffixes
1416 \row \li Windows \li \c .dll
1417 \row \li Unix/Linux \li \c .so
1418 \row \li AIX \li \c .a
1419 \row \li HP-UX \li \c .sl, \c .so (HP-UXi)
1420 \row \li Mac OS X \li \c .dylib, \c .bundle, \c .so
1423 Version number on unix are ignored.
1425 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1426 const QString &qmldirPath, const QString &qmldirPluginPath,
1427 const QString &baseName)
1429 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
1430 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1433 << QLatin1String("d.dll") // try a qmake-style debug build first
1435 << QLatin1String(".dll"));
1438 # if defined(Q_OS_DARWIN)
1440 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1443 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
1444 << QLatin1String(".dylib")
1446 << QLatin1String(".dylib")
1447 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
1449 << QLatin1String(".so")
1450 << QLatin1String(".bundle"),
1451 QLatin1String("lib"));
1452 # else // Generic Unix
1453 QStringList validSuffixList;
1455 # if defined(Q_OS_HPUX)
1457 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
1458 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
1459 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
1461 validSuffixList << QLatin1String(".sl");
1463 validSuffixList << QLatin1String(".so");
1465 # elif defined(Q_OS_AIX)
1466 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
1467 # elif defined(Q_OS_UNIX)
1468 validSuffixList << QLatin1String(".so");
1471 // Examples of valid library names:
1474 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
1483 QStringList QQmlImportDatabase::pluginPathList() const
1485 return filePluginPath;
1491 void QQmlImportDatabase::setPluginPathList(const QStringList &paths)
1493 if (qmlImportTrace())
1494 qDebug().nospace() << "QQmlImportDatabase::setPluginPathList: " << paths;
1496 filePluginPath = paths;
1502 void QQmlImportDatabase::addPluginPath(const QString& path)
1504 if (qmlImportTrace())
1505 qDebug().nospace() << "QQmlImportDatabase::addPluginPath: " << path;
1507 QUrl url = QUrl(path);
1508 if (url.isRelative() || url.scheme() == QLatin1String("file")
1509 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1510 QDir dir = QDir(path);
1511 filePluginPath.prepend(dir.canonicalPath());
1513 filePluginPath.prepend(path);
1520 void QQmlImportDatabase::addImportPath(const QString& path)
1522 if (qmlImportTrace())
1523 qDebug().nospace() << "QQmlImportDatabase::addImportPath: " << path;
1528 QUrl url = QUrl(path);
1531 if (url.scheme() == QLatin1String("file")) {
1532 cPath = QQmlFile::urlToLocalFileOrQrc(url);
1533 } else if (path.startsWith(QLatin1Char(':'))) {
1534 // qrc directory, e.g. :/foo
1535 // need to convert to a qrc url, e.g. qrc:/foo
1536 cPath = QStringLiteral("qrc") + path;
1537 cPath.replace(Backslash, Slash);
1538 } else if (url.isRelative() ||
1539 (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1540 QDir dir = QDir(path);
1541 cPath = dir.canonicalPath();
1544 cPath.replace(Backslash, Slash);
1547 if (!cPath.isEmpty()
1548 && !fileImportPath.contains(cPath))
1549 fileImportPath.prepend(cPath);
1555 QStringList QQmlImportDatabase::importPathList(PathType type) const
1557 if (type == LocalOrRemote)
1558 return fileImportPath;
1561 foreach (const QString &path, fileImportPath) {
1562 bool localPath = isPathAbsolute(path) || QQmlFile::isLocalFile(path);
1563 if (localPath == (type == Local))
1573 void QQmlImportDatabase::setImportPathList(const QStringList &paths)
1575 if (qmlImportTrace())
1576 qDebug().nospace() << "QQmlImportDatabase::setImportPathList: " << paths;
1578 fileImportPath = paths;
1580 // Our existing cached paths may have been invalidated
1581 qmldirCache.clear();
1587 bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &uri, const QString &typeNamespace, QList<QQmlError> *errors)
1589 if (qmlImportTrace())
1590 qDebug().nospace() << "QQmlImportDatabase::importPlugin: " << uri << " from " << filePath;
1592 #ifndef QT_NO_LIBRARY
1593 QFileInfo fileInfo(filePath);
1594 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1596 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1597 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1599 if (typesRegistered) {
1600 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1601 "QQmlImportDatabase::importPlugin",
1602 "Internal error: Plugin imported previously with different uri");
1605 if (!engineInitialized || !typesRegistered) {
1606 if (!QQml_isFileCaseCorrect(absoluteFilePath)) {
1609 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1610 errors->prepend(error);
1614 QPluginLoader loader(absoluteFilePath);
1616 if (!loader.load()) {
1619 error.setDescription(loader.errorString());
1620 errors->prepend(error);
1625 QObject *instance = loader.instance();
1626 if (QQmlTypesExtensionInterface *iface = qobject_cast<QQmlExtensionInterface *>(instance)) {
1628 const QByteArray bytes = uri.toUtf8();
1629 const char *moduleId = bytes.constData();
1630 if (!typesRegistered) {
1632 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1634 QStringList registrationFailures;
1637 QWriteLocker lock(QQmlMetaType::typeRegistrationLock());
1639 if (!typeNamespace.isEmpty()) {
1640 // This is an 'identified' module
1641 if (typeNamespace != uri) {
1642 // The namespace for type registrations must match the URI for locating the module
1644 error.setDescription(tr("Module namespace '%1' does not match import URI '%2'").arg(typeNamespace).arg(uri));
1645 errors->prepend(error);
1649 if (QQmlMetaType::namespaceContainsRegistrations(typeNamespace)) {
1650 // Other modules have already installed to this namespace
1652 error.setDescription(tr("Namespace '%1' has already been used for type registration").arg(typeNamespace));
1653 errors->prepend(error);
1656 QQmlMetaType::protectNamespace(typeNamespace);
1659 // This is not an identified module - provide a warning
1660 qWarning().nospace() << qPrintable(tr("Module '%1' does not contain a module identifier directive - it cannot be protected from external registrations.").arg(uri));
1663 QQmlMetaType::setTypeRegistrationNamespace(typeNamespace);
1665 iface->registerTypes(moduleId);
1667 registrationFailures = QQmlMetaType::typeRegistrationFailures();
1668 QQmlMetaType::setTypeRegistrationNamespace(QString());
1671 if (!registrationFailures.isEmpty()) {
1672 foreach (const QString &failure, registrationFailures) {
1674 error.setDescription(failure);
1675 errors->prepend(error);
1680 if (!engineInitialized) {
1681 // things on the engine (eg. adding new global objects) have to be done for every
1683 // XXX protect against double initialization
1684 initializedPlugins.insert(absoluteFilePath);
1686 QQmlExtensionInterface *eiface =
1687 qobject_cast<QQmlExtensionInterface *>(instance);
1689 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1690 ep->typeLoader.initializeEngine(eiface, moduleId);
1696 error.setDescription(loader.errorString());
1697 errors->prepend(error);