|
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/filetree |
|
44 \title File System Example |
|
45 |
|
46 This example shows how to use QtXmlPatterns for querying non-XML |
|
47 data that is modeled to look like XML. |
|
48 |
|
49 \tableofcontents |
|
50 |
|
51 \section1 Introduction |
|
52 |
|
53 The example models your computer's file system to look like XML and |
|
54 allows you to query the file system with XQuery. Suppose we want to |
|
55 find all the \c{cpp} files in the subtree beginning at |
|
56 \c{/filetree}: |
|
57 |
|
58 \image filetree_1-example.png |
|
59 |
|
60 \section2 The User Inteface |
|
61 |
|
62 The example is shown below. First, we use \c{File->Open Directory} |
|
63 (not shown) to select the \c{/filetree} directory. Then we use the |
|
64 combobox on the right to select the XQuery that searches for \c{cpp} |
|
65 files (\c{listCPPFiles.xq}). Selecting an XQuery runs the query, |
|
66 which in this case traverses the model looking for all the \c{cpp} |
|
67 files. The XQuery text and the query results are shown on the right: |
|
68 |
|
69 \image filetree_2-example.png |
|
70 |
|
71 Don't be mislead by the XML representation of the \c{/filetree} |
|
72 directory shown on the left. This is not the node model itself but |
|
73 the XML obtained by traversing the node model and outputting it as |
|
74 XML. Constructing and using the custom node model is explained in |
|
75 the code walk-through. |
|
76 |
|
77 \section2 Running your own XQueries |
|
78 |
|
79 You can write your own XQuery files and run them in the example |
|
80 program. The file \c{xmlpatterns/filetree/queries.qrc} is the \l{The |
|
81 Qt Resource System} {resource file} for this example. It is used in |
|
82 \c{main.cpp} (\c{Q_INIT_RESOURCE(queries);}). It lists the XQuery |
|
83 files (\c{.xq}) that can be selected in the combobox. |
|
84 |
|
85 \quotefromfile examples/xmlpatterns/filetree/queries.qrc |
|
86 \printuntil |
|
87 |
|
88 To add your own queries to the example's combobox, store your |
|
89 \c{.xq} files in the \c{examples/xmlpatterns/filetree/queries} |
|
90 directory and add them to \c{queries.qrc} as shown above. |
|
91 |
|
92 \section1 Code Walk-Through |
|
93 |
|
94 The strategy is to create a custom node model that represents the |
|
95 directory tree of the computer's file system. That tree structure is |
|
96 non-XML data. The custom node model must have the same callback |
|
97 interface as the XML node models that the QtXmlPatterns query engine |
|
98 uses to execute queries. The query engine can then traverse the |
|
99 custom node model as if it were traversing the node model built from |
|
100 an XML document. |
|
101 |
|
102 The required callback interface is in QAbstractXmlNodeModel, so we |
|
103 create a custom node model by subclassing QAbstractXmlNodeModel and |
|
104 providing implementations for its pure virtual functions. For many |
|
105 cases, the implementations of several of the virtual functions are |
|
106 always the same, so QtXmlPatterns also provides QSimpleXmlNodeModel, |
|
107 which subclasses QAbstractXmlNodeModel and provides implementations |
|
108 for the callback functions that you can ignore. By subclassing |
|
109 QSimpleXmlNodeModel instead of QAbstractXmlNodeModel, you can reduce |
|
110 development time. |
|
111 |
|
112 \section2 The Custom Node Model Class: FileTree |
|
113 |
|
114 The custom node model for this example is class \c{FileTree}, which |
|
115 is derived from QSimpleXmlNodeModel. \c{FileTree} implements all the |
|
116 callback functions that don't have standard implementations in |
|
117 QSimpleXmlNodeModel. When you implement your own custom node model, |
|
118 you must provide implementations for these callback functions: |
|
119 |
|
120 \snippet examples/xmlpatterns/filetree/filetree.h 0 |
|
121 \snippet examples/xmlpatterns/filetree/filetree.h 1 |
|
122 |
|
123 The \c{FileTree} class declares four data members: |
|
124 |
|
125 \snippet examples/xmlpatterns/filetree/filetree.h 2 |
|
126 |
|
127 The QVector \c{m_fileInfos} will contain the node model. Each |
|
128 QFileInfo in the vector will represent a file or a directory in the |
|
129 file system. At this point it is instructive to note that although |
|
130 the node model class for this example (\c{FileTree}) actually builds |
|
131 and contains the custom node model, building the custom node model |
|
132 isn't always required. The node model class for the \l{QObject XML |
|
133 Model Example} {QObject node model example} does not build its node |
|
134 model but instead uses an already existing QObject tree as its node |
|
135 model and just implements the callback interface for that already |
|
136 existing data structure. In this file system example, however, |
|
137 although we have an already existing data structure, i.e. the file |
|
138 system, that data structure is not in memory and is not in a form we |
|
139 can use. So we must build an analog of the file system in memory |
|
140 from instances of QFileInfo, and we use that analog as the custom |
|
141 node model. |
|
142 |
|
143 The two sets of flags, \c{m_filterAllowAll} and \c{m_sortFlags}, |
|
144 contain OR'ed flags from QDir::Filters and QDir::SortFlags |
|
145 respectively. They are set by the \c{FileTree} constructor and used |
|
146 in calls to QDir::entryInfoList() for getting the child list for a |
|
147 directory node, i.e. a QFileInfoList containing the file and |
|
148 directory nodes for all the immediate children of a directory. |
|
149 |
|
150 The QVector \c{m_names} is an auxiliary component of the node |
|
151 model. It holds the XML element and attribute names (QXmlName) for |
|
152 all the node types that will be found in the node model. \c{m_names} |
|
153 is indexed by the enum \c{FileTree::Type}, which specifies the node |
|
154 types: |
|
155 |
|
156 \target Node_Type |
|
157 \snippet examples/xmlpatterns/filetree/filetree.h 4 |
|
158 |
|
159 \c{Directory} and \c{File} will represent the XML element nodes for |
|
160 directories and files respectively, and the other enum values will |
|
161 represent the XML attribute nodes for a file's path, name, suffix, |
|
162 its size in bytes, and its mime type. The \c{FileTree} constructor |
|
163 initializes \c{m_names} with an appropriate QXmlName for each |
|
164 element and attribute type: |
|
165 |
|
166 \snippet examples/xmlpatterns/filetree/filetree.cpp 2 |
|
167 |
|
168 Note that the constructor does \e{not} pre-build the entire node |
|
169 model. Instead, the node model is built \e{incrementally} as the |
|
170 query engine evaluates a query. To see how the query engine causes |
|
171 the node model to be built incrementally, see \l{Building And |
|
172 Traversing The Node Model}. To see how the query engine accesses the |
|
173 node model, see \l{Accessing the node model}. See also: \l{Node |
|
174 Model Building Strategy}. |
|
175 |
|
176 \section3 Accessing The Node Model |
|
177 |
|
178 Since the node model is stored outside the query engine in the |
|
179 \c{FileTree} class, the query engine knows nothing about it and can |
|
180 only access it by calling functions in the callback interface. When |
|
181 the query engine calls any callback function to access data in the |
|
182 node model, it passes a QXmlNodeModelIndex to identify the node in |
|
183 the node model that it wants to access. Hence all the virtual |
|
184 functions in the callback interface use a QXmlNodeModelIndex to |
|
185 uniquely identify a node in the model. |
|
186 |
|
187 We use the index of a QFileInfo in \c{m_fileInfos} to uniquely |
|
188 identify a node in the node model. To get the QXmlNodeModelIndex for |
|
189 a QFileInfo, the class uses the private function \c{toNodeIndex()}: |
|
190 |
|
191 \target main toNodeIndex |
|
192 \snippet examples/xmlpatterns/filetree/filetree.cpp 1 |
|
193 |
|
194 It searches the \c{m_fileInfos} vector for a QFileInfo that matches |
|
195 \c{fileInfo}. If a match is found, its array index is passed to |
|
196 QAbstractXmlNodeModel::createIndex() as the \c data value for the |
|
197 QXmlNodeIndex. If no match is found, the unmatched QFileInfo is |
|
198 appended to the vector, so this function is also doing the actual |
|
199 incremental model building (see \l{Building And Traversing The Node |
|
200 Model}). |
|
201 |
|
202 Note that \c{toNodeIndex()} gets a \l{Node_Type} {node type} as the |
|
203 second parameter, which it just passes on to |
|
204 \l{QAbstractXmlNodeModel::createIndex()} {createIndex()} as the |
|
205 \c{additionalData} value. Logically, this second parameter |
|
206 represents a second dimension in the node model, where the first |
|
207 dimension represents the \e element nodes, and the second dimension |
|
208 represents each element's attribute nodes. The meaning is that each |
|
209 QFileInfo in the \c{m_fileInfos} vector can represent an \e{element} |
|
210 node \e{and} one or more \e{attribute} nodes. In particular, the |
|
211 QFileInfo for a file will contain the values for the attribute nodes |
|
212 path, name, suffix, size, and mime type (see |
|
213 \c{FileTree::attributes()}). Since the attributes are contained in |
|
214 the QFileInfo of the file element, there aren't actually any |
|
215 attribute nodes in the node model. Hence, we can use a QVector for |
|
216 \c{m_fileInfos}. |
|
217 |
|
218 A convenience overloading of \l{toNodeIndex of convenience} |
|
219 {toNodeIndex()} is also called in several places, wherever it is |
|
220 known that the QXmlNodeModelIndex being requested is for a directory |
|
221 or a file and not for an attribute. The convenience function takes |
|
222 only the QFileInfo parameter and calls the other \l{main toNodeIndex} |
|
223 {toNodeIndex()}, after obtaining either the Directory or File node |
|
224 type directly from the QFileInfo: |
|
225 |
|
226 \target toNodeIndex of convenience |
|
227 \snippet examples/xmlpatterns/filetree/filetree.cpp 0 |
|
228 |
|
229 Note that the auxiliary vector \c{m_names} is accessed using the |
|
230 \l{Node_Type} {node type}, for example: |
|
231 |
|
232 \snippet examples/xmlpatterns/filetree/filetree.cpp 3 |
|
233 |
|
234 Most of the virtual functions in the callback interface are as |
|
235 simple as the ones described so far, but the callback function used |
|
236 for traversing (and building) the node model is more complex. |
|
237 |
|
238 \section3 Building And Traversing The Node Model |
|
239 |
|
240 The node model in \c{FileTree} is not fully built before the query |
|
241 engine begins evaluating the query. In fact, when the query engine |
|
242 begins evaluating its first query, the only node in the node model |
|
243 is the one representing the root directory for the selected part of |
|
244 the file system. See \l{The UI Class: MainWindow} below for details |
|
245 about how the UI triggers creation of the model. |
|
246 |
|
247 The query engine builds the node model incrementally each time it |
|
248 calls the \l{next node on axis} {nextFromSimpleAxis()} callback |
|
249 function, as it traverses the node model to evaluate a query. Thus |
|
250 the query engine only builds the region of the node model that it |
|
251 needs for evaluating the query. |
|
252 |
|
253 \l{next node on axis} {nextFromSimpleAxis()} takes an |
|
254 \l{QAbstractXmlNodeModel::SimpleAxis} {axis identifier} and a |
|
255 \l{QXmlNodeModelIndex} {node identifier} as parameters. The |
|
256 \l{QXmlNodeModelIndex} {node identifier} represents the \e{context |
|
257 node} (i.e. the query engine's current location in the model), and |
|
258 the \l{QAbstractXmlNodeModel::SimpleAxis} {axis identifier} |
|
259 represents the direction we want to move from the context node. The |
|
260 function finds the appropriate next node and returns its |
|
261 QXmlNodeModelIndex. |
|
262 |
|
263 \l{next node on axis} {nextFromSimpleAxis()} is where most of the |
|
264 work of implementing a custom node model will be required. The |
|
265 obvious way to do it is to use a switch statement with a case for |
|
266 each \l{QAbstractXmlNodeModel::SimpleAxis} {axis}. |
|
267 |
|
268 \target next node on axis |
|
269 \snippet examples/xmlpatterns/filetree/filetree.cpp 4 |
|
270 |
|
271 The first thing this function does is call \l{to file info} |
|
272 {toFileInfo()} to get the QFileInfo of the context node. The use of |
|
273 QVector::at() here is guaranteed to succeed because the context node |
|
274 must already be in the node model, and hence must have a QFileInfo |
|
275 in \c{m_fileInfos}. |
|
276 |
|
277 \target to file info |
|
278 \snippet examples/xmlpatterns/filetree/filetree.cpp 6 |
|
279 |
|
280 The \l{QAbstractXmlNodeModel::Parent} {Parent} case looks up the |
|
281 context node's parent by constructing a QFileInfo from the context |
|
282 node's \l{QFileInfo::absoluteFilePath()} {path} and passing it to |
|
283 \l{main toNodeIndex} {toNodeIndex()} to find the QFileInfo in |
|
284 \c{m_fileInfos}. |
|
285 |
|
286 The \l{QAbstractXmlNodeModel::FirstChild} {FirstChild} case requires |
|
287 that the context node must be a directory, because a file doesn't |
|
288 have children. If the context node is not a directory, a default |
|
289 constructed QXmlNodeModelIndex is returned. Otherwise, |
|
290 QDir::entryInfoList() constructs a QFileInfoList of the context |
|
291 node's children. The first QFileInfo in the list is passed to |
|
292 \l{toNodeIndex of convenience} {toNodeIndex()} to get its |
|
293 QXmlNodeModelIndex. Note that this will add the child to the node |
|
294 model, if it isn't in the model yet. |
|
295 |
|
296 The \l{QAbstractXmlNodeModel::PreviousSibling} {PreviousSibling} and |
|
297 \l{QAbstractXmlNodeModel::NextSibling} {NextSibling} cases call the |
|
298 \l{nextSibling helper} {nextSibling() helper function}. It takes the |
|
299 QXmlNodeModelIndex of the context node, the QFileInfo of the context |
|
300 node, and an offest of +1 or -1. The context node is a child of some |
|
301 parent, so the function gets the parent and then gets the child list |
|
302 for the parent. The child list is searched to find the QFileInfo of |
|
303 the context node. It must be there. Then the offset is applied, -1 |
|
304 for the previous sibling and +1 for the next sibling. The resulting |
|
305 index is passed to \l{toNodeIndex of convenience} {toNodeIndex()} to |
|
306 get its QXmlNodeModelIndex. Note again that this will add the |
|
307 sibling to the node model, if it isn't in the model yet. |
|
308 |
|
309 \target nextSibling helper |
|
310 \snippet examples/xmlpatterns/filetree/filetree.cpp 5 |
|
311 |
|
312 \section2 The UI Class: MainWindow |
|
313 |
|
314 The example's UI is a conventional Qt GUI application inheriting |
|
315 QMainWindow and the Ui_MainWindow base class generated by |
|
316 \l{Qt Designer Manual} {Qt Designer}. |
|
317 |
|
318 \snippet examples/xmlpatterns/filetree/mainwindow.h 0 |
|
319 |
|
320 It contains the custom node model (\c{m_fileTree}) and an instance |
|
321 of QXmlNodeModelIndex (\c{m_fileNode}) used for holding the node |
|
322 index for the root of the file system subtree. \c{m_fileNode} will |
|
323 be bound to a $variable in the XQuery to be evaluated. |
|
324 |
|
325 Two actions of interest are handled by slot functions: \l{Selecting |
|
326 A Directory To Model} and \l{Selecting And Running An XQuery}. |
|
327 |
|
328 \section3 Selecting A Directory To Model |
|
329 |
|
330 The user selects \c{File->Open Directory} to choose a directory to |
|
331 be loaded into the custom node model. Choosing a directory signals |
|
332 the \c{on_actionOpenDirectory_triggered()} slot: |
|
333 |
|
334 \snippet examples/xmlpatterns/filetree/mainwindow.cpp 1 |
|
335 |
|
336 The slot function simply calls the private function |
|
337 \c{loadDirectory()} with the path of the chosen directory: |
|
338 |
|
339 \target the standard code pattern |
|
340 \snippet examples/xmlpatterns/filetree/mainwindow.cpp 4 |
|
341 |
|
342 \c{loadDirectory()} demonstrates a standard code pattern for using |
|
343 QtXmlPatterns programatically. First it gets the node model index |
|
344 for the root of the selected directory. Then it creates an instance |
|
345 of QXmlQuery and calls QXmlQuery::bindVariable() to bind the node |
|
346 index to the XQuery variable \c{$fileTree}. It then calls |
|
347 QXmlQuery::setQuery() to load the XQuery text. |
|
348 |
|
349 \note QXmlQuery::bindVariable() must be called \e before calling |
|
350 QXmlQuery::setQuery(), which loads and parses the XQuery text and |
|
351 must have access to the variable binding as the text is parsed. |
|
352 |
|
353 The next lines create an output device for outputting the query |
|
354 result, which is then used to create a QXmlFormatter to format the |
|
355 query result as XML. QXmlQuery::evaluateTo() is called to run the |
|
356 query, and the formatted XML output is displayed in the left panel |
|
357 of the UI window. |
|
358 |
|
359 Finally, the private function \l{Selecting And Running An XQuery} |
|
360 {evaluateResult()} is called to run the currently selected XQuery |
|
361 over the custom node model. |
|
362 |
|
363 \note As described in \l{Building And Traversing The Node Model}, |
|
364 the \c FileTree class wants to build the custom node model |
|
365 incrementally as it evaluates the XQuery. But, because the |
|
366 \c{loadDirectory()} function runs the \c{wholeTree.xq} XQuery, it |
|
367 actually builds the entire node model anyway. See \l{Node Model |
|
368 Building Strategy} for a discussion about building your custom node |
|
369 model. |
|
370 |
|
371 \section3 Selecting And Running An XQuery |
|
372 |
|
373 The user chooses an XQuery from the menu in the combobox on the |
|
374 right. Choosing an XQuery signals the |
|
375 \c{on_queryBox_currentIndexChanged()} slot: |
|
376 |
|
377 \snippet examples/xmlpatterns/filetree/mainwindow.cpp 2 |
|
378 |
|
379 The slot function opens and loads the query file and then calls the |
|
380 private function \c{evaluateResult()} to run the query: |
|
381 |
|
382 \snippet examples/xmlpatterns/filetree/mainwindow.cpp 3 |
|
383 |
|
384 \c{evaluateResult()} is a second example of the same code pattern |
|
385 shown in \l{the standard code pattern} {loadDirectory()}. In this |
|
386 case, it runs the XQuery currently selected in the combobox instead |
|
387 of \c{qrc:/queries/wholeTree.xq}, and it outputs the query result to |
|
388 the panel on the lower right of the UI window. |
|
389 |
|
390 \section2 Node Model Building Strategy |
|
391 |
|
392 We saw that the \l{The Custom Node Model Class: FileTree} {FileTree} |
|
393 tries to build its custom node model incrementally, but we also saw |
|
394 that the \l{the standard code pattern} {MainWindow::loadDirectory()} |
|
395 function in the UI class immediately subverts the incremental build |
|
396 by running the \c{wholeTree.xq} XQuery, which traverses the entire |
|
397 selected directory, thereby causing the entire node model to be |
|
398 built. |
|
399 |
|
400 If we want to preserve the incremental build capability of the |
|
401 \c{FileTree} class, we can strip the running of \c{wholeTree.xq} out |
|
402 of \l{the standard code pattern} {MainWindow::loadDirectory()}: |
|
403 |
|
404 \snippet examples/xmlpatterns/filetree/mainwindow.cpp 5 |
|
405 \snippet examples/xmlpatterns/filetree/mainwindow.cpp 6 |
|
406 |
|
407 Note, however, that \c{FileTree} doesn't have the capability of |
|
408 deleting all or part of the node model. The node model, once built, |
|
409 is only deleted when the \c{FileTree} instance goes out of scope. |
|
410 |
|
411 In this example, each element node in the node model represents a |
|
412 directory or a file in the computer's file system, and each node is |
|
413 represented by an instance of QFileInfo. An instance of QFileInfo is |
|
414 not costly to produce, but you might imagine a node model where |
|
415 building new nodes is very costly. In such cases, the capability to |
|
416 build the node model incrementally is important, because it allows |
|
417 us to only build the region of the model we need for evaluating the |
|
418 query. In other cases, it will be simpler to just build the entire |
|
419 node model. |
|
420 |
|
421 */ |