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