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 <QtCore/qfileinfo.h>
47 #include <QtCore/qpluginloader.h>
48 #include <QtCore/qlibraryinfo.h>
49 #include <QtQml/qqmlextensioninterface.h>
50 #include <private/qqmlglobal_p.h>
51 #include <private/qqmltypenamecache_p.h>
52 #include <private/qqmlengine_p.h>
56 DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
57 DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
59 static bool greaterThan(const QString &s1, const QString &s2)
64 QString resolveLocalUrl(const QString &url, const QString &relative)
66 if (relative.contains(QLatin1Char(':'))) {
67 // contains a host name
68 return QUrl(url).resolved(QUrl(relative)).toString();
69 } else if (relative.isEmpty()) {
71 } else if (relative.at(0) == QLatin1Char('/') || !url.contains(QLatin1Char('/'))) {
74 if (relative == QLatin1String("."))
75 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1);
76 else if (relative.startsWith(QLatin1String("./")))
77 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative.mid(2);
78 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative;
84 typedef QMap<QString, QString> StringStringMap;
85 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
87 class QQmlImportedNamespace
96 QQmlDirComponents qmlDirComponents;
97 QQmlDirScripts qmlDirScripts;
102 bool find_helper(QQmlTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
103 QQmlType** type_return, QString* url_return,
104 QString *base = 0, bool *typeRecursionDetected = 0);
105 bool find(QQmlTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QQmlType** type_return,
106 QString* url_return, QString *base = 0, QList<QQmlError> *errors = 0);
109 class QQmlImportsPrivate {
111 QQmlImportsPrivate(QQmlTypeLoader *loader);
112 ~QQmlImportsPrivate();
114 bool importExtension(const QString &absoluteFilePath, const QString &uri,
115 QQmlImportDatabase *database, QQmlDirComponents* components,
116 QQmlDirScripts *scripts,
117 QList<QQmlError> *errors);
119 QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database);
120 QString add(const QQmlDirComponents &qmldircomponentsnetwork,
121 const QString& uri_arg, const QString& prefix,
122 int vmaj, int vmin, QQmlScript::Import::Type importType,
123 QQmlImportDatabase *database, QList<QQmlError> *errors);
124 bool find(const QString& type, int *vmajor, int *vminor,
125 QQmlType** type_return, QString* url_return, QList<QQmlError> *errors);
127 QQmlImportedNamespace *findNamespace(const QString& type);
133 QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
134 QQmlImportedNamespace unqualifiedset;
135 QHash<QString,QQmlImportedNamespace* > set;
136 QQmlTypeLoader *typeLoader;
141 \brief The QQmlImports class encapsulates one QML document's import statements.
144 QQmlImports::QQmlImports(const QQmlImports ©)
151 QQmlImports::operator =(const QQmlImports ©)
160 QQmlImports::QQmlImports(QQmlTypeLoader *typeLoader)
161 : d(new QQmlImportsPrivate(typeLoader))
165 QQmlImports::~QQmlImports()
172 Sets the base URL to be used for all relative file imports added.
174 void QQmlImports::setBaseUrl(const QUrl& url, const QString &urlString)
178 if (urlString.isEmpty()) {
179 d->base = url.toString();
181 //Q_ASSERT(url.toString() == urlString);
187 Returns the base URL to be used for all relative file imports added.
189 QUrl QQmlImports::baseUrl() const
194 void QQmlImports::populateCache(QQmlTypeNameCache *cache, QQmlEngine *engine) const
196 const QQmlImportedNamespace &set = d->unqualifiedset;
198 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
199 const QQmlImportedNamespace::Data &data = set.imports.at(ii);
200 QQmlTypeModule *module = QQmlMetaType::typeModule(data.uri, data.majversion);
202 cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, data.minversion));
205 for (QHash<QString,QQmlImportedNamespace* >::ConstIterator iter = d->set.begin();
206 iter != d->set.end();
209 const QQmlImportedNamespace &set = *iter.value();
210 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
211 const QQmlImportedNamespace::Data &data = set.imports.at(ii);
212 QQmlTypeModule *module = QQmlMetaType::typeModule(data.uri, data.majversion);
214 QQmlTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
215 import.modules.append(QQmlTypeModuleVersion(module, data.minversion));
218 QQmlMetaType::ModuleApi moduleApi = QQmlMetaType::moduleApi(data.uri, data.majversion, data.minversion);
219 if (moduleApi.script || moduleApi.qobject) {
220 QQmlTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
221 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
222 import.moduleApi = ep->moduleApiInstance(moduleApi);
228 QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
230 QList<QQmlImports::ScriptReference> scripts;
232 const QQmlImportedNamespace &set = d->unqualifiedset;
234 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
235 const QQmlImportedNamespace::Data &data = set.imports.at(ii);
237 foreach (const QQmlDirParser::Script &script, data.qmlDirScripts) {
239 ref.nameSpace = script.nameSpace;
240 ref.location = QUrl(data.url).resolved(QUrl(script.fileName));
245 for (QHash<QString,QQmlImportedNamespace* >::ConstIterator iter = d->set.constBegin();
246 iter != d->set.constEnd();
248 const QQmlImportedNamespace &set = *iter.value();
250 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
251 const QQmlImportedNamespace::Data &data = set.imports.at(ii);
253 foreach (const QQmlDirParser::Script &script, data.qmlDirScripts) {
255 ref.nameSpace = script.nameSpace;
256 ref.qualifier = iter.key();
257 ref.location = QUrl(data.url).resolved(QUrl(script.fileName));
269 The given (namespace qualified) \a type is resolved to either
271 \li a QQmlImportedNamespace stored at \a ns_return,
272 \li a QQmlType stored at \a type_return, or
273 \li a component located at \a url_return.
276 If any return pointer is 0, the corresponding search is not done.
280 bool QQmlImports::resolveType(const QString& type,
281 QQmlType** type_return, QString* url_return, int *vmaj, int *vmin,
282 QQmlImportedNamespace** ns_return, QList<QQmlError> *errors) const
284 QQmlImportedNamespace* ns = d->findNamespace(type);
290 if (type_return || url_return) {
291 if (d->find(type,vmaj,vmin,type_return,url_return, errors)) {
292 if (qmlImportTrace()) {
293 if (type_return && *type_return && url_return && !url_return->isEmpty())
294 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
295 << type << " => " << (*type_return)->typeName() << " " << *url_return;
296 if (type_return && *type_return)
297 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
298 << type << " => " << (*type_return)->typeName();
299 if (url_return && !url_return->isEmpty())
300 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
301 << type << " => " << *url_return;
312 Searching \e only in the namespace \a ns (previously returned in a call to
313 resolveType(), \a type is found and returned to either
314 a QQmlType stored at \a type_return, or
315 a component located at \a url_return.
317 If either return pointer is 0, the corresponding search is not done.
319 bool QQmlImports::resolveType(QQmlImportedNamespace* ns, const QString& type,
320 QQmlType** type_return, QString* url_return,
321 int *vmaj, int *vmin) const
323 return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return);
326 bool QQmlImportedNamespace::find_helper(QQmlTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
327 QQmlType** type_return, QString* url_return,
328 QString *base, bool *typeRecursionDetected)
330 int vmaj = data.majversion;
331 int vmin = data.minversion;
333 if (vmaj >= 0 && vmin >= 0) {
334 QString qt = data.uri + QLatin1Char('/') + type;
335 QQmlType *t = QQmlMetaType::qmlType(qt,vmaj,vmin);
337 if (vmajor) *vmajor = vmaj;
338 if (vminor) *vminor = vmin;
345 const QQmlDirComponents &qmldircomponents = data.qmlDirComponents;
346 bool typeWasDeclaredInQmldir = false;
347 if (!qmldircomponents.isEmpty()) {
348 foreach (const QQmlDirParser::Component &c, qmldircomponents) {
349 if (c.typeName == type) {
350 typeWasDeclaredInQmldir = true;
351 // importing version -1 means import ALL versions
352 if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
353 QString url(data.url + type + QLatin1String(".qml"));
354 QString candidate = resolveLocalUrl(url, c.fileName);
355 if (c.internal && base) {
356 if (resolveLocalUrl(*base, c.fileName) != candidate)
357 continue; // failed attempt to access an internal type
359 if (base && *base == candidate) {
360 if (typeRecursionDetected)
361 *typeRecursionDetected = true;
362 continue; // no recursion
365 *url_return = candidate;
372 if (!typeWasDeclaredInQmldir && !data.isLibrary) {
373 // XXX search non-files too! (eg. zip files, see QT-524)
374 QString url(data.url + type + QLatin1String(".qml"));
375 QString file = QQmlEnginePrivate::urlToLocalFileOrQrc(url);
376 if (!typeLoader->absoluteFilePath(file).isEmpty()) {
377 if (base && *base == url) { // no recursion
378 if (typeRecursionDetected)
379 *typeRecursionDetected = true;
390 QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader)
391 : ref(1), typeLoader(loader)
395 QQmlImportsPrivate::~QQmlImportsPrivate()
397 foreach (QQmlImportedNamespace* s, set.values())
401 bool QQmlImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
402 QQmlImportDatabase *database,
403 QQmlDirComponents* components,
404 QQmlDirScripts* scripts,
405 QList<QQmlError> *errors)
407 const QQmlDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
408 if (qmldirParser->hasError()) {
410 const QList<QQmlError> qmldirErrors = qmldirParser->errors(uri);
411 for (int i = 0; i < qmldirErrors.size(); ++i)
412 errors->prepend(qmldirErrors.at(i));
417 if (qmlImportTrace())
418 qDebug().nospace() << "QQmlImports(" << qPrintable(base) << "::importExtension: "
419 << "loaded " << absoluteFilePath;
421 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
422 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
424 QString qmldirPath = absoluteFilePath;
425 int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
427 qmldirPath.truncate(slash);
428 foreach (const QQmlDirParser::Plugin &plugin, qmldirParser->plugins()) {
430 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
431 if (!resolvedFilePath.isEmpty()) {
432 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
434 // XXX TODO: should we leave the import plugin error alone?
435 // Here, we pop it off the top and coalesce it into this error's message.
436 // The reason is that the lower level may add url and line/column numbering information.
437 QQmlError poppedError = errors->takeFirst();
439 error.setDescription(QQmlImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
440 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
441 errors->prepend(error);
448 error.setDescription(QQmlImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
449 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
450 errors->prepend(error);
458 *components = qmldirParser->components();
460 *scripts = qmldirParser->scripts();
465 QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
467 QString dir = dir_arg;
468 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
471 QStringList paths = database->fileImportPath;
472 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
474 QString stableRelativePath = dir;
475 foreach(const QString &path, paths) {
476 if (dir.startsWith(path)) {
477 stableRelativePath = dir.mid(path.length()+1);
482 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
484 // remove optional versioning in dot notation from uri
485 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
486 if (lastSlash >= 0) {
487 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
489 stableRelativePath = stableRelativePath.left(versionDot);
492 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
493 return stableRelativePath;
496 QString QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork,
497 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
498 QQmlScript::Import::Type importType,
499 QQmlImportDatabase *database, QList<QQmlError> *errors)
501 static QLatin1String Slash_qmldir("/qmldir");
502 static QLatin1Char Slash('/');
504 QQmlDirComponents qmldircomponents = qmldircomponentsnetwork;
505 QQmlDirScripts qmldirscripts;
506 QString uri = uri_arg;
507 QQmlImportedNamespace *s;
508 if (prefix.isEmpty()) {
511 s = set.value(prefix);
513 set.insert(prefix,(s=new QQmlImportedNamespace));
516 bool versionFound = false;
517 if (importType == QQmlScript::Import::Library) {
519 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
521 url.replace(QLatin1Char('.'), Slash);
526 // step 1: search for extension with fully encoded version number
527 foreach (const QString &p, database->fileImportPath) {
530 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
531 const QString absoluteFilePath = fi.absoluteFilePath();
536 const QString absolutePath = fi.absolutePath();
537 if (absolutePath.at(0) == QLatin1Char(':'))
538 url = QLatin1String("qrc://") + absolutePath.mid(1);
540 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
541 uri = resolvedUri(dir, database);
542 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
549 // step 2: search for extension with encoded version major
550 foreach (const QString &p, database->fileImportPath) {
553 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
554 const QString absoluteFilePath = fi.absoluteFilePath();
559 const QString absolutePath = fi.absolutePath();
560 if (absolutePath.at(0) == QLatin1Char(':'))
561 url = QLatin1String("qrc://") + absolutePath.mid(1);
563 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
564 uri = resolvedUri(dir, database);
565 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
573 // step 3: search for extension without version number
575 foreach (const QString &p, database->fileImportPath) {
577 qmldir = dir+Slash_qmldir;
579 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
580 if (!absoluteFilePath.isEmpty()) {
582 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
583 if (absolutePath.at(0) == QLatin1Char(':'))
584 url = QLatin1String("qrc://") + absolutePath.mid(1);
586 url = QUrl::fromLocalFile(absolutePath).toString();
587 uri = resolvedUri(dir, database);
588 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
595 if (QQmlMetaType::isModule(uri, vmaj, vmin))
598 if (!versionFound && qmldircomponents.isEmpty() && qmldirscripts.isEmpty()) {
600 QQmlError error; // we don't set the url or line or column as these will be set by the loader.
601 if (QQmlMetaType::isAnyModule(uri))
602 error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
604 error.setDescription(QQmlImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
605 errors->prepend(error);
610 if (importType == QQmlScript::Import::File && qmldircomponents.isEmpty()) {
611 QString importUrl = resolveLocalUrl(base, uri + Slash_qmldir);
612 QString localFileOrQrc = QQmlEnginePrivate::urlToLocalFileOrQrc(importUrl);
613 if (!localFileOrQrc.isEmpty()) {
614 QString dir = QQmlEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
615 if (!typeLoader->directoryExists(dir)) {
617 QQmlError error; // we don't set the line or column as these will be set by the loader.
618 error.setDescription(QQmlImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
619 error.setUrl(QUrl(importUrl));
620 errors->prepend(error);
622 return QString(); // local import dirs must exist
624 uri = resolvedUri(dir, database);
625 if (uri.endsWith(Slash))
627 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
628 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,&qmldirscripts,errors))
632 if (prefix.isEmpty()) {
633 // directory must at least exist for valid import
634 QString localFileOrQrc = QQmlEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
635 if (!typeLoader->directoryExists(localFileOrQrc)) {
637 QQmlError error; // we don't set the line or column as these will be set by the loader.
638 if (localFileOrQrc.isEmpty())
639 error.setDescription(QQmlImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
641 error.setDescription(QQmlImportDatabase::tr("\"%1\": no such directory").arg(uri));
642 error.setUrl(QUrl(importUrl));
643 errors->prepend(error);
651 url = resolveLocalUrl(base, url);
654 if (!versionFound && (vmaj > -1) && (vmin > -1) && !qmldircomponents.isEmpty()) {
655 int lowest_min = INT_MAX;
656 int highest_min = INT_MIN;
658 QList<QQmlDirParser::Component>::const_iterator cend = qmldircomponents.constEnd();
659 for (QList<QQmlDirParser::Component>::const_iterator cit = qmldircomponents.constBegin(); cit != cend; ++cit) {
660 if (cit->majorVersion == vmaj) {
661 lowest_min = qMin(lowest_min, cit->minorVersion);
662 highest_min = qMax(highest_min, cit->minorVersion);
666 if (lowest_min > vmin || highest_min < vmin) {
668 QQmlError error; // we don't set the url or line or column information, as these will be set by the loader.
669 error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
670 errors->prepend(error);
676 if (!url.endsWith(Slash))
679 QMap<QString, QQmlDirParser::Script> scripts;
681 if (!qmldirscripts.isEmpty()) {
682 // Verify that we haven't imported these scripts already
683 QList<QQmlImportedNamespace::Data>::const_iterator end = s->imports.constEnd();
684 for (QList<QQmlImportedNamespace::Data>::const_iterator it = s->imports.constBegin(); it != end; ++it) {
685 if (it->uri == uri) {
687 error.setDescription(QQmlImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg(it->url));
688 errors->prepend(error);
693 QList<QQmlDirParser::Script>::const_iterator send = qmldirscripts.constEnd();
694 for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin(); sit != send; ++sit) {
695 // Only include scripts that match our requested version
696 if (((vmaj == -1) || (sit->majorVersion == vmaj)) &&
697 ((vmin == -1) || (sit->minorVersion <= vmin))) {
699 // Load the highest version that matches
700 QMap<QString, QQmlDirParser::Script>::iterator it = scripts.find(sit->nameSpace);
701 if (it == scripts.end() || (it->minorVersion < sit->minorVersion)) {
702 scripts.insert(sit->nameSpace, *sit);
708 QQmlImportedNamespace::Data data;
711 data.majversion = vmaj;
712 data.minversion = vmin;
713 data.isLibrary = importType == QQmlScript::Import::Library;
714 data.qmlDirComponents = qmldircomponents;
715 data.qmlDirScripts = scripts.values();
717 s->imports.prepend(data);
722 bool QQmlImportsPrivate::find(const QString& type, int *vmajor, int *vminor, QQmlType** type_return,
723 QString* url_return, QList<QQmlError> *errors)
725 QQmlImportedNamespace *s = 0;
726 int slash = type.indexOf(QLatin1Char('/'));
728 QString namespaceName = type.left(slash);
729 s = set.value(namespaceName);
733 error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
734 errors->prepend(error);
738 int nslash = type.indexOf(QLatin1Char('/'),slash+1);
742 error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
743 errors->prepend(error);
750 QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
752 if (s->find(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
754 if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
755 // qualified, and only 1 url
756 *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml"));
764 QQmlImportedNamespace *QQmlImportsPrivate::findNamespace(const QString& type)
766 return set.value(type);
769 bool QQmlImportedNamespace::find(QQmlTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QQmlType** type_return,
770 QString* url_return, QString *base, QList<QQmlError> *errors)
772 bool typeRecursionDetected = false;
773 for (int i=0; i<imports.count(); ++i) {
774 if (find_helper(typeLoader, imports.at(i), type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
775 if (qmlCheckTypes()) {
776 // check for type clashes
777 for (int j = i+1; j<imports.count(); ++j) {
778 if (find_helper(typeLoader, imports.at(j), type, vmajor, vminor, 0, 0, base)) {
780 QString u1 = imports.at(i).url;
781 QString u2 = imports.at(j).url;
784 int slash = b.lastIndexOf(QLatin1Char('/'));
787 QString l = b.left(slash);
788 if (u1.startsWith(b))
789 u1 = u1.mid(b.count());
791 u1 = QQmlImportDatabase::tr("local directory");
792 if (u2.startsWith(b))
793 u2 = u2.mid(b.count());
795 u2 = QQmlImportDatabase::tr("local directory");
801 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
803 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
805 .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
806 .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
808 errors->prepend(error);
819 if (typeRecursionDetected)
820 error.setDescription(QQmlImportDatabase::tr("is instantiated recursively"));
822 error.setDescription(QQmlImportDatabase::tr("is not a type"));
823 errors->prepend(error);
829 \class QQmlImportDatabase
830 \brief The QQmlImportDatabase class manages the QML imports for a QQmlEngine.
833 QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
836 filePluginPath << QLatin1String(".");
838 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
840 #ifndef QT_NO_SETTINGS
841 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
842 addImportPath(installImportsPath);
843 #endif // QT_NO_SETTINGS
846 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
847 if (!envImportPath.isEmpty()) {
848 #if defined(Q_OS_WIN)
849 QLatin1Char pathSep(';');
851 QLatin1Char pathSep(':');
853 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
854 for (int ii = paths.count() - 1; ii >= 0; --ii)
855 addImportPath(paths.at(ii));
858 addImportPath(QCoreApplication::applicationDirPath());
861 QQmlImportDatabase::~QQmlImportDatabase()
868 Adds information to \a imports such that subsequent calls to resolveType()
869 will resolve types qualified by \a prefix by considering types found at the given \a uri.
871 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
872 added via addImportPath() (if importType is LibraryImport).
874 The \a prefix may be empty, in which case the import location is considered for
877 Returns the resolved URL of the import on success.
879 The base URL must already have been set with Import::setBaseUrl().
881 QString QQmlImports::addImport(QQmlImportDatabase *importDb,
882 const QString& uri, const QString& prefix, int vmaj, int vmin,
883 QQmlScript::Import::Type importType,
884 const QQmlDirComponents &qmldircomponentsnetwork,
885 QList<QQmlError> *errors)
887 if (qmlImportTrace())
888 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
889 << uri << " " << vmaj << '.' << vmin << " "
890 << (importType==QQmlScript::Import::Library? "Library" : "File")
893 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
899 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
900 The \a prefix must contain the dot.
902 \a qmldirPath is the location of the qmldir file.
904 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
905 const QString &qmldirPath, const QString &qmldirPluginPath,
906 const QString &baseName, const QStringList &suffixes,
907 const QString &prefix)
909 QStringList searchPaths = filePluginPath;
910 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
911 if (!qmldirPluginPathIsRelative)
912 searchPaths.prepend(qmldirPluginPath);
914 foreach (const QString &pluginPath, searchPaths) {
916 QString resolvedPath;
917 if (pluginPath == QLatin1String(".")) {
918 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
919 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath);
921 resolvedPath = qmldirPath;
923 if (QDir::isRelativePath(pluginPath))
924 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath);
926 resolvedPath = pluginPath;
929 // hack for resources, should probably go away
930 if (resolvedPath.startsWith(QLatin1Char(':')))
931 resolvedPath = QCoreApplication::applicationDirPath();
933 if (!resolvedPath.endsWith(QLatin1Char('/')))
934 resolvedPath += QLatin1Char('/');
936 foreach (const QString &suffix, suffixes) {
937 QString pluginFileName = prefix;
939 pluginFileName += baseName;
940 pluginFileName += suffix;
942 QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
943 if (!absolutePath.isEmpty())
948 if (qmlImportTrace())
949 qDebug() << "QQmlImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
950 << "in" << qmldirPath;
958 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
961 \header \li Platform \li Valid suffixes
962 \row \li Windows \li \c .dll
963 \row \li Unix/Linux \li \c .so
964 \row \li AIX \li \c .a
965 \row \li HP-UX \li \c .sl, \c .so (HP-UXi)
966 \row \li Mac OS X \li \c .dylib, \c .bundle, \c .so
969 Version number on unix are ignored.
971 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
972 const QString &qmldirPath, const QString &qmldirPluginPath,
973 const QString &baseName)
975 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
976 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
979 << QLatin1String("d.dll") // try a qmake-style debug build first
981 << QLatin1String(".dll"));
984 # if defined(Q_OS_DARWIN)
986 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
989 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
990 << QLatin1String(".dylib")
992 << QLatin1String(".dylib")
993 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
995 << QLatin1String(".so")
996 << QLatin1String(".bundle"),
997 QLatin1String("lib"));
998 # else // Generic Unix
999 QStringList validSuffixList;
1001 # if defined(Q_OS_HPUX)
1003 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
1004 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
1005 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
1007 validSuffixList << QLatin1String(".sl");
1009 validSuffixList << QLatin1String(".so");
1011 # elif defined(Q_OS_AIX)
1012 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
1013 # elif defined(Q_OS_UNIX)
1014 validSuffixList << QLatin1String(".so");
1017 // Examples of valid library names:
1020 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
1029 QStringList QQmlImportDatabase::pluginPathList() const
1031 return filePluginPath;
1037 void QQmlImportDatabase::setPluginPathList(const QStringList &paths)
1039 filePluginPath = paths;
1045 void QQmlImportDatabase::addPluginPath(const QString& path)
1047 if (qmlImportTrace())
1048 qDebug().nospace() << "QQmlImportDatabase::addPluginPath: " << path;
1050 QUrl url = QUrl(path);
1051 if (url.isRelative() || url.scheme() == QLatin1String("file")
1052 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1053 QDir dir = QDir(path);
1054 filePluginPath.prepend(dir.canonicalPath());
1056 filePluginPath.prepend(path);
1063 void QQmlImportDatabase::addImportPath(const QString& path)
1065 if (qmlImportTrace())
1066 qDebug().nospace() << "QQmlImportDatabase::addImportPath: " << path;
1071 QUrl url = QUrl(path);
1074 if (url.isRelative() || url.scheme() == QLatin1String("file")
1075 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1076 QDir dir = QDir(path);
1077 cPath = dir.canonicalPath();
1080 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1083 if (!cPath.isEmpty()
1084 && !fileImportPath.contains(cPath))
1085 fileImportPath.prepend(cPath);
1091 QStringList QQmlImportDatabase::importPathList() const
1093 return fileImportPath;
1099 void QQmlImportDatabase::setImportPathList(const QStringList &paths)
1101 fileImportPath = paths;
1107 bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)
1109 if (qmlImportTrace())
1110 qDebug().nospace() << "QQmlImportDatabase::importPlugin: " << uri << " from " << filePath;
1112 #ifndef QT_NO_LIBRARY
1113 QFileInfo fileInfo(filePath);
1114 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1116 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1117 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1119 if (typesRegistered) {
1120 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1121 "QQmlImportDatabase::importExtension",
1122 "Internal error: Plugin imported previously with different uri");
1125 if (!engineInitialized || !typesRegistered) {
1126 if (!QQml_isFileCaseCorrect(absoluteFilePath)) {
1129 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1130 errors->prepend(error);
1134 QPluginLoader loader(absoluteFilePath);
1136 if (!loader.load()) {
1139 error.setDescription(loader.errorString());
1140 errors->prepend(error);
1145 QObject *instance = loader.instance();
1146 if (QQmlTypesExtensionInterface *iface = qobject_cast<QQmlExtensionInterface *>(instance)) {
1148 const QByteArray bytes = uri.toUtf8();
1149 const char *moduleId = bytes.constData();
1150 if (!typesRegistered) {
1152 // XXX thread this code should probably be protected with a mutex.
1153 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1154 iface->registerTypes(moduleId);
1156 if (!engineInitialized) {
1157 // things on the engine (eg. adding new global objects) have to be done for every
1159 // XXX protect against double initialization
1160 initializedPlugins.insert(absoluteFilePath);
1162 QQmlExtensionInterface *eiface =
1163 qobject_cast<QQmlExtensionInterface *>(instance);
1165 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1166 ep->typeLoader.initializeEngine(eiface, moduleId);
1172 error.setDescription(loader.errorString());
1173 errors->prepend(error);