|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 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 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 typedef QMap<QString, QString> StringStringMap; |
|
65 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri |
|
66 |
|
67 class QDeclarativeImportedNamespace |
|
68 { |
|
69 public: |
|
70 QStringList uris; |
|
71 QStringList urls; |
|
72 QList<int> majversions; |
|
73 QList<int> minversions; |
|
74 QList<bool> isLibrary; |
|
75 QList<QDeclarativeDirComponents> qmlDirComponents; |
|
76 |
|
77 |
|
78 bool find_helper(int i, const QByteArray& type, int *vmajor, int *vminor, |
|
79 QDeclarativeType** type_return, QUrl* url_return, |
|
80 QUrl *base = 0, bool *typeRecursionDetected = 0); |
|
81 bool find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, |
|
82 QUrl* url_return, QUrl *base = 0, QString *errorString = 0); |
|
83 }; |
|
84 |
|
85 class QDeclarativeImportsPrivate { |
|
86 public: |
|
87 QDeclarativeImportsPrivate(); |
|
88 ~QDeclarativeImportsPrivate(); |
|
89 |
|
90 bool importExtension(const QString &absoluteFilePath, const QString &uri, |
|
91 QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components, |
|
92 QString *errorString); |
|
93 |
|
94 QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database); |
|
95 bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork, |
|
96 const QString& uri_arg, const QString& prefix, |
|
97 int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType, |
|
98 QDeclarativeImportDatabase *database, QString *errorString); |
|
99 bool find(const QByteArray& type, int *vmajor, int *vminor, |
|
100 QDeclarativeType** type_return, QUrl* url_return, QString *errorString); |
|
101 |
|
102 QDeclarativeImportedNamespace *findNamespace(const QString& type); |
|
103 |
|
104 QUrl base; |
|
105 int ref; |
|
106 |
|
107 QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded; |
|
108 QDeclarativeImportedNamespace unqualifiedset; |
|
109 QHash<QString,QDeclarativeImportedNamespace* > set; |
|
110 }; |
|
111 |
|
112 QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports ©) |
|
113 : d(copy.d) |
|
114 { |
|
115 ++d->ref; |
|
116 } |
|
117 |
|
118 QDeclarativeImports & |
|
119 QDeclarativeImports::operator =(const QDeclarativeImports ©) |
|
120 { |
|
121 ++copy.d->ref; |
|
122 if (--d->ref == 0) |
|
123 delete d; |
|
124 d = copy.d; |
|
125 return *this; |
|
126 } |
|
127 |
|
128 QDeclarativeImports::QDeclarativeImports() |
|
129 : d(new QDeclarativeImportsPrivate) |
|
130 { |
|
131 } |
|
132 |
|
133 QDeclarativeImports::~QDeclarativeImports() |
|
134 { |
|
135 if (--d->ref == 0) |
|
136 delete d; |
|
137 } |
|
138 |
|
139 /*! |
|
140 Sets the base URL to be used for all relative file imports added. |
|
141 */ |
|
142 void QDeclarativeImports::setBaseUrl(const QUrl& url) |
|
143 { |
|
144 d->base = url; |
|
145 } |
|
146 |
|
147 /*! |
|
148 Returns the base URL to be used for all relative file imports added. |
|
149 */ |
|
150 QUrl QDeclarativeImports::baseUrl() const |
|
151 { |
|
152 return d->base; |
|
153 } |
|
154 |
|
155 static QDeclarativeTypeNameCache * |
|
156 cacheForNamespace(QDeclarativeEngine *engine, const QDeclarativeImportedNamespace &set, |
|
157 QDeclarativeTypeNameCache *cache) |
|
158 { |
|
159 if (!cache) |
|
160 cache = new QDeclarativeTypeNameCache(engine); |
|
161 |
|
162 QList<QDeclarativeType *> types = QDeclarativeMetaType::qmlTypes(); |
|
163 |
|
164 for (int ii = 0; ii < set.uris.count(); ++ii) { |
|
165 QByteArray base = set.uris.at(ii).toUtf8() + '/'; |
|
166 int major = set.majversions.at(ii); |
|
167 int minor = set.minversions.at(ii); |
|
168 |
|
169 foreach (QDeclarativeType *type, types) { |
|
170 if (type->qmlTypeName().startsWith(base) && |
|
171 type->qmlTypeName().lastIndexOf('/') == (base.length() - 1) && |
|
172 type->availableInVersion(major,minor)) |
|
173 { |
|
174 QString name = QString::fromUtf8(type->qmlTypeName().mid(base.length())); |
|
175 |
|
176 cache->add(name, type); |
|
177 } |
|
178 } |
|
179 } |
|
180 |
|
181 return cache; |
|
182 } |
|
183 |
|
184 void QDeclarativeImports::cache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const |
|
185 { |
|
186 const QDeclarativeImportedNamespace &set = d->unqualifiedset; |
|
187 |
|
188 for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin(); |
|
189 iter != d->set.end(); ++iter) { |
|
190 |
|
191 QDeclarativeTypeNameCache::Data *d = cache->data(iter.key()); |
|
192 if (d) { |
|
193 if (!d->typeNamespace) |
|
194 cacheForNamespace(engine, *(*iter), d->typeNamespace); |
|
195 } else { |
|
196 QDeclarativeTypeNameCache *nc = cacheForNamespace(engine, *(*iter), 0); |
|
197 cache->add(iter.key(), nc); |
|
198 nc->release(); |
|
199 } |
|
200 } |
|
201 |
|
202 cacheForNamespace(engine, set, cache); |
|
203 } |
|
204 bool QDeclarativeImportedNamespace::find_helper(int i, const QByteArray& type, int *vmajor, int *vminor, |
|
205 QDeclarativeType** type_return, QUrl* url_return, |
|
206 QUrl *base, bool *typeRecursionDetected) |
|
207 { |
|
208 int vmaj = majversions.at(i); |
|
209 int vmin = minversions.at(i); |
|
210 |
|
211 QByteArray qt = uris.at(i).toUtf8(); |
|
212 qt += '/'; |
|
213 qt += type; |
|
214 |
|
215 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin); |
|
216 if (t) { |
|
217 if (vmajor) *vmajor = vmaj; |
|
218 if (vminor) *vminor = vmin; |
|
219 if (type_return) |
|
220 *type_return = t; |
|
221 return true; |
|
222 } |
|
223 |
|
224 QUrl url = QUrl(urls.at(i) + QLatin1Char('/') + QString::fromUtf8(type) + QLatin1String(".qml")); |
|
225 QDeclarativeDirComponents qmldircomponents = qmlDirComponents.at(i); |
|
226 |
|
227 bool typeWasDeclaredInQmldir = false; |
|
228 if (!qmldircomponents.isEmpty()) { |
|
229 const QString typeName = QString::fromUtf8(type); |
|
230 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) { |
|
231 if (c.typeName == typeName) { |
|
232 typeWasDeclaredInQmldir = true; |
|
233 |
|
234 // importing version -1 means import ALL versions |
|
235 if ((vmaj == -1) || (c.majorVersion < vmaj || (c.majorVersion == vmaj && vmin >= c.minorVersion))) { |
|
236 QUrl candidate = url.resolved(QUrl(c.fileName)); |
|
237 if (c.internal && base) { |
|
238 if (base->resolved(QUrl(c.fileName)) != candidate) |
|
239 continue; // failed attempt to access an internal type |
|
240 } |
|
241 if (base && *base == candidate) { |
|
242 if (typeRecursionDetected) |
|
243 *typeRecursionDetected = true; |
|
244 continue; // no recursion |
|
245 } |
|
246 if (url_return) |
|
247 *url_return = candidate; |
|
248 return true; |
|
249 } |
|
250 } |
|
251 } |
|
252 } |
|
253 |
|
254 if (!typeWasDeclaredInQmldir && !isLibrary.at(i)) { |
|
255 // XXX search non-files too! (eg. zip files, see QT-524) |
|
256 QFileInfo f(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url)); |
|
257 if (f.exists()) { |
|
258 if (base && *base == url) { // no recursion |
|
259 if (typeRecursionDetected) |
|
260 *typeRecursionDetected = true; |
|
261 } else { |
|
262 if (url_return) |
|
263 *url_return = url; |
|
264 return true; |
|
265 } |
|
266 } |
|
267 } |
|
268 return false; |
|
269 } |
|
270 |
|
271 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate() |
|
272 : ref(1) |
|
273 { |
|
274 } |
|
275 |
|
276 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate() |
|
277 { |
|
278 foreach (QDeclarativeImportedNamespace* s, set.values()) |
|
279 delete s; |
|
280 } |
|
281 |
|
282 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri, |
|
283 QDeclarativeImportDatabase *database, |
|
284 QDeclarativeDirComponents* components, QString *errorString) |
|
285 { |
|
286 QFile file(absoluteFilePath); |
|
287 QString filecontent; |
|
288 if (file.open(QFile::ReadOnly)) { |
|
289 filecontent = QString::fromUtf8(file.readAll()); |
|
290 if (qmlImportTrace()) |
|
291 qDebug() << "QDeclarativeImportDatabase::add: loaded" << absoluteFilePath; |
|
292 } else { |
|
293 if (errorString) |
|
294 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" definition \"%2\" not readable").arg(uri).arg(absoluteFilePath); |
|
295 return false; |
|
296 } |
|
297 QDir dir = QFileInfo(file).dir(); |
|
298 |
|
299 QDeclarativeDirParser qmldirParser; |
|
300 qmldirParser.setSource(filecontent); |
|
301 qmldirParser.parse(); |
|
302 |
|
303 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) { |
|
304 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath); |
|
305 |
|
306 |
|
307 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser.plugins()) { |
|
308 |
|
309 QString resolvedFilePath = database->resolvePlugin(dir, plugin.path, plugin.name); |
|
310 |
|
311 if (!resolvedFilePath.isEmpty()) { |
|
312 if (!database->importPlugin(resolvedFilePath, uri, errorString)) { |
|
313 if (errorString) |
|
314 *errorString = QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(*errorString); |
|
315 return false; |
|
316 } |
|
317 } else { |
|
318 if (errorString) |
|
319 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name); |
|
320 return false; |
|
321 } |
|
322 } |
|
323 } |
|
324 |
|
325 if (components) |
|
326 *components = qmldirParser.components(); |
|
327 |
|
328 return true; |
|
329 } |
|
330 |
|
331 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database) |
|
332 { |
|
333 QString dir = dir_arg; |
|
334 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\'))) |
|
335 dir.chop(1); |
|
336 |
|
337 QStringList paths = database->fileImportPath; |
|
338 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents. |
|
339 |
|
340 QString stableRelativePath = dir; |
|
341 foreach( QString path, paths) { |
|
342 if (dir.startsWith(path)) { |
|
343 stableRelativePath = dir.mid(path.length()+1); |
|
344 break; |
|
345 } |
|
346 } |
|
347 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.')); |
|
348 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('.')); |
|
349 return stableRelativePath; |
|
350 } |
|
351 |
|
352 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork, |
|
353 const QString& uri_arg, const QString& prefix, int vmaj, int vmin, |
|
354 QDeclarativeScriptParser::Import::Type importType, |
|
355 QDeclarativeImportDatabase *database, QString *errorString) |
|
356 { |
|
357 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork; |
|
358 QString uri = uri_arg; |
|
359 QDeclarativeImportedNamespace *s; |
|
360 if (prefix.isEmpty()) { |
|
361 s = &unqualifiedset; |
|
362 } else { |
|
363 s = set.value(prefix); |
|
364 if (!s) |
|
365 set.insert(prefix,(s=new QDeclarativeImportedNamespace)); |
|
366 } |
|
367 |
|
368 QString url = uri; |
|
369 if (importType == QDeclarativeScriptParser::Import::Library) { |
|
370 url.replace(QLatin1Char('.'), QLatin1Char('/')); |
|
371 bool found = false; |
|
372 QString dir; |
|
373 |
|
374 |
|
375 foreach (const QString &p, database->fileImportPath) { |
|
376 dir = p+QLatin1Char('/')+url; |
|
377 |
|
378 QFileInfo fi(dir+QLatin1String("/qmldir")); |
|
379 const QString absoluteFilePath = fi.absoluteFilePath(); |
|
380 |
|
381 if (fi.isFile()) { |
|
382 found = true; |
|
383 |
|
384 url = QUrl::fromLocalFile(fi.absolutePath()).toString(); |
|
385 uri = resolvedUri(dir, database); |
|
386 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) |
|
387 return false; |
|
388 break; |
|
389 } |
|
390 } |
|
391 |
|
392 if (!found) { |
|
393 found = QDeclarativeMetaType::isModule(uri.toUtf8(), vmaj, vmin); |
|
394 if (!found) { |
|
395 if (errorString) { |
|
396 bool anyversion = QDeclarativeMetaType::isModule(uri.toUtf8(), -1, -1); |
|
397 if (anyversion) |
|
398 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin); |
|
399 else |
|
400 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg); |
|
401 } |
|
402 return false; |
|
403 } |
|
404 } |
|
405 } else { |
|
406 |
|
407 if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) { |
|
408 QUrl importUrl = base.resolved(QUrl(uri + QLatin1String("/qmldir"))); |
|
409 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl); |
|
410 if (!localFileOrQrc.isEmpty()) { |
|
411 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))); |
|
412 if (dir.isEmpty() || !QDir().exists(dir)) { |
|
413 if (errorString) |
|
414 *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg); |
|
415 return false; // local import dirs must exist |
|
416 } |
|
417 uri = resolvedUri(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))), database); |
|
418 if (uri.endsWith(QLatin1Char('/'))) |
|
419 uri.chop(1); |
|
420 if (QFile::exists(localFileOrQrc)) { |
|
421 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errorString)) |
|
422 return false; |
|
423 } |
|
424 } else { |
|
425 if (prefix.isEmpty()) { |
|
426 // directory must at least exist for valid import |
|
427 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))); |
|
428 if (localFileOrQrc.isEmpty() || !QDir().exists(localFileOrQrc)) { |
|
429 if (errorString) { |
|
430 if (localFileOrQrc.isEmpty()) |
|
431 *errorString = QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri); |
|
432 else |
|
433 *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri); |
|
434 } |
|
435 return false; |
|
436 } |
|
437 } |
|
438 } |
|
439 } |
|
440 |
|
441 url = base.resolved(QUrl(url)).toString(); |
|
442 if (url.endsWith(QLatin1Char('/'))) |
|
443 url.chop(1); |
|
444 } |
|
445 |
|
446 if (vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) { |
|
447 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin(); |
|
448 for (; it != qmldircomponents.end(); ++it) { |
|
449 if (it->majorVersion > vmaj || (it->majorVersion == vmaj && it->minorVersion >= vmin)) |
|
450 break; |
|
451 } |
|
452 if (it == qmldircomponents.end()) { |
|
453 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin); |
|
454 return false; |
|
455 } |
|
456 } |
|
457 |
|
458 s->uris.prepend(uri); |
|
459 s->urls.prepend(url); |
|
460 s->majversions.prepend(vmaj); |
|
461 s->minversions.prepend(vmin); |
|
462 s->isLibrary.prepend(importType == QDeclarativeScriptParser::Import::Library); |
|
463 s->qmlDirComponents.prepend(qmldircomponents); |
|
464 return true; |
|
465 } |
|
466 |
|
467 bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, |
|
468 QUrl* url_return, QString *errorString) |
|
469 { |
|
470 QDeclarativeImportedNamespace *s = 0; |
|
471 int slash = type.indexOf('/'); |
|
472 if (slash >= 0) { |
|
473 QString namespaceName = QString::fromUtf8(type.left(slash)); |
|
474 s = set.value(namespaceName); |
|
475 if (!s) { |
|
476 if (errorString) |
|
477 *errorString = QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName); |
|
478 return false; |
|
479 } |
|
480 int nslash = type.indexOf('/',slash+1); |
|
481 if (nslash > 0) { |
|
482 if (errorString) |
|
483 *errorString = QDeclarativeImportDatabase::tr("- nested namespaces not allowed"); |
|
484 return false; |
|
485 } |
|
486 } else { |
|
487 s = &unqualifiedset; |
|
488 } |
|
489 QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower) |
|
490 if (s) { |
|
491 if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errorString)) |
|
492 return true; |
|
493 if (s->urls.count() == 1 && !s->isLibrary[0] && url_return && s != &unqualifiedset) { |
|
494 // qualified, and only 1 url |
|
495 *url_return = QUrl(s->urls[0]+QLatin1Char('/')).resolved(QUrl(QString::fromUtf8(unqualifiedtype) + QLatin1String(".qml"))); |
|
496 return true; |
|
497 } |
|
498 } |
|
499 |
|
500 return false; |
|
501 } |
|
502 |
|
503 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type) |
|
504 { |
|
505 return set.value(type); |
|
506 } |
|
507 |
|
508 bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, |
|
509 QUrl* url_return, QUrl *base, QString *errorString) |
|
510 { |
|
511 bool typeRecursionDetected = false; |
|
512 for (int i=0; i<urls.count(); ++i) { |
|
513 if (find_helper(i, type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) { |
|
514 if (qmlCheckTypes()) { |
|
515 // check for type clashes |
|
516 for (int j = i+1; j<urls.count(); ++j) { |
|
517 if (find_helper(j, type, vmajor, vminor, 0, 0, base)) { |
|
518 if (errorString) { |
|
519 QString u1 = urls.at(i); |
|
520 QString u2 = urls.at(j); |
|
521 if (base) { |
|
522 QString b = base->toString(); |
|
523 int slash = b.lastIndexOf(QLatin1Char('/')); |
|
524 if (slash >= 0) { |
|
525 b = b.left(slash+1); |
|
526 QString l = b.left(slash); |
|
527 if (u1.startsWith(b)) |
|
528 u1 = u1.mid(b.count()); |
|
529 else if (u1 == l) |
|
530 u1 = QDeclarativeImportDatabase::tr("local directory"); |
|
531 if (u2.startsWith(b)) |
|
532 u2 = u2.mid(b.count()); |
|
533 else if (u2 == l) |
|
534 u2 = QDeclarativeImportDatabase::tr("local directory"); |
|
535 } |
|
536 } |
|
537 |
|
538 if (u1 != u2) |
|
539 *errorString |
|
540 = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2") |
|
541 .arg(u1).arg(u2); |
|
542 else |
|
543 *errorString |
|
544 = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5") |
|
545 .arg(u1) |
|
546 .arg(majversions.at(i)).arg(minversions.at(i)) |
|
547 .arg(majversions.at(j)).arg(minversions.at(j)); |
|
548 } |
|
549 return false; |
|
550 } |
|
551 } |
|
552 } |
|
553 return true; |
|
554 } |
|
555 } |
|
556 if (errorString) { |
|
557 if (typeRecursionDetected) |
|
558 *errorString = QDeclarativeImportDatabase::tr("is instantiated recursively"); |
|
559 else |
|
560 *errorString = QDeclarativeImportDatabase::tr("is not a type"); |
|
561 } |
|
562 return false; |
|
563 } |
|
564 |
|
565 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e) |
|
566 : engine(e) |
|
567 { |
|
568 filePluginPath << QLatin1String("."); |
|
569 |
|
570 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath |
|
571 |
|
572 addImportPath(QLibraryInfo::location(QLibraryInfo::ImportsPath)); |
|
573 |
|
574 // env import paths |
|
575 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH"); |
|
576 if (!envImportPath.isEmpty()) { |
|
577 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) |
|
578 QLatin1Char pathSep(';'); |
|
579 #else |
|
580 QLatin1Char pathSep(':'); |
|
581 #endif |
|
582 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts); |
|
583 for (int ii = paths.count() - 1; ii >= 0; --ii) |
|
584 addImportPath(paths.at(ii)); |
|
585 } |
|
586 |
|
587 addImportPath(QCoreApplication::applicationDirPath()); |
|
588 } |
|
589 |
|
590 QDeclarativeImportDatabase::~QDeclarativeImportDatabase() |
|
591 { |
|
592 } |
|
593 |
|
594 /*! |
|
595 \internal |
|
596 |
|
597 Adds information to \a imports such that subsequent calls to resolveType() |
|
598 will resolve types qualified by \a prefix by considering types found at the given \a uri. |
|
599 |
|
600 The uri is either a directory (if importType is FileImport), or a URI resolved using paths |
|
601 added via addImportPath() (if importType is LibraryImport). |
|
602 |
|
603 The \a prefix may be empty, in which case the import location is considered for |
|
604 unqualified types. |
|
605 |
|
606 The base URL must already have been set with Import::setBaseUrl(). |
|
607 */ |
|
608 bool QDeclarativeImportDatabase::addToImport(QDeclarativeImports* imports, |
|
609 const QDeclarativeDirComponents &qmldircomponentsnetwork, |
|
610 const QString& uri, const QString& prefix, int vmaj, int vmin, |
|
611 QDeclarativeScriptParser::Import::Type importType, |
|
612 QString *errorString) |
|
613 { |
|
614 if (qmlImportTrace()) |
|
615 qDebug().nospace() << "QDeclarativeImportDatabase::addToImport " << imports << " " << uri << " " |
|
616 << vmaj << '.' << vmin << " " |
|
617 << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File") |
|
618 << " as " << prefix; |
|
619 |
|
620 bool ok = imports->d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, this, errorString); |
|
621 return ok; |
|
622 } |
|
623 |
|
624 /*! |
|
625 \internal |
|
626 |
|
627 Using the given \a imports, the given (namespace qualified) \a type is resolved to either |
|
628 a QDeclarativeImportedNamespace stored at \a ns_return, |
|
629 a QDeclarativeType stored at \a type_return, or |
|
630 a component located at \a url_return. |
|
631 |
|
632 If any return pointer is 0, the corresponding search is not done. |
|
633 |
|
634 \sa addToImport() |
|
635 */ |
|
636 bool QDeclarativeImportDatabase::resolveType(const QDeclarativeImports& imports, const QByteArray& type, |
|
637 QDeclarativeType** type_return, QUrl* url_return, int *vmaj, int *vmin, |
|
638 QDeclarativeImportedNamespace** ns_return, QString *errorString) const |
|
639 { |
|
640 QDeclarativeImportedNamespace* ns = imports.d->findNamespace(QString::fromUtf8(type)); |
|
641 if (ns) { |
|
642 if (ns_return) |
|
643 *ns_return = ns; |
|
644 return true; |
|
645 } |
|
646 if (type_return || url_return) { |
|
647 if (imports.d->find(type,vmaj,vmin,type_return,url_return, errorString)) { |
|
648 if (qmlImportTrace()) { |
|
649 if (type_return && *type_return && url_return && !url_return->isEmpty()) |
|
650 qDebug() << "QDeclarativeImportDatabase::resolveType" << type << '=' << (*type_return)->typeName() << *url_return; |
|
651 if (type_return && *type_return) |
|
652 qDebug() << "QDeclarativeImportDatabase::resolveType" << type << '=' << (*type_return)->typeName(); |
|
653 if (url_return && !url_return->isEmpty()) |
|
654 qDebug() << "QDeclarativeImportDatabase::resolveType" << type << '=' << *url_return; |
|
655 } |
|
656 return true; |
|
657 } |
|
658 } |
|
659 return false; |
|
660 } |
|
661 |
|
662 /*! |
|
663 \internal |
|
664 |
|
665 Searching \e only in the namespace \a ns (previously returned in a call to |
|
666 resolveType(), \a type is found and returned to either |
|
667 a QDeclarativeType stored at \a type_return, or |
|
668 a component located at \a url_return. |
|
669 |
|
670 If either return pointer is 0, the corresponding search is not done. |
|
671 */ |
|
672 bool QDeclarativeImportDatabase::resolveTypeInNamespace(QDeclarativeImportedNamespace* ns, const QByteArray& type, |
|
673 QDeclarativeType** type_return, QUrl* url_return, |
|
674 int *vmaj, int *vmin) const |
|
675 { |
|
676 return ns->find(type,vmaj,vmin,type_return,url_return); |
|
677 } |
|
678 |
|
679 /*! |
|
680 \internal |
|
681 |
|
682 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix. |
|
683 The \a prefix must contain the dot. |
|
684 |
|
685 \a qmldirPath is the location of the qmldir file. |
|
686 */ |
|
687 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, |
|
688 const QString &baseName, const QStringList &suffixes, |
|
689 const QString &prefix) |
|
690 { |
|
691 QStringList searchPaths = filePluginPath; |
|
692 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath); |
|
693 if (!qmldirPluginPathIsRelative) |
|
694 searchPaths.prepend(qmldirPluginPath); |
|
695 |
|
696 foreach (const QString &pluginPath, searchPaths) { |
|
697 |
|
698 QString resolvedPath; |
|
699 |
|
700 if (pluginPath == QLatin1String(".")) { |
|
701 if (qmldirPluginPathIsRelative) |
|
702 resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath); |
|
703 else |
|
704 resolvedPath = qmldirPath.absolutePath(); |
|
705 } else { |
|
706 resolvedPath = pluginPath; |
|
707 } |
|
708 |
|
709 // hack for resources, should probably go away |
|
710 if (resolvedPath.startsWith(QLatin1Char(':'))) |
|
711 resolvedPath = QCoreApplication::applicationDirPath(); |
|
712 |
|
713 QDir dir(resolvedPath); |
|
714 foreach (const QString &suffix, suffixes) { |
|
715 QString pluginFileName = prefix; |
|
716 |
|
717 pluginFileName += baseName; |
|
718 pluginFileName += suffix; |
|
719 |
|
720 QFileInfo fileInfo(dir, pluginFileName); |
|
721 |
|
722 if (fileInfo.exists()) |
|
723 return fileInfo.absoluteFilePath(); |
|
724 } |
|
725 } |
|
726 |
|
727 if (qmlImportTrace()) |
|
728 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName |
|
729 << "in" << qmldirPath.absolutePath(); |
|
730 |
|
731 return QString(); |
|
732 } |
|
733 |
|
734 /*! |
|
735 \internal |
|
736 |
|
737 Returns the result of the merge of \a baseName with \a dir and the platform suffix. |
|
738 |
|
739 \table |
|
740 \header \i Platform \i Valid suffixes |
|
741 \row \i Windows \i \c .dll |
|
742 \row \i Unix/Linux \i \c .so |
|
743 \row \i AIX \i \c .a |
|
744 \row \i HP-UX \i \c .sl, \c .so (HP-UXi) |
|
745 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so |
|
746 \row \i Symbian \i \c .dll |
|
747 \endtable |
|
748 |
|
749 Version number on unix are ignored. |
|
750 */ |
|
751 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, |
|
752 const QString &baseName) |
|
753 { |
|
754 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) |
|
755 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, |
|
756 QStringList() |
|
757 # ifdef QT_DEBUG |
|
758 << QLatin1String("d.dll") // try a qmake-style debug build first |
|
759 # endif |
|
760 << QLatin1String(".dll")); |
|
761 #elif defined(Q_OS_SYMBIAN) |
|
762 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, |
|
763 QStringList() |
|
764 << QLatin1String(".dll") |
|
765 << QLatin1String(".qtplugin")); |
|
766 #else |
|
767 |
|
768 # if defined(Q_OS_DARWIN) |
|
769 |
|
770 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, |
|
771 QStringList() |
|
772 # ifdef QT_DEBUG |
|
773 << QLatin1String("_debug.dylib") // try a qmake-style debug build first |
|
774 << QLatin1String(".dylib") |
|
775 # else |
|
776 << QLatin1String(".dylib") |
|
777 << QLatin1String("_debug.dylib") // try a qmake-style debug build after |
|
778 # endif |
|
779 << QLatin1String(".so") |
|
780 << QLatin1String(".bundle"), |
|
781 QLatin1String("lib")); |
|
782 # else // Generic Unix |
|
783 QStringList validSuffixList; |
|
784 |
|
785 # if defined(Q_OS_HPUX) |
|
786 /* |
|
787 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF": |
|
788 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit), |
|
789 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix." |
|
790 */ |
|
791 validSuffixList << QLatin1String(".sl"); |
|
792 # if defined __ia64 |
|
793 validSuffixList << QLatin1String(".so"); |
|
794 # endif |
|
795 # elif defined(Q_OS_AIX) |
|
796 validSuffixList << QLatin1String(".a") << QLatin1String(".so"); |
|
797 # elif defined(Q_OS_UNIX) |
|
798 validSuffixList << QLatin1String(".so"); |
|
799 # endif |
|
800 |
|
801 // Examples of valid library names: |
|
802 // libfoo.so |
|
803 |
|
804 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib")); |
|
805 # endif |
|
806 |
|
807 #endif |
|
808 } |
|
809 |
|
810 QStringList QDeclarativeImportDatabase::pluginPathList() const |
|
811 { |
|
812 return filePluginPath; |
|
813 } |
|
814 |
|
815 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths) |
|
816 { |
|
817 filePluginPath = paths; |
|
818 } |
|
819 |
|
820 void QDeclarativeImportDatabase::addPluginPath(const QString& path) |
|
821 { |
|
822 if (qmlImportTrace()) |
|
823 qDebug() << "QDeclarativeImportDatabase::addPluginPath" << path; |
|
824 |
|
825 QUrl url = QUrl(path); |
|
826 if (url.isRelative() || url.scheme() == QString::fromLocal8Bit("file")) { |
|
827 QDir dir = QDir(path); |
|
828 filePluginPath.prepend(dir.canonicalPath()); |
|
829 } else { |
|
830 filePluginPath.prepend(path); |
|
831 } |
|
832 } |
|
833 |
|
834 void QDeclarativeImportDatabase::addImportPath(const QString& path) |
|
835 { |
|
836 if (qmlImportTrace()) |
|
837 qDebug() << "QDeclarativeImportDatabase::addImportPath" << path; |
|
838 |
|
839 if (path.isEmpty()) |
|
840 return; |
|
841 |
|
842 QUrl url = QUrl(path); |
|
843 QString cPath; |
|
844 |
|
845 if (url.isRelative() || url.scheme() == QString::fromLocal8Bit("file")) { |
|
846 QDir dir = QDir(path); |
|
847 cPath = dir.canonicalPath(); |
|
848 } else { |
|
849 cPath = path; |
|
850 } |
|
851 |
|
852 if (!cPath.isEmpty() |
|
853 && !fileImportPath.contains(cPath)) |
|
854 fileImportPath.prepend(cPath); |
|
855 } |
|
856 |
|
857 QStringList QDeclarativeImportDatabase::importPathList() const |
|
858 { |
|
859 return fileImportPath; |
|
860 } |
|
861 |
|
862 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths) |
|
863 { |
|
864 fileImportPath = paths; |
|
865 } |
|
866 |
|
867 |
|
868 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QString *errorString) |
|
869 { |
|
870 if (qmlImportTrace()) |
|
871 qDebug() << "QDeclarativeImportDatabase::importPlugin" << uri << "from" << filePath; |
|
872 |
|
873 QFileInfo fileInfo(filePath); |
|
874 const QString absoluteFilePath = fileInfo.absoluteFilePath(); |
|
875 |
|
876 bool engineInitialized = initializedPlugins.contains(absoluteFilePath); |
|
877 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath); |
|
878 |
|
879 if (typesRegistered) { |
|
880 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri, |
|
881 "QDeclarativeImportDatabase::importExtension", |
|
882 "Internal error: Plugin imported previously with different uri"); |
|
883 } |
|
884 |
|
885 if (!engineInitialized || !typesRegistered) { |
|
886 QPluginLoader loader(absoluteFilePath); |
|
887 |
|
888 if (!loader.load()) { |
|
889 if (errorString) |
|
890 *errorString = loader.errorString(); |
|
891 return false; |
|
892 } |
|
893 |
|
894 if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) { |
|
895 |
|
896 const QByteArray bytes = uri.toUtf8(); |
|
897 const char *moduleId = bytes.constData(); |
|
898 if (!typesRegistered) { |
|
899 |
|
900 // ### this code should probably be protected with a mutex. |
|
901 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri); |
|
902 iface->registerTypes(moduleId); |
|
903 } |
|
904 if (!engineInitialized) { |
|
905 // things on the engine (eg. adding new global objects) have to be done for every engine. |
|
906 |
|
907 // protect against double initialization |
|
908 initializedPlugins.insert(absoluteFilePath); |
|
909 iface->initializeEngine(engine, moduleId); |
|
910 } |
|
911 } else { |
|
912 if (errorString) |
|
913 *errorString = loader.errorString(); |
|
914 return false; |
|
915 } |
|
916 } |
|
917 |
|
918 return true; |
|
919 } |
|
920 |
|
921 |
|
922 QT_END_NAMESPACE |