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