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