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.toUtf8(), data.majversion, data.minversion);
214 if (moduleApi.script || moduleApi.qobject) {
215 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
216 QDeclarativeMetaType::ModuleApiInstance *a = ep->moduleApiInstances.value(moduleApi);
218 a = new QDeclarativeMetaType::ModuleApiInstance;
219 a->scriptCallback = moduleApi.script;
220 a->qobjectCallback = moduleApi.qobject;
221 ep->moduleApiInstances.insert(moduleApi, a);
223 import.moduleApi = a;
234 The given (namespace qualified) \a type is resolved to either
236 \o a QDeclarativeImportedNamespace stored at \a ns_return,
237 \o a QDeclarativeType stored at \a type_return, or
238 \o a component located at \a url_return.
241 If any return pointer is 0, the corresponding search is not done.
245 bool QDeclarativeImports::resolveType(const QString& type,
246 QDeclarativeType** type_return, QString* url_return, int *vmaj, int *vmin,
247 QDeclarativeImportedNamespace** ns_return, QList<QDeclarativeError> *errors) const
249 QDeclarativeImportedNamespace* ns = d->findNamespace(type);
255 if (type_return || url_return) {
256 if (d->find(type,vmaj,vmin,type_return,url_return, errors)) {
257 if (qmlImportTrace()) {
258 if (type_return && *type_return && url_return && !url_return->isEmpty())
259 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
260 << type << " => " << (*type_return)->typeName() << " " << *url_return;
261 if (type_return && *type_return)
262 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
263 << type << " => " << (*type_return)->typeName();
264 if (url_return && !url_return->isEmpty())
265 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
266 << type << " => " << *url_return;
277 Searching \e only in the namespace \a ns (previously returned in a call to
278 resolveType(), \a type is found and returned to either
279 a QDeclarativeType stored at \a type_return, or
280 a component located at \a url_return.
282 If either return pointer is 0, the corresponding search is not done.
284 bool QDeclarativeImports::resolveType(QDeclarativeImportedNamespace* ns, const QString& type,
285 QDeclarativeType** type_return, QString* url_return,
286 int *vmaj, int *vmin) const
288 return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return);
291 bool QDeclarativeImportedNamespace::find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
292 QDeclarativeType** type_return, QString* url_return,
293 QString *base, bool *typeRecursionDetected)
295 int vmaj = data.majversion;
296 int vmin = data.minversion;
298 if (vmaj >= 0 && vmin >= 0) {
299 QString qt = data.uri + QLatin1Char('/') + type;
300 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
302 if (vmajor) *vmajor = vmaj;
303 if (vminor) *vminor = vmin;
310 const QDeclarativeDirComponents &qmldircomponents = data.qmlDirComponents;
311 bool typeWasDeclaredInQmldir = false;
312 if (!qmldircomponents.isEmpty()) {
313 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
314 if (c.typeName == type) {
315 typeWasDeclaredInQmldir = true;
316 // importing version -1 means import ALL versions
317 if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
318 QString url(data.url + type + QLatin1String(".qml"));
319 QString candidate = resolveLocalUrl(url, c.fileName);
320 if (c.internal && base) {
321 if (resolveLocalUrl(*base, c.fileName) != candidate)
322 continue; // failed attempt to access an internal type
324 if (base && *base == candidate) {
325 if (typeRecursionDetected)
326 *typeRecursionDetected = true;
327 continue; // no recursion
330 *url_return = candidate;
337 if (!typeWasDeclaredInQmldir && !data.isLibrary) {
338 // XXX search non-files too! (eg. zip files, see QT-524)
339 QString url(data.url + type + QLatin1String(".qml"));
340 QString file = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
341 if (!typeLoader->absoluteFilePath(file).isEmpty()) {
342 if (base && *base == url) { // no recursion
343 if (typeRecursionDetected)
344 *typeRecursionDetected = true;
355 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader)
356 : ref(1), typeLoader(loader)
360 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
362 foreach (QDeclarativeImportedNamespace* s, set.values())
366 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
367 QDeclarativeImportDatabase *database,
368 QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors)
370 const QDeclarativeDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
371 if (qmldirParser->hasError()) {
373 const QList<QDeclarativeError> qmldirErrors = qmldirParser->errors(uri);
374 for (int i = 0; i < qmldirErrors.size(); ++i)
375 errors->prepend(qmldirErrors.at(i));
380 if (qmlImportTrace())
381 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base) << "::importExtension: "
382 << "loaded " << absoluteFilePath;
384 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
385 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
387 QString qmldirPath = absoluteFilePath;
388 int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
390 qmldirPath.truncate(slash);
391 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser->plugins()) {
393 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
394 #if defined(QT_LIBINFIX) && defined(Q_OS_SYMBIAN)
395 if (resolvedFilePath.isEmpty()) {
396 // In case of libinfixed build, attempt to load libinfixed version, too.
397 QString infixedPluginName = plugin.name + QLatin1String(QT_LIBINFIX);
398 resolvedFilePath = database->resolvePlugin(dir, plugin.path, infixedPluginName);
401 if (!resolvedFilePath.isEmpty()) {
402 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
404 // XXX TODO: should we leave the import plugin error alone?
405 // Here, we pop it off the top and coalesce it into this error's message.
406 // The reason is that the lower level may add url and line/column numbering information.
407 QDeclarativeError poppedError = errors->takeFirst();
408 QDeclarativeError error;
409 error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
410 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
411 errors->prepend(error);
417 QDeclarativeError error;
418 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
419 error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
420 errors->prepend(error);
428 *components = qmldirParser->components();
433 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
435 QString dir = dir_arg;
436 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
439 QStringList paths = database->fileImportPath;
440 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
442 QString stableRelativePath = dir;
443 foreach(const QString &path, paths) {
444 if (dir.startsWith(path)) {
445 stableRelativePath = dir.mid(path.length()+1);
450 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
452 // remove optional versioning in dot notation from uri
453 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
454 if (lastSlash >= 0) {
455 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
457 stableRelativePath = stableRelativePath.left(versionDot);
460 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
461 return stableRelativePath;
464 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
465 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
466 QDeclarativeScript::Import::Type importType,
467 QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors)
469 static QLatin1String Slash_qmldir("/qmldir");
470 static QLatin1Char Slash('/');
472 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
473 QString uri = uri_arg;
474 QDeclarativeImportedNamespace *s;
475 if (prefix.isEmpty()) {
478 s = set.value(prefix);
480 set.insert(prefix,(s=new QDeclarativeImportedNamespace));
483 bool versionFound = false;
484 if (importType == QDeclarativeScript::Import::Library) {
486 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
488 url.replace(QLatin1Char('.'), Slash);
493 // step 1: search for extension with fully encoded version number
494 foreach (const QString &p, database->fileImportPath) {
497 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
498 const QString absoluteFilePath = fi.absoluteFilePath();
503 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
504 uri = resolvedUri(dir, database);
505 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
511 // step 2: search for extension with encoded version major
512 foreach (const QString &p, database->fileImportPath) {
515 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
516 const QString absoluteFilePath = fi.absoluteFilePath();
521 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
522 uri = resolvedUri(dir, database);
523 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
530 // step 3: search for extension without version number
532 foreach (const QString &p, database->fileImportPath) {
534 qmldir = dir+Slash_qmldir;
536 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
537 if (!absoluteFilePath.isEmpty()) {
539 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
540 url = QLatin1String("file://") + absolutePath;
541 uri = resolvedUri(dir, database);
542 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
549 if (QDeclarativeMetaType::isModule(uri, vmaj, vmin))
552 if (!versionFound && qmldircomponents.isEmpty()) {
554 QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
555 if (QDeclarativeMetaType::isAnyModule(uri))
556 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
558 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
559 errors->prepend(error);
564 if (importType == QDeclarativeScript::Import::File && qmldircomponents.isEmpty()) {
565 QString importUrl = resolveLocalUrl(base, uri + Slash_qmldir);
566 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
567 if (!localFileOrQrc.isEmpty()) {
568 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
569 if (!typeLoader->directoryExists(dir)) {
571 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
572 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
573 error.setUrl(QUrl(importUrl));
574 errors->prepend(error);
576 return false; // local import dirs must exist
578 uri = resolvedUri(dir, database);
579 if (uri.endsWith(Slash))
581 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
582 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors))
586 if (prefix.isEmpty()) {
587 // directory must at least exist for valid import
588 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
589 if (!typeLoader->directoryExists(localFileOrQrc)) {
591 QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
592 if (localFileOrQrc.isEmpty())
593 error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
595 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri));
596 error.setUrl(QUrl(importUrl));
597 errors->prepend(error);
605 url = resolveLocalUrl(base, url);
608 if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
609 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
610 int lowest_min = INT_MAX;
611 int highest_min = INT_MIN;
612 for (; it != qmldircomponents.end(); ++it) {
613 if (it->majorVersion == vmaj) {
614 lowest_min = qMin(lowest_min, it->minorVersion);
615 highest_min = qMax(highest_min, it->minorVersion);
618 if (lowest_min > vmin || highest_min < vmin) {
620 QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
621 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
622 errors->prepend(error);
628 if (!url.endsWith(Slash))
631 QDeclarativeImportedNamespace::Data data;
634 data.majversion = vmaj;
635 data.minversion = vmin;
636 data.isLibrary = importType == QDeclarativeScript::Import::Library;
637 data.qmlDirComponents = qmldircomponents;
638 s->imports.prepend(data);
643 bool QDeclarativeImportsPrivate::find(const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
644 QString* url_return, QList<QDeclarativeError> *errors)
646 QDeclarativeImportedNamespace *s = 0;
647 int slash = type.indexOf(QLatin1Char('/'));
649 QString namespaceName = type.left(slash);
650 s = set.value(namespaceName);
653 QDeclarativeError error;
654 error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
655 errors->prepend(error);
659 int nslash = type.indexOf(QLatin1Char('/'),slash+1);
662 QDeclarativeError error;
663 error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed"));
664 errors->prepend(error);
671 QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
673 if (s->find(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
675 if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
676 // qualified, and only 1 url
677 *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml"));
685 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
687 return set.value(type);
690 bool QDeclarativeImportedNamespace::find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
691 QString* url_return, QString *base, QList<QDeclarativeError> *errors)
693 bool typeRecursionDetected = false;
694 for (int i=0; i<imports.count(); ++i) {
695 if (find_helper(typeLoader, imports.at(i), type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
696 if (qmlCheckTypes()) {
697 // check for type clashes
698 for (int j = i+1; j<imports.count(); ++j) {
699 if (find_helper(typeLoader, imports.at(j), type, vmajor, vminor, 0, 0, base)) {
701 QString u1 = imports.at(i).url;
702 QString u2 = imports.at(j).url;
705 int slash = b.lastIndexOf(QLatin1Char('/'));
708 QString l = b.left(slash);
709 if (u1.startsWith(b))
710 u1 = u1.mid(b.count());
712 u1 = QDeclarativeImportDatabase::tr("local directory");
713 if (u2.startsWith(b))
714 u2 = u2.mid(b.count());
716 u2 = QDeclarativeImportDatabase::tr("local directory");
720 QDeclarativeError error;
722 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
724 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
726 .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
727 .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
729 errors->prepend(error);
739 QDeclarativeError error;
740 if (typeRecursionDetected)
741 error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively"));
743 error.setDescription(QDeclarativeImportDatabase::tr("is not a type"));
744 errors->prepend(error);
750 \class QDeclarativeImportDatabase
751 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
754 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
757 filePluginPath << QLatin1String(".");
759 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
761 #ifndef QT_NO_SETTINGS
762 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
764 #if defined(Q_OS_SYMBIAN)
765 // Append imports path for all available drives in Symbian
766 if (installImportsPath.at(1) != QChar(QLatin1Char(':'))) {
767 QString tempPath = installImportsPath;
768 if (tempPath.at(tempPath.length() - 1) != QDir::separator()) {
769 tempPath += QDir::separator();
771 RFs& fs = qt_s60GetRFs();
772 TPtrC tempPathPtr(reinterpret_cast<const TText*> (tempPath.constData()));
773 TFindFile finder(fs);
774 TInt err = finder.FindByDir(tempPathPtr, tempPathPtr);
775 while (err == KErrNone) {
776 QString foundDir(reinterpret_cast<const QChar *>(finder.File().Ptr()),
777 finder.File().Length());
778 foundDir = QDir(foundDir).canonicalPath();
779 addImportPath(foundDir);
783 addImportPath(installImportsPath);
786 addImportPath(installImportsPath);
789 #endif // QT_NO_SETTINGS
792 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
793 if (!envImportPath.isEmpty()) {
794 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
795 QLatin1Char pathSep(';');
797 QLatin1Char pathSep(':');
799 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
800 for (int ii = paths.count() - 1; ii >= 0; --ii)
801 addImportPath(paths.at(ii));
804 addImportPath(QCoreApplication::applicationDirPath());
807 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
814 Adds information to \a imports such that subsequent calls to resolveType()
815 will resolve types qualified by \a prefix by considering types found at the given \a uri.
817 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
818 added via addImportPath() (if importType is LibraryImport).
820 The \a prefix may be empty, in which case the import location is considered for
823 The base URL must already have been set with Import::setBaseUrl().
825 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
826 const QString& uri, const QString& prefix, int vmaj, int vmin,
827 QDeclarativeScript::Import::Type importType,
828 const QDeclarativeDirComponents &qmldircomponentsnetwork,
829 QList<QDeclarativeError> *errors)
831 if (qmlImportTrace())
832 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
833 << uri << " " << vmaj << '.' << vmin << " "
834 << (importType==QDeclarativeScript::Import::Library? "Library" : "File")
837 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
843 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
844 The \a prefix must contain the dot.
846 \a qmldirPath is the location of the qmldir file.
848 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
849 const QString &qmldirPath, const QString &qmldirPluginPath,
850 const QString &baseName, const QStringList &suffixes,
851 const QString &prefix)
853 QStringList searchPaths = filePluginPath;
854 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
855 if (!qmldirPluginPathIsRelative)
856 searchPaths.prepend(qmldirPluginPath);
858 foreach (const QString &pluginPath, searchPaths) {
860 QString resolvedPath;
861 if (pluginPath == QLatin1String(".")) {
862 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
863 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath);
865 resolvedPath = qmldirPath;
867 if (QDir::isRelativePath(pluginPath))
868 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath);
870 resolvedPath = pluginPath;
873 // hack for resources, should probably go away
874 if (resolvedPath.startsWith(QLatin1Char(':')))
875 resolvedPath = QCoreApplication::applicationDirPath();
877 if (!resolvedPath.endsWith(QLatin1Char('/')))
878 resolvedPath += QLatin1Char('/');
880 foreach (const QString &suffix, suffixes) {
881 QString pluginFileName = prefix;
883 pluginFileName += baseName;
884 pluginFileName += suffix;
886 QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
887 if (!absolutePath.isEmpty())
892 if (qmlImportTrace())
893 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
894 << "in" << qmldirPath;
902 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
905 \header \i Platform \i Valid suffixes
906 \row \i Windows \i \c .dll
907 \row \i Unix/Linux \i \c .so
909 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
910 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
911 \row \i Symbian \i \c .dll
914 Version number on unix are ignored.
916 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
917 const QString &qmldirPath, const QString &qmldirPluginPath,
918 const QString &baseName)
920 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
921 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
924 << QLatin1String("d.dll") // try a qmake-style debug build first
926 << QLatin1String(".dll"));
927 #elif defined(Q_OS_SYMBIAN)
928 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
930 << QLatin1String(".dll")
931 << QLatin1String(".qtplugin"));
934 # if defined(Q_OS_DARWIN)
936 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
939 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
940 << QLatin1String(".dylib")
942 << QLatin1String(".dylib")
943 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
945 << QLatin1String(".so")
946 << QLatin1String(".bundle"),
947 QLatin1String("lib"));
948 # else // Generic Unix
949 QStringList validSuffixList;
951 # if defined(Q_OS_HPUX)
953 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
954 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
955 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
957 validSuffixList << QLatin1String(".sl");
959 validSuffixList << QLatin1String(".so");
961 # elif defined(Q_OS_AIX)
962 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
963 # elif defined(Q_OS_UNIX)
964 validSuffixList << QLatin1String(".so");
967 // Examples of valid library names:
970 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
979 QStringList QDeclarativeImportDatabase::pluginPathList() const
981 return filePluginPath;
987 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
989 filePluginPath = paths;
995 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
997 if (qmlImportTrace())
998 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
1000 QUrl url = QUrl(path);
1001 if (url.isRelative() || url.scheme() == QLatin1String("file")
1002 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1003 QDir dir = QDir(path);
1004 filePluginPath.prepend(dir.canonicalPath());
1006 filePluginPath.prepend(path);
1013 void QDeclarativeImportDatabase::addImportPath(const QString& path)
1015 if (qmlImportTrace())
1016 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
1021 QUrl url = QUrl(path);
1024 if (url.isRelative() || url.scheme() == QLatin1String("file")
1025 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1026 QDir dir = QDir(path);
1027 cPath = dir.canonicalPath();
1030 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1033 if (!cPath.isEmpty()
1034 && !fileImportPath.contains(cPath))
1035 fileImportPath.prepend(cPath);
1041 QStringList QDeclarativeImportDatabase::importPathList() const
1043 return fileImportPath;
1049 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1051 fileImportPath = paths;
1057 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors)
1059 if (qmlImportTrace())
1060 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1062 #ifndef QT_NO_LIBRARY
1063 QFileInfo fileInfo(filePath);
1064 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1066 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1067 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1069 if (typesRegistered) {
1070 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1071 "QDeclarativeImportDatabase::importExtension",
1072 "Internal error: Plugin imported previously with different uri");
1075 if (!engineInitialized || !typesRegistered) {
1076 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1078 QDeclarativeError error;
1079 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1080 errors->prepend(error);
1084 QPluginLoader loader(absoluteFilePath);
1086 if (!loader.load()) {
1088 QDeclarativeError error;
1089 error.setDescription(loader.errorString());
1090 errors->prepend(error);
1095 if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) {
1097 const QByteArray bytes = uri.toUtf8();
1098 const char *moduleId = bytes.constData();
1099 if (!typesRegistered) {
1101 // ### this code should probably be protected with a mutex.
1102 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1103 iface->registerTypes(moduleId);
1105 if (!engineInitialized) {
1106 // things on the engine (eg. adding new global objects) have to be done for every engine.
1108 // protect against double initialization
1109 initializedPlugins.insert(absoluteFilePath);
1110 iface->initializeEngine(engine, moduleId);
1114 QDeclarativeError error;
1115 error.setDescription(loader.errorString());
1116 errors->prepend(error);