1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QFile>
30#include <QStack>
31
32#include <QXmlNamePool>
33#include <QXmlStreamReader>
34#include <QtDebug>
35#include <QTest>
36
37#include "LoadingModel.h"
38LoadingModel::LoadingModel(const Node::Vector &content,
39 const QXmlNamePool &np) : QSimpleXmlNodeModel(np)
40 , m_nodes(content)
41{
42 /*
43 for (const Node *n : content)
44 qDebug() << "this:" << n
45 << "kind:" << n->kind
46 << "parent: " << n->parent
47 << "preceding: " << n->precedingSibling
48 << "following: " << n->followingSibling
49 << "firstChild: " << n->firstChild
50 << "value: " << n->value;
51 */
52}
53
54LoadingModel::~LoadingModel()
55{
56 qDeleteAll(c: m_nodes);
57}
58
59const LoadingModel::Node *LoadingModel::toInternal(const QXmlNodeModelIndex &ni) const
60{
61 return static_cast<const Node *>(ni.internalPointer());
62}
63
64QXmlNodeModelIndex LoadingModel::createIndex(const Node *const internal) const
65{
66 if (!internal)
67 qFatal(msg: "%s: cannot construct a model index from a null pointer", Q_FUNC_INFO);
68 return QAbstractXmlNodeModel::createIndex(pointer: const_cast<Node *>(internal));
69}
70
71QUrl LoadingModel::documentUri(const QXmlNodeModelIndex &) const
72{
73 qFatal(msg: "%s: This method should not be called during the test", Q_FUNC_INFO);
74 return QUrl();
75}
76
77QXmlNodeModelIndex::NodeKind LoadingModel::kind(const QXmlNodeModelIndex &ni) const
78{
79 if (ni.isNull())
80 qFatal(msg: "%s: node model index should not be null", Q_FUNC_INFO);
81 return toInternal(ni)->kind;
82}
83
84QXmlNodeModelIndex::DocumentOrder LoadingModel::compareOrder(const QXmlNodeModelIndex &n1, const QXmlNodeModelIndex &n2) const
85{
86 const Node *const in1 = toInternal(ni: n1);
87 const Node *const in2 = toInternal(ni: n2);
88 if (m_nodes.indexOf(t: in1) == -1)
89 qFatal(msg: "%s: node n1 is not in internal node list", Q_FUNC_INFO);
90 if (m_nodes.indexOf(t: in2) == -1)
91 qFatal(msg: "%s: node n2 is not in internal node list", Q_FUNC_INFO);
92
93 if(in1 == in2)
94 return QXmlNodeModelIndex::Is;
95 else if(m_nodes.indexOf(t: in1) < m_nodes.indexOf(t: in2))
96 return QXmlNodeModelIndex::Precedes;
97 else
98 return QXmlNodeModelIndex::Follows;
99}
100
101QXmlNodeModelIndex LoadingModel::root(const QXmlNodeModelIndex &) const
102{
103 if (kind(ni: createIndex(internal: m_nodes.first())) != QXmlNodeModelIndex::Document) {
104 qWarning(msg: "%s: first node must be a Document node", Q_FUNC_INFO);
105 return QXmlNodeModelIndex();
106 }
107 return createIndex(internal: m_nodes.first());
108}
109
110QXmlName LoadingModel::name(const QXmlNodeModelIndex &ni) const
111{
112 return toInternal(ni)->name;
113}
114
115QVariant LoadingModel::typedValue(const QXmlNodeModelIndex &ni) const
116{
117 const Node *const internal = toInternal(ni);
118
119 if (internal->kind != QXmlNodeModelIndex::Attribute
120 && internal->kind != QXmlNodeModelIndex::Element) {
121 qWarning(msg: "%s: node must be an attribute or element", Q_FUNC_INFO);
122 return QVariant();
123 }
124
125 return internal->value;
126}
127
128QString LoadingModel::stringValue(const QXmlNodeModelIndex &ni) const
129{
130 const Node *const internal = toInternal(ni);
131
132 switch(internal->kind)
133 {
134 case QXmlNodeModelIndex::Text:
135 /* Fallthrough. */
136 case QXmlNodeModelIndex::ProcessingInstruction:
137 /* Fallthrough. */
138 case QXmlNodeModelIndex::Comment:
139 /* Fallthrough. */
140 case QXmlNodeModelIndex::Attribute:
141 return internal->value;
142 default:
143 return QString();
144 }
145}
146
147QXmlNodeModelIndex LoadingModel::nextFromSimpleAxis(QAbstractXmlNodeModel::SimpleAxis axis,
148 const QXmlNodeModelIndex &ni) const
149{
150 const Node *const internal = toInternal(ni);
151
152 /* Note that a QXmlNodeModelIndex containing a null pointer is not a null node. */
153 switch(axis)
154 {
155 case Parent:
156 return internal->parent ? createIndex(internal: internal->parent) : QXmlNodeModelIndex();
157 case FirstChild:
158 return internal->firstChild ? createIndex(internal: internal->firstChild) : QXmlNodeModelIndex();
159 case PreviousSibling:
160 return internal->precedingSibling ? createIndex(internal: internal->precedingSibling) : QXmlNodeModelIndex();
161 case NextSibling:
162 return internal->followingSibling ? createIndex(internal: internal->followingSibling) : QXmlNodeModelIndex();
163 default:
164 qWarning(msg: "%s: unknown axis enum value %d", Q_FUNC_INFO, static_cast<int>(axis));
165 return QXmlNodeModelIndex();
166 }
167}
168
169QVector<QXmlNodeModelIndex> LoadingModel::attributes(const QXmlNodeModelIndex &ni) const
170{
171 QVector<QXmlNodeModelIndex> retval;
172 for (const Node *n : toInternal(ni)->attributes)
173 retval.append(t: createIndex(internal: n));
174
175 return retval;
176}
177
178class Loader
179{
180public:
181 inline Loader(const QXmlNamePool &namePool) : m_namePool(namePool)
182 , m_currentNode(0)
183 {
184 m_parentStack.push(t: 0);
185 }
186
187private:
188 inline void adjustSiblings(LoadingModel::Node *const justBorn);
189 friend class LoadingModel;
190 Q_DISABLE_COPY(Loader);
191
192 void load();
193
194 QXmlNamePool m_namePool;
195 QXmlStreamReader m_reader;
196 LoadingModel::Node::Vector m_result;
197 LoadingModel::Node * m_currentNode;
198 QStack<LoadingModel::Node *> m_parentStack;
199};
200
201inline void Loader::adjustSiblings(LoadingModel::Node *const justBorn)
202{
203 if(m_currentNode)
204 {
205 if(m_currentNode->parent == justBorn->parent)
206 justBorn->precedingSibling = m_currentNode;
207
208 m_currentNode->followingSibling = justBorn;
209 }
210
211 m_currentNode = justBorn;
212
213 /* Otherwise we're the first child, and our precedingSibling should remain null. */
214
215 if(m_parentStack.top() && !m_parentStack.top()->firstChild)
216 m_parentStack.top()->firstChild = justBorn;
217}
218
219void Loader::load()
220{
221 QFile in(QLatin1String("tree.xml"));
222
223 /* LoadingModel::m_result will be null, signalling failure. */
224 if(!in.open(flags: QIODevice::ReadOnly))
225 return;
226
227 QXmlStreamReader reader(&in);
228 while(!reader.atEnd())
229 {
230 reader.readNext();
231
232 switch(reader.tokenType())
233 {
234 case QXmlStreamReader::StartDocument:
235 /* Fallthrough. */
236 case QXmlStreamReader::StartElement:
237 {
238 QXmlName name;
239 if(reader.tokenType() == QXmlStreamReader::StartElement)
240 {
241 name = QXmlName(m_namePool,
242 reader.name().toString(),
243 reader.namespaceUri().toString(),
244 reader.prefix().toString());
245 }
246 /* Else, the name is null. */
247
248 LoadingModel::Node *const tmp = new LoadingModel::Node(reader.tokenType() == QXmlStreamReader::StartElement
249 ? QXmlNodeModelIndex::Element
250 : QXmlNodeModelIndex::Document,
251 m_parentStack.top(),
252 QString(),
253 name);
254 m_result.append(t: tmp);
255
256 if(m_currentNode)
257 {
258 if(m_currentNode->parent == m_parentStack.top())
259 m_currentNode->followingSibling = tmp;
260 }
261
262 const QXmlStreamAttributes attributes(reader.attributes());
263 const int len = attributes.count();
264
265 for(int i = 0; i < len; ++i)
266 {
267 const QXmlStreamAttribute &attr = attributes.at(i);
268 const LoadingModel::Node *const a = new LoadingModel::Node(QXmlNodeModelIndex::Attribute,
269 m_parentStack.top(),
270 attr.value().toString(),
271 QXmlName(m_namePool,
272 attr.name().toString(),
273 attr.namespaceUri().toString(),
274 attr.prefix().toString()));
275 /* We add it also to m_result such that compareOrder() is correct
276 * for attributes. m_result owns a. */
277 tmp->attributes.append(t: a);
278 m_result.append(t: a);
279 }
280
281 adjustSiblings(justBorn: tmp);
282 m_parentStack.push(t: m_currentNode);
283 break;
284 }
285 case QXmlStreamReader::EndDocument:
286 /* Fallthrough. */
287 case QXmlStreamReader::EndElement:
288 {
289 m_currentNode->followingSibling = 0;
290 m_currentNode = m_parentStack.pop();
291
292 if(reader.tokenType() == QXmlStreamReader::EndDocument)
293 const_cast<LoadingModel::Node *>(m_result.first())->followingSibling = 0;
294
295 break;
296 }
297 case QXmlStreamReader::Characters:
298 {
299 LoadingModel::Node *const tmp = new LoadingModel::Node(QXmlNodeModelIndex::Text, m_parentStack.top(), reader.text().toString());
300 m_result.append(t: tmp);
301 adjustSiblings(justBorn: tmp);
302 break;
303 }
304 case QXmlStreamReader::ProcessingInstruction:
305 {
306 LoadingModel::Node *const tmp = new LoadingModel::Node(QXmlNodeModelIndex::ProcessingInstruction,
307 m_parentStack.top(),
308 reader.processingInstructionData().toString(),
309 QXmlName(m_namePool, reader.processingInstructionTarget().toString()));
310 m_result.append(t: tmp);
311 adjustSiblings(justBorn: tmp);
312 break;
313 }
314 case QXmlStreamReader::Comment:
315 {
316 LoadingModel::Node *const tmp = new LoadingModel::Node(QXmlNodeModelIndex::Comment, m_parentStack.top(), reader.text().toString());
317 m_result.append(t: tmp);
318 adjustSiblings(justBorn: tmp);
319 break;
320 }
321 case QXmlStreamReader::DTD:
322 qFatal(msg: "%s: QXmlStreamReader::DTD token is not supported", Q_FUNC_INFO);
323 break;
324 case QXmlStreamReader::EntityReference:
325 qFatal(msg: "%s: QXmlStreamReader::EntityReference token is not supported", Q_FUNC_INFO);
326 break;
327 case QXmlStreamReader::NoToken:
328 /* Fallthrough. */
329 case QXmlStreamReader::Invalid:
330 {
331 qWarning(msg: "%s", qPrintable(reader.errorString()));
332 m_result.clear();
333 return;
334 }
335 }
336 }
337
338 if(reader.hasError())
339 {
340 qWarning(msg: "%s", qPrintable(reader.errorString()));
341 m_result.clear();
342 }
343}
344
345QAbstractXmlNodeModel::Ptr LoadingModel::create(const QXmlNamePool &np)
346{
347 Loader loader(np);
348 loader.load();
349 if (loader.m_result.isEmpty()) {
350 qWarning(msg: "%s: attempt to create model with no content", Q_FUNC_INFO);
351 return Ptr(0);
352 }
353
354 return Ptr(new LoadingModel(loader.m_result, np));
355}
356

source code of qtxmlpatterns/tests/auto/qabstractxmlnodemodel/LoadingModel.cpp