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