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