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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
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 typedef QMap<QString, QString> StringStringMap;
69 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
71 class QDeclarativeImportedNamespace
76 QList<int> majversions;
77 QList<int> minversions;
78 QList<bool> isLibrary;
79 QList<QDeclarativeDirComponents> qmlDirComponents;
82 bool find_helper(int i, const QByteArray& type, int *vmajor, int *vminor,
83 QDeclarativeType** type_return, QUrl* url_return,
84 QUrl *base = 0, bool *typeRecursionDetected = 0);
85 bool find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
86 QUrl* url_return, QUrl *base = 0, QList<QDeclarativeError> *errors = 0);
89 class QDeclarativeImportsPrivate {
91 QDeclarativeImportsPrivate();
92 ~QDeclarativeImportsPrivate();
94 bool importExtension(const QString &absoluteFilePath, const QString &uri,
95 QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components,
96 QList<QDeclarativeError> *errors);
98 QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database);
99 bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
100 const QString& uri_arg, const QString& prefix,
101 int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType,
102 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors);
103 bool find(const QByteArray& type, int *vmajor, int *vminor,
104 QDeclarativeType** type_return, QUrl* url_return, QList<QDeclarativeError> *errors);
106 QDeclarativeImportedNamespace *findNamespace(const QString& type);
111 QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
112 QDeclarativeImportedNamespace unqualifiedset;
113 QHash<QString,QDeclarativeImportedNamespace* > set;
117 \class QDeclarativeImports
118 \brief The QDeclarativeImports class encapsulates one QML document's import statements.
121 QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports ©)
127 QDeclarativeImports &
128 QDeclarativeImports::operator =(const QDeclarativeImports ©)
137 QDeclarativeImports::QDeclarativeImports()
138 : d(new QDeclarativeImportsPrivate)
142 QDeclarativeImports::~QDeclarativeImports()
149 Sets the base URL to be used for all relative file imports added.
151 void QDeclarativeImports::setBaseUrl(const QUrl& url)
157 Returns the base URL to be used for all relative file imports added.
159 QUrl QDeclarativeImports::baseUrl() const
164 static QDeclarativeTypeNameCache *
165 cacheForNamespace(QDeclarativeEngine *engine, const QDeclarativeImportedNamespace &set,
166 QDeclarativeTypeNameCache *cache, bool importWasQualified)
169 cache = new QDeclarativeTypeNameCache(engine);
171 QList<QDeclarativeType *> types = QDeclarativeMetaType::qmlTypes();
173 for (int ii = 0; ii < set.uris.count(); ++ii) {
174 QByteArray uri = set.uris.at(ii).toUtf8();
175 int major = set.majversions.at(ii);
176 int minor = set.minversions.at(ii);
178 if (importWasQualified) {
179 QDeclarativeMetaType::ModuleApi moduleApi = QDeclarativeMetaType::moduleApi(uri, major, minor);
180 if (moduleApi.script || moduleApi.qobject) {
181 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
182 QDeclarativeMetaType::ModuleApiInstance *a = ep->moduleApiInstances.value(moduleApi);
184 a = new QDeclarativeMetaType::ModuleApiInstance;
185 a->scriptCallback = moduleApi.script;
186 a->qobjectCallback = moduleApi.qobject;
187 ep->moduleApiInstances.insert(moduleApi, a);
189 cache->setModuleApi(a);
193 QByteArray base = uri + '/';
195 foreach (QDeclarativeType *type, types) {
196 if (type->qmlTypeName().startsWith(base) &&
197 type->qmlTypeName().lastIndexOf('/') == (base.length() - 1) &&
198 type->availableInVersion(major,minor))
200 QString name = QString::fromUtf8(type->qmlTypeName().mid(base.length()));
202 cache->add(name, type);
210 void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const
212 const QDeclarativeImportedNamespace &set = d->unqualifiedset;
214 for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin();
215 iter != d->set.end(); ++iter) {
217 QDeclarativeTypeNameCache::Data *d = cache->data(iter.key());
219 if (!d->typeNamespace)
220 cacheForNamespace(engine, *(*iter), d->typeNamespace, true);
222 QDeclarativeTypeNameCache *nc = cacheForNamespace(engine, *(*iter), 0, true);
223 cache->add(iter.key(), nc);
228 cacheForNamespace(engine, set, cache, false);
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, QUrl* 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, QUrl* url_return,
286 int *vmaj, int *vmin) const
288 return ns->find(type,vmaj,vmin,type_return,url_return);
291 bool QDeclarativeImportedNamespace::find_helper(int i, const QByteArray& type, int *vmajor, int *vminor,
292 QDeclarativeType** type_return, QUrl* url_return,
293 QUrl *base, bool *typeRecursionDetected)
295 int vmaj = majversions.at(i);
296 int vmin = minversions.at(i);
298 QByteArray qt = uris.at(i).toUtf8();
302 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
304 if (vmajor) *vmajor = vmaj;
305 if (vminor) *vminor = vmin;
311 QUrl url = QUrl(urls.at(i) + QLatin1Char('/') + QString::fromUtf8(type) + QLatin1String(".qml"));
312 QDeclarativeDirComponents qmldircomponents = qmlDirComponents.at(i);
314 bool typeWasDeclaredInQmldir = false;
315 if (!qmldircomponents.isEmpty()) {
316 const QString typeName = QString::fromUtf8(type);
317 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
318 if (c.typeName == typeName) {
319 typeWasDeclaredInQmldir = true;
321 // importing version -1 means import ALL versions
322 if ((vmaj == -1) || (c.majorVersion < vmaj || (c.majorVersion == vmaj && vmin >= c.minorVersion))) {
323 QUrl candidate = url.resolved(QUrl(c.fileName));
324 if (c.internal && base) {
325 if (base->resolved(QUrl(c.fileName)) != candidate)
326 continue; // failed attempt to access an internal type
328 if (base && *base == candidate) {
329 if (typeRecursionDetected)
330 *typeRecursionDetected = true;
331 continue; // no recursion
334 *url_return = candidate;
341 if (!typeWasDeclaredInQmldir && !isLibrary.at(i)) {
342 // XXX search non-files too! (eg. zip files, see QT-524)
343 QFileInfo f(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url));
345 if (base && *base == url) { // no recursion
346 if (typeRecursionDetected)
347 *typeRecursionDetected = true;
358 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate()
363 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
365 foreach (QDeclarativeImportedNamespace* s, set.values())
369 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
370 QDeclarativeImportDatabase *database,
371 QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors)
373 QFile file(absoluteFilePath);
375 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
377 QDeclarativeError error;
378 error.setDescription(QDeclarativeImportDatabase::tr("cannot load module \"%1\": File name case mismatch for \"%2\"").arg(uri).arg(absoluteFilePath));
379 errors->prepend(error);
382 } else if (file.open(QFile::ReadOnly)) {
383 filecontent = QString::fromUtf8(file.readAll());
384 if (qmlImportTrace())
385 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base.toString()) << "::importExtension: "
386 << "loaded " << absoluteFilePath;
389 QDeclarativeError error;
390 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" definition \"%2\" not readable").arg(uri).arg(absoluteFilePath));
391 errors->prepend(error);
395 QDir dir = QFileInfo(file).dir();
396 QUrl url = QUrl::fromLocalFile(absoluteFilePath);
398 QDeclarativeDirParser qmldirParser;
399 qmldirParser.setSource(filecontent);
400 qmldirParser.setUrl(url);
402 // propagate any errors reported by the parser back up to the typeloader.
403 if (qmldirParser.parse()) {
405 for (int i = 0; i < qmldirParser.errors().size(); ++i) {
406 errors->prepend(qmldirParser.errors().at(i));
412 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
413 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
416 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser.plugins()) {
418 QString resolvedFilePath = database->resolvePlugin(dir, plugin.path, plugin.name);
419 #if defined(QT_LIBINFIX) && defined(Q_OS_SYMBIAN)
420 if (resolvedFilePath.isEmpty()) {
421 // In case of libinfixed build, attempt to load libinfixed version, too.
422 QString infixedPluginName = plugin.name + QLatin1String(QT_LIBINFIX);
423 resolvedFilePath = database->resolvePlugin(dir, plugin.path, infixedPluginName);
426 if (!resolvedFilePath.isEmpty()) {
427 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
429 // XXX TODO: should we leave the import plugin error alone?
430 // Here, we pop it off the top and coalesce it into this error's message.
431 // The reason is that the lower level may add url and line/column numbering information.
432 QDeclarativeError poppedError = errors->takeFirst();
433 QDeclarativeError error;
434 error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
436 errors->prepend(error);
442 QDeclarativeError error;
443 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
445 errors->prepend(error);
453 *components = qmldirParser.components();
458 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
460 QString dir = dir_arg;
461 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
464 QStringList paths = database->fileImportPath;
465 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
467 QString stableRelativePath = dir;
468 foreach(const QString &path, paths) {
469 if (dir.startsWith(path)) {
470 stableRelativePath = dir.mid(path.length()+1);
475 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
477 // remove optional versioning in dot notation from uri
478 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
479 if (lastSlash >= 0) {
480 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
482 stableRelativePath = stableRelativePath.left(versionDot);
485 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
486 return stableRelativePath;
489 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
490 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
491 QDeclarativeScriptParser::Import::Type importType,
492 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors)
494 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
495 QString uri = uri_arg;
496 QDeclarativeImportedNamespace *s;
497 if (prefix.isEmpty()) {
500 s = set.value(prefix);
502 set.insert(prefix,(s=new QDeclarativeImportedNamespace));
506 bool versionFound = false;
507 if (importType == QDeclarativeScriptParser::Import::Library) {
508 url.replace(QLatin1Char('.'), QLatin1Char('/'));
513 // step 1: search for extension with fully encoded version number
514 if (vmaj >= 0 && vmin >= 0) {
515 foreach (const QString &p, database->fileImportPath) {
516 dir = p+QLatin1Char('/')+url;
518 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
519 const QString absoluteFilePath = fi.absoluteFilePath();
524 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
525 uri = resolvedUri(dir, database);
526 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
532 // step 2: search for extension with encoded version major
533 if (vmaj >= 0 && vmin >= 0) {
534 foreach (const QString &p, database->fileImportPath) {
535 dir = p+QLatin1Char('/')+url;
537 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
538 const QString absoluteFilePath = fi.absoluteFilePath();
543 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
544 uri = resolvedUri(dir, database);
545 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
552 // step 3: search for extension without version number
554 foreach (const QString &p, database->fileImportPath) {
555 dir = p+QLatin1Char('/')+url;
557 QFileInfo fi(dir+QLatin1String("/qmldir"));
558 const QString absoluteFilePath = fi.absoluteFilePath();
563 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
564 uri = resolvedUri(dir, database);
565 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
572 if (QDeclarativeMetaType::isModule(uri.toUtf8(), vmaj, vmin))
575 if (!versionFound && qmldircomponents.isEmpty()) {
577 bool anyversion = QDeclarativeMetaType::isModule(uri.toUtf8(), -1, -1);
578 QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
580 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
582 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
583 errors->prepend(error);
589 if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) {
590 QUrl importUrl = base.resolved(QUrl(uri + QLatin1String("/qmldir")));
591 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
592 if (!localFileOrQrc.isEmpty()) {
593 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
594 QFileInfo dirinfo(dir);
595 if (dir.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) {
597 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
598 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
599 error.setUrl(importUrl);
600 errors->prepend(error);
602 return false; // local import dirs must exist
604 uri = resolvedUri(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))), database);
605 if (uri.endsWith(QLatin1Char('/')))
607 if (QFile::exists(localFileOrQrc)) {
608 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors))
612 if (prefix.isEmpty()) {
613 // directory must at least exist for valid import
614 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
615 QFileInfo dirinfo(localFileOrQrc);
616 if (localFileOrQrc.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) {
618 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
619 if (localFileOrQrc.isEmpty())
620 error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
622 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri));
623 error.setUrl(importUrl);
624 errors->prepend(error);
632 url = base.resolved(QUrl(url)).toString();
633 if (url.endsWith(QLatin1Char('/')))
637 if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
638 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
639 int lowest_maj = INT_MAX;
640 int lowest_min = INT_MAX;
641 int highest_maj = INT_MIN;
642 int highest_min = INT_MIN;
643 for (; it != qmldircomponents.end(); ++it) {
644 if (it->majorVersion > highest_maj || (it->majorVersion == highest_maj && it->minorVersion > highest_min)) {
645 highest_maj = it->majorVersion;
646 highest_min = it->minorVersion;
648 if (it->majorVersion < lowest_maj || (it->majorVersion == lowest_maj && it->minorVersion < lowest_min)) {
649 lowest_maj = it->majorVersion;
650 lowest_min = it->minorVersion;
653 if (lowest_maj > vmaj || (lowest_maj == vmaj && lowest_min > vmin)
654 || highest_maj < vmaj || (highest_maj == vmaj && highest_min < vmin))
657 QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
658 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
659 errors->prepend(error);
665 s->uris.prepend(uri);
666 s->urls.prepend(url);
667 s->majversions.prepend(vmaj);
668 s->minversions.prepend(vmin);
669 s->isLibrary.prepend(importType == QDeclarativeScriptParser::Import::Library);
670 s->qmlDirComponents.prepend(qmldircomponents);
674 bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
675 QUrl* url_return, QList<QDeclarativeError> *errors)
677 QDeclarativeImportedNamespace *s = 0;
678 int slash = type.indexOf('/');
680 QString namespaceName = QString::fromUtf8(type.left(slash));
681 s = set.value(namespaceName);
684 QDeclarativeError error;
685 error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
686 errors->prepend(error);
690 int nslash = type.indexOf('/',slash+1);
693 QDeclarativeError error;
694 error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed"));
695 errors->prepend(error);
702 QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
704 if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
706 if (s->urls.count() == 1 && !s->isLibrary[0] && url_return && s != &unqualifiedset) {
707 // qualified, and only 1 url
708 *url_return = QUrl(s->urls[0]+QLatin1Char('/')).resolved(QUrl(QString::fromUtf8(unqualifiedtype) + QLatin1String(".qml")));
716 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
718 return set.value(type);
721 bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
722 QUrl* url_return, QUrl *base, QList<QDeclarativeError> *errors)
724 bool typeRecursionDetected = false;
725 for (int i=0; i<urls.count(); ++i) {
726 if (find_helper(i, type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
727 if (qmlCheckTypes()) {
728 // check for type clashes
729 for (int j = i+1; j<urls.count(); ++j) {
730 if (find_helper(j, type, vmajor, vminor, 0, 0, base)) {
732 QString u1 = urls.at(i);
733 QString u2 = urls.at(j);
735 QString b = base->toString();
736 int slash = b.lastIndexOf(QLatin1Char('/'));
739 QString l = b.left(slash);
740 if (u1.startsWith(b))
741 u1 = u1.mid(b.count());
743 u1 = QDeclarativeImportDatabase::tr("local directory");
744 if (u2.startsWith(b))
745 u2 = u2.mid(b.count());
747 u2 = QDeclarativeImportDatabase::tr("local directory");
751 QDeclarativeError error;
753 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
755 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
757 .arg(majversions.at(i)).arg(minversions.at(i))
758 .arg(majversions.at(j)).arg(minversions.at(j)));
760 errors->prepend(error);
770 QDeclarativeError error;
771 if (typeRecursionDetected)
772 error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively"));
774 error.setDescription(QDeclarativeImportDatabase::tr("is not a type"));
775 errors->prepend(error);
781 \class QDeclarativeImportDatabase
782 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
785 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
788 filePluginPath << QLatin1String(".");
790 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
792 #ifndef QT_NO_SETTINGS
793 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
795 #if defined(Q_OS_SYMBIAN)
796 // Append imports path for all available drives in Symbian
797 if (installImportsPath.at(1) != QChar(QLatin1Char(':'))) {
798 QString tempPath = installImportsPath;
799 if (tempPath.at(tempPath.length() - 1) != QDir::separator()) {
800 tempPath += QDir::separator();
802 RFs& fs = qt_s60GetRFs();
803 TPtrC tempPathPtr(reinterpret_cast<const TText*> (tempPath.constData()));
804 TFindFile finder(fs);
805 TInt err = finder.FindByDir(tempPathPtr, tempPathPtr);
806 while (err == KErrNone) {
807 QString foundDir(reinterpret_cast<const QChar *>(finder.File().Ptr()),
808 finder.File().Length());
809 foundDir = QDir(foundDir).canonicalPath();
810 addImportPath(foundDir);
814 addImportPath(installImportsPath);
817 addImportPath(installImportsPath);
820 #endif // QT_NO_SETTINGS
823 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
824 if (!envImportPath.isEmpty()) {
825 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
826 QLatin1Char pathSep(';');
828 QLatin1Char pathSep(':');
830 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
831 for (int ii = paths.count() - 1; ii >= 0; --ii)
832 addImportPath(paths.at(ii));
835 addImportPath(QCoreApplication::applicationDirPath());
838 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
845 Adds information to \a imports such that subsequent calls to resolveType()
846 will resolve types qualified by \a prefix by considering types found at the given \a uri.
848 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
849 added via addImportPath() (if importType is LibraryImport).
851 The \a prefix may be empty, in which case the import location is considered for
854 The base URL must already have been set with Import::setBaseUrl().
856 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
857 const QString& uri, const QString& prefix, int vmaj, int vmin,
858 QDeclarativeScriptParser::Import::Type importType,
859 const QDeclarativeDirComponents &qmldircomponentsnetwork,
860 QList<QDeclarativeError> *errors)
862 if (qmlImportTrace())
863 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
864 << uri << " " << vmaj << '.' << vmin << " "
865 << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File")
868 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
874 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
875 The \a prefix must contain the dot.
877 \a qmldirPath is the location of the qmldir file.
879 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
880 const QString &baseName, const QStringList &suffixes,
881 const QString &prefix)
883 QStringList searchPaths = filePluginPath;
884 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
885 if (!qmldirPluginPathIsRelative)
886 searchPaths.prepend(qmldirPluginPath);
888 foreach (const QString &pluginPath, searchPaths) {
890 QString resolvedPath;
892 if (pluginPath == QLatin1String(".")) {
893 if (qmldirPluginPathIsRelative)
894 resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath);
896 resolvedPath = qmldirPath.absolutePath();
898 resolvedPath = pluginPath;
901 // hack for resources, should probably go away
902 if (resolvedPath.startsWith(QLatin1Char(':')))
903 resolvedPath = QCoreApplication::applicationDirPath();
905 QDir dir(resolvedPath);
906 foreach (const QString &suffix, suffixes) {
907 QString pluginFileName = prefix;
909 pluginFileName += baseName;
910 pluginFileName += suffix;
912 QFileInfo fileInfo(dir, pluginFileName);
914 if (fileInfo.exists())
915 return fileInfo.absoluteFilePath();
919 if (qmlImportTrace())
920 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
921 << "in" << qmldirPath.absolutePath();
929 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
932 \header \i Platform \i Valid suffixes
933 \row \i Windows \i \c .dll
934 \row \i Unix/Linux \i \c .so
936 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
937 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
938 \row \i Symbian \i \c .dll
941 Version number on unix are ignored.
943 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
944 const QString &baseName)
946 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
947 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
950 << QLatin1String("d.dll") // try a qmake-style debug build first
952 << QLatin1String(".dll"));
953 #elif defined(Q_OS_SYMBIAN)
954 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
956 << QLatin1String(".dll")
957 << QLatin1String(".qtplugin"));
960 # if defined(Q_OS_DARWIN)
962 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
965 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
966 << QLatin1String(".dylib")
968 << QLatin1String(".dylib")
969 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
971 << QLatin1String(".so")
972 << QLatin1String(".bundle"),
973 QLatin1String("lib"));
974 # else // Generic Unix
975 QStringList validSuffixList;
977 # if defined(Q_OS_HPUX)
979 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
980 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
981 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
983 validSuffixList << QLatin1String(".sl");
985 validSuffixList << QLatin1String(".so");
987 # elif defined(Q_OS_AIX)
988 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
989 # elif defined(Q_OS_UNIX)
990 validSuffixList << QLatin1String(".so");
993 // Examples of valid library names:
996 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
1005 QStringList QDeclarativeImportDatabase::pluginPathList() const
1007 return filePluginPath;
1013 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
1015 filePluginPath = paths;
1021 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
1023 if (qmlImportTrace())
1024 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
1026 QUrl url = QUrl(path);
1027 if (url.isRelative() || url.scheme() == QLatin1String("file")
1028 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1029 QDir dir = QDir(path);
1030 filePluginPath.prepend(dir.canonicalPath());
1032 filePluginPath.prepend(path);
1039 void QDeclarativeImportDatabase::addImportPath(const QString& path)
1041 if (qmlImportTrace())
1042 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
1047 QUrl url = QUrl(path);
1050 if (url.isRelative() || url.scheme() == QLatin1String("file")
1051 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1052 QDir dir = QDir(path);
1053 cPath = dir.canonicalPath();
1056 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1059 if (!cPath.isEmpty()
1060 && !fileImportPath.contains(cPath))
1061 fileImportPath.prepend(cPath);
1067 QStringList QDeclarativeImportDatabase::importPathList() const
1069 return fileImportPath;
1075 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1077 fileImportPath = paths;
1083 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors)
1085 if (qmlImportTrace())
1086 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1088 #ifndef QT_NO_LIBRARY
1089 QFileInfo fileInfo(filePath);
1090 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1092 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1093 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1095 if (typesRegistered) {
1096 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1097 "QDeclarativeImportDatabase::importExtension",
1098 "Internal error: Plugin imported previously with different uri");
1101 if (!engineInitialized || !typesRegistered) {
1102 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1104 QDeclarativeError error;
1105 error.setDescription(tr("File name case mismatch for \"%2\"").arg(absoluteFilePath));
1106 errors->prepend(error);
1110 QPluginLoader loader(absoluteFilePath);
1112 if (!loader.load()) {
1114 QDeclarativeError error;
1115 error.setDescription(loader.errorString());
1116 errors->prepend(error);
1121 if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) {
1123 const QByteArray bytes = uri.toUtf8();
1124 const char *moduleId = bytes.constData();
1125 if (!typesRegistered) {
1127 // ### this code should probably be protected with a mutex.
1128 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1129 iface->registerTypes(moduleId);
1131 if (!engineInitialized) {
1132 // things on the engine (eg. adding new global objects) have to be done for every engine.
1134 // protect against double initialization
1135 initializedPlugins.insert(absoluteFilePath);
1136 iface->initializeEngine(engine, moduleId);
1140 QDeclarativeError error;
1141 error.setDescription(loader.errorString());
1142 errors->prepend(error);