1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativeimport_p.h"
44 #include <QtCore/qdebug.h>
45 #include <QtCore/qdir.h>
46 #include <QtCore/qfileinfo.h>
47 #include <QtCore/qpluginloader.h>
48 #include <QtCore/qlibraryinfo.h>
49 #include <QtDeclarative/qdeclarativeextensioninterface.h>
50 #include <private/qdeclarativeglobal_p.h>
51 #include <private/qdeclarativetypenamecache_p.h>
52 #include <private/qdeclarativeengine_p.h>
55 #include <private/qcore_symbian_p.h>
60 DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
61 DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
63 static bool greaterThan(const QString &s1, const QString &s2)
68 QString resolveLocalUrl(const QString &url, const QString &relative)
70 if (relative.contains(QLatin1Char(':'))) {
71 // contains a host name
72 return QUrl(url).resolved(QUrl(relative)).toString();
73 } else if (relative.isEmpty()) {
75 } else if (relative.at(0) == QLatin1Char('/') || !url.contains(QLatin1Char('/'))) {
78 if (relative == QLatin1String("."))
79 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1);
80 else if (relative.startsWith(QLatin1String("./")))
81 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative.mid(2);
82 return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative;
88 typedef QMap<QString, QString> StringStringMap;
89 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
91 class QDeclarativeImportedNamespace
100 QDeclarativeDirComponents qmlDirComponents;
105 bool find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
106 QDeclarativeType** type_return, QString* url_return,
107 QString *base = 0, bool *typeRecursionDetected = 0);
108 bool find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
109 QString* url_return, QString *base = 0, QList<QDeclarativeError> *errors = 0);
112 class QDeclarativeImportsPrivate {
114 QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader);
115 ~QDeclarativeImportsPrivate();
117 bool importExtension(const QString &absoluteFilePath, const QString &uri,
118 QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components,
119 QList<QDeclarativeError> *errors);
121 QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database);
122 bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
123 const QString& uri_arg, const QString& prefix,
124 int vmaj, int vmin, QDeclarativeScript::Import::Type importType,
125 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors);
126 bool find(const QString& type, int *vmajor, int *vminor,
127 QDeclarativeType** type_return, QString* url_return, QList<QDeclarativeError> *errors);
129 QDeclarativeImportedNamespace *findNamespace(const QString& type);
135 QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
136 QDeclarativeImportedNamespace unqualifiedset;
137 QHash<QString,QDeclarativeImportedNamespace* > set;
138 QDeclarativeTypeLoader *typeLoader;
142 \class QDeclarativeImports
143 \brief The QDeclarativeImports class encapsulates one QML document's import statements.
146 QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports ©)
152 QDeclarativeImports &
153 QDeclarativeImports::operator =(const QDeclarativeImports ©)
162 QDeclarativeImports::QDeclarativeImports(QDeclarativeTypeLoader *typeLoader)
163 : d(new QDeclarativeImportsPrivate(typeLoader))
167 QDeclarativeImports::~QDeclarativeImports()
174 Sets the base URL to be used for all relative file imports added.
176 void QDeclarativeImports::setBaseUrl(const QUrl& url)
179 d->base = url.toString();
183 Returns the base URL to be used for all relative file imports added.
185 QUrl QDeclarativeImports::baseUrl() const
190 void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const
192 const QDeclarativeImportedNamespace &set = d->unqualifiedset;
194 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
195 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
196 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
198 cache->m_anonymousImports.append(QDeclarativeTypeModuleVersion(module, data.minversion));
201 for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin();
202 iter != d->set.end();
205 const QDeclarativeImportedNamespace &set = *iter.value();
206 QDeclarativeTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
207 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
208 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
209 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
211 import.modules.append(QDeclarativeTypeModuleVersion(module, data.minversion));
213 QDeclarativeMetaType::ModuleApi moduleApi = QDeclarativeMetaType::moduleApi(data.uri, data.majversion, data.minversion);
214 if (moduleApi.script || moduleApi.qobject) {
215 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
216 import.moduleApi = ep->moduleApiInstance(moduleApi);
225 The given (namespace qualified) \a type is resolved to either
227 \o a QDeclarativeImportedNamespace stored at \a ns_return,
228 \o a QDeclarativeType stored at \a type_return, or
229 \o a component located at \a url_return.
232 If any return pointer is 0, the corresponding search is not done.
236 bool QDeclarativeImports::resolveType(const QString& type,
237 QDeclarativeType** type_return, QString* url_return, int *vmaj, int *vmin,
238 QDeclarativeImportedNamespace** ns_return, QList<QDeclarativeError> *errors) const
240 QDeclarativeImportedNamespace* ns = d->findNamespace(type);
246 if (type_return || url_return) {
247 if (d->find(type,vmaj,vmin,type_return,url_return, errors)) {
248 if (qmlImportTrace()) {
249 if (type_return && *type_return && url_return && !url_return->isEmpty())
250 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
251 << type << " => " << (*type_return)->typeName() << " " << *url_return;
252 if (type_return && *type_return)
253 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
254 << type << " => " << (*type_return)->typeName();
255 if (url_return && !url_return->isEmpty())
256 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
257 << type << " => " << *url_return;
268 Searching \e only in the namespace \a ns (previously returned in a call to
269 resolveType(), \a type is found and returned to either
270 a QDeclarativeType stored at \a type_return, or
271 a component located at \a url_return.
273 If either return pointer is 0, the corresponding search is not done.
275 bool QDeclarativeImports::resolveType(QDeclarativeImportedNamespace* ns, const QString& type,
276 QDeclarativeType** type_return, QString* url_return,
277 int *vmaj, int *vmin) const
279 return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return);
282 bool QDeclarativeImportedNamespace::find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
283 QDeclarativeType** type_return, QString* url_return,
284 QString *base, bool *typeRecursionDetected)
286 int vmaj = data.majversion;
287 int vmin = data.minversion;
289 if (vmaj >= 0 && vmin >= 0) {
290 QString qt = data.uri + QLatin1Char('/') + type;
291 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
293 if (vmajor) *vmajor = vmaj;
294 if (vminor) *vminor = vmin;
301 const QDeclarativeDirComponents &qmldircomponents = data.qmlDirComponents;
302 bool typeWasDeclaredInQmldir = false;
303 if (!qmldircomponents.isEmpty()) {
304 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
305 if (c.typeName == type) {
306 typeWasDeclaredInQmldir = true;
307 // importing version -1 means import ALL versions
308 if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
309 QString url(data.url + type + QLatin1String(".qml"));
310 QString candidate = resolveLocalUrl(url, c.fileName);
311 if (c.internal && base) {
312 if (resolveLocalUrl(*base, c.fileName) != candidate)
313 continue; // failed attempt to access an internal type
315 if (base && *base == candidate) {
316 if (typeRecursionDetected)
317 *typeRecursionDetected = true;
318 continue; // no recursion
321 *url_return = candidate;
328 if (!typeWasDeclaredInQmldir && !data.isLibrary) {
329 // XXX search non-files too! (eg. zip files, see QT-524)
330 QString url(data.url + type + QLatin1String(".qml"));
331 QString file = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
332 if (!typeLoader->absoluteFilePath(file).isEmpty()) {
333 if (base && *base == url) { // no recursion
334 if (typeRecursionDetected)
335 *typeRecursionDetected = true;
346 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader)
347 : ref(1), typeLoader(loader)
351 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
353 foreach (QDeclarativeImportedNamespace* s, set.values())
357 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
358 QDeclarativeImportDatabase *database,
359 QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors)
361 const QDeclarativeDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
362 if (qmldirParser->hasError()) {
364 const QList<QDeclarativeError> qmldirErrors = qmldirParser->errors(uri);
365 for (int i = 0; i < qmldirErrors.size(); ++i)
366 errors->prepend(qmldirErrors.at(i));
371 if (qmlImportTrace())
372 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base) << "::importExtension: "
373 << "loaded " << absoluteFilePath;
375 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
376 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
378 QString qmldirPath = absoluteFilePath;
379 int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
381 qmldirPath.truncate(slash);
382 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser->plugins()) {
384 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
385 #if defined(QT_LIBINFIX) && defined(Q_OS_SYMBIAN)
386 if (resolvedFilePath.isEmpty()) {
387 // In case of libinfixed build, attempt to load libinfixed version, too.
388 QString infixedPluginName = plugin.name + QLatin1String(QT_LIBINFIX);
389 resolvedFilePath = database->resolvePlugin(dir, plugin.path, infixedPluginName);
392 if (!resolvedFilePath.isEmpty()) {
393 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
395 // XXX TODO: should we leave the import plugin error alone?
396 // Here, we pop it off the top and coalesce it into this error's message.
397 // The reason is that the lower level may add url and line/column numbering information.
398 QDeclarativeError poppedError = errors->takeFirst();
399 QDeclarativeError error;
400 error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
401 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
402 errors->prepend(error);
408 QDeclarativeError error;
409 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
410 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
411 errors->prepend(error);
419 *components = qmldirParser->components();
424 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
426 QString dir = dir_arg;
427 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
430 QStringList paths = database->fileImportPath;
431 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
433 QString stableRelativePath = dir;
434 foreach(const QString &path, paths) {
435 if (dir.startsWith(path)) {
436 stableRelativePath = dir.mid(path.length()+1);
441 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
443 // remove optional versioning in dot notation from uri
444 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
445 if (lastSlash >= 0) {
446 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
448 stableRelativePath = stableRelativePath.left(versionDot);
451 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
452 return stableRelativePath;
455 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
456 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
457 QDeclarativeScript::Import::Type importType,
458 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors)
460 static QLatin1String Slash_qmldir("/qmldir");
461 static QLatin1Char Slash('/');
463 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
464 QString uri = uri_arg;
465 QDeclarativeImportedNamespace *s;
466 if (prefix.isEmpty()) {
469 s = set.value(prefix);
471 set.insert(prefix,(s=new QDeclarativeImportedNamespace));
474 bool versionFound = false;
475 if (importType == QDeclarativeScript::Import::Library) {
477 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
479 url.replace(QLatin1Char('.'), Slash);
484 // step 1: search for extension with fully encoded version number
485 foreach (const QString &p, database->fileImportPath) {
488 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
489 const QString absoluteFilePath = fi.absoluteFilePath();
494 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
495 uri = resolvedUri(dir, database);
496 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
502 // step 2: search for extension with encoded version major
503 foreach (const QString &p, database->fileImportPath) {
506 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
507 const QString absoluteFilePath = fi.absoluteFilePath();
512 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
513 uri = resolvedUri(dir, database);
514 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
521 // step 3: search for extension without version number
523 foreach (const QString &p, database->fileImportPath) {
525 qmldir = dir+Slash_qmldir;
527 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
528 if (!absoluteFilePath.isEmpty()) {
530 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
531 url = QLatin1String("file://") + absolutePath;
532 uri = resolvedUri(dir, database);
533 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
540 if (QDeclarativeMetaType::isModule(uri, vmaj, vmin))
543 if (!versionFound && qmldircomponents.isEmpty()) {
545 QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
546 if (QDeclarativeMetaType::isAnyModule(uri))
547 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
549 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
550 errors->prepend(error);
555 if (importType == QDeclarativeScript::Import::File && qmldircomponents.isEmpty()) {
556 QString importUrl = resolveLocalUrl(base, uri + Slash_qmldir);
557 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
558 if (!localFileOrQrc.isEmpty()) {
559 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
560 if (!typeLoader->directoryExists(dir)) {
562 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
563 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
564 error.setUrl(QUrl(importUrl));
565 errors->prepend(error);
567 return false; // local import dirs must exist
569 uri = resolvedUri(dir, database);
570 if (uri.endsWith(Slash))
572 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
573 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors))
577 if (prefix.isEmpty()) {
578 // directory must at least exist for valid import
579 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
580 if (!typeLoader->directoryExists(localFileOrQrc)) {
582 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
583 if (localFileOrQrc.isEmpty())
584 error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
586 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri));
587 error.setUrl(QUrl(importUrl));
588 errors->prepend(error);
596 url = resolveLocalUrl(base, url);
599 if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
600 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
601 int lowest_min = INT_MAX;
602 int highest_min = INT_MIN;
603 for (; it != qmldircomponents.end(); ++it) {
604 if (it->majorVersion == vmaj) {
605 lowest_min = qMin(lowest_min, it->minorVersion);
606 highest_min = qMax(highest_min, it->minorVersion);
609 if (lowest_min > vmin || highest_min < vmin) {
611 QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
612 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
613 errors->prepend(error);
619 if (!url.endsWith(Slash))
622 QDeclarativeImportedNamespace::Data data;
625 data.majversion = vmaj;
626 data.minversion = vmin;
627 data.isLibrary = importType == QDeclarativeScript::Import::Library;
628 data.qmlDirComponents = qmldircomponents;
629 s->imports.prepend(data);
634 bool QDeclarativeImportsPrivate::find(const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
635 QString* url_return, QList<QDeclarativeError> *errors)
637 QDeclarativeImportedNamespace *s = 0;
638 int slash = type.indexOf(QLatin1Char('/'));
640 QString namespaceName = type.left(slash);
641 s = set.value(namespaceName);
644 QDeclarativeError error;
645 error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
646 errors->prepend(error);
650 int nslash = type.indexOf(QLatin1Char('/'),slash+1);
653 QDeclarativeError error;
654 error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed"));
655 errors->prepend(error);
662 QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
664 if (s->find(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
666 if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
667 // qualified, and only 1 url
668 *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml"));
676 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
678 return set.value(type);
681 bool QDeclarativeImportedNamespace::find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
682 QString* url_return, QString *base, QList<QDeclarativeError> *errors)
684 bool typeRecursionDetected = false;
685 for (int i=0; i<imports.count(); ++i) {
686 if (find_helper(typeLoader, imports.at(i), type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
687 if (qmlCheckTypes()) {
688 // check for type clashes
689 for (int j = i+1; j<imports.count(); ++j) {
690 if (find_helper(typeLoader, imports.at(j), type, vmajor, vminor, 0, 0, base)) {
692 QString u1 = imports.at(i).url;
693 QString u2 = imports.at(j).url;
696 int slash = b.lastIndexOf(QLatin1Char('/'));
699 QString l = b.left(slash);
700 if (u1.startsWith(b))
701 u1 = u1.mid(b.count());
703 u1 = QDeclarativeImportDatabase::tr("local directory");
704 if (u2.startsWith(b))
705 u2 = u2.mid(b.count());
707 u2 = QDeclarativeImportDatabase::tr("local directory");
711 QDeclarativeError error;
713 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
715 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
717 .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
718 .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
720 errors->prepend(error);
730 QDeclarativeError error;
731 if (typeRecursionDetected)
732 error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively"));
734 error.setDescription(QDeclarativeImportDatabase::tr("is not a type"));
735 errors->prepend(error);
741 \class QDeclarativeImportDatabase
742 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
745 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
748 filePluginPath << QLatin1String(".");
750 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
752 #ifndef QT_NO_SETTINGS
753 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
755 #if defined(Q_OS_SYMBIAN)
756 // Append imports path for all available drives in Symbian
757 if (installImportsPath.at(1) != QChar(QLatin1Char(':'))) {
758 QString tempPath = installImportsPath;
759 if (tempPath.at(tempPath.length() - 1) != QDir::separator()) {
760 tempPath += QDir::separator();
762 RFs& fs = qt_s60GetRFs();
763 TPtrC tempPathPtr(reinterpret_cast<const TText*> (tempPath.constData()));
764 TFindFile finder(fs);
765 TInt err = finder.FindByDir(tempPathPtr, tempPathPtr);
766 while (err == KErrNone) {
767 QString foundDir(reinterpret_cast<const QChar *>(finder.File().Ptr()),
768 finder.File().Length());
769 foundDir = QDir(foundDir).canonicalPath();
770 addImportPath(foundDir);
774 addImportPath(installImportsPath);
777 addImportPath(installImportsPath);
780 #endif // QT_NO_SETTINGS
783 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
784 if (!envImportPath.isEmpty()) {
785 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
786 QLatin1Char pathSep(';');
788 QLatin1Char pathSep(':');
790 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
791 for (int ii = paths.count() - 1; ii >= 0; --ii)
792 addImportPath(paths.at(ii));
795 addImportPath(QCoreApplication::applicationDirPath());
798 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
805 Adds information to \a imports such that subsequent calls to resolveType()
806 will resolve types qualified by \a prefix by considering types found at the given \a uri.
808 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
809 added via addImportPath() (if importType is LibraryImport).
811 The \a prefix may be empty, in which case the import location is considered for
814 The base URL must already have been set with Import::setBaseUrl().
816 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
817 const QString& uri, const QString& prefix, int vmaj, int vmin,
818 QDeclarativeScript::Import::Type importType,
819 const QDeclarativeDirComponents &qmldircomponentsnetwork,
820 QList<QDeclarativeError> *errors)
822 if (qmlImportTrace())
823 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
824 << uri << " " << vmaj << '.' << vmin << " "
825 << (importType==QDeclarativeScript::Import::Library? "Library" : "File")
828 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
834 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
835 The \a prefix must contain the dot.
837 \a qmldirPath is the location of the qmldir file.
839 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
840 const QString &qmldirPath, const QString &qmldirPluginPath,
841 const QString &baseName, const QStringList &suffixes,
842 const QString &prefix)
844 QStringList searchPaths = filePluginPath;
845 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
846 if (!qmldirPluginPathIsRelative)
847 searchPaths.prepend(qmldirPluginPath);
849 foreach (const QString &pluginPath, searchPaths) {
851 QString resolvedPath;
852 if (pluginPath == QLatin1String(".")) {
853 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
854 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath);
856 resolvedPath = qmldirPath;
858 if (QDir::isRelativePath(pluginPath))
859 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath);
861 resolvedPath = pluginPath;
864 // hack for resources, should probably go away
865 if (resolvedPath.startsWith(QLatin1Char(':')))
866 resolvedPath = QCoreApplication::applicationDirPath();
868 if (!resolvedPath.endsWith(QLatin1Char('/')))
869 resolvedPath += QLatin1Char('/');
871 foreach (const QString &suffix, suffixes) {
872 QString pluginFileName = prefix;
874 pluginFileName += baseName;
875 pluginFileName += suffix;
877 QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
878 if (!absolutePath.isEmpty())
883 if (qmlImportTrace())
884 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
885 << "in" << qmldirPath;
893 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
896 \header \i Platform \i Valid suffixes
897 \row \i Windows \i \c .dll
898 \row \i Unix/Linux \i \c .so
900 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
901 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
902 \row \i Symbian \i \c .dll
905 Version number on unix are ignored.
907 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
908 const QString &qmldirPath, const QString &qmldirPluginPath,
909 const QString &baseName)
911 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
912 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
915 << QLatin1String("d.dll") // try a qmake-style debug build first
917 << QLatin1String(".dll"));
918 #elif defined(Q_OS_SYMBIAN)
919 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
921 << QLatin1String(".dll")
922 << QLatin1String(".qtplugin"));
925 # if defined(Q_OS_DARWIN)
927 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
930 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
931 << QLatin1String(".dylib")
933 << QLatin1String(".dylib")
934 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
936 << QLatin1String(".so")
937 << QLatin1String(".bundle"),
938 QLatin1String("lib"));
939 # else // Generic Unix
940 QStringList validSuffixList;
942 # if defined(Q_OS_HPUX)
944 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
945 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
946 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
948 validSuffixList << QLatin1String(".sl");
950 validSuffixList << QLatin1String(".so");
952 # elif defined(Q_OS_AIX)
953 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
954 # elif defined(Q_OS_UNIX)
955 validSuffixList << QLatin1String(".so");
958 // Examples of valid library names:
961 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
970 QStringList QDeclarativeImportDatabase::pluginPathList() const
972 return filePluginPath;
978 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
980 filePluginPath = paths;
986 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
988 if (qmlImportTrace())
989 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
991 QUrl url = QUrl(path);
992 if (url.isRelative() || url.scheme() == QLatin1String("file")
993 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
994 QDir dir = QDir(path);
995 filePluginPath.prepend(dir.canonicalPath());
997 filePluginPath.prepend(path);
1004 void QDeclarativeImportDatabase::addImportPath(const QString& path)
1006 if (qmlImportTrace())
1007 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
1012 QUrl url = QUrl(path);
1015 if (url.isRelative() || url.scheme() == QLatin1String("file")
1016 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1017 QDir dir = QDir(path);
1018 cPath = dir.canonicalPath();
1021 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1024 if (!cPath.isEmpty()
1025 && !fileImportPath.contains(cPath))
1026 fileImportPath.prepend(cPath);
1032 QStringList QDeclarativeImportDatabase::importPathList() const
1034 return fileImportPath;
1040 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1042 fileImportPath = paths;
1048 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors)
1050 if (qmlImportTrace())
1051 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1053 #ifndef QT_NO_LIBRARY
1054 QFileInfo fileInfo(filePath);
1055 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1057 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1058 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1060 if (typesRegistered) {
1061 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1062 "QDeclarativeImportDatabase::importExtension",
1063 "Internal error: Plugin imported previously with different uri");
1066 if (!engineInitialized || !typesRegistered) {
1067 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1069 QDeclarativeError error;
1070 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1071 errors->prepend(error);
1075 QPluginLoader loader(absoluteFilePath);
1077 if (!loader.load()) {
1079 QDeclarativeError error;
1080 error.setDescription(loader.errorString());
1081 errors->prepend(error);
1086 QObject *instance = loader.instance();
1087 if (QDeclarativeTypesExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(instance)) {
1089 const QByteArray bytes = uri.toUtf8();
1090 const char *moduleId = bytes.constData();
1091 if (!typesRegistered) {
1093 // XXX thread this code should probably be protected with a mutex.
1094 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1095 iface->registerTypes(moduleId);
1097 if (!engineInitialized) {
1098 // things on the engine (eg. adding new global objects) have to be done for every
1100 // XXX protect against double initialization
1101 initializedPlugins.insert(absoluteFilePath);
1103 QDeclarativeExtensionInterface *eiface =
1104 qobject_cast<QDeclarativeExtensionInterface *>(instance);
1106 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
1107 ep->typeLoader.initializeEngine(eiface, moduleId);
1112 QDeclarativeError error;
1113 error.setDescription(loader.errorString());
1114 errors->prepend(error);