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