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