Initial bundle support
[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 <QtQml/qqmlextensioninterface.h>
51 #include <private/qqmlglobal_p.h>
52 #include <private/qqmltypenamecache_p.h>
53 #include <private/qqmlengine_p.h>
54
55 QT_BEGIN_NAMESPACE
56
57 DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
58 DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
59
60 QString resolveLocalUrl(const QString &url, const QString &relative)
61 {
62     if (relative.contains(QLatin1Char(':'))) {
63         // contains a host name
64         return QUrl(url).resolved(QUrl(relative)).toString();
65     } else if (relative.isEmpty()) {
66         return url;
67     } else if (relative.at(0) == QLatin1Char('/') || !url.contains(QLatin1Char('/'))) {
68         return relative;
69     } else {
70         QString base(url.left(url.lastIndexOf(QLatin1Char('/')) + 1));
71
72         if (relative == QLatin1String("."))
73             return base;
74
75         base.append(relative);
76
77         // Remove any relative directory elements in the path
78         const QLatin1Char dot('.');
79         const QLatin1Char slash('/');
80
81         int length = base.length();
82         int index = 0;
83         while ((index = base.indexOf(QLatin1String("/."), index)) != -1) {
84             if ((length > (index + 2)) && (base.at(index + 2) == dot) &&
85                 (length == (index + 3) || (base.at(index + 3) == slash))) {
86                 // Either "/../" or "/..<END>"
87                 int previous = base.lastIndexOf(slash, index - 1);
88                 if (previous == -1)
89                     break;
90
91                 int removeLength = (index - previous) + 3;
92                 base.remove(previous + 1, removeLength);
93                 length -= removeLength;
94                 index = previous;
95             } else if ((length == (index + 2)) || (base.at(index + 2) == slash)) {
96                 // Either "/./" or "/.<END>"
97                 base.remove(index, 2);
98                 length -= 2;
99             } else {
100                 ++index;
101             }
102         }
103
104         return base;
105     }
106 }
107
108 typedef QMap<QString, QString> StringStringMap;
109 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
110
111 class QQmlImportNamespace
112 {
113 public:
114     struct Import {
115         QString uri;
116         QString url;
117         int majversion;
118         int minversion;
119         bool isLibrary;
120         QQmlDirComponents qmlDirComponents;
121         QQmlDirScripts qmlDirScripts;
122
123         bool resolveType(QQmlTypeLoader *typeLoader, const QString& type,
124                          int *vmajor, int *vminor,
125                          QQmlType** type_return, QString* url_return,
126                          QString *base = 0, bool *typeRecursionDetected = 0) const;
127     };
128     QList<Import> imports;
129
130     bool resolveType(QQmlTypeLoader *typeLoader, const QString& type,
131                      int *vmajor, int *vminor,
132                      QQmlType** type_return, QString* url_return,
133                      QString *base = 0, QList<QQmlError> *errors = 0);
134 };
135
136 class QQmlImportsPrivate
137 {
138 public:
139     QQmlImportsPrivate(QQmlTypeLoader *loader);
140     ~QQmlImportsPrivate();
141
142     bool addImport(const QQmlDirComponents &qmldircomponentsnetwork,
143                    const QString &importedUri, const QString& prefix,
144                    int vmaj, int vmin, QQmlScript::Import::Type importType,
145                    bool isImplicitImport, QQmlImportDatabase *database,
146                    QString *, QList<QQmlError> *errors);
147
148     bool resolveType(const QString& type, int *vmajor, int *vminor,
149                      QQmlType** type_return, QString* url_return,
150                      QList<QQmlError> *errors);
151
152     QUrl baseUrl;
153     QString base;
154     int ref;
155
156     QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
157     QQmlImportNamespace unqualifiedset;
158     QHash<QString, QQmlImportNamespace *> set;
159     QQmlTypeLoader *typeLoader;
160
161     static inline QString tr(const char *str) {
162         return QQmlImportDatabase::tr(str);
163     }
164
165 private:
166     static bool locateQmldir(const QString &uri, int vmaj, int vmin,
167                              QQmlImportDatabase *database,
168                              QString *outQmldirFilePath, QString *outUrl);
169     bool importExtension(const QString &absoluteFilePath, const QString &uri,
170                          QQmlImportDatabase *database, QQmlDirComponents* components,
171                          QQmlDirScripts *scripts,
172                          QString *url, QList<QQmlError> *errors);
173     QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database);
174 };
175
176 /*!
177 \class QQmlImports
178 \brief The QQmlImports class encapsulates one QML document's import statements.
179 \internal
180 */
181 QQmlImports::QQmlImports(const QQmlImports &copy) 
182 : d(copy.d)
183 {
184     ++d->ref;
185 }
186
187 QQmlImports &
188 QQmlImports::operator =(const QQmlImports &copy)
189 {
190     ++copy.d->ref;
191     if (--d->ref == 0)
192         delete d;
193     d = copy.d;
194     return *this;
195 }
196
197 QQmlImports::QQmlImports(QQmlTypeLoader *typeLoader)
198     : d(new QQmlImportsPrivate(typeLoader))
199 {
200 }
201
202 QQmlImports::~QQmlImports()
203 {
204     if (--d->ref == 0)
205         delete d;
206 }
207
208 /*!
209   Sets the base URL to be used for all relative file imports added.
210 */
211 void QQmlImports::setBaseUrl(const QUrl& url, const QString &urlString)
212 {
213     d->baseUrl = url;
214
215     if (urlString.isEmpty()) {
216         d->base = url.toString();
217     } else {
218         //Q_ASSERT(url.toString() == urlString);
219         d->base = urlString;
220     }
221 }
222
223 /*!
224   Returns the base URL to be used for all relative file imports added.
225 */
226 QUrl QQmlImports::baseUrl() const
227 {
228     return d->baseUrl;
229 }
230
231 void QQmlImports::populateCache(QQmlTypeNameCache *cache, QQmlEngine *engine) const
232 {
233     const QQmlImportNamespace &set = d->unqualifiedset;
234
235     for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
236         const QQmlImportNamespace::Import &import = set.imports.at(ii);
237         QQmlTypeModule *module = QQmlMetaType::typeModule(import.uri, import.majversion);
238         if (module)
239             cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import.minversion));
240     }
241
242     for (QHash<QString, QQmlImportNamespace* >::ConstIterator iter = d->set.begin();
243          iter != d->set.end(); 
244          ++iter) {
245
246         const QQmlImportNamespace &set = *iter.value();
247         for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
248             const QQmlImportNamespace::Import &import = set.imports.at(ii);
249             QQmlTypeModule *module = QQmlMetaType::typeModule(import.uri, import.majversion);
250             if (module) {
251                 QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[iter.key()];
252                 typeimport.modules.append(QQmlTypeModuleVersion(module, import.minversion));
253             }
254
255             QQmlMetaType::ModuleApi moduleApi = QQmlMetaType::moduleApi(import.uri, import.majversion,
256                                                                         import.minversion);
257             if (moduleApi.script || moduleApi.qobject) {
258                 QQmlTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
259                 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
260                 import.moduleApi = ep->moduleApiInstance(moduleApi);
261             }
262         }
263     }
264 }
265
266 QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
267 {
268     QList<QQmlImports::ScriptReference> scripts;
269
270     const QQmlImportNamespace &set = d->unqualifiedset;
271
272     for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
273         const QQmlImportNamespace::Import &import = set.imports.at(ii);
274
275         foreach (const QQmlDirParser::Script &script, import.qmlDirScripts) {
276             ScriptReference ref;
277             ref.nameSpace = script.nameSpace;
278             ref.location = QUrl(import.url).resolved(QUrl(script.fileName));
279             scripts.append(ref);
280         }
281     }
282
283     for (QHash<QString, QQmlImportNamespace* >::ConstIterator iter = d->set.constBegin();
284          iter != d->set.constEnd();
285          ++iter) {
286         const QQmlImportNamespace &set = *iter.value();
287
288         for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
289             const QQmlImportNamespace::Import &import = set.imports.at(ii);
290
291             foreach (const QQmlDirParser::Script &script, import.qmlDirScripts) {
292                 ScriptReference ref;
293                 ref.nameSpace = script.nameSpace;
294                 ref.qualifier = iter.key();
295                 ref.location = QUrl(import.url).resolved(QUrl(script.fileName));
296                 scripts.append(ref);
297             }
298         }
299     }
300
301     return scripts;
302 }
303
304 /*!
305   \internal
306
307   The given (namespace qualified) \a type is resolved to either
308   \list
309   \li a QQmlImportNamespace stored at \a ns_return,
310   \li a QQmlType stored at \a type_return, or
311   \li a component located at \a url_return.
312   \endlist
313
314   If any return pointer is 0, the corresponding search is not done.
315
316   \sa addImport()
317 */
318 bool QQmlImports::resolveType(const QString& type,
319                               QQmlType** type_return, QString* url_return, int *vmaj, int *vmin,
320                               QQmlImportNamespace** ns_return, QList<QQmlError> *errors) const
321 {
322     QQmlImportNamespace* ns = d->set.value(type);
323     if (ns) {
324         if (ns_return)
325             *ns_return = ns;
326         return true;
327     }
328     if (type_return || url_return) {
329         if (d->resolveType(type,vmaj,vmin,type_return,url_return, errors)) {
330             if (qmlImportTrace()) {
331 #define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \
332                                               << ")" << "::resolveType: " << type << " => "
333
334                 if (type_return && *type_return && url_return && !url_return->isEmpty())
335                     RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << " " << *url_return;
336                 if (type_return && *type_return)
337                     RESOLVE_TYPE_DEBUG << (*type_return)->typeName();
338                 if (url_return && !url_return->isEmpty())
339                     RESOLVE_TYPE_DEBUG << *url_return;
340
341 #undef RESOLVE_TYPE_DEBUG
342             }
343             return true;
344         }
345     }
346     return false;
347 }
348
349 /*!
350   \internal
351
352   Searching \e only in the namespace \a ns (previously returned in a call to
353   resolveType(), \a type is found and returned to either
354   a QQmlType stored at \a type_return, or
355   a component located at \a url_return.
356
357   If either return pointer is 0, the corresponding search is not done.
358 */
359 bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QString& type,
360                               QQmlType** type_return, QString* url_return,
361                               int *vmaj, int *vmin) const
362 {
363     return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return,url_return);
364 }
365
366 bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader,
367                                               const QString& type, int *vmajor, int *vminor,
368                                               QQmlType** type_return, QString* url_return,
369                                               QString *base, bool *typeRecursionDetected) const
370 {
371     if (majversion >= 0 && minversion >= 0) {
372         QString qt = uri + QLatin1Char('/') + type;
373         QQmlType *t = QQmlMetaType::qmlType(qt, majversion, minversion);
374         if (t) {
375             if (vmajor) *vmajor = majversion;
376             if (vminor) *vminor = minversion;
377             if (type_return)
378                 *type_return = t;
379             return true;
380         }
381     }
382
383     bool typeWasDeclaredInQmldir = false;
384     if (!qmlDirComponents.isEmpty()) {
385         foreach (const QQmlDirParser::Component &c, qmlDirComponents) {
386             if (c.typeName == type) {
387                 typeWasDeclaredInQmldir = true;
388                 // importing version -1 means import ALL versions
389                 if ((majversion == -1) || (c.majorVersion == majversion &&
390                                            minversion >= c.minorVersion)) {
391                     QString candidate = resolveLocalUrl(QString(url + type + QLatin1String(".qml")),
392                                                         c.fileName);
393                     if (c.internal && base) {
394                         if (resolveLocalUrl(*base, c.fileName) != candidate)
395                             continue; // failed attempt to access an internal type
396                     }
397                     if (base && *base == candidate) {
398                         if (typeRecursionDetected)
399                             *typeRecursionDetected = true;
400                         continue; // no recursion
401                     }
402                     if (url_return)
403                         *url_return = candidate;
404                     return true;
405                 }
406             }
407         }
408     }
409
410     if (!typeWasDeclaredInQmldir && !isLibrary) {
411         QString qmlUrl(url + type + QLatin1String(".qml"));
412
413         bool exists = false;
414
415         if (QQmlFile::isBundle(qmlUrl)) {
416             exists = QQmlFile::bundleFileExists(qmlUrl, typeLoader->engine());
417         } else {
418             QString file = QQmlFile::urlToLocalFileOrQrc(qmlUrl);
419             exists = !typeLoader->absoluteFilePath(QQmlFile::urlToLocalFileOrQrc(qmlUrl)).isEmpty();
420         }
421
422         if (exists) {
423             if (base && *base == qmlUrl) { // no recursion
424                 if (typeRecursionDetected)
425                     *typeRecursionDetected = true;
426             } else {
427                 if (url_return)
428                     *url_return = qmlUrl;
429                 return true;
430             }
431         }
432     }
433
434     return false;
435 }
436
437 bool QQmlImportsPrivate::resolveType(const QString& type, int *vmajor, int *vminor,
438                                      QQmlType** type_return, QString* url_return,
439                                      QList<QQmlError> *errors)
440 {
441     QQmlImportNamespace *s = 0;
442     int slash = type.indexOf(QLatin1Char('/'));
443     if (slash >= 0) {
444         QString namespaceName = type.left(slash);
445         s = set.value(namespaceName);
446         if (!s) {
447             if (errors) {
448                 QQmlError error;
449                 error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
450                 errors->prepend(error);
451             }
452             return false;
453         }
454         int nslash = type.indexOf(QLatin1Char('/'),slash+1);
455         if (nslash > 0) {
456             if (errors) {
457                 QQmlError error;
458                 error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
459                 errors->prepend(error);
460             }
461             return false;
462         }
463     } else {
464         s = &unqualifiedset;
465     }
466     QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
467     if (s) {
468         if (s->resolveType(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
469             return true;
470         if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
471             // qualified, and only 1 url
472             *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml"));
473             return true;
474         }
475     }
476
477     return false;
478 }
479
480 bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QString& type, int *vmajor,
481                                       int *vminor, QQmlType** type_return,
482                                       QString* url_return, QString *base, QList<QQmlError> *errors)
483 {
484     bool typeRecursionDetected = false;
485     for (int i=0; i<imports.count(); ++i) {
486         const Import &import = imports.at(i);
487         if (import.resolveType(typeLoader, type, vmajor, vminor, type_return, url_return,
488                                base, &typeRecursionDetected)) {
489             if (qmlCheckTypes()) {
490                 // check for type clashes
491                 for (int j = i+1; j<imports.count(); ++j) {
492                     const Import &import2 = imports.at(j);
493                     if (import2.resolveType(typeLoader, type, vmajor, vminor, 0, 0, base)) {
494                         if (errors) {
495                             QString u1 = imports.at(i).url;
496                             QString u2 = imports.at(j).url;
497                             if (base) {
498                                 QString b = *base;
499                                 int slash = b.lastIndexOf(QLatin1Char('/'));
500                                 if (slash >= 0) {
501                                     b = b.left(slash+1);
502                                     QString l = b.left(slash);
503                                     if (u1.startsWith(b))
504                                         u1 = u1.mid(b.count());
505                                     else if (u1 == l)
506                                         u1 = QQmlImportDatabase::tr("local directory");
507                                     if (u2.startsWith(b))
508                                         u2 = u2.mid(b.count());
509                                     else if (u2 == l)
510                                         u2 = QQmlImportDatabase::tr("local directory");
511                                 }
512                             }
513
514                             QQmlError error;
515                             if (u1 != u2) {
516                                 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
517                             } else {
518                                 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
519                                                         .arg(u1)
520                                                         .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
521                                                         .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
522                             }
523                             errors->prepend(error);
524                         }
525                         return false;
526                     }
527                 }
528             }
529             return true;
530         }
531     }
532     if (errors) {
533         QQmlError error;
534         if (typeRecursionDetected)
535             error.setDescription(QQmlImportDatabase::tr("is instantiated recursively"));
536         else
537             error.setDescription(QQmlImportDatabase::tr("is not a type"));
538         errors->prepend(error);
539     }
540     return false;
541 }
542
543 QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader)
544 : ref(1), typeLoader(loader) {
545 }
546
547 QQmlImportsPrivate::~QQmlImportsPrivate()
548 {
549     foreach (QQmlImportNamespace* s, set.values())
550         delete s;
551 }
552
553 /*!
554 Import an extension defined by a qmldir file.
555
556 \a qmldirFilePath is either a raw file path, or a bundle url.
557
558 This call will modify the \a url parameter if importing the extension redirects to
559 a bundle path.
560 */
561 bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
562                                          const QString &uri,
563                                          QQmlImportDatabase *database,
564                                          QQmlDirComponents* components,
565                                          QQmlDirScripts* scripts,
566                                          QString *url,
567                                          QList<QQmlError> *errors)
568 {
569     // As qmldirFilePath is always local, this method can always return synchronously
570     const QQmlDirParser *qmldirParser = typeLoader->qmlDirParser(qmldirFilePath, uri, url);
571     if (qmldirParser->hasError()) {
572         if (errors) {
573             QUrl url;
574
575             if (QQmlFile::isBundle(qmldirFilePath))
576                 url = QUrl(qmldirFilePath);
577             else
578                 url = QUrl::fromLocalFile(qmldirFilePath);
579
580             const QList<QQmlError> qmldirErrors = qmldirParser->errors(uri);
581             for (int i = 0; i < qmldirErrors.size(); ++i) {
582                 QQmlError error = qmldirErrors.at(i);
583                 error.setUrl(url);
584                 errors->append(error);
585             }
586         }
587         return false;
588     }
589
590     if (qmlImportTrace())
591         qDebug().nospace() << "QQmlImports(" << qPrintable(base) << "::importExtension: "
592                            << "loaded " << qmldirFilePath;
593
594     if (!qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(qmldirFilePath)) {
595         qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(qmldirFilePath);
596
597         QString qmldirPath = qmldirFilePath;
598
599         if (QQmlFile::isBundle(*url))
600             qmldirPath = QQmlFile::bundleFileName(*url, typeLoader->engine());
601
602         int slash = qmldirFilePath.lastIndexOf(QLatin1Char('/'));
603         if (slash > 0)
604             qmldirPath.truncate(slash);
605
606         foreach (const QQmlDirParser::Plugin &plugin, qmldirParser->plugins()) {
607             QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath,
608                                                                plugin.path, plugin.name);
609             if (!resolvedFilePath.isEmpty()) {
610                 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
611                     if (errors) {
612                         // XXX TODO: should we leave the import plugin error alone?
613                         // Here, we pop it off the top and coalesce it into this error's message.
614                         // The reason is that the lower level may add url and line/column numbering information.
615                         QQmlError poppedError = errors->takeFirst();
616                         QQmlError error;
617                         error.setDescription(tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
618                         error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
619                         errors->prepend(error);
620                     }
621                     return false;
622                 }
623             } else {
624                 if (errors) {
625                     QQmlError error;
626                     error.setDescription(tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
627                     error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
628                     errors->prepend(error);
629                 }
630                 return false;
631             }
632         }
633     }
634
635     if (components)
636         *components = qmldirParser->components();
637     if (scripts)
638         *scripts = qmldirParser->scripts();
639
640     return true;
641 }
642
643 QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
644 {
645     struct I { static bool greaterThan(const QString &s1, const QString &s2) {
646         return s1 > s2;
647     } };
648
649     QString dir = dir_arg;
650     if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
651         dir.chop(1);
652
653     QStringList paths = database->fileImportPath;
654     qSort(paths.begin(), paths.end(), I::greaterThan); // Ensure subdirs preceed their parents.
655
656     QString stableRelativePath = dir;
657     foreach(const QString &path, paths) {
658         if (dir.startsWith(path)) {
659             stableRelativePath = dir.mid(path.length()+1);
660             break;
661         }
662     }
663
664     stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
665
666     // remove optional versioning in dot notation from uri
667     int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
668     if (lastSlash >= 0) {
669         int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
670         if (versionDot >= 0)
671             stableRelativePath = stableRelativePath.left(versionDot);
672     }
673
674     stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
675
676     return stableRelativePath;
677 }
678
679 /*
680 Locates the qmldir file for \a uri version \a vmaj.vmin.  Returns true if found,
681 and fills in outQmldirFilePath and outQmldirUrl appropriately.  Otherwise returns
682 false.
683 */
684 bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin,
685                                       QQmlImportDatabase *database,
686                                       QString *outQmldirFilePath,
687                                       QString *outQmldirPathUrl)
688 {
689     Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
690
691     // Check cache first
692
693     QQmlImportDatabase::QmldirCache *cacheHead = 0;
694     {
695     QQmlImportDatabase::QmldirCache **cachePtr = database->qmldirCache.value(uri);
696     if (cachePtr) {
697         cacheHead = *cachePtr;
698         QQmlImportDatabase::QmldirCache *cache = cacheHead;
699         while (cache) {
700             if (cache->versionMajor == vmaj && cache->versionMinor == vmin) {
701                 *outQmldirFilePath = cache->qmldirFilePath;
702                 *outQmldirPathUrl = cache->qmldirPathUrl;
703                 return !cache->qmldirFilePath.isEmpty();
704             }
705             cache = cache->next;
706         }
707     }
708     }
709
710     static QLatin1Char Slash('/');
711     static QLatin1String Slash_qmldir("/qmldir");
712
713     QString url = uri;
714     url.replace(QLatin1Char('.'), Slash);
715
716     // step 0: search for extension with fully encoded version number (eg. MyModule.3.2)
717     // step 1: search for extension with encoded version major (eg. MyModule.3)
718     // step 2: search for extension without version number (eg. MyModule)
719     for (int step = 0; step <= 2; ++step) {
720         foreach (const QString &p, database->fileImportPath) {
721             QString dir = p + Slash + url;
722
723             QString qmldirFile = dir;
724             if (step == 0) qmldirFile += QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin);
725             else if (step == 1) qmldirFile += QString(QLatin1String(".%1")).arg(vmaj);
726             qmldirFile += Slash_qmldir;
727
728             QQmlTypeLoader &typeLoader = QQmlEnginePrivate::get(database->engine)->typeLoader;
729             QString absoluteFilePath = typeLoader.absoluteFilePath(qmldirFile);
730             if (!absoluteFilePath.isEmpty()) {
731                 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
732                 if (absolutePath.at(0) == QLatin1Char(':'))
733                     url = QLatin1String("qrc://") + absolutePath.mid(1);
734                 else
735                     url = QUrl::fromLocalFile(absolutePath).toString();
736
737                 QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
738                 cache->versionMajor = vmaj;
739                 cache->versionMinor = vmin;
740                 cache->qmldirFilePath = absoluteFilePath;
741                 cache->qmldirPathUrl = url;
742                 cache->next = cacheHead;
743                 database->qmldirCache.insert(uri, cache);
744
745                 *outQmldirFilePath = absoluteFilePath;
746                 *outQmldirPathUrl = url;
747
748                 return true;
749             }
750         }
751     }
752
753     QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
754     cache->versionMajor = vmaj;
755     cache->versionMinor = vmin;
756     cache->next = cacheHead;
757     database->qmldirCache.insert(uri, cache);
758
759     return false;
760 }
761
762 bool QQmlImportsPrivate::addImport(const QQmlDirComponents &qmldircomponentsnetwork,
763                                    const QString& importedUri, const QString& prefix,
764                                    int vmaj, int vmin, QQmlScript::Import::Type importType,
765                                    bool isImplicitImport, QQmlImportDatabase *database,
766                                    QString *outUrl, QList<QQmlError> *errors)
767 {
768     Q_ASSERT(errors);
769     Q_ASSERT(importType == QQmlScript::Import::File || importType == QQmlScript::Import::Library);
770
771     static QLatin1String String_qmldir("qmldir");
772     static QLatin1String Slash_qmldir("/qmldir");
773     static QLatin1Char Slash('/');
774
775     // The list of components defined by a qmldir file for this import.
776     QQmlDirComponents qmldircomponents;
777     // The list of scripts defined by a qmldir file for this import.
778     QQmlDirScripts qmldirscripts;
779     // The namespace that this import affects.
780     QQmlImportNamespace *importSet = 0;
781     // The uri for this import.  For library imports this is the same as importedUri
782     // specified by the user, but it may be different in the case of file imports.
783     QString uri;
784     // The url for the path containing files for this import.
785     QString url;
786
787     qmldircomponents = qmldircomponentsnetwork;
788     uri = importedUri;
789     if (prefix.isEmpty()) {
790         importSet = &unqualifiedset;
791     } else {
792         importSet = set.value(prefix);
793         if (!importSet) {
794             importSet = new QQmlImportNamespace;
795             set.insert(prefix, importSet);
796         }
797     }
798
799     if (importType == QQmlScript::Import::Library) {
800         Q_ASSERT(vmaj >= 0 && vmin >= 0);
801
802         QString qmldirFilePath;
803
804         if (locateQmldir(uri, vmaj, vmin, database, &qmldirFilePath, &url)) {
805
806             if (!importExtension(qmldirFilePath, uri, database, &qmldircomponents,
807                                  &qmldirscripts, &url, errors))
808                 return false;
809
810         }
811
812         if (!QQmlMetaType::isModule(uri, vmaj, vmin)) {
813
814             if (qmldircomponents.isEmpty() && qmldirscripts.isEmpty()) {
815                 QQmlError error;
816                 if (QQmlMetaType::isAnyModule(uri))
817                     error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(importedUri).arg(vmaj).arg(vmin));
818                 else
819                     error.setDescription(tr("module \"%1\" is not installed").arg(importedUri));
820                 errors->prepend(error);
821                 return false;
822             } else {
823                 int lowest_min = INT_MAX;
824                 int highest_min = INT_MIN;
825                 typedef QList<QQmlDirParser::Component>::const_iterator ConstIterator;
826                 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
827
828                 for (ConstIterator cit = qmldircomponents.constBegin();
829                      cit != qmldircomponents.constEnd(); ++cit) {
830                     if (cit->majorVersion == vmaj) {
831                         lowest_min = qMin(lowest_min, cit->minorVersion);
832                         highest_min = qMax(highest_min, cit->minorVersion);
833                     }
834                 }
835
836                 for (SConstIterator cit = qmldirscripts.constBegin();
837                      cit != qmldirscripts.constEnd(); ++cit) {
838                     if (cit->majorVersion == vmaj) {
839                         lowest_min = qMin(lowest_min, cit->minorVersion);
840                         highest_min = qMax(highest_min, cit->minorVersion);
841                     }
842                 }
843
844                 if (lowest_min > vmin || highest_min < vmin) {
845                     QQmlError error;
846                     error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(importedUri).arg(vmaj).arg(vmin));
847                     errors->prepend(error);
848                     return false;
849                 }
850             }
851
852         }
853     } else {
854
855         Q_ASSERT(importType == QQmlScript::Import::File);
856
857         if (qmldircomponents.isEmpty()) {
858
859             QString qmldirPath = uri;
860             if (uri.endsWith(Slash)) qmldirPath += String_qmldir;
861             else qmldirPath += Slash_qmldir;
862             QString qmldirUrl = resolveLocalUrl(base, qmldirPath);
863
864             if (QQmlFile::isBundle(qmldirUrl)) {
865
866                 QString dir = resolveLocalUrl(base, uri);
867                 Q_ASSERT(QQmlFile::isBundle(dir));
868                 if (!QQmlFile::bundleDirectoryExists(dir, typeLoader->engine())) {
869                     if (!isImplicitImport) {
870                         QQmlError error;
871                         error.setDescription(tr("\"%1\": no such directory").arg(importedUri));
872                         error.setUrl(QUrl(qmldirUrl));
873                         errors->prepend(error);
874                     }
875                     return false;
876                 }
877
878                 // Transforms the (possible relative) uri into our best guess relative to the
879                 // import paths.
880                 uri = resolvedUri(dir, database);
881
882                 if (uri.endsWith(Slash))
883                     uri.chop(1);
884                 if (QQmlFile::bundleFileExists(qmldirUrl, typeLoader->engine())) {
885                     if (!importExtension(qmldirUrl, uri, database, &qmldircomponents,
886                                          &qmldirscripts, &url, errors))
887                         return false;
888                 }
889
890             } else if (QQmlFile::isLocalFile(qmldirUrl)) {
891
892                 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
893                 Q_ASSERT(!localFileOrQrc.isEmpty());
894
895                 QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
896                 if (!typeLoader->directoryExists(dir)) {
897                     if (!isImplicitImport) {
898                         QQmlError error;
899                         error.setDescription(tr("\"%1\": no such directory").arg(importedUri));
900                         error.setUrl(QUrl(qmldirUrl));
901                         errors->prepend(error);
902                     }
903                     return false;
904                 }
905
906                 // Transforms the (possible relative) uri into our best guess relative to the
907                 // import paths.
908                 uri = resolvedUri(dir, database);
909
910                 if (uri.endsWith(Slash))
911                     uri.chop(1);
912                 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
913                     if (!importExtension(localFileOrQrc, uri, database, &qmldircomponents,
914                                          &qmldirscripts, &url, errors))
915                         return false;
916                 }
917
918             } else if (prefix.isEmpty()) {
919
920                 if (!isImplicitImport) {
921                     QQmlError error;
922                     error.setDescription(tr("import \"%1\" has no qmldir and no namespace").arg(uri));
923                     error.setUrl(QUrl(qmldirUrl));
924                     errors->prepend(error);
925                 }
926
927                 return false;
928
929             }
930         }
931
932         url = resolveLocalUrl(base, importedUri);
933         if (!url.endsWith(Slash))
934             url += Slash;
935     }
936
937     Q_ASSERT(url.isEmpty() || url.endsWith(Slash));
938
939     QMap<QString, QQmlDirParser::Script> scripts;
940     if (!qmldirscripts.isEmpty()) {
941         // Verify that we haven't imported these scripts already
942         for (QList<QQmlImportNamespace::Import>::const_iterator it = importSet->imports.constBegin();
943              it != importSet->imports.constEnd(); ++it) {
944             if (it->uri == uri) {
945                 QQmlError error;
946                 error.setDescription(tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg(it->url));
947                 errors->prepend(error);
948                 return false;
949             }
950         }
951
952         for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
953              sit != qmldirscripts.constEnd(); ++sit) {
954             // Only include scripts that match our requested version
955             if (((vmaj == -1) || (sit->majorVersion == vmaj)) &&
956                 ((vmin == -1) || (sit->minorVersion <= vmin))) {
957
958                 // Load the highest version that matches
959                 QMap<QString, QQmlDirParser::Script>::iterator it = scripts.find(sit->nameSpace);
960                 if (it == scripts.end() || (it->minorVersion < sit->minorVersion)) {
961                     scripts.insert(sit->nameSpace, *sit);
962                 }
963             }
964         }
965     }
966
967     QQmlImportNamespace::Import import;
968     import.uri = uri;
969     import.url = url;
970     import.majversion = vmaj;
971     import.minversion = vmin;
972     import.isLibrary = importType == QQmlScript::Import::Library;
973     import.qmlDirComponents = qmldircomponents;
974     import.qmlDirScripts = scripts.values();
975
976     importSet->imports.prepend(import);
977
978     if (outUrl) *outUrl = url;
979
980     return true;
981 }
982
983 /*!
984   \internal
985
986   Adds an implicit "." file import.  This is equivalent to calling addImport(), but error
987   messages related to the path or qmldir file not existing are suppressed.
988 */
989 bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb,
990                                     const QQmlDirComponents &qmldircomponentsnetwork,
991                                     QList<QQmlError> *errors)
992 {
993     Q_ASSERT(errors);
994
995     if (qmlImportTrace())
996         qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString())
997                            << ")::addImplicitImport";
998
999
1000     return d->addImport(qmldircomponentsnetwork, QLatin1String("."), QString(), -1, -1,
1001                         QQmlScript::Import::File, true, importDb, 0, errors);
1002 }
1003
1004 /*!
1005   \internal
1006
1007   Adds information to \a imports such that subsequent calls to resolveType()
1008   will resolve types qualified by \a prefix by considering types found at the given \a uri.
1009
1010   The uri is either a directory (if importType is FileImport), or a URI resolved using paths
1011   added via addImportPath() (if importType is LibraryImport).
1012
1013   The \a prefix may be empty, in which case the import location is considered for
1014   unqualified types.
1015
1016   The base URL must already have been set with Import::setBaseUrl().
1017
1018   Optionally, the url the import resolved to can be returned by providing the url parameter.
1019   Not all imports will result in an output url being generated, in which case the url will
1020   be set to an empty string.
1021
1022   Returns true on success, and false on failure.  In case of failure, the errors array will
1023   filled appropriately.
1024 */
1025 bool QQmlImports::addImport(QQmlImportDatabase *importDb,
1026                             const QString& uri, const QString& prefix, int vmaj, int vmin,
1027                             QQmlScript::Import::Type importType,
1028                             const QQmlDirComponents &qmldircomponentsnetwork,
1029                             QString *url, QList<QQmlError> *errors)
1030 {
1031     Q_ASSERT(errors);
1032
1033     if (qmlImportTrace())
1034         qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
1035                            << uri << " " << vmaj << '.' << vmin << " "
1036                            << (importType==QQmlScript::Import::Library? "Library" : "File")
1037                            << " as " << prefix;
1038
1039     return d->addImport(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, false,
1040                         importDb, url, errors);
1041 }
1042
1043 /*!
1044 \class QQmlImportDatabase
1045 \brief The QQmlImportDatabase class manages the QML imports for a QQmlEngine.
1046 \internal
1047 */
1048 QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
1049 : engine(e)
1050 {
1051     filePluginPath << QLatin1String(".");
1052
1053     // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
1054
1055 #ifndef QT_NO_SETTINGS
1056     QString installImportsPath =  QLibraryInfo::location(QLibraryInfo::ImportsPath);
1057     addImportPath(installImportsPath);
1058 #endif // QT_NO_SETTINGS
1059
1060     // env import paths
1061     QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
1062     if (!envImportPath.isEmpty()) {
1063 #if defined(Q_OS_WIN)
1064         QLatin1Char pathSep(';');
1065 #else
1066         QLatin1Char pathSep(':');
1067 #endif
1068         QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
1069         for (int ii = paths.count() - 1; ii >= 0; --ii)
1070             addImportPath(paths.at(ii));
1071     }
1072
1073     addImportPath(QCoreApplication::applicationDirPath());
1074 }
1075
1076 QQmlImportDatabase::~QQmlImportDatabase()
1077 {
1078     for (QStringHash<QmldirCache *>::ConstIterator iter = qmldirCache.begin();
1079          iter != qmldirCache.end(); ++iter) {
1080
1081         QmldirCache *c = *iter;
1082         while (c) {
1083             QmldirCache *n = c->next;
1084             delete c;
1085             c = n;
1086         }
1087     }
1088 }
1089
1090 /*!
1091   \internal
1092
1093   Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
1094   The \a prefix must contain the dot.
1095
1096   \a qmldirPath is the location of the qmldir file.
1097  */
1098 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1099                                           const QString &qmldirPath,
1100                                           const QString &qmldirPluginPath,
1101                                           const QString &baseName, const QStringList &suffixes,
1102                                           const QString &prefix)
1103 {
1104     QStringList searchPaths = filePluginPath;
1105     bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
1106     if (!qmldirPluginPathIsRelative)
1107         searchPaths.prepend(qmldirPluginPath);
1108
1109     foreach (const QString &pluginPath, searchPaths) {
1110
1111         QString resolvedPath;
1112         if (pluginPath == QLatin1String(".")) {
1113             if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
1114                 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath);
1115             else
1116                 resolvedPath = qmldirPath;
1117         } else {
1118             if (QDir::isRelativePath(pluginPath))
1119                 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath);
1120             else
1121                 resolvedPath = pluginPath;
1122         }
1123
1124         // hack for resources, should probably go away
1125         if (resolvedPath.startsWith(QLatin1Char(':')))
1126             resolvedPath = QCoreApplication::applicationDirPath();
1127
1128         if (!resolvedPath.endsWith(QLatin1Char('/')))
1129             resolvedPath += QLatin1Char('/');
1130
1131         foreach (const QString &suffix, suffixes) {
1132             QString pluginFileName = prefix;
1133
1134             pluginFileName += baseName;
1135             pluginFileName += suffix;
1136
1137             QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
1138             if (!absolutePath.isEmpty())
1139                 return absolutePath;
1140         }
1141     }
1142
1143     if (qmlImportTrace())
1144         qDebug() << "QQmlImportDatabase::resolvePlugin: Could not resolve plugin" << baseName 
1145                  << "in" << qmldirPath;
1146
1147     return QString();
1148 }
1149
1150 /*!
1151   \internal
1152
1153   Returns the result of the merge of \a baseName with \a dir and the platform suffix.
1154
1155   \table
1156   \header \li Platform \li Valid suffixes
1157   \row \li Windows     \li \c .dll
1158   \row \li Unix/Linux  \li \c .so
1159   \row \li AIX  \li \c .a
1160   \row \li HP-UX       \li \c .sl, \c .so (HP-UXi)
1161   \row \li Mac OS X    \li \c .dylib, \c .bundle, \c .so
1162   \endtable
1163
1164   Version number on unix are ignored.
1165 */
1166 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1167                                                   const QString &qmldirPath, const QString &qmldirPluginPath,
1168                                                   const QString &baseName)
1169 {
1170 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
1171     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1172                          QStringList()
1173 # ifdef QT_DEBUG
1174                          << QLatin1String("d.dll") // try a qmake-style debug build first
1175 # endif
1176                          << QLatin1String(".dll"));
1177 #else
1178
1179 # if defined(Q_OS_DARWIN)
1180
1181     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1182                          QStringList()
1183 # ifdef QT_DEBUG
1184                          << QLatin1String("_debug.dylib") // try a qmake-style debug build first
1185                          << QLatin1String(".dylib")
1186 # else
1187                          << QLatin1String(".dylib")
1188                          << QLatin1String("_debug.dylib") // try a qmake-style debug build after
1189 # endif
1190                          << QLatin1String(".so")
1191                          << QLatin1String(".bundle"),
1192                          QLatin1String("lib"));
1193 # else  // Generic Unix
1194     QStringList validSuffixList;
1195
1196 #  if defined(Q_OS_HPUX)
1197 /*
1198     See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
1199     "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
1200     the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
1201  */
1202     validSuffixList << QLatin1String(".sl");
1203 #   if defined __ia64
1204     validSuffixList << QLatin1String(".so");
1205 #   endif
1206 #  elif defined(Q_OS_AIX)
1207     validSuffixList << QLatin1String(".a") << QLatin1String(".so");
1208 #  elif defined(Q_OS_UNIX)
1209     validSuffixList << QLatin1String(".so");
1210 #  endif
1211
1212     // Examples of valid library names:
1213     //  libfoo.so
1214
1215     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
1216 # endif
1217
1218 #endif
1219 }
1220
1221 /*!
1222     \internal
1223 */
1224 QStringList QQmlImportDatabase::pluginPathList() const
1225 {
1226     return filePluginPath;
1227 }
1228
1229 /*!
1230     \internal
1231 */
1232 void QQmlImportDatabase::setPluginPathList(const QStringList &paths)
1233 {
1234     filePluginPath = paths;
1235 }
1236
1237 /*!
1238     \internal
1239 */
1240 void QQmlImportDatabase::addPluginPath(const QString& path)
1241 {
1242     if (qmlImportTrace())
1243         qDebug().nospace() << "QQmlImportDatabase::addPluginPath: " << path;
1244
1245     QUrl url = QUrl(path);
1246     if (url.isRelative() || url.scheme() == QLatin1String("file")
1247             || (url.scheme().length() == 1 && QFile::exists(path)) ) {  // windows path
1248         QDir dir = QDir(path);
1249         filePluginPath.prepend(dir.canonicalPath());
1250     } else {
1251         filePluginPath.prepend(path);
1252     }
1253 }
1254
1255 /*!
1256     \internal
1257 */
1258 void QQmlImportDatabase::addImportPath(const QString& path)
1259 {
1260     if (qmlImportTrace())
1261         qDebug().nospace() << "QQmlImportDatabase::addImportPath: " << path;
1262
1263     if (path.isEmpty())
1264         return;
1265
1266     QUrl url = QUrl(path);
1267     QString cPath;
1268
1269     if (url.isRelative() || url.scheme() == QLatin1String("file")
1270             || (url.scheme().length() == 1 && QFile::exists(path)) ) {  // windows path
1271         QDir dir = QDir(path);
1272         cPath = dir.canonicalPath();
1273     } else {
1274         cPath = path;
1275         cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1276     }
1277
1278     if (!cPath.isEmpty()
1279         && !fileImportPath.contains(cPath))
1280         fileImportPath.prepend(cPath);
1281 }
1282
1283 /*!
1284     \internal
1285 */
1286 QStringList QQmlImportDatabase::importPathList() const
1287 {
1288     return fileImportPath;
1289 }
1290
1291 /*!
1292     \internal
1293 */
1294 void QQmlImportDatabase::setImportPathList(const QStringList &paths)
1295 {
1296     fileImportPath = paths;
1297 }
1298
1299 /*!
1300     \internal
1301 */
1302 bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)
1303 {
1304     if (qmlImportTrace())
1305         qDebug().nospace() << "QQmlImportDatabase::importPlugin: " << uri << " from " << filePath;
1306
1307 #ifndef QT_NO_LIBRARY
1308     QFileInfo fileInfo(filePath);
1309     const QString absoluteFilePath = fileInfo.absoluteFilePath();
1310
1311     bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1312     bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1313
1314     if (typesRegistered) {
1315         Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1316                    "QQmlImportDatabase::importExtension",
1317                    "Internal error: Plugin imported previously with different uri");
1318     }
1319
1320     if (!engineInitialized || !typesRegistered) {
1321         if (!QQml_isFileCaseCorrect(absoluteFilePath)) {
1322             if (errors) {
1323                 QQmlError error;
1324                 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1325                 errors->prepend(error);
1326             }
1327             return false;
1328         }
1329         QPluginLoader loader(absoluteFilePath);
1330
1331         if (!loader.load()) {
1332             if (errors) {
1333                 QQmlError error;
1334                 error.setDescription(loader.errorString());
1335                 errors->prepend(error);
1336             }
1337             return false;
1338         }
1339
1340         QObject *instance = loader.instance();
1341         if (QQmlTypesExtensionInterface *iface = qobject_cast<QQmlExtensionInterface *>(instance)) {
1342
1343             const QByteArray bytes = uri.toUtf8();
1344             const char *moduleId = bytes.constData();
1345             if (!typesRegistered) {
1346
1347                 // XXX thread this code should probably be protected with a mutex.
1348                 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1349                 iface->registerTypes(moduleId);
1350             }
1351             if (!engineInitialized) {
1352                 // things on the engine (eg. adding new global objects) have to be done for every 
1353                 // engine.  
1354                 // XXX protect against double initialization
1355                 initializedPlugins.insert(absoluteFilePath);
1356
1357                 QQmlExtensionInterface *eiface = 
1358                     qobject_cast<QQmlExtensionInterface *>(instance);
1359                 if (eiface) {
1360                     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1361                     ep->typeLoader.initializeEngine(eiface, moduleId);
1362                 }
1363             }
1364         } else {
1365             if (errors) {
1366                 QQmlError error;
1367                 error.setDescription(loader.errorString());
1368                 errors->prepend(error);
1369             }
1370             return false;
1371         }
1372     }
1373
1374     return true;
1375 #else
1376     return false;
1377 #endif
1378 }
1379
1380 QT_END_NAMESPACE