Remove Q_WS_*, symbian and maemo code in QtDeclarative
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativeimport.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativeimport_p.h"
43
44 #include <QtCore/qdebug.h>
45 #include <QtCore/qdir.h>
46 #include <QtCore/qfileinfo.h>
47 #include <QtCore/qpluginloader.h>
48 #include <QtCore/qlibraryinfo.h>
49 #include <QtDeclarative/qdeclarativeextensioninterface.h>
50 #include <private/qdeclarativeglobal_p.h>
51 #include <private/qdeclarativetypenamecache_p.h>
52 #include <private/qdeclarativeengine_p.h>
53
54 QT_BEGIN_NAMESPACE
55
56 DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
57 DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
58
59 static bool greaterThan(const QString &s1, const QString &s2)
60 {
61     return s1 > s2;
62 }
63
64 QString resolveLocalUrl(const QString &url, const QString &relative)
65 {
66     if (relative.contains(QLatin1Char(':'))) {
67         // contains a host name
68         return QUrl(url).resolved(QUrl(relative)).toString();
69     } else if (relative.isEmpty()) {
70         return url;
71     } else if (relative.at(0) == QLatin1Char('/') || !url.contains(QLatin1Char('/'))) {
72         return relative;
73     } else {
74         if (relative == QLatin1String("."))
75             return url.left(url.lastIndexOf(QLatin1Char('/')) + 1);
76         else if (relative.startsWith(QLatin1String("./")))
77             return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative.mid(2);
78         return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative;
79     }
80 }
81
82
83
84 typedef QMap<QString, QString> StringStringMap;
85 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
86
87 class QDeclarativeImportedNamespace 
88 {
89 public:
90     struct Data {
91         QString uri;
92         QString url;
93         int majversion;
94         int minversion;
95         bool isLibrary;
96         QDeclarativeDirComponents qmlDirComponents;
97     };
98     QList<Data> imports;
99
100
101     bool find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
102                                  QDeclarativeType** type_return, QString* url_return,
103                                  QString *base = 0, bool *typeRecursionDetected = 0);
104     bool find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
105               QString* url_return, QString *base = 0, QList<QDeclarativeError> *errors = 0);
106 };
107
108 class QDeclarativeImportsPrivate {
109 public:
110     QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader);
111     ~QDeclarativeImportsPrivate();
112
113     bool importExtension(const QString &absoluteFilePath, const QString &uri, 
114                          QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components, 
115                          QList<QDeclarativeError> *errors);
116
117     QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database);
118     bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork, 
119              const QString& uri_arg, const QString& prefix, 
120              int vmaj, int vmin, QDeclarativeScript::Import::Type importType, 
121              QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors);
122     bool find(const QString& type, int *vmajor, int *vminor,
123               QDeclarativeType** type_return, QString* url_return, QList<QDeclarativeError> *errors);
124
125     QDeclarativeImportedNamespace *findNamespace(const QString& type);
126
127     QUrl baseUrl;
128     QString base;
129     int ref;
130
131     QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
132     QDeclarativeImportedNamespace unqualifiedset;
133     QHash<QString,QDeclarativeImportedNamespace* > set;
134     QDeclarativeTypeLoader *typeLoader;
135 };
136
137 /*!
138 \class QDeclarativeImports
139 \brief The QDeclarativeImports class encapsulates one QML document's import statements.
140 \internal
141 */
142 QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports &copy) 
143 : d(copy.d)
144 {
145     ++d->ref;
146 }
147
148 QDeclarativeImports &
149 QDeclarativeImports::operator =(const QDeclarativeImports &copy)
150 {
151     ++copy.d->ref;
152     if (--d->ref == 0)
153         delete d;
154     d = copy.d;
155     return *this;
156 }
157
158 QDeclarativeImports::QDeclarativeImports(QDeclarativeTypeLoader *typeLoader)
159     : d(new QDeclarativeImportsPrivate(typeLoader))
160 {
161 }
162
163 QDeclarativeImports::~QDeclarativeImports()
164 {
165     if (--d->ref == 0)
166         delete d;
167 }
168
169 /*!
170   Sets the base URL to be used for all relative file imports added.
171 */
172 void QDeclarativeImports::setBaseUrl(const QUrl& url)
173 {
174     d->baseUrl = url;
175     d->base = url.toString();
176 }
177
178 /*!
179   Returns the base URL to be used for all relative file imports added.
180 */
181 QUrl QDeclarativeImports::baseUrl() const
182 {
183     return d->baseUrl;
184 }
185
186 void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const
187 {
188     const QDeclarativeImportedNamespace &set = d->unqualifiedset;
189
190     for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
191         const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
192         QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
193         if (module)
194             cache->m_anonymousImports.append(QDeclarativeTypeModuleVersion(module, data.minversion));
195     }
196
197     for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin();
198          iter != d->set.end(); 
199          ++iter) {
200
201         const QDeclarativeImportedNamespace &set = *iter.value();
202         QDeclarativeTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
203         for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
204             const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
205             QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
206             if (module)
207                 import.modules.append(QDeclarativeTypeModuleVersion(module, data.minversion));
208
209             QDeclarativeMetaType::ModuleApi moduleApi = QDeclarativeMetaType::moduleApi(data.uri, data.majversion, data.minversion);
210             if (moduleApi.script || moduleApi.qobject) {
211                 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
212                 import.moduleApi = ep->moduleApiInstance(moduleApi);
213             }
214         }
215     }
216 }
217
218 /*!
219   \internal
220
221   The given (namespace qualified) \a type is resolved to either
222   \list
223   \o a QDeclarativeImportedNamespace stored at \a ns_return,
224   \o a QDeclarativeType stored at \a type_return, or
225   \o a component located at \a url_return.
226   \endlist
227
228   If any return pointer is 0, the corresponding search is not done.
229
230   \sa addImport()
231 */
232 bool QDeclarativeImports::resolveType(const QString& type,
233                                       QDeclarativeType** type_return, QString* url_return, int *vmaj, int *vmin,
234                                       QDeclarativeImportedNamespace** ns_return, QList<QDeclarativeError> *errors) const
235 {
236     QDeclarativeImportedNamespace* ns = d->findNamespace(type);
237     if (ns) {
238         if (ns_return)
239             *ns_return = ns;
240         return true;
241     }
242     if (type_return || url_return) {
243         if (d->find(type,vmaj,vmin,type_return,url_return, errors)) {
244             if (qmlImportTrace()) {
245                 if (type_return && *type_return && url_return && !url_return->isEmpty())
246                     qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: " 
247                                        << type << " => " << (*type_return)->typeName() << " " << *url_return;
248                 if (type_return && *type_return)
249                     qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: " 
250                                        << type << " => " << (*type_return)->typeName();
251                 if (url_return && !url_return->isEmpty())
252                     qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: " 
253                                        << type << " => " << *url_return;
254             }
255             return true;
256         }
257     }
258     return false;
259 }
260
261 /*!
262   \internal
263
264   Searching \e only in the namespace \a ns (previously returned in a call to
265   resolveType(), \a type is found and returned to either
266   a QDeclarativeType stored at \a type_return, or
267   a component located at \a url_return.
268
269   If either return pointer is 0, the corresponding search is not done.
270 */
271 bool QDeclarativeImports::resolveType(QDeclarativeImportedNamespace* ns, const QString& type,
272                                       QDeclarativeType** type_return, QString* url_return,
273                                       int *vmaj, int *vmin) const
274 {
275     return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return);
276 }
277
278 bool QDeclarativeImportedNamespace::find_helper(QDeclarativeTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
279                                  QDeclarativeType** type_return, QString* url_return,
280                                  QString *base, bool *typeRecursionDetected)
281 {
282     int vmaj = data.majversion;
283     int vmin = data.minversion;
284
285     if (vmaj >= 0 && vmin >= 0) {
286         QString qt = data.uri + QLatin1Char('/') + type;
287         QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
288         if (t) {
289             if (vmajor) *vmajor = vmaj;
290             if (vminor) *vminor = vmin;
291             if (type_return)
292                 *type_return = t;
293             return true;
294         }
295     }
296
297     const QDeclarativeDirComponents &qmldircomponents = data.qmlDirComponents;
298     bool typeWasDeclaredInQmldir = false;
299     if (!qmldircomponents.isEmpty()) {
300         foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
301             if (c.typeName == type) {
302                 typeWasDeclaredInQmldir = true;
303                 // importing version -1 means import ALL versions
304                 if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
305                     QString url(data.url + type + QLatin1String(".qml"));
306                     QString candidate = resolveLocalUrl(url, c.fileName);
307                     if (c.internal && base) {
308                         if (resolveLocalUrl(*base, c.fileName) != candidate)
309                             continue; // failed attempt to access an internal type
310                     }
311                     if (base && *base == candidate) {
312                         if (typeRecursionDetected)
313                             *typeRecursionDetected = true;
314                         continue; // no recursion
315                     }
316                     if (url_return)
317                         *url_return = candidate;
318                     return true;
319                 }
320             }
321         }
322     }
323
324     if (!typeWasDeclaredInQmldir && !data.isLibrary) {
325         // XXX search non-files too! (eg. zip files, see QT-524)
326         QString url(data.url + type + QLatin1String(".qml"));
327         QString file = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
328         if (!typeLoader->absoluteFilePath(file).isEmpty()) {
329             if (base && *base == url) { // no recursion
330                 if (typeRecursionDetected)
331                     *typeRecursionDetected = true;
332             } else {
333                 if (url_return)
334                     *url_return = url;
335                 return true;
336             }
337         }
338     }
339     return false;
340 }
341
342 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader)
343     : ref(1), typeLoader(loader)
344 {
345 }
346
347 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
348 {
349     foreach (QDeclarativeImportedNamespace* s, set.values())
350         delete s;
351 }
352
353 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri, 
354                                                  QDeclarativeImportDatabase *database, 
355                                                  QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors)
356 {
357     const QDeclarativeDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
358     if (qmldirParser->hasError()) {
359         if (errors) {
360             const QList<QDeclarativeError> qmldirErrors = qmldirParser->errors(uri);
361             for (int i = 0; i < qmldirErrors.size(); ++i)
362                 errors->prepend(qmldirErrors.at(i));
363         }
364         return false;
365     }
366
367     if (qmlImportTrace())
368         qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base) << "::importExtension: "
369                            << "loaded " << absoluteFilePath;
370
371     if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
372         qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
373
374         QString qmldirPath = absoluteFilePath;
375         int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
376         if (slash > 0)
377             qmldirPath.truncate(slash);
378         foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser->plugins()) {
379
380             QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
381             if (!resolvedFilePath.isEmpty()) {
382                 if (!database->importPlugin(resolvedFilePath, uri, errors)) {
383                     if (errors) {
384                         // XXX TODO: should we leave the import plugin error alone?
385                         // Here, we pop it off the top and coalesce it into this error's message.
386                         // The reason is that the lower level may add url and line/column numbering information.
387                         QDeclarativeError poppedError = errors->takeFirst();
388                         QDeclarativeError error;
389                         error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
390                         error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
391                         errors->prepend(error);
392                     }
393                     return false;
394                 }
395             } else {
396                 if (errors) {
397                     QDeclarativeError error;
398                     error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
399                     error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
400                     errors->prepend(error);
401                 }
402                 return false;
403             }
404         }
405     }
406
407     if (components)
408         *components = qmldirParser->components();
409
410     return true;
411 }
412
413 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
414 {
415     QString dir = dir_arg;
416     if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
417         dir.chop(1);
418
419     QStringList paths = database->fileImportPath;
420     qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
421
422     QString stableRelativePath = dir;
423     foreach(const QString &path, paths) {
424         if (dir.startsWith(path)) {
425             stableRelativePath = dir.mid(path.length()+1);
426             break;
427         }
428     }
429
430     stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
431
432     // remove optional versioning in dot notation from uri
433     int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
434     if (lastSlash >= 0) {
435         int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
436         if (versionDot >= 0)
437             stableRelativePath = stableRelativePath.left(versionDot);
438     }
439
440     stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
441     return stableRelativePath;
442 }
443
444 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork, 
445                                      const QString& uri_arg, const QString& prefix, int vmaj, int vmin, 
446                                      QDeclarativeScript::Import::Type importType, 
447                                      QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors)
448 {
449     static QLatin1String Slash_qmldir("/qmldir");
450     static QLatin1Char Slash('/');
451
452     QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
453     QString uri = uri_arg;
454     QDeclarativeImportedNamespace *s;
455     if (prefix.isEmpty()) {
456         s = &unqualifiedset;
457     } else {
458         s = set.value(prefix);
459         if (!s)
460             set.insert(prefix,(s=new QDeclarativeImportedNamespace));
461     }
462     QString url = uri;
463     bool versionFound = false;
464     if (importType == QDeclarativeScript::Import::Library) {
465
466         Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
467
468         url.replace(QLatin1Char('.'), Slash);
469         bool found = false;
470         QString dir;
471         QString qmldir;
472
473         // step 1: search for extension with fully encoded version number
474         foreach (const QString &p, database->fileImportPath) {
475             dir = p+Slash+url;
476
477             QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
478             const QString absoluteFilePath = fi.absoluteFilePath();
479
480             if (fi.isFile()) {
481                 found = true;
482
483                 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
484                 uri = resolvedUri(dir, database);
485                 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
486                     return false;
487                 break;
488             }
489         }
490
491         // step 2: search for extension with encoded version major
492         foreach (const QString &p, database->fileImportPath) {
493             dir = p+Slash+url;
494
495             QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
496             const QString absoluteFilePath = fi.absoluteFilePath();
497
498             if (fi.isFile()) {
499                 found = true;
500
501                 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
502                 uri = resolvedUri(dir, database);
503                 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
504                     return false;
505                 break;
506             }
507         }
508
509         if (!found) {
510             // step 3: search for extension without version number
511
512             foreach (const QString &p, database->fileImportPath) {
513                 dir = p+Slash+url;
514                 qmldir = dir+Slash_qmldir;
515
516                 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
517                 if (!absoluteFilePath.isEmpty()) {
518                     found = true;
519                     QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
520                     url = QLatin1String("file://") + absolutePath;
521                     uri = resolvedUri(dir, database);
522                     if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
523                         return false;
524                     break;
525                 }
526             }
527         }
528
529         if (QDeclarativeMetaType::isModule(uri, vmaj, vmin))
530             versionFound = true;
531
532         if (!versionFound && qmldircomponents.isEmpty()) {
533             if (errors) {
534                 QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
535                 if (QDeclarativeMetaType::isAnyModule(uri))
536                     error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
537                 else
538                     error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
539                 errors->prepend(error);
540             }
541             return false;
542         }
543     } else {
544         if (importType == QDeclarativeScript::Import::File && qmldircomponents.isEmpty()) {
545             QString importUrl = resolveLocalUrl(base, uri + Slash_qmldir);
546             QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
547             if (!localFileOrQrc.isEmpty()) {
548                 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
549                 if (!typeLoader->directoryExists(dir)) {
550                     if (errors) {
551                         QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
552                         error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
553                         error.setUrl(QUrl(importUrl));
554                         errors->prepend(error);
555                     }
556                     return false; // local import dirs must exist
557                 }
558                 uri = resolvedUri(dir, database);
559                 if (uri.endsWith(Slash))
560                     uri.chop(1);
561                 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
562                     if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors))
563                         return false;
564                 }
565             } else {
566                 if (prefix.isEmpty()) {
567                     // directory must at least exist for valid import
568                     QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
569                     if (!typeLoader->directoryExists(localFileOrQrc)) {
570                         if (errors) {
571                             QDeclarativeError error; // we don't set the line or column as these will be set by the loader.
572                             if (localFileOrQrc.isEmpty())
573                                 error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
574                             else
575                                 error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri));
576                             error.setUrl(QUrl(importUrl));
577                             errors->prepend(error);
578                         }
579                         return false;
580                     }
581                 }
582             }
583         }
584
585         url = resolveLocalUrl(base, url);
586     }
587
588     if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
589         QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
590         int lowest_min = INT_MAX;
591         int highest_min = INT_MIN;
592         for (; it != qmldircomponents.end(); ++it) {
593             if (it->majorVersion == vmaj) {
594                 lowest_min = qMin(lowest_min, it->minorVersion);
595                 highest_min = qMax(highest_min, it->minorVersion);
596             }
597         }
598         if (lowest_min > vmin || highest_min < vmin) {
599             if (errors) {
600                 QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
601                 error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
602                 errors->prepend(error);
603             }
604             return false;
605         }
606     }
607
608     if (!url.endsWith(Slash))
609         url += Slash;
610
611     QDeclarativeImportedNamespace::Data data;
612     data.uri = uri;
613     data.url = url;
614     data.majversion = vmaj;
615     data.minversion = vmin;
616     data.isLibrary = importType == QDeclarativeScript::Import::Library;
617     data.qmlDirComponents = qmldircomponents;
618     s->imports.prepend(data);
619
620     return true;
621 }
622
623 bool QDeclarativeImportsPrivate::find(const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
624                                       QString* url_return, QList<QDeclarativeError> *errors)
625 {
626     QDeclarativeImportedNamespace *s = 0;
627     int slash = type.indexOf(QLatin1Char('/'));
628     if (slash >= 0) {
629         QString namespaceName = type.left(slash);
630         s = set.value(namespaceName);
631         if (!s) {
632             if (errors) {
633                 QDeclarativeError error;
634                 error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
635                 errors->prepend(error);
636             }
637             return false;
638         }
639         int nslash = type.indexOf(QLatin1Char('/'),slash+1);
640         if (nslash > 0) {
641             if (errors) {
642                 QDeclarativeError error;
643                 error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed"));
644                 errors->prepend(error);
645             }
646             return false;
647         }
648     } else {
649         s = &unqualifiedset;
650     }
651     QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
652     if (s) {
653         if (s->find(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
654             return true;
655         if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
656             // qualified, and only 1 url
657             *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml"));
658             return true;
659         }
660     }
661
662     return false;
663 }
664
665 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
666 {
667     return set.value(type);
668 }
669
670 bool QDeclarativeImportedNamespace::find(QDeclarativeTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
671           QString* url_return, QString *base, QList<QDeclarativeError> *errors)
672 {
673     bool typeRecursionDetected = false;
674     for (int i=0; i<imports.count(); ++i) {
675         if (find_helper(typeLoader, imports.at(i), type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
676             if (qmlCheckTypes()) {
677                 // check for type clashes
678                 for (int j = i+1; j<imports.count(); ++j) {
679                     if (find_helper(typeLoader, imports.at(j), type, vmajor, vminor, 0, 0, base)) {
680                         if (errors) {
681                             QString u1 = imports.at(i).url;
682                             QString u2 = imports.at(j).url;
683                             if (base) {
684                                 QString b = *base;
685                                 int slash = b.lastIndexOf(QLatin1Char('/'));
686                                 if (slash >= 0) {
687                                     b = b.left(slash+1);
688                                     QString l = b.left(slash);
689                                     if (u1.startsWith(b))
690                                         u1 = u1.mid(b.count());
691                                     else if (u1 == l)
692                                         u1 = QDeclarativeImportDatabase::tr("local directory");
693                                     if (u2.startsWith(b))
694                                         u2 = u2.mid(b.count());
695                                     else if (u2 == l)
696                                         u2 = QDeclarativeImportDatabase::tr("local directory");
697                                 }
698                             }
699
700                             QDeclarativeError error;
701                             if (u1 != u2) {
702                                 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
703                             } else {
704                                 error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
705                                                         .arg(u1)
706                                                         .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
707                                                         .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
708                             }
709                             errors->prepend(error);
710                         }
711                         return false;
712                     }
713                 }
714             }
715             return true;
716         }
717     }
718     if (errors) {
719         QDeclarativeError error;
720         if (typeRecursionDetected)
721             error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively"));
722         else
723             error.setDescription(QDeclarativeImportDatabase::tr("is not a type"));
724         errors->prepend(error);
725     }
726     return false;
727 }
728
729 /*!
730 \class QDeclarativeImportDatabase
731 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
732 \internal
733 */
734 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
735 : engine(e)
736 {
737     filePluginPath << QLatin1String(".");
738
739     // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
740
741 #ifndef QT_NO_SETTINGS
742     QString installImportsPath =  QLibraryInfo::location(QLibraryInfo::ImportsPath);
743     addImportPath(installImportsPath);
744 #endif // QT_NO_SETTINGS
745
746     // env import paths
747     QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
748     if (!envImportPath.isEmpty()) {
749 #if defined(Q_OS_WIN)
750         QLatin1Char pathSep(';');
751 #else
752         QLatin1Char pathSep(':');
753 #endif
754         QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
755         for (int ii = paths.count() - 1; ii >= 0; --ii)
756             addImportPath(paths.at(ii));
757     }
758
759     addImportPath(QCoreApplication::applicationDirPath());
760 }
761
762 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
763 {
764 }
765
766 /*!
767   \internal
768
769   Adds information to \a imports such that subsequent calls to resolveType()
770   will resolve types qualified by \a prefix by considering types found at the given \a uri.
771
772   The uri is either a directory (if importType is FileImport), or a URI resolved using paths
773   added via addImportPath() (if importType is LibraryImport).
774
775   The \a prefix may be empty, in which case the import location is considered for
776   unqualified types.
777
778   The base URL must already have been set with Import::setBaseUrl().
779 */
780 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb, 
781                                     const QString& uri, const QString& prefix, int vmaj, int vmin, 
782                                     QDeclarativeScript::Import::Type importType, 
783                                     const QDeclarativeDirComponents &qmldircomponentsnetwork, 
784                                     QList<QDeclarativeError> *errors)
785 {
786     if (qmlImportTrace())
787         qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: " 
788                            << uri << " " << vmaj << '.' << vmin << " " 
789                            << (importType==QDeclarativeScript::Import::Library? "Library" : "File") 
790                            << " as " << prefix;
791
792     return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
793 }
794
795 /*!
796   \internal
797
798   Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
799   The \a prefix must contain the dot.
800
801   \a qmldirPath is the location of the qmldir file.
802  */
803 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
804                                                   const QString &qmldirPath, const QString &qmldirPluginPath,
805                                                   const QString &baseName, const QStringList &suffixes,
806                                                   const QString &prefix)
807 {
808     QStringList searchPaths = filePluginPath;
809     bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
810     if (!qmldirPluginPathIsRelative)
811         searchPaths.prepend(qmldirPluginPath);
812
813     foreach (const QString &pluginPath, searchPaths) {
814
815         QString resolvedPath;
816         if (pluginPath == QLatin1String(".")) {
817             if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
818                 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath);
819             else
820                 resolvedPath = qmldirPath;
821         } else {
822             if (QDir::isRelativePath(pluginPath))
823                 resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath);
824             else
825                 resolvedPath = pluginPath;
826         }
827
828         // hack for resources, should probably go away
829         if (resolvedPath.startsWith(QLatin1Char(':')))
830             resolvedPath = QCoreApplication::applicationDirPath();
831
832         if (!resolvedPath.endsWith(QLatin1Char('/')))
833             resolvedPath += QLatin1Char('/');
834
835         foreach (const QString &suffix, suffixes) {
836             QString pluginFileName = prefix;
837
838             pluginFileName += baseName;
839             pluginFileName += suffix;
840
841             QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
842             if (!absolutePath.isEmpty())
843                 return absolutePath;
844         }
845     }
846
847     if (qmlImportTrace())
848         qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName 
849                  << "in" << qmldirPath;
850
851     return QString();
852 }
853
854 /*!
855   \internal
856
857   Returns the result of the merge of \a baseName with \a dir and the platform suffix.
858
859   \table
860   \header \i Platform \i Valid suffixes
861   \row \i Windows     \i \c .dll
862   \row \i Unix/Linux  \i \c .so
863   \row \i AIX  \i \c .a
864   \row \i HP-UX       \i \c .sl, \c .so (HP-UXi)
865   \row \i Mac OS X    \i \c .dylib, \c .bundle, \c .so
866   \endtable
867
868   Version number on unix are ignored.
869 */
870 QString QDeclarativeImportDatabase::resolvePlugin(QDeclarativeTypeLoader *typeLoader,
871                                                   const QString &qmldirPath, const QString &qmldirPluginPath,
872                                                   const QString &baseName)
873 {
874 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
875     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
876                          QStringList()
877 # ifdef QT_DEBUG
878                          << QLatin1String("d.dll") // try a qmake-style debug build first
879 # endif
880                          << QLatin1String(".dll"));
881 #else
882
883 # if defined(Q_OS_DARWIN)
884
885     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
886                          QStringList()
887 # ifdef QT_DEBUG
888                          << QLatin1String("_debug.dylib") // try a qmake-style debug build first
889                          << QLatin1String(".dylib")
890 # else
891                          << QLatin1String(".dylib")
892                          << QLatin1String("_debug.dylib") // try a qmake-style debug build after
893 # endif
894                          << QLatin1String(".so")
895                          << QLatin1String(".bundle"),
896                          QLatin1String("lib"));
897 # else  // Generic Unix
898     QStringList validSuffixList;
899
900 #  if defined(Q_OS_HPUX)
901 /*
902     See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
903     "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
904     the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
905  */
906     validSuffixList << QLatin1String(".sl");
907 #   if defined __ia64
908     validSuffixList << QLatin1String(".so");
909 #   endif
910 #  elif defined(Q_OS_AIX)
911     validSuffixList << QLatin1String(".a") << QLatin1String(".so");
912 #  elif defined(Q_OS_UNIX)
913     validSuffixList << QLatin1String(".so");
914 #  endif
915
916     // Examples of valid library names:
917     //  libfoo.so
918
919     return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
920 # endif
921
922 #endif
923 }
924
925 /*!
926     \internal
927 */
928 QStringList QDeclarativeImportDatabase::pluginPathList() const
929 {
930     return filePluginPath;
931 }
932
933 /*!
934     \internal
935 */
936 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
937 {
938     filePluginPath = paths;
939 }
940
941 /*!
942     \internal
943 */
944 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
945 {
946     if (qmlImportTrace())
947         qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
948
949     QUrl url = QUrl(path);
950     if (url.isRelative() || url.scheme() == QLatin1String("file")
951             || (url.scheme().length() == 1 && QFile::exists(path)) ) {  // windows path
952         QDir dir = QDir(path);
953         filePluginPath.prepend(dir.canonicalPath());
954     } else {
955         filePluginPath.prepend(path);
956     }
957 }
958
959 /*!
960     \internal
961 */
962 void QDeclarativeImportDatabase::addImportPath(const QString& path)
963 {
964     if (qmlImportTrace())
965         qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
966
967     if (path.isEmpty())
968         return;
969
970     QUrl url = QUrl(path);
971     QString cPath;
972
973     if (url.isRelative() || url.scheme() == QLatin1String("file")
974             || (url.scheme().length() == 1 && QFile::exists(path)) ) {  // windows path
975         QDir dir = QDir(path);
976         cPath = dir.canonicalPath();
977     } else {
978         cPath = path;
979         cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
980     }
981
982     if (!cPath.isEmpty()
983         && !fileImportPath.contains(cPath))
984         fileImportPath.prepend(cPath);
985 }
986
987 /*!
988     \internal
989 */
990 QStringList QDeclarativeImportDatabase::importPathList() const
991 {
992     return fileImportPath;
993 }
994
995 /*!
996     \internal
997 */
998 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
999 {
1000     fileImportPath = paths;
1001 }
1002
1003 /*!
1004     \internal
1005 */
1006 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors)
1007 {
1008     if (qmlImportTrace())
1009         qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1010
1011 #ifndef QT_NO_LIBRARY
1012     QFileInfo fileInfo(filePath);
1013     const QString absoluteFilePath = fileInfo.absoluteFilePath();
1014
1015     bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1016     bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1017
1018     if (typesRegistered) {
1019         Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1020                    "QDeclarativeImportDatabase::importExtension",
1021                    "Internal error: Plugin imported previously with different uri");
1022     }
1023
1024     if (!engineInitialized || !typesRegistered) {
1025         if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1026             if (errors) {
1027                 QDeclarativeError error;
1028                 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1029                 errors->prepend(error);
1030             }
1031             return false;
1032         }
1033         QPluginLoader loader(absoluteFilePath);
1034
1035         if (!loader.load()) {
1036             if (errors) {
1037                 QDeclarativeError error;
1038                 error.setDescription(loader.errorString());
1039                 errors->prepend(error);
1040             }
1041             return false;
1042         }
1043
1044         QObject *instance = loader.instance();
1045         if (QDeclarativeTypesExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(instance)) {
1046
1047             const QByteArray bytes = uri.toUtf8();
1048             const char *moduleId = bytes.constData();
1049             if (!typesRegistered) {
1050
1051                 // XXX thread this code should probably be protected with a mutex.
1052                 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1053                 iface->registerTypes(moduleId);
1054             }
1055             if (!engineInitialized) {
1056                 // things on the engine (eg. adding new global objects) have to be done for every 
1057                 // engine.  
1058                 // XXX protect against double initialization
1059                 initializedPlugins.insert(absoluteFilePath);
1060
1061                 QDeclarativeExtensionInterface *eiface = 
1062                     qobject_cast<QDeclarativeExtensionInterface *>(instance);
1063                 if (eiface) {
1064                     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
1065                     ep->typeLoader.initializeEngine(eiface, moduleId);
1066                 }
1067             }
1068         } else {
1069             if (errors) {
1070                 QDeclarativeError error;
1071                 error.setDescription(loader.errorString());
1072                 errors->prepend(error);
1073             }
1074             return false;
1075         }
1076     }
1077
1078     return true;
1079 #else
1080     return false;
1081 #endif
1082 }
1083
1084 QT_END_NAMESPACE