1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativeimport_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 <QtDeclarative/qdeclarativeextensioninterface.h>
50 #include <private/qdeclarativeglobal_p.h>
51 #include <private/qdeclarativetypenamecache_p.h>
52 #include <private/qdeclarativeengine_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 QDeclarativeImportedNamespace
96 QDeclarativeDirComponents qmlDirComponents;
101 bool find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
102 QDeclarativeType** type_return, QString* url_return,
103 QString *base = 0, bool *typeRecursionDetected = 0);
104 bool find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
105 QString* url_return, QString *base = 0, QList<QDeclarativeError> *errors = 0);
108 class QDeclarativeImportsPrivate {
110 QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader);
111 ~QDeclarativeImportsPrivate();
113 bool importExtension(const QString &absoluteFilePath, const QString &uri,
114 QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components,
115 QList<QDeclarativeError> *errors);
117 QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database);
118 bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
119 const QString& uri_arg, const QString& prefix,
120 int vmaj, int vmin, QDeclarativeScript::Import::Type importType,
121 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors);
122 bool find(const QString& type, int *vmajor, int *vminor,
123 QDeclarativeType** type_return, QString* url_return, QList<QDeclarativeError> *errors);
125 QDeclarativeImportedNamespace *findNamespace(const QString& type);
131 QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
132 QDeclarativeImportedNamespace unqualifiedset;
133 QHash<QString,QDeclarativeImportedNamespace* > set;
134 QDeclarativeTypeLoader *typeLoader;
138 \class QDeclarativeImports
139 \brief The QDeclarativeImports class encapsulates one QML document's import statements.
142 QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports ©)
148 QDeclarativeImports &
149 QDeclarativeImports::operator =(const QDeclarativeImports ©)
158 QDeclarativeImports::QDeclarativeImports(QDeclarativeTypeLoader *typeLoader)
159 : d(new QDeclarativeImportsPrivate(typeLoader))
163 QDeclarativeImports::~QDeclarativeImports()
170 Sets the base URL to be used for all relative file imports added.
172 void QDeclarativeImports::setBaseUrl(const QUrl& url)
175 d->base = url.toString();
179 Returns the base URL to be used for all relative file imports added.
181 QUrl QDeclarativeImports::baseUrl() const
186 void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const
188 const QDeclarativeImportedNamespace &set = d->unqualifiedset;
190 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
191 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
192 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
194 cache->m_anonymousImports.append(QDeclarativeTypeModuleVersion(module, data.minversion));
197 for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin();
198 iter != d->set.end();
201 const QDeclarativeImportedNamespace &set = *iter.value();
202 QDeclarativeTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
203 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
204 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
205 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
207 import.modules.append(QDeclarativeTypeModuleVersion(module, data.minversion));
209 QDeclarativeMetaType::ModuleApi moduleApi = QDeclarativeMetaType::moduleApi(data.uri, data.majversion, data.minversion);
210 if (moduleApi.script || moduleApi.qobject) {
211 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
212 import.moduleApi = ep->moduleApiInstance(moduleApi);
221 The given (namespace qualified) \a type is resolved to either
223 \o a QDeclarativeImportedNamespace stored at \a ns_return,
224 \o a QDeclarativeType stored at \a type_return, or
225 \o a component located at \a url_return.
228 If any return pointer is 0, the corresponding search is not done.
232 bool QDeclarativeImports::resolveType(const QString& type,
233 QDeclarativeType** type_return, QString* url_return, int *vmaj, int *vmin,
234 QDeclarativeImportedNamespace** ns_return, QList<QDeclarativeError> *errors) const
236 QDeclarativeImportedNamespace* ns = d->findNamespace(type);
242 if (type_return || url_return) {
243 if (d->find(type,vmaj,vmin,type_return,url_return, errors)) {
244 if (qmlImportTrace()) {
245 if (type_return && *type_return && url_return && !url_return->isEmpty())
246 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
247 << type << " => " << (*type_return)->typeName() << " " << *url_return;
248 if (type_return && *type_return)
249 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
250 << type << " => " << (*type_return)->typeName();
251 if (url_return && !url_return->isEmpty())
252 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
253 << type << " => " << *url_return;
264 Searching \e only in the namespace \a ns (previously returned in a call to
265 resolveType(), \a type is found and returned to either
266 a QDeclarativeType stored at \a type_return, or
267 a component located at \a url_return.
269 If either return pointer is 0, the corresponding search is not done.
271 bool QDeclarativeImports::resolveType(QDeclarativeImportedNamespace* ns, const QString& type,
272 QDeclarativeType** type_return, QString* url_return,
273 int *vmaj, int *vmin) const
275 return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return);
278 bool QDeclarativeImportedNamespace::find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
279 QDeclarativeType** type_return, QString* url_return,
280 QString *base, bool *typeRecursionDetected)
282 int vmaj = data.majversion;
283 int vmin = data.minversion;
285 if (vmaj >= 0 && vmin >= 0) {
286 QString qt = data.uri + QLatin1Char('/') + type;
287 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
289 if (vmajor) *vmajor = vmaj;
290 if (vminor) *vminor = vmin;
297 const QDeclarativeDirComponents &qmldircomponents = data.qmlDirComponents;
298 bool typeWasDeclaredInQmldir = false;
299 if (!qmldircomponents.isEmpty()) {
300 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
301 if (c.typeName == type) {
302 typeWasDeclaredInQmldir = true;
303 // importing version -1 means import ALL versions
304 if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
305 QString url(data.url + type + QLatin1String(".qml"));
306 QString candidate = resolveLocalUrl(url, c.fileName);
307 if (c.internal && base) {
308 if (resolveLocalUrl(*base, c.fileName) != candidate)
309 continue; // failed attempt to access an internal type
311 if (base && *base == candidate) {
312 if (typeRecursionDetected)
313 *typeRecursionDetected = true;
314 continue; // no recursion
317 *url_return = candidate;
324 if (!typeWasDeclaredInQmldir && !data.isLibrary) {
325 // XXX search non-files too! (eg. zip files, see QT-524)
326 QString url(data.url + type + QLatin1String(".qml"));
327 QString file = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
328 if (!typeLoader->absoluteFilePath(file).isEmpty()) {
329 if (base && *base == url) { // no recursion
330 if (typeRecursionDetected)
331 *typeRecursionDetected = true;
342 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader)
343 : ref(1), typeLoader(loader)
347 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
349 foreach (QDeclarativeImportedNamespace* s, set.values())
353 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
354 QDeclarativeImportDatabase *database,
355 QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors)
357 const QDeclarativeDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
358 if (qmldirParser->hasError()) {
360 const QList<QDeclarativeError> qmldirErrors = qmldirParser->errors(uri);
361 for (int i = 0; i < qmldirErrors.size(); ++i)
362 errors->prepend(qmldirErrors.at(i));
367 if (qmlImportTrace())
368 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base) << "::importExtension: "
369 << "loaded " << absoluteFilePath;
371 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
372 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
374 QString qmldirPath = absoluteFilePath;
375 int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
377 qmldirPath.truncate(slash);
378 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser->plugins()) {
380 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
381 if (!resolvedFilePath.isEmpty()) {
382 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
384 // XXX TODO: should we leave the import plugin error alone?
385 // Here, we pop it off the top and coalesce it into this error's message.
386 // The reason is that the lower level may add url and line/column numbering information.
387 QDeclarativeError poppedError = errors->takeFirst();
388 QDeclarativeError error;
389 error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
390 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
391 errors->prepend(error);
397 QDeclarativeError error;
398 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
399 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
400 errors->prepend(error);
408 *components = qmldirParser->components();
413 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
415 QString dir = dir_arg;
416 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
419 QStringList paths = database->fileImportPath;
420 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
422 QString stableRelativePath = dir;
423 foreach(const QString &path, paths) {
424 if (dir.startsWith(path)) {
425 stableRelativePath = dir.mid(path.length()+1);
430 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
432 // remove optional versioning in dot notation from uri
433 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
434 if (lastSlash >= 0) {
435 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
437 stableRelativePath = stableRelativePath.left(versionDot);
440 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
441 return stableRelativePath;
444 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
445 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
446 QDeclarativeScript::Import::Type importType,
447 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors)
449 static QLatin1String Slash_qmldir("/qmldir");
450 static QLatin1Char Slash('/');
452 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
453 QString uri = uri_arg;
454 QDeclarativeImportedNamespace *s;
455 if (prefix.isEmpty()) {
458 s = set.value(prefix);
460 set.insert(prefix,(s=new QDeclarativeImportedNamespace));
463 bool versionFound = false;
464 if (importType == QDeclarativeScript::Import::Library) {
466 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
468 url.replace(QLatin1Char('.'), Slash);
473 // step 1: search for extension with fully encoded version number
474 foreach (const QString &p, database->fileImportPath) {
477 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
478 const QString absoluteFilePath = fi.absoluteFilePath();
483 const QString absolutePath = fi.absolutePath();
484 if (absolutePath.at(0) == QLatin1Char(':'))
485 url = QLatin1String("qrc://") + absolutePath.mid(1);
487 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
488 uri = resolvedUri(dir, database);
489 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
495 // step 2: search for extension with encoded version major
496 foreach (const QString &p, database->fileImportPath) {
499 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
500 const QString absoluteFilePath = fi.absoluteFilePath();
505 const QString absolutePath = fi.absolutePath();
506 if (absolutePath.at(0) == QLatin1Char(':'))
507 url = QLatin1String("qrc://") + absolutePath.mid(1);
509 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
510 uri = resolvedUri(dir, database);
511 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
518 // step 3: search for extension without version number
520 foreach (const QString &p, database->fileImportPath) {
522 qmldir = dir+Slash_qmldir;
524 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
525 if (!absoluteFilePath.isEmpty()) {
527 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
528 if (absolutePath.at(0) == QLatin1Char(':'))
529 url = QLatin1String("qrc://") + absolutePath.mid(1);
531 url = QUrl::fromLocalFile(absolutePath).toString();
532 uri = resolvedUri(dir, database);
533 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
540 if (QDeclarativeMetaType::isModule(uri, vmaj, vmin))
543 if (!versionFound && qmldircomponents.isEmpty()) {
545 QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
546 if (QDeclarativeMetaType::isAnyModule(uri))
547 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
549 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
550 errors->prepend(error);
555 if (importType == QDeclarativeScript::Import::File && qmldircomponents.isEmpty()) {
556 QString importUrl = resolveLocalUrl(base, uri + Slash_qmldir);
557 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
558 if (!localFileOrQrc.isEmpty()) {
559 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
560 if (!typeLoader->directoryExists(dir)) {
562 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
563 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
564 error.setUrl(QUrl(importUrl));
565 errors->prepend(error);
567 return false; // local import dirs must exist
569 uri = resolvedUri(dir, database);
570 if (uri.endsWith(Slash))
572 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
573 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors))
577 if (prefix.isEmpty()) {
578 // directory must at least exist for valid import
579 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
580 if (!typeLoader->directoryExists(localFileOrQrc)) {
582 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
583 if (localFileOrQrc.isEmpty())
584 error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
586 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri));
587 error.setUrl(QUrl(importUrl));
588 errors->prepend(error);
596 url = resolveLocalUrl(base, url);
599 if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
600 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
601 int lowest_min = INT_MAX;
602 int highest_min = INT_MIN;
603 for (; it != qmldircomponents.end(); ++it) {
604 if (it->majorVersion == vmaj) {
605 lowest_min = qMin(lowest_min, it->minorVersion);
606 highest_min = qMax(highest_min, it->minorVersion);
609 if (lowest_min > vmin || highest_min < vmin) {
611 QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
612 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
613 errors->prepend(error);
619 if (!url.endsWith(Slash))
622 QDeclarativeImportedNamespace::Data data;
625 data.majversion = vmaj;
626 data.minversion = vmin;
627 data.isLibrary = importType == QDeclarativeScript::Import::Library;
628 data.qmlDirComponents = qmldircomponents;
629 s->imports.prepend(data);
634 bool QDeclarativeImportsPrivate::find(const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
635 QString* url_return, QList<QDeclarativeError> *errors)
637 QDeclarativeImportedNamespace *s = 0;
638 int slash = type.indexOf(QLatin1Char('/'));
640 QString namespaceName = type.left(slash);
641 s = set.value(namespaceName);
644 QDeclarativeError error;
645 error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
646 errors->prepend(error);
650 int nslash = type.indexOf(QLatin1Char('/'),slash+1);
653 QDeclarativeError error;
654 error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed"));
655 errors->prepend(error);
662 QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
664 if (s->find(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
666 if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
667 // qualified, and only 1 url
668 *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml"));
676 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
678 return set.value(type);
681 bool QDeclarativeImportedNamespace::find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
682 QString* url_return, QString *base, QList<QDeclarativeError> *errors)
684 bool typeRecursionDetected = false;
685 for (int i=0; i<imports.count(); ++i) {
686 if (find_helper(typeLoader, imports.at(i), type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
687 if (qmlCheckTypes()) {
688 // check for type clashes
689 for (int j = i+1; j<imports.count(); ++j) {
690 if (find_helper(typeLoader, imports.at(j), type, vmajor, vminor, 0, 0, base)) {
692 QString u1 = imports.at(i).url;
693 QString u2 = imports.at(j).url;
696 int slash = b.lastIndexOf(QLatin1Char('/'));
699 QString l = b.left(slash);
700 if (u1.startsWith(b))
701 u1 = u1.mid(b.count());
703 u1 = QDeclarativeImportDatabase::tr("local directory");
704 if (u2.startsWith(b))
705 u2 = u2.mid(b.count());
707 u2 = QDeclarativeImportDatabase::tr("local directory");
711 QDeclarativeError error;
713 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
715 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
717 .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
718 .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
720 errors->prepend(error);
730 QDeclarativeError error;
731 if (typeRecursionDetected)
732 error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively"));
734 error.setDescription(QDeclarativeImportDatabase::tr("is not a type"));
735 errors->prepend(error);
741 \class QDeclarativeImportDatabase
742 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
745 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
748 filePluginPath << QLatin1String(".");
750 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
752 #ifndef QT_NO_SETTINGS
753 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
754 addImportPath(installImportsPath);
755 #endif // QT_NO_SETTINGS
758 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
759 if (!envImportPath.isEmpty()) {
760 #if defined(Q_OS_WIN)
761 QLatin1Char pathSep(';');
763 QLatin1Char pathSep(':');
765 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
766 for (int ii = paths.count() - 1; ii >= 0; --ii)
767 addImportPath(paths.at(ii));
770 addImportPath(QCoreApplication::applicationDirPath());
773 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
780 Adds information to \a imports such that subsequent calls to resolveType()
781 will resolve types qualified by \a prefix by considering types found at the given \a uri.
783 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
784 added via addImportPath() (if importType is LibraryImport).
786 The \a prefix may be empty, in which case the import location is considered for
789 The base URL must already have been set with Import::setBaseUrl().
791 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
792 const QString& uri, const QString& prefix, int vmaj, int vmin,
793 QDeclarativeScript::Import::Type importType,
794 const QDeclarativeDirComponents &qmldircomponentsnetwork,
795 QList<QDeclarativeError> *errors)
797 if (qmlImportTrace())
798 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
799 << uri << " " << vmaj << '.' << vmin << " "
800 << (importType==QDeclarativeScript::Import::Library? "Library" : "File")
803 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
809 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
810 The \a prefix must contain the dot.
812 \a qmldirPath is the location of the qmldir file.
814 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
815 const QString &qmldirPath, const QString &qmldirPluginPath,
816 const QString &baseName, const QStringList &suffixes,
817 const QString &prefix)
819 QStringList searchPaths = filePluginPath;
820 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
821 if (!qmldirPluginPathIsRelative)
822 searchPaths.prepend(qmldirPluginPath);
824 foreach (const QString &pluginPath, searchPaths) {
826 QString resolvedPath;
827 if (pluginPath == QLatin1String(".")) {
828 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
829 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath);
831 resolvedPath = qmldirPath;
833 if (QDir::isRelativePath(pluginPath))
834 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath);
836 resolvedPath = pluginPath;
839 // hack for resources, should probably go away
840 if (resolvedPath.startsWith(QLatin1Char(':')))
841 resolvedPath = QCoreApplication::applicationDirPath();
843 if (!resolvedPath.endsWith(QLatin1Char('/')))
844 resolvedPath += QLatin1Char('/');
846 foreach (const QString &suffix, suffixes) {
847 QString pluginFileName = prefix;
849 pluginFileName += baseName;
850 pluginFileName += suffix;
852 QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
853 if (!absolutePath.isEmpty())
858 if (qmlImportTrace())
859 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
860 << "in" << qmldirPath;
868 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
871 \header \i Platform \i Valid suffixes
872 \row \i Windows \i \c .dll
873 \row \i Unix/Linux \i \c .so
875 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
876 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
879 Version number on unix are ignored.
881 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
882 const QString &qmldirPath, const QString &qmldirPluginPath,
883 const QString &baseName)
885 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
886 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
889 << QLatin1String("d.dll") // try a qmake-style debug build first
891 << QLatin1String(".dll"));
894 # if defined(Q_OS_DARWIN)
896 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
899 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
900 << QLatin1String(".dylib")
902 << QLatin1String(".dylib")
903 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
905 << QLatin1String(".so")
906 << QLatin1String(".bundle"),
907 QLatin1String("lib"));
908 # else // Generic Unix
909 QStringList validSuffixList;
911 # if defined(Q_OS_HPUX)
913 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
914 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
915 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
917 validSuffixList << QLatin1String(".sl");
919 validSuffixList << QLatin1String(".so");
921 # elif defined(Q_OS_AIX)
922 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
923 # elif defined(Q_OS_UNIX)
924 validSuffixList << QLatin1String(".so");
927 // Examples of valid library names:
930 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
939 QStringList QDeclarativeImportDatabase::pluginPathList() const
941 return filePluginPath;
947 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
949 filePluginPath = paths;
955 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
957 if (qmlImportTrace())
958 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
960 QUrl url = QUrl(path);
961 if (url.isRelative() || url.scheme() == QLatin1String("file")
962 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
963 QDir dir = QDir(path);
964 filePluginPath.prepend(dir.canonicalPath());
966 filePluginPath.prepend(path);
973 void QDeclarativeImportDatabase::addImportPath(const QString& path)
975 if (qmlImportTrace())
976 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
981 QUrl url = QUrl(path);
984 if (url.isRelative() || url.scheme() == QLatin1String("file")
985 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
986 QDir dir = QDir(path);
987 cPath = dir.canonicalPath();
990 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
994 && !fileImportPath.contains(cPath))
995 fileImportPath.prepend(cPath);
1001 QStringList QDeclarativeImportDatabase::importPathList() const
1003 return fileImportPath;
1009 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1011 fileImportPath = paths;
1017 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors)
1019 if (qmlImportTrace())
1020 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1022 #ifndef QT_NO_LIBRARY
1023 QFileInfo fileInfo(filePath);
1024 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1026 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1027 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1029 if (typesRegistered) {
1030 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1031 "QDeclarativeImportDatabase::importExtension",
1032 "Internal error: Plugin imported previously with different uri");
1035 if (!engineInitialized || !typesRegistered) {
1036 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1038 QDeclarativeError error;
1039 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1040 errors->prepend(error);
1044 QPluginLoader loader(absoluteFilePath);
1046 if (!loader.load()) {
1048 QDeclarativeError error;
1049 error.setDescription(loader.errorString());
1050 errors->prepend(error);
1055 QObject *instance = loader.instance();
1056 if (QDeclarativeTypesExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(instance)) {
1058 const QByteArray bytes = uri.toUtf8();
1059 const char *moduleId = bytes.constData();
1060 if (!typesRegistered) {
1062 // XXX thread this code should probably be protected with a mutex.
1063 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1064 iface->registerTypes(moduleId);
1066 if (!engineInitialized) {
1067 // things on the engine (eg. adding new global objects) have to be done for every
1069 // XXX protect against double initialization
1070 initializedPlugins.insert(absoluteFilePath);
1072 QDeclarativeExtensionInterface *eiface =
1073 qobject_cast<QDeclarativeExtensionInterface *>(instance);
1075 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
1076 ep->typeLoader.initializeEngine(eiface, moduleId);
1081 QDeclarativeError error;
1082 error.setDescription(loader.errorString());
1083 errors->prepend(error);