Report clashing qmldir entries for components and scripts
[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.constEnd();
856                 for (ConstIterator cit = qmldircomponents.constBegin(); cit != cend; ++cit) {
857                     for (ConstIterator cit2 = qmldircomponents.constBegin(); cit2 != cit; ++cit2) {
858                         if ((cit2->typeName == cit->typeName) &&
859                             (cit2->majorVersion == cit->majorVersion) &&
860                             (cit2->minorVersion == cit->minorVersion)) {
861                             // This is entry clashes with a predecessor
862                             QQmlError error;
863                             error.setDescription(tr("\"%1\" version %2.%3 is already defined in module \"%4\"")
864                                                  .arg(cit->typeName).arg(cit->majorVersion).arg(cit->minorVersion).arg(importedUri));
865                             errors->prepend(error);
866                             return false;
867                         }
868                     }
869
870                     if (cit->majorVersion == vmaj) {
871                         lowest_min = qMin(lowest_min, cit->minorVersion);
872                         highest_min = qMax(highest_min, cit->minorVersion);
873                     }
874                 }
875
876                 SConstIterator send = qmldirscripts.constEnd();
877                 for (SConstIterator sit = qmldirscripts.constBegin(); sit != send; ++sit) {
878                     for (SConstIterator sit2 = qmldirscripts.constBegin(); sit2 != sit; ++sit2) {
879                         if ((sit2->nameSpace == sit->nameSpace) &&
880                             (sit2->majorVersion == sit->majorVersion) &&
881                             (sit2->minorVersion == sit->minorVersion)) {
882                             // This is entry clashes with a predecessor
883                             QQmlError error;
884                             error.setDescription(tr("\"%1\" version %2.%3 is already defined in module \"%4\"")
885                                                  .arg(sit->nameSpace).arg(sit->majorVersion).arg(sit->minorVersion).arg(importedUri));
886                             errors->prepend(error);
887                             return false;
888                         }
889                     }
890
891                     if (sit->majorVersion == vmaj) {
892                         lowest_min = qMin(lowest_min, sit->minorVersion);
893                         highest_min = qMax(highest_min, sit->minorVersion);
894                     }
895                 }
896
897                 if (lowest_min > vmin || highest_min < vmin) {
898                     QQmlError error;
899                     error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(importedUri).arg(vmaj).arg(vmin));
900                     errors->prepend(error);
901                     return false;
902                 }
903             }
904
905         }
906     } else {
907
908         Q_ASSERT(importType == QQmlScript::Import::File);
909
910         if (qmldircomponents.isEmpty()) {
911
912             QString qmldirPath = uri;
913             if (uri.endsWith(Slash)) qmldirPath += String_qmldir;
914             else qmldirPath += Slash_qmldir;
915             QString qmldirUrl = resolveLocalUrl(base, qmldirPath);
916
917             if (QQmlFile::isBundle(qmldirUrl)) {
918
919                 QString dir = resolveLocalUrl(base, uri);
920                 Q_ASSERT(QQmlFile::isBundle(dir));
921                 if (!QQmlFile::bundleDirectoryExists(dir, typeLoader->engine())) {
922                     if (!isImplicitImport) {
923                         QQmlError error;
924                         error.setDescription(tr("\"%1\": no such directory").arg(importedUri));
925                         error.setUrl(QUrl(qmldirUrl));
926                         errors->prepend(error);
927                     }
928                     return false;
929                 }
930
931                 // Transforms the (possible relative) uri into our best guess relative to the
932                 // import paths.
933                 uri = resolvedUri(dir, database);
934
935                 if (uri.endsWith(Slash))
936                     uri.chop(1);
937                 if (QQmlFile::bundleFileExists(qmldirUrl, typeLoader->engine())) {
938                     if (!importExtension(qmldirUrl, uri, database, &qmldircomponents,
939                                          &qmldirscripts, &url, errors))
940                         return false;
941                 }
942
943             } else if (QQmlFile::isLocalFile(qmldirUrl)) {
944
945                 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
946                 Q_ASSERT(!localFileOrQrc.isEmpty());
947
948                 QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
949                 if (!typeLoader->directoryExists(dir)) {
950                     if (!isImplicitImport) {
951                         QQmlError error;
952                         error.setDescription(tr("\"%1\": no such directory").arg(importedUri));
953                         error.setUrl(QUrl(qmldirUrl));
954                         errors->prepend(error);
955                     }
956                     return false;
957                 }
958
959                 // Transforms the (possible relative) uri into our best guess relative to the
960                 // import paths.
961                 uri = resolvedUri(dir, database);
962
963                 if (uri.endsWith(Slash))
964                     uri.chop(1);
965                 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
966                     if (!importExtension(localFileOrQrc, uri, database, &qmldircomponents,
967                                          &qmldirscripts, &url, errors))
968                         return false;
969                 }
970
971             } else if (prefix.isEmpty()) {
972
973                 if (!isImplicitImport) {
974                     QQmlError error;
975                     error.setDescription(tr("import \"%1\" has no qmldir and no namespace").arg(uri));
976                     error.setUrl(QUrl(qmldirUrl));
977                     errors->prepend(error);
978                 }
979
980                 return false;
981
982             }
983         }
984
985         url = resolveLocalUrl(base, importedUri);
986         if (!url.endsWith(Slash))
987             url += Slash;
988     }
989
990     Q_ASSERT(url.isEmpty() || url.endsWith(Slash));
991
992     QMap<QString, QQmlDirParser::Script> scripts;
993     if (!qmldirscripts.isEmpty()) {
994         // Verify that we haven't imported these scripts already
995         for (QList<QQmlImportNamespace::Import>::const_iterator it = importSet->imports.constBegin();
996              it != importSet->imports.constEnd(); ++it) {
997             if (it->uri == uri) {
998                 QQmlError error;
999                 error.setDescription(tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg(it->url));
1000                 errors->prepend(error);
1001                 return false;
1002             }
1003         }
1004
1005         for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
1006              sit != qmldirscripts.constEnd(); ++sit) {
1007             // Only include scripts that match our requested version
1008             if (((vmaj == -1) || (sit->majorVersion == vmaj)) &&
1009                 ((vmin == -1) || (sit->minorVersion <= vmin))) {
1010
1011                 // Load the highest version that matches
1012                 QMap<QString, QQmlDirParser::Script>::iterator it = scripts.find(sit->nameSpace);
1013                 if (it == scripts.end() || (it->minorVersion < sit->minorVersion)) {
1014                     scripts.insert(sit->nameSpace, *sit);
1015                 }
1016             }
1017         }
1018     }
1019
1020     QQmlImportNamespace::Import import;
1021     import.uri = uri;
1022     import.url = url;
1023     import.majversion = vmaj;
1024     import.minversion = vmin;
1025     import.isLibrary = importType == QQmlScript::Import::Library;
1026     import.qmlDirComponents = qmldircomponents;
1027     import.qmlDirScripts = scripts.values();
1028
1029     importSet->imports.prepend(import);
1030
1031     if (outUrl) *outUrl = url;
1032
1033     return true;
1034 }
1035
1036 /*!
1037   \internal
1038
1039   Adds an implicit "." file import.  This is equivalent to calling addImport(), but error
1040   messages related to the path or qmldir file not existing are suppressed.
1041 */
1042 bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb,
1043                                     const QQmlDirComponents &qmldircomponentsnetwork,
1044                                     QList<QQmlError> *errors)
1045 {
1046     Q_ASSERT(errors);
1047
1048     if (qmlImportTrace())
1049         qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString())
1050                            << ")::addImplicitImport";
1051
1052
1053     return d->addImport(qmldircomponentsnetwork, QLatin1String("."), QString(), -1, -1,
1054                         QQmlScript::Import::File, true, importDb, 0, errors);
1055 }
1056
1057 /*!
1058   \internal
1059
1060   Adds information to \a imports such that subsequent calls to resolveType()
1061   will resolve types qualified by \a prefix by considering types found at the given \a uri.
1062
1063   The uri is either a directory (if importType is FileImport), or a URI resolved using paths
1064   added via addImportPath() (if importType is LibraryImport).
1065
1066   The \a prefix may be empty, in which case the import location is considered for
1067   unqualified types.
1068
1069   The base URL must already have been set with Import::setBaseUrl().
1070
1071   Optionally, the url the import resolved to can be returned by providing the url parameter.
1072   Not all imports will result in an output url being generated, in which case the url will
1073   be set to an empty string.
1074
1075   Returns true on success, and false on failure.  In case of failure, the errors array will
1076   filled appropriately.
1077 */
1078 bool QQmlImports::addImport(QQmlImportDatabase *importDb,
1079                             const QString& uri, const QString& prefix, int vmaj, int vmin,
1080                             QQmlScript::Import::Type importType,
1081                             const QQmlDirComponents &qmldircomponentsnetwork,
1082                             QString *url, QList<QQmlError> *errors)
1083 {
1084     Q_ASSERT(errors);
1085
1086     if (qmlImportTrace())
1087         qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::addImport: "
1088                            << uri << ' ' << vmaj << '.' << vmin << ' '
1089                            << (importType==QQmlScript::Import::Library? "Library" : "File")
1090                            << " as " << prefix;
1091
1092     return d->addImport(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, false,
1093                         importDb, url, errors);
1094 }
1095
1096 /*!
1097 \class QQmlImportDatabase
1098 \brief The QQmlImportDatabase class manages the QML imports for a QQmlEngine.
1099 \internal
1100 */
1101 QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
1102 : engine(e)
1103 {
1104     filePluginPath << QLatin1String(".");
1105
1106     // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
1107
1108 #ifndef QT_NO_SETTINGS
1109     QString installImportsPath =  QLibraryInfo::location(QLibraryInfo::ImportsPath);
1110     addImportPath(installImportsPath);
1111 #endif // QT_NO_SETTINGS
1112
1113     // env import paths
1114     QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
1115     if (!envImportPath.isEmpty()) {
1116 #if defined(Q_OS_WIN)
1117         QLatin1Char pathSep(';');
1118 #else
1119         QLatin1Char pathSep(':');
1120 #endif
1121         QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
1122         for (int ii = paths.count() - 1; ii >= 0; --ii)
1123             addImportPath(paths.at(ii));
1124     }
1125
1126     addImportPath(QCoreApplication::applicationDirPath());
1127 }
1128
1129 QQmlImportDatabase::~QQmlImportDatabase()
1130 {
1131     for (QStringHash<QmldirCache *>::ConstIterator iter = qmldirCache.begin();
1132          iter != qmldirCache.end(); ++iter) {
1133
1134         QmldirCache *c = *iter;
1135         while (c) {
1136             QmldirCache *n = c->next;
1137             delete c;
1138             c = n;
1139         }
1140     }
1141 }
1142
1143 /*!
1144   \internal
1145
1146   Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
1147   The \a prefix must contain the dot.
1148
1149   \a qmldirPath is the location of the qmldir file.
1150  */
1151 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1152                                           const QString &qmldirPath,
1153                                           const QString &qmldirPluginPath,
1154                                           const QString &baseName, const QStringList &suffixes,
1155                                           const QString &prefix)
1156 {
1157     QStringList searchPaths = filePluginPath;
1158     bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
1159     if (!qmldirPluginPathIsRelative)
1160         searchPaths.prepend(qmldirPluginPath);
1161
1162     foreach (const QString &pluginPath, searchPaths) {
1163
1164         QString resolvedPath;
1165         if (pluginPath == QLatin1String(".")) {
1166             if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
1167                 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath);
1168             else
1169                 resolvedPath = qmldirPath;
1170         } else {
1171             if (QDir::isRelativePath(pluginPath))
1172                 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath);
1173             else
1174                 resolvedPath = pluginPath;
1175         }
1176
1177         // hack for resources, should probably go away
1178         if (resolvedPath.startsWith(QLatin1Char(':')))
1179             resolvedPath = QCoreApplication::applicationDirPath();
1180
1181         if (!resolvedPath.endsWith(QLatin1Char('/')))
1182             resolvedPath += QLatin1Char('/');
1183
1184         foreach (const QString &suffix, suffixes) {
1185             QString pluginFileName = prefix;
1186
1187             pluginFileName += baseName;
1188             pluginFileName += suffix;
1189
1190             QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
1191             if (!absolutePath.isEmpty())
1192                 return absolutePath;
1193         }
1194     }
1195
1196     if (qmlImportTrace())
1197         qDebug() << "QQmlImportDatabase::resolvePlugin: Could not resolve plugin" << baseName 
1198                  << "in" << qmldirPath;
1199
1200     return QString();
1201 }
1202
1203 /*!
1204   \internal
1205
1206   Returns the result of the merge of \a baseName with \a dir and the platform suffix.
1207
1208   \table
1209   \header \li Platform \li Valid suffixes
1210   \row \li Windows     \li \c .dll
1211   \row \li Unix/Linux  \li \c .so
1212   \row \li AIX  \li \c .a
1213   \row \li HP-UX       \li \c .sl, \c .so (HP-UXi)
1214   \row \li Mac OS X    \li \c .dylib, \c .bundle, \c .so
1215   \endtable
1216
1217   Version number on unix are ignored.
1218 */
1219 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1220                                                   const QString &qmldirPath, const QString &qmldirPluginPath,
1221                                                   const QString &baseName)
1222 {
1223 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
1224     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1225                          QStringList()
1226 # ifdef QT_DEBUG
1227                          << QLatin1String("d.dll") // try a qmake-style debug build first
1228 # endif
1229                          << QLatin1String(".dll"));
1230 #else
1231
1232 # if defined(Q_OS_DARWIN)
1233
1234     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1235                          QStringList()
1236 # ifdef QT_DEBUG
1237                          << QLatin1String("_debug.dylib") // try a qmake-style debug build first
1238                          << QLatin1String(".dylib")
1239 # else
1240                          << QLatin1String(".dylib")
1241                          << QLatin1String("_debug.dylib") // try a qmake-style debug build after
1242 # endif
1243                          << QLatin1String(".so")
1244                          << QLatin1String(".bundle"),
1245                          QLatin1String("lib"));
1246 # else  // Generic Unix
1247     QStringList validSuffixList;
1248
1249 #  if defined(Q_OS_HPUX)
1250 /*
1251     See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
1252     "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
1253     the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
1254  */
1255     validSuffixList << QLatin1String(".sl");
1256 #   if defined __ia64
1257     validSuffixList << QLatin1String(".so");
1258 #   endif
1259 #  elif defined(Q_OS_AIX)
1260     validSuffixList << QLatin1String(".a") << QLatin1String(".so");
1261 #  elif defined(Q_OS_UNIX)
1262     validSuffixList << QLatin1String(".so");
1263 #  endif
1264
1265     // Examples of valid library names:
1266     //  libfoo.so
1267
1268     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
1269 # endif
1270
1271 #endif
1272 }
1273
1274 /*!
1275     \internal
1276 */
1277 QStringList QQmlImportDatabase::pluginPathList() const
1278 {
1279     return filePluginPath;
1280 }
1281
1282 /*!
1283     \internal
1284 */
1285 void QQmlImportDatabase::setPluginPathList(const QStringList &paths)
1286 {
1287     if (qmlImportTrace())
1288         qDebug().nospace() << "QQmlImportDatabase::setPluginPathList: " << paths;
1289
1290     filePluginPath = paths;
1291 }
1292
1293 /*!
1294     \internal
1295 */
1296 void QQmlImportDatabase::addPluginPath(const QString& path)
1297 {
1298     if (qmlImportTrace())
1299         qDebug().nospace() << "QQmlImportDatabase::addPluginPath: " << path;
1300
1301     QUrl url = QUrl(path);
1302     if (url.isRelative() || url.scheme() == QLatin1String("file")
1303             || (url.scheme().length() == 1 && QFile::exists(path)) ) {  // windows path
1304         QDir dir = QDir(path);
1305         filePluginPath.prepend(dir.canonicalPath());
1306     } else {
1307         filePluginPath.prepend(path);
1308     }
1309 }
1310
1311 /*!
1312     \internal
1313 */
1314 void QQmlImportDatabase::addImportPath(const QString& path)
1315 {
1316     if (qmlImportTrace())
1317         qDebug().nospace() << "QQmlImportDatabase::addImportPath: " << path;
1318
1319     if (path.isEmpty())
1320         return;
1321
1322     QUrl url = QUrl(path);
1323     QString cPath;
1324
1325     if (url.isRelative() || url.scheme() == QLatin1String("file")
1326             || (url.scheme().length() == 1 && QFile::exists(path)) ) {  // windows path
1327         QDir dir = QDir(path);
1328         cPath = dir.canonicalPath();
1329     } else {
1330         cPath = path;
1331         cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1332     }
1333
1334     if (!cPath.isEmpty()
1335         && !fileImportPath.contains(cPath))
1336         fileImportPath.prepend(cPath);
1337 }
1338
1339 /*!
1340     \internal
1341 */
1342 QStringList QQmlImportDatabase::importPathList() const
1343 {
1344     return fileImportPath;
1345 }
1346
1347 /*!
1348     \internal
1349 */
1350 void QQmlImportDatabase::setImportPathList(const QStringList &paths)
1351 {
1352     if (qmlImportTrace())
1353         qDebug().nospace() << "QQmlImportDatabase::setImportPathList: " << paths;
1354
1355     fileImportPath = paths;
1356 }
1357
1358 /*!
1359     \internal
1360 */
1361 bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)
1362 {
1363     if (qmlImportTrace())
1364         qDebug().nospace() << "QQmlImportDatabase::importPlugin: " << uri << " from " << filePath;
1365
1366 #ifndef QT_NO_LIBRARY
1367     QFileInfo fileInfo(filePath);
1368     const QString absoluteFilePath = fileInfo.absoluteFilePath();
1369
1370     bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1371     bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1372
1373     if (typesRegistered) {
1374         Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1375                    "QQmlImportDatabase::importExtension",
1376                    "Internal error: Plugin imported previously with different uri");
1377     }
1378
1379     if (!engineInitialized || !typesRegistered) {
1380         if (!QQml_isFileCaseCorrect(absoluteFilePath)) {
1381             if (errors) {
1382                 QQmlError error;
1383                 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1384                 errors->prepend(error);
1385             }
1386             return false;
1387         }
1388         QPluginLoader loader(absoluteFilePath);
1389
1390         if (!loader.load()) {
1391             if (errors) {
1392                 QQmlError error;
1393                 error.setDescription(loader.errorString());
1394                 errors->prepend(error);
1395             }
1396             return false;
1397         }
1398
1399         QObject *instance = loader.instance();
1400         if (QQmlTypesExtensionInterface *iface = qobject_cast<QQmlExtensionInterface *>(instance)) {
1401
1402             const QByteArray bytes = uri.toUtf8();
1403             const char *moduleId = bytes.constData();
1404             if (!typesRegistered) {
1405
1406                 // XXX thread this code should probably be protected with a mutex.
1407                 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1408                 iface->registerTypes(moduleId);
1409             }
1410             if (!engineInitialized) {
1411                 // things on the engine (eg. adding new global objects) have to be done for every 
1412                 // engine.  
1413                 // XXX protect against double initialization
1414                 initializedPlugins.insert(absoluteFilePath);
1415
1416                 QQmlExtensionInterface *eiface = 
1417                     qobject_cast<QQmlExtensionInterface *>(instance);
1418                 if (eiface) {
1419                     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1420                     ep->typeLoader.initializeEngine(eiface, moduleId);
1421                 }
1422             }
1423         } else {
1424             if (errors) {
1425                 QQmlError error;
1426                 error.setDescription(loader.errorString());
1427                 errors->prepend(error);
1428             }
1429             return false;
1430         }
1431     }
1432
1433     return true;
1434 #else
1435     return false;
1436 #endif
1437 }
1438
1439 QT_END_NAMESPACE