tools/assistant/lib/qhelpprojectdata.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 Qt Assistant 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 "qhelpprojectdata_p.h"
       
    43 
       
    44 #include <QtCore/QDir>
       
    45 #include <QtCore/QFileInfo>
       
    46 #include <QtCore/QStack>
       
    47 #include <QtCore/QMap>
       
    48 #include <QtCore/QRegExp>
       
    49 #include <QtCore/QVariant>
       
    50 #include <QtXml/QXmlStreamReader>
       
    51 
       
    52 QT_BEGIN_NAMESPACE
       
    53 
       
    54 class QHelpProjectDataPrivate : public QXmlStreamReader
       
    55 {
       
    56 public:
       
    57     void readData(const QByteArray &contents);
       
    58 
       
    59     QString virtualFolder;
       
    60     QString namespaceName;
       
    61     QString rootPath;
       
    62 
       
    63     QStringList fileList;
       
    64     QList<QHelpDataCustomFilter> customFilterList;
       
    65     QList<QHelpDataFilterSection> filterSectionList;
       
    66     QMap<QString, QVariant> metaData;
       
    67 
       
    68     QString errorMsg;
       
    69 
       
    70 private:
       
    71     void readProject();
       
    72     void readCustomFilter();
       
    73     void readFilterSection();
       
    74     void readTOC();
       
    75     void readKeywords();
       
    76     void readFiles();
       
    77     void raiseUnknownTokenError();
       
    78     void addMatchingFiles(const QString &pattern);
       
    79 
       
    80     QMap<QString, QStringList> dirEntriesCache;
       
    81 };
       
    82 
       
    83 void QHelpProjectDataPrivate::raiseUnknownTokenError()
       
    84 {
       
    85     raiseError(QObject::tr("Unknown token."));
       
    86 }
       
    87 
       
    88 void QHelpProjectDataPrivate::readData(const QByteArray &contents)
       
    89 {
       
    90     addData(contents);
       
    91     while (!atEnd()) {
       
    92         readNext();
       
    93         if (isStartElement()) {
       
    94             if (name() == QLatin1String("QtHelpProject")
       
    95                 && attributes().value(QLatin1String("version")) == QLatin1String("1.0"))
       
    96                 readProject();
       
    97             else
       
    98                 raiseError(QObject::tr("Unknown token. Expected \"QtHelpProject\"!"));
       
    99         }
       
   100     }
       
   101 
       
   102     if (hasError()) {
       
   103         raiseError(QObject::tr("Error in line %1: %2").arg(lineNumber())
       
   104             .arg(errorString()));
       
   105     }
       
   106 }
       
   107 
       
   108 void QHelpProjectDataPrivate::readProject()
       
   109 {
       
   110     while (!atEnd()) {
       
   111         readNext();
       
   112         if (isStartElement()) {
       
   113             if (name() == QLatin1String("virtualFolder")) {
       
   114                 virtualFolder = readElementText();
       
   115                 if (virtualFolder.contains(QLatin1String("/")))
       
   116                     raiseError(QObject::tr("A virtual folder must not contain a \'/\' character!"));
       
   117             } else if (name() == QLatin1String("namespace")) {
       
   118                 namespaceName = readElementText();
       
   119                 if (namespaceName.contains(QLatin1String("/")))
       
   120                     raiseError(QObject::tr("A namespace must not contain a \'/\' character!"));
       
   121             } else if (name() == QLatin1String("customFilter")) {
       
   122                 readCustomFilter();
       
   123             } else if (name() == QLatin1String("filterSection")) {
       
   124                 readFilterSection();
       
   125             } else if (name() == QLatin1String("metaData")) {
       
   126                 QString n = attributes().value(QLatin1String("name")).toString();
       
   127                 if (!metaData.contains(n))
       
   128                     metaData[n] = attributes().value(QLatin1String("value")).toString();
       
   129                 else
       
   130                     metaData.insert(n, attributes().value(QLatin1String("value")).toString());
       
   131             } else {
       
   132                 raiseUnknownTokenError();
       
   133             }
       
   134         } else if (isEndElement() && name() == QLatin1String("QtHelpProject")) {
       
   135             if (namespaceName.isEmpty())
       
   136                 raiseError(QObject::tr("Missing namespace in QtHelpProject."));
       
   137             else if (virtualFolder.isEmpty())
       
   138                 raiseError(QObject::tr("Missing virtual folder in QtHelpProject"));
       
   139             break;
       
   140         }
       
   141     }
       
   142 }
       
   143 
       
   144 void QHelpProjectDataPrivate::readCustomFilter()
       
   145 {
       
   146     QHelpDataCustomFilter filter;
       
   147     filter.name = attributes().value(QLatin1String("name")).toString();
       
   148     while (!atEnd()) {
       
   149         readNext();
       
   150         if (isStartElement()) {
       
   151             if (name() == QLatin1String("filterAttribute"))
       
   152                 filter.filterAttributes.append(readElementText());
       
   153             else
       
   154                 raiseUnknownTokenError();
       
   155         } else if (isEndElement() && name() == QLatin1String("customFilter")) {
       
   156             break;
       
   157         }
       
   158     }
       
   159     customFilterList.append(filter);
       
   160 }
       
   161 
       
   162 void QHelpProjectDataPrivate::readFilterSection()
       
   163 {
       
   164     filterSectionList.append(QHelpDataFilterSection());
       
   165     while (!atEnd()) {
       
   166         readNext();
       
   167         if (isStartElement()) {
       
   168             if (name() == QLatin1String("filterAttribute"))
       
   169                 filterSectionList.last().addFilterAttribute(readElementText());
       
   170             else if (name() == QLatin1String("toc"))
       
   171                 readTOC();
       
   172             else if (name() == QLatin1String("keywords"))
       
   173                 readKeywords();
       
   174             else if (name() == QLatin1String("files"))
       
   175                 readFiles();
       
   176             else
       
   177                 raiseUnknownTokenError();
       
   178         } else if (isEndElement() && name() == QLatin1String("filterSection")) {
       
   179             break;
       
   180         }
       
   181     }
       
   182 }
       
   183 
       
   184 void QHelpProjectDataPrivate::readTOC()
       
   185 {
       
   186     QStack<QHelpDataContentItem*> contentStack;
       
   187     QHelpDataContentItem *itm = 0;
       
   188     while (!atEnd()) {
       
   189         readNext();
       
   190         if (isStartElement()) {
       
   191             if (name() == QLatin1String("section")) {
       
   192                 QString title = attributes().value(QLatin1String("title")).toString();
       
   193                 QString ref = attributes().value(QLatin1String("ref")).toString();
       
   194                 if (contentStack.isEmpty()) {
       
   195                     itm = new QHelpDataContentItem(0, title, ref);
       
   196                     filterSectionList.last().addContent(itm);
       
   197                 } else {
       
   198                     itm = new QHelpDataContentItem(contentStack.top(), title, ref);
       
   199                 }
       
   200                 contentStack.push(itm);
       
   201             } else {
       
   202                 raiseUnknownTokenError();
       
   203             }
       
   204         } else if (isEndElement()) {
       
   205             if (name() == QLatin1String("section")) {
       
   206                 contentStack.pop();
       
   207                 continue;
       
   208             } else if (name() == QLatin1String("toc") && contentStack.isEmpty()) {
       
   209                 break;
       
   210             } else {
       
   211                 raiseUnknownTokenError();
       
   212             }
       
   213         }
       
   214     }
       
   215 }
       
   216 
       
   217 void QHelpProjectDataPrivate::readKeywords()
       
   218 {
       
   219     while (!atEnd()) {
       
   220         readNext();
       
   221         if (isStartElement()) {
       
   222             if (name() == QLatin1String("keyword")) {
       
   223                 if (attributes().value(QLatin1String("ref")).toString().isEmpty()
       
   224                     || (attributes().value(QLatin1String("name")).toString().isEmpty()
       
   225                     && attributes().value(QLatin1String("id")).toString().isEmpty()))
       
   226                     raiseError(QObject::tr("Missing attribute in keyword at line %1.")
       
   227                         .arg(lineNumber()));
       
   228                 filterSectionList.last().addIndex(
       
   229                     QHelpDataIndexItem(attributes().value(QLatin1String("name")).toString(),
       
   230                         attributes().value(QLatin1String("id")).toString(),
       
   231                         attributes().value(QLatin1String("ref")).toString()));
       
   232             } else {
       
   233                 raiseUnknownTokenError();
       
   234             }
       
   235         } else if (isEndElement()) {
       
   236             if (name() == QLatin1String("keyword"))
       
   237                 continue;
       
   238             else if (name() == QLatin1String("keywords"))
       
   239                 break;
       
   240             else
       
   241                 raiseUnknownTokenError();
       
   242         }
       
   243     }
       
   244 }
       
   245 
       
   246 void QHelpProjectDataPrivate::readFiles()
       
   247 {
       
   248     while (!atEnd()) {
       
   249         readNext();
       
   250         if (isStartElement()) {
       
   251             if (name() == QLatin1String("file"))
       
   252                 addMatchingFiles(readElementText());
       
   253             else
       
   254                 raiseUnknownTokenError();
       
   255         } else if (isEndElement()) {
       
   256             if (name() == QLatin1String("file"))
       
   257                 continue;
       
   258             else if (name() == QLatin1String("files"))
       
   259                 break;
       
   260             else
       
   261                 raiseUnknownTokenError();
       
   262         }
       
   263     }
       
   264 }
       
   265 
       
   266 // Expand file pattern and add matches into list. If the pattern does not match
       
   267 // any files, insert the pattern itself so the QHelpGenerator will emit a
       
   268 // meaningful warning later.
       
   269 void QHelpProjectDataPrivate::addMatchingFiles(const QString &pattern)
       
   270 {
       
   271     // The pattern matching is expensive, so we skip it if no
       
   272     // wildcard symbols occur in the string.
       
   273     if (!pattern.contains('?') && !pattern.contains('*')
       
   274         && !pattern.contains('[') && !pattern.contains(']')) {
       
   275         filterSectionList.last().addFile(pattern);
       
   276         return;
       
   277     }
       
   278 
       
   279     QFileInfo fileInfo(rootPath + '/' + pattern);
       
   280     const QDir &dir = fileInfo.dir();
       
   281     const QString &path = dir.canonicalPath();
       
   282 
       
   283     // QDir::entryList() is expensive, so we cache the results.
       
   284     QMap<QString, QStringList>::ConstIterator it = dirEntriesCache.find(path);
       
   285     const QStringList &entries = it != dirEntriesCache.constEnd() ?
       
   286                                  it.value() : dir.entryList(QDir::Files);
       
   287     if (it == dirEntriesCache.constEnd())
       
   288         dirEntriesCache.insert(path, entries);
       
   289 
       
   290     bool matchFound = false;
       
   291 #ifdef Q_OS_WIN
       
   292     Qt::CaseSensitivity cs = Qt::CaseInsensitive;
       
   293 #else
       
   294     Qt::CaseSensitivity cs = Qt::CaseSensitive;
       
   295 #endif
       
   296     QRegExp regExp(fileInfo.fileName(), cs, QRegExp::Wildcard);
       
   297     foreach (const QString &file, entries) {
       
   298         if (regExp.exactMatch(file)) {
       
   299             matchFound = true;
       
   300             filterSectionList.last().
       
   301                 addFile(QFileInfo(pattern).dir().path() + '/' + file);
       
   302         }
       
   303     }
       
   304     if (!matchFound)
       
   305         filterSectionList.last().addFile(pattern);
       
   306 }
       
   307 
       
   308 /*!
       
   309     \internal
       
   310     \class QHelpProjectData
       
   311     \since 4.4
       
   312     \brief The QHelpProjectData class stores all information found
       
   313     in a Qt help project file.
       
   314 
       
   315     The structure is filled with data by calling readData(). The
       
   316     specified file has to have the Qt help project file format in
       
   317     order to be read successfully. Possible reading errors can be
       
   318     retrieved by calling errorMessage().
       
   319 */
       
   320 
       
   321 /*!
       
   322     Constructs a Qt help project data structure.
       
   323 */
       
   324 QHelpProjectData::QHelpProjectData()
       
   325 {
       
   326     d = new QHelpProjectDataPrivate;
       
   327 }
       
   328 
       
   329 /*!
       
   330     Destroys the help project data.
       
   331 */
       
   332 QHelpProjectData::~QHelpProjectData()
       
   333 {
       
   334     delete d;
       
   335 }
       
   336 
       
   337 /*!
       
   338     Reads the file \a fileName and stores the help data. The file has to
       
   339     have the Qt help project file format. Returns true if the file
       
   340     was successfully read, otherwise false.
       
   341 
       
   342     \sa errorMessage()
       
   343 */
       
   344 bool QHelpProjectData::readData(const QString &fileName)
       
   345 {
       
   346     d->rootPath = QFileInfo(fileName).absolutePath();
       
   347     QFile file(fileName);
       
   348     if (!file.open(QIODevice::ReadOnly)) {
       
   349         d->errorMsg = QObject::tr("The input file %1 could not be opened!")
       
   350             .arg(fileName);
       
   351         return false;
       
   352     }
       
   353 
       
   354     d->readData(file.readAll());
       
   355     return !d->hasError();
       
   356 }
       
   357 
       
   358 /*!
       
   359     Returns an error message if the reading of the Qt help project
       
   360     file failed. Otherwise, an empty QString is returned.
       
   361 
       
   362     \sa readData()
       
   363 */
       
   364 QString QHelpProjectData::errorMessage() const
       
   365 {
       
   366     if (d->hasError())
       
   367         return d->errorString();
       
   368     return d->errorMsg;
       
   369 }
       
   370 
       
   371 /*!
       
   372     \internal
       
   373 */
       
   374 QString QHelpProjectData::namespaceName() const
       
   375 {
       
   376     return d->namespaceName;
       
   377 }
       
   378 
       
   379 /*!
       
   380     \internal
       
   381 */
       
   382 QString QHelpProjectData::virtualFolder() const
       
   383 {
       
   384     return d->virtualFolder;
       
   385 }
       
   386 
       
   387 /*!
       
   388     \internal
       
   389 */
       
   390 QList<QHelpDataCustomFilter> QHelpProjectData::customFilters() const
       
   391 {
       
   392     return d->customFilterList;
       
   393 }
       
   394 
       
   395 /*!
       
   396     \internal
       
   397 */
       
   398 QList<QHelpDataFilterSection> QHelpProjectData::filterSections() const
       
   399 {
       
   400     return d->filterSectionList;
       
   401 }
       
   402 
       
   403 /*!
       
   404     \internal
       
   405 */
       
   406 QMap<QString, QVariant> QHelpProjectData::metaData() const
       
   407 {
       
   408     return d->metaData;
       
   409 }
       
   410 
       
   411 /*!
       
   412     \internal
       
   413 */
       
   414 QString QHelpProjectData::rootPath() const
       
   415 {
       
   416     return d->rootPath;
       
   417 }
       
   418 
       
   419 QT_END_NAMESPACE