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 void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const
166 const QDeclarativeImportedNamespace &set = d->unqualifiedset;
168 for (int ii = set.uris.count() - 1; ii >= 0; --ii) {
169 const QByteArray uri = set.uris.at(ii).toUtf8(); // XXX sigh
170 int majversion = set.majversions.at(ii);
171 int minversion = set.minversions.at(ii);
172 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(uri, majversion);
173 if (module) cache->m_anonymousImports.append(QDeclarativeTypeModuleVersion(module, minversion));
176 for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin();
177 iter != d->set.end();
180 const QDeclarativeImportedNamespace &set = *iter.value();
181 QDeclarativeTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
182 for (int ii = set.uris.count() - 1; ii >= 0; --ii) {
183 const QByteArray uri = set.uris.at(ii).toUtf8(); // XXX sigh
184 int majversion = set.majversions.at(ii);
185 int minversion = set.minversions.at(ii);
186 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(uri, majversion);
187 if (module) import.modules.append(QDeclarativeTypeModuleVersion(module, minversion));
189 QDeclarativeMetaType::ModuleApi moduleApi = QDeclarativeMetaType::moduleApi(uri, majversion, minversion);
190 if (moduleApi.script || moduleApi.qobject) {
191 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
192 QDeclarativeMetaType::ModuleApiInstance *a = ep->moduleApiInstances.value(moduleApi);
194 a = new QDeclarativeMetaType::ModuleApiInstance;
195 a->scriptCallback = moduleApi.script;
196 a->qobjectCallback = moduleApi.qobject;
197 ep->moduleApiInstances.insert(moduleApi, a);
199 import.moduleApi = a;
210 The given (namespace qualified) \a type is resolved to either
212 \o a QDeclarativeImportedNamespace stored at \a ns_return,
213 \o a QDeclarativeType stored at \a type_return, or
214 \o a component located at \a url_return.
217 If any return pointer is 0, the corresponding search is not done.
221 bool QDeclarativeImports::resolveType(const QByteArray& type,
222 QDeclarativeType** type_return, QUrl* url_return, int *vmaj, int *vmin,
223 QDeclarativeImportedNamespace** ns_return, QList<QDeclarativeError> *errors) const
225 QDeclarativeImportedNamespace* ns = d->findNamespace(QString::fromUtf8(type));
231 if (type_return || url_return) {
232 if (d->find(type,vmaj,vmin,type_return,url_return, errors)) {
233 if (qmlImportTrace()) {
234 if (type_return && *type_return && url_return && !url_return->isEmpty())
235 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
236 << type << " => " << (*type_return)->typeName() << " " << *url_return;
237 if (type_return && *type_return)
238 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
239 << type << " => " << (*type_return)->typeName();
240 if (url_return && !url_return->isEmpty())
241 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
242 << type << " => " << *url_return;
253 Searching \e only in the namespace \a ns (previously returned in a call to
254 resolveType(), \a type is found and returned to either
255 a QDeclarativeType stored at \a type_return, or
256 a component located at \a url_return.
258 If either return pointer is 0, the corresponding search is not done.
260 bool QDeclarativeImports::resolveType(QDeclarativeImportedNamespace* ns, const QByteArray& type,
261 QDeclarativeType** type_return, QUrl* url_return,
262 int *vmaj, int *vmin) const
264 return ns->find(type,vmaj,vmin,type_return,url_return);
267 bool QDeclarativeImportedNamespace::find_helper(int i, const QByteArray& type, int *vmajor, int *vminor,
268 QDeclarativeType** type_return, QUrl* url_return,
269 QUrl *base, bool *typeRecursionDetected)
271 int vmaj = majversions.at(i);
272 int vmin = minversions.at(i);
274 if (vmaj >= 0 && vmin >= 0) {
275 QByteArray qt = uris.at(i).toUtf8();
279 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
281 if (vmajor) *vmajor = vmaj;
282 if (vminor) *vminor = vmin;
289 QUrl url = QUrl(urls.at(i) + QLatin1Char('/') + QString::fromUtf8(type) + QLatin1String(".qml"));
290 QDeclarativeDirComponents qmldircomponents = qmlDirComponents.at(i);
292 bool typeWasDeclaredInQmldir = false;
293 if (!qmldircomponents.isEmpty()) {
294 const QString typeName = QString::fromUtf8(type);
295 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
296 if (c.typeName == typeName) {
297 typeWasDeclaredInQmldir = true;
299 // importing version -1 means import ALL versions
300 if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
301 QUrl candidate = url.resolved(QUrl(c.fileName));
302 if (c.internal && base) {
303 if (base->resolved(QUrl(c.fileName)) != candidate)
304 continue; // failed attempt to access an internal type
306 if (base && *base == candidate) {
307 if (typeRecursionDetected)
308 *typeRecursionDetected = true;
309 continue; // no recursion
312 *url_return = candidate;
319 if (!typeWasDeclaredInQmldir && !isLibrary.at(i)) {
320 // XXX search non-files too! (eg. zip files, see QT-524)
321 QFileInfo f(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url));
323 if (base && *base == url) { // no recursion
324 if (typeRecursionDetected)
325 *typeRecursionDetected = true;
336 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate()
341 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
343 foreach (QDeclarativeImportedNamespace* s, set.values())
347 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
348 QDeclarativeImportDatabase *database,
349 QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors)
351 QFile file(absoluteFilePath);
353 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
355 QDeclarativeError error;
356 error.setDescription(QDeclarativeImportDatabase::tr("cannot load module \"%1\": File name case mismatch for \"%2\"").arg(uri).arg(absoluteFilePath));
357 errors->prepend(error);
360 } else if (file.open(QFile::ReadOnly)) {
361 filecontent = QString::fromUtf8(file.readAll());
362 if (qmlImportTrace())
363 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base.toString()) << "::importExtension: "
364 << "loaded " << absoluteFilePath;
367 QDeclarativeError error;
368 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" definition \"%2\" not readable").arg(uri).arg(absoluteFilePath));
369 errors->prepend(error);
373 QDir dir = QFileInfo(file).dir();
374 QUrl url = QUrl::fromLocalFile(absoluteFilePath);
376 QDeclarativeDirParser qmldirParser;
377 qmldirParser.setSource(filecontent);
378 qmldirParser.setUrl(url);
380 // propagate any errors reported by the parser back up to the typeloader.
381 if (qmldirParser.parse()) {
383 for (int i = 0; i < qmldirParser.errors().size(); ++i) {
384 errors->prepend(qmldirParser.errors().at(i));
390 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
391 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
394 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser.plugins()) {
396 QString resolvedFilePath = database->resolvePlugin(dir, plugin.path, plugin.name);
397 #if defined(QT_LIBINFIX) && defined(Q_OS_SYMBIAN)
398 if (resolvedFilePath.isEmpty()) {
399 // In case of libinfixed build, attempt to load libinfixed version, too.
400 QString infixedPluginName = plugin.name + QLatin1String(QT_LIBINFIX);
401 resolvedFilePath = database->resolvePlugin(dir, plugin.path, infixedPluginName);
404 if (!resolvedFilePath.isEmpty()) {
405 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
407 // XXX TODO: should we leave the import plugin error alone?
408 // Here, we pop it off the top and coalesce it into this error's message.
409 // The reason is that the lower level may add url and line/column numbering information.
410 QDeclarativeError poppedError = errors->takeFirst();
411 QDeclarativeError error;
412 error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
414 errors->prepend(error);
420 QDeclarativeError error;
421 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
423 errors->prepend(error);
431 *components = qmldirParser.components();
436 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
438 QString dir = dir_arg;
439 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
442 QStringList paths = database->fileImportPath;
443 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
445 QString stableRelativePath = dir;
446 foreach(const QString &path, paths) {
447 if (dir.startsWith(path)) {
448 stableRelativePath = dir.mid(path.length()+1);
453 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
455 // remove optional versioning in dot notation from uri
456 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
457 if (lastSlash >= 0) {
458 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
460 stableRelativePath = stableRelativePath.left(versionDot);
463 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
464 return stableRelativePath;
467 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
468 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
469 QDeclarativeScriptParser::Import::Type importType,
470 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors)
472 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
473 QString uri = uri_arg;
474 QDeclarativeImportedNamespace *s;
475 if (prefix.isEmpty()) {
478 s = set.value(prefix);
480 set.insert(prefix,(s=new QDeclarativeImportedNamespace));
483 bool versionFound = false;
484 if (importType == QDeclarativeScriptParser::Import::Library) {
486 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
488 url.replace(QLatin1Char('.'), QLatin1Char('/'));
492 // step 1: search for extension with fully encoded version number
494 foreach (const QString &p, database->fileImportPath) {
495 dir = p+QLatin1Char('/')+url;
497 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
498 const QString absoluteFilePath = fi.absoluteFilePath();
503 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
504 uri = resolvedUri(dir, database);
505 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
511 // step 2: search for extension with encoded version major
513 foreach (const QString &p, database->fileImportPath) {
514 dir = p+QLatin1Char('/')+url;
516 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
517 const QString absoluteFilePath = fi.absoluteFilePath();
522 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
523 uri = resolvedUri(dir, database);
524 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
531 // step 3: search for extension without version number
533 foreach (const QString &p, database->fileImportPath) {
534 dir = p+QLatin1Char('/')+url;
536 QFileInfo fi(dir+QLatin1String("/qmldir"));
537 const QString absoluteFilePath = fi.absoluteFilePath();
542 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
543 uri = resolvedUri(dir, database);
544 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
551 if (QDeclarativeMetaType::isModule(uri.toUtf8(), vmaj, vmin)) {
555 if (!versionFound && qmldircomponents.isEmpty()) {
557 QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
558 if (QDeclarativeMetaType::isAnyModule(uri.toUtf8()))
559 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
561 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
562 errors->prepend(error);
568 if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) {
569 QUrl importUrl = base.resolved(QUrl(uri + QLatin1String("/qmldir")));
570 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
571 if (!localFileOrQrc.isEmpty()) {
572 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
573 QFileInfo dirinfo(dir);
574 if (dir.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) {
576 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
577 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
578 error.setUrl(importUrl);
579 errors->prepend(error);
581 return false; // local import dirs must exist
583 uri = resolvedUri(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))), database);
584 if (uri.endsWith(QLatin1Char('/')))
586 if (QFile::exists(localFileOrQrc)) {
587 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors))
591 if (prefix.isEmpty()) {
592 // directory must at least exist for valid import
593 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
594 QFileInfo dirinfo(localFileOrQrc);
595 if (localFileOrQrc.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 if (localFileOrQrc.isEmpty())
599 error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
601 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri));
602 error.setUrl(importUrl);
603 errors->prepend(error);
611 url = base.resolved(QUrl(url)).toString();
612 if (url.endsWith(QLatin1Char('/')))
616 if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
617 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
618 int lowest_min = INT_MAX;
619 int highest_min = INT_MIN;
620 for (; it != qmldircomponents.end(); ++it) {
621 if (it->majorVersion == vmaj) {
622 lowest_min = qMin(lowest_min, it->minorVersion);
623 highest_min = qMax(highest_min, it->minorVersion);
626 if (lowest_min > vmin || highest_min < vmin) {
628 QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
629 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
630 errors->prepend(error);
636 s->uris.prepend(uri);
637 s->urls.prepend(url);
638 s->majversions.prepend(vmaj);
639 s->minversions.prepend(vmin);
640 s->isLibrary.prepend(importType == QDeclarativeScriptParser::Import::Library);
641 s->qmlDirComponents.prepend(qmldircomponents);
645 bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
646 QUrl* url_return, QList<QDeclarativeError> *errors)
648 QDeclarativeImportedNamespace *s = 0;
649 int slash = type.indexOf('/');
651 QString namespaceName = QString::fromUtf8(type.left(slash));
652 s = set.value(namespaceName);
655 QDeclarativeError error;
656 error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
657 errors->prepend(error);
661 int nslash = type.indexOf('/',slash+1);
664 QDeclarativeError error;
665 error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed"));
666 errors->prepend(error);
673 QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
675 if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
677 if (s->urls.count() == 1 && !s->isLibrary[0] && url_return && s != &unqualifiedset) {
678 // qualified, and only 1 url
679 *url_return = QUrl(s->urls[0]+QLatin1Char('/')).resolved(QUrl(QString::fromUtf8(unqualifiedtype) + QLatin1String(".qml")));
687 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
689 return set.value(type);
692 bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
693 QUrl* url_return, QUrl *base, QList<QDeclarativeError> *errors)
695 bool typeRecursionDetected = false;
696 for (int i=0; i<urls.count(); ++i) {
697 if (find_helper(i, type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
698 if (qmlCheckTypes()) {
699 // check for type clashes
700 for (int j = i+1; j<urls.count(); ++j) {
701 if (find_helper(j, type, vmajor, vminor, 0, 0, base)) {
703 QString u1 = urls.at(i);
704 QString u2 = urls.at(j);
706 QString b = base->toString();
707 int slash = b.lastIndexOf(QLatin1Char('/'));
710 QString l = b.left(slash);
711 if (u1.startsWith(b))
712 u1 = u1.mid(b.count());
714 u1 = QDeclarativeImportDatabase::tr("local directory");
715 if (u2.startsWith(b))
716 u2 = u2.mid(b.count());
718 u2 = QDeclarativeImportDatabase::tr("local directory");
722 QDeclarativeError error;
724 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
726 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
728 .arg(majversions.at(i)).arg(minversions.at(i))
729 .arg(majversions.at(j)).arg(minversions.at(j)));
731 errors->prepend(error);
741 QDeclarativeError error;
742 if (typeRecursionDetected)
743 error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively"));
745 error.setDescription(QDeclarativeImportDatabase::tr("is not a type"));
746 errors->prepend(error);
752 \class QDeclarativeImportDatabase
753 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
756 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
759 filePluginPath << QLatin1String(".");
761 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
763 #ifndef QT_NO_SETTINGS
764 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
766 #if defined(Q_OS_SYMBIAN)
767 // Append imports path for all available drives in Symbian
768 if (installImportsPath.at(1) != QChar(QLatin1Char(':'))) {
769 QString tempPath = installImportsPath;
770 if (tempPath.at(tempPath.length() - 1) != QDir::separator()) {
771 tempPath += QDir::separator();
773 RFs& fs = qt_s60GetRFs();
774 TPtrC tempPathPtr(reinterpret_cast<const TText*> (tempPath.constData()));
775 TFindFile finder(fs);
776 TInt err = finder.FindByDir(tempPathPtr, tempPathPtr);
777 while (err == KErrNone) {
778 QString foundDir(reinterpret_cast<const QChar *>(finder.File().Ptr()),
779 finder.File().Length());
780 foundDir = QDir(foundDir).canonicalPath();
781 addImportPath(foundDir);
785 addImportPath(installImportsPath);
788 addImportPath(installImportsPath);
791 #endif // QT_NO_SETTINGS
794 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
795 if (!envImportPath.isEmpty()) {
796 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
797 QLatin1Char pathSep(';');
799 QLatin1Char pathSep(':');
801 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
802 for (int ii = paths.count() - 1; ii >= 0; --ii)
803 addImportPath(paths.at(ii));
806 addImportPath(QCoreApplication::applicationDirPath());
809 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
816 Adds information to \a imports such that subsequent calls to resolveType()
817 will resolve types qualified by \a prefix by considering types found at the given \a uri.
819 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
820 added via addImportPath() (if importType is LibraryImport).
822 The \a prefix may be empty, in which case the import location is considered for
825 The base URL must already have been set with Import::setBaseUrl().
827 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
828 const QString& uri, const QString& prefix, int vmaj, int vmin,
829 QDeclarativeScriptParser::Import::Type importType,
830 const QDeclarativeDirComponents &qmldircomponentsnetwork,
831 QList<QDeclarativeError> *errors)
833 if (qmlImportTrace())
834 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
835 << uri << " " << vmaj << '.' << vmin << " "
836 << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File")
839 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
845 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
846 The \a prefix must contain the dot.
848 \a qmldirPath is the location of the qmldir file.
850 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
851 const QString &baseName, const QStringList &suffixes,
852 const QString &prefix)
854 QStringList searchPaths = filePluginPath;
855 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
856 if (!qmldirPluginPathIsRelative)
857 searchPaths.prepend(qmldirPluginPath);
859 foreach (const QString &pluginPath, searchPaths) {
861 QString resolvedPath;
863 if (pluginPath == QLatin1String(".")) {
864 if (qmldirPluginPathIsRelative)
865 resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath);
867 resolvedPath = qmldirPath.absolutePath();
869 resolvedPath = pluginPath;
872 // hack for resources, should probably go away
873 if (resolvedPath.startsWith(QLatin1Char(':')))
874 resolvedPath = QCoreApplication::applicationDirPath();
876 QDir dir(resolvedPath);
877 foreach (const QString &suffix, suffixes) {
878 QString pluginFileName = prefix;
880 pluginFileName += baseName;
881 pluginFileName += suffix;
883 QFileInfo fileInfo(dir, pluginFileName);
885 if (fileInfo.exists())
886 return fileInfo.absoluteFilePath();
890 if (qmlImportTrace())
891 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
892 << "in" << qmldirPath.absolutePath();
900 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
903 \header \i Platform \i Valid suffixes
904 \row \i Windows \i \c .dll
905 \row \i Unix/Linux \i \c .so
907 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
908 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
909 \row \i Symbian \i \c .dll
912 Version number on unix are ignored.
914 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
915 const QString &baseName)
917 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
918 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
921 << QLatin1String("d.dll") // try a qmake-style debug build first
923 << QLatin1String(".dll"));
924 #elif defined(Q_OS_SYMBIAN)
925 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
927 << QLatin1String(".dll")
928 << QLatin1String(".qtplugin"));
931 # if defined(Q_OS_DARWIN)
933 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
936 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
937 << QLatin1String(".dylib")
939 << QLatin1String(".dylib")
940 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
942 << QLatin1String(".so")
943 << QLatin1String(".bundle"),
944 QLatin1String("lib"));
945 # else // Generic Unix
946 QStringList validSuffixList;
948 # if defined(Q_OS_HPUX)
950 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
951 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
952 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
954 validSuffixList << QLatin1String(".sl");
956 validSuffixList << QLatin1String(".so");
958 # elif defined(Q_OS_AIX)
959 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
960 # elif defined(Q_OS_UNIX)
961 validSuffixList << QLatin1String(".so");
964 // Examples of valid library names:
967 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
976 QStringList QDeclarativeImportDatabase::pluginPathList() const
978 return filePluginPath;
984 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
986 filePluginPath = paths;
992 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
994 if (qmlImportTrace())
995 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
997 QUrl url = QUrl(path);
998 if (url.isRelative() || url.scheme() == QLatin1String("file")
999 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1000 QDir dir = QDir(path);
1001 filePluginPath.prepend(dir.canonicalPath());
1003 filePluginPath.prepend(path);
1010 void QDeclarativeImportDatabase::addImportPath(const QString& path)
1012 if (qmlImportTrace())
1013 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
1018 QUrl url = QUrl(path);
1021 if (url.isRelative() || url.scheme() == QLatin1String("file")
1022 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1023 QDir dir = QDir(path);
1024 cPath = dir.canonicalPath();
1027 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1030 if (!cPath.isEmpty()
1031 && !fileImportPath.contains(cPath))
1032 fileImportPath.prepend(cPath);
1038 QStringList QDeclarativeImportDatabase::importPathList() const
1040 return fileImportPath;
1046 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1048 fileImportPath = paths;
1054 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors)
1056 if (qmlImportTrace())
1057 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1059 #ifndef QT_NO_LIBRARY
1060 QFileInfo fileInfo(filePath);
1061 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1063 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1064 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1066 if (typesRegistered) {
1067 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1068 "QDeclarativeImportDatabase::importExtension",
1069 "Internal error: Plugin imported previously with different uri");
1072 if (!engineInitialized || !typesRegistered) {
1073 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1075 QDeclarativeError error;
1076 error.setDescription(tr("File name case mismatch for \"%2\"").arg(absoluteFilePath));
1077 errors->prepend(error);
1081 QPluginLoader loader(absoluteFilePath);
1083 if (!loader.load()) {
1085 QDeclarativeError error;
1086 error.setDescription(loader.errorString());
1087 errors->prepend(error);
1092 if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) {
1094 const QByteArray bytes = uri.toUtf8();
1095 const char *moduleId = bytes.constData();
1096 if (!typesRegistered) {
1098 // ### this code should probably be protected with a mutex.
1099 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1100 iface->registerTypes(moduleId);
1102 if (!engineInitialized) {
1103 // things on the engine (eg. adding new global objects) have to be done for every engine.
1105 // protect against double initialization
1106 initializedPlugins.insert(absoluteFilePath);
1107 iface->initializeEngine(engine, moduleId);
1111 QDeclarativeError error;
1112 error.setDescription(loader.errorString());
1113 errors->prepend(error);