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 examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
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** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include <QtCore/QUrl>
52#include <QtCore/QVariant>
53#include <QtXmlPatterns/QXmlNamePool>
54#include "filetree.h"
55
56/*
57The model has two types of nodes: elements & attributes.
58
59 <directory name="">
60 <file name="">
61 </file>
62 </directory>
63
64 In QXmlNodeModelIndex we store two values. QXmlNodeIndex::data()
65 is treated as a signed int, and it is an index into m_fileInfos
66 unless it is -1, in which case it has no meaning and the value
67 of QXmlNodeModelIndex::additionalData() is a Type name instead.
68 */
69
70/*!
71 The constructor passes \a pool to the base class, then loads an
72 internal vector with an instance of QXmlName for each of the
73 strings "file", "directory", "fileName", "filePath", "size",
74 "mimeType", and "suffix".
75 */
76//! [2]
77FileTree::FileTree(const QXmlNamePool& pool)
78 : QSimpleXmlNodeModel(pool),
79 m_filterAllowAll(QDir::AllEntries |
80 QDir::AllDirs |
81 QDir::NoDotAndDotDot |
82 QDir::Hidden),
83 m_sortFlags(QDir::Name)
84{
85 QXmlNamePool np = namePool();
86 m_names.resize(asize: 7);
87 m_names[File] = QXmlName(np, QLatin1String("file"));
88 m_names[Directory] = QXmlName(np, QLatin1String("directory"));
89 m_names[AttributeFileName] = QXmlName(np, QLatin1String("fileName"));
90 m_names[AttributeFilePath] = QXmlName(np, QLatin1String("filePath"));
91 m_names[AttributeSize] = QXmlName(np, QLatin1String("size"));
92 m_names[AttributeMIMEType] = QXmlName(np, QLatin1String("mimeType"));
93 m_names[AttributeSuffix] = QXmlName(np, QLatin1String("suffix"));
94}
95//! [2]
96
97/*!
98 Returns the QXmlNodeModelIndex for the model node representing
99 the directory \a dirName.
100
101 It calls QDir::cleanPath(), because an instance of QFileInfo
102 constructed for a path ending in '/' will return the empty string in
103 fileName(), instead of the directory name.
104*/
105QXmlNodeModelIndex FileTree::nodeFor(const QString& dirName) const
106{
107 QFileInfo dirInfo(QDir::cleanPath(path: dirName));
108 Q_ASSERT(dirInfo.exists());
109 return toNodeIndex(index: dirInfo);
110}
111
112/*!
113 Since the value will always be in m_fileInfos, it is safe for
114 us to return a const reference to it.
115 */
116//! [6]
117const QFileInfo&
118FileTree::toFileInfo(const QXmlNodeModelIndex &nodeIndex) const
119{
120 return m_fileInfos.at(i: nodeIndex.data());
121}
122//! [6]
123
124/*!
125 Returns the model node index for the node specified by the
126 QFileInfo and node Type.
127 */
128//! [1]
129QXmlNodeModelIndex
130FileTree::toNodeIndex(const QFileInfo &fileInfo, Type attributeName) const
131{
132 const int indexOf = m_fileInfos.indexOf(t: fileInfo);
133
134 if (indexOf == -1) {
135 m_fileInfos.append(t: fileInfo);
136 return createIndex(data: m_fileInfos.count()-1, additionalData: attributeName);
137 }
138 else
139 return createIndex(data: indexOf, additionalData: attributeName);
140}
141//! [1]
142
143/*!
144 Returns the model node index for the node specified by the
145 QFileInfo, which must be a Type::File or Type::Directory.
146 */
147//! [0]
148QXmlNodeModelIndex FileTree::toNodeIndex(const QFileInfo &fileInfo) const
149{
150 return toNodeIndex(fileInfo, attributeName: fileInfo.isDir() ? Directory : File);
151}
152//! [0]
153
154/*!
155 This private helper function is only called by nextFromSimpleAxis().
156 It is called whenever nextFromSimpleAxis() is called with an axis
157 parameter of either \c{PreviousSibling} or \c{NextSibling}.
158 */
159//! [5]
160QXmlNodeModelIndex FileTree::nextSibling(const QXmlNodeModelIndex &nodeIndex,
161 const QFileInfo &fileInfo,
162 qint8 offset) const
163{
164 Q_ASSERT(offset == -1 || offset == 1);
165
166 // Get the context node's parent.
167 const QXmlNodeModelIndex parent(nextFromSimpleAxis(Parent, nodeIndex));
168
169 if (parent.isNull())
170 return QXmlNodeModelIndex();
171
172 // Get the parent's child list.
173 const QFileInfo parentFI(toFileInfo(nodeIndex: parent));
174 Q_ASSERT(Type(parent.additionalData()) == Directory);
175 const QFileInfoList siblings(QDir(parentFI.absoluteFilePath()).entryInfoList(nameFilters: QStringList(),
176 filters: m_filterAllowAll,
177 sort: m_sortFlags));
178 Q_ASSERT_X(!siblings.isEmpty(), Q_FUNC_INFO, "Can't happen! We started at a child.");
179
180 // Find the index of the child where we started.
181 const int indexOfMe = siblings.indexOf(t: fileInfo);
182
183 // Apply the offset.
184 const int siblingIndex = indexOfMe + offset;
185 if (siblingIndex < 0 || siblingIndex > siblings.count() - 1)
186 return QXmlNodeModelIndex();
187 else
188 return toNodeIndex(fileInfo: siblings.at(i: siblingIndex));
189}
190//! [5]
191
192/*!
193 This function is called by the Qt XML Patterns query engine when it
194 wants to move to the next node in the model. It moves along an \a
195 axis, \e from the node specified by \a nodeIndex.
196
197 This function is usually the one that requires the most design and
198 implementation work, because the implementation depends on the
199 perhaps unique structure of your non-XML data.
200
201 There are \l {QAbstractXmlNodeModel::SimpleAxis} {four values} for
202 \a axis that the implementation must handle, but there are really
203 only two axes, i.e., vertical and horizontal. Two of the four values
204 specify direction on the vertical axis (\c{Parent} and
205 \c{FirstChild}), and the other two values specify direction on the
206 horizontal axis (\c{PreviousSibling} and \c{NextSibling}).
207
208 The typical implementation will be a \c switch statement with
209 a case for each of the four \a axis values.
210 */
211//! [4]
212QXmlNodeModelIndex
213FileTree::nextFromSimpleAxis(SimpleAxis axis, const QXmlNodeModelIndex &nodeIndex) const
214{
215 const QFileInfo fi(toFileInfo(nodeIndex));
216 const Type type = Type(nodeIndex.additionalData());
217
218 if (type != File && type != Directory) {
219 Q_ASSERT_X(axis == Parent, Q_FUNC_INFO, "An attribute only has a parent!");
220 return toNodeIndex(fileInfo: fi, attributeName: Directory);
221 }
222
223 switch (axis) {
224 case Parent:
225 return toNodeIndex(fileInfo: QFileInfo(fi.path()), attributeName: Directory);
226
227 case FirstChild:
228 {
229 if (type == File) // A file has no children.
230 return QXmlNodeModelIndex();
231 else {
232 Q_ASSERT(type == Directory);
233 Q_ASSERT_X(fi.isDir(), Q_FUNC_INFO, "It isn't really a directory!");
234 const QDir dir(fi.absoluteFilePath());
235 Q_ASSERT(dir.exists());
236
237 const QFileInfoList children(dir.entryInfoList(nameFilters: QStringList(),
238 filters: m_filterAllowAll,
239 sort: m_sortFlags));
240 if (children.isEmpty())
241 return QXmlNodeModelIndex();
242 const QFileInfo firstChild(children.first());
243 return toNodeIndex(fileInfo: firstChild);
244 }
245 }
246
247 case PreviousSibling:
248 return nextSibling(nodeIndex, fileInfo: fi, offset: -1);
249
250 case NextSibling:
251 return nextSibling(nodeIndex, fileInfo: fi, offset: 1);
252 }
253
254 Q_ASSERT_X(false, Q_FUNC_INFO, "Don't ever get here!");
255 return QXmlNodeModelIndex();
256}
257//! [4]
258
259/*!
260 No matter what part of the file system we model (the whole file
261 tree or a subtree), \a node will always have \c{file:///} as
262 the document URI.
263 */
264QUrl FileTree::documentUri(const QXmlNodeModelIndex &node) const
265{
266 Q_UNUSED(node);
267 return QUrl("file:///");
268}
269
270/*!
271 This function returns QXmlNodeModelIndex::Element if \a node
272 is a directory or a file, and QXmlNodeModelIndex::Attribute
273 otherwise.
274 */
275QXmlNodeModelIndex::NodeKind
276FileTree::kind(const QXmlNodeModelIndex &node) const
277{
278 switch (Type(node.additionalData())) {
279 case Directory:
280 case File:
281 return QXmlNodeModelIndex::Element;
282 default:
283 return QXmlNodeModelIndex::Attribute;
284 }
285}
286
287/*!
288 No order is defined for this example, so we always return
289 QXmlNodeModelIndex::Is, just to keep everyone happy.
290 */
291QXmlNodeModelIndex::DocumentOrder
292FileTree::compareOrder(const QXmlNodeModelIndex&,
293 const QXmlNodeModelIndex&) const
294{
295 return QXmlNodeModelIndex::Is;
296}
297
298/*!
299 Returns the name of \a node. The caller guarantees that \a node is
300 not null and that it is contained in this node model.
301 */
302//! [3]
303QXmlName FileTree::name(const QXmlNodeModelIndex &node) const
304{
305 return m_names.at(i: node.additionalData());
306}
307//! [3]
308
309/*!
310 Always returns the QXmlNodeModelIndex for the root of the
311 file system, i.e. "/".
312 */
313QXmlNodeModelIndex FileTree::root(const QXmlNodeModelIndex &node) const
314{
315 Q_UNUSED(node);
316 return toNodeIndex(fileInfo: QFileInfo(QLatin1String("/")));
317}
318
319/*!
320 Returns the typed value for \a node, which must be either an
321 attribute or an element. The QVariant returned represents the atomic
322 value of an attribute or the atomic value contained in an element.
323
324 If the QVariant is returned as a default constructed variant,
325 it means that \a node has no typed value.
326 */
327QVariant FileTree::typedValue(const QXmlNodeModelIndex &node) const
328{
329 const QFileInfo &fi = toFileInfo(nodeIndex: node);
330
331 switch (Type(node.additionalData())) {
332 case Directory:
333 // deliberate fall through.
334 case File:
335 return QString();
336 case AttributeFileName:
337 return fi.fileName();
338 case AttributeFilePath:
339 return fi.filePath();
340 case AttributeSize:
341 return fi.size();
342 case AttributeMIMEType:
343 {
344 /* We don't have any MIME detection code currently, so return
345 * the most generic one. */
346 return QLatin1String("application/octet-stream");
347 }
348 case AttributeSuffix:
349 return fi.suffix();
350 }
351
352 Q_ASSERT_X(false, Q_FUNC_INFO, "This line should never be reached.");
353 return QString();
354}
355
356/*!
357 Returns the attributes of \a element. The caller guarantees
358 that \a element is an element in this node model.
359 */
360QVector<QXmlNodeModelIndex>
361FileTree::attributes(const QXmlNodeModelIndex &element) const
362{
363 QVector<QXmlNodeModelIndex> result;
364
365 /* Both elements has this attribute. */
366 const QFileInfo &forElement = toFileInfo(nodeIndex: element);
367 result.append(t: toNodeIndex(fileInfo: forElement, attributeName: AttributeFilePath));
368 result.append(t: toNodeIndex(fileInfo: forElement, attributeName: AttributeFileName));
369
370 if (Type(element.additionalData() == File)) {
371 result.append(t: toNodeIndex(fileInfo: forElement, attributeName: AttributeSize));
372 result.append(t: toNodeIndex(fileInfo: forElement, attributeName: AttributeSuffix));
373 //result.append(toNodeIndex(forElement, AttributeMIMEType));
374 }
375 else {
376 Q_ASSERT(element.additionalData() == Directory);
377 }
378
379 return result;
380}
381
382

source code of qtxmlpatterns/examples/xmlpatterns/filetree/filetree.cpp