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 QString& 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 QString& 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 QString& 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.toUtf8(), 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 QString& 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(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 QString& 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 QString& type, int *vmajor, int *vminor,
292 QDeclarativeType** type_return, QString* url_return,
293 QString *base, bool *typeRecursionDetected)
295 int vmaj = data.majversion;
296 int vmin = data.minversion;
298 if (vmaj >= 0 && vmin >= 0) {
299 QString qt = data.uri + QLatin1Char('/') + type;
300 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
302 if (vmajor) *vmajor = vmaj;
303 if (vminor) *vminor = vmin;
310 const QDeclarativeDirComponents &qmldircomponents = data.qmlDirComponents;
311 bool typeWasDeclaredInQmldir = false;
312 if (!qmldircomponents.isEmpty()) {
313 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
314 if (c.typeName == type) {
315 typeWasDeclaredInQmldir = true;
316 // importing version -1 means import ALL versions
317 if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
318 QString url(data.url + type + QLatin1String(".qml"));
319 QString candidate = resolveLocalUrl(url, c.fileName);
320 if (c.internal && base) {
321 if (resolveLocalUrl(*base, c.fileName) != candidate)
322 continue; // failed attempt to access an internal type
324 if (base && *base == candidate) {
325 if (typeRecursionDetected)
326 *typeRecursionDetected = true;
327 continue; // no recursion
330 *url_return = candidate;
337 if (!typeWasDeclaredInQmldir && !data.isLibrary) {
338 // XXX search non-files too! (eg. zip files, see QT-524)
339 QString url(data.url + type + QLatin1String(".qml"));
340 QString file = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
341 if (!typeLoader->absoluteFilePath(file).isEmpty()) {
342 if (base && *base == url) { // no recursion
343 if (typeRecursionDetected)
344 *typeRecursionDetected = true;
355 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader)
356 : ref(1), typeLoader(loader)
360 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
362 foreach (QDeclarativeImportedNamespace* s, set.values())
366 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
367 QDeclarativeImportDatabase *database,
368 QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors)
370 const QDeclarativeDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
371 if (qmldirParser->hasError()) {
373 for (int i = 0; i < qmldirParser->errors().size(); ++i)
374 errors->prepend(qmldirParser->errors().at(i));
379 if (qmlImportTrace())
380 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base) << "::importExtension: "
381 << "loaded " << absoluteFilePath;
383 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
384 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
386 QString qmldirPath = absoluteFilePath;
387 int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
389 qmldirPath.truncate(slash);
390 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser->plugins()) {
392 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
393 #if defined(QT_LIBINFIX) && defined(Q_OS_SYMBIAN)
394 if (resolvedFilePath.isEmpty()) {
395 // In case of libinfixed build, attempt to load libinfixed version, too.
396 QString infixedPluginName = plugin.name + QLatin1String(QT_LIBINFIX);
397 resolvedFilePath = database->resolvePlugin(dir, plugin.path, infixedPluginName);
400 if (!resolvedFilePath.isEmpty()) {
401 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
403 // XXX TODO: should we leave the import plugin error alone?
404 // Here, we pop it off the top and coalesce it into this error's message.
405 // The reason is that the lower level may add url and line/column numbering information.
406 QDeclarativeError poppedError = errors->takeFirst();
407 QDeclarativeError error;
408 error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
409 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
410 errors->prepend(error);
416 QDeclarativeError error;
417 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
418 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
419 errors->prepend(error);
427 *components = qmldirParser->components();
432 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
434 QString dir = dir_arg;
435 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
438 QStringList paths = database->fileImportPath;
439 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
441 QString stableRelativePath = dir;
442 foreach(const QString &path, paths) {
443 if (dir.startsWith(path)) {
444 stableRelativePath = dir.mid(path.length()+1);
449 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
451 // remove optional versioning in dot notation from uri
452 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
453 if (lastSlash >= 0) {
454 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
456 stableRelativePath = stableRelativePath.left(versionDot);
459 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
460 return stableRelativePath;
463 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
464 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
465 QDeclarativeScriptParser::Import::Type importType,
466 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors)
468 static QLatin1String Slash_qmldir("/qmldir");
469 static QLatin1Char Slash('/');
471 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
472 QString uri = uri_arg;
473 QDeclarativeImportedNamespace *s;
474 if (prefix.isEmpty()) {
477 s = set.value(prefix);
479 set.insert(prefix,(s=new QDeclarativeImportedNamespace));
482 bool versionFound = false;
483 if (importType == QDeclarativeScriptParser::Import::Library) {
485 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
487 url.replace(QLatin1Char('.'), Slash);
492 // step 1: search for extension with fully encoded version number
493 foreach (const QString &p, database->fileImportPath) {
496 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
497 const QString absoluteFilePath = fi.absoluteFilePath();
502 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
503 uri = resolvedUri(dir, database);
504 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
510 // step 2: search for extension with encoded version major
511 foreach (const QString &p, database->fileImportPath) {
514 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
515 const QString absoluteFilePath = fi.absoluteFilePath();
520 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
521 uri = resolvedUri(dir, database);
522 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
529 // step 3: search for extension without version number
531 foreach (const QString &p, database->fileImportPath) {
533 qmldir = dir+Slash_qmldir;
535 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
536 if (!absoluteFilePath.isEmpty()) {
538 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
539 url = QLatin1String("file://") + absolutePath;
540 uri = resolvedUri(dir, database);
541 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
548 if (QDeclarativeMetaType::isModule(uri, vmaj, vmin))
551 if (!versionFound && qmldircomponents.isEmpty()) {
553 QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
554 if (QDeclarativeMetaType::isAnyModule(uri))
555 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
557 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
558 errors->prepend(error);
563 if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) {
564 QString importUrl = resolveLocalUrl(base, uri + Slash_qmldir);
565 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
566 if (!localFileOrQrc.isEmpty()) {
567 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
568 if (!typeLoader->directoryExists(dir)) {
570 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
571 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
572 error.setUrl(QUrl(importUrl));
573 errors->prepend(error);
575 return false; // local import dirs must exist
577 uri = resolvedUri(dir, database);
578 if (uri.endsWith(Slash))
580 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
581 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors))
585 if (prefix.isEmpty()) {
586 // directory must at least exist for valid import
587 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
588 if (!typeLoader->directoryExists(localFileOrQrc)) {
590 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
591 if (localFileOrQrc.isEmpty())
592 error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
594 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri));
595 error.setUrl(QUrl(importUrl));
596 errors->prepend(error);
604 url = resolveLocalUrl(base, url);
607 if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
608 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
609 int lowest_min = INT_MAX;
610 int highest_min = INT_MIN;
611 for (; it != qmldircomponents.end(); ++it) {
612 if (it->majorVersion == vmaj) {
613 lowest_min = qMin(lowest_min, it->minorVersion);
614 highest_min = qMax(highest_min, it->minorVersion);
617 if (lowest_min > vmin || highest_min < vmin) {
619 QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
620 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
621 errors->prepend(error);
627 if (!url.endsWith(Slash))
630 QDeclarativeImportedNamespace::Data data;
633 data.majversion = vmaj;
634 data.minversion = vmin;
635 data.isLibrary = importType == QDeclarativeScriptParser::Import::Library;
636 data.qmlDirComponents = qmldircomponents;
637 s->imports.prepend(data);
642 bool QDeclarativeImportsPrivate::find(const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
643 QString* url_return, QList<QDeclarativeError> *errors)
645 QDeclarativeImportedNamespace *s = 0;
646 int slash = type.indexOf(QLatin1Char('/'));
648 QString namespaceName = type.left(slash);
649 s = set.value(namespaceName);
652 QDeclarativeError error;
653 error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
654 errors->prepend(error);
658 int nslash = type.indexOf(QLatin1Char('/'),slash+1);
661 QDeclarativeError error;
662 error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed"));
663 errors->prepend(error);
670 QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
672 if (s->find(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
674 if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
675 // qualified, and only 1 url
676 *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml"));
684 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
686 return set.value(type);
689 bool QDeclarativeImportedNamespace::find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
690 QString* url_return, QString *base, QList<QDeclarativeError> *errors)
692 bool typeRecursionDetected = false;
693 for (int i=0; i<imports.count(); ++i) {
694 if (find_helper(typeLoader, imports.at(i), type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
695 if (qmlCheckTypes()) {
696 // check for type clashes
697 for (int j = i+1; j<imports.count(); ++j) {
698 if (find_helper(typeLoader, imports.at(j), type, vmajor, vminor, 0, 0, base)) {
700 QString u1 = imports.at(i).url;
701 QString u2 = imports.at(j).url;
704 int slash = b.lastIndexOf(QLatin1Char('/'));
707 QString l = b.left(slash);
708 if (u1.startsWith(b))
709 u1 = u1.mid(b.count());
711 u1 = QDeclarativeImportDatabase::tr("local directory");
712 if (u2.startsWith(b))
713 u2 = u2.mid(b.count());
715 u2 = QDeclarativeImportDatabase::tr("local directory");
719 QDeclarativeError error;
721 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
723 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
725 .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
726 .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
728 errors->prepend(error);
738 QDeclarativeError error;
739 if (typeRecursionDetected)
740 error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively"));
742 error.setDescription(QDeclarativeImportDatabase::tr("is not a type"));
743 errors->prepend(error);
749 \class QDeclarativeImportDatabase
750 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
753 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
756 filePluginPath << QLatin1String(".");
758 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
760 #ifndef QT_NO_SETTINGS
761 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
763 #if defined(Q_OS_SYMBIAN)
764 // Append imports path for all available drives in Symbian
765 if (installImportsPath.at(1) != QChar(QLatin1Char(':'))) {
766 QString tempPath = installImportsPath;
767 if (tempPath.at(tempPath.length() - 1) != QDir::separator()) {
768 tempPath += QDir::separator();
770 RFs& fs = qt_s60GetRFs();
771 TPtrC tempPathPtr(reinterpret_cast<const TText*> (tempPath.constData()));
772 TFindFile finder(fs);
773 TInt err = finder.FindByDir(tempPathPtr, tempPathPtr);
774 while (err == KErrNone) {
775 QString foundDir(reinterpret_cast<const QChar *>(finder.File().Ptr()),
776 finder.File().Length());
777 foundDir = QDir(foundDir).canonicalPath();
778 addImportPath(foundDir);
782 addImportPath(installImportsPath);
785 addImportPath(installImportsPath);
788 #endif // QT_NO_SETTINGS
791 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
792 if (!envImportPath.isEmpty()) {
793 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
794 QLatin1Char pathSep(';');
796 QLatin1Char pathSep(':');
798 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
799 for (int ii = paths.count() - 1; ii >= 0; --ii)
800 addImportPath(paths.at(ii));
803 addImportPath(QCoreApplication::applicationDirPath());
806 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
813 Adds information to \a imports such that subsequent calls to resolveType()
814 will resolve types qualified by \a prefix by considering types found at the given \a uri.
816 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
817 added via addImportPath() (if importType is LibraryImport).
819 The \a prefix may be empty, in which case the import location is considered for
822 The base URL must already have been set with Import::setBaseUrl().
824 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
825 const QString& uri, const QString& prefix, int vmaj, int vmin,
826 QDeclarativeScriptParser::Import::Type importType,
827 const QDeclarativeDirComponents &qmldircomponentsnetwork,
828 QList<QDeclarativeError> *errors)
830 if (qmlImportTrace())
831 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
832 << uri << " " << vmaj << '.' << vmin << " "
833 << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File")
836 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
842 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
843 The \a prefix must contain the dot.
845 \a qmldirPath is the location of the qmldir file.
847 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
848 const QString &qmldirPath, const QString &qmldirPluginPath,
849 const QString &baseName, const QStringList &suffixes,
850 const QString &prefix)
852 QStringList searchPaths = filePluginPath;
853 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
854 if (!qmldirPluginPathIsRelative)
855 searchPaths.prepend(qmldirPluginPath);
857 foreach (const QString &pluginPath, searchPaths) {
859 QString resolvedPath;
860 if (pluginPath == QLatin1String(".")) {
861 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
862 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath);
864 resolvedPath = qmldirPath;
866 if (QDir::isRelativePath(pluginPath))
867 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath);
869 resolvedPath = pluginPath;
872 // hack for resources, should probably go away
873 if (resolvedPath.startsWith(QLatin1Char(':')))
874 resolvedPath = QCoreApplication::applicationDirPath();
876 if (!resolvedPath.endsWith(QLatin1Char('/')))
877 resolvedPath += QLatin1Char('/');
879 foreach (const QString &suffix, suffixes) {
880 QString pluginFileName = prefix;
882 pluginFileName += baseName;
883 pluginFileName += suffix;
885 QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
886 if (!absolutePath.isEmpty())
891 if (qmlImportTrace())
892 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
893 << "in" << qmldirPath;
901 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
904 \header \i Platform \i Valid suffixes
905 \row \i Windows \i \c .dll
906 \row \i Unix/Linux \i \c .so
908 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
909 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
910 \row \i Symbian \i \c .dll
913 Version number on unix are ignored.
915 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
916 const QString &qmldirPath, const QString &qmldirPluginPath,
917 const QString &baseName)
919 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
920 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
923 << QLatin1String("d.dll") // try a qmake-style debug build first
925 << QLatin1String(".dll"));
926 #elif defined(Q_OS_SYMBIAN)
927 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
929 << QLatin1String(".dll")
930 << QLatin1String(".qtplugin"));
933 # if defined(Q_OS_DARWIN)
935 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
938 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
939 << QLatin1String(".dylib")
941 << QLatin1String(".dylib")
942 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
944 << QLatin1String(".so")
945 << QLatin1String(".bundle"),
946 QLatin1String("lib"));
947 # else // Generic Unix
948 QStringList validSuffixList;
950 # if defined(Q_OS_HPUX)
952 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
953 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
954 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
956 validSuffixList << QLatin1String(".sl");
958 validSuffixList << QLatin1String(".so");
960 # elif defined(Q_OS_AIX)
961 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
962 # elif defined(Q_OS_UNIX)
963 validSuffixList << QLatin1String(".so");
966 // Examples of valid library names:
969 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
978 QStringList QDeclarativeImportDatabase::pluginPathList() const
980 return filePluginPath;
986 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
988 filePluginPath = paths;
994 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
996 if (qmlImportTrace())
997 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
999 QUrl url = QUrl(path);
1000 if (url.isRelative() || url.scheme() == QLatin1String("file")
1001 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1002 QDir dir = QDir(path);
1003 filePluginPath.prepend(dir.canonicalPath());
1005 filePluginPath.prepend(path);
1012 void QDeclarativeImportDatabase::addImportPath(const QString& path)
1014 if (qmlImportTrace())
1015 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
1020 QUrl url = QUrl(path);
1023 if (url.isRelative() || url.scheme() == QLatin1String("file")
1024 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1025 QDir dir = QDir(path);
1026 cPath = dir.canonicalPath();
1029 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1032 if (!cPath.isEmpty()
1033 && !fileImportPath.contains(cPath))
1034 fileImportPath.prepend(cPath);
1040 QStringList QDeclarativeImportDatabase::importPathList() const
1042 return fileImportPath;
1048 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1050 fileImportPath = paths;
1056 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors)
1058 if (qmlImportTrace())
1059 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1061 #ifndef QT_NO_LIBRARY
1062 QFileInfo fileInfo(filePath);
1063 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1065 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1066 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1068 if (typesRegistered) {
1069 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1070 "QDeclarativeImportDatabase::importExtension",
1071 "Internal error: Plugin imported previously with different uri");
1074 if (!engineInitialized || !typesRegistered) {
1075 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1077 QDeclarativeError error;
1078 error.setDescription(tr("File name case mismatch for \"%2\"").arg(absoluteFilePath));
1079 errors->prepend(error);
1083 QPluginLoader loader(absoluteFilePath);
1085 if (!loader.load()) {
1087 QDeclarativeError error;
1088 error.setDescription(loader.errorString());
1089 errors->prepend(error);
1094 if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) {
1096 const QByteArray bytes = uri.toUtf8();
1097 const char *moduleId = bytes.constData();
1098 if (!typesRegistered) {
1100 // ### this code should probably be protected with a mutex.
1101 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1102 iface->registerTypes(moduleId);
1104 if (!engineInitialized) {
1105 // things on the engine (eg. adding new global objects) have to be done for every engine.
1107 // protect against double initialization
1108 initializedPlugins.insert(absoluteFilePath);
1109 iface->initializeEngine(engine, moduleId);
1113 QDeclarativeError error;
1114 error.setDescription(loader.errorString());
1115 errors->prepend(error);