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>
56 DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
57 DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
59 static bool greaterThan(const QString &s1, const QString &s2)
64 QString resolveLocalUrl(const QString &url, const QString &relative)
66 if (relative.contains(QLatin1Char(':'))) {
67 // contains a host name
68 return QUrl(url).resolved(QUrl(relative)).toString();
69 } else if (relative.isEmpty()) {
71 } else if (relative.at(0) == QLatin1Char('/') || !url.contains(QLatin1Char('/'))) {
74 if (relative == QLatin1String("."))
75 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1);
76 else if (relative.startsWith(QLatin1String("./")))
77 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative.mid(2);
78 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative;
84 typedef QMap<QString, QString> StringStringMap;
85 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
87 class QDeclarativeImportedNamespace
96 QDeclarativeDirComponents qmlDirComponents;
101 bool find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
102 QDeclarativeType** type_return, QString* url_return,
103 QString *base = 0, bool *typeRecursionDetected = 0);
104 bool find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
105 QString* url_return, QString *base = 0, QList<QDeclarativeError> *errors = 0);
108 class QDeclarativeImportsPrivate {
110 QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader);
111 ~QDeclarativeImportsPrivate();
113 bool importExtension(const QString &absoluteFilePath, const QString &uri,
114 QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components,
115 QList<QDeclarativeError> *errors);
117 QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database);
118 bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
119 const QString& uri_arg, const QString& prefix,
120 int vmaj, int vmin, QDeclarativeScript::Import::Type importType,
121 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors);
122 bool find(const QString& type, int *vmajor, int *vminor,
123 QDeclarativeType** type_return, QString* url_return, QList<QDeclarativeError> *errors);
125 QDeclarativeImportedNamespace *findNamespace(const QString& type);
131 QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
132 QDeclarativeImportedNamespace unqualifiedset;
133 QHash<QString,QDeclarativeImportedNamespace* > set;
134 QDeclarativeTypeLoader *typeLoader;
138 \class QDeclarativeImports
139 \brief The QDeclarativeImports class encapsulates one QML document's import statements.
142 QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports ©)
148 QDeclarativeImports &
149 QDeclarativeImports::operator =(const QDeclarativeImports ©)
158 QDeclarativeImports::QDeclarativeImports(QDeclarativeTypeLoader *typeLoader)
159 : d(new QDeclarativeImportsPrivate(typeLoader))
163 QDeclarativeImports::~QDeclarativeImports()
170 Sets the base URL to be used for all relative file imports added.
172 void QDeclarativeImports::setBaseUrl(const QUrl& url)
175 d->base = url.toString();
179 Returns the base URL to be used for all relative file imports added.
181 QUrl QDeclarativeImports::baseUrl() const
186 void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const
188 const QDeclarativeImportedNamespace &set = d->unqualifiedset;
190 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
191 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
192 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
194 cache->m_anonymousImports.append(QDeclarativeTypeModuleVersion(module, data.minversion));
197 for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin();
198 iter != d->set.end();
201 const QDeclarativeImportedNamespace &set = *iter.value();
202 QDeclarativeTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
203 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
204 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
205 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
207 import.modules.append(QDeclarativeTypeModuleVersion(module, data.minversion));
209 QDeclarativeMetaType::ModuleApi moduleApi = QDeclarativeMetaType::moduleApi(data.uri, data.majversion, data.minversion);
210 if (moduleApi.script || moduleApi.qobject) {
211 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
212 import.moduleApi = ep->moduleApiInstance(moduleApi);
221 The given (namespace qualified) \a type is resolved to either
223 \o a QDeclarativeImportedNamespace stored at \a ns_return,
224 \o a QDeclarativeType stored at \a type_return, or
225 \o a component located at \a url_return.
228 If any return pointer is 0, the corresponding search is not done.
232 bool QDeclarativeImports::resolveType(const QString& type,
233 QDeclarativeType** type_return, QString* url_return, int *vmaj, int *vmin,
234 QDeclarativeImportedNamespace** ns_return, QList<QDeclarativeError> *errors) const
236 QDeclarativeImportedNamespace* ns = d->findNamespace(type);
242 if (type_return || url_return) {
243 if (d->find(type,vmaj,vmin,type_return,url_return, errors)) {
244 if (qmlImportTrace()) {
245 if (type_return && *type_return && url_return && !url_return->isEmpty())
246 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
247 << type << " => " << (*type_return)->typeName() << " " << *url_return;
248 if (type_return && *type_return)
249 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
250 << type << " => " << (*type_return)->typeName();
251 if (url_return && !url_return->isEmpty())
252 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
253 << type << " => " << *url_return;
264 Searching \e only in the namespace \a ns (previously returned in a call to
265 resolveType(), \a type is found and returned to either
266 a QDeclarativeType stored at \a type_return, or
267 a component located at \a url_return.
269 If either return pointer is 0, the corresponding search is not done.
271 bool QDeclarativeImports::resolveType(QDeclarativeImportedNamespace* ns, const QString& type,
272 QDeclarativeType** type_return, QString* url_return,
273 int *vmaj, int *vmin) const
275 return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return);
278 bool QDeclarativeImportedNamespace::find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
279 QDeclarativeType** type_return, QString* url_return,
280 QString *base, bool *typeRecursionDetected)
282 int vmaj = data.majversion;
283 int vmin = data.minversion;
285 if (vmaj >= 0 && vmin >= 0) {
286 QString qt = data.uri + QLatin1Char('/') + type;
287 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
289 if (vmajor) *vmajor = vmaj;
290 if (vminor) *vminor = vmin;
297 const QDeclarativeDirComponents &qmldircomponents = data.qmlDirComponents;
298 bool typeWasDeclaredInQmldir = false;
299 if (!qmldircomponents.isEmpty()) {
300 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
301 if (c.typeName == type) {
302 typeWasDeclaredInQmldir = true;
303 // importing version -1 means import ALL versions
304 if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
305 QString url(data.url + type + QLatin1String(".qml"));
306 QString candidate = resolveLocalUrl(url, c.fileName);
307 if (c.internal && base) {
308 if (resolveLocalUrl(*base, c.fileName) != candidate)
309 continue; // failed attempt to access an internal type
311 if (base && *base == candidate) {
312 if (typeRecursionDetected)
313 *typeRecursionDetected = true;
314 continue; // no recursion
317 *url_return = candidate;
324 if (!typeWasDeclaredInQmldir && !data.isLibrary) {
325 // XXX search non-files too! (eg. zip files, see QT-524)
326 QString url(data.url + type + QLatin1String(".qml"));
327 QString file = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
328 if (!typeLoader->absoluteFilePath(file).isEmpty()) {
329 if (base && *base == url) { // no recursion
330 if (typeRecursionDetected)
331 *typeRecursionDetected = true;
342 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader)
343 : ref(1), typeLoader(loader)
347 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
349 foreach (QDeclarativeImportedNamespace* s, set.values())
353 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
354 QDeclarativeImportDatabase *database,
355 QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors)
357 const QDeclarativeDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
358 if (qmldirParser->hasError()) {
360 const QList<QDeclarativeError> qmldirErrors = qmldirParser->errors(uri);
361 for (int i = 0; i < qmldirErrors.size(); ++i)
362 errors->prepend(qmldirErrors.at(i));
367 if (qmlImportTrace())
368 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base) << "::importExtension: "
369 << "loaded " << absoluteFilePath;
371 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
372 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
374 QString qmldirPath = absoluteFilePath;
375 int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
377 qmldirPath.truncate(slash);
378 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser->plugins()) {
380 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
381 if (!resolvedFilePath.isEmpty()) {
382 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
384 // XXX TODO: should we leave the import plugin error alone?
385 // Here, we pop it off the top and coalesce it into this error's message.
386 // The reason is that the lower level may add url and line/column numbering information.
387 QDeclarativeError poppedError = errors->takeFirst();
388 QDeclarativeError error;
389 error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
390 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
391 errors->prepend(error);
397 QDeclarativeError error;
398 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
399 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
400 errors->prepend(error);
408 *components = qmldirParser->components();
413 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
415 QString dir = dir_arg;
416 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
419 QStringList paths = database->fileImportPath;
420 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
422 QString stableRelativePath = dir;
423 foreach(const QString &path, paths) {
424 if (dir.startsWith(path)) {
425 stableRelativePath = dir.mid(path.length()+1);
430 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
432 // remove optional versioning in dot notation from uri
433 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
434 if (lastSlash >= 0) {
435 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
437 stableRelativePath = stableRelativePath.left(versionDot);
440 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
441 return stableRelativePath;
444 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
445 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
446 QDeclarativeScript::Import::Type importType,
447 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors)
449 static QLatin1String Slash_qmldir("/qmldir");
450 static QLatin1Char Slash('/');
452 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
453 QString uri = uri_arg;
454 QDeclarativeImportedNamespace *s;
455 if (prefix.isEmpty()) {
458 s = set.value(prefix);
460 set.insert(prefix,(s=new QDeclarativeImportedNamespace));
463 bool versionFound = false;
464 if (importType == QDeclarativeScript::Import::Library) {
466 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
468 url.replace(QLatin1Char('.'), Slash);
473 // step 1: search for extension with fully encoded version number
474 foreach (const QString &p, database->fileImportPath) {
477 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
478 const QString absoluteFilePath = fi.absoluteFilePath();
483 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
484 uri = resolvedUri(dir, database);
485 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
491 // step 2: search for extension with encoded version major
492 foreach (const QString &p, database->fileImportPath) {
495 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
496 const QString absoluteFilePath = fi.absoluteFilePath();
501 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
502 uri = resolvedUri(dir, database);
503 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
510 // step 3: search for extension without version number
512 foreach (const QString &p, database->fileImportPath) {
514 qmldir = dir+Slash_qmldir;
516 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
517 if (!absoluteFilePath.isEmpty()) {
519 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
520 url = QLatin1String("file://") + absolutePath;
521 uri = resolvedUri(dir, database);
522 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
529 if (QDeclarativeMetaType::isModule(uri, vmaj, vmin))
532 if (!versionFound && qmldircomponents.isEmpty()) {
534 QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
535 if (QDeclarativeMetaType::isAnyModule(uri))
536 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
538 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
539 errors->prepend(error);
544 if (importType == QDeclarativeScript::Import::File && qmldircomponents.isEmpty()) {
545 QString importUrl = resolveLocalUrl(base, uri + Slash_qmldir);
546 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
547 if (!localFileOrQrc.isEmpty()) {
548 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
549 if (!typeLoader->directoryExists(dir)) {
551 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
552 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
553 error.setUrl(QUrl(importUrl));
554 errors->prepend(error);
556 return false; // local import dirs must exist
558 uri = resolvedUri(dir, database);
559 if (uri.endsWith(Slash))
561 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
562 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors))
566 if (prefix.isEmpty()) {
567 // directory must at least exist for valid import
568 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
569 if (!typeLoader->directoryExists(localFileOrQrc)) {
571 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
572 if (localFileOrQrc.isEmpty())
573 error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
575 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri));
576 error.setUrl(QUrl(importUrl));
577 errors->prepend(error);
585 url = resolveLocalUrl(base, url);
588 if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
589 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
590 int lowest_min = INT_MAX;
591 int highest_min = INT_MIN;
592 for (; it != qmldircomponents.end(); ++it) {
593 if (it->majorVersion == vmaj) {
594 lowest_min = qMin(lowest_min, it->minorVersion);
595 highest_min = qMax(highest_min, it->minorVersion);
598 if (lowest_min > vmin || highest_min < vmin) {
600 QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
601 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
602 errors->prepend(error);
608 if (!url.endsWith(Slash))
611 QDeclarativeImportedNamespace::Data data;
614 data.majversion = vmaj;
615 data.minversion = vmin;
616 data.isLibrary = importType == QDeclarativeScript::Import::Library;
617 data.qmlDirComponents = qmldircomponents;
618 s->imports.prepend(data);
623 bool QDeclarativeImportsPrivate::find(const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
624 QString* url_return, QList<QDeclarativeError> *errors)
626 QDeclarativeImportedNamespace *s = 0;
627 int slash = type.indexOf(QLatin1Char('/'));
629 QString namespaceName = type.left(slash);
630 s = set.value(namespaceName);
633 QDeclarativeError error;
634 error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
635 errors->prepend(error);
639 int nslash = type.indexOf(QLatin1Char('/'),slash+1);
642 QDeclarativeError error;
643 error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed"));
644 errors->prepend(error);
651 QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
653 if (s->find(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
655 if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
656 // qualified, and only 1 url
657 *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml"));
665 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
667 return set.value(type);
670 bool QDeclarativeImportedNamespace::find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
671 QString* url_return, QString *base, QList<QDeclarativeError> *errors)
673 bool typeRecursionDetected = false;
674 for (int i=0; i<imports.count(); ++i) {
675 if (find_helper(typeLoader, imports.at(i), type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
676 if (qmlCheckTypes()) {
677 // check for type clashes
678 for (int j = i+1; j<imports.count(); ++j) {
679 if (find_helper(typeLoader, imports.at(j), type, vmajor, vminor, 0, 0, base)) {
681 QString u1 = imports.at(i).url;
682 QString u2 = imports.at(j).url;
685 int slash = b.lastIndexOf(QLatin1Char('/'));
688 QString l = b.left(slash);
689 if (u1.startsWith(b))
690 u1 = u1.mid(b.count());
692 u1 = QDeclarativeImportDatabase::tr("local directory");
693 if (u2.startsWith(b))
694 u2 = u2.mid(b.count());
696 u2 = QDeclarativeImportDatabase::tr("local directory");
700 QDeclarativeError error;
702 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
704 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
706 .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
707 .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
709 errors->prepend(error);
719 QDeclarativeError error;
720 if (typeRecursionDetected)
721 error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively"));
723 error.setDescription(QDeclarativeImportDatabase::tr("is not a type"));
724 errors->prepend(error);
730 \class QDeclarativeImportDatabase
731 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
734 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
737 filePluginPath << QLatin1String(".");
739 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
741 #ifndef QT_NO_SETTINGS
742 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
743 addImportPath(installImportsPath);
744 #endif // QT_NO_SETTINGS
747 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
748 if (!envImportPath.isEmpty()) {
749 #if defined(Q_OS_WIN)
750 QLatin1Char pathSep(';');
752 QLatin1Char pathSep(':');
754 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
755 for (int ii = paths.count() - 1; ii >= 0; --ii)
756 addImportPath(paths.at(ii));
759 addImportPath(QCoreApplication::applicationDirPath());
762 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
769 Adds information to \a imports such that subsequent calls to resolveType()
770 will resolve types qualified by \a prefix by considering types found at the given \a uri.
772 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
773 added via addImportPath() (if importType is LibraryImport).
775 The \a prefix may be empty, in which case the import location is considered for
778 The base URL must already have been set with Import::setBaseUrl().
780 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
781 const QString& uri, const QString& prefix, int vmaj, int vmin,
782 QDeclarativeScript::Import::Type importType,
783 const QDeclarativeDirComponents &qmldircomponentsnetwork,
784 QList<QDeclarativeError> *errors)
786 if (qmlImportTrace())
787 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
788 << uri << " " << vmaj << '.' << vmin << " "
789 << (importType==QDeclarativeScript::Import::Library? "Library" : "File")
792 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
798 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
799 The \a prefix must contain the dot.
801 \a qmldirPath is the location of the qmldir file.
803 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
804 const QString &qmldirPath, const QString &qmldirPluginPath,
805 const QString &baseName, const QStringList &suffixes,
806 const QString &prefix)
808 QStringList searchPaths = filePluginPath;
809 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
810 if (!qmldirPluginPathIsRelative)
811 searchPaths.prepend(qmldirPluginPath);
813 foreach (const QString &pluginPath, searchPaths) {
815 QString resolvedPath;
816 if (pluginPath == QLatin1String(".")) {
817 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
818 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath);
820 resolvedPath = qmldirPath;
822 if (QDir::isRelativePath(pluginPath))
823 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath);
825 resolvedPath = pluginPath;
828 // hack for resources, should probably go away
829 if (resolvedPath.startsWith(QLatin1Char(':')))
830 resolvedPath = QCoreApplication::applicationDirPath();
832 if (!resolvedPath.endsWith(QLatin1Char('/')))
833 resolvedPath += QLatin1Char('/');
835 foreach (const QString &suffix, suffixes) {
836 QString pluginFileName = prefix;
838 pluginFileName += baseName;
839 pluginFileName += suffix;
841 QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
842 if (!absolutePath.isEmpty())
847 if (qmlImportTrace())
848 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
849 << "in" << qmldirPath;
857 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
860 \header \i Platform \i Valid suffixes
861 \row \i Windows \i \c .dll
862 \row \i Unix/Linux \i \c .so
864 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
865 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
868 Version number on unix are ignored.
870 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
871 const QString &qmldirPath, const QString &qmldirPluginPath,
872 const QString &baseName)
874 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
875 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
878 << QLatin1String("d.dll") // try a qmake-style debug build first
880 << QLatin1String(".dll"));
883 # if defined(Q_OS_DARWIN)
885 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
888 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
889 << QLatin1String(".dylib")
891 << QLatin1String(".dylib")
892 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
894 << QLatin1String(".so")
895 << QLatin1String(".bundle"),
896 QLatin1String("lib"));
897 # else // Generic Unix
898 QStringList validSuffixList;
900 # if defined(Q_OS_HPUX)
902 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
903 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
904 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
906 validSuffixList << QLatin1String(".sl");
908 validSuffixList << QLatin1String(".so");
910 # elif defined(Q_OS_AIX)
911 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
912 # elif defined(Q_OS_UNIX)
913 validSuffixList << QLatin1String(".so");
916 // Examples of valid library names:
919 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
928 QStringList QDeclarativeImportDatabase::pluginPathList() const
930 return filePluginPath;
936 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
938 filePluginPath = paths;
944 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
946 if (qmlImportTrace())
947 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
949 QUrl url = QUrl(path);
950 if (url.isRelative() || url.scheme() == QLatin1String("file")
951 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
952 QDir dir = QDir(path);
953 filePluginPath.prepend(dir.canonicalPath());
955 filePluginPath.prepend(path);
962 void QDeclarativeImportDatabase::addImportPath(const QString& path)
964 if (qmlImportTrace())
965 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
970 QUrl url = QUrl(path);
973 if (url.isRelative() || url.scheme() == QLatin1String("file")
974 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
975 QDir dir = QDir(path);
976 cPath = dir.canonicalPath();
979 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
983 && !fileImportPath.contains(cPath))
984 fileImportPath.prepend(cPath);
990 QStringList QDeclarativeImportDatabase::importPathList() const
992 return fileImportPath;
998 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1000 fileImportPath = paths;
1006 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors)
1008 if (qmlImportTrace())
1009 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1011 #ifndef QT_NO_LIBRARY
1012 QFileInfo fileInfo(filePath);
1013 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1015 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1016 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1018 if (typesRegistered) {
1019 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1020 "QDeclarativeImportDatabase::importExtension",
1021 "Internal error: Plugin imported previously with different uri");
1024 if (!engineInitialized || !typesRegistered) {
1025 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1027 QDeclarativeError error;
1028 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1029 errors->prepend(error);
1033 QPluginLoader loader(absoluteFilePath);
1035 if (!loader.load()) {
1037 QDeclarativeError error;
1038 error.setDescription(loader.errorString());
1039 errors->prepend(error);
1044 QObject *instance = loader.instance();
1045 if (QDeclarativeTypesExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(instance)) {
1047 const QByteArray bytes = uri.toUtf8();
1048 const char *moduleId = bytes.constData();
1049 if (!typesRegistered) {
1051 // XXX thread this code should probably be protected with a mutex.
1052 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1053 iface->registerTypes(moduleId);
1055 if (!engineInitialized) {
1056 // things on the engine (eg. adding new global objects) have to be done for every
1058 // XXX protect against double initialization
1059 initializedPlugins.insert(absoluteFilePath);
1061 QDeclarativeExtensionInterface *eiface =
1062 qobject_cast<QDeclarativeExtensionInterface *>(instance);
1064 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
1065 ep->typeLoader.initializeEngine(eiface, moduleId);
1070 QDeclarativeError error;
1071 error.setDescription(loader.errorString());
1072 errors->prepend(error);