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