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 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 (major < 0 || 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 if (vmaj >= 0 && vmin >= 0) {
299 QByteArray qt = uris.at(i).toUtf8();
303 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
305 if (vmajor) *vmajor = vmaj;
306 if (vminor) *vminor = vmin;
313 QUrl url = QUrl(urls.at(i) + QLatin1Char('/') + QString::fromUtf8(type) + QLatin1String(".qml"));
314 QDeclarativeDirComponents qmldircomponents = qmlDirComponents.at(i);
316 bool typeWasDeclaredInQmldir = false;
317 if (!qmldircomponents.isEmpty()) {
318 const QString typeName = QString::fromUtf8(type);
319 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
320 if (c.typeName == typeName) {
321 typeWasDeclaredInQmldir = true;
323 // importing version -1 means import ALL versions
324 if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
325 QUrl candidate = url.resolved(QUrl(c.fileName));
326 if (c.internal && base) {
327 if (base->resolved(QUrl(c.fileName)) != candidate)
328 continue; // failed attempt to access an internal type
330 if (base && *base == candidate) {
331 if (typeRecursionDetected)
332 *typeRecursionDetected = true;
333 continue; // no recursion
336 *url_return = candidate;
343 if (!typeWasDeclaredInQmldir && !isLibrary.at(i)) {
344 // XXX search non-files too! (eg. zip files, see QT-524)
345 QFileInfo f(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url));
347 if (base && *base == url) { // no recursion
348 if (typeRecursionDetected)
349 *typeRecursionDetected = true;
360 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate()
365 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
367 foreach (QDeclarativeImportedNamespace* s, set.values())
371 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
372 QDeclarativeImportDatabase *database,
373 QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors)
375 QFile file(absoluteFilePath);
377 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
379 QDeclarativeError error;
380 error.setDescription(QDeclarativeImportDatabase::tr("cannot load module \"%1\": File name case mismatch for \"%2\"").arg(uri).arg(absoluteFilePath));
381 errors->prepend(error);
384 } else if (file.open(QFile::ReadOnly)) {
385 filecontent = QString::fromUtf8(file.readAll());
386 if (qmlImportTrace())
387 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base.toString()) << "::importExtension: "
388 << "loaded " << absoluteFilePath;
391 QDeclarativeError error;
392 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" definition \"%2\" not readable").arg(uri).arg(absoluteFilePath));
393 errors->prepend(error);
397 QDir dir = QFileInfo(file).dir();
398 QUrl url = QUrl::fromLocalFile(absoluteFilePath);
400 QDeclarativeDirParser qmldirParser;
401 qmldirParser.setSource(filecontent);
402 qmldirParser.setUrl(url);
404 // propagate any errors reported by the parser back up to the typeloader.
405 if (qmldirParser.parse()) {
407 for (int i = 0; i < qmldirParser.errors().size(); ++i) {
408 errors->prepend(qmldirParser.errors().at(i));
414 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
415 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
418 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser.plugins()) {
420 QString resolvedFilePath = database->resolvePlugin(dir, plugin.path, plugin.name);
421 #if defined(QT_LIBINFIX) && defined(Q_OS_SYMBIAN)
422 if (resolvedFilePath.isEmpty()) {
423 // In case of libinfixed build, attempt to load libinfixed version, too.
424 QString infixedPluginName = plugin.name + QLatin1String(QT_LIBINFIX);
425 resolvedFilePath = database->resolvePlugin(dir, plugin.path, infixedPluginName);
428 if (!resolvedFilePath.isEmpty()) {
429 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
431 // XXX TODO: should we leave the import plugin error alone?
432 // Here, we pop it off the top and coalesce it into this error's message.
433 // The reason is that the lower level may add url and line/column numbering information.
434 QDeclarativeError poppedError = errors->takeFirst();
435 QDeclarativeError error;
436 error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
438 errors->prepend(error);
444 QDeclarativeError error;
445 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
447 errors->prepend(error);
455 *components = qmldirParser.components();
460 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
462 QString dir = dir_arg;
463 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
466 QStringList paths = database->fileImportPath;
467 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
469 QString stableRelativePath = dir;
470 foreach(const QString &path, paths) {
471 if (dir.startsWith(path)) {
472 stableRelativePath = dir.mid(path.length()+1);
477 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
479 // remove optional versioning in dot notation from uri
480 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
481 if (lastSlash >= 0) {
482 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
484 stableRelativePath = stableRelativePath.left(versionDot);
487 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
488 return stableRelativePath;
491 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
492 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
493 QDeclarativeScriptParser::Import::Type importType,
494 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors)
496 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
497 QString uri = uri_arg;
498 QDeclarativeImportedNamespace *s;
499 if (prefix.isEmpty()) {
502 s = set.value(prefix);
504 set.insert(prefix,(s=new QDeclarativeImportedNamespace));
507 bool versionFound = false;
508 if (importType == QDeclarativeScriptParser::Import::Library) {
510 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
512 url.replace(QLatin1Char('.'), QLatin1Char('/'));
517 // step 1: search for extension with fully encoded version number
518 foreach (const QString &p, database->fileImportPath) {
519 dir = p+QLatin1Char('/')+url;
521 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
522 const QString absoluteFilePath = fi.absoluteFilePath();
527 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
528 uri = resolvedUri(dir, database);
529 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
535 // step 2: search for extension with encoded version major
536 foreach (const QString &p, database->fileImportPath) {
537 dir = p+QLatin1Char('/')+url;
539 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
540 const QString absoluteFilePath = fi.absoluteFilePath();
545 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
546 uri = resolvedUri(dir, database);
547 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
554 // step 3: search for extension without version number
556 foreach (const QString &p, database->fileImportPath) {
557 dir = p+QLatin1Char('/')+url;
559 QFileInfo fi(dir+QLatin1String("/qmldir"));
560 const QString absoluteFilePath = fi.absoluteFilePath();
565 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
566 uri = resolvedUri(dir, database);
567 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
574 if (QDeclarativeMetaType::isModule(uri.toUtf8(), vmaj, vmin)) {
578 if (!versionFound && qmldircomponents.isEmpty()) {
580 bool anyversion = QDeclarativeMetaType::isAnyModule(uri.toUtf8());
581 QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
583 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
585 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
586 errors->prepend(error);
592 if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) {
593 QUrl importUrl = base.resolved(QUrl(uri + QLatin1String("/qmldir")));
594 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
595 if (!localFileOrQrc.isEmpty()) {
596 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
597 QFileInfo dirinfo(dir);
598 if (dir.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) {
600 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
601 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
602 error.setUrl(importUrl);
603 errors->prepend(error);
605 return false; // local import dirs must exist
607 uri = resolvedUri(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))), database);
608 if (uri.endsWith(QLatin1Char('/')))
610 if (QFile::exists(localFileOrQrc)) {
611 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors))
615 if (prefix.isEmpty()) {
616 // directory must at least exist for valid import
617 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
618 QFileInfo dirinfo(localFileOrQrc);
619 if (localFileOrQrc.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) {
621 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
622 if (localFileOrQrc.isEmpty())
623 error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
625 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri));
626 error.setUrl(importUrl);
627 errors->prepend(error);
635 url = base.resolved(QUrl(url)).toString();
636 if (url.endsWith(QLatin1Char('/')))
640 if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
641 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
642 int lowest_min = INT_MAX;
643 int highest_min = INT_MIN;
644 for (; it != qmldircomponents.end(); ++it) {
645 if (it->majorVersion == vmaj) {
646 lowest_min = qMin(lowest_min, it->minorVersion);
647 highest_min = qMax(highest_min, it->minorVersion);
650 if (lowest_min > vmin || highest_min < vmin) {
652 QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
653 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
654 errors->prepend(error);
660 s->uris.prepend(uri);
661 s->urls.prepend(url);
662 s->majversions.prepend(vmaj);
663 s->minversions.prepend(vmin);
664 s->isLibrary.prepend(importType == QDeclarativeScriptParser::Import::Library);
665 s->qmlDirComponents.prepend(qmldircomponents);
669 bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
670 QUrl* url_return, QList<QDeclarativeError> *errors)
672 QDeclarativeImportedNamespace *s = 0;
673 int slash = type.indexOf('/');
675 QString namespaceName = QString::fromUtf8(type.left(slash));
676 s = set.value(namespaceName);
679 QDeclarativeError error;
680 error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
681 errors->prepend(error);
685 int nslash = type.indexOf('/',slash+1);
688 QDeclarativeError error;
689 error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed"));
690 errors->prepend(error);
697 QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
699 if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
701 if (s->urls.count() == 1 && !s->isLibrary[0] && url_return && s != &unqualifiedset) {
702 // qualified, and only 1 url
703 *url_return = QUrl(s->urls[0]+QLatin1Char('/')).resolved(QUrl(QString::fromUtf8(unqualifiedtype) + QLatin1String(".qml")));
711 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
713 return set.value(type);
716 bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
717 QUrl* url_return, QUrl *base, QList<QDeclarativeError> *errors)
719 bool typeRecursionDetected = false;
720 for (int i=0; i<urls.count(); ++i) {
721 if (find_helper(i, type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
722 if (qmlCheckTypes()) {
723 // check for type clashes
724 for (int j = i+1; j<urls.count(); ++j) {
725 if (find_helper(j, type, vmajor, vminor, 0, 0, base)) {
727 QString u1 = urls.at(i);
728 QString u2 = urls.at(j);
730 QString b = base->toString();
731 int slash = b.lastIndexOf(QLatin1Char('/'));
734 QString l = b.left(slash);
735 if (u1.startsWith(b))
736 u1 = u1.mid(b.count());
738 u1 = QDeclarativeImportDatabase::tr("local directory");
739 if (u2.startsWith(b))
740 u2 = u2.mid(b.count());
742 u2 = QDeclarativeImportDatabase::tr("local directory");
746 QDeclarativeError error;
748 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
750 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
752 .arg(majversions.at(i)).arg(minversions.at(i))
753 .arg(majversions.at(j)).arg(minversions.at(j)));
755 errors->prepend(error);
765 QDeclarativeError error;
766 if (typeRecursionDetected)
767 error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively"));
769 error.setDescription(QDeclarativeImportDatabase::tr("is not a type"));
770 errors->prepend(error);
776 \class QDeclarativeImportDatabase
777 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
780 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
783 filePluginPath << QLatin1String(".");
785 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
787 #ifndef QT_NO_SETTINGS
788 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
790 #if defined(Q_OS_SYMBIAN)
791 // Append imports path for all available drives in Symbian
792 if (installImportsPath.at(1) != QChar(QLatin1Char(':'))) {
793 QString tempPath = installImportsPath;
794 if (tempPath.at(tempPath.length() - 1) != QDir::separator()) {
795 tempPath += QDir::separator();
797 RFs& fs = qt_s60GetRFs();
798 TPtrC tempPathPtr(reinterpret_cast<const TText*> (tempPath.constData()));
799 TFindFile finder(fs);
800 TInt err = finder.FindByDir(tempPathPtr, tempPathPtr);
801 while (err == KErrNone) {
802 QString foundDir(reinterpret_cast<const QChar *>(finder.File().Ptr()),
803 finder.File().Length());
804 foundDir = QDir(foundDir).canonicalPath();
805 addImportPath(foundDir);
809 addImportPath(installImportsPath);
812 addImportPath(installImportsPath);
815 #endif // QT_NO_SETTINGS
818 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
819 if (!envImportPath.isEmpty()) {
820 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
821 QLatin1Char pathSep(';');
823 QLatin1Char pathSep(':');
825 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
826 for (int ii = paths.count() - 1; ii >= 0; --ii)
827 addImportPath(paths.at(ii));
830 addImportPath(QCoreApplication::applicationDirPath());
833 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
840 Adds information to \a imports such that subsequent calls to resolveType()
841 will resolve types qualified by \a prefix by considering types found at the given \a uri.
843 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
844 added via addImportPath() (if importType is LibraryImport).
846 The \a prefix may be empty, in which case the import location is considered for
849 The base URL must already have been set with Import::setBaseUrl().
851 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
852 const QString& uri, const QString& prefix, int vmaj, int vmin,
853 QDeclarativeScriptParser::Import::Type importType,
854 const QDeclarativeDirComponents &qmldircomponentsnetwork,
855 QList<QDeclarativeError> *errors)
857 if (qmlImportTrace())
858 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
859 << uri << " " << vmaj << '.' << vmin << " "
860 << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File")
863 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
869 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
870 The \a prefix must contain the dot.
872 \a qmldirPath is the location of the qmldir file.
874 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
875 const QString &baseName, const QStringList &suffixes,
876 const QString &prefix)
878 QStringList searchPaths = filePluginPath;
879 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
880 if (!qmldirPluginPathIsRelative)
881 searchPaths.prepend(qmldirPluginPath);
883 foreach (const QString &pluginPath, searchPaths) {
885 QString resolvedPath;
887 if (pluginPath == QLatin1String(".")) {
888 if (qmldirPluginPathIsRelative)
889 resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath);
891 resolvedPath = qmldirPath.absolutePath();
893 resolvedPath = pluginPath;
896 // hack for resources, should probably go away
897 if (resolvedPath.startsWith(QLatin1Char(':')))
898 resolvedPath = QCoreApplication::applicationDirPath();
900 QDir dir(resolvedPath);
901 foreach (const QString &suffix, suffixes) {
902 QString pluginFileName = prefix;
904 pluginFileName += baseName;
905 pluginFileName += suffix;
907 QFileInfo fileInfo(dir, pluginFileName);
909 if (fileInfo.exists())
910 return fileInfo.absoluteFilePath();
914 if (qmlImportTrace())
915 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
916 << "in" << qmldirPath.absolutePath();
924 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
927 \header \i Platform \i Valid suffixes
928 \row \i Windows \i \c .dll
929 \row \i Unix/Linux \i \c .so
931 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
932 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
933 \row \i Symbian \i \c .dll
936 Version number on unix are ignored.
938 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
939 const QString &baseName)
941 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
942 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
945 << QLatin1String("d.dll") // try a qmake-style debug build first
947 << QLatin1String(".dll"));
948 #elif defined(Q_OS_SYMBIAN)
949 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
951 << QLatin1String(".dll")
952 << QLatin1String(".qtplugin"));
955 # if defined(Q_OS_DARWIN)
957 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
960 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
961 << QLatin1String(".dylib")
963 << QLatin1String(".dylib")
964 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
966 << QLatin1String(".so")
967 << QLatin1String(".bundle"),
968 QLatin1String("lib"));
969 # else // Generic Unix
970 QStringList validSuffixList;
972 # if defined(Q_OS_HPUX)
974 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
975 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
976 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
978 validSuffixList << QLatin1String(".sl");
980 validSuffixList << QLatin1String(".so");
982 # elif defined(Q_OS_AIX)
983 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
984 # elif defined(Q_OS_UNIX)
985 validSuffixList << QLatin1String(".so");
988 // Examples of valid library names:
991 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
1000 QStringList QDeclarativeImportDatabase::pluginPathList() const
1002 return filePluginPath;
1008 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
1010 filePluginPath = paths;
1016 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
1018 if (qmlImportTrace())
1019 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
1021 QUrl url = QUrl(path);
1022 if (url.isRelative() || url.scheme() == QLatin1String("file")
1023 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1024 QDir dir = QDir(path);
1025 filePluginPath.prepend(dir.canonicalPath());
1027 filePluginPath.prepend(path);
1034 void QDeclarativeImportDatabase::addImportPath(const QString& path)
1036 if (qmlImportTrace())
1037 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
1042 QUrl url = QUrl(path);
1045 if (url.isRelative() || url.scheme() == QLatin1String("file")
1046 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1047 QDir dir = QDir(path);
1048 cPath = dir.canonicalPath();
1051 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1054 if (!cPath.isEmpty()
1055 && !fileImportPath.contains(cPath))
1056 fileImportPath.prepend(cPath);
1062 QStringList QDeclarativeImportDatabase::importPathList() const
1064 return fileImportPath;
1070 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1072 fileImportPath = paths;
1078 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors)
1080 if (qmlImportTrace())
1081 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1083 #ifndef QT_NO_LIBRARY
1084 QFileInfo fileInfo(filePath);
1085 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1087 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1088 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1090 if (typesRegistered) {
1091 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1092 "QDeclarativeImportDatabase::importExtension",
1093 "Internal error: Plugin imported previously with different uri");
1096 if (!engineInitialized || !typesRegistered) {
1097 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1099 QDeclarativeError error;
1100 error.setDescription(tr("File name case mismatch for \"%2\"").arg(absoluteFilePath));
1101 errors->prepend(error);
1105 QPluginLoader loader(absoluteFilePath);
1107 if (!loader.load()) {
1109 QDeclarativeError error;
1110 error.setDescription(loader.errorString());
1111 errors->prepend(error);
1116 if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) {
1118 const QByteArray bytes = uri.toUtf8();
1119 const char *moduleId = bytes.constData();
1120 if (!typesRegistered) {
1122 // ### this code should probably be protected with a mutex.
1123 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1124 iface->registerTypes(moduleId);
1126 if (!engineInitialized) {
1127 // things on the engine (eg. adding new global objects) have to be done for every engine.
1129 // protect against double initialization
1130 initializedPlugins.insert(absoluteFilePath);
1131 iface->initializeEngine(engine, moduleId);
1135 QDeclarativeError error;
1136 error.setDescription(loader.errorString());
1137 errors->prepend(error);