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