From a2892ad1d7584de62a1356812a4f9348f37e1d29 Mon Sep 17 00:00:00 2001 From: Martin Smith Date: Mon, 29 Apr 2013 10:00:45 +0200 Subject: [PATCH] qdoc: Config class keeps track of current path The Config class is modified to build a single multimap containing a record for each variable found in each .qdocconf file. Each reacord contains not only the name and value of the variable, but also its location in the qdocconf file it was read from and the path to that file. This single multimap replaces 3 maps in the Config class. Task-number: QTBUG-30725 Change-Id: I049a69790f943b24c014a24b55b2b39725a1b56f Reviewed-by: Jerome Pasion --- src/tools/qdoc/config.cpp | 313 +++++++++++++++------------- src/tools/qdoc/config.h | 39 ++-- src/tools/qdoc/ditaxmlgenerator.cpp | 13 +- src/tools/qdoc/ditaxmlgenerator.h | 4 +- src/tools/qdoc/location.cpp | 29 ++- src/tools/qdoc/location.h | 3 +- 6 files changed, 221 insertions(+), 180 deletions(-) diff --git a/src/tools/qdoc/config.cpp b/src/tools/qdoc/config.cpp index 766697dfc2..5467791316 100644 --- a/src/tools/qdoc/config.cpp +++ b/src/tools/qdoc/config.cpp @@ -55,8 +55,9 @@ QT_BEGIN_NAMESPACE -/* - An entry on the MetaStack. +/*! + An entry in a stack, where each entry is a list + of string values. */ class MetaStackEntry { @@ -68,14 +69,19 @@ public: QStringList next; }; -/* +/*! + Start accumulating values in a list by appending an empty + string to the list. */ void MetaStackEntry::open() { next.append(QString()); } -/* +/*! + Stop accumulating values and append the list of accumulated + values to the complete list of accumulated values. + */ void MetaStackEntry::close() { @@ -83,8 +89,10 @@ void MetaStackEntry::close() next.clear(); } -/* - ### +/*! + \class MetaStack + + This class maintains a stack of values of config file variables. */ class MetaStack : private QStack { @@ -97,12 +105,21 @@ public: QStringList getExpanded(const Location& location); }; +/*! + The default constructor pushes a new stack entry and + opens it. + */ MetaStack::MetaStack() { push(MetaStackEntry()); top().open(); } +/*! + Processes the character \a ch using the \a location. + It really just builds up a name by appending \a ch to + it. + */ void MetaStack::process(QChar ch, const Location& location) { if (ch == QLatin1Char('{')) { @@ -133,6 +150,9 @@ void MetaStack::process(QChar ch, const Location& location) top().open(); } else { + /* + This is where all the processing is done. + */ QStringList::Iterator pre = top().next.begin(); while (pre != top().next.end()) { *pre += ch; @@ -141,6 +161,9 @@ void MetaStack::process(QChar ch, const Location& location) } } +/*! + Returns the accumulated string values. + */ QStringList MetaStack::getExpanded(const Location& location) { if (count() > 1) @@ -176,9 +199,7 @@ Config::Config(const QString& programName) { loc = Location::null; lastLocation_ = Location::null; - locMap.clear(); - stringPairMap.clear(); - stringListPairMap.clear(); + configVars_.clear(); numInstances++; } @@ -210,19 +231,6 @@ void Config::load(const QString& fileName) lastLocation_ = Location::null; } -/*! - Writes the qdoc configuration data to the named file. - The previous contents of the file are overwritten. - */ -void Config::unload(const QString& fileName) -{ - QStringPairMap::ConstIterator v = stringPairMap.constBegin(); - while (v != stringPairMap.constEnd()) { - qDebug() << v.key() << " = " << v.value().second; - ++v; - } - qDebug() << "fileName:" << fileName; -} /*! Joins all the strings in \a values into a single string with the individual \a values separated by ' '. Then it inserts the result @@ -233,10 +241,7 @@ void Config::unload(const QString& fileName) */ void Config::setStringList(const QString& var, const QStringList& values) { - stringPairMap[var].first = QDir::currentPath(); - stringPairMap[var].second = values.join(QLatin1Char(' ')); - stringListPairMap[var].first = QDir::currentPath(); - stringListPairMap[var].second = values; + configVars_.insert(var,ConfigVar(var, values, QDir::currentPath())); } /*! @@ -303,30 +308,29 @@ QSet Config::getOutputFormats() const */ QString Config::getString(const QString& var) const { - if (!locMap[var].isEmpty()) - (Location&) lastLocation_ = locMap[var]; - return stringPairMap[var].second; -} - -/*! - This function looks up the variable \a var in the location map - and, if found, sets the internal variable \c{lastLocation_} to the - location that \a var maps to. - - Then it looks up \a var in the configuration variable map and, - if found, constructs a path from the pair value, which consists - of the directory path of the configuration file where the value - came from, and the value itself. The constructed path is returned. - */ -QString Config::getPath(const QString& var) const -{ - if (!locMap[var].isEmpty()) - (Location&) lastLocation_ = locMap[var]; - QString path; - if (stringPairMap.contains(var)) { - path = QDir(stringPairMap[var].first + "/" + stringPairMap[var].second).absolutePath(); + QList configVars = configVars_.values(var); + QString value; + int high = 0; + if (!configVars.empty()) { + int i = configVars.size() - 1; + while (i >= 0) { + const ConfigVar& cv = configVars[i]; + if (!cv.location_.isEmpty()) + (Location&) lastLocation_ = cv.location_; + if (!cv.values_.isEmpty()) { + if (!cv.plus_) + value.clear(); + for (int j=0; j Config::getStringSet(const QString& var) const /*! First, this function looks up the configuration variable \a var - in the location map and, if found, sets the internal variable + in the location map. If found, it sets the internal variable \c{lastLocation_} to the Location that \a var maps to. - Then it looks up the configuration variable \a var in the string - list map, and returns the string list that \a var maps to. + Then it looks up the configuration variable \a var in the map of + configuration variable records. If found, it gets a list of all + the records for \a var. Then it appends all the values for \a var + to a list and returns the list. As it appends the values from each + record, if the \a var used '=' instead of '+=' the list is cleared + before the values are appended. \note '+=' should always be used. + The final list is returned. */ QStringList Config::getStringList(const QString& var) const { - if (!locMap[var].isEmpty()) - (Location&) lastLocation_ = locMap[var]; - return stringListPairMap[var].second; + QList configVars = configVars_.values(var); + QStringList values; + if (!configVars.empty()) { + int i = configVars.size() - 1; + while (i >= 0) { + if (!configVars[i].location_.isEmpty()) + (Location&) lastLocation_ = configVars[i].location_; + if (configVars[i].plus_) + values.append(configVars[i].values_); + else + values = configVars[i].values_; + --i; + } + } + return values; } - /*! \brief Returns the a path list where all paths are canonicalized, then made relative to the config file. @@ -363,18 +383,26 @@ QStringList Config::getStringList(const QString& var) const */ QStringList Config::getCanonicalRelativePathList(const QString& var) const { - if (!locMap[var].isEmpty()) - (Location&) lastLocation_ = locMap[var]; QStringList t; - QStringListPairMap::const_iterator it = stringListPairMap.constFind(var); - if (it != stringListPairMap.constEnd()) { - const QStringList& sl = it.value().second; - if (!sl.isEmpty()) { - t.reserve(sl.size()); - for (int i=0; i configVars = configVars_.values(var); + if (!configVars.empty()) { + int i = configVars.size() - 1; + while (i >= 0) { + const ConfigVar& cv = configVars[i]; + if (!cv.location_.isEmpty()) + (Location&) lastLocation_ = cv.location_; + if (!cv.plus_) + t.clear(); + const QString d = cv.currentPath_; + const QStringList& sl = cv.values_; + if (!sl.isEmpty()) { + t.reserve(t.size() + sl.size()); + for (int i=0; i configVars = configVars_.values(var); + if (!configVars.empty()) { + int i = configVars.size() - 1; + while (i >= 0) { + const ConfigVar& cv = configVars[i]; + if (!cv.plus_) + t.clear(); + if (!cv.location_.isEmpty()) + (Location&) lastLocation_ = cv.location_; + const QStringList& sl = cv.values_; + if (!sl.isEmpty()) { + t.reserve(t.size() + sl.size()); + for (int i=0; i configVars = configVars_.values(var); + if (!configVars.empty()) { + int i = configVars.size() - 1; + while (i >= 0) { + const ConfigVar& cv = configVars[i]; + if (!cv.plus_) + t.clear(); + if (!cv.location_.isEmpty()) + (Location&) lastLocation_ = cv.location_; + const QString d = cv.currentPath_; + const QStringList& sl = cv.values_; + if (!sl.isEmpty()) { + t.reserve(t.size() + sl.size()); + for (int i=0; i Config::subVars(const QString& var) const { QSet result; QString varDot = var + QLatin1Char('.'); - QStringPairMap::ConstIterator v = stringPairMap.constBegin(); - while (v != stringPairMap.constEnd()) { - if (v.key().startsWith(varDot)) { - QString subVar = v.key().mid(varDot.length()); + ConfigVarMultimap::ConstIterator i = configVars_.constBegin(); + while (i != configVars_.constEnd()) { + if (i.key().startsWith(varDot)) { + QString subVar = i.key().mid(varDot.length()); int dot = subVar.indexOf(QLatin1Char('.')); if (dot != -1) subVar.truncate(dot); - result.insert(subVar); + if (!result.contains(subVar)) + result.insert(subVar); } - ++v; + ++i; } return result; } /*! - Same as subVars(), but in this case we return a string map - with the matching keys (stripped of the prefix \a var and - mapped to their values. The pairs are inserted into \a t + Same as subVars(), but in this case we return a config var + multimap with the matching keys (stripped of the prefix \a var + and mapped to their values. The pairs are inserted into \a t */ -void Config::subVarsAndValues(const QString& var, QStringPairMap& t) const +void Config::subVarsAndValues(const QString& var, ConfigVarMultimap& t) const { QString varDot = var + QLatin1Char('.'); - QStringPairMap::ConstIterator v = stringPairMap.constBegin(); - while (v != stringPairMap.constEnd()) { + ConfigVarMultimap::ConstIterator v = configVars_.constBegin(); + while (v != configVars_.constEnd()) { if (v.key().startsWith(varDot)) { QString subVar = v.key().mid(varDot.length()); int dot = subVar.indexOf(QLatin1Char('.')); @@ -869,7 +905,7 @@ void Config::load(Location location, const QString& fileName) Location keyLoc = location; bool plus = false; QString stringValue; - QStringList stringListValue; + QStringList rhsValues; QString word; bool inQuote = false; bool prevWordQuoted = true; @@ -931,6 +967,7 @@ void Config::load(Location location, const QString& fileName) else { /* It wasn't an include statement, so it's something else. + We must see either '=' or '+=' next. If not, fatal error. */ if (cc == '+') { plus = true; @@ -972,7 +1009,12 @@ void Config::load(Location location, const QString& fileName) if (metWord) stringValue += QLatin1Char(' '); stringValue += word; - stringListValue << word; +#if 0 + if (metWord) + rhsValues << QString(" " + word); + else +#endif + rhsValues << word; metWord = true; word.clear(); prevWordQuoted = false; @@ -988,7 +1030,7 @@ void Config::load(Location location, const QString& fileName) stringValue += QLatin1Char(' '); stringValue += word; if (!word.isEmpty()) - stringListValue << word; + rhsValues << word; metWord = true; word.clear(); prevWordQuoted = true; @@ -1025,30 +1067,9 @@ void Config::load(Location location, const QString& fileName) if (!keySyntax.exactMatch(*key)) keyLoc.fatal(tr("Invalid key '%1'").arg(*key)); - if (plus) { - if (locMap[*key].isEmpty()) { - locMap[*key] = keyLoc; - } - else { - locMap[*key].setEtc(true); - } - if (stringPairMap[*key].second.isEmpty()) { - stringPairMap[*key].first = QDir::currentPath(); - stringPairMap[*key].second = stringValue; - } - else { - stringPairMap[*key].second += QLatin1Char(' ') + stringValue; - } - stringListPairMap[*key].first = QDir::currentPath(); - stringListPairMap[*key].second += stringListValue; - } - else { - locMap[*key] = keyLoc; - stringPairMap[*key].first = QDir::currentPath(); - stringPairMap[*key].second = stringValue; - stringListPairMap[*key].first = QDir::currentPath(); - stringListPairMap[*key].second = stringListValue; - } + ConfigVarMultimap::Iterator i; + i = configVars_.insert(*key, ConfigVar(*key, rhsValues, QDir::currentPath(), keyLoc)); + i.value().plus_ = (plus ? true : false); ++key; } } @@ -1058,8 +1079,9 @@ void Config::load(Location location, const QString& fileName) } } popWorkingDir(); - if (!workingDirs_.isEmpty()) - QDir::setCurrent(QFileInfo(workingDirs_.top()).path()); + if (!workingDirs_.isEmpty()) { + QDir::setCurrent(workingDirs_.top()); + } } QStringList Config::getFilesHere(const QString& uncleanDir, @@ -1068,8 +1090,7 @@ QStringList Config::getFilesHere(const QString& uncleanDir, const QSet &excludedDirs, const QSet &excludedFiles) { - // - QString dir = location.isEmpty() ? QDir::cleanPath(uncleanDir) : location.canonicalRelativePath(uncleanDir); + QString dir = location.isEmpty() ? QDir::cleanPath(uncleanDir) : Location::canonicalRelativePath(uncleanDir); QStringList result; if (excludedDirs.contains(dir)) return result; diff --git a/src/tools/qdoc/config.h b/src/tools/qdoc/config.h index 521f1c12b8..c601cc062e 100644 --- a/src/tools/qdoc/config.h +++ b/src/tools/qdoc/config.h @@ -56,19 +56,29 @@ QT_BEGIN_NAMESPACE /* - In QStringPair, the first string is the path to a directory; - the second string is some value. + This struct contains all the information for + one config variable found in a qdocconf file. */ -typedef QPair QStringPair; +struct ConfigVar { + bool plus_; + QString name_; + QStringList values_; + QString currentPath_; + Location location_; + + ConfigVar() : plus_(false) { } + + ConfigVar(const QString& name, const QStringList& values, const QString& dir) + : plus_(true), name_(name), values_(values), currentPath_(dir) { } + + ConfigVar(const QString& name, const QStringList& values, const QString& dir, const Location& loc) + : plus_(false), name_(name), values_(values), currentPath_(dir), location_(loc) { } +}; /* - In QStringListPair, the first string is the path to a directory; - the string list is a list of string values. + In this multimap, the key is a config variable name. */ -typedef QPair QStringListPair; -typedef QMultiMap QStringPairMultiMap; -typedef QMap QStringPairMap; -typedef QMap QStringListPairMap; +typedef QMultiMap ConfigVarMultimap; class Config { @@ -79,7 +89,6 @@ public: ~Config(); void load(const QString& fileName); - void unload(const QString& fileName); void setStringList(const QString& var, const QStringList& values); const QString& programName() const { return prog; } @@ -90,7 +99,6 @@ public: QString getOutputDir() const; QSet getOutputFormats() const; QString getString(const QString& var) const; - QString getPath(const QString& var) const; QSet getStringSet(const QString& var) const; QStringList getStringList(const QString& var) const; QStringList getCanonicalRelativePathList(const QString& var) const; @@ -99,7 +107,7 @@ public: QRegExp getRegExp(const QString& var) const; QList getRegExpList(const QString& var) const; QSet subVars(const QString& var) const; - void subVarsAndValues(const QString& var, QStringPairMap& t) const; + void subVarsAndValues(const QString& var, ConfigVarMultimap& t) const; QStringList getAllFiles(const QString& filesVar, const QString& dirsVar, const QSet &excludedDirs = QSet(), @@ -146,12 +154,7 @@ private: QString prog; Location loc; Location lastLocation_; - QMap locMap; - QMap stringValueMap; - QMap stringListValueMap; - - QStringPairMap stringPairMap; - QStringListPairMap stringListPairMap; + ConfigVarMultimap configVars_; static QMap uncompressedFiles; static QMap extractedDirs; diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp index a828101551..314dda3a9d 100644 --- a/src/tools/qdoc/ditaxmlgenerator.cpp +++ b/src/tools/qdoc/ditaxmlgenerator.cpp @@ -5829,8 +5829,11 @@ bool DitaXmlGenerator::writeMetadataElements(const InnerNode* inner, QString DitaXmlGenerator::getMetadataElement(const InnerNode* inner, DitaXmlGenerator::DitaTag t) { QString s = Generator::getMetadataElement(inner, ditaTags[t]); - if (s.isEmpty()) - s = metadataDefault(t); + if (s.isEmpty()) { + QStringList sl = metadataDefault(t); + if (!sl.isEmpty()) + s = sl[0]; + } return s; } @@ -5850,7 +5853,7 @@ QStringList DitaXmlGenerator::getMetadataElements(const InnerNode* inner, { QStringList s = Generator::getMetadataElements(inner,ditaTags[t]); if (s.isEmpty()) - s.append(metadataDefault(t)); + s = metadataDefault(t); return s; } @@ -5858,9 +5861,9 @@ QStringList DitaXmlGenerator::getMetadataElements(const InnerNode* inner, Returns the value of key \a t or an empty string if \a t is not found in the map. */ -QString DitaXmlGenerator::metadataDefault(DitaTag t) const +QStringList DitaXmlGenerator::metadataDefault(DitaTag t) const { - return metadataDefaults.value(ditaTags[t]).second; + return metadataDefaults.value(ditaTags[t]).values_; } /*! diff --git a/src/tools/qdoc/ditaxmlgenerator.h b/src/tools/qdoc/ditaxmlgenerator.h index 15ef4260b2..a55ccf81c8 100644 --- a/src/tools/qdoc/ditaxmlgenerator.h +++ b/src/tools/qdoc/ditaxmlgenerator.h @@ -458,7 +458,7 @@ private: int leaveSection(); bool inSection() const { return (sectionNestingLevel > 0); } int currentSectionNestingLevel() const { return sectionNestingLevel; } - QString metadataDefault(DitaTag t) const; + QStringList metadataDefault(DitaTag t) const; QString stripMarkup(const QString& src) const; Node* collectNodesByTypeAndSubtype(const InnerNode* parent); void writeDitaRefs(const DitaRefList& ditarefs); @@ -507,7 +507,7 @@ private: static QString ditaTags[]; QStack xmlWriterStack; QStack tagStack; - QStringPairMap metadataDefaults; + ConfigVarMultimap metadataDefaults; QVector nodeTypeMaps; QVector nodeSubtypeMaps; QVector pageTypeMaps; diff --git a/src/tools/qdoc/location.cpp b/src/tools/qdoc/location.cpp index 86329b9190..91ee214aa1 100644 --- a/src/tools/qdoc/location.cpp +++ b/src/tools/qdoc/location.cpp @@ -229,15 +229,13 @@ QString Location::fileName() const /*! - * \brief Returns \a path which is canonicalized and relative to the config file. - * - * QDir::relativeFilePath does not canonicalize the paths, so - * if the config file is located at qtbase\src\widgets\doc\qtwidgets.qdocconf - * and it has a reference to any ancestor folder (e.g. ".." or even "../doc") - * \param path - * \return + \brief Returns \a path which is canonicalized and relative to the config file. + + QDir::relativeFilePath does not canonicalize the paths, so + if the config file is located at qtbase\src\widgets\doc\qtwidgets.qdocconf + and it has a reference to any ancestor folder (e.g. ".." or even "../doc") */ -QString Location::canonicalRelativePath(const QString &path) const +QString Location::canonicalRelativePath(const QString &path) { QDir configFileDir(QDir::current()); QDir dir(path); @@ -245,6 +243,21 @@ QString Location::canonicalRelativePath(const QString &path) const return configFileDir.relativeFilePath(canon); } +/*! + \brief Returns \a path which is canonicalized and relative to the \a configDir. + + QDir::relativeFilePath does not canonicalize the paths, so + if the config file is located at qtbase\src\widgets\doc\qtwidgets.qdocconf + and it has a reference to any ancestor folder (e.g. ".." or even "../doc") + */ +QString Location::canonicalRelativePath(const QString &path, const QString &configDir) +{ + QDir configFileDir(configDir); + QDir dir(path); + const QString canon = dir.canonicalPath(); + return configFileDir.relativeFilePath(canon); +} + /*! \fn int Location::lineNo() const Returns the current line number. Must not be called on an empty Location object. diff --git a/src/tools/qdoc/location.h b/src/tools/qdoc/location.h index 20ddfd076e..4604358fe5 100644 --- a/src/tools/qdoc/location.h +++ b/src/tools/qdoc/location.h @@ -81,7 +81,6 @@ public: int depth() const { return stkDepth; } const QString& filePath() const { return stkTop->filePath; } QString fileName() const; - QString canonicalRelativePath(const QString &path) const; int lineNo() const { return stkTop->lineNo; } int columnNo() const { return stkTop->columnNo; } bool etc() const { return etcetera; } @@ -101,6 +100,8 @@ public: static void logToStdErr(const QString& message); static void startLoggingProgress() { logProgress_ = true; } static void stopLoggingProgress() { logProgress_ = false; } + static QString canonicalRelativePath(const QString &path); + static QString canonicalRelativePath(const QString &path, const QString &configDir); private: enum MessageType { Warning, Error };