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