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