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