|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 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 */ |