0
|
1 |
/****************************************************************************
|
|
2 |
**
|
|
3 |
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
4 |
** All rights reserved.
|
|
5 |
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
6 |
**
|
|
7 |
** This file is part of the documentation of the Qt Toolkit.
|
|
8 |
**
|
|
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
|
|
14 |
** this package.
|
|
15 |
**
|
|
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.
|
|
23 |
**
|
|
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.
|
|
27 |
**
|
|
28 |
** If you have questions regarding the use of this file, please contact
|
|
29 |
** Nokia at qt-info@nokia.com.
|
|
30 |
**
|
|
31 |
**
|
|
32 |
**
|
|
33 |
**
|
|
34 |
**
|
|
35 |
**
|
|
36 |
**
|
|
37 |
**
|
|
38 |
** $QT_END_LICENSE$
|
|
39 |
**
|
|
40 |
****************************************************************************/
|
|
41 |
|
|
42 |
/*!
|
|
43 |
\example xmlpatterns/qobjectxmlmodel
|
|
44 |
\title QObject XML Model Example
|
|
45 |
|
|
46 |
This example shows how to use QtXmlPatterns to query QObject trees
|
|
47 |
by modeling the non-XML data structure of a QObject tree to look
|
|
48 |
like XML.
|
|
49 |
|
|
50 |
\tableofcontents
|
|
51 |
|
|
52 |
\section1 Introduction
|
|
53 |
|
|
54 |
This example illustrates two important points about using XQuery to
|
|
55 |
query non-XML data modeled to look like XML. The first point is that
|
|
56 |
a custom node model class doesn't always have to actually build the
|
|
57 |
node model. Sometimes the node model can be an already existing data
|
|
58 |
structure, like the QObject tree used in this example. The second
|
|
59 |
point is to explain what is required to make non-XML data look like
|
|
60 |
XML.
|
|
61 |
|
|
62 |
In this example, we want to model a QObject tree to look like
|
|
63 |
XML. That is easy to do because a QObject tree maps to the XML tree
|
|
64 |
structure in a staightforward way. Each QObject node is modeled as
|
|
65 |
an XML element node. However, when we want to add the QMetaObject tree
|
|
66 |
to the QObject tree node model, we are trying to add a second tree to
|
|
67 |
the node model. The QMetaObject tree exists \e{behind} the QObject
|
|
68 |
tree. Adding the QMetaObject tree to the node model changes the two
|
|
69 |
dimensional tree into a three dimensional tree.
|
|
70 |
|
|
71 |
The query engine can only traverse two dimensional trees, because an
|
|
72 |
XML document is always a two dimensional tree. If we want to add the
|
|
73 |
QMetaObject tree to the node model, we have to somehow flatten it
|
|
74 |
into the same plane as the QObject tree. This requires that the
|
|
75 |
node model class must build an auxiliary data structure and make it
|
|
76 |
part of the two dimensional QObject node model. How to do this is
|
|
77 |
explained in \l{Including The QMetaObject Tree}.
|
|
78 |
|
|
79 |
\section2 The User Interface
|
|
80 |
|
|
81 |
The UI for this example was created using Qt Designer:
|
|
82 |
|
|
83 |
\image qobjectxmlmodel-example.png
|
|
84 |
|
|
85 |
\section1 Code Walk-Through
|
|
86 |
|
|
87 |
The strategy for this example is different from the strategy for the
|
|
88 |
\l{File System Example}{file system example}. In the file system
|
|
89 |
example, the node model class had to actually build a node model
|
|
90 |
because the non-XML data to be traversed was the computer's file
|
|
91 |
system, a structure stored on disk in a form that the query engine
|
|
92 |
couldn't use. The node model class had to build an analog of the
|
|
93 |
computer's file system in memory.
|
|
94 |
|
|
95 |
For this example, the data structure to be traversed already exists
|
|
96 |
in memory in a usable form. It is the QObject tree of the example
|
|
97 |
application itself. All we need is the pointer to the root of the
|
|
98 |
QObject tree.
|
|
99 |
|
|
100 |
\note When we add the QMetaObject tree to the node model, the node
|
|
101 |
model class will have to build an auxiliary data structure to move
|
|
102 |
the QMetaObject tree into the same plane as the QObject tree. This
|
|
103 |
is explained later in \l{Including The QMetaObject Tree}.
|
|
104 |
|
|
105 |
\section2 The Custom Node Model Class: QObjextXmlModel
|
|
106 |
|
|
107 |
The node model class for this example is QObjextXmlModel, which is
|
|
108 |
derived from QSimpleXmlNodeModel. QObjextXmlModel implements the
|
|
109 |
callback interface functions that don't have implementations in
|
|
110 |
QSimpleXmlNodeModel:
|
|
111 |
|
|
112 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 0
|
|
113 |
|
|
114 |
The node model class declares three data members:
|
|
115 |
|
|
116 |
\target Three Data Members
|
|
117 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 2
|
|
118 |
|
|
119 |
The constructor sets \c m_baseURI to the QUrl constructed from the
|
|
120 |
\l{QCoreApplication::applicationFilePath()}{file path} of the
|
|
121 |
application executable. This is the value returned by
|
|
122 |
\l{QAbstractXmlNodeModel::documentUri()}{documentUri()}. The
|
|
123 |
constructor sets \c{m_root} to point to the QObject tree for the
|
|
124 |
example application. This is the node model that the query engine
|
|
125 |
will use. And the constructor calls a local function to build the
|
|
126 |
auxiliary data structure (\c{m_allMetaObjects}) for including the
|
|
127 |
QMetaObject tree in the node model. How this auxiliary data
|
|
128 |
structure is incorporated into the QObject node model is discussed
|
|
129 |
in \l{Including The QMetaObject Tree}.
|
|
130 |
|
|
131 |
\section3 Accessing The Node Model
|
|
132 |
|
|
133 |
Since the query engine knows nothing about QObject trees, it can
|
|
134 |
only access them by calling functions in the node model callback
|
|
135 |
interface. The query engine passes a QXmlNodeModelIndex to uniquely
|
|
136 |
identify a node in the node model. The QXmlNodeModelIndex is
|
|
137 |
constructed from a pointer to the QObject that represents the node.
|
|
138 |
\l{QAbstractXmlNodeModel::createIndex()}{createIndex()} creates the
|
|
139 |
QXmlNodeModelIndex, as in the local \c{root()} function, for example:
|
|
140 |
|
|
141 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 0
|
|
142 |
|
|
143 |
A QObject represents an element node in the node model, but we also
|
|
144 |
need to represent attribute nodes. For example, the class name of a
|
|
145 |
QObject is an attribute of the QObject, so it should be an attribute
|
|
146 |
node in the node model. A QObject's class name is obtained from the
|
|
147 |
QObject. (Actually, it is in the QMetaObject, which is obtained from
|
|
148 |
the QObject). This means that a single QObject logically represents
|
|
149 |
multiple nodes in the node model: the element node and potentially
|
|
150 |
many attribute nodes.
|
|
151 |
|
|
152 |
To uniquely identify an attribute node, we need the pointer to the
|
|
153 |
QObject containing the attribute, and an additional value that
|
|
154 |
identifies the attribute in the QObject. For this \e{additional
|
|
155 |
data} value, we use \c{enum QObjectNodeType}:
|
|
156 |
|
|
157 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 3
|
|
158 |
|
|
159 |
Ignore the \c{MetaObjectXXX} values for now. They will be explained
|
|
160 |
in \l{Including The QMetaObject Tree}. Here we are interested in the
|
|
161 |
three node types for QObject nodes: \c{IsQObject}, which represents
|
|
162 |
the element node type for a QObject, and \c{QObjectProperty} and
|
|
163 |
\c{QObjectClassName}, which represent the attribute node types for
|
|
164 |
the attributes of a QObject.
|
|
165 |
|
|
166 |
The \l{QAbstractXmlNodeModel::createIndex()}{createIndex()}
|
|
167 |
function called in the \c{root()} snippet above is the overload that
|
|
168 |
accepts a \c{void*} pointer and a second parameter,
|
|
169 |
\c{additionalData}, with default value 0 (\c{IsQObject}). Wherever
|
|
170 |
you see a call to \l{QAbstractXmlNodeModel::createIndex()}
|
|
171 |
{createIndex()} that only passes the QObject pointer, it is creating
|
|
172 |
the node index for a QObject element node. To create the node index
|
|
173 |
for the class name attribute, for example, the \l{QObject
|
|
174 |
attributes} {attributes()} function uses
|
|
175 |
\c{createIndex(object,QObjectClassName)}.
|
|
176 |
|
|
177 |
\target QObject attributes
|
|
178 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 6
|
|
179 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 8
|
|
180 |
|
|
181 |
\l{QObject attributes} {attributes()} is one of the callback
|
|
182 |
functions you have to implement in your custom node model class. It
|
|
183 |
returns a QVector of \l{QXmlNodeModelIndex} {node indexes} for all
|
|
184 |
the attribute nodes for QObject \c{n}. It calls
|
|
185 |
\l{QAbstractXmlNodeModel::createIndex()} {createIndex()} in two places.
|
|
186 |
Both calls use the QObject pointer from the current node \c{n} (the
|
|
187 |
element node), and just add a different value for the \e{additional data}
|
|
188 |
parameter. This makes sense because, in XML, the attributes of an
|
|
189 |
element are part of that element.
|
|
190 |
|
|
191 |
\section3 Traversing The Node Model
|
|
192 |
|
|
193 |
The query engine traverses the QObject tree by calling back to the
|
|
194 |
node model class's implementation of \l{QObject nextFromSimpleAxis}
|
|
195 |
{nextFromSimpleAxis()}. This function is the heart of the callback
|
|
196 |
interface, and it will probably be the most complex to implement in
|
|
197 |
your custom node model class. Below is a partial listing of the
|
|
198 |
implementation for this example. The full listing will be shown in
|
|
199 |
\l{Including The QMetaObject Tree}, where we discuss traversing the
|
|
200 |
QMetaObject tree.
|
|
201 |
|
|
202 |
\target QObject nextFromSimpleAxis
|
|
203 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 2
|
|
204 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 4
|
|
205 |
|
|
206 |
The main switch uses \c toNodeType(), which obtains the node
|
|
207 |
type from \l{QXmlNodeModelIndex::additionalData()}:
|
|
208 |
|
|
209 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 1
|
|
210 |
|
|
211 |
\c{case IsObject} case is the most interesting. It switches again on
|
|
212 |
the value of the \c{axis} parameter, which specifies the direction
|
|
213 |
the query engine wants to take from the current node. It is one of
|
|
214 |
the four enum values of \l{QAbstractXmlNodeModel::SimpleAxis}. The
|
|
215 |
\l{QAbstractXmlNodeModel::Parent} {Parent} and
|
|
216 |
\l{QAbstractXmlNodeModel::FirstChild} {FirstChild} cases reduce to
|
|
217 |
calls to QObject::parent() and QObject::children()
|
|
218 |
respectively. Note that a default constructed QXmlNodeModelIndex is
|
|
219 |
returned in the \l{QAbstractXmlNodeModel::Parent} {Parent} case if
|
|
220 |
the current node is the root, and in the
|
|
221 |
\l{QAbstractXmlNodeModel::FirstChild} {FirstChild} case if the
|
|
222 |
current node has no children.
|
|
223 |
|
|
224 |
For the \l{QAbstractXmlNodeModel::NextSibling} {NextSibling} and
|
|
225 |
\l{QAbstractXmlNodeModel::PreviousSibling} {PreviousSibling} axes,
|
|
226 |
the helper function \c{qObjectSibling()} is called, with +1 to
|
|
227 |
traverse to the \l{QAbstractXmlNodeModel::NextSibling} {NextSibling}
|
|
228 |
and -1 to traverse to the
|
|
229 |
\l{QAbstractXmlNodeModel::PreviousSibling} {PreviousSibling}.
|
|
230 |
|
|
231 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 5
|
|
232 |
|
|
233 |
\c{qObjectSibling()} determines whether or not the node has any
|
|
234 |
siblings. It is called with \c{n}, the index of the current node.
|
|
235 |
If the current node is a child, then it has a parent with children
|
|
236 |
(the current node one of these).
|
|
237 |
So, we get the \l{QObject::parent()}{parent}, obtain the parent's
|
|
238 |
\l{QObject::children()} {child list}, find the current node in the
|
|
239 |
list, and construct the node index for the next or previous child
|
|
240 |
(sibling) and return it.
|
|
241 |
|
|
242 |
\note In \l{QObject nextFromSimpleAxis} {nextFromSimpleAxis()}, the
|
|
243 |
special case of asking for the
|
|
244 |
\l{QAbstractXmlNodeModel::PreviousSibling} {PreviousSibling} of the
|
|
245 |
root node is discussed in \l{Including The QMetaObject Tree}.
|
|
246 |
|
|
247 |
Traversing away from a \c{QObjectClassName} attribute node or a
|
|
248 |
\c{QObjectProperty} attribute node might seem a bit confusing at
|
|
249 |
first glance. The only move allowed from an attribute node is to the
|
|
250 |
\l{QAbstractXmlNodeModel::Parent} {Parent}, because attribute nodes
|
|
251 |
don't have children. But these two cases simply return the
|
|
252 |
\l{QXmlNodeModelIndex} {node index} of the current node.
|
|
253 |
|
|
254 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 7
|
|
255 |
|
|
256 |
Since \c n is the QXmlNodeModelIndex of the current node, all this
|
|
257 |
does is create another QXmlNodeModelIndex for the current node and
|
|
258 |
return it. This was explained above in \l{Accessing The Node Model},
|
|
259 |
where we saw that each QObject in the node model actually represents
|
|
260 |
an element node and potentially many attribute nodes. Traversing to
|
|
261 |
the parent node of an attribute simply creates a node index for the
|
|
262 |
same QObject, but with an \e{additional data} value of 0
|
|
263 |
(\c{IsQObject}).
|
|
264 |
|
|
265 |
If we only wanted to traverse the QObject tree with XQuery, we could
|
|
266 |
just implement the rest of the virtual callback functions listed
|
|
267 |
earlier and we would be done. The implementations for the remaining
|
|
268 |
functions are straightforward. But if we also want to use XQuery to
|
|
269 |
traverse the QMetaObject tree, we must include the QMetaObject tree
|
|
270 |
in the custom node model.
|
|
271 |
|
|
272 |
\section3 Including The QMetaObject Tree
|
|
273 |
|
|
274 |
The \l{Meta-Object System} {metaobject system} not only enables Qt's
|
|
275 |
\l{Signals and Slots} {signals and slots}, it also provides type
|
|
276 |
information that is useful at run-time; e.g., getting and setting
|
|
277 |
properties without knowing the property names at compile time. Each
|
|
278 |
QObject has an associated QMetaObject tree which contains all this
|
|
279 |
useful type information. Given a QObject, its QMetaObject is
|
|
280 |
obtained with QObject::metaObject(). Then QMetaObject::superClass()
|
|
281 |
can be called repeatedly to get the QMetaObject for each class in the
|
|
282 |
class hierarchy for the original QObject.
|
|
283 |
|
|
284 |
However, the QMetaObject hierarchy is a second tree in a plan that
|
|
285 |
exists logically behind the plane of the QObject tree. The QtXmlPatterns
|
|
286 |
query engine can only traverse a two dimensional node model that
|
|
287 |
represents an XML tree. If we want to include the QMetaObject in the
|
|
288 |
same node model that represents the QObject tree, we must find a way
|
|
289 |
to flatten the QMetaObject tree into the same plane as the QObject
|
|
290 |
tree.
|
|
291 |
|
|
292 |
The node model class declares \l{All MetaObjects}{m_allMetaObjects}
|
|
293 |
as a vector of pointers to QMetaObject:
|
|
294 |
|
|
295 |
\target All MetaObjects
|
|
296 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 1
|
|
297 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 4
|
|
298 |
|
|
299 |
This vector gets populated by the QObjectXmlModel constructor by
|
|
300 |
calling the private allMetaObjects() function:
|
|
301 |
|
|
302 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 9
|
|
303 |
|
|
304 |
The first half of the function is an example of the standard code
|
|
305 |
pattern for using QtXmlPatterns to run an XQuery. First it creates an
|
|
306 |
instance of QXmlQuery. Then it \l{QXmlQuery::bindVariable()}{binds}
|
|
307 |
the XQuery variable \c{$root} to the root node of the of the node
|
|
308 |
model; i.e., the root of the QObject tree. Then it
|
|
309 |
\l{QXmlQuery::setQuery()} {sets the query} to be an XQuery that
|
|
310 |
returns all the QObjects in the node model. Finally, the query is
|
|
311 |
evaluated into a \l{QXmlResultItems} {result item list}.
|
|
312 |
|
|
313 |
\note \l{QXmlQuery::bindVariable()} must be called before
|
|
314 |
\l{QXmlQuery::setQuery()}, because setting the query causes
|
|
315 |
QtXmlPatterns to \e compile the XQuery, which requires knowledge of
|
|
316 |
the variable bindings.
|
|
317 |
|
|
318 |
The second half of the function traverses the \l{QXmlResultItems}
|
|
319 |
{result item list}, getting the QMetaObject hierarchy for each
|
|
320 |
QObject and appending it to \l{All MetaObjects} {m_allMetaObjects},
|
|
321 |
if it isn't already there. But how do we include this vector of
|
|
322 |
pointers to QMetaObjects in the node model? The key insight is
|
|
323 |
shown in the full listing of \l{Full Listing of nextFromSimpleAxis}
|
|
324 |
{nextFromSimpleAxis()}, where we are interested now in the
|
|
325 |
\c{MetaObjectXXX} cases:
|
|
326 |
|
|
327 |
\target Full Listing of nextFromSimpleAxis
|
|
328 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 2
|
|
329 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 3
|
|
330 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 4
|
|
331 |
|
|
332 |
But first, revisit the \c{PreviousSibling} case for the
|
|
333 |
\c{IsQObject} case:
|
|
334 |
|
|
335 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 10
|
|
336 |
|
|
337 |
When asking for the previous sibling of the root of the QObject
|
|
338 |
tree, it creates a node model index with a null QObject pointer and
|
|
339 |
an \c{additionalData} value of \c{MetaObjects}. This effectively
|
|
340 |
allows the query engine to jump from the QObject tree to the
|
|
341 |
QMetaObject tree.
|
|
342 |
|
|
343 |
The query engine can jump from the QMetaObject tree back to the
|
|
344 |
QObject tree in the \c{NextSibling} case of case \c{MetaObjects},
|
|
345 |
where the \c{root()} function is called:
|
|
346 |
|
|
347 |
\snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 11
|
|
348 |
|
|
349 |
Having jumped from the QObject tree to the QMetaObject tree, the
|
|
350 |
query engine will use the \c{MetaObject}, \c{MetaObjectClassName},
|
|
351 |
and \c{MetaObjectSuperClass} cases, which are similar to the cases
|
|
352 |
for \c{IsQObject}, \c{QObjectProperty}, and \c{QObjectClassName}.
|
|
353 |
*/
|