summaryrefslogtreecommitdiffstats
path: root/doc/src/examples/qobjectxmlmodel.qdoc
blob: 8054ae2886d9bb31f7a94ad053a381417fa382d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in a
** written agreement between you and Nokia.
**
** GNU Free Documentation License
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of this
** file.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
  \example xmlpatterns/qobjectxmlmodel
  \title QObject XML Model Example

  This example shows how to use QtXmlPatterns to query QObject trees
  by modeling the non-XML data structure of a QObject tree to look
  like XML.

  \tableofcontents

  \section1 Introduction

  This example illustrates two important points about using XQuery to
  query non-XML data modeled to look like XML. The first point is that
  a custom node model class doesn't always have to actually build the
  node model. Sometimes the node model can be an already existing data
  structure, like the QObject tree used in this example. The second
  point is to explain what is required to make non-XML data look like
  XML.

  In this example, we want to model a QObject tree to look like
  XML. That is easy to do because a QObject tree maps to the XML tree
  structure in a staightforward way. Each QObject node is modeled as
  an XML element node. However, when we want to add the QMetaObject tree
  to the QObject tree node model, we are trying to add a second tree to
  the node model. The QMetaObject tree exists \e{behind} the QObject
  tree. Adding the QMetaObject tree to the node model changes the two
  dimensional tree into a three dimensional tree.

  The query engine can only traverse two dimensional trees, because an
  XML document is always a two dimensional tree. If we want to add the
  QMetaObject tree to the node model, we have to somehow flatten it
  into the same plane as the QObject tree. This requires that the
  node model class must build an auxiliary data structure and make it
  part of the two dimensional QObject node model. How to do this is
  explained in \l{Including The QMetaObject Tree}.

  \section2 The User Interface

  The UI for this example was created using Qt Designer:

  \image qobjectxmlmodel-example.png

  \section1 Code Walk-Through

  The strategy for this example is different from the strategy for the
  \l{File System Example}{file system example}. In the file system
  example, the node model class had to actually build a node model
  because the non-XML data to be traversed was the computer's file
  system, a structure stored on disk in a form that the query engine
  couldn't use. The node model class had to build an analog of the
  computer's file system in memory.

  For this example, the data structure to be traversed already exists
  in memory in a usable form. It is the QObject tree of the example
  application itself. All we need is the pointer to the root of the
  QObject tree.

  \note When we add the QMetaObject tree to the node model, the node
  model class will have to build an auxiliary data structure to move
  the QMetaObject tree into the same plane as the QObject tree. This
  is explained later in \l{Including The QMetaObject Tree}.

  \section2 The Custom Node Model Class: QObjextXmlModel

  The node model class for this example is QObjextXmlModel, which is
  derived from QSimpleXmlNodeModel. QObjextXmlModel implements the
  callback interface functions that don't have implementations in
  QSimpleXmlNodeModel: 

  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 0

  The node model class declares three data members:

  \target Three Data Members
  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 2

  The constructor sets \c m_baseURI to the QUrl constructed from the
  \l{QCoreApplication::applicationFilePath()}{file path} of the
  application executable.  This is the value returned by
  \l{QAbstractXmlNodeModel::documentUri()}{documentUri()}. The
  constructor sets \c{m_root} to point to the QObject tree for the
  example application. This is the node model that the query engine
  will use. And the constructor calls a local function to build the
  auxiliary data structure (\c{m_allMetaObjects}) for including the
  QMetaObject tree in the node model. How this auxiliary data
  structure is incorporated into the QObject node model is discussed
  in \l{Including The QMetaObject Tree}.

  \section3 Accessing The Node Model

  Since the query engine knows nothing about QObject trees, it can
  only access them by calling functions in the node model callback
  interface. The query engine passes a QXmlNodeModelIndex to uniquely
  identify a node in the node model. The QXmlNodeModelIndex is
  constructed from a pointer to the QObject that represents the node.
  \l{QAbstractXmlNodeModel::createIndex()}{createIndex()} creates the
  QXmlNodeModelIndex, as in the local \c{root()} function, for example:

  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 0

  A QObject represents an element node in the node model, but we also
  need to represent attribute nodes. For example, the class name of a
  QObject is an attribute of the QObject, so it should be an attribute
  node in the node model. A QObject's class name is obtained from the
  QObject. (Actually, it is in the QMetaObject, which is obtained from
  the QObject). This means that a single QObject logically represents
  multiple nodes in the node model: the element node and potentially
  many attribute nodes.

  To uniquely identify an attribute node, we need the pointer to the
  QObject containing the attribute, and an additional value that
  identifies the attribute in the QObject. For this \e{additional
  data} value, we use \c{enum QObjectNodeType}:

  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 3

  Ignore the \c{MetaObjectXXX} values for now. They will be explained
  in \l{Including The QMetaObject Tree}. Here we are interested in the
  three node types for QObject nodes: \c{IsQObject}, which represents
  the element node type for a QObject, and \c{QObjectProperty} and
  \c{QObjectClassName}, which represent the attribute node types for
  the attributes of a QObject.

  The \l{QAbstractXmlNodeModel::createIndex()}{createIndex()}
  function called in the \c{root()} snippet above is the overload that
  accepts a \c{void*} pointer and a second parameter,
  \c{additionalData}, with default value 0 (\c{IsQObject}). Wherever
  you see a call to \l{QAbstractXmlNodeModel::createIndex()}
  {createIndex()} that only passes the QObject pointer, it is creating
  the node index for a QObject element node. To create the node index
  for the class name attribute, for example, the \l{QObject
  attributes} {attributes()} function uses
  \c{createIndex(object,QObjectClassName)}.

  \target QObject attributes
  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 6
  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 8

  \l{QObject attributes} {attributes()} is one of the callback
  functions you have to implement in your custom node model class. It
  returns a QVector of \l{QXmlNodeModelIndex} {node indexes} for all
  the attribute nodes for QObject \c{n}. It calls
  \l{QAbstractXmlNodeModel::createIndex()} {createIndex()} in two places.
  Both calls use the QObject pointer from the current node \c{n} (the
  element node), and just add a different value for the \e{additional data}
  parameter. This makes sense because, in XML, the attributes of an
  element are part of that element. 

  \section3 Traversing The Node Model

  The query engine traverses the QObject tree by calling back to the
  node model class's implementation of \l{QObject nextFromSimpleAxis}
  {nextFromSimpleAxis()}. This function is the heart of the callback
  interface, and it will probably be the most complex to implement in
  your custom node model class. Below is a partial listing of the
  implementation for this example. The full listing will be shown in
  \l{Including The QMetaObject Tree}, where we discuss traversing the
  QMetaObject tree.

  \target QObject nextFromSimpleAxis
  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 2
  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 4

  The main switch uses \c toNodeType(), which obtains the node
  type from \l{QXmlNodeModelIndex::additionalData()}:

  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 1

  \c{case IsObject} case is the most interesting. It switches again on
  the value of the \c{axis} parameter, which specifies the direction
  the query engine wants to take from the current node. It is one of
  the four enum values of \l{QAbstractXmlNodeModel::SimpleAxis}.  The
  \l{QAbstractXmlNodeModel::Parent} {Parent} and
  \l{QAbstractXmlNodeModel::FirstChild} {FirstChild} cases reduce to
  calls to QObject::parent() and QObject::children()
  respectively. Note that a default constructed QXmlNodeModelIndex is
  returned in the \l{QAbstractXmlNodeModel::Parent} {Parent} case if
  the current node is the root, and in the
  \l{QAbstractXmlNodeModel::FirstChild} {FirstChild} case if the
  current node has no children.

  For the \l{QAbstractXmlNodeModel::NextSibling} {NextSibling} and
  \l{QAbstractXmlNodeModel::PreviousSibling} {PreviousSibling} axes,
  the helper function \c{qObjectSibling()} is called, with +1 to
  traverse to the \l{QAbstractXmlNodeModel::NextSibling} {NextSibling}
  and -1 to traverse to the
  \l{QAbstractXmlNodeModel::PreviousSibling} {PreviousSibling}.

  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 5

  \c{qObjectSibling()} determines whether or not the node has any
  siblings. It is called with \c{n}, the index of the current node.
  If the current node is a child, then it has a parent with children
  (the current node one of these).
  So, we get the \l{QObject::parent()}{parent}, obtain the parent's
  \l{QObject::children()} {child list}, find the current node in the
  list, and construct the node index for the next or previous child
  (sibling) and return it.

  \note In \l{QObject nextFromSimpleAxis} {nextFromSimpleAxis()}, the
  special case of asking for the
  \l{QAbstractXmlNodeModel::PreviousSibling} {PreviousSibling} of the
  root node is discussed in \l{Including The QMetaObject Tree}.

  Traversing away from a \c{QObjectClassName} attribute node or a
  \c{QObjectProperty} attribute node might seem a bit confusing at
  first glance. The only move allowed from an attribute node is to the
  \l{QAbstractXmlNodeModel::Parent} {Parent}, because attribute nodes
  don't have children. But these two cases simply return the 
  \l{QXmlNodeModelIndex} {node index} of the current node.

  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 7

  Since \c n is the QXmlNodeModelIndex of the current node, all this
  does is create another QXmlNodeModelIndex for the current node and
  return it. This was explained above in \l{Accessing The Node Model},
  where we saw that each QObject in the node model actually represents
  an element node and potentially many attribute nodes. Traversing to
  the parent node of an attribute simply creates a node index for the
  same QObject, but with an \e{additional data} value of 0
  (\c{IsQObject}).

  If we only wanted to traverse the QObject tree with XQuery, we could
  just implement the rest of the virtual callback functions listed
  earlier and we would be done. The implementations for the remaining
  functions are straightforward. But if we also want to use XQuery to
  traverse the QMetaObject tree, we must include the QMetaObject tree
  in the custom node model.

  \section3 Including The QMetaObject Tree

  The \l{Meta-Object System} {metaobject system} not only enables Qt's
  \l{Signals and Slots} {signals and slots}, it also provides type
  information that is useful at run-time; e.g., getting and setting
  properties without knowing the property names at compile time. Each
  QObject has an associated QMetaObject tree which contains all this
  useful type information. Given a QObject, its QMetaObject is
  obtained with QObject::metaObject(). Then QMetaObject::superClass()
  can be called repeatedly to get the QMetaObject for each class in the 
  class hierarchy for the original QObject.

  However, the QMetaObject hierarchy is a second tree in a plan that
  exists logically behind the plane of the QObject tree. The QtXmlPatterns
  query engine can only traverse a two dimensional node model that
  represents an XML tree. If we want to include the QMetaObject in the
  same node model that represents the QObject tree, we must find a way
  to flatten the QMetaObject tree into the same plane as the QObject
  tree.

  The node model class declares \l{All MetaObjects}{m_allMetaObjects}
  as a vector of pointers to QMetaObject:

  \target All MetaObjects
  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 1
  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 4

  This vector gets populated by the QObjectXmlModel constructor by
  calling the private allMetaObjects() function:

  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 9

  The first half of the function is an example of the standard code
  pattern for using QtXmlPatterns to run an XQuery. First it creates an
  instance of QXmlQuery. Then it \l{QXmlQuery::bindVariable()}{binds}
  the XQuery variable \c{$root} to the root node of the of the node
  model; i.e., the root of the QObject tree. Then it
  \l{QXmlQuery::setQuery()} {sets the query} to be an XQuery that
  returns all the QObjects in the node model. Finally, the query is
  evaluated into a \l{QXmlResultItems} {result item list}.

  \note \l{QXmlQuery::bindVariable()} must be called before
  \l{QXmlQuery::setQuery()}, because setting the query causes
  QtXmlPatterns to \e compile the XQuery, which requires knowledge of
  the variable bindings.

  The second half of the function traverses the \l{QXmlResultItems}
  {result item list}, getting the QMetaObject hierarchy for each
  QObject and appending it to \l{All MetaObjects} {m_allMetaObjects},
  if it isn't already there. But how do we include this vector of
  pointers to QMetaObjects in the node model? The key insight is
  shown in the full listing of \l{Full Listing of nextFromSimpleAxis}
  {nextFromSimpleAxis()}, where we are interested now in the 
  \c{MetaObjectXXX} cases:

  \target Full Listing of nextFromSimpleAxis
  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 2
  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 3
  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 4

  But first, revisit the \c{PreviousSibling} case for the
  \c{IsQObject} case:

  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 10

  When asking for the previous sibling of the root of the QObject
  tree, it creates a node model index with a null QObject pointer and
  an \c{additionalData} value of \c{MetaObjects}. This effectively
  allows the query engine to jump from the QObject tree to the
  QMetaObject tree.

  The query engine can jump from the QMetaObject tree back to the
  QObject tree in the \c{NextSibling} case of case \c{MetaObjects},
  where the \c{root()} function is called:

  \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 11

  Having jumped from the QObject tree to the QMetaObject tree, the
  query engine will use the \c{MetaObject}, \c{MetaObjectClassName},
  and \c{MetaObjectSuperClass} cases, which are similar to the cases
  for \c{IsQObject}, \c{QObjectProperty}, and \c{QObjectClassName}.  
*/