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