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