1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
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>
55 #include "private/qcore_symbian_p.h"
60 DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
61 DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
63 static bool greaterThan(const QString &s1, const QString &s2)
68 QString resolveLocalUrl(const QString &url, const QString &relative)
70 if (relative.contains(QLatin1Char(':'))) {
71 // contains a host name
72 return QUrl(url).resolved(QUrl(relative)).toString();
73 } else if (relative.isEmpty()) {
75 } else if (relative.at(0) == QLatin1Char('/') || !url.contains(QLatin1Char('/'))) {
78 if (relative == QLatin1String("."))
79 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1);
80 else if (relative.startsWith(QLatin1String("./")))
81 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative.mid(2);
82 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative;
88 typedef QMap<QString, QString> StringStringMap;
89 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
91 class QDeclarativeImportedNamespace
100 QDeclarativeDirComponents qmlDirComponents;
105 bool find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QByteArray& type, int *vmajor, int *vminor,
106 QDeclarativeType** type_return, QString* url_return,
107 QString *base = 0, bool *typeRecursionDetected = 0);
108 bool find(QDeclarativeTypeLoader *typeLoader, const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
109 QString* url_return, QString *base = 0, QList<QDeclarativeError> *errors = 0);
112 class QDeclarativeImportsPrivate {
114 QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader);
115 ~QDeclarativeImportsPrivate();
117 bool importExtension(const QString &absoluteFilePath, const QString &uri,
118 QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components,
119 QList<QDeclarativeError> *errors);
121 QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database);
122 bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
123 const QString& uri_arg, const QString& prefix,
124 int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType,
125 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors);
126 bool find(const QByteArray& type, int *vmajor, int *vminor,
127 QDeclarativeType** type_return, QString* url_return, QList<QDeclarativeError> *errors);
129 QDeclarativeImportedNamespace *findNamespace(const QString& type);
135 QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
136 QDeclarativeImportedNamespace unqualifiedset;
137 QHash<QString,QDeclarativeImportedNamespace* > set;
138 QDeclarativeTypeLoader *typeLoader;
142 \class QDeclarativeImports
143 \brief The QDeclarativeImports class encapsulates one QML document's import statements.
146 QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports ©)
152 QDeclarativeImports &
153 QDeclarativeImports::operator =(const QDeclarativeImports ©)
162 QDeclarativeImports::QDeclarativeImports(QDeclarativeTypeLoader *typeLoader)
163 : d(new QDeclarativeImportsPrivate(typeLoader))
167 QDeclarativeImports::~QDeclarativeImports()
174 Sets the base URL to be used for all relative file imports added.
176 void QDeclarativeImports::setBaseUrl(const QUrl& url)
179 d->base = url.toString();
183 Returns the base URL to be used for all relative file imports added.
185 QUrl QDeclarativeImports::baseUrl() const
190 void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const
192 const QDeclarativeImportedNamespace &set = d->unqualifiedset;
194 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
195 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
196 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
198 cache->m_anonymousImports.append(QDeclarativeTypeModuleVersion(module, data.minversion));
201 for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin();
202 iter != d->set.end();
205 const QDeclarativeImportedNamespace &set = *iter.value();
206 QDeclarativeTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
207 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
208 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
209 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
211 import.modules.append(QDeclarativeTypeModuleVersion(module, data.minversion));
213 QDeclarativeMetaType::ModuleApi moduleApi = QDeclarativeMetaType::moduleApi(data.uri, data.majversion, data.minversion);
214 if (moduleApi.script || moduleApi.qobject) {
215 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
216 QDeclarativeMetaType::ModuleApiInstance *a = ep->moduleApiInstances.value(moduleApi);
218 a = new QDeclarativeMetaType::ModuleApiInstance;
219 a->scriptCallback = moduleApi.script;
220 a->qobjectCallback = moduleApi.qobject;
221 ep->moduleApiInstances.insert(moduleApi, a);
223 import.moduleApi = a;
234 The given (namespace qualified) \a type is resolved to either
236 \o a QDeclarativeImportedNamespace stored at \a ns_return,
237 \o a QDeclarativeType stored at \a type_return, or
238 \o a component located at \a url_return.
241 If any return pointer is 0, the corresponding search is not done.
245 bool QDeclarativeImports::resolveType(const QByteArray& type,
246 QDeclarativeType** type_return, QString* url_return, int *vmaj, int *vmin,
247 QDeclarativeImportedNamespace** ns_return, QList<QDeclarativeError> *errors) const
249 QDeclarativeImportedNamespace* ns = d->findNamespace(QString::fromUtf8(type));
255 if (type_return || url_return) {
256 if (d->find(type,vmaj,vmin,type_return,url_return, errors)) {
257 if (qmlImportTrace()) {
258 if (type_return && *type_return && url_return && !url_return->isEmpty())
259 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
260 << type << " => " << (*type_return)->typeName() << " " << *url_return;
261 if (type_return && *type_return)
262 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
263 << type << " => " << (*type_return)->typeName();
264 if (url_return && !url_return->isEmpty())
265 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
266 << type << " => " << *url_return;
277 Searching \e only in the namespace \a ns (previously returned in a call to
278 resolveType(), \a type is found and returned to either
279 a QDeclarativeType stored at \a type_return, or
280 a component located at \a url_return.
282 If either return pointer is 0, the corresponding search is not done.
284 bool QDeclarativeImports::resolveType(QDeclarativeImportedNamespace* ns, const QByteArray& type,
285 QDeclarativeType** type_return, QString* url_return,
286 int *vmaj, int *vmin) const
288 return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return);
291 bool QDeclarativeImportedNamespace::find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QByteArray& type, int *vmajor, int *vminor,
292 QDeclarativeType** type_return, QString* url_return,
293 QString *base, bool *typeRecursionDetected)
295 if (vmaj >= 0 && vmin >= 0) {
296 QString qt = data.uri + QLatin1Char('/') + type;
297 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
299 if (vmajor) *vmajor = vmaj;
300 if (vminor) *vminor = vmin;
307 const QDeclarativeDirComponents &qmldircomponents = data.qmlDirComponents;
308 bool typeWasDeclaredInQmldir = false;
309 if (!qmldircomponents.isEmpty()) {
310 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
311 if (c.typeName == type) {
312 typeWasDeclaredInQmldir = true;
313 // importing version -1 means import ALL versions
314 if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
315 QString url(data.url + type + QLatin1String(".qml"));
316 QString candidate = resolveLocalUrl(url, c.fileName);
317 if (c.internal && base) {
318 if (resolveLocalUrl(*base, c.fileName) != candidate)
319 continue; // failed attempt to access an internal type
321 if (base && *base == candidate) {
322 if (typeRecursionDetected)
323 *typeRecursionDetected = true;
324 continue; // no recursion
327 *url_return = candidate;
334 if (!typeWasDeclaredInQmldir && !data.isLibrary) {
335 // XXX search non-files too! (eg. zip files, see QT-524)
336 QString url(data.url + QString::fromUtf8(type) + QLatin1String(".qml"));
337 QString file = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
338 if (!typeLoader->absoluteFilePath(file).isEmpty()) {
339 if (base && *base == url) { // no recursion
340 if (typeRecursionDetected)
341 *typeRecursionDetected = true;
352 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader)
353 : ref(1), typeLoader(loader)
357 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
359 foreach (QDeclarativeImportedNamespace* s, set.values())
363 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
364 QDeclarativeImportDatabase *database,
365 QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors)
367 const QDeclarativeDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
368 if (qmldirParser->hasError()) {
370 for (int i = 0; i < qmldirParser->errors().size(); ++i)
371 errors->prepend(qmldirParser->errors().at(i));
376 if (qmlImportTrace())
377 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base) << "::importExtension: "
378 << "loaded " << absoluteFilePath;
380 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
381 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
383 QString qmldirPath = absoluteFilePath;
384 int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
386 qmldirPath.truncate(slash);
387 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser->plugins()) {
389 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
390 #if defined(QT_LIBINFIX) && defined(Q_OS_SYMBIAN)
391 if (resolvedFilePath.isEmpty()) {
392 // In case of libinfixed build, attempt to load libinfixed version, too.
393 QString infixedPluginName = plugin.name + QLatin1String(QT_LIBINFIX);
394 resolvedFilePath = database->resolvePlugin(dir, plugin.path, infixedPluginName);
397 if (!resolvedFilePath.isEmpty()) {
398 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
400 // XXX TODO: should we leave the import plugin error alone?
401 // Here, we pop it off the top and coalesce it into this error's message.
402 // The reason is that the lower level may add url and line/column numbering information.
403 QDeclarativeError poppedError = errors->takeFirst();
404 QDeclarativeError error;
405 error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
406 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
407 errors->prepend(error);
413 QDeclarativeError error;
414 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
415 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
416 errors->prepend(error);
424 *components = qmldirParser->components();
429 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
431 QString dir = dir_arg;
432 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
435 QStringList paths = database->fileImportPath;
436 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
438 QString stableRelativePath = dir;
439 foreach(const QString &path, paths) {
440 if (dir.startsWith(path)) {
441 stableRelativePath = dir.mid(path.length()+1);
446 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
448 // remove optional versioning in dot notation from uri
449 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
450 if (lastSlash >= 0) {
451 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
453 stableRelativePath = stableRelativePath.left(versionDot);
456 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
457 return stableRelativePath;
460 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
461 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
462 QDeclarativeScriptParser::Import::Type importType,
463 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors)
465 static QLatin1String Slash_qmldir("/qmldir");
466 static QLatin1Char Slash('/');
468 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
469 QString uri = uri_arg;
470 QDeclarativeImportedNamespace *s;
471 if (prefix.isEmpty()) {
474 s = set.value(prefix);
476 set.insert(prefix,(s=new QDeclarativeImportedNamespace));
479 bool versionFound = false;
480 if (importType == QDeclarativeScriptParser::Import::Library) {
482 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
484 url.replace(QLatin1Char('.'), Slash);
489 // step 1: search for extension with fully encoded version number
490 foreach (const QString &p, database->fileImportPath) {
493 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
494 const QString absoluteFilePath = fi.absoluteFilePath();
499 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
500 uri = resolvedUri(dir, database);
501 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
507 // step 2: search for extension with encoded version major
508 foreach (const QString &p, database->fileImportPath) {
511 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
512 const QString absoluteFilePath = fi.absoluteFilePath();
517 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
518 uri = resolvedUri(dir, database);
519 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
526 // step 3: search for extension without version number
528 foreach (const QString &p, database->fileImportPath) {
530 qmldir = dir+Slash_qmldir;
532 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
533 if (!absoluteFilePath.isEmpty()) {
535 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
536 url = QLatin1String("file://") + absolutePath;
537 uri = resolvedUri(dir, database);
538 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
545 if (QDeclarativeMetaType::isModule(uri.toUtf8(), vmaj, vmin)) {
549 if (!versionFound && qmldircomponents.isEmpty()) {
551 QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
552 if (QDeclarativeMetaType::isAnyModule(uri.toUtf8()))
553 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
555 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
556 errors->prepend(error);
561 if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) {
562 QString importUrl = resolveLocalUrl(base, uri + Slash_qmldir);
563 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
564 if (!localFileOrQrc.isEmpty()) {
565 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
566 if (!typeLoader->directoryExists(dir)) {
568 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
569 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
570 error.setUrl(QUrl(importUrl));
571 errors->prepend(error);
573 return false; // local import dirs must exist
575 uri = resolvedUri(dir, database);
576 if (uri.endsWith(Slash))
578 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
579 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors))
583 if (prefix.isEmpty()) {
584 // directory must at least exist for valid import
585 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
586 if (!typeLoader->directoryExists(localFileOrQrc)) {
588 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
589 if (localFileOrQrc.isEmpty())
590 error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
592 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri));
593 error.setUrl(QUrl(importUrl));
594 errors->prepend(error);
602 url = resolveLocalUrl(base, url);
605 if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
606 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
607 int lowest_min = INT_MAX;
608 int highest_min = INT_MIN;
609 for (; it != qmldircomponents.end(); ++it) {
610 if (it->majorVersion == vmaj) {
611 lowest_min = qMin(lowest_min, it->minorVersion);
612 highest_min = qMax(highest_min, it->minorVersion);
615 if (lowest_min > vmin || highest_min < vmin) {
617 QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
618 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
619 errors->prepend(error);
625 if (!url.endsWith(Slash))
628 QDeclarativeImportedNamespace::Data data;
629 data.uri = uri.toUtf8();
631 data.majversion = vmaj;
632 data.minversion = vmin;
633 data.isLibrary = importType == QDeclarativeScriptParser::Import::Library;
634 data.qmlDirComponents = qmldircomponents;
635 s->imports.prepend(data);
640 bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
641 QString* url_return, QList<QDeclarativeError> *errors)
643 QDeclarativeImportedNamespace *s = 0;
644 int slash = type.indexOf('/');
646 QString namespaceName = QString::fromUtf8(type.left(slash));
647 s = set.value(namespaceName);
650 QDeclarativeError error;
651 error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
652 errors->prepend(error);
656 int nslash = type.indexOf('/',slash+1);
659 QDeclarativeError error;
660 error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed"));
661 errors->prepend(error);
668 QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
670 if (s->find(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
672 if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
673 // qualified, and only 1 url
674 *url_return = resolveLocalUrl(s->imports.at(0).url, QString::fromUtf8(unqualifiedtype) + QLatin1String(".qml"));
682 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
684 return set.value(type);
687 bool QDeclarativeImportedNamespace::find(QDeclarativeTypeLoader *typeLoader, const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
688 QString* url_return, QString *base, QList<QDeclarativeError> *errors)
690 bool typeRecursionDetected = false;
691 for (int i=0; i<imports.count(); ++i) {
692 if (find_helper(typeLoader, imports.at(i), type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
693 if (qmlCheckTypes()) {
694 // check for type clashes
695 for (int j = i+1; j<imports.count(); ++j) {
696 if (find_helper(typeLoader, imports.at(j), type, vmajor, vminor, 0, 0, base)) {
698 QString u1 = imports.at(i).url;
699 QString u2 = imports.at(j).url;
702 int slash = b.lastIndexOf(QLatin1Char('/'));
705 QString l = b.left(slash);
706 if (u1.startsWith(b))
707 u1 = u1.mid(b.count());
709 u1 = QDeclarativeImportDatabase::tr("local directory");
710 if (u2.startsWith(b))
711 u2 = u2.mid(b.count());
713 u2 = QDeclarativeImportDatabase::tr("local directory");
717 QDeclarativeError error;
719 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
721 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
723 .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
724 .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
726 errors->prepend(error);
736 QDeclarativeError error;
737 if (typeRecursionDetected)
738 error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively"));
740 error.setDescription(QDeclarativeImportDatabase::tr("is not a type"));
741 errors->prepend(error);
747 \class QDeclarativeImportDatabase
748 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
751 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
754 filePluginPath << QLatin1String(".");
756 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
758 #ifndef QT_NO_SETTINGS
759 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
761 #if defined(Q_OS_SYMBIAN)
762 // Append imports path for all available drives in Symbian
763 if (installImportsPath.at(1) != QChar(QLatin1Char(':'))) {
764 QString tempPath = installImportsPath;
765 if (tempPath.at(tempPath.length() - 1) != QDir::separator()) {
766 tempPath += QDir::separator();
768 RFs& fs = qt_s60GetRFs();
769 TPtrC tempPathPtr(reinterpret_cast<const TText*> (tempPath.constData()));
770 TFindFile finder(fs);
771 TInt err = finder.FindByDir(tempPathPtr, tempPathPtr);
772 while (err == KErrNone) {
773 QString foundDir(reinterpret_cast<const QChar *>(finder.File().Ptr()),
774 finder.File().Length());
775 foundDir = QDir(foundDir).canonicalPath();
776 addImportPath(foundDir);
780 addImportPath(installImportsPath);
783 addImportPath(installImportsPath);
786 #endif // QT_NO_SETTINGS
789 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
790 if (!envImportPath.isEmpty()) {
791 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
792 QLatin1Char pathSep(';');
794 QLatin1Char pathSep(':');
796 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
797 for (int ii = paths.count() - 1; ii >= 0; --ii)
798 addImportPath(paths.at(ii));
801 addImportPath(QCoreApplication::applicationDirPath());
804 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
811 Adds information to \a imports such that subsequent calls to resolveType()
812 will resolve types qualified by \a prefix by considering types found at the given \a uri.
814 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
815 added via addImportPath() (if importType is LibraryImport).
817 The \a prefix may be empty, in which case the import location is considered for
820 The base URL must already have been set with Import::setBaseUrl().
822 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
823 const QString& uri, const QString& prefix, int vmaj, int vmin,
824 QDeclarativeScriptParser::Import::Type importType,
825 const QDeclarativeDirComponents &qmldircomponentsnetwork,
826 QList<QDeclarativeError> *errors)
828 if (qmlImportTrace())
829 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
830 << uri << " " << vmaj << '.' << vmin << " "
831 << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File")
834 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
840 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
841 The \a prefix must contain the dot.
843 \a qmldirPath is the location of the qmldir file.
845 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
846 const QString &qmldirPath, const QString &qmldirPluginPath,
847 const QString &baseName, const QStringList &suffixes,
848 const QString &prefix)
850 QStringList searchPaths = filePluginPath;
851 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
852 if (!qmldirPluginPathIsRelative)
853 searchPaths.prepend(qmldirPluginPath);
855 foreach (const QString &pluginPath, searchPaths) {
857 QString resolvedPath;
858 if (pluginPath == QLatin1String(".")) {
859 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
860 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath);
862 resolvedPath = qmldirPath;
864 if (QDir::isRelativePath(pluginPath))
865 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath);
867 resolvedPath = pluginPath;
870 // hack for resources, should probably go away
871 if (resolvedPath.startsWith(QLatin1Char(':')))
872 resolvedPath = QCoreApplication::applicationDirPath();
874 if (!resolvedPath.endsWith(QLatin1Char('/')))
875 resolvedPath += QLatin1Char('/');
877 foreach (const QString &suffix, suffixes) {
878 QString pluginFileName = prefix;
880 pluginFileName += baseName;
881 pluginFileName += suffix;
883 QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
884 if (!absolutePath.isEmpty())
889 if (qmlImportTrace())
890 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
891 << "in" << qmldirPath;
899 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
902 \header \i Platform \i Valid suffixes
903 \row \i Windows \i \c .dll
904 \row \i Unix/Linux \i \c .so
906 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
907 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
908 \row \i Symbian \i \c .dll
911 Version number on unix are ignored.
913 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
914 const QString &qmldirPath, const QString &qmldirPluginPath,
915 const QString &baseName)
917 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
918 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
921 << QLatin1String("d.dll") // try a qmake-style debug build first
923 << QLatin1String(".dll"));
924 #elif defined(Q_OS_SYMBIAN)
925 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
927 << QLatin1String(".dll")
928 << QLatin1String(".qtplugin"));
931 # if defined(Q_OS_DARWIN)
933 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
936 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
937 << QLatin1String(".dylib")
939 << QLatin1String(".dylib")
940 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
942 << QLatin1String(".so")
943 << QLatin1String(".bundle"),
944 QLatin1String("lib"));
945 # else // Generic Unix
946 QStringList validSuffixList;
948 # if defined(Q_OS_HPUX)
950 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
951 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
952 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
954 validSuffixList << QLatin1String(".sl");
956 validSuffixList << QLatin1String(".so");
958 # elif defined(Q_OS_AIX)
959 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
960 # elif defined(Q_OS_UNIX)
961 validSuffixList << QLatin1String(".so");
964 // Examples of valid library names:
967 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
976 QStringList QDeclarativeImportDatabase::pluginPathList() const
978 return filePluginPath;
984 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
986 filePluginPath = paths;
992 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
994 if (qmlImportTrace())
995 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
997 QUrl url = QUrl(path);
998 if (url.isRelative() || url.scheme() == QLatin1String("file")
999 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1000 QDir dir = QDir(path);
1001 filePluginPath.prepend(dir.canonicalPath());
1003 filePluginPath.prepend(path);
1010 void QDeclarativeImportDatabase::addImportPath(const QString& path)
1012 if (qmlImportTrace())
1013 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
1018 QUrl url = QUrl(path);
1021 if (url.isRelative() || url.scheme() == QLatin1String("file")
1022 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1023 QDir dir = QDir(path);
1024 cPath = dir.canonicalPath();
1027 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1030 if (!cPath.isEmpty()
1031 && !fileImportPath.contains(cPath))
1032 fileImportPath.prepend(cPath);
1038 QStringList QDeclarativeImportDatabase::importPathList() const
1040 return fileImportPath;
1046 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1048 fileImportPath = paths;
1054 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors)
1056 if (qmlImportTrace())
1057 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1059 #ifndef QT_NO_LIBRARY
1060 QFileInfo fileInfo(filePath);
1061 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1063 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1064 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1066 if (typesRegistered) {
1067 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1068 "QDeclarativeImportDatabase::importExtension",
1069 "Internal error: Plugin imported previously with different uri");
1072 if (!engineInitialized || !typesRegistered) {
1073 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1075 QDeclarativeError error;
1076 error.setDescription(tr("File name case mismatch for \"%2\"").arg(absoluteFilePath));
1077 errors->prepend(error);
1081 QPluginLoader loader(absoluteFilePath);
1083 if (!loader.load()) {
1085 QDeclarativeError error;
1086 error.setDescription(loader.errorString());
1087 errors->prepend(error);
1092 if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) {
1094 const QByteArray bytes = uri.toUtf8();
1095 const char *moduleId = bytes.constData();
1096 if (!typesRegistered) {
1098 // ### this code should probably be protected with a mutex.
1099 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1100 iface->registerTypes(moduleId);
1102 if (!engineInitialized) {
1103 // things on the engine (eg. adding new global objects) have to be done for every engine.
1105 // protect against double initialization
1106 initializedPlugins.insert(absoluteFilePath);
1107 iface->initializeEngine(engine, moduleId);
1111 QDeclarativeError error;
1112 error.setDescription(loader.errorString());
1113 errors->prepend(error);