Implement strict mode for qmldir modules
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlimport.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmlimport_p.h"
43
44 #include <QtCore/qdebug.h>
45 #include <QtCore/qdir.h>
46 #include <QtQml/qqmlfile.h>
47 #include <QtCore/qfileinfo.h>
48 #include <QtCore/qpluginloader.h>
49 #include <QtCore/qlibraryinfo.h>
50 #include <QtCore/qreadwritelock.h>
51 #include <QtQml/qqmlextensioninterface.h>
52 #include <private/qqmlglobal_p.h>
53 #include <private/qqmltypenamecache_p.h>
54 #include <private/qqmlengine_p.h>
55 #include <private/qfieldlist_p.h>
56
57 QT_BEGIN_NAMESPACE
58
59 DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
60 DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
61
62 static const QLatin1Char Dot('.');
63 static const QLatin1Char Slash('/');
64 static const QLatin1Char Backslash('\\');
65 static const QLatin1Char Colon(':');
66 static const QLatin1String Slash_qmldir("/qmldir");
67 static const QLatin1String String_qmldir("qmldir");
68 static const QString dotqml_string(QLatin1String(".qml"));
69
70 namespace {
71
72 QString resolveLocalUrl(const QString &url, const QString &relative)
73 {
74     if (relative.contains(Colon)) {
75         // contains a host name
76         return QUrl(url).resolved(QUrl(relative)).toString();
77     } else if (relative.isEmpty()) {
78         return url;
79     } else if (relative.at(0) == Slash || !url.contains(Slash)) {
80         return relative;
81     } else {
82         QString base(url.left(url.lastIndexOf(Slash) + 1));
83
84         if (relative == QLatin1String("."))
85             return base;
86
87         base.append(relative);
88
89         // Remove any relative directory elements in the path
90         int length = base.length();
91         int index = 0;
92         while ((index = base.indexOf(QLatin1String("/."), index)) != -1) {
93             if ((length > (index + 2)) && (base.at(index + 2) == Dot) &&
94                 (length == (index + 3) || (base.at(index + 3) == Slash))) {
95                 // Either "/../" or "/..<END>"
96                 int previous = base.lastIndexOf(Slash, index - 1);
97                 if (previous == -1)
98                     break;
99
100                 int removeLength = (index - previous) + 3;
101                 base.remove(previous + 1, removeLength);
102                 length -= removeLength;
103                 index = previous;
104             } else if ((length == (index + 2)) || (base.at(index + 2) == Slash)) {
105                 // Either "/./" or "/.<END>"
106                 base.remove(index, 2);
107                 length -= 2;
108             } else {
109                 ++index;
110             }
111         }
112
113         return base;
114     }
115 }
116
117 bool isPathAbsolute(const QString &path)
118 {
119 #if defined(Q_OS_UNIX)
120     return (path.at(0) == Slash);
121 #else
122     QFileInfo fi(path);
123     return fi.isAbsolute();
124 #endif
125 }
126
127 }
128
129 typedef QMap<QString, QString> StringStringMap;
130 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
131
132 class QQmlImportNamespace
133 {
134 public:
135     QQmlImportNamespace() : nextNamespace(0) {}
136     ~QQmlImportNamespace() { qDeleteAll(imports); }
137
138     struct Import {
139         QString uri;
140         QString url;
141         int majversion;
142         int minversion;
143         bool isLibrary;
144         QQmlDirComponents qmlDirComponents;
145         QQmlDirScripts qmlDirScripts;
146
147         bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir,
148                               QQmlImportNamespace *nameSpace, QList<QQmlError> *errors);
149
150         static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin);
151
152         bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type,
153                          int *vmajor, int *vminor,
154                          QQmlType** type_return, QString* url_return,
155                          QString *base = 0, bool *typeRecursionDetected = 0) const;
156     };
157     QList<Import *> imports;
158
159     Import *findImport(const QString &uri);
160
161     bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type,
162                      int *vmajor, int *vminor,
163                      QQmlType** type_return, QString* url_return,
164                      QString *base = 0, QList<QQmlError> *errors = 0);
165
166     // Prefix when used as a qualified import.  Otherwise empty.
167     QHashedString prefix;
168
169     // Used by QQmlImportsPrivate::qualifiedSets
170     QQmlImportNamespace *nextNamespace;
171 };
172
173 class QQmlImportsPrivate
174 {
175 public:
176     QQmlImportsPrivate(QQmlTypeLoader *loader);
177     ~QQmlImportsPrivate();
178
179     QQmlImportNamespace *importNamespace(const QString &prefix) const;
180
181     bool addLibraryImport(const QString& uri, const QString &prefix,
182                           int vmaj, int vmin, const QString &qmldirIdentifier, const QString &qmldirUrl, bool incomplete,
183                           QQmlImportDatabase *database,
184                           QList<QQmlError> *errors);
185
186     bool addFileImport(const QString &uri, const QString &prefix,
187                        int vmaj, int vmin,
188                        bool isImplicitImport, bool incomplete, QQmlImportDatabase *database,
189                        QList<QQmlError> *errors);
190
191     bool updateQmldirContent(const QString &uri, const QString &prefix,
192                              const QString &qmldirIdentifier, const QString& qmldirUrl,
193                              QQmlImportDatabase *database,
194                              QList<QQmlError> *errors);
195
196     bool resolveType(const QHashedStringRef &type, int *vmajor, int *vminor,
197                      QQmlType** type_return, QString* url_return,
198                      QList<QQmlError> *errors);
199
200     QUrl baseUrl;
201     QString base;
202     int ref;
203
204     mutable QQmlImportNamespace unqualifiedset;
205
206     QQmlImportNamespace *findQualifiedNamespace(const QHashedStringRef &) const;
207     mutable QFieldList<QQmlImportNamespace, &QQmlImportNamespace::nextNamespace> qualifiedSets;
208
209     QQmlTypeLoader *typeLoader;
210
211     static inline QString tr(const char *str) {
212         return QQmlImportDatabase::tr(str);
213     }
214
215     static bool locateQmldir(const QString &uri, int vmaj, int vmin,
216                              QQmlImportDatabase *database,
217                              QString *outQmldirFilePath, QString *outUrl);
218
219     static bool validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin,
220                                       QList<QQmlError> *errors);
221
222     bool importExtension(const QString &absoluteFilePath, const QString &uri,
223                          QQmlImportDatabase *database,
224                          const QQmlTypeLoader::QmldirContent *qmldir,
225                          QList<QQmlError> *errors);
226
227     bool getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
228                           const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors);
229
230     QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database);
231
232     QQmlImportNamespace::Import *addImportToNamespace(QQmlImportNamespace *nameSpace,
233                                                       const QString &uri, const QString &url,
234                                                       int vmaj, int vmin, QQmlScript::Import::Type type,
235                                                       QList<QQmlError> *errors);
236 };
237
238 /*!
239 \class QQmlImports
240 \brief The QQmlImports class encapsulates one QML document's import statements.
241 \internal
242 */
243 QQmlImports::QQmlImports(const QQmlImports &copy) 
244 : d(copy.d)
245 {
246     ++d->ref;
247 }
248
249 QQmlImports &
250 QQmlImports::operator =(const QQmlImports &copy)
251 {
252     ++copy.d->ref;
253     if (--d->ref == 0)
254         delete d;
255     d = copy.d;
256     return *this;
257 }
258
259 QQmlImports::QQmlImports(QQmlTypeLoader *typeLoader)
260     : d(new QQmlImportsPrivate(typeLoader))
261 {
262 }
263
264 QQmlImports::~QQmlImports()
265 {
266     if (--d->ref == 0)
267         delete d;
268 }
269
270 /*!
271   Sets the base URL to be used for all relative file imports added.
272 */
273 void QQmlImports::setBaseUrl(const QUrl& url, const QString &urlString)
274 {
275     d->baseUrl = url;
276
277     if (urlString.isEmpty()) {
278         d->base = url.toString();
279     } else {
280         //Q_ASSERT(url.toString() == urlString);
281         d->base = urlString;
282     }
283 }
284
285 /*!
286   Returns the base URL to be used for all relative file imports added.
287 */
288 QUrl QQmlImports::baseUrl() const
289 {
290     return d->baseUrl;
291 }
292
293 void QQmlImports::populateCache(QQmlTypeNameCache *cache, QQmlEngine *engine) const
294 {
295     const QQmlImportNamespace &set = d->unqualifiedset;
296
297     for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
298         const QQmlImportNamespace::Import *import = set.imports.at(ii);
299         QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion);
300         if (module)
301             cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import->minversion));
302     }
303
304     for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(ns)) {
305
306         const QQmlImportNamespace &set = *ns;
307
308         for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
309             const QQmlImportNamespace::Import *import = set.imports.at(ii);
310             QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion);
311             if (module) {
312                 QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[set.prefix];
313                 typeimport.modules.append(QQmlTypeModuleVersion(module, import->minversion));
314             }
315
316             QQmlMetaType::ModuleApi moduleApi = QQmlMetaType::moduleApi(import->uri, import->majversion,
317                                                                         import->minversion);
318             if (moduleApi.script || moduleApi.qobject) {
319                 QQmlTypeNameCache::Import &import = cache->m_namedImports[set.prefix];
320                 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
321                 import.moduleApi = ep->moduleApiInstance(moduleApi);
322             }
323         }
324     }
325 }
326
327 QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
328 {
329     QList<QQmlImports::ScriptReference> scripts;
330
331     const QQmlImportNamespace &set = d->unqualifiedset;
332
333     for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
334         const QQmlImportNamespace::Import *import = set.imports.at(ii);
335
336         foreach (const QQmlDirParser::Script &script, import->qmlDirScripts) {
337             ScriptReference ref;
338             ref.nameSpace = script.nameSpace;
339             ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
340             scripts.append(ref);
341         }
342     }
343
344     for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(ns)) {
345         const QQmlImportNamespace &set = *ns;
346
347         for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
348             const QQmlImportNamespace::Import *import = set.imports.at(ii);
349
350             foreach (const QQmlDirParser::Script &script, import->qmlDirScripts) {
351                 ScriptReference ref;
352                 ref.nameSpace = script.nameSpace;
353                 ref.qualifier = set.prefix;
354                 ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
355                 scripts.append(ref);
356             }
357         }
358     }
359
360     return scripts;
361 }
362
363 /*!
364     Form a complete path to a qmldir file, from a base URL, a module URI and version specification.
365 */
366 QString QQmlImports::completeQmldirPath(const QString &uri, const QString &base, int vmaj, int vmin,
367                                         ImportVersion version)
368 {
369     QString url = uri;
370     url.replace(Dot, Slash);
371
372     QString dir = base;
373     if (!dir.endsWith(Slash) && !dir.endsWith(Backslash))
374         dir += Slash;
375     dir += url;
376
377     if (version == QQmlImports::FullyVersioned) {
378         // extension with fully encoded version number (eg. MyModule.3.2)
379         dir += QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin);
380     } else if (version == QQmlImports::PartiallyVersioned) {
381         // extension with encoded version major (eg. MyModule.3)
382         dir += QString(QLatin1String(".%1")).arg(vmaj);
383     } // else extension without version number (eg. MyModule)
384
385     return dir + Slash_qmldir;
386 }
387
388 /*!
389   \internal
390
391   The given (namespace qualified) \a type is resolved to either
392   \list
393   \li a QQmlImportNamespace stored at \a ns_return,
394   \li a QQmlType stored at \a type_return, or
395   \li a component located at \a url_return.
396   \endlist
397
398   If any return pointer is 0, the corresponding search is not done.
399
400   \sa addFileImport(), addLibraryImport
401 */
402 bool QQmlImports::resolveType(const QHashedStringRef &type,
403                               QQmlType** type_return, QString* url_return, int *vmaj, int *vmin,
404                               QQmlImportNamespace** ns_return, QList<QQmlError> *errors) const
405 {
406     QQmlImportNamespace* ns = d->findQualifiedNamespace(type);
407     if (ns) {
408         if (ns_return)
409             *ns_return = ns;
410         return true;
411     }
412     if (type_return || url_return) {
413         if (d->resolveType(type,vmaj,vmin,type_return,url_return, errors)) {
414             if (qmlImportTrace()) {
415 #define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \
416                                               << ')' << "::resolveType: " << type.toString() << " => "
417
418                 if (type_return && *type_return && url_return && !url_return->isEmpty())
419                     RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << ' ' << *url_return << " TYPE/URL";
420                 if (type_return && *type_return)
421                     RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << " TYPE";
422                 if (url_return && !url_return->isEmpty())
423                     RESOLVE_TYPE_DEBUG << *url_return << " URL";
424
425 #undef RESOLVE_TYPE_DEBUG
426             }
427             return true;
428         }
429     }
430     return false;
431 }
432
433 bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors)
434 {
435     Q_ASSERT(resolvedUrl.endsWith(Slash));
436     url = resolvedUrl;
437
438     qmlDirComponents = qmldir->components();
439
440     const QQmlDirScripts &scripts = qmldir->scripts();
441     if (!scripts.isEmpty()) {
442         // Verify that we haven't imported these scripts already
443         for (QList<QQmlImportNamespace::Import *>::const_iterator it = nameSpace->imports.constBegin();
444              it != nameSpace->imports.constEnd(); ++it) {
445             if ((*it != this) && ((*it)->uri == uri)) {
446                 QQmlError error;
447                 error.setDescription(QQmlImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg((*it)->url));
448                 errors->prepend(error);
449                 return false;
450             }
451         }
452
453         qmlDirScripts = getVersionedScripts(scripts, majversion, minversion);
454     }
455
456     return true;
457 }
458
459 QQmlDirScripts QQmlImportNamespace::Import::getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin)
460 {
461     QMap<QString, QQmlDirParser::Script> versioned;
462
463     for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
464          sit != qmldirscripts.constEnd(); ++sit) {
465         // Only include scripts that match our requested version
466         if (((vmaj == -1) || (sit->majorVersion == vmaj)) &&
467             ((vmin == -1) || (sit->minorVersion <= vmin))) {
468             // Load the highest version that matches
469             QMap<QString, QQmlDirParser::Script>::iterator vit = versioned.find(sit->nameSpace);
470             if (vit == versioned.end() || (vit->minorVersion < sit->minorVersion)) {
471                 versioned.insert(sit->nameSpace, *sit);
472             }
473         }
474     }
475
476     return versioned.values();
477 }
478
479 /*!
480   \internal
481
482   Searching \e only in the namespace \a ns (previously returned in a call to
483   resolveType(), \a type is found and returned to either
484   a QQmlType stored at \a type_return, or
485   a component located at \a url_return.
486
487   If either return pointer is 0, the corresponding search is not done.
488 */
489 bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QHashedStringRef &type,
490                               QQmlType** type_return, QString* url_return,
491                               int *vmaj, int *vmin) const
492 {
493     return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return,url_return);
494 }
495
496 bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader,
497                                               const QHashedStringRef& type, int *vmajor, int *vminor,
498                                               QQmlType** type_return, QString* url_return,
499                                               QString *base, bool *typeRecursionDetected) const
500 {
501     if (majversion >= 0 && minversion >= 0) {
502         QQmlType *t = QQmlMetaType::qmlType(type, uri, majversion, minversion);
503         if (t) {
504             if (vmajor) *vmajor = majversion;
505             if (vminor) *vminor = minversion;
506             if (type_return)
507                 *type_return = t;
508             return true;
509         }
510     }
511
512     QQmlDirComponents::ConstIterator it = qmlDirComponents.find(type), end = qmlDirComponents.end();
513     if (it != end) {
514         QString componentUrl;
515         QQmlDirComponents::ConstIterator candidate = end;
516         for ( ; it != end && it.key() == type; ++it) {
517             const QQmlDirParser::Component &c = *it;
518
519             // importing version -1 means import ALL versions
520             if ((majversion == -1) ||
521                 (c.majorVersion == majversion && c.minorVersion <= minversion)) {
522                 // Is this better than the previous candidate?
523                 if ((candidate == end) ||
524                     (c.majorVersion > candidate->majorVersion) ||
525                     ((c.majorVersion == candidate->majorVersion) && (c.minorVersion > candidate->minorVersion))) {
526                     componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
527                     if (c.internal && base) {
528                         if (resolveLocalUrl(*base, c.fileName) != componentUrl)
529                             continue; // failed attempt to access an internal type
530                     }
531                     if (base && (*base == componentUrl)) {
532                         if (typeRecursionDetected)
533                             *typeRecursionDetected = true;
534                         continue; // no recursion
535                     }
536
537                     // This is our best candidate so far
538                     candidate = it;
539                 }
540             }
541         }
542
543         if (candidate != end) {
544             if (url_return)
545                 *url_return = componentUrl;
546             return true;
547         }
548     } else if (!isLibrary) {
549         QString qmlUrl = url + QString::fromRawData(type.constData(), type.length()) + dotqml_string;
550
551         bool exists = false;
552
553         if (QQmlFile::isBundle(qmlUrl)) {
554             exists = QQmlFile::bundleFileExists(qmlUrl, typeLoader->engine());
555         } else {
556             exists = !typeLoader->absoluteFilePath(QQmlFile::urlToLocalFileOrQrc(qmlUrl)).isEmpty();
557         }
558
559         if (exists) {
560             if (base && (*base == qmlUrl)) { // no recursion
561                 if (typeRecursionDetected)
562                     *typeRecursionDetected = true;
563             } else {
564                 if (url_return)
565                     *url_return = qmlUrl;
566                 return true;
567             }
568         }
569     }
570
571     return false;
572 }
573
574 bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, int *vminor,
575                                      QQmlType** type_return, QString* url_return,
576                                      QList<QQmlError> *errors)
577 {
578     QQmlImportNamespace *s = 0;
579     int dot = type.indexOf(Dot);
580     if (dot >= 0) {
581         QHashedStringRef namespaceName(type.constData(), dot);
582         s = findQualifiedNamespace(namespaceName);
583         if (!s) {
584             if (errors) {
585                 QQmlError error;
586                 error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName.toString()));
587                 errors->prepend(error);
588             }
589             return false;
590         }
591         int ndot = type.indexOf(Dot,dot+1);
592         if (ndot > 0) {
593             if (errors) {
594                 QQmlError error;
595                 error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
596                 errors->prepend(error);
597             }
598             return false;
599         }
600     } else {
601         s = &unqualifiedset;
602     }
603     QHashedStringRef unqualifiedtype = dot < 0 ? type : QHashedStringRef(type.constData()+dot+1, type.length()-dot-1);
604     if (s) {
605         if (s->resolveType(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
606             return true;
607         if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && url_return && s != &unqualifiedset) {
608             // qualified, and only 1 url
609             *url_return = resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml"));
610             return true;
611         }
612     }
613
614     return false;
615 }
616
617 QQmlImportNamespace::Import *QQmlImportNamespace::findImport(const QString &uri)
618 {
619     for (QList<Import *>::iterator it = imports.begin(), end = imports.end(); it != end; ++it) {
620         if ((*it)->uri == uri)
621             return *it;
622     }
623
624     return 0;
625 }
626
627 bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type,
628                                       int *vmajor, int *vminor, QQmlType** type_return,
629                                       QString* url_return, QString *base, QList<QQmlError> *errors)
630 {
631     bool typeRecursionDetected = false;
632     for (int i=0; i<imports.count(); ++i) {
633         const Import *import = imports.at(i);
634         if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, url_return,
635                                base, &typeRecursionDetected)) {
636             if (qmlCheckTypes()) {
637                 // check for type clashes
638                 for (int j = i+1; j<imports.count(); ++j) {
639                     const Import *import2 = imports.at(j);
640                     if (import2->resolveType(typeLoader, type, vmajor, vminor, 0, 0, base)) {
641                         if (errors) {
642                             QString u1 = import->url;
643                             QString u2 = import2->url;
644                             if (base) {
645                                 QString b = *base;
646                                 int dot = b.lastIndexOf(Dot);
647                                 if (dot >= 0) {
648                                     b = b.left(dot+1);
649                                     QString l = b.left(dot);
650                                     if (u1.startsWith(b))
651                                         u1 = u1.mid(b.count());
652                                     else if (u1 == l)
653                                         u1 = QQmlImportDatabase::tr("local directory");
654                                     if (u2.startsWith(b))
655                                         u2 = u2.mid(b.count());
656                                     else if (u2 == l)
657                                         u2 = QQmlImportDatabase::tr("local directory");
658                                 }
659                             }
660
661                             QQmlError error;
662                             if (u1 != u2) {
663                                 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
664                             } else {
665                                 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
666                                                         .arg(u1)
667                                                         .arg(import->majversion).arg(import->minversion)
668                                                         .arg(import2->majversion).arg(import2->minversion));
669                             }
670                             errors->prepend(error);
671                         }
672                         return false;
673                     }
674                 }
675             }
676             return true;
677         }
678     }
679     if (errors) {
680         QQmlError error;
681         if (typeRecursionDetected)
682             error.setDescription(QQmlImportDatabase::tr("is instantiated recursively"));
683         else
684             error.setDescription(QQmlImportDatabase::tr("is not a type"));
685         errors->prepend(error);
686     }
687     return false;
688 }
689
690 QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader)
691 : ref(1), typeLoader(loader) {
692 }
693
694 QQmlImportsPrivate::~QQmlImportsPrivate()
695 {
696     while (QQmlImportNamespace *ns = qualifiedSets.takeFirst())
697         delete ns;
698 }
699
700 QQmlImportNamespace *QQmlImportsPrivate::findQualifiedNamespace(const QHashedStringRef &prefix) const
701 {
702     for (QQmlImportNamespace *ns = qualifiedSets.first(); ns; ns = qualifiedSets.next(ns)) {
703         if (prefix == ns->prefix)
704             return ns;
705     }
706     return 0;
707 }
708
709 /*!
710 Import an extension defined by a qmldir file.
711
712 \a qmldirFilePath is either a raw file path, or a bundle url.
713 */
714 bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
715                                          const QString &uri,
716                                          QQmlImportDatabase *database,
717                                          const QQmlTypeLoader::QmldirContent *qmldir,
718                                          QList<QQmlError> *errors)
719 {
720     Q_ASSERT(qmldir);
721
722     if (qmlImportTrace())
723         qDebug().nospace() << "QQmlImports(" << qPrintable(base) << ")::importExtension: "
724                            << "loaded " << qmldirFilePath;
725
726     if (!database->qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(qmldirFilePath)) {
727
728         QString qmldirPath = qmldirFilePath;
729
730         int slash = qmldirPath.lastIndexOf(Slash);
731         if (slash > 0)
732             qmldirPath.truncate(slash);
733
734         foreach (const QQmlDirParser::Plugin &plugin, qmldir->plugins()) {
735             QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath,
736                                                                plugin.path, plugin.name);
737             if (!resolvedFilePath.isEmpty()) {
738                 if (!database->importPlugin(resolvedFilePath, uri, qmldir->typeNamespace(), errors)) {
739                     if (errors) {
740                         // XXX TODO: should we leave the import plugin error alone?
741                         // Here, we pop it off the top and coalesce it into this error's message.
742                         // The reason is that the lower level may add url and line/column numbering information.
743                         QQmlError poppedError = errors->takeFirst();
744                         QQmlError error;
745                         error.setDescription(tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
746                         error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
747                         errors->prepend(error);
748                     }
749                     return false;
750                 }
751             } else {
752                 if (errors) {
753                     QQmlError error;
754                     error.setDescription(tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
755                     error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
756                     errors->prepend(error);
757                 }
758                 return false;
759             }
760         }
761
762         database->qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(qmldirFilePath);
763     }
764
765     return true;
766 }
767
768 bool QQmlImportsPrivate::getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
769                                           const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors)
770 {
771     Q_ASSERT(errors);
772     Q_ASSERT(qmldir);
773
774     *qmldir = typeLoader->qmldirContent(qmldirIdentifier, uri);
775     if (*qmldir) {
776         // Ensure that parsing was successful
777         if ((*qmldir)->hasError()) {
778             QUrl url;
779
780             if (QQmlFile::isBundle(qmldirIdentifier))
781                 url = QUrl(qmldirIdentifier);
782             else
783                 url = QUrl::fromLocalFile(qmldirIdentifier);
784
785             const QList<QQmlError> qmldirErrors = (*qmldir)->errors(uri);
786             for (int i = 0; i < qmldirErrors.size(); ++i) {
787                 QQmlError error = qmldirErrors.at(i);
788                 error.setUrl(url);
789                 errors->append(error);
790             }
791             return false;
792         }
793     }
794
795     return true;
796 }
797
798 QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
799 {
800     struct I { static bool greaterThan(const QString &s1, const QString &s2) {
801         return s1 > s2;
802     } };
803
804     QString dir = dir_arg;
805     if (dir.endsWith(Slash) || dir.endsWith(Backslash))
806         dir.chop(1);
807
808     QStringList paths = database->fileImportPath;
809     qSort(paths.begin(), paths.end(), I::greaterThan); // Ensure subdirs preceed their parents.
810
811     QString stableRelativePath = dir;
812     foreach(const QString &path, paths) {
813         if (dir.startsWith(path)) {
814             stableRelativePath = dir.mid(path.length()+1);
815             break;
816         }
817     }
818
819     stableRelativePath.replace(Backslash, Slash);
820
821     // remove optional versioning in dot notation from uri
822     int lastSlash = stableRelativePath.lastIndexOf(Slash);
823     if (lastSlash >= 0) {
824         int versionDot = stableRelativePath.indexOf(Dot, lastSlash);
825         if (versionDot >= 0)
826             stableRelativePath = stableRelativePath.left(versionDot);
827     }
828
829     stableRelativePath.replace(Slash, Dot);
830
831     return stableRelativePath;
832 }
833
834 /*
835 Locates the qmldir file for \a uri version \a vmaj.vmin.  Returns true if found,
836 and fills in outQmldirFilePath and outQmldirUrl appropriately.  Otherwise returns
837 false.
838 */
839 bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database,
840                                       QString *outQmldirFilePath, QString *outQmldirPathUrl)
841 {
842     Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
843
844     // Check cache first
845
846     QQmlImportDatabase::QmldirCache *cacheHead = 0;
847     {
848     QQmlImportDatabase::QmldirCache **cachePtr = database->qmldirCache.value(uri);
849     if (cachePtr) {
850         cacheHead = *cachePtr;
851         QQmlImportDatabase::QmldirCache *cache = cacheHead;
852         while (cache) {
853             if (cache->versionMajor == vmaj && cache->versionMinor == vmin) {
854                 *outQmldirFilePath = cache->qmldirFilePath;
855                 *outQmldirPathUrl = cache->qmldirPathUrl;
856                 return !cache->qmldirFilePath.isEmpty();
857             }
858             cache = cache->next;
859         }
860     }
861     }
862
863     QQmlTypeLoader &typeLoader = QQmlEnginePrivate::get(database->engine)->typeLoader;
864
865     QStringList localImportPaths = database->importPathList(QQmlImportDatabase::Local);
866
867     // Search local import paths for a matching version
868     for (int version = QQmlImports::FullyVersioned; version <= QQmlImports::Unversioned; ++version) {
869         foreach (const QString &path, localImportPaths) {
870             QString qmldirPath = QQmlImports::completeQmldirPath(uri, path, vmaj, vmin, static_cast<QQmlImports::ImportVersion>(version));
871
872             QString absoluteFilePath = typeLoader.absoluteFilePath(qmldirPath);
873             if (!absoluteFilePath.isEmpty()) {
874                 QString url;
875                 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
876                 if (absolutePath.at(0) == Colon)
877                     url = QLatin1String("qrc://") + absolutePath.mid(1);
878                 else
879                     url = QUrl::fromLocalFile(absolutePath).toString();
880
881                 QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
882                 cache->versionMajor = vmaj;
883                 cache->versionMinor = vmin;
884                 cache->qmldirFilePath = absoluteFilePath;
885                 cache->qmldirPathUrl = url;
886                 cache->next = cacheHead;
887                 database->qmldirCache.insert(uri, cache);
888
889                 *outQmldirFilePath = absoluteFilePath;
890                 *outQmldirPathUrl = url;
891
892                 return true;
893             }
894         }
895     }
896
897     QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
898     cache->versionMajor = vmaj;
899     cache->versionMinor = vmin;
900     cache->next = cacheHead;
901     database->qmldirCache.insert(uri, cache);
902
903     return false;
904 }
905
906 bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin,
907                                                QList<QQmlError> *errors)
908 {
909     int lowest_min = INT_MAX;
910     int highest_min = INT_MIN;
911
912     typedef QQmlDirComponents::const_iterator ConstIterator;
913     const QQmlDirComponents &components = qmldir->components();
914
915     ConstIterator cend = components.constEnd();
916     for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
917         for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
918             if ((cit2->typeName == cit->typeName) &&
919                 (cit2->majorVersion == cit->majorVersion) &&
920                 (cit2->minorVersion == cit->minorVersion)) {
921                 // This entry clashes with a predecessor
922                 QQmlError error;
923                 error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
924                                      .arg(cit->typeName).arg(cit->majorVersion).arg(cit->minorVersion).arg(uri));
925                 errors->prepend(error);
926                 return false;
927             }
928         }
929
930         if (cit->majorVersion == vmaj) {
931             lowest_min = qMin(lowest_min, cit->minorVersion);
932             highest_min = qMax(highest_min, cit->minorVersion);
933         }
934     }
935
936     typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
937     const QQmlDirScripts &scripts = qmldir->scripts();
938
939     SConstIterator send = scripts.constEnd();
940     for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
941         for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
942             if ((sit2->nameSpace == sit->nameSpace) &&
943                 (sit2->majorVersion == sit->majorVersion) &&
944                 (sit2->minorVersion == sit->minorVersion)) {
945                 // This entry clashes with a predecessor
946                 QQmlError error;
947                 error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
948                                      .arg(sit->nameSpace).arg(sit->majorVersion).arg(sit->minorVersion).arg(uri));
949                 errors->prepend(error);
950                 return false;
951             }
952         }
953
954         if (sit->majorVersion == vmaj) {
955             lowest_min = qMin(lowest_min, sit->minorVersion);
956             highest_min = qMax(highest_min, sit->minorVersion);
957         }
958     }
959
960     if (lowest_min > vmin || highest_min < vmin) {
961         QQmlError error;
962         error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
963         errors->prepend(error);
964         return false;
965     }
966
967     return true;
968 }
969
970 QQmlImportNamespace *QQmlImportsPrivate::importNamespace(const QString &prefix) const
971 {
972     QQmlImportNamespace *nameSpace = 0;
973
974     if (prefix.isEmpty()) {
975         nameSpace = &unqualifiedset;
976     } else {
977         nameSpace = findQualifiedNamespace(prefix);
978
979         if (!nameSpace) {
980             nameSpace = new QQmlImportNamespace;
981             nameSpace->prefix = prefix;
982             qualifiedSets.append(nameSpace);
983         }
984     }
985
986     return nameSpace;
987 }
988
989 QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace,
990                                                                       const QString &uri, const QString &url, int vmaj, int vmin,
991                                                                       QQmlScript::Import::Type type,
992                                                                       QList<QQmlError> *errors)
993 {
994     Q_ASSERT(nameSpace);
995     Q_ASSERT(errors);
996     Q_ASSERT(url.isEmpty() || url.endsWith(Slash));
997
998     QQmlImportNamespace::Import *import = new QQmlImportNamespace::Import;
999     import->uri = uri;
1000     import->url = url;
1001     import->majversion = vmaj;
1002     import->minversion = vmin;
1003     import->isLibrary = (type == QQmlScript::Import::Library);
1004
1005     nameSpace->imports.prepend(import);
1006     return import;
1007 }
1008
1009 bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &prefix,
1010                                           int vmaj, int vmin, const QString &qmldirIdentifier, const QString &qmldirUrl, bool incomplete,
1011                                           QQmlImportDatabase *database,
1012                                           QList<QQmlError> *errors)
1013 {
1014     Q_ASSERT(database);
1015     Q_ASSERT(errors);
1016
1017     QQmlImportNamespace *nameSpace = importNamespace(prefix);
1018     Q_ASSERT(nameSpace);
1019
1020     QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, uri, qmldirUrl, vmaj, vmin, QQmlScript::Import::Library, errors);
1021     Q_ASSERT(inserted);
1022
1023     if (!incomplete) {
1024         const QQmlTypeLoader::QmldirContent *qmldir = 0;
1025
1026         if (!qmldirIdentifier.isEmpty()) {
1027             if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
1028                 return false;
1029
1030             if (qmldir) {
1031                 if (!importExtension(qmldir->pluginLocation(), uri, database, qmldir, errors))
1032                     return false;
1033
1034                 if (!inserted->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors))
1035                     return false;
1036             }
1037         }
1038
1039         // Ensure that we are actually providing something
1040         if ((vmaj < 0) || (vmin < 0) || !QQmlMetaType::isModule(uri, vmaj, vmin)) {
1041             if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1042                 QQmlError error;
1043                 if (QQmlMetaType::isAnyModule(uri))
1044                     error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
1045                 else
1046                     error.setDescription(tr("module \"%1\" is not installed").arg(uri));
1047                 errors->prepend(error);
1048                 return false;
1049             } else if ((vmaj >= 0) && (vmin >= 0) && qmldir) {
1050                 // Verify that the qmldir content is valid for this version
1051                 if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors))
1052                     return false;
1053             }
1054         }
1055     }
1056
1057     return true;
1058 }
1059
1060 bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix,
1061                                        int vmaj, int vmin,
1062                                        bool isImplicitImport, bool incomplete, QQmlImportDatabase *database,
1063                                        QList<QQmlError> *errors)
1064 {
1065     Q_ASSERT(errors);
1066
1067     QQmlImportNamespace *nameSpace = importNamespace(prefix);
1068     Q_ASSERT(nameSpace);
1069
1070     // The uri for this import.  For library imports this is the same as uri
1071     // specified by the user, but it may be different in the case of file imports.
1072     QString importUri = uri;
1073
1074     QString qmldirPath = importUri;
1075     if (importUri.endsWith(Slash))
1076         qmldirPath += String_qmldir;
1077     else
1078         qmldirPath += Slash_qmldir;
1079
1080     QString qmldirUrl = resolveLocalUrl(base, qmldirPath);
1081
1082     QString qmldirIdentifier;
1083
1084     if (QQmlFile::isBundle(qmldirUrl)) {
1085
1086         QString dir = resolveLocalUrl(base, importUri);
1087         Q_ASSERT(QQmlFile::isBundle(dir));
1088         if (!QQmlFile::bundleDirectoryExists(dir, typeLoader->engine())) {
1089             if (!isImplicitImport) {
1090                 QQmlError error;
1091                 error.setDescription(tr("\"%1\": no such directory").arg(uri));
1092                 error.setUrl(QUrl(qmldirUrl));
1093                 errors->prepend(error);
1094             }
1095             return false;
1096         }
1097
1098         // Transforms the (possible relative) uri into our best guess relative to the
1099         // import paths.
1100         importUri = resolvedUri(dir, database);
1101         if (importUri.endsWith(Slash))
1102             importUri.chop(1);
1103
1104         if (QQmlFile::bundleFileExists(qmldirUrl, typeLoader->engine()))
1105             qmldirIdentifier = qmldirUrl;
1106
1107     } else if (QQmlFile::isLocalFile(qmldirUrl)) {
1108
1109         QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1110         Q_ASSERT(!localFileOrQrc.isEmpty());
1111
1112         QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, importUri));
1113         if (!typeLoader->directoryExists(dir)) {
1114             if (!isImplicitImport) {
1115                 QQmlError error;
1116                 error.setDescription(tr("\"%1\": no such directory").arg(uri));
1117                 error.setUrl(QUrl(qmldirUrl));
1118                 errors->prepend(error);
1119             }
1120             return false;
1121         }
1122
1123         // Transforms the (possible relative) uri into our best guess relative to the
1124         // import paths.
1125         importUri = resolvedUri(dir, database);
1126         if (importUri.endsWith(Slash))
1127             importUri.chop(1);
1128
1129         if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty())
1130             qmldirIdentifier = localFileOrQrc;
1131
1132     } else if (nameSpace->prefix.isEmpty() && !incomplete) {
1133
1134         if (!isImplicitImport) {
1135             QQmlError error;
1136             error.setDescription(tr("import \"%1\" has no qmldir and no namespace").arg(importUri));
1137             error.setUrl(QUrl(qmldirUrl));
1138             errors->prepend(error);
1139         }
1140
1141         return false;
1142
1143     }
1144
1145     // The url for the path containing files for this import
1146     QString url = resolveLocalUrl(base, uri);
1147     if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1148         url += Slash;
1149
1150     QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QQmlScript::Import::File, errors);
1151     Q_ASSERT(inserted);
1152
1153     if (!incomplete && !qmldirIdentifier.isEmpty()) {
1154         const QQmlTypeLoader::QmldirContent *qmldir = 0;
1155         if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors))
1156             return false;
1157
1158         if (qmldir) {
1159             if (!importExtension(qmldir->pluginLocation(), importUri, database, qmldir, errors))
1160                 return false;
1161
1162             if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors))
1163                 return false;
1164         }
1165     }
1166
1167     return true;
1168 }
1169
1170 bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString &prefix,
1171                                              const QString &qmldirIdentifier, const QString& qmldirUrl,
1172                                              QQmlImportDatabase *database, QList<QQmlError> *errors)
1173 {
1174     QQmlImportNamespace *nameSpace = importNamespace(prefix);
1175     Q_ASSERT(nameSpace);
1176
1177     if (QQmlImportNamespace::Import *import = nameSpace->findImport(uri)) {
1178         const QQmlTypeLoader::QmldirContent *qmldir = 0;
1179         if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
1180             return false;
1181
1182         if (qmldir) {
1183             if (!importExtension(qmldir->pluginLocation(), uri, database, qmldir, errors))
1184                 return false;
1185
1186             if (import->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) {
1187                 // Ensure that we are actually providing something
1188                 int vmaj = import->majversion;
1189                 int vmin = import->minversion;
1190
1191                 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1192                     // The implicit import qmldir can be empty
1193                     if (uri != QLatin1String(".")) {
1194                         QQmlError error;
1195                         if (QQmlMetaType::isAnyModule(uri))
1196                             error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
1197                         else
1198                             error.setDescription(tr("module \"%1\" is not installed").arg(uri));
1199                         errors->prepend(error);
1200                         return false;
1201                     }
1202                 } else if ((vmaj >= 0) && (vmin >= 0)) {
1203                     // Verify that the qmldir content is valid for this version
1204                     if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors))
1205                         return false;
1206                 }
1207                 return true;
1208             }
1209         }
1210     }
1211
1212     if (errors->isEmpty()) {
1213         QQmlError error;
1214         error.setDescription(QQmlTypeLoader::tr("Cannot update qmldir content for '%1'").arg(uri));
1215         errors->prepend(error);
1216     }
1217
1218     return false;
1219 }
1220
1221 /*!
1222   \internal
1223
1224   Adds an implicit "." file import.  This is equivalent to calling addFileImport(), but error
1225   messages related to the path or qmldir file not existing are suppressed.
1226 */
1227 bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlError> *errors)
1228 {
1229     Q_ASSERT(errors);
1230
1231     if (qmlImportTrace())
1232         qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString())
1233                            << ")::addImplicitImport";
1234
1235     bool incomplete = !isLocal(baseUrl());
1236     return d->addFileImport(QLatin1String("."), QString(), -1, -1, true, incomplete, importDb, errors);
1237 }
1238
1239 /*!
1240   \internal
1241
1242   Adds information to \a imports such that subsequent calls to resolveType()
1243   will resolve types qualified by \a prefix by considering types found at the given \a uri.
1244
1245   The uri is either a directory (if importType is FileImport), or a URI resolved using paths
1246   added via addImportPath() (if importType is LibraryImport).
1247
1248   The \a prefix may be empty, in which case the import location is considered for
1249   unqualified types.
1250
1251   The base URL must already have been set with Import::setBaseUrl().
1252
1253   Optionally, the url the import resolved to can be returned by providing the url parameter.
1254   Not all imports will result in an output url being generated, in which case the url will
1255   be set to an empty string.
1256
1257   Returns true on success, and false on failure.  In case of failure, the errors array will
1258   filled appropriately.
1259 */
1260 bool QQmlImports::addFileImport(QQmlImportDatabase *importDb,
1261                                 const QString& uri, const QString& prefix, int vmaj, int vmin,
1262                                 bool incomplete, QList<QQmlError> *errors)
1263 {
1264     Q_ASSERT(importDb);
1265     Q_ASSERT(errors);
1266
1267     if (qmlImportTrace())
1268         qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::addFileImport: "
1269                            << uri << ' ' << vmaj << '.' << vmin << " as " << prefix;
1270
1271     return d->addFileImport(uri, prefix, vmaj, vmin, false, incomplete, importDb, errors);
1272 }
1273
1274 bool QQmlImports::addLibraryImport(QQmlImportDatabase *importDb,
1275                                    const QString &uri, const QString &prefix, int vmaj, int vmin,
1276                                    const QString &qmldirIdentifier, const QString& qmldirUrl, bool incomplete, QList<QQmlError> *errors)
1277 {
1278     Q_ASSERT(importDb);
1279     Q_ASSERT(errors);
1280
1281     if (qmlImportTrace())
1282         qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::addLibraryImport: "
1283                            << uri << ' ' << vmaj << '.' << vmin << " as " << prefix;
1284
1285     return d->addLibraryImport(uri, prefix, vmaj, vmin, qmldirIdentifier, qmldirUrl, incomplete, importDb, errors);
1286 }
1287
1288 bool QQmlImports::updateQmldirContent(QQmlImportDatabase *importDb,
1289                                       const QString &uri, const QString &prefix,
1290                                       const QString &qmldirIdentifier, const QString& qmldirUrl, QList<QQmlError> *errors)
1291 {
1292     Q_ASSERT(importDb);
1293     Q_ASSERT(errors);
1294
1295     if (qmlImportTrace())
1296         qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::updateQmldirContent: "
1297                            << uri << " to " << qmldirUrl << " as " << prefix;
1298
1299     return d->updateQmldirContent(uri, prefix, qmldirIdentifier, qmldirUrl, importDb, errors);
1300 }
1301
1302 bool QQmlImports::locateQmldir(QQmlImportDatabase *importDb,
1303                                const QString& uri, int vmaj, int vmin,
1304                                QString *qmldirFilePath, QString *url)
1305 {
1306     return d->locateQmldir(uri, vmaj, vmin, importDb, qmldirFilePath, url);
1307 }
1308
1309 bool QQmlImports::isLocal(const QString &url)
1310 {
1311     return QQmlFile::isBundle(url) || !QQmlFile::urlToLocalFileOrQrc(url).isEmpty();
1312 }
1313
1314 bool QQmlImports::isLocal(const QUrl &url)
1315 {
1316     return QQmlFile::isBundle(url) || !QQmlFile::urlToLocalFileOrQrc(url).isEmpty();
1317 }
1318
1319
1320 /*!
1321 \class QQmlImportDatabase
1322 \brief The QQmlImportDatabase class manages the QML imports for a QQmlEngine.
1323 \internal
1324 */
1325 QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
1326 : engine(e)
1327 {
1328     filePluginPath << QLatin1String(".");
1329
1330     // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
1331
1332     QString installImportsPath =  QLibraryInfo::location(QLibraryInfo::ImportsPath);
1333     addImportPath(installImportsPath);
1334
1335     // env import paths
1336     QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
1337     if (!envImportPath.isEmpty()) {
1338 #if defined(Q_OS_WIN)
1339         QLatin1Char pathSep(';');
1340 #else
1341         QLatin1Char pathSep(':');
1342 #endif
1343         QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
1344         for (int ii = paths.count() - 1; ii >= 0; --ii)
1345             addImportPath(paths.at(ii));
1346     }
1347
1348     addImportPath(QCoreApplication::applicationDirPath());
1349 }
1350
1351 QQmlImportDatabase::~QQmlImportDatabase()
1352 {
1353     qmldirCache.clear();
1354 }
1355
1356 /*!
1357   \internal
1358
1359   Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
1360   The \a prefix must contain the dot.
1361
1362   \a qmldirPath is the location of the qmldir file.
1363  */
1364 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1365                                           const QString &qmldirPath,
1366                                           const QString &qmldirPluginPath,
1367                                           const QString &baseName, const QStringList &suffixes,
1368                                           const QString &prefix)
1369 {
1370     QStringList searchPaths = filePluginPath;
1371     bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
1372     if (!qmldirPluginPathIsRelative)
1373         searchPaths.prepend(qmldirPluginPath);
1374
1375     foreach (const QString &pluginPath, searchPaths) {
1376
1377         QString resolvedPath;
1378         if (pluginPath == QLatin1String(".")) {
1379             if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
1380                 resolvedPath = QDir::cleanPath(qmldirPath + Slash + qmldirPluginPath);
1381             else
1382                 resolvedPath = qmldirPath;
1383         } else {
1384             if (QDir::isRelativePath(pluginPath))
1385                 resolvedPath = QDir::cleanPath(qmldirPath + Slash + pluginPath);
1386             else
1387                 resolvedPath = pluginPath;
1388         }
1389
1390         // hack for resources, should probably go away
1391         if (resolvedPath.startsWith(Colon))
1392             resolvedPath = QCoreApplication::applicationDirPath();
1393
1394         if (!resolvedPath.endsWith(Slash))
1395             resolvedPath += Slash;
1396
1397         foreach (const QString &suffix, suffixes) {
1398             QString pluginFileName = prefix;
1399
1400             pluginFileName += baseName;
1401             pluginFileName += suffix;
1402
1403             QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
1404             if (!absolutePath.isEmpty())
1405                 return absolutePath;
1406         }
1407     }
1408
1409     if (qmlImportTrace())
1410         qDebug() << "QQmlImportDatabase::resolvePlugin: Could not resolve plugin" << baseName 
1411                  << "in" << qmldirPath;
1412
1413     return QString();
1414 }
1415
1416 /*!
1417   \internal
1418
1419   Returns the result of the merge of \a baseName with \a dir and the platform suffix.
1420
1421   \table
1422   \header \li Platform \li Valid suffixes
1423   \row \li Windows     \li \c .dll
1424   \row \li Unix/Linux  \li \c .so
1425   \row \li AIX  \li \c .a
1426   \row \li HP-UX       \li \c .sl, \c .so (HP-UXi)
1427   \row \li Mac OS X    \li \c .dylib, \c .bundle, \c .so
1428   \endtable
1429
1430   Version number on unix are ignored.
1431 */
1432 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1433                                                   const QString &qmldirPath, const QString &qmldirPluginPath,
1434                                                   const QString &baseName)
1435 {
1436 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
1437     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1438                          QStringList()
1439 # ifdef QT_DEBUG
1440                          << QLatin1String("d.dll") // try a qmake-style debug build first
1441 # endif
1442                          << QLatin1String(".dll"));
1443 #else
1444
1445 # if defined(Q_OS_DARWIN)
1446
1447     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1448                          QStringList()
1449 # ifdef QT_DEBUG
1450                          << QLatin1String("_debug.dylib") // try a qmake-style debug build first
1451                          << QLatin1String(".dylib")
1452 # else
1453                          << QLatin1String(".dylib")
1454                          << QLatin1String("_debug.dylib") // try a qmake-style debug build after
1455 # endif
1456                          << QLatin1String(".so")
1457                          << QLatin1String(".bundle"),
1458                          QLatin1String("lib"));
1459 # else  // Generic Unix
1460     QStringList validSuffixList;
1461
1462 #  if defined(Q_OS_HPUX)
1463 /*
1464     See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
1465     "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
1466     the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
1467  */
1468     validSuffixList << QLatin1String(".sl");
1469 #   if defined __ia64
1470     validSuffixList << QLatin1String(".so");
1471 #   endif
1472 #  elif defined(Q_OS_AIX)
1473     validSuffixList << QLatin1String(".a") << QLatin1String(".so");
1474 #  elif defined(Q_OS_UNIX)
1475     validSuffixList << QLatin1String(".so");
1476 #  endif
1477
1478     // Examples of valid library names:
1479     //  libfoo.so
1480
1481     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
1482 # endif
1483
1484 #endif
1485 }
1486
1487 /*!
1488     \internal
1489 */
1490 QStringList QQmlImportDatabase::pluginPathList() const
1491 {
1492     return filePluginPath;
1493 }
1494
1495 /*!
1496     \internal
1497 */
1498 void QQmlImportDatabase::setPluginPathList(const QStringList &paths)
1499 {
1500     if (qmlImportTrace())
1501         qDebug().nospace() << "QQmlImportDatabase::setPluginPathList: " << paths;
1502
1503     filePluginPath = paths;
1504 }
1505
1506 /*!
1507     \internal
1508 */
1509 void QQmlImportDatabase::addPluginPath(const QString& path)
1510 {
1511     if (qmlImportTrace())
1512         qDebug().nospace() << "QQmlImportDatabase::addPluginPath: " << path;
1513
1514     QUrl url = QUrl(path);
1515     if (url.isRelative() || url.scheme() == QLatin1String("file")
1516             || (url.scheme().length() == 1 && QFile::exists(path)) ) {  // windows path
1517         QDir dir = QDir(path);
1518         filePluginPath.prepend(dir.canonicalPath());
1519     } else {
1520         filePluginPath.prepend(path);
1521     }
1522 }
1523
1524 /*!
1525     \internal
1526 */
1527 void QQmlImportDatabase::addImportPath(const QString& path)
1528 {
1529     if (qmlImportTrace())
1530         qDebug().nospace() << "QQmlImportDatabase::addImportPath: " << path;
1531
1532     if (path.isEmpty())
1533         return;
1534
1535     QUrl url = QUrl(path);
1536     QString cPath;
1537
1538     if (url.scheme() == QLatin1String("file")) {
1539         cPath = QQmlFile::urlToLocalFileOrQrc(url);
1540     } else if (path.startsWith(QLatin1Char(':'))) {
1541         // qrc directory, e.g. :/foo
1542         // need to convert to a qrc url, e.g. qrc:/foo
1543         cPath = QStringLiteral("qrc") + path;
1544         cPath.replace(Backslash, Slash);
1545     } else if (url.isRelative() ||
1546                (url.scheme().length() == 1 && QFile::exists(path)) ) {  // windows path
1547         QDir dir = QDir(path);
1548         cPath = dir.canonicalPath();
1549     } else {
1550         cPath = path;
1551         cPath.replace(Backslash, Slash);
1552     }
1553
1554     if (!cPath.isEmpty()
1555         && !fileImportPath.contains(cPath))
1556         fileImportPath.prepend(cPath);
1557 }
1558
1559 /*!
1560     \internal
1561 */
1562 QStringList QQmlImportDatabase::importPathList(PathType type) const
1563 {
1564     if (type == LocalOrRemote)
1565         return fileImportPath;
1566
1567     QStringList list;
1568     foreach (const QString &path, fileImportPath) {
1569         bool localPath = isPathAbsolute(path) || QQmlFile::isLocalFile(path);
1570         if (localPath == (type == Local))
1571             list.append(path);
1572     }
1573
1574     return list;
1575 }
1576
1577 /*!
1578     \internal
1579 */
1580 void QQmlImportDatabase::setImportPathList(const QStringList &paths)
1581 {
1582     if (qmlImportTrace())
1583         qDebug().nospace() << "QQmlImportDatabase::setImportPathList: " << paths;
1584
1585     fileImportPath = paths;
1586
1587     // Our existing cached paths may have been invalidated
1588     qmldirCache.clear();
1589 }
1590
1591 /*!
1592     \internal
1593 */
1594 bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &uri, const QString &typeNamespace, QList<QQmlError> *errors)
1595 {
1596     if (qmlImportTrace())
1597         qDebug().nospace() << "QQmlImportDatabase::importPlugin: " << uri << " from " << filePath;
1598
1599 #ifndef QT_NO_LIBRARY
1600     QFileInfo fileInfo(filePath);
1601     const QString absoluteFilePath = fileInfo.absoluteFilePath();
1602
1603     bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1604     bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1605
1606     if (typesRegistered) {
1607         Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1608                    "QQmlImportDatabase::importPlugin",
1609                    "Internal error: Plugin imported previously with different uri");
1610     }
1611
1612     if (!engineInitialized || !typesRegistered) {
1613         if (!QQml_isFileCaseCorrect(absoluteFilePath)) {
1614             if (errors) {
1615                 QQmlError error;
1616                 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1617                 errors->prepend(error);
1618             }
1619             return false;
1620         }
1621         QPluginLoader loader(absoluteFilePath);
1622
1623         if (!loader.load()) {
1624             if (errors) {
1625                 QQmlError error;
1626                 error.setDescription(loader.errorString());
1627                 errors->prepend(error);
1628             }
1629             return false;
1630         }
1631
1632         QObject *instance = loader.instance();
1633         if (QQmlTypesExtensionInterface *iface = qobject_cast<QQmlExtensionInterface *>(instance)) {
1634
1635             const QByteArray bytes = uri.toUtf8();
1636             const char *moduleId = bytes.constData();
1637             if (!typesRegistered) {
1638
1639                 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1640
1641                 QStringList registrationFailures;
1642
1643                 {
1644                     QWriteLocker lock(QQmlMetaType::typeRegistrationLock());
1645
1646                     if (!typeNamespace.isEmpty()) {
1647                         // This is a 'strict' module
1648                         if (typeNamespace != uri) {
1649                             // The namespace for type registrations must match the URI for locating the module
1650                             QQmlError error;
1651                             error.setDescription(tr("Module namespace '%1' does not match import URI '%2'").arg(typeNamespace).arg(uri));
1652                             errors->prepend(error);
1653                             return false;
1654                         }
1655
1656                         if (QQmlMetaType::namespaceContainsRegistrations(typeNamespace)) {
1657                             // Other modules have already installed to this namespace
1658                             QQmlError error;
1659                             error.setDescription(tr("Namespace '%1' has already been used for type registration").arg(typeNamespace));
1660                             errors->prepend(error);
1661                             return false;
1662                         } else {
1663                             QQmlMetaType::protectNamespace(typeNamespace);
1664                         }
1665                     } else {
1666                         // This is not a stict module - provide a warning
1667                         qWarning().nospace() << qPrintable(tr("Module '%1' does not contain a module directive - it cannot be protected from external registrations.").arg(uri));
1668                     }
1669
1670                     QQmlMetaType::setTypeRegistrationNamespace(typeNamespace);
1671
1672                     iface->registerTypes(moduleId);
1673
1674                     registrationFailures = QQmlMetaType::typeRegistrationFailures();
1675                     QQmlMetaType::setTypeRegistrationNamespace(QString());
1676                 }
1677
1678                 if (!registrationFailures.isEmpty()) {
1679                     foreach (const QString &failure, registrationFailures) {
1680                         QQmlError error;
1681                         error.setDescription(failure);
1682                         errors->prepend(error);
1683                     }
1684                     return false;
1685                 }
1686             }
1687             if (!engineInitialized) {
1688                 // things on the engine (eg. adding new global objects) have to be done for every 
1689                 // engine.  
1690                 // XXX protect against double initialization
1691                 initializedPlugins.insert(absoluteFilePath);
1692
1693                 QQmlExtensionInterface *eiface = 
1694                     qobject_cast<QQmlExtensionInterface *>(instance);
1695                 if (eiface) {
1696                     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1697                     ep->typeLoader.initializeEngine(eiface, moduleId);
1698                 }
1699             }
1700         } else {
1701             if (errors) {
1702                 QQmlError error;
1703                 error.setDescription(loader.errorString());
1704                 errors->prepend(error);
1705             }
1706             return false;
1707         }
1708     }
1709
1710     return true;
1711 #else
1712     return false;
1713 #endif
1714 }
1715
1716 QT_END_NAMESPACE