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