1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
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;
97 QDeclarativeDirScripts qmlDirScripts;
102 bool find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
103 QDeclarativeType** type_return, QString* url_return,
104 QString *base = 0, bool *typeRecursionDetected = 0);
105 bool find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
106 QString* url_return, QString *base = 0, QList<QDeclarativeError> *errors = 0);
109 class QDeclarativeImportsPrivate {
111 QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader);
112 ~QDeclarativeImportsPrivate();
114 bool importExtension(const QString &absoluteFilePath, const QString &uri,
115 QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components,
116 QDeclarativeDirScripts *scripts,
117 QList<QDeclarativeError> *errors);
119 QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database);
120 bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
121 const QString& uri_arg, const QString& prefix,
122 int vmaj, int vmin, QDeclarativeScript::Import::Type importType,
123 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors);
124 bool find(const QString& type, int *vmajor, int *vminor,
125 QDeclarativeType** type_return, QString* url_return, QList<QDeclarativeError> *errors);
127 QDeclarativeImportedNamespace *findNamespace(const QString& type);
133 QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
134 QDeclarativeImportedNamespace unqualifiedset;
135 QHash<QString,QDeclarativeImportedNamespace* > set;
136 QDeclarativeTypeLoader *typeLoader;
140 \class QDeclarativeImports
141 \brief The QDeclarativeImports class encapsulates one QML document's import statements.
144 QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports ©)
150 QDeclarativeImports &
151 QDeclarativeImports::operator =(const QDeclarativeImports ©)
160 QDeclarativeImports::QDeclarativeImports(QDeclarativeTypeLoader *typeLoader)
161 : d(new QDeclarativeImportsPrivate(typeLoader))
165 QDeclarativeImports::~QDeclarativeImports()
172 Sets the base URL to be used for all relative file imports added.
174 void QDeclarativeImports::setBaseUrl(const QUrl& url)
177 d->base = url.toString();
181 Returns the base URL to be used for all relative file imports added.
183 QUrl QDeclarativeImports::baseUrl() const
188 void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const
190 const QDeclarativeImportedNamespace &set = d->unqualifiedset;
192 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
193 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
194 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
196 cache->m_anonymousImports.append(QDeclarativeTypeModuleVersion(module, data.minversion));
199 for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin();
200 iter != d->set.end();
203 const QDeclarativeImportedNamespace &set = *iter.value();
204 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
205 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
206 QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
208 QDeclarativeTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
209 import.modules.append(QDeclarativeTypeModuleVersion(module, data.minversion));
212 QDeclarativeMetaType::ModuleApi moduleApi = QDeclarativeMetaType::moduleApi(data.uri, data.majversion, data.minversion);
213 if (moduleApi.script || moduleApi.qobject) {
214 QDeclarativeTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
215 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
216 import.moduleApi = ep->moduleApiInstance(moduleApi);
222 QList<QDeclarativeImports::ScriptReference> QDeclarativeImports::resolvedScripts() const
224 QList<QDeclarativeImports::ScriptReference> scripts;
226 const QDeclarativeImportedNamespace &set = d->unqualifiedset;
228 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
229 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
231 foreach (const QDeclarativeDirParser::Script &script, data.qmlDirScripts) {
233 ref.nameSpace = script.nameSpace;
234 ref.location = QUrl(data.url).resolved(QUrl(script.fileName));
239 for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.constBegin();
240 iter != d->set.constEnd();
242 const QDeclarativeImportedNamespace &set = *iter.value();
244 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
245 const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
247 foreach (const QDeclarativeDirParser::Script &script, data.qmlDirScripts) {
249 ref.nameSpace = script.nameSpace;
250 ref.qualifier = iter.key();
251 ref.location = QUrl(data.url).resolved(QUrl(script.fileName));
263 The given (namespace qualified) \a type is resolved to either
265 \o a QDeclarativeImportedNamespace stored at \a ns_return,
266 \o a QDeclarativeType stored at \a type_return, or
267 \o a component located at \a url_return.
270 If any return pointer is 0, the corresponding search is not done.
274 bool QDeclarativeImports::resolveType(const QString& type,
275 QDeclarativeType** type_return, QString* url_return, int *vmaj, int *vmin,
276 QDeclarativeImportedNamespace** ns_return, QList<QDeclarativeError> *errors) const
278 QDeclarativeImportedNamespace* ns = d->findNamespace(type);
284 if (type_return || url_return) {
285 if (d->find(type,vmaj,vmin,type_return,url_return, errors)) {
286 if (qmlImportTrace()) {
287 if (type_return && *type_return && url_return && !url_return->isEmpty())
288 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
289 << type << " => " << (*type_return)->typeName() << " " << *url_return;
290 if (type_return && *type_return)
291 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
292 << type << " => " << (*type_return)->typeName();
293 if (url_return && !url_return->isEmpty())
294 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
295 << type << " => " << *url_return;
306 Searching \e only in the namespace \a ns (previously returned in a call to
307 resolveType(), \a type is found and returned to either
308 a QDeclarativeType stored at \a type_return, or
309 a component located at \a url_return.
311 If either return pointer is 0, the corresponding search is not done.
313 bool QDeclarativeImports::resolveType(QDeclarativeImportedNamespace* ns, const QString& type,
314 QDeclarativeType** type_return, QString* url_return,
315 int *vmaj, int *vmin) const
317 return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return);
320 bool QDeclarativeImportedNamespace::find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
321 QDeclarativeType** type_return, QString* url_return,
322 QString *base, bool *typeRecursionDetected)
324 int vmaj = data.majversion;
325 int vmin = data.minversion;
327 if (vmaj >= 0 && vmin >= 0) {
328 QString qt = data.uri + QLatin1Char('/') + type;
329 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
331 if (vmajor) *vmajor = vmaj;
332 if (vminor) *vminor = vmin;
339 const QDeclarativeDirComponents &qmldircomponents = data.qmlDirComponents;
340 bool typeWasDeclaredInQmldir = false;
341 if (!qmldircomponents.isEmpty()) {
342 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
343 if (c.typeName == type) {
344 typeWasDeclaredInQmldir = true;
345 // importing version -1 means import ALL versions
346 if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
347 QString url(data.url + type + QLatin1String(".qml"));
348 QString candidate = resolveLocalUrl(url, c.fileName);
349 if (c.internal && base) {
350 if (resolveLocalUrl(*base, c.fileName) != candidate)
351 continue; // failed attempt to access an internal type
353 if (base && *base == candidate) {
354 if (typeRecursionDetected)
355 *typeRecursionDetected = true;
356 continue; // no recursion
359 *url_return = candidate;
366 if (!typeWasDeclaredInQmldir && !data.isLibrary) {
367 // XXX search non-files too! (eg. zip files, see QT-524)
368 QString url(data.url + type + QLatin1String(".qml"));
369 QString file = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
370 if (!typeLoader->absoluteFilePath(file).isEmpty()) {
371 if (base && *base == url) { // no recursion
372 if (typeRecursionDetected)
373 *typeRecursionDetected = true;
384 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader)
385 : ref(1), typeLoader(loader)
389 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
391 foreach (QDeclarativeImportedNamespace* s, set.values())
395 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
396 QDeclarativeImportDatabase *database,
397 QDeclarativeDirComponents* components,
398 QDeclarativeDirScripts* scripts,
399 QList<QDeclarativeError> *errors)
401 const QDeclarativeDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
402 if (qmldirParser->hasError()) {
404 const QList<QDeclarativeError> qmldirErrors = qmldirParser->errors(uri);
405 for (int i = 0; i < qmldirErrors.size(); ++i)
406 errors->prepend(qmldirErrors.at(i));
411 if (qmlImportTrace())
412 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base) << "::importExtension: "
413 << "loaded " << absoluteFilePath;
415 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
416 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
418 QString qmldirPath = absoluteFilePath;
419 int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
421 qmldirPath.truncate(slash);
422 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser->plugins()) {
424 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
425 if (!resolvedFilePath.isEmpty()) {
426 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
428 // XXX TODO: should we leave the import plugin error alone?
429 // Here, we pop it off the top and coalesce it into this error's message.
430 // The reason is that the lower level may add url and line/column numbering information.
431 QDeclarativeError poppedError = errors->takeFirst();
432 QDeclarativeError error;
433 error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
434 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
435 errors->prepend(error);
441 QDeclarativeError error;
442 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
443 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
444 errors->prepend(error);
452 *components = qmldirParser->components();
454 *scripts = qmldirParser->scripts();
459 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
461 QString dir = dir_arg;
462 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
465 QStringList paths = database->fileImportPath;
466 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
468 QString stableRelativePath = dir;
469 foreach(const QString &path, paths) {
470 if (dir.startsWith(path)) {
471 stableRelativePath = dir.mid(path.length()+1);
476 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
478 // remove optional versioning in dot notation from uri
479 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
480 if (lastSlash >= 0) {
481 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
483 stableRelativePath = stableRelativePath.left(versionDot);
486 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
487 return stableRelativePath;
490 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
491 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
492 QDeclarativeScript::Import::Type importType,
493 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors)
495 static QLatin1String Slash_qmldir("/qmldir");
496 static QLatin1Char Slash('/');
498 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
499 QDeclarativeDirScripts qmldirscripts;
500 QString uri = uri_arg;
501 QDeclarativeImportedNamespace *s;
502 if (prefix.isEmpty()) {
505 s = set.value(prefix);
507 set.insert(prefix,(s=new QDeclarativeImportedNamespace));
510 bool versionFound = false;
511 if (importType == QDeclarativeScript::Import::Library) {
513 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
515 url.replace(QLatin1Char('.'), Slash);
520 // step 1: search for extension with fully encoded version number
521 foreach (const QString &p, database->fileImportPath) {
524 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
525 const QString absoluteFilePath = fi.absoluteFilePath();
530 const QString absolutePath = fi.absolutePath();
531 if (absolutePath.at(0) == QLatin1Char(':'))
532 url = QLatin1String("qrc://") + absolutePath.mid(1);
534 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
535 uri = resolvedUri(dir, database);
536 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
542 // TODO: Should this search be omitted if found == true?
544 // step 2: search for extension with encoded version major
545 foreach (const QString &p, database->fileImportPath) {
548 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
549 const QString absoluteFilePath = fi.absoluteFilePath();
554 const QString absolutePath = fi.absolutePath();
555 if (absolutePath.at(0) == QLatin1Char(':'))
556 url = QLatin1String("qrc://") + absolutePath.mid(1);
558 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
559 uri = resolvedUri(dir, database);
560 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
567 // step 3: search for extension without version number
569 foreach (const QString &p, database->fileImportPath) {
571 qmldir = dir+Slash_qmldir;
573 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
574 if (!absoluteFilePath.isEmpty()) {
576 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
577 if (absolutePath.at(0) == QLatin1Char(':'))
578 url = QLatin1String("qrc://") + absolutePath.mid(1);
580 url = QUrl::fromLocalFile(absolutePath).toString();
581 uri = resolvedUri(dir, database);
582 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
589 if (QDeclarativeMetaType::isModule(uri, vmaj, vmin))
592 if (!versionFound && qmldircomponents.isEmpty() && qmldirscripts.isEmpty()) {
594 QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
595 if (QDeclarativeMetaType::isAnyModule(uri))
596 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
598 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
599 errors->prepend(error);
604 if (importType == QDeclarativeScript::Import::File && qmldircomponents.isEmpty()) {
605 QString importUrl = resolveLocalUrl(base, uri + Slash_qmldir);
606 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
607 if (!localFileOrQrc.isEmpty()) {
608 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
609 if (!typeLoader->directoryExists(dir)) {
611 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
612 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
613 error.setUrl(QUrl(importUrl));
614 errors->prepend(error);
616 return false; // local import dirs must exist
618 uri = resolvedUri(dir, database);
619 if (uri.endsWith(Slash))
621 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
622 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,&qmldirscripts,errors))
626 if (prefix.isEmpty()) {
627 // directory must at least exist for valid import
628 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
629 if (!typeLoader->directoryExists(localFileOrQrc)) {
631 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
632 if (localFileOrQrc.isEmpty())
633 error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
635 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri));
636 error.setUrl(QUrl(importUrl));
637 errors->prepend(error);
645 url = resolveLocalUrl(base, url);
648 if (!versionFound && (vmaj > -1) && (vmin > -1) && !qmldircomponents.isEmpty()) {
649 int lowest_min = INT_MAX;
650 int highest_min = INT_MIN;
652 QList<QDeclarativeDirParser::Component>::const_iterator cend = qmldircomponents.constEnd();
653 for (QList<QDeclarativeDirParser::Component>::const_iterator cit = qmldircomponents.constBegin(); cit != cend; ++cit) {
654 if (cit->majorVersion == vmaj) {
655 lowest_min = qMin(lowest_min, cit->minorVersion);
656 highest_min = qMax(highest_min, cit->minorVersion);
660 if (lowest_min > vmin || highest_min < vmin) {
662 QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
663 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
664 errors->prepend(error);
670 if (!url.endsWith(Slash))
673 QMap<QString, QDeclarativeDirParser::Script> scripts;
675 if (!qmldirscripts.isEmpty()) {
676 // Verify that we haven't imported these scripts already
677 QList<QDeclarativeImportedNamespace::Data>::const_iterator end = s->imports.constEnd();
678 for (QList<QDeclarativeImportedNamespace::Data>::const_iterator it = s->imports.constBegin(); it != end; ++it) {
679 if (it->uri == uri) {
680 QDeclarativeError error;
681 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg(it->url));
682 errors->prepend(error);
687 QList<QDeclarativeDirParser::Script>::const_iterator send = qmldirscripts.constEnd();
688 for (QList<QDeclarativeDirParser::Script>::const_iterator sit = qmldirscripts.constBegin(); sit != send; ++sit) {
689 // Only include scripts that match our requested version
690 if (((vmaj == -1) || (sit->majorVersion == vmaj)) &&
691 ((vmin == -1) || (sit->minorVersion <= vmin))) {
693 // Load the highest version that matches
694 QMap<QString, QDeclarativeDirParser::Script>::iterator it = scripts.find(sit->nameSpace);
695 if (it == scripts.end() || (it->minorVersion < sit->minorVersion)) {
696 scripts.insert(sit->nameSpace, *sit);
702 QDeclarativeImportedNamespace::Data data;
705 data.majversion = vmaj;
706 data.minversion = vmin;
707 data.isLibrary = importType == QDeclarativeScript::Import::Library;
708 data.qmlDirComponents = qmldircomponents;
709 data.qmlDirScripts = scripts.values();
711 s->imports.prepend(data);
716 bool QDeclarativeImportsPrivate::find(const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
717 QString* url_return, QList<QDeclarativeError> *errors)
719 QDeclarativeImportedNamespace *s = 0;
720 int slash = type.indexOf(QLatin1Char('/'));
722 QString namespaceName = type.left(slash);
723 s = set.value(namespaceName);
726 QDeclarativeError error;
727 error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
728 errors->prepend(error);
732 int nslash = type.indexOf(QLatin1Char('/'),slash+1);
735 QDeclarativeError error;
736 error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed"));
737 errors->prepend(error);
744 QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
746 if (s->find(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
748 if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
749 // qualified, and only 1 url
750 *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml"));
758 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
760 return set.value(type);
763 bool QDeclarativeImportedNamespace::find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
764 QString* url_return, QString *base, QList<QDeclarativeError> *errors)
766 bool typeRecursionDetected = false;
767 for (int i=0; i<imports.count(); ++i) {
768 if (find_helper(typeLoader, imports.at(i), type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
769 if (qmlCheckTypes()) {
770 // check for type clashes
771 for (int j = i+1; j<imports.count(); ++j) {
772 if (find_helper(typeLoader, imports.at(j), type, vmajor, vminor, 0, 0, base)) {
774 QString u1 = imports.at(i).url;
775 QString u2 = imports.at(j).url;
778 int slash = b.lastIndexOf(QLatin1Char('/'));
781 QString l = b.left(slash);
782 if (u1.startsWith(b))
783 u1 = u1.mid(b.count());
785 u1 = QDeclarativeImportDatabase::tr("local directory");
786 if (u2.startsWith(b))
787 u2 = u2.mid(b.count());
789 u2 = QDeclarativeImportDatabase::tr("local directory");
793 QDeclarativeError error;
795 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
797 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
799 .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
800 .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
802 errors->prepend(error);
812 QDeclarativeError error;
813 if (typeRecursionDetected)
814 error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively"));
816 error.setDescription(QDeclarativeImportDatabase::tr("is not a type"));
817 errors->prepend(error);
823 \class QDeclarativeImportDatabase
824 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
827 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
830 filePluginPath << QLatin1String(".");
832 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
834 #ifndef QT_NO_SETTINGS
835 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
836 addImportPath(installImportsPath);
837 #endif // QT_NO_SETTINGS
840 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
841 if (!envImportPath.isEmpty()) {
842 #if defined(Q_OS_WIN)
843 QLatin1Char pathSep(';');
845 QLatin1Char pathSep(':');
847 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
848 for (int ii = paths.count() - 1; ii >= 0; --ii)
849 addImportPath(paths.at(ii));
852 addImportPath(QCoreApplication::applicationDirPath());
855 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
862 Adds information to \a imports such that subsequent calls to resolveType()
863 will resolve types qualified by \a prefix by considering types found at the given \a uri.
865 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
866 added via addImportPath() (if importType is LibraryImport).
868 The \a prefix may be empty, in which case the import location is considered for
871 The base URL must already have been set with Import::setBaseUrl().
873 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
874 const QString& uri, const QString& prefix, int vmaj, int vmin,
875 QDeclarativeScript::Import::Type importType,
876 const QDeclarativeDirComponents &qmldircomponentsnetwork,
877 QList<QDeclarativeError> *errors)
879 if (qmlImportTrace())
880 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
881 << uri << " " << vmaj << '.' << vmin << " "
882 << (importType==QDeclarativeScript::Import::Library? "Library" : "File")
885 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
891 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
892 The \a prefix must contain the dot.
894 \a qmldirPath is the location of the qmldir file.
896 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
897 const QString &qmldirPath, const QString &qmldirPluginPath,
898 const QString &baseName, const QStringList &suffixes,
899 const QString &prefix)
901 QStringList searchPaths = filePluginPath;
902 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
903 if (!qmldirPluginPathIsRelative)
904 searchPaths.prepend(qmldirPluginPath);
906 foreach (const QString &pluginPath, searchPaths) {
908 QString resolvedPath;
909 if (pluginPath == QLatin1String(".")) {
910 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
911 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath);
913 resolvedPath = qmldirPath;
915 if (QDir::isRelativePath(pluginPath))
916 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath);
918 resolvedPath = pluginPath;
921 // hack for resources, should probably go away
922 if (resolvedPath.startsWith(QLatin1Char(':')))
923 resolvedPath = QCoreApplication::applicationDirPath();
925 if (!resolvedPath.endsWith(QLatin1Char('/')))
926 resolvedPath += QLatin1Char('/');
928 foreach (const QString &suffix, suffixes) {
929 QString pluginFileName = prefix;
931 pluginFileName += baseName;
932 pluginFileName += suffix;
934 QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
935 if (!absolutePath.isEmpty())
940 if (qmlImportTrace())
941 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
942 << "in" << qmldirPath;
950 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
953 \header \i Platform \i Valid suffixes
954 \row \i Windows \i \c .dll
955 \row \i Unix/Linux \i \c .so
957 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
958 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
961 Version number on unix are ignored.
963 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
964 const QString &qmldirPath, const QString &qmldirPluginPath,
965 const QString &baseName)
967 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
968 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
971 << QLatin1String("d.dll") // try a qmake-style debug build first
973 << QLatin1String(".dll"));
976 # if defined(Q_OS_DARWIN)
978 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
981 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
982 << QLatin1String(".dylib")
984 << QLatin1String(".dylib")
985 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
987 << QLatin1String(".so")
988 << QLatin1String(".bundle"),
989 QLatin1String("lib"));
990 # else // Generic Unix
991 QStringList validSuffixList;
993 # if defined(Q_OS_HPUX)
995 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
996 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
997 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
999 validSuffixList << QLatin1String(".sl");
1001 validSuffixList << QLatin1String(".so");
1003 # elif defined(Q_OS_AIX)
1004 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
1005 # elif defined(Q_OS_UNIX)
1006 validSuffixList << QLatin1String(".so");
1009 // Examples of valid library names:
1012 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
1021 QStringList QDeclarativeImportDatabase::pluginPathList() const
1023 return filePluginPath;
1029 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
1031 filePluginPath = paths;
1037 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
1039 if (qmlImportTrace())
1040 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
1042 QUrl url = QUrl(path);
1043 if (url.isRelative() || url.scheme() == QLatin1String("file")
1044 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1045 QDir dir = QDir(path);
1046 filePluginPath.prepend(dir.canonicalPath());
1048 filePluginPath.prepend(path);
1055 void QDeclarativeImportDatabase::addImportPath(const QString& path)
1057 if (qmlImportTrace())
1058 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
1063 QUrl url = QUrl(path);
1066 if (url.isRelative() || url.scheme() == QLatin1String("file")
1067 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1068 QDir dir = QDir(path);
1069 cPath = dir.canonicalPath();
1072 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1075 if (!cPath.isEmpty()
1076 && !fileImportPath.contains(cPath))
1077 fileImportPath.prepend(cPath);
1083 QStringList QDeclarativeImportDatabase::importPathList() const
1085 return fileImportPath;
1091 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1093 fileImportPath = paths;
1099 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors)
1101 if (qmlImportTrace())
1102 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1104 #ifndef QT_NO_LIBRARY
1105 QFileInfo fileInfo(filePath);
1106 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1108 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1109 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1111 if (typesRegistered) {
1112 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1113 "QDeclarativeImportDatabase::importExtension",
1114 "Internal error: Plugin imported previously with different uri");
1117 if (!engineInitialized || !typesRegistered) {
1118 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1120 QDeclarativeError error;
1121 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1122 errors->prepend(error);
1126 QPluginLoader loader(absoluteFilePath);
1128 if (!loader.load()) {
1130 QDeclarativeError error;
1131 error.setDescription(loader.errorString());
1132 errors->prepend(error);
1137 QObject *instance = loader.instance();
1138 if (QDeclarativeTypesExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(instance)) {
1140 const QByteArray bytes = uri.toUtf8();
1141 const char *moduleId = bytes.constData();
1142 if (!typesRegistered) {
1144 // XXX thread this code should probably be protected with a mutex.
1145 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1146 iface->registerTypes(moduleId);
1148 if (!engineInitialized) {
1149 // things on the engine (eg. adding new global objects) have to be done for every
1151 // XXX protect against double initialization
1152 initializedPlugins.insert(absoluteFilePath);
1154 QDeclarativeExtensionInterface *eiface =
1155 qobject_cast<QDeclarativeExtensionInterface *>(instance);
1157 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
1158 ep->typeLoader.initializeEngine(eiface, moduleId);
1163 QDeclarativeError error;
1164 error.setDescription(loader.errorString());
1165 errors->prepend(error);