1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativexmllistmodel_p.h"
44 #include <qdeclarativecontext.h>
45 #include <private/qdeclarativeengine_p.h>
48 #include <QStringList>
52 #include <QXmlResultItems>
53 #include <QXmlNodeModelIndex>
55 #include <QNetworkRequest>
56 #include <QNetworkReply>
60 #include <private/qobject_p.h>
62 Q_DECLARE_METATYPE(QDeclarativeXmlQueryResult)
67 typedef QPair<int, int> QDeclarativeXmlListRange;
69 #define XMLLISTMODEL_CLEAR_ID 0
72 \qmlclass XmlRole QDeclarativeXmlListModelRole
73 \inqmlmodule QtQuick 2
74 \ingroup qml-working-with-data
75 \brief The XmlRole element allows you to specify a role for an XmlListModel.
81 \qmlproperty string QtQuick2::XmlRole::name
83 The name for the role. This name is used to access the model data for this role.
85 For example, the following model has a role named "title", which can be accessed
86 from the view's delegate:
94 query: "title/string()"
102 delegate: Text { text: title }
108 \qmlproperty string QtQuick2::XmlRole::query
109 The relative XPath expression query for this role. The query must be relative; it cannot start
112 For example, if there is an XML document like this:
114 \quotefile doc/src/snippets/declarative/xmlrole.xml
115 Here are some valid XPath expressions for XmlRole queries on this document:
117 \snippet doc/src/snippets/declarative/xmlrole.qml 0
119 \snippet doc/src/snippets/declarative/xmlrole.qml 1
121 See the \l{http://www.w3.org/TR/xpath20/}{W3C XPath 2.0 specification} for more information.
125 \qmlproperty bool QtQuick2::XmlRole::isKey
126 Defines whether this is a key role.
127 Key roles are used to to determine whether a set of values should
128 be updated or added to the XML list model when XmlListModel::reload()
140 QStringList roleQueries;
141 QList<void*> roleQueryErrorId; // the ptr to send back if there is an error
142 QStringList keyRoleQueries;
143 QStringList keyRoleResultsCache;
148 class QDeclarativeXmlQueryEngine;
149 class QDeclarativeXmlQueryThreadObject : public QObject
153 QDeclarativeXmlQueryThreadObject(QDeclarativeXmlQueryEngine *);
156 virtual bool event(QEvent *e);
159 QDeclarativeXmlQueryEngine *m_queryEngine;
163 class QDeclarativeXmlQueryEngine : public QThread
167 QDeclarativeXmlQueryEngine(QDeclarativeEngine *eng);
168 ~QDeclarativeXmlQueryEngine();
170 int doQuery(QString query, QString namespaces, QByteArray data, QList<QDeclarativeXmlListModelRole *>* roleObjects, QStringList keyRoleResultsCache);
175 static QDeclarativeXmlQueryEngine *instance(QDeclarativeEngine *engine);
178 void queryCompleted(const QDeclarativeXmlQueryResult &);
179 void error(void*, const QString&);
185 void processQuery(XmlQueryJob *job);
186 void doQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult);
187 void doSubQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult);
188 void getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const;
189 void addIndexToRangeList(QList<QDeclarativeXmlListRange> *ranges, int index) const;
192 QDeclarativeXmlQueryThreadObject *m_threadObject;
193 QList<XmlQueryJob> m_jobs;
194 QSet<int> m_cancelledJobs;
195 QAtomicInt m_queryIds;
197 QDeclarativeEngine *m_engine;
198 QObject *m_eventLoopQuitHack;
200 static QHash<QDeclarativeEngine *,QDeclarativeXmlQueryEngine*> queryEngines;
201 static QMutex queryEnginesMutex;
203 QHash<QDeclarativeEngine *,QDeclarativeXmlQueryEngine*> QDeclarativeXmlQueryEngine::queryEngines;
204 QMutex QDeclarativeXmlQueryEngine::queryEnginesMutex;
207 QDeclarativeXmlQueryThreadObject::QDeclarativeXmlQueryThreadObject(QDeclarativeXmlQueryEngine *e)
212 void QDeclarativeXmlQueryThreadObject::processJobs()
214 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
217 bool QDeclarativeXmlQueryThreadObject::event(QEvent *e)
219 if (e->type() == QEvent::User) {
220 m_queryEngine->processJobs();
223 return QObject::event(e);
229 QDeclarativeXmlQueryEngine::QDeclarativeXmlQueryEngine(QDeclarativeEngine *eng)
230 : QThread(eng), m_threadObject(0), m_queryIds(XMLLISTMODEL_CLEAR_ID + 1), m_engine(eng), m_eventLoopQuitHack(0)
232 qRegisterMetaType<QDeclarativeXmlQueryResult>("QDeclarativeXmlQueryResult");
234 m_eventLoopQuitHack = new QObject;
235 m_eventLoopQuitHack->moveToThread(this);
236 connect(m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
237 start(QThread::IdlePriority);
240 QDeclarativeXmlQueryEngine::~QDeclarativeXmlQueryEngine()
242 queryEnginesMutex.lock();
243 queryEngines.remove(m_engine);
244 queryEnginesMutex.unlock();
246 m_eventLoopQuitHack->deleteLater();
250 int QDeclarativeXmlQueryEngine::doQuery(QString query, QString namespaces, QByteArray data, QList<QDeclarativeXmlListModelRole *>* roleObjects, QStringList keyRoleResultsCache) {
252 QMutexLocker m1(&m_mutex);
254 if (m_queryIds.load() <= 0)
259 job.queryId = m_queryIds.load();
261 job.query = QLatin1String("doc($src)") + query;
262 job.namespaces = namespaces;
263 job.keyRoleResultsCache = keyRoleResultsCache;
265 for (int i=0; i<roleObjects->count(); i++) {
266 if (!roleObjects->at(i)->isValid()) {
267 job.roleQueries << QString();
270 job.roleQueries << roleObjects->at(i)->query();
271 job.roleQueryErrorId << static_cast<void*>(roleObjects->at(i));
272 if (roleObjects->at(i)->isKey())
273 job.keyRoleQueries << job.roleQueries.last();
277 QMutexLocker ml(&m_mutex);
280 m_threadObject->processJobs();
286 void QDeclarativeXmlQueryEngine::abort(int id)
288 QMutexLocker ml(&m_mutex);
290 m_cancelledJobs.insert(id);
293 void QDeclarativeXmlQueryEngine::run()
296 m_threadObject = new QDeclarativeXmlQueryThreadObject(this);
302 delete m_threadObject;
306 void QDeclarativeXmlQueryEngine::processJobs()
308 QMutexLocker locker(&m_mutex);
311 if (m_jobs.isEmpty())
314 XmlQueryJob currentJob = m_jobs.takeLast();
315 while (m_cancelledJobs.remove(currentJob.queryId)) {
316 if (m_jobs.isEmpty())
318 currentJob = m_jobs.takeLast();
322 processQuery(¤tJob);
327 QDeclarativeXmlQueryEngine *QDeclarativeXmlQueryEngine::instance(QDeclarativeEngine *engine)
329 queryEnginesMutex.lock();
330 QDeclarativeXmlQueryEngine *queryEng = queryEngines.value(engine);
332 queryEng = new QDeclarativeXmlQueryEngine(engine);
333 queryEngines.insert(engine, queryEng);
335 queryEnginesMutex.unlock();
340 void QDeclarativeXmlQueryEngine::processQuery(XmlQueryJob *job)
342 QDeclarativeXmlQueryResult result;
343 result.queryId = job->queryId;
344 doQueryJob(job, &result);
345 doSubQueryJob(job, &result);
348 QMutexLocker ml(&m_mutex);
349 if (m_cancelledJobs.contains(job->queryId)) {
350 m_cancelledJobs.remove(job->queryId);
352 emit queryCompleted(result);
357 void QDeclarativeXmlQueryEngine::doQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult)
359 Q_ASSERT(currentJob->queryId != -1);
363 QBuffer buffer(¤tJob->data);
364 buffer.open(QIODevice::ReadOnly);
365 query.bindVariable(QLatin1String("src"), &buffer);
366 query.setQuery(currentJob->namespaces + currentJob->query);
367 query.evaluateTo(&r);
369 //always need a single root element
370 QByteArray xml = "<dummy:items xmlns:dummy=\"http://qtsotware.com/dummy\">\n" + r.toUtf8() + "</dummy:items>";
372 b.open(QIODevice::ReadOnly);
374 QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + currentJob->namespaces;
375 QString prefix = QLatin1String("doc($inputDocument)/dummy:items") +
376 currentJob->query.mid(currentJob->query.lastIndexOf(QLatin1Char('/')));
378 //figure out how many items we are dealing with
381 QXmlResultItems result;
382 QXmlQuery countquery;
383 countquery.bindVariable(QLatin1String("inputDocument"), &b);
384 countquery.setQuery(namespaces + QLatin1String("count(") + prefix + QLatin1Char(')'));
385 countquery.evaluateTo(&result);
386 QXmlItem item(result.next());
387 if (item.isAtomicValue())
388 count = item.toAtomicValue().toInt();
391 currentJob->data = xml;
392 currentJob->prefix = namespaces + prefix + QLatin1Char('/');
393 currentResult->size = (count > 0 ? count : 0);
396 void QDeclarativeXmlQueryEngine::getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const
398 const QStringList &keysQueries = currentJob.keyRoleQueries;
400 if (keysQueries.count() == 1)
401 keysQuery = currentJob.prefix + keysQueries[0];
402 else if (keysQueries.count() > 1)
403 keysQuery = currentJob.prefix + QLatin1String("concat(") + keysQueries.join(QLatin1String(",")) + QLatin1String(")");
405 if (!keysQuery.isEmpty()) {
406 query->setQuery(keysQuery);
407 QXmlResultItems resultItems;
408 query->evaluateTo(&resultItems);
409 QXmlItem item(resultItems.next());
410 while (!item.isNull()) {
411 values->append(item.toAtomicValue().toString());
412 item = resultItems.next();
417 void QDeclarativeXmlQueryEngine::addIndexToRangeList(QList<QDeclarativeXmlListRange> *ranges, int index) const {
418 if (ranges->isEmpty())
419 ranges->append(qMakePair(index, 1));
420 else if (ranges->last().first + ranges->last().second == index)
421 ranges->last().second += 1;
423 ranges->append(qMakePair(index, 1));
426 void QDeclarativeXmlQueryEngine::doSubQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult)
428 Q_ASSERT(currentJob->queryId != -1);
430 QBuffer b(¤tJob->data);
431 b.open(QIODevice::ReadOnly);
434 subquery.bindVariable(QLatin1String("inputDocument"), &b);
436 QStringList keyRoleResults;
437 getValuesOfKeyRoles(*currentJob, &keyRoleResults, &subquery);
439 // See if any values of key roles have been inserted or removed.
441 if (currentJob->keyRoleResultsCache.isEmpty()) {
442 currentResult->inserted << qMakePair(0, currentResult->size);
444 if (keyRoleResults != currentJob->keyRoleResultsCache) {
446 for (int i=0; i<currentJob->keyRoleResultsCache.count(); i++) {
447 if (!keyRoleResults.contains(currentJob->keyRoleResultsCache[i]))
448 addIndexToRangeList(¤tResult->removed, i);
450 temp << currentJob->keyRoleResultsCache[i];
452 for (int i=0; i<keyRoleResults.count(); i++) {
453 if (temp.count() == i || keyRoleResults[i] != temp[i]) {
454 temp.insert(i, keyRoleResults[i]);
455 addIndexToRangeList(¤tResult->inserted, i);
460 currentResult->keyRoleResultsCache = keyRoleResults;
462 // Get the new values for each role.
463 //### we might be able to condense even further (query for everything in one go)
464 const QStringList &queries = currentJob->roleQueries;
465 for (int i = 0; i < queries.size(); ++i) {
466 QList<QVariant> resultList;
467 if (!queries[i].isEmpty()) {
468 subquery.setQuery(currentJob->prefix + QLatin1String("(let $v := string(") + queries[i] + QLatin1String(") return if ($v) then ") + queries[i] + QLatin1String(" else \"\")"));
469 if (subquery.isValid()) {
470 QXmlResultItems resultItems;
471 subquery.evaluateTo(&resultItems);
472 QXmlItem item(resultItems.next());
473 while (!item.isNull()) {
474 resultList << item.toAtomicValue(); //### we used to trim strings
475 item = resultItems.next();
478 emit error(currentJob->roleQueryErrorId.at(i), queries[i]);
481 //### should warn here if things have gone wrong.
482 while (resultList.count() < currentResult->size)
483 resultList << QVariant();
484 currentResult->data << resultList;
488 //this method is much slower, but works better for incremental loading
489 /*for (int j = 0; j < m_size; ++j) {
490 QList<QVariant> resultList;
491 for (int i = 0; i < m_roleObjects->size(); ++i) {
492 QDeclarativeXmlListModelRole *role = m_roleObjects->at(i);
493 subquery.setQuery(m_prefix.arg(j+1) + role->query());
494 if (role->isStringList()) {
496 subquery.evaluateTo(&data);
497 resultList << QVariant(data);
501 subquery.evaluateTo(&s);
502 if (role->isCData()) {
504 s.replace(QLatin1String("<"), QLatin1String("<"));
505 s.replace(QLatin1String(">"), QLatin1String(">"));
506 s.replace(QLatin1String("&"), QLatin1String("&"));
508 resultList << s.trimmed();
513 m_modelData << resultList;
517 class QDeclarativeXmlListModelPrivate : public QObjectPrivate
519 Q_DECLARE_PUBLIC(QDeclarativeXmlListModel)
521 QDeclarativeXmlListModelPrivate()
522 : isComponentComplete(true), size(-1), highestRole(Qt::UserRole)
523 , reply(0), status(QDeclarativeXmlListModel::Null), progress(0.0)
524 , queryId(-1), roleObjects(), redirectCount(0) {}
527 void notifyQueryStarted(bool remoteSource) {
528 Q_Q(QDeclarativeXmlListModel);
529 progress = remoteSource ? 0.0 : 1.0;
530 status = QDeclarativeXmlListModel::Loading;
532 emit q->progressChanged(progress);
533 emit q->statusChanged(status);
537 Q_Q(QDeclarativeXmlListModel);
539 QObject::disconnect(reply, 0, q, 0);
540 reply->deleteLater();
545 bool isComponentComplete;
552 QStringList roleNames;
555 QNetworkReply *reply;
556 QDeclarativeXmlListModel::Status status;
560 QStringList keyRoleResultsCache;
561 QList<QDeclarativeXmlListModelRole *> roleObjects;
563 static void append_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list, QDeclarativeXmlListModelRole *role);
564 static void clear_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list);
565 QList<QList<QVariant> > data;
570 void QDeclarativeXmlListModelPrivate::append_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list, QDeclarativeXmlListModelRole *role)
572 QDeclarativeXmlListModel *_this = qobject_cast<QDeclarativeXmlListModel *>(list->object);
574 int i = _this->d_func()->roleObjects.count();
575 _this->d_func()->roleObjects.append(role);
576 if (_this->d_func()->roleNames.contains(role->name())) {
577 qmlInfo(role) << QObject::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name());
580 _this->d_func()->roles.insert(i, _this->d_func()->highestRole);
581 _this->d_func()->roleNames.insert(i, role->name());
582 ++_this->d_func()->highestRole;
586 //### clear needs to invalidate any cached data (in data table) as well
587 // (and the model should emit the appropriate signals)
588 void QDeclarativeXmlListModelPrivate::clear_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list)
590 QDeclarativeXmlListModel *_this = static_cast<QDeclarativeXmlListModel *>(list->object);
591 _this->d_func()->roles.clear();
592 _this->d_func()->roleNames.clear();
593 _this->d_func()->roleObjects.clear();
597 \qmlclass XmlListModel QDeclarativeXmlListModel
598 \inqmlmodule QtQuick 2
599 \ingroup qml-working-with-data
600 \brief The XmlListModel element is used to specify a read-only model using XPath expressions.
602 XmlListModel is used to create a read-only model from XML data. It can be used as a data source
603 for view elements (such as ListView, PathView, GridView) and other elements that interact with model
604 data (such as \l Repeater).
606 For example, if there is a XML document at http://www.mysite.com/feed.xml like this:
609 <?xml version="1.0" encoding="utf-8"?>
614 <title>A blog post</title>
615 <pubDate>Sat, 07 Sep 2010 10:00:01 GMT</pubDate>
618 <title>Another blog post</title>
619 <pubDate>Sat, 07 Sep 2010 15:35:01 GMT</pubDate>
625 A XmlListModel could create a model from this data, like this:
632 source: "http://www.mysite.com/feed.xml"
633 query: "/rss/channel/item"
635 XmlRole { name: "title"; query: "title/string()" }
636 XmlRole { name: "pubDate"; query: "pubDate/string()" }
640 The \l {XmlListModel::query}{query} value of "/rss/channel/item" specifies that the XmlListModel should generate
641 a model item for each \c <item> in the XML document.
643 The XmlRole objects define the
644 model item attributes. Here, each model item will have \c title and \c pubDate
645 attributes that match the \c title and \c pubDate values of its corresponding \c <item>.
646 (See \l XmlRole::query for more examples of valid XPath expressions for XmlRole.)
648 The model could be used in a ListView, like this:
652 width: 180; height: 300
654 delegate: Text { text: title + ": " + pubDate }
658 \image qml-xmllistmodel-example.png
660 The XmlListModel data is loaded asynchronously, and \l status
661 is set to \c XmlListModel.Ready when loading is complete.
662 Note this means when XmlListModel is used for a view, the view is not
663 populated until the model is loaded.
666 \section2 Using key XML roles
668 You can define certain roles as "keys" so that when reload() is called,
669 the model will only add and refresh data that contains new values for
672 For example, if above role for "pubDate" was defined like this instead:
675 XmlRole { name: "pubDate"; query: "pubDate/string()"; isKey: true }
678 Then when reload() is called, the model will only add and reload
679 items with a "pubDate" value that is not already
680 present in the model.
682 This is useful when displaying the contents of XML documents that
683 are incrementally updated (such as RSS feeds) to avoid repainting the
684 entire contents of a model in a view.
686 If multiple key roles are specified, the model only adds and reload items
687 with a combined value of all key roles that is not already present in
693 QDeclarativeXmlListModel::QDeclarativeXmlListModel(QObject *parent)
694 : QListModelInterface(*(new QDeclarativeXmlListModelPrivate), parent)
698 QDeclarativeXmlListModel::~QDeclarativeXmlListModel()
703 \qmlproperty list<XmlRole> QtQuick2::XmlListModel::roles
705 The roles to make available for this model.
707 QDeclarativeListProperty<QDeclarativeXmlListModelRole> QDeclarativeXmlListModel::roleObjects()
709 Q_D(QDeclarativeXmlListModel);
710 QDeclarativeListProperty<QDeclarativeXmlListModelRole> list(this, d->roleObjects);
711 list.append = &QDeclarativeXmlListModelPrivate::append_role;
712 list.clear = &QDeclarativeXmlListModelPrivate::clear_role;
716 QHash<int,QVariant> QDeclarativeXmlListModel::data(int index, const QList<int> &roles) const
718 Q_D(const QDeclarativeXmlListModel);
719 QHash<int, QVariant> rv;
720 for (int i = 0; i < roles.size(); ++i) {
721 int role = roles.at(i);
722 int roleIndex = d->roles.indexOf(role);
723 rv.insert(role, roleIndex == -1 ? QVariant() : d->data.value(roleIndex).value(index));
728 QVariant QDeclarativeXmlListModel::data(int index, int role) const
730 Q_D(const QDeclarativeXmlListModel);
731 int roleIndex = d->roles.indexOf(role);
732 return (roleIndex == -1) ? QVariant() : d->data.value(roleIndex).value(index);
736 \qmlproperty int QtQuick2::XmlListModel::count
737 The number of data entries in the model.
739 int QDeclarativeXmlListModel::count() const
741 Q_D(const QDeclarativeXmlListModel);
745 QList<int> QDeclarativeXmlListModel::roles() const
747 Q_D(const QDeclarativeXmlListModel);
751 QString QDeclarativeXmlListModel::toString(int role) const
753 Q_D(const QDeclarativeXmlListModel);
754 int index = d->roles.indexOf(role);
757 return d->roleNames.at(index);
761 \qmlproperty url QtQuick2::XmlListModel::source
762 The location of the XML data source.
764 If both \c source and \l xml are set, \l xml is used.
766 QUrl QDeclarativeXmlListModel::source() const
768 Q_D(const QDeclarativeXmlListModel);
772 void QDeclarativeXmlListModel::setSource(const QUrl &src)
774 Q_D(QDeclarativeXmlListModel);
777 if (d->xml.isEmpty()) // src is only used if d->xml is not set
779 emit sourceChanged();
784 \qmlproperty string QtQuick2::XmlListModel::xml
785 This property holds the XML data for this model, if set.
787 The text is assumed to be UTF-8 encoded.
789 If both \l source and \c xml are set, \c xml is used.
791 QString QDeclarativeXmlListModel::xml() const
793 Q_D(const QDeclarativeXmlListModel);
797 void QDeclarativeXmlListModel::setXml(const QString &xml)
799 Q_D(QDeclarativeXmlListModel);
808 \qmlproperty string QtQuick2::XmlListModel::query
809 An absolute XPath query representing the base query for creating model items
810 from this model's XmlRole objects. The query should start with '/' or '//'.
812 QString QDeclarativeXmlListModel::query() const
814 Q_D(const QDeclarativeXmlListModel);
818 void QDeclarativeXmlListModel::setQuery(const QString &query)
820 Q_D(QDeclarativeXmlListModel);
821 if (!query.startsWith(QLatin1Char('/'))) {
822 qmlInfo(this) << QCoreApplication::translate("QDeclarativeXmlRoleList", "An XmlListModel query must start with '/' or \"//\"");
826 if (d->query != query) {
834 \qmlproperty string QtQuick2::XmlListModel::namespaceDeclarations
835 The namespace declarations to be used in the XPath queries.
837 The namespaces should be declared as in XQuery. For example, if a requested document
838 at http://mysite.com/feed.xml uses the namespace "http://www.w3.org/2005/Atom",
839 this can be declared as the default namespace:
843 source: "http://mysite.com/feed.xml"
845 namespaceDeclarations: "declare default element namespace 'http://www.w3.org/2005/Atom';"
847 XmlRole { name: "title"; query: "title/string()" }
851 QString QDeclarativeXmlListModel::namespaceDeclarations() const
853 Q_D(const QDeclarativeXmlListModel);
854 return d->namespaces;
857 void QDeclarativeXmlListModel::setNamespaceDeclarations(const QString &declarations)
859 Q_D(QDeclarativeXmlListModel);
860 if (d->namespaces != declarations) {
861 d->namespaces = declarations;
863 emit namespaceDeclarationsChanged();
868 \qmlmethod object QtQuick2::XmlListModel::get(int index)
870 Returns the item at \a index in the model.
872 For example, for a model like this:
877 source: "http://mysite.com/feed.xml"
879 XmlRole { name: "title"; query: "title/string()" }
883 This will access the \c title value for the first item in the model:
886 var title = model.get(0).title;
889 QDeclarativeV8Handle QDeclarativeXmlListModel::get(int index) const
891 // Must be called with a context and handle scope
892 Q_D(const QDeclarativeXmlListModel);
894 if (index < 0 || index >= count())
895 return QDeclarativeV8Handle::fromHandle(v8::Undefined());
897 QDeclarativeEngine *engine = qmlContext(this)->engine();
898 QV8Engine *v8engine = QDeclarativeEnginePrivate::getV8Engine(engine);
899 v8::Local<v8::Object> rv = v8::Object::New();
900 for (int ii = 0; ii < d->roleObjects.count(); ++ii)
901 rv->Set(v8engine->toString(d->roleObjects[ii]->name()),
902 v8engine->fromVariant(d->data.value(ii).value(index)));
904 return QDeclarativeV8Handle::fromHandle(rv);
908 \qmlproperty enumeration QtQuick2::XmlListModel::status
909 Specifies the model loading status, which can be one of the following:
912 \o XmlListModel.Null - No XML data has been set for this model.
913 \o XmlListModel.Ready - The XML data has been loaded into the model.
914 \o XmlListModel.Loading - The model is in the process of reading and loading XML data.
915 \o XmlListModel.Error - An error occurred while the model was loading. See errorString() for details
922 QDeclarativeXmlListModel::Status QDeclarativeXmlListModel::status() const
924 Q_D(const QDeclarativeXmlListModel);
929 \qmlproperty real QtQuick2::XmlListModel::progress
931 This indicates the current progress of the downloading of the XML data
932 source. This value ranges from 0.0 (no data downloaded) to
933 1.0 (all data downloaded). If the XML data is not from a remote source,
934 the progress becomes 1.0 as soon as the data is read.
936 Note that when the progress is 1.0, the XML data has been downloaded, but
937 it is yet to be loaded into the model at this point. Use the status
938 property to find out when the XML data has been read and loaded into
943 qreal QDeclarativeXmlListModel::progress() const
945 Q_D(const QDeclarativeXmlListModel);
950 \qmlmethod void QtQuick2::XmlListModel::errorString()
952 Returns a string description of the last error that occurred
953 if \l status is XmlListModel::Error.
955 QString QDeclarativeXmlListModel::errorString() const
957 Q_D(const QDeclarativeXmlListModel);
958 return d->errorString;
961 void QDeclarativeXmlListModel::classBegin()
963 Q_D(QDeclarativeXmlListModel);
964 d->isComponentComplete = false;
966 QDeclarativeXmlQueryEngine *queryEngine = QDeclarativeXmlQueryEngine::instance(qmlEngine(this));
967 connect(queryEngine, SIGNAL(queryCompleted(QDeclarativeXmlQueryResult)),
968 SLOT(queryCompleted(QDeclarativeXmlQueryResult)));
969 connect(queryEngine, SIGNAL(error(void*,QString)),
970 SLOT(queryError(void*,QString)));
973 void QDeclarativeXmlListModel::componentComplete()
975 Q_D(QDeclarativeXmlListModel);
976 d->isComponentComplete = true;
981 \qmlmethod QtQuick2::XmlListModel::reload()
985 If no key roles have been specified, all existing model
986 data is removed, and the model is rebuilt from scratch.
988 Otherwise, items are only added if the model does not already
989 contain items with matching key role values.
991 \sa {Using key XML roles}, XmlRole::isKey
993 void QDeclarativeXmlListModel::reload()
995 Q_D(QDeclarativeXmlListModel);
997 if (!d->isComponentComplete)
1000 QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->abort(d->queryId);
1011 if (!d->xml.isEmpty()) {
1012 d->queryId = QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache);
1013 d->notifyQueryStarted(false);
1015 } else if (d->src.isEmpty()) {
1016 d->queryId = XMLLISTMODEL_CLEAR_ID;
1017 d->notifyQueryStarted(false);
1018 QTimer::singleShot(0, this, SLOT(dataCleared()));
1021 d->notifyQueryStarted(true);
1022 QNetworkRequest req(d->src);
1023 req.setRawHeader("Accept", "application/xml,*/*");
1024 d->reply = qmlContext(this)->engine()->networkAccessManager()->get(req);
1025 QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(requestFinished()));
1026 QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
1027 this, SLOT(requestProgress(qint64,qint64)));
1031 #define XMLLISTMODEL_MAX_REDIRECT 16
1033 void QDeclarativeXmlListModel::requestFinished()
1035 Q_D(QDeclarativeXmlListModel);
1038 if (d->redirectCount < XMLLISTMODEL_MAX_REDIRECT) {
1039 QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
1040 if (redirect.isValid()) {
1041 QUrl url = d->reply->url().resolved(redirect.toUrl());
1047 d->redirectCount = 0;
1049 if (d->reply->error() != QNetworkReply::NoError) {
1050 d->errorString = d->reply->errorString();
1053 int count = this->count();
1057 emit itemsRemoved(0, count);
1058 emit countChanged();
1063 emit statusChanged(d->status);
1065 QByteArray data = d->reply->readAll();
1066 if (data.isEmpty()) {
1067 d->queryId = XMLLISTMODEL_CLEAR_ID;
1068 QTimer::singleShot(0, this, SLOT(dataCleared()));
1070 d->queryId = QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache);
1075 emit progressChanged(d->progress);
1079 void QDeclarativeXmlListModel::requestProgress(qint64 received, qint64 total)
1081 Q_D(QDeclarativeXmlListModel);
1082 if (d->status == Loading && total > 0) {
1083 d->progress = qreal(received)/total;
1084 emit progressChanged(d->progress);
1088 void QDeclarativeXmlListModel::dataCleared()
1090 Q_D(QDeclarativeXmlListModel);
1091 QDeclarativeXmlQueryResult r;
1092 r.queryId = XMLLISTMODEL_CLEAR_ID;
1094 r.removed << qMakePair(0, count());
1095 r.keyRoleResultsCache = d->keyRoleResultsCache;
1099 void QDeclarativeXmlListModel::queryError(void* object, const QString& error)
1101 // Be extra careful, object may no longer exist, it's just an ID.
1102 Q_D(QDeclarativeXmlListModel);
1103 for (int i=0; i<d->roleObjects.count(); i++) {
1104 if (d->roleObjects.at(i) == static_cast<QDeclarativeXmlListModelRole*>(object)) {
1105 qmlInfo(d->roleObjects.at(i)) << QObject::tr("invalid query: \"%1\"").arg(error);
1109 qmlInfo(this) << QObject::tr("invalid query: \"%1\"").arg(error);
1112 void QDeclarativeXmlListModel::queryCompleted(const QDeclarativeXmlQueryResult &result)
1114 Q_D(QDeclarativeXmlListModel);
1115 if (result.queryId != d->queryId)
1118 int origCount = d->size;
1119 bool sizeChanged = result.size != d->size;
1121 d->size = result.size;
1122 d->data = result.data;
1123 d->keyRoleResultsCache = result.keyRoleResultsCache;
1124 if (d->src.isEmpty() && d->xml.isEmpty())
1128 d->errorString.clear();
1131 bool hasKeys = false;
1132 for (int i=0; i<d->roleObjects.count(); i++) {
1133 if (d->roleObjects[i]->isKey()) {
1139 if (!(origCount == 0 && d->size == 0)) {
1140 emit itemsRemoved(0, origCount);
1141 emit itemsInserted(0, d->size);
1142 emit countChanged();
1146 for (int i=0; i<result.removed.count(); i++)
1147 emit itemsRemoved(result.removed[i].first, result.removed[i].second);
1148 for (int i=0; i<result.inserted.count(); i++)
1149 emit itemsInserted(result.inserted[i].first, result.inserted[i].second);
1152 emit countChanged();
1155 emit statusChanged(d->status);
1160 #include <qdeclarativexmllistmodel.moc>