source: trunk/src/declarative/qml/qdeclarativeimport.cpp

Last change on this file was 1056, checked in by Dmitry A. Kuminov, 14 years ago

OS/2: Use correct path separator when resolving QML plugin DLLs.

File size: 39.4 KB
Line 
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** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
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
58QT_BEGIN_NAMESPACE
59
60DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
61DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
62
63static bool greaterThan(const QString &s1, const QString &s2)
64{
65 return s1 > s2;
66}
67
68typedef QMap<QString, QString> StringStringMap;
69Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
70
71class QDeclarativeImportedNamespace
72{
73public:
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
89class QDeclarativeImportsPrivate {
90public:
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*/
121QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports &copy)
122: d(copy.d)
123{
124 ++d->ref;
125}
126
127QDeclarativeImports &
128QDeclarativeImports::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
137QDeclarativeImports::QDeclarativeImports()
138: d(new QDeclarativeImportsPrivate)
139{
140}
141
142QDeclarativeImports::~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*/
151void 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*/
159QUrl QDeclarativeImports::baseUrl() const
160{
161 return d->base;
162}
163
164static QDeclarativeTypeNameCache *
165cacheForNamespace(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
193void 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*/
228bool 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*/
267bool 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
274bool 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
341QDeclarativeImportsPrivate::QDeclarativeImportsPrivate()
342: ref(1)
343{
344}
345
346QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
347{
348 foreach (QDeclarativeImportedNamespace* s, set.values())
349 delete s;
350}
351
352bool 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
412QString 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
443bool 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 if (importType == QDeclarativeScriptParser::Import::Library) {
461 url.replace(QLatin1Char('.'), QLatin1Char('/'));
462 bool found = false;
463 QString dir;
464
465
466 // step 1: search for extension with fully encoded version number
467 if (vmaj >= 0 && vmin >= 0) {
468 foreach (const QString &p, database->fileImportPath) {
469 dir = p+QLatin1Char('/')+url;
470
471 QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
472 const QString absoluteFilePath = fi.absoluteFilePath();
473
474 if (fi.isFile()) {
475 found = true;
476
477 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
478 uri = resolvedUri(dir, database);
479 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString))
480 return false;
481 break;
482 }
483 }
484 }
485 // step 2: search for extension with encoded version major
486 if (vmaj >= 0 && vmin >= 0) {
487 foreach (const QString &p, database->fileImportPath) {
488 dir = p+QLatin1Char('/')+url;
489
490 QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
491 const QString absoluteFilePath = fi.absoluteFilePath();
492
493 if (fi.isFile()) {
494 found = true;
495
496 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
497 uri = resolvedUri(dir, database);
498 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString))
499 return false;
500 break;
501 }
502 }
503 }
504 if (!found) {
505 // step 3: search for extension without version number
506
507 foreach (const QString &p, database->fileImportPath) {
508 dir = p+QLatin1Char('/')+url;
509
510 QFileInfo fi(dir+QLatin1String("/qmldir"));
511 const QString absoluteFilePath = fi.absoluteFilePath();
512
513 if (fi.isFile()) {
514 found = true;
515
516 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
517 uri = resolvedUri(dir, database);
518 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString))
519 return false;
520 break;
521 }
522 }
523 }
524
525 if (!found) {
526 found = QDeclarativeMetaType::isModule(uri.toUtf8(), vmaj, vmin);
527 if (!found) {
528 if (errorString) {
529 bool anyversion = QDeclarativeMetaType::isModule(uri.toUtf8(), -1, -1);
530 if (anyversion)
531 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin);
532 else
533 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg);
534 }
535 return false;
536 }
537 }
538 } else {
539
540 if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) {
541 QUrl importUrl = base.resolved(QUrl(uri + QLatin1String("/qmldir")));
542 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
543 if (!localFileOrQrc.isEmpty()) {
544 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
545 QFileInfo dirinfo(dir);
546 if (dir.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) {
547 if (errorString)
548 *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg);
549 return false; // local import dirs must exist
550 }
551 uri = resolvedUri(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))), database);
552 if (uri.endsWith(QLatin1Char('/')))
553 uri.chop(1);
554 if (QFile::exists(localFileOrQrc)) {
555 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errorString))
556 return false;
557 }
558 } else {
559 if (prefix.isEmpty()) {
560 // directory must at least exist for valid import
561 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
562 QFileInfo dirinfo(localFileOrQrc);
563 if (localFileOrQrc.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) {
564 if (errorString) {
565 if (localFileOrQrc.isEmpty())
566 *errorString = QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri);
567 else
568 *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri);
569 }
570 return false;
571 }
572 }
573 }
574 }
575
576 url = base.resolved(QUrl(url)).toString();
577 if (url.endsWith(QLatin1Char('/')))
578 url.chop(1);
579 }
580
581 if (vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
582 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
583 int lowest_maj = INT_MAX;
584 int lowest_min = INT_MAX;
585 int highest_maj = INT_MIN;
586 int highest_min = INT_MIN;
587 for (; it != qmldircomponents.end(); ++it) {
588 if (it->majorVersion > highest_maj || (it->majorVersion == highest_maj && it->minorVersion > highest_min)) {
589 highest_maj = it->majorVersion;
590 highest_min = it->minorVersion;
591 }
592 if (it->majorVersion < lowest_maj || (it->majorVersion == lowest_maj && it->minorVersion < lowest_min)) {
593 lowest_maj = it->majorVersion;
594 lowest_min = it->minorVersion;
595 }
596 }
597 if (lowest_maj > vmaj || (lowest_maj == vmaj && lowest_min > vmin)
598 || highest_maj < vmaj || (highest_maj == vmaj && highest_min < vmin))
599 {
600 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin);
601 return false;
602 }
603 }
604
605 s->uris.prepend(uri);
606 s->urls.prepend(url);
607 s->majversions.prepend(vmaj);
608 s->minversions.prepend(vmin);
609 s->isLibrary.prepend(importType == QDeclarativeScriptParser::Import::Library);
610 s->qmlDirComponents.prepend(qmldircomponents);
611 return true;
612}
613
614bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
615 QUrl* url_return, QString *errorString)
616{
617 QDeclarativeImportedNamespace *s = 0;
618 int slash = type.indexOf('/');
619 if (slash >= 0) {
620 QString namespaceName = QString::fromUtf8(type.left(slash));
621 s = set.value(namespaceName);
622 if (!s) {
623 if (errorString)
624 *errorString = QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName);
625 return false;
626 }
627 int nslash = type.indexOf('/',slash+1);
628 if (nslash > 0) {
629 if (errorString)
630 *errorString = QDeclarativeImportDatabase::tr("- nested namespaces not allowed");
631 return false;
632 }
633 } else {
634 s = &unqualifiedset;
635 }
636 QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
637 if (s) {
638 if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errorString))
639 return true;
640 if (s->urls.count() == 1 && !s->isLibrary[0] && url_return && s != &unqualifiedset) {
641 // qualified, and only 1 url
642 *url_return = QUrl(s->urls[0]+QLatin1Char('/')).resolved(QUrl(QString::fromUtf8(unqualifiedtype) + QLatin1String(".qml")));
643 return true;
644 }
645 }
646
647 return false;
648}
649
650QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
651{
652 return set.value(type);
653}
654
655bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
656 QUrl* url_return, QUrl *base, QString *errorString)
657{
658 bool typeRecursionDetected = false;
659 for (int i=0; i<urls.count(); ++i) {
660 if (find_helper(i, type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
661 if (qmlCheckTypes()) {
662 // check for type clashes
663 for (int j = i+1; j<urls.count(); ++j) {
664 if (find_helper(j, type, vmajor, vminor, 0, 0, base)) {
665 if (errorString) {
666 QString u1 = urls.at(i);
667 QString u2 = urls.at(j);
668 if (base) {
669 QString b = base->toString();
670 int slash = b.lastIndexOf(QLatin1Char('/'));
671 if (slash >= 0) {
672 b = b.left(slash+1);
673 QString l = b.left(slash);
674 if (u1.startsWith(b))
675 u1 = u1.mid(b.count());
676 else if (u1 == l)
677 u1 = QDeclarativeImportDatabase::tr("local directory");
678 if (u2.startsWith(b))
679 u2 = u2.mid(b.count());
680 else if (u2 == l)
681 u2 = QDeclarativeImportDatabase::tr("local directory");
682 }
683 }
684
685 if (u1 != u2)
686 *errorString
687 = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2")
688 .arg(u1).arg(u2);
689 else
690 *errorString
691 = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
692 .arg(u1)
693 .arg(majversions.at(i)).arg(minversions.at(i))
694 .arg(majversions.at(j)).arg(minversions.at(j));
695 }
696 return false;
697 }
698 }
699 }
700 return true;
701 }
702 }
703 if (errorString) {
704 if (typeRecursionDetected)
705 *errorString = QDeclarativeImportDatabase::tr("is instantiated recursively");
706 else
707 *errorString = QDeclarativeImportDatabase::tr("is not a type");
708 }
709 return false;
710}
711
712/*!
713\class QDeclarativeImportDatabase
714\brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
715\internal
716*/
717QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
718: engine(e)
719{
720 filePluginPath << QLatin1String(".");
721
722 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
723
724 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
725
726#if defined(Q_OS_SYMBIAN)
727 // Append imports path for all available drives in Symbian
728 if (installImportsPath.at(1) != QChar(QLatin1Char(':'))) {
729 QString tempPath = installImportsPath;
730 if (tempPath.at(tempPath.length() - 1) != QDir::separator()) {
731 tempPath += QDir::separator();
732 }
733 RFs& fs = qt_s60GetRFs();
734 TPtrC tempPathPtr(reinterpret_cast<const TText*> (tempPath.constData()));
735 TFindFile finder(fs);
736 TInt err = finder.FindByDir(tempPathPtr, tempPathPtr);
737 while (err == KErrNone) {
738 QString foundDir(reinterpret_cast<const QChar *>(finder.File().Ptr()),
739 finder.File().Length());
740 foundDir = QDir(foundDir).canonicalPath();
741 addImportPath(foundDir);
742 err = finder.Find();
743 }
744 } else {
745 addImportPath(installImportsPath);
746 }
747#else
748 addImportPath(installImportsPath);
749#endif
750 // env import paths
751 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
752 if (!envImportPath.isEmpty()) {
753#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) || defined(Q_OS_OS2)
754 QLatin1Char pathSep(';');
755#else
756 QLatin1Char pathSep(':');
757#endif
758 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
759 for (int ii = paths.count() - 1; ii >= 0; --ii)
760 addImportPath(paths.at(ii));
761 }
762
763 addImportPath(QCoreApplication::applicationDirPath());
764}
765
766QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
767{
768}
769
770/*!
771 \internal
772
773 Adds information to \a imports such that subsequent calls to resolveType()
774 will resolve types qualified by \a prefix by considering types found at the given \a uri.
775
776 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
777 added via addImportPath() (if importType is LibraryImport).
778
779 The \a prefix may be empty, in which case the import location is considered for
780 unqualified types.
781
782 The base URL must already have been set with Import::setBaseUrl().
783*/
784bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
785 const QString& uri, const QString& prefix, int vmaj, int vmin,
786 QDeclarativeScriptParser::Import::Type importType,
787 const QDeclarativeDirComponents &qmldircomponentsnetwork,
788 QString *errorString)
789{
790 if (qmlImportTrace())
791 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
792 << uri << " " << vmaj << '.' << vmin << " "
793 << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File")
794 << " as " << prefix;
795
796 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errorString);
797}
798
799/*!
800 \internal
801
802 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
803 The \a prefix must contain the dot.
804
805 \a qmldirPath is the location of the qmldir file.
806 */
807QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
808 const QString &baseName, const QStringList &suffixes,
809 const QString &prefix)
810{
811 QStringList searchPaths = filePluginPath;
812 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
813 if (!qmldirPluginPathIsRelative)
814 searchPaths.prepend(qmldirPluginPath);
815
816 foreach (const QString &pluginPath, searchPaths) {
817
818 QString resolvedPath;
819
820 if (pluginPath == QLatin1String(".")) {
821 if (qmldirPluginPathIsRelative)
822 resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath);
823 else
824 resolvedPath = qmldirPath.absolutePath();
825 } else {
826 resolvedPath = pluginPath;
827 }
828
829 // hack for resources, should probably go away
830 if (resolvedPath.startsWith(QLatin1Char(':')))
831 resolvedPath = QCoreApplication::applicationDirPath();
832
833 QDir dir(resolvedPath);
834 foreach (const QString &suffix, suffixes) {
835 QString pluginFileName = prefix;
836
837 pluginFileName += baseName;
838 pluginFileName += suffix;
839
840 QFileInfo fileInfo(dir, pluginFileName);
841
842 if (fileInfo.exists())
843 return fileInfo.absoluteFilePath();
844 }
845 }
846
847 if (qmlImportTrace())
848 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
849 << "in" << qmldirPath.absolutePath();
850
851 return QString();
852}
853
854/*!
855 \internal
856
857 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
858
859 \table
860 \header \i Platform \i Valid suffixes
861 \row \i Windows \i \c .dll
862 \row \i Unix/Linux \i \c .so
863 \row \i AIX \i \c .a
864 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
865 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
866 \row \i Symbian \i \c .dll
867 \endtable
868
869 Version number on unix are ignored.
870*/
871QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
872 const QString &baseName)
873{
874#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_OS2)
875 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
876 QStringList()
877# ifdef QT_DEBUG
878 << QLatin1String("d.dll") // try a qmake-style debug build first
879# endif
880 << QLatin1String(".dll"));
881#elif defined(Q_OS_SYMBIAN)
882 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
883 QStringList()
884 << QLatin1String(".dll")
885 << QLatin1String(".qtplugin"));
886#else
887
888# if defined(Q_OS_DARWIN)
889
890 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
891 QStringList()
892# ifdef QT_DEBUG
893 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
894 << QLatin1String(".dylib")
895# else
896 << QLatin1String(".dylib")
897 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
898# endif
899 << QLatin1String(".so")
900 << QLatin1String(".bundle"),
901 QLatin1String("lib"));
902# else // Generic Unix
903 QStringList validSuffixList;
904
905# if defined(Q_OS_HPUX)
906/*
907 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
908 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
909 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
910 */
911 validSuffixList << QLatin1String(".sl");
912# if defined __ia64
913 validSuffixList << QLatin1String(".so");
914# endif
915# elif defined(Q_OS_AIX)
916 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
917# elif defined(Q_OS_UNIX)
918 validSuffixList << QLatin1String(".so");
919# endif
920
921 // Examples of valid library names:
922 // libfoo.so
923
924 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
925# endif
926
927#endif
928}
929
930/*!
931 \internal
932*/
933QStringList QDeclarativeImportDatabase::pluginPathList() const
934{
935 return filePluginPath;
936}
937
938/*!
939 \internal
940*/
941void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
942{
943 filePluginPath = paths;
944}
945
946/*!
947 \internal
948*/
949void QDeclarativeImportDatabase::addPluginPath(const QString& path)
950{
951 if (qmlImportTrace())
952 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
953
954 QUrl url = QUrl(path);
955 if (url.isRelative() || url.scheme() == QLatin1String("file")
956 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
957 QDir dir = QDir(path);
958 filePluginPath.prepend(dir.canonicalPath());
959 } else {
960 filePluginPath.prepend(path);
961 }
962}
963
964/*!
965 \internal
966*/
967void QDeclarativeImportDatabase::addImportPath(const QString& path)
968{
969 if (qmlImportTrace())
970 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
971
972 if (path.isEmpty())
973 return;
974
975 QUrl url = QUrl(path);
976 QString cPath;
977
978 if (url.isRelative() || url.scheme() == QLatin1String("file")
979 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
980 QDir dir = QDir(path);
981 cPath = dir.canonicalPath();
982 } else {
983 cPath = path;
984 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
985 }
986
987 if (!cPath.isEmpty()
988 && !fileImportPath.contains(cPath))
989 fileImportPath.prepend(cPath);
990}
991
992/*!
993 \internal
994*/
995QStringList QDeclarativeImportDatabase::importPathList() const
996{
997 return fileImportPath;
998}
999
1000/*!
1001 \internal
1002*/
1003void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1004{
1005 fileImportPath = paths;
1006}
1007
1008/*!
1009 \internal
1010*/
1011bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QString *errorString)
1012{
1013 if (qmlImportTrace())
1014 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1015
1016 QFileInfo fileInfo(filePath);
1017 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1018
1019 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1020 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1021
1022 if (typesRegistered) {
1023 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1024 "QDeclarativeImportDatabase::importExtension",
1025 "Internal error: Plugin imported previously with different uri");
1026 }
1027
1028 if (!engineInitialized || !typesRegistered) {
1029 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1030 if (errorString)
1031 *errorString = tr("File name case mismatch for \"%2\"").arg(absoluteFilePath);
1032 return false;
1033 }
1034 QPluginLoader loader(absoluteFilePath);
1035
1036 if (!loader.load()) {
1037 if (errorString)
1038 *errorString = loader.errorString();
1039 return false;
1040 }
1041
1042 if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) {
1043
1044 const QByteArray bytes = uri.toUtf8();
1045 const char *moduleId = bytes.constData();
1046 if (!typesRegistered) {
1047
1048 // ### this code should probably be protected with a mutex.
1049 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1050 iface->registerTypes(moduleId);
1051 }
1052 if (!engineInitialized) {
1053 // things on the engine (eg. adding new global objects) have to be done for every engine.
1054
1055 // protect against double initialization
1056 initializedPlugins.insert(absoluteFilePath);
1057 iface->initializeEngine(engine, moduleId);
1058 }
1059 } else {
1060 if (errorString)
1061 *errorString = loader.errorString();
1062 return false;
1063 }
1064 }
1065
1066 return true;
1067}
1068
1069
1070QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.