1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 **   * Redistributions of source code must retain the above copyright
15 **     notice, this list of conditions and the following disclaimer.
16 **   * Redistributions in binary form must reproduce the above copyright
17 **     notice, this list of conditions and the following disclaimer in
18 **     the documentation and/or other materials provided with the
19 **     distribution.
20 **   * Neither the name of The Qt Company Ltd nor the names of its
21 **     contributors may be used to endorse or promote products derived
22 **     from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include <QVector>
42 #include <QtDebug>
43 
44 #include <QCoreApplication>
45 #include <QMetaProperty>
46 #include <QXmlQuery>
47 #include <QXmlResultItems>
48 
49 #include "qobjectxmlmodel.h"
50 
51 QT_BEGIN_NAMESPACE
52 
53 /*
54 <metaObjects>
55     <metaObject className="QObject"/>
56     <metaObject className="QWidget" superClass="QObject">
57     </metaObject>
58     ...
59 </metaObjects>
60 <QObject objectName="MyWidget" property1="..." property2="..."> <!-- This is root() -->
61     <QObject objectName="MyFOO" property1="..."/>
62     ....
63 </QObject>
64 */
65 
QObjectXmlModel(QObject * const object,const QXmlNamePool & np)66 QObjectXmlModel::QObjectXmlModel(QObject *const object, const QXmlNamePool &np)
67     : QSimpleXmlNodeModel(np),
68       m_baseURI(QUrl::fromLocalFile(QCoreApplication::applicationFilePath())),
69       m_root(object),
70       m_allMetaObjects(allMetaObjects())
71 {
72     Q_ASSERT(m_baseURI.isValid());
73 }
74 
75 //! [5]
qObjectSibling(const int pos,const QXmlNodeModelIndex & n) const76 QXmlNodeModelIndex QObjectXmlModel::qObjectSibling(const int pos, const QXmlNodeModelIndex &n) const
77 {
78     Q_ASSERT(pos == 1 || pos == -1);
79     Q_ASSERT(asQObject(n));
80 
81     const QObject *parent = asQObject(n)->parent();
82     if (parent) {
83         const QList<QObject *> &children = parent->children();
84         const int siblingPos = children.indexOf(asQObject(n)) + pos;
85 
86         if (siblingPos >= 0 && siblingPos < children.count())
87             return createIndex(children.at(siblingPos));
88         else
89             return QXmlNodeModelIndex();
90     }
91     else
92         return QXmlNodeModelIndex();
93 }
94 //! [5]
95 
96 //! [1]
toNodeType(const QXmlNodeModelIndex & n)97 QObjectXmlModel::QObjectNodeType QObjectXmlModel::toNodeType(const QXmlNodeModelIndex &n)
98 {
99     return QObjectNodeType(n.additionalData() & (15 << 26));
100 }
101 //! [1]
102 
103 //! [9]
allMetaObjects() const104 QObjectXmlModel::AllMetaObjects QObjectXmlModel::allMetaObjects() const
105 {
106     QXmlQuery query(namePool());
107     query.bindVariable("root", root());
108     query.setQuery("declare variable $root external;"
109                    "$root/descendant-or-self::QObject");
110     Q_ASSERT(query.isValid());
111 
112     QXmlResultItems result;
113     query.evaluateTo(&result);
114     QXmlItem i(result.next());
115 
116     AllMetaObjects objects;
117     while (!i.isNull()) {
118         const QMetaObject *moo = asQObject(i.toNodeModelIndex())->metaObject();
119         while (moo) {
120             if (!objects.contains(moo))
121                 objects.append(moo);
122             moo = moo->superClass();
123         }
124         i = result.next();
125     }
126 
127     Q_ASSERT(!objects.contains(0));
128     return objects;
129 }
130 //! [9]
131 
metaObjectSibling(const int pos,const QXmlNodeModelIndex & n) const132 QXmlNodeModelIndex QObjectXmlModel::metaObjectSibling(const int pos, const QXmlNodeModelIndex &n) const
133 {
134     Q_ASSERT(pos == 1 || pos == -1);
135     Q_ASSERT(!n.isNull());
136 
137     const int indexOf = m_allMetaObjects.indexOf(static_cast<const QMetaObject *>(n.internalPointer())) + pos;
138 
139     if (indexOf >= 0 && indexOf < m_allMetaObjects.count())
140         return createIndex(const_cast<QMetaObject *>(m_allMetaObjects.at(indexOf)), MetaObject);
141     else
142         return QXmlNodeModelIndex();
143 }
144 
145 //! [2]
nextFromSimpleAxis(SimpleAxis axis,const QXmlNodeModelIndex & n) const146 QXmlNodeModelIndex QObjectXmlModel::nextFromSimpleAxis(SimpleAxis axis, const QXmlNodeModelIndex &n) const
147 {
148     switch (toNodeType(n))
149     {
150         case IsQObject:
151         {
152             switch (axis)
153             {
154                 case Parent:
155                     return createIndex(asQObject(n)->parent());
156 
157                 case FirstChild:
158                 {
159                     if (!asQObject(n) || asQObject(n)->children().isEmpty())
160                         return QXmlNodeModelIndex();
161                     else
162                         return createIndex(asQObject(n)->children().first());
163                 }
164 
165                 case NextSibling:
166                     return qObjectSibling(1, n);
167 
168 //! [10]
169                 case PreviousSibling:
170                 {
171                     if (asQObject(n) == m_root)
172                         return createIndex(qint64(0), MetaObjects);
173                     else
174                         return qObjectSibling(-1, n);
175                 }
176 //! [10]
177             }
178             Q_ASSERT(false);
179         }
180 
181 //! [7]
182         case QObjectClassName:
183         case QObjectProperty:
184         {
185             Q_ASSERT(axis == Parent);
186             return createIndex(asQObject(n));
187         }
188 //! [7]
189 //! [2]
190 //! [3]
191 
192 //! [11]
193         case MetaObjects:
194         {
195             switch (axis)
196             {
197                 case Parent:
198                     return QXmlNodeModelIndex();
199                 case PreviousSibling:
200                     return QXmlNodeModelIndex();
201                 case NextSibling:
202                     return root();
203                 case FirstChild:
204                 {
205                     return createIndex(const_cast<QMetaObject*>(m_allMetaObjects.first()),MetaObject);
206                 }
207             }
208             Q_ASSERT(false);
209         }
210 //! [11]
211 
212         case MetaObject:
213         {
214             switch (axis)
215             {
216                 case FirstChild:
217                     return QXmlNodeModelIndex();
218                 case Parent:
219                     return createIndex(qint64(0), MetaObjects);
220                 case PreviousSibling:
221                     return metaObjectSibling(-1, n);
222                 case NextSibling:
223                     return metaObjectSibling(1, n);
224             }
225         }
226 
227         case MetaObjectClassName:
228         case MetaObjectSuperClass:
229         {
230             Q_ASSERT(axis == Parent);
231             return createIndex(asQObject(n), MetaObject);
232         }
233 //! [3]
234 //! [4]
235     }
236 
237     Q_ASSERT(false);
238     return QXmlNodeModelIndex();
239 }
240 //! [4]
241 
242 //! [6]
attributes(const QXmlNodeModelIndex & n) const243 QVector<QXmlNodeModelIndex> QObjectXmlModel::attributes(const QXmlNodeModelIndex& n) const
244 {
245      QVector<QXmlNodeModelIndex> result;
246      QObject *const object = asQObject(n);
247 
248      switch(toNodeType(n))
249      {
250         case IsQObject:
251         {
252             const QMetaObject *const metaObject = object->metaObject();
253             const int count = metaObject->propertyCount();
254             result.append(createIndex(object, QObjectClassName));
255 
256             for (int i = 0; i < count; ++i) {
257                 const QMetaProperty qmp(metaObject->property(i));
258                 const int ii = metaObject->indexOfProperty(qmp.name());
259                 if (i == ii)
260                     result.append(createIndex(object, QObjectProperty | i));
261             }
262             return result;
263         }
264 //! [6]
265 
266         case MetaObject:
267         {
268             result.append(createIndex(object, MetaObjectClassName));
269             result.append(createIndex(object, MetaObjectSuperClass));
270             return result;
271         }
272 //! [8]
273         default:
274             return QVector<QXmlNodeModelIndex>();
275      }
276 }
277 //! [8]
278 
asQObject(const QXmlNodeModelIndex & n)279 QObject *QObjectXmlModel::asQObject(const QXmlNodeModelIndex &n)
280 {
281     return static_cast<QObject *>(n.internalPointer());
282 }
283 
isProperty(const QXmlNodeModelIndex n)284 bool QObjectXmlModel::isProperty(const QXmlNodeModelIndex n)
285 {
286     return n.additionalData() & QObjectProperty;
287 }
288 
documentUri(const QXmlNodeModelIndex &) const289 QUrl QObjectXmlModel::documentUri(const QXmlNodeModelIndex& ) const
290 {
291     return m_baseURI;
292 }
293 
kind(const QXmlNodeModelIndex & n) const294 QXmlNodeModelIndex::NodeKind QObjectXmlModel::kind(const QXmlNodeModelIndex& n) const
295 {
296     switch (toNodeType(n))
297     {
298         case IsQObject:
299         case MetaObject:
300         case MetaObjects:
301             return QXmlNodeModelIndex::Element;
302 
303         case QObjectProperty:
304         case MetaObjectClassName:
305         case MetaObjectSuperClass:
306         case QObjectClassName:
307             return QXmlNodeModelIndex::Attribute;
308     }
309 
310     Q_ASSERT(false);
311     return QXmlNodeModelIndex::Element;
312 }
313 
compareOrder(const QXmlNodeModelIndex &,const QXmlNodeModelIndex &) const314 QXmlNodeModelIndex::DocumentOrder QObjectXmlModel::compareOrder(const QXmlNodeModelIndex& , const QXmlNodeModelIndex& ) const
315 {
316     return QXmlNodeModelIndex::Follows; // TODO
317 }
318 
319 //! [0]
root() const320 QXmlNodeModelIndex QObjectXmlModel::root() const
321 {
322     return createIndex(m_root);
323 }
324 //! [0]
325 
root(const QXmlNodeModelIndex & n) const326 QXmlNodeModelIndex QObjectXmlModel::root(const QXmlNodeModelIndex& n) const
327 {
328     QObject *p = asQObject(n);
329     Q_ASSERT(p);
330 
331     do {
332         QObject *const candidate = p->parent();
333         if (candidate)
334             p = candidate;
335         else
336             break;
337     }
338     while (true);
339 
340     return createIndex(p);
341 }
342 
343 /*!
344   We simply throw all of them into a QList and
345   return an iterator over it.
346  */
ancestors(const QXmlNodeModelIndex n) const347 QXmlNodeModelIndex::List QObjectXmlModel::ancestors(const QXmlNodeModelIndex n) const
348 {
349     const QObject *p = asQObject(n);
350     Q_ASSERT(p);
351 
352     QXmlNodeModelIndex::List result;
353     do {
354         QObject *const candidate = p->parent();
355         if (candidate) {
356             result.append(createIndex(candidate, 0));
357             p = candidate;
358         }
359         else
360             break;
361     }
362     while (true);
363 
364     return result;
365 }
366 
toMetaProperty(const QXmlNodeModelIndex & n)367 QMetaProperty QObjectXmlModel::toMetaProperty(const QXmlNodeModelIndex &n)
368 {
369     const int propertyOffset = n.additionalData() & (~QObjectProperty);
370     const QObject *const qo = asQObject(n);
371     return qo->metaObject()->property(propertyOffset);
372 }
373 
name(const QXmlNodeModelIndex & n) const374 QXmlName QObjectXmlModel::name(const QXmlNodeModelIndex &n) const
375 {
376     switch (toNodeType(n))
377     {
378         case IsQObject:
379             return QXmlName(namePool(), QLatin1String("QObject"));
380         case MetaObject:
381             return QXmlName(namePool(), QLatin1String("metaObject"));
382         case QObjectClassName:
383         case MetaObjectClassName:
384             return QXmlName(namePool(), QLatin1String("className"));
385         case QObjectProperty:
386             return QXmlName(namePool(), toMetaProperty(n).name());
387         case MetaObjects:
388             return QXmlName(namePool(), QLatin1String("metaObjects"));
389         case MetaObjectSuperClass:
390             return QXmlName(namePool(), QLatin1String("superClass"));
391     }
392 
393     Q_ASSERT(false);
394     return QXmlName();
395 }
396 
typedValue(const QXmlNodeModelIndex & n) const397 QVariant QObjectXmlModel::typedValue(const QXmlNodeModelIndex &n) const
398 {
399     switch (toNodeType(n))
400     {
401         case QObjectProperty:
402         {
403            const QVariant &candidate = toMetaProperty(n).read(asQObject(n));
404            if (isTypeSupported(candidate.type()))
405                return candidate;
406            else
407                return QVariant();
408         }
409 
410         case MetaObjectClassName:
411             return QVariant(static_cast<QMetaObject*>(n.internalPointer())->className());
412 
413         case MetaObjectSuperClass:
414         {
415             const QMetaObject *const superClass = static_cast<QMetaObject*>(n.internalPointer())->superClass();
416             if (superClass)
417                 return QVariant(superClass->className());
418             else
419                 return QVariant();
420         }
421 
422         case QObjectClassName:
423             return QVariant(asQObject(n)->metaObject()->className());
424 
425         default:
426             return QVariant();
427     }
428 }
429 
430 /*!
431  Returns \c true if QVariants of type \a type can be used
432  in QtXmlPatterns, otherwise \c false.
433  */
isTypeSupported(QVariant::Type type)434 bool QObjectXmlModel::isTypeSupported(QVariant::Type type)
435 {
436     /* See data/qatomicvalue.cpp too. */
437     switch (type)
438     {
439         /* Fallthrough all these. */
440         case QVariant::Char:
441         case QVariant::String:
442         case QVariant::Url:
443         case QVariant::Bool:
444         case QVariant::ByteArray:
445         case QVariant::Int:
446         case QVariant::LongLong:
447         case QVariant::ULongLong:
448         case QVariant::Date:
449         case QVariant::DateTime:
450         case QVariant::Time:
451         case QVariant::Double:
452             return true;
453         default:
454             return false;
455     }
456 }
457 
458 QT_END_NAMESPACE
459