examples/xmlpatterns/filetree/filetree.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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 examples 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 #include <QtCore/QUrl>
       
    43 #include <QtCore/QVariant>
       
    44 #include <QtXmlPatterns/QXmlNamePool>
       
    45 #include "filetree.h"
       
    46 
       
    47 /*
       
    48 The model has two types of nodes: elements & attributes.
       
    49 
       
    50     <directory name="">
       
    51         <file name="">
       
    52         </file>
       
    53     </directory>
       
    54 
       
    55   In QXmlNodeModelIndex we store two values. QXmlNodeIndex::data()
       
    56   is treated as a signed int, and it is an index into m_fileInfos
       
    57   unless it is -1, in which case it has no meaning and the value
       
    58   of QXmlNodeModelIndex::additionalData() is a Type name instead.
       
    59  */
       
    60 
       
    61 /*!
       
    62   The constructor passes \a pool to the base class, then loads an
       
    63   internal vector with an instance of QXmlName for each of the
       
    64   strings "file", "directory", "fileName", "filePath", "size",
       
    65   "mimeType", and "suffix".
       
    66  */
       
    67 //! [2]
       
    68 FileTree::FileTree(const QXmlNamePool& pool)
       
    69   : QSimpleXmlNodeModel(pool),
       
    70     m_filterAllowAll(QDir::AllEntries |
       
    71                      QDir::AllDirs |
       
    72                      QDir::NoDotAndDotDot |
       
    73                      QDir::Hidden),
       
    74     m_sortFlags(QDir::Name)
       
    75 {
       
    76     QXmlNamePool np = namePool();
       
    77     m_names.resize(7);
       
    78     m_names[File]               = QXmlName(np, QLatin1String("file"));
       
    79     m_names[Directory]          = QXmlName(np, QLatin1String("directory"));
       
    80     m_names[AttributeFileName]  = QXmlName(np, QLatin1String("fileName"));
       
    81     m_names[AttributeFilePath]  = QXmlName(np, QLatin1String("filePath"));
       
    82     m_names[AttributeSize]      = QXmlName(np, QLatin1String("size"));
       
    83     m_names[AttributeMIMEType]  = QXmlName(np, QLatin1String("mimeType"));
       
    84     m_names[AttributeSuffix]    = QXmlName(np, QLatin1String("suffix"));
       
    85 }
       
    86 //! [2]
       
    87 
       
    88 /*!
       
    89   Returns the QXmlNodeModelIndex for the model node representing
       
    90   the directory \a dirName.
       
    91 
       
    92   It calls QDir::cleanPath(), because an instance of QFileInfo
       
    93   constructed for a path ending in '/' will return the empty string in
       
    94   fileName(), instead of the directory name.
       
    95 */
       
    96 QXmlNodeModelIndex FileTree::nodeFor(const QString& dirName) const
       
    97 {
       
    98     QFileInfo dirInfo(QDir::cleanPath(dirName));
       
    99     Q_ASSERT(dirInfo.exists());
       
   100     return toNodeIndex(dirInfo);
       
   101 }
       
   102 
       
   103 /*!
       
   104   Since the value will always be in m_fileInfos, it is safe for
       
   105   us to return a const reference to it.
       
   106  */
       
   107 //! [6]
       
   108 const QFileInfo&
       
   109 FileTree::toFileInfo(const QXmlNodeModelIndex &nodeIndex) const
       
   110 {
       
   111     return m_fileInfos.at(nodeIndex.data());
       
   112 }
       
   113 //! [6]
       
   114 
       
   115 /*!
       
   116   Returns the model node index for the node specified by the
       
   117   QFileInfo and node Type.
       
   118  */
       
   119 //! [1]
       
   120 QXmlNodeModelIndex
       
   121 FileTree::toNodeIndex(const QFileInfo &fileInfo, Type attributeName) const
       
   122 {
       
   123     const int indexOf = m_fileInfos.indexOf(fileInfo);
       
   124 
       
   125     if (indexOf == -1) {
       
   126         m_fileInfos.append(fileInfo);
       
   127         return createIndex(m_fileInfos.count()-1, attributeName);
       
   128     }
       
   129     else
       
   130         return createIndex(indexOf, attributeName);
       
   131 }
       
   132 //! [1]
       
   133 
       
   134 /*!
       
   135   Returns the model node index for the node specified by the
       
   136   QFileInfo, which must be a  Type::File or Type::Directory.
       
   137  */
       
   138 //! [0]
       
   139 QXmlNodeModelIndex FileTree::toNodeIndex(const QFileInfo &fileInfo) const
       
   140 {
       
   141     return toNodeIndex(fileInfo, fileInfo.isDir() ? Directory : File);
       
   142 }
       
   143 //! [0]
       
   144 
       
   145 /*!
       
   146   This private helper function is only called by nextFromSimpleAxis().
       
   147   It is called whenever nextFromSimpleAxis() is called with an axis
       
   148   parameter of either \c{PreviousSibling} or \c{NextSibling}. 
       
   149  */
       
   150 //! [5]
       
   151 QXmlNodeModelIndex FileTree::nextSibling(const QXmlNodeModelIndex &nodeIndex,
       
   152                                          const QFileInfo &fileInfo,
       
   153                                          qint8 offset) const
       
   154 {
       
   155     Q_ASSERT(offset == -1 || offset == 1);
       
   156 
       
   157     // Get the context node's parent.
       
   158     const QXmlNodeModelIndex parent(nextFromSimpleAxis(Parent, nodeIndex));
       
   159 
       
   160     if (parent.isNull())
       
   161         return QXmlNodeModelIndex();
       
   162 
       
   163     // Get the parent's child list.
       
   164     const QFileInfo parentFI(toFileInfo(parent));
       
   165     Q_ASSERT(Type(parent.additionalData()) == Directory);
       
   166     const QFileInfoList siblings(QDir(parentFI.absoluteFilePath()).entryInfoList(QStringList(),
       
   167                                                                                  m_filterAllowAll,
       
   168                                                                                  m_sortFlags));
       
   169     Q_ASSERT_X(!siblings.isEmpty(), Q_FUNC_INFO, "Can't happen! We started at a child.");
       
   170 
       
   171     // Find the index of the child where we started.
       
   172     const int indexOfMe = siblings.indexOf(fileInfo);
       
   173 
       
   174     // Apply the offset.
       
   175     const int siblingIndex = indexOfMe + offset;
       
   176     if (siblingIndex < 0 || siblingIndex > siblings.count() - 1)
       
   177         return QXmlNodeModelIndex();
       
   178     else
       
   179         return toNodeIndex(siblings.at(siblingIndex));
       
   180 }
       
   181 //! [5]
       
   182 
       
   183 /*!
       
   184   This function is called by the QtXmlPatterns query engine when it
       
   185   wants to move to the next node in the model. It moves along an \a
       
   186   axis, \e from the node specified by \a nodeIndex.
       
   187 
       
   188   This function is usually the one that requires the most design and
       
   189   implementation work, because the implementation depends on the
       
   190   perhaps unique structure of your non-XML data.
       
   191 
       
   192   There are \l {QAbstractXmlNodeModel::SimpleAxis} {four values} for
       
   193   \a axis that the implementation must handle, but there are really
       
   194   only two axes, i.e., vertical and horizontal. Two of the four values
       
   195   specify direction on the vertical axis (\c{Parent} and
       
   196   \c{FirstChild}), and the other two values specify direction on the
       
   197   horizontal axis (\c{PreviousSibling} and \c{NextSibling}).
       
   198 
       
   199   The typical implementation will be a \c switch statement with
       
   200   a case for each of the four \a axis values.
       
   201  */
       
   202 //! [4]
       
   203 QXmlNodeModelIndex
       
   204 FileTree::nextFromSimpleAxis(SimpleAxis axis, const QXmlNodeModelIndex &nodeIndex) const
       
   205 {
       
   206     const QFileInfo fi(toFileInfo(nodeIndex));
       
   207     const Type type = Type(nodeIndex.additionalData());
       
   208 
       
   209     if (type != File && type != Directory) {
       
   210         Q_ASSERT_X(axis == Parent, Q_FUNC_INFO, "An attribute only has a parent!");
       
   211         return toNodeIndex(fi, Directory);
       
   212     }
       
   213 
       
   214     switch (axis) {
       
   215         case Parent:
       
   216             return toNodeIndex(QFileInfo(fi.path()), Directory);
       
   217             
       
   218         case FirstChild:
       
   219         {
       
   220             if (type == File) // A file has no children.
       
   221                 return QXmlNodeModelIndex();
       
   222             else {
       
   223                 Q_ASSERT(type == Directory);
       
   224                 Q_ASSERT_X(fi.isDir(), Q_FUNC_INFO, "It isn't really a directory!");
       
   225                 const QDir dir(fi.absoluteFilePath());
       
   226                 Q_ASSERT(dir.exists());
       
   227 
       
   228                 const QFileInfoList children(dir.entryInfoList(QStringList(),
       
   229                                                                m_filterAllowAll,
       
   230                                                                m_sortFlags));
       
   231                 if (children.isEmpty())
       
   232                     return QXmlNodeModelIndex();
       
   233                 const QFileInfo firstChild(children.first());
       
   234                 return toNodeIndex(firstChild);
       
   235             }
       
   236         }
       
   237         
       
   238         case PreviousSibling:
       
   239             return nextSibling(nodeIndex, fi, -1);
       
   240 
       
   241         case NextSibling:
       
   242             return nextSibling(nodeIndex, fi, 1);
       
   243     }
       
   244 
       
   245     Q_ASSERT_X(false, Q_FUNC_INFO, "Don't ever get here!");
       
   246     return QXmlNodeModelIndex();
       
   247 }
       
   248 //! [4]
       
   249 
       
   250 /*!
       
   251   No matter what part of the file system we model (the whole file
       
   252   tree or a subtree), \a node will always have \c{file:///} as
       
   253   the document URI.
       
   254  */
       
   255 QUrl FileTree::documentUri(const QXmlNodeModelIndex &node) const
       
   256 {
       
   257     Q_UNUSED(node);
       
   258     return QUrl("file:///");
       
   259 }
       
   260 
       
   261 /*!
       
   262   This function returns QXmlNodeModelIndex::Element if \a node
       
   263   is a directory or a file, and QXmlNodeModelIndex::Attribute
       
   264   otherwise.
       
   265  */
       
   266 QXmlNodeModelIndex::NodeKind
       
   267 FileTree::kind(const QXmlNodeModelIndex &node) const
       
   268 {
       
   269     switch (Type(node.additionalData())) {
       
   270         case Directory:
       
   271         case File:
       
   272             return QXmlNodeModelIndex::Element;
       
   273         default:
       
   274             return QXmlNodeModelIndex::Attribute;
       
   275     }
       
   276 }
       
   277 
       
   278 /*!
       
   279   No order is defined for this example, so we always return
       
   280   QXmlNodeModelIndex::Precedes, just to keep everyone happy.
       
   281  */
       
   282 QXmlNodeModelIndex::DocumentOrder
       
   283 FileTree::compareOrder(const QXmlNodeModelIndex&,
       
   284                        const QXmlNodeModelIndex&) const
       
   285 {
       
   286     return QXmlNodeModelIndex::Precedes;
       
   287 }
       
   288 
       
   289 /*!
       
   290   Returns the name of \a node. The caller guarantees that \a node is
       
   291   not null and that it is contained in this node model.
       
   292  */
       
   293 //! [3]
       
   294 QXmlName FileTree::name(const QXmlNodeModelIndex &node) const
       
   295 {
       
   296     return m_names.at(node.additionalData());
       
   297 }
       
   298 //! [3]
       
   299 
       
   300 /*!
       
   301   Always returns the QXmlNodeModelIndex for the root of the
       
   302   file system, i.e. "/".
       
   303  */
       
   304 QXmlNodeModelIndex FileTree::root(const QXmlNodeModelIndex &node) const
       
   305 {
       
   306     Q_UNUSED(node);
       
   307     return toNodeIndex(QFileInfo(QLatin1String("/")));
       
   308 }
       
   309 
       
   310 /*!
       
   311   Returns the typed value for \a node, which must be either an
       
   312   attribute or an element. The QVariant returned represents the atomic
       
   313   value of an attribute or the atomic value contained in an element.
       
   314 
       
   315   If the QVariant is returned as a default constructed variant,
       
   316   it means that \a node has no typed value.
       
   317  */
       
   318 QVariant FileTree::typedValue(const QXmlNodeModelIndex &node) const
       
   319 {
       
   320     const QFileInfo &fi = toFileInfo(node);
       
   321 
       
   322     switch (Type(node.additionalData())) {
       
   323         case Directory:
       
   324             // deliberate fall through.
       
   325         case File:
       
   326             return QString();
       
   327         case AttributeFileName:
       
   328             return fi.fileName();
       
   329         case AttributeFilePath:
       
   330             return fi.filePath();
       
   331         case AttributeSize:
       
   332             return fi.size();
       
   333         case AttributeMIMEType:
       
   334             {
       
   335                 /* We don't have any MIME detection code currently, so return
       
   336                  * the most generic one. */
       
   337                 return QLatin1String("application/octet-stream");
       
   338             }
       
   339         case AttributeSuffix:
       
   340             return fi.suffix();
       
   341     }
       
   342 
       
   343     Q_ASSERT_X(false, Q_FUNC_INFO, "This line should never be reached.");
       
   344     return QString();
       
   345 }
       
   346 
       
   347 /*!
       
   348   Returns the attributes of \a element. The caller guarantees
       
   349   that \a element is an element in this node model.
       
   350  */
       
   351 QVector<QXmlNodeModelIndex>
       
   352 FileTree::attributes(const QXmlNodeModelIndex &element) const
       
   353 {
       
   354     QVector<QXmlNodeModelIndex> result;
       
   355 
       
   356     /* Both elements has this attribute. */
       
   357     const QFileInfo &forElement = toFileInfo(element);
       
   358     result.append(toNodeIndex(forElement, AttributeFilePath));
       
   359     result.append(toNodeIndex(forElement, AttributeFileName));
       
   360 
       
   361     if (Type(element.additionalData() == File)) {
       
   362         result.append(toNodeIndex(forElement, AttributeSize));
       
   363         result.append(toNodeIndex(forElement, AttributeSuffix));
       
   364         //result.append(toNodeIndex(forElement, AttributeMIMEType));
       
   365     }
       
   366     else {
       
   367         Q_ASSERT(element.additionalData() == Directory);
       
   368     }
       
   369 
       
   370     return result;
       
   371 }
       
   372