1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "private/qdeclarativexmllistmodel_p.h"
44 #include <qdeclarativecontext.h>
45 #include <qdeclarativeengine_p.h>
48 #include <QStringList>
50 #include <QApplication>
53 #include <QXmlResultItems>
54 #include <QXmlNodeModelIndex>
56 #include <QNetworkRequest>
57 #include <QNetworkReply>
61 #include <private/qobject_p.h>
63 Q_DECLARE_METATYPE(QDeclarativeXmlQueryResult)
68 typedef QPair<int, int> QDeclarativeXmlListRange;
70 #define XMLLISTMODEL_CLEAR_ID 0
73 \qmlclass XmlRole QDeclarativeXmlListModelRole
74 \ingroup qml-working-with-data
76 \brief The XmlRole element allows you to specify a role for an XmlListModel.
82 \qmlproperty string XmlRole::name
84 The name for the role. This name is used to access the model data for this role.
86 For example, the following model has a role named "title", which can be accessed
87 from the view's delegate:
95 query: "title/string()"
103 delegate: Text { text: title }
109 \qmlproperty string XmlRole::query
110 The relative XPath expression query for this role. The query must be relative; it cannot start
113 For example, if there is an XML document like this:
115 \quotefile doc/src/snippets/declarative/xmlrole.xml
117 Here are some valid XPath expressions for XmlRole queries on this document:
119 \snippet doc/src/snippets/declarative/xmlrole.qml 0
121 \snippet doc/src/snippets/declarative/xmlrole.qml 1
123 See the \l{http://www.w3.org/TR/xpath20/}{W3C XPath 2.0 specification} for more information.
127 \qmlproperty bool XmlRole::isKey
128 Defines whether this is a key role.
130 Key roles are used to to determine whether a set of values should
131 be updated or added to the XML list model when XmlListModel::reload()
143 QStringList roleQueries;
144 QList<void*> roleQueryErrorId; // the ptr to send back if there is an error
145 QStringList keyRoleQueries;
146 QStringList keyRoleResultsCache;
150 class QDeclarativeXmlQuery : public QObject
154 QDeclarativeXmlQuery(QObject *parent=0)
155 : QObject(parent), m_queryIds(XMLLISTMODEL_CLEAR_ID + 1) {
156 qRegisterMetaType<QDeclarativeXmlQueryResult>("QDeclarativeXmlQueryResult");
157 moveToThread(&m_thread);
158 m_thread.start(QThread::IdlePriority);
161 ~QDeclarativeXmlQuery() {
162 if(m_thread.isRunning()) {
169 QMutexLocker ml(&m_mutex);
175 int doQuery(QString query, QString namespaces, QByteArray data, QList<QDeclarativeXmlListModelRole *>* roleObjects, QStringList keyRoleResultsCache) {
177 QMutexLocker m1(&m_mutex);
184 job.queryId = m_queryIds;
186 job.query = QLatin1String("doc($src)") + query;
187 job.namespaces = namespaces;
188 job.keyRoleResultsCache = keyRoleResultsCache;
190 for (int i=0; i<roleObjects->count(); i++) {
191 if (!roleObjects->at(i)->isValid()) {
192 job.roleQueries << QString();
195 job.roleQueries << roleObjects->at(i)->query();
196 job.roleQueryErrorId << static_cast<void*>(roleObjects->at(i));
197 if (roleObjects->at(i)->isKey())
198 job.keyRoleQueries << job.roleQueries.last();
202 QMutexLocker ml(&m_mutex);
203 m_jobs.insert(m_queryIds, job);
206 QMetaObject::invokeMethod(this, "processQuery", Qt::QueuedConnection, Q_ARG(int, job.queryId));
211 void processQuery(int queryId) {
215 QMutexLocker ml(&m_mutex);
216 if (!m_jobs.contains(queryId))
218 job = m_jobs.value(queryId);
221 QDeclarativeXmlQueryResult result;
222 result.queryId = job.queryId;
223 doQueryJob(&job, &result);
224 doSubQueryJob(&job, &result);
227 QMutexLocker ml(&m_mutex);
228 if (m_jobs.contains(queryId)) {
229 emit queryCompleted(result);
230 m_jobs.remove(queryId);
236 void queryCompleted(const QDeclarativeXmlQueryResult &);
237 void error(void*, const QString&);
243 void doQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult);
244 void doSubQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult);
245 void getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const;
246 void addIndexToRangeList(QList<QDeclarativeXmlListRange> *ranges, int index) const;
251 QMap<int, XmlQueryJob> m_jobs;
252 QAtomicInt m_queryIds;
255 Q_GLOBAL_STATIC(QDeclarativeXmlQuery, globalXmlQuery)
257 void QDeclarativeXmlQuery::doQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult)
259 Q_ASSERT(currentJob->queryId != -1);
263 QBuffer buffer(¤tJob->data);
264 buffer.open(QIODevice::ReadOnly);
265 query.bindVariable(QLatin1String("src"), &buffer);
266 query.setQuery(currentJob->namespaces + currentJob->query);
267 query.evaluateTo(&r);
269 //always need a single root element
270 QByteArray xml = "<dummy:items xmlns:dummy=\"http://qtsotware.com/dummy\">\n" + r.toUtf8() + "</dummy:items>";
272 b.open(QIODevice::ReadOnly);
274 QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + currentJob->namespaces;
275 QString prefix = QLatin1String("doc($inputDocument)/dummy:items") +
276 currentJob->query.mid(currentJob->query.lastIndexOf(QLatin1Char('/')));
278 //figure out how many items we are dealing with
281 QXmlResultItems result;
282 QXmlQuery countquery;
283 countquery.bindVariable(QLatin1String("inputDocument"), &b);
284 countquery.setQuery(namespaces + QLatin1String("count(") + prefix + QLatin1Char(')'));
285 countquery.evaluateTo(&result);
286 QXmlItem item(result.next());
287 if (item.isAtomicValue())
288 count = item.toAtomicValue().toInt();
291 currentJob->data = xml;
292 currentJob->prefix = namespaces + prefix + QLatin1Char('/');
293 currentResult->size = (count > 0 ? count : 0);
296 void QDeclarativeXmlQuery::getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const
298 const QStringList &keysQueries = currentJob.keyRoleQueries;
300 if (keysQueries.count() == 1)
301 keysQuery = currentJob.prefix + keysQueries[0];
302 else if (keysQueries.count() > 1)
303 keysQuery = currentJob.prefix + QLatin1String("concat(") + keysQueries.join(QLatin1String(",")) + QLatin1String(")");
305 if (!keysQuery.isEmpty()) {
306 query->setQuery(keysQuery);
307 QXmlResultItems resultItems;
308 query->evaluateTo(&resultItems);
309 QXmlItem item(resultItems.next());
310 while (!item.isNull()) {
311 values->append(item.toAtomicValue().toString());
312 item = resultItems.next();
317 void QDeclarativeXmlQuery::addIndexToRangeList(QList<QDeclarativeXmlListRange> *ranges, int index) const {
318 if (ranges->isEmpty())
319 ranges->append(qMakePair(index, 1));
320 else if (ranges->last().first + ranges->last().second == index)
321 ranges->last().second += 1;
323 ranges->append(qMakePair(index, 1));
326 void QDeclarativeXmlQuery::doSubQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult)
328 Q_ASSERT(currentJob->queryId != -1);
330 QBuffer b(¤tJob->data);
331 b.open(QIODevice::ReadOnly);
334 subquery.bindVariable(QLatin1String("inputDocument"), &b);
336 QStringList keyRoleResults;
337 getValuesOfKeyRoles(*currentJob, &keyRoleResults, &subquery);
339 // See if any values of key roles have been inserted or removed.
341 if (currentJob->keyRoleResultsCache.isEmpty()) {
342 currentResult->inserted << qMakePair(0, currentResult->size);
344 if (keyRoleResults != currentJob->keyRoleResultsCache) {
346 for (int i=0; i<currentJob->keyRoleResultsCache.count(); i++) {
347 if (!keyRoleResults.contains(currentJob->keyRoleResultsCache[i]))
348 addIndexToRangeList(¤tResult->removed, i);
350 temp << currentJob->keyRoleResultsCache[i];
353 for (int i=0; i<keyRoleResults.count(); i++) {
354 if (temp.count() == i || keyRoleResults[i] != temp[i]) {
355 temp.insert(i, keyRoleResults[i]);
356 addIndexToRangeList(¤tResult->inserted, i);
361 currentResult->keyRoleResultsCache = keyRoleResults;
363 // Get the new values for each role.
364 //### we might be able to condense even further (query for everything in one go)
365 const QStringList &queries = currentJob->roleQueries;
366 for (int i = 0; i < queries.size(); ++i) {
367 QList<QVariant> resultList;
368 if (!queries[i].isEmpty()) {
369 subquery.setQuery(currentJob->prefix + QLatin1String("(let $v := string(") + queries[i] + QLatin1String(") return if ($v) then ") + queries[i] + QLatin1String(" else \"\")"));
370 if (subquery.isValid()) {
371 QXmlResultItems resultItems;
372 subquery.evaluateTo(&resultItems);
373 QXmlItem item(resultItems.next());
374 while (!item.isNull()) {
375 resultList << item.toAtomicValue(); //### we used to trim strings
376 item = resultItems.next();
379 emit error(currentJob->roleQueryErrorId.at(i), queries[i]);
382 //### should warn here if things have gone wrong.
383 while (resultList.count() < currentResult->size)
384 resultList << QVariant();
385 currentResult->data << resultList;
389 //this method is much slower, but works better for incremental loading
390 /*for (int j = 0; j < m_size; ++j) {
391 QList<QVariant> resultList;
392 for (int i = 0; i < m_roleObjects->size(); ++i) {
393 QDeclarativeXmlListModelRole *role = m_roleObjects->at(i);
394 subquery.setQuery(m_prefix.arg(j+1) + role->query());
395 if (role->isStringList()) {
397 subquery.evaluateTo(&data);
398 resultList << QVariant(data);
402 subquery.evaluateTo(&s);
403 if (role->isCData()) {
405 s.replace(QLatin1String("<"), QLatin1String("<"));
406 s.replace(QLatin1String(">"), QLatin1String(">"));
407 s.replace(QLatin1String("&"), QLatin1String("&"));
409 resultList << s.trimmed();
414 m_modelData << resultList;
418 class QDeclarativeXmlListModelPrivate : public QObjectPrivate
420 Q_DECLARE_PUBLIC(QDeclarativeXmlListModel)
422 QDeclarativeXmlListModelPrivate()
423 : isComponentComplete(true), size(-1), highestRole(Qt::UserRole)
424 , reply(0), status(QDeclarativeXmlListModel::Null), progress(0.0)
425 , queryId(-1), roleObjects(), redirectCount(0) {}
428 void notifyQueryStarted(bool remoteSource) {
429 Q_Q(QDeclarativeXmlListModel);
430 progress = remoteSource ? 0.0 : 1.0;
431 status = QDeclarativeXmlListModel::Loading;
433 emit q->progressChanged(progress);
434 emit q->statusChanged(status);
437 bool isComponentComplete;
444 QStringList roleNames;
446 QNetworkReply *reply;
447 QDeclarativeXmlListModel::Status status;
451 QStringList keyRoleResultsCache;
452 QList<QDeclarativeXmlListModelRole *> roleObjects;
453 static void append_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list, QDeclarativeXmlListModelRole *role);
454 static void clear_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list);
455 QList<QList<QVariant> > data;
460 void QDeclarativeXmlListModelPrivate::append_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list, QDeclarativeXmlListModelRole *role)
462 QDeclarativeXmlListModel *_this = qobject_cast<QDeclarativeXmlListModel *>(list->object);
464 int i = _this->d_func()->roleObjects.count();
465 _this->d_func()->roleObjects.append(role);
466 if (_this->d_func()->roleNames.contains(role->name())) {
467 qmlInfo(role) << QObject::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name());
470 _this->d_func()->roles.insert(i, _this->d_func()->highestRole);
471 _this->d_func()->roleNames.insert(i, role->name());
472 ++_this->d_func()->highestRole;
476 //### clear needs to invalidate any cached data (in data table) as well
477 // (and the model should emit the appropriate signals)
478 void QDeclarativeXmlListModelPrivate::clear_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list)
480 QDeclarativeXmlListModel *_this = static_cast<QDeclarativeXmlListModel *>(list->object);
481 _this->d_func()->roles.clear();
482 _this->d_func()->roleNames.clear();
483 _this->d_func()->roleObjects.clear();
487 \qmlclass XmlListModel QDeclarativeXmlListModel
488 \ingroup qml-working-with-data
490 \brief The XmlListModel element is used to specify a read-only model using XPath expressions.
492 XmlListModel is used to create a read-only model from XML data. It can be used as a data source
493 for view elements (such as ListView, PathView, GridView) and other elements that interact with model
494 data (such as \l Repeater).
496 For example, if there is a XML document at http://www.mysite.com/feed.xml like this:
499 <?xml version="1.0" encoding="utf-8"?>
504 <title>A blog post</title>
505 <pubDate>Sat, 07 Sep 2010 10:00:01 GMT</pubDate>
508 <title>Another blog post</title>
509 <pubDate>Sat, 07 Sep 2010 15:35:01 GMT</pubDate>
515 A XmlListModel could create a model from this data, like this:
522 source: "http://www.mysite.com/feed.xml"
523 query: "/rss/channel/item"
525 XmlRole { name: "title"; query: "title/string()" }
526 XmlRole { name: "pubDate"; query: "pubDate/string()" }
530 The \l {XmlListModel::query}{query} value of "/rss/channel/item" specifies that the XmlListModel should generate
531 a model item for each \c <item> in the XML document.
533 The XmlRole objects define the
534 model item attributes. Here, each model item will have \c title and \c pubDate
535 attributes that match the \c title and \c pubDate values of its corresponding \c <item>.
536 (See \l XmlRole::query for more examples of valid XPath expressions for XmlRole.)
538 The model could be used in a ListView, like this:
542 width: 180; height: 300
544 delegate: Text { text: title + ": " + pubDate }
548 \image qml-xmllistmodel-example.png
550 The XmlListModel data is loaded asynchronously, and \l status
551 is set to \c XmlListModel.Ready when loading is complete.
552 Note this means when XmlListModel is used for a view, the view is not
553 populated until the model is loaded.
556 \section2 Using key XML roles
558 You can define certain roles as "keys" so that when reload() is called,
559 the model will only add and refresh data that contains new values for
562 For example, if above role for "pubDate" was defined like this instead:
565 XmlRole { name: "pubDate"; query: "pubDate/string()"; isKey: true }
568 Then when reload() is called, the model will only add and reload
569 items with a "pubDate" value that is not already
570 present in the model.
572 This is useful when displaying the contents of XML documents that
573 are incrementally updated (such as RSS feeds) to avoid repainting the
574 entire contents of a model in a view.
576 If multiple key roles are specified, the model only adds and reload items
577 with a combined value of all key roles that is not already present in
583 QDeclarativeXmlListModel::QDeclarativeXmlListModel(QObject *parent)
584 : QListModelInterface(*(new QDeclarativeXmlListModelPrivate), parent)
586 connect(globalXmlQuery(), SIGNAL(queryCompleted(QDeclarativeXmlQueryResult)),
587 this, SLOT(queryCompleted(QDeclarativeXmlQueryResult)));
588 connect(globalXmlQuery(), SIGNAL(error(void*,QString)),
589 this, SLOT(queryError(void*,QString)));
592 QDeclarativeXmlListModel::~QDeclarativeXmlListModel()
597 \qmlproperty list<XmlRole> XmlListModel::roles
599 The roles to make available for this model.
601 QDeclarativeListProperty<QDeclarativeXmlListModelRole> QDeclarativeXmlListModel::roleObjects()
603 Q_D(QDeclarativeXmlListModel);
604 QDeclarativeListProperty<QDeclarativeXmlListModelRole> list(this, d->roleObjects);
605 list.append = &QDeclarativeXmlListModelPrivate::append_role;
606 list.clear = &QDeclarativeXmlListModelPrivate::clear_role;
610 QHash<int,QVariant> QDeclarativeXmlListModel::data(int index, const QList<int> &roles) const
612 Q_D(const QDeclarativeXmlListModel);
613 QHash<int, QVariant> rv;
614 for (int i = 0; i < roles.size(); ++i) {
615 int role = roles.at(i);
616 int roleIndex = d->roles.indexOf(role);
617 rv.insert(role, roleIndex == -1 ? QVariant() : d->data.value(roleIndex).value(index));
622 QVariant QDeclarativeXmlListModel::data(int index, int role) const
624 Q_D(const QDeclarativeXmlListModel);
625 int roleIndex = d->roles.indexOf(role);
626 return (roleIndex == -1) ? QVariant() : d->data.value(roleIndex).value(index);
630 \qmlproperty int XmlListModel::count
631 The number of data entries in the model.
633 int QDeclarativeXmlListModel::count() const
635 Q_D(const QDeclarativeXmlListModel);
639 QList<int> QDeclarativeXmlListModel::roles() const
641 Q_D(const QDeclarativeXmlListModel);
645 QString QDeclarativeXmlListModel::toString(int role) const
647 Q_D(const QDeclarativeXmlListModel);
648 int index = d->roles.indexOf(role);
651 return d->roleNames.at(index);
655 \qmlproperty url XmlListModel::source
656 The location of the XML data source.
658 If both \c source and \l xml are set, \l xml is used.
660 QUrl QDeclarativeXmlListModel::source() const
662 Q_D(const QDeclarativeXmlListModel);
666 void QDeclarativeXmlListModel::setSource(const QUrl &src)
668 Q_D(QDeclarativeXmlListModel);
671 if (d->xml.isEmpty()) // src is only used if d->xml is not set
673 emit sourceChanged();
678 \qmlproperty string XmlListModel::xml
679 This property holds the XML data for this model, if set.
681 The text is assumed to be UTF-8 encoded.
683 If both \l source and \c xml are set, \c xml is used.
685 QString QDeclarativeXmlListModel::xml() const
687 Q_D(const QDeclarativeXmlListModel);
691 void QDeclarativeXmlListModel::setXml(const QString &xml)
693 Q_D(QDeclarativeXmlListModel);
702 \qmlproperty string XmlListModel::query
703 An absolute XPath query representing the base query for creating model items
704 from this model's XmlRole objects. The query should start with '/' or '//'.
706 QString QDeclarativeXmlListModel::query() const
708 Q_D(const QDeclarativeXmlListModel);
712 void QDeclarativeXmlListModel::setQuery(const QString &query)
714 Q_D(QDeclarativeXmlListModel);
715 if (!query.startsWith(QLatin1Char('/'))) {
716 qmlInfo(this) << QCoreApplication::translate("QDeclarativeXmlRoleList", "An XmlListModel query must start with '/' or \"//\"");
720 if (d->query != query) {
728 \qmlproperty string XmlListModel::namespaceDeclarations
729 The namespace declarations to be used in the XPath queries.
731 The namespaces should be declared as in XQuery. For example, if a requested document
732 at http://mysite.com/feed.xml uses the namespace "http://www.w3.org/2005/Atom",
733 this can be declared as the default namespace:
737 source: "http://mysite.com/feed.xml"
739 namespaceDeclarations: "declare default element namespace 'http://www.w3.org/2005/Atom';"
741 XmlRole { name: "title"; query: "title/string()" }
745 QString QDeclarativeXmlListModel::namespaceDeclarations() const
747 Q_D(const QDeclarativeXmlListModel);
748 return d->namespaces;
751 void QDeclarativeXmlListModel::setNamespaceDeclarations(const QString &declarations)
753 Q_D(QDeclarativeXmlListModel);
754 if (d->namespaces != declarations) {
755 d->namespaces = declarations;
757 emit namespaceDeclarationsChanged();
762 \qmlmethod object XmlListModel::get(int index)
764 Returns the item at \a index in the model.
766 For example, for a model like this:
771 source: "http://mysite.com/feed.xml"
773 XmlRole { name: "title"; query: "title/string()" }
777 This will access the \c title value for the first item in the model:
780 var title = model.get(0).title;
783 QScriptValue QDeclarativeXmlListModel::get(int index) const
785 Q_D(const QDeclarativeXmlListModel);
787 QScriptEngine *sengine = QDeclarativeEnginePrivate::getScriptEngine(qmlContext(this)->engine());
788 if (index < 0 || index >= count())
789 return sengine->undefinedValue();
791 QScriptValue sv = sengine->newObject();
792 for (int i=0; i<d->roleObjects.count(); i++)
793 sv.setProperty(d->roleObjects[i]->name(), sengine->toScriptValue(d->data.value(i).value(index)));
798 \qmlproperty enumeration XmlListModel::status
799 Specifies the model loading status, which can be one of the following:
802 \o XmlListModel.Null - No XML data has been set for this model.
803 \o XmlListModel.Ready - The XML data has been loaded into the model.
804 \o XmlListModel.Loading - The model is in the process of reading and loading XML data.
805 \o XmlListModel.Error - An error occurred while the model was loading. See errorString() for details
812 QDeclarativeXmlListModel::Status QDeclarativeXmlListModel::status() const
814 Q_D(const QDeclarativeXmlListModel);
819 \qmlproperty real XmlListModel::progress
821 This indicates the current progress of the downloading of the XML data
822 source. This value ranges from 0.0 (no data downloaded) to
823 1.0 (all data downloaded). If the XML data is not from a remote source,
824 the progress becomes 1.0 as soon as the data is read.
826 Note that when the progress is 1.0, the XML data has been downloaded, but
827 it is yet to be loaded into the model at this point. Use the status
828 property to find out when the XML data has been read and loaded into
833 qreal QDeclarativeXmlListModel::progress() const
835 Q_D(const QDeclarativeXmlListModel);
840 \qmlmethod void XmlListModel::errorString()
842 Returns a string description of the last error that occurred
843 if \l status is XmlListModel::Error.
845 QString QDeclarativeXmlListModel::errorString() const
847 Q_D(const QDeclarativeXmlListModel);
848 return d->errorString;
851 void QDeclarativeXmlListModel::classBegin()
853 Q_D(QDeclarativeXmlListModel);
854 d->isComponentComplete = false;
857 void QDeclarativeXmlListModel::componentComplete()
859 Q_D(QDeclarativeXmlListModel);
860 d->isComponentComplete = true;
865 \qmlmethod XmlListModel::reload()
869 If no key roles have been specified, all existing model
870 data is removed, and the model is rebuilt from scratch.
872 Otherwise, items are only added if the model does not already
873 contain items with matching key role values.
875 \sa {Using key XML roles}, XmlRole::isKey
877 void QDeclarativeXmlListModel::reload()
879 Q_D(QDeclarativeXmlListModel);
881 if (!d->isComponentComplete)
884 globalXmlQuery()->abort(d->queryId);
893 // abort will generally have already done this (and more)
894 d->reply->deleteLater();
899 if (!d->xml.isEmpty()) {
900 d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache);
901 d->notifyQueryStarted(false);
903 } else if (d->src.isEmpty()) {
904 d->queryId = XMLLISTMODEL_CLEAR_ID;
905 d->notifyQueryStarted(false);
906 QTimer::singleShot(0, this, SLOT(dataCleared()));
909 d->notifyQueryStarted(true);
910 QNetworkRequest req(d->src);
911 req.setRawHeader("Accept", "application/xml,*/*");
912 d->reply = qmlContext(this)->engine()->networkAccessManager()->get(req);
913 QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(requestFinished()));
914 QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
915 this, SLOT(requestProgress(qint64,qint64)));
919 #define XMLLISTMODEL_MAX_REDIRECT 16
921 void QDeclarativeXmlListModel::requestFinished()
923 Q_D(QDeclarativeXmlListModel);
926 if (d->redirectCount < XMLLISTMODEL_MAX_REDIRECT) {
927 QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
928 if (redirect.isValid()) {
929 QUrl url = d->reply->url().resolved(redirect.toUrl());
930 d->reply->deleteLater();
936 d->redirectCount = 0;
938 if (d->reply->error() != QNetworkReply::NoError) {
939 d->errorString = d->reply->errorString();
940 disconnect(d->reply, 0, this, 0);
941 d->reply->deleteLater();
944 int count = this->count();
948 emit itemsRemoved(0, count);
954 emit statusChanged(d->status);
956 QByteArray data = d->reply->readAll();
957 if (data.isEmpty()) {
958 d->queryId = XMLLISTMODEL_CLEAR_ID;
959 QTimer::singleShot(0, this, SLOT(dataCleared()));
961 d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache);
963 disconnect(d->reply, 0, this, 0);
964 d->reply->deleteLater();
968 emit progressChanged(d->progress);
972 void QDeclarativeXmlListModel::requestProgress(qint64 received, qint64 total)
974 Q_D(QDeclarativeXmlListModel);
975 if (d->status == Loading && total > 0) {
976 d->progress = qreal(received)/total;
977 emit progressChanged(d->progress);
981 void QDeclarativeXmlListModel::dataCleared()
983 Q_D(QDeclarativeXmlListModel);
984 QDeclarativeXmlQueryResult r;
985 r.queryId = XMLLISTMODEL_CLEAR_ID;
987 r.removed << qMakePair(0, count());
988 r.keyRoleResultsCache = d->keyRoleResultsCache;
992 void QDeclarativeXmlListModel::queryError(void* object, const QString& error)
994 // Be extra careful, object may no longer exist, it's just an ID.
995 Q_D(QDeclarativeXmlListModel);
996 for (int i=0; i<d->roleObjects.count(); i++) {
997 if (d->roleObjects.at(i) == static_cast<QDeclarativeXmlListModelRole*>(object)) {
998 qmlInfo(d->roleObjects.at(i)) << QObject::tr("invalid query: \"%1\"").arg(error);
1002 qmlInfo(this) << QObject::tr("invalid query: \"%1\"").arg(error);
1005 void QDeclarativeXmlListModel::queryCompleted(const QDeclarativeXmlQueryResult &result)
1007 Q_D(QDeclarativeXmlListModel);
1008 if (result.queryId != d->queryId)
1011 int origCount = d->size;
1012 bool sizeChanged = result.size != d->size;
1014 d->size = result.size;
1015 d->data = result.data;
1016 d->keyRoleResultsCache = result.keyRoleResultsCache;
1018 d->errorString.clear();
1021 bool hasKeys = false;
1022 for (int i=0; i<d->roleObjects.count(); i++) {
1023 if (d->roleObjects[i]->isKey()) {
1029 if (!(origCount == 0 && d->size == 0)) {
1030 emit itemsRemoved(0, origCount);
1031 emit itemsInserted(0, d->size);
1032 emit countChanged();
1036 for (int i=0; i<result.removed.count(); i++)
1037 emit itemsRemoved(result.removed[i].first, result.removed[i].second);
1038 for (int i=0; i<result.inserted.count(); i++)
1039 emit itemsInserted(result.inserted[i].first, result.inserted[i].second);
1042 emit countChanged();
1045 emit statusChanged(d->status);
1050 #include <qdeclarativexmllistmodel.moc>