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