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 <jerome.pasion@digia.com>
bb10
Martin Smith 2013-04-29 10:00:45 +02:00 committed by The Qt Project
parent 542e3be40d
commit a2892ad1d7
6 changed files with 221 additions and 180 deletions

View File

@ -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<MetaStackEntry>
{
@ -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<QString> 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<ConfigVar> 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<cv.values_.size(); ++j) {
if (!value.isEmpty() && !value.endsWith(QChar('\n')))
value.append(QChar(' '));
value.append(cv.values_[j]);
high = j;
}
}
--i;
}
}
return path;
return value;
}
/*!
@ -341,20 +345,36 @@ QSet<QString> 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<ConfigVar> 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<sl.size(); ++i) {
const QString &canonicalized = location().canonicalRelativePath(sl[i]);
t.append(canonicalized);
QList<ConfigVar> 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<sl.size(); ++i) {
const QString& crp = Location::canonicalRelativePath(sl[i], d);
t.append(crp);
}
}
--i;
}
}
return t;
@ -382,32 +410,30 @@ QStringList Config::getCanonicalRelativePathList(const QString& var) const
/*!
This function should only be called when the configuration
variable \a var maps to a string list that contains file paths.
variable \a var maps to string lists that contain file paths.
It cleans the paths with QDir::cleanPath() before returning
them.
First, this function looks up the configuration variable \a var
in the location map and, if found, sets the internal variable
\c{lastLocation_} the Location that \a var maps to.
Then it looks up the configuration variable \a var in the string
list map, which maps to a string list that contains file paths.
These paths might not be clean, so QDir::cleanPath() is called
for each one. The string list returned contains cleaned paths.
*/
QStringList Config::getCleanPathList(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<sl.size(); ++i) {
t.append(QDir::cleanPath(sl[i]));
QList<ConfigVar> 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<sl.size(); ++i) {
t.append(QDir::cleanPath(sl[i].simplified()));
}
}
--i;
}
}
return t;
@ -415,7 +441,7 @@ QStringList Config::getCleanPathList(const QString& var) const
/*!
This function should only be called when the configuration
variable \a var maps to a string list that contains file paths.
variable \a var maps to string lists that contain file paths.
It cleans the paths with QDir::cleanPath() before returning
them.
@ -424,30 +450,39 @@ QStringList Config::getCleanPathList(const QString& var) const
\c{lastLocation_} the Location that \a var maps to.
Then it looks up the configuration variable \a var in the string
list map, which maps to a string list that contains file paths.
list map, which maps to one or more records that each contains a
list of file paths.
These paths might not be clean, so QDir::cleanPath() is called
for each one. The string list returned contains cleaned paths.
*/
QStringList Config::getPathList(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;
const QString d = it.value().first;
if (!sl.isEmpty()) {
t.reserve(sl.size());
for (int i=0; i<sl.size(); ++i) {
QFileInfo fileInfo;
QString path = d + "/" + QDir::cleanPath(sl[i]);
fileInfo.setFile(path);
if (!fileInfo.exists())
lastLocation_.warning(tr("File '%1' does not exist").arg(path));
else
t.append(path);
QList<ConfigVar> 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<sl.size(); ++i) {
QFileInfo fileInfo;
QString path = d + "/" + QDir::cleanPath(sl[i].simplified());
fileInfo.setFile(path);
if (!fileInfo.exists())
lastLocation_.warning(tr("File '%1' does not exist").arg(path));
else
t.append(path);
}
}
--i;
}
}
return t;
@ -509,30 +544,31 @@ QSet<QString> Config::subVars(const QString& var) const
{
QSet<QString> 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<QString> &excludedDirs,
const QSet<QString> &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;

View File

@ -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<QString, QString> 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<QString, QStringList> QStringListPair;
typedef QMultiMap<QString, QStringPair> QStringPairMultiMap;
typedef QMap<QString, QStringPair> QStringPairMap;
typedef QMap<QString, QStringListPair> QStringListPairMap;
typedef QMultiMap<QString, ConfigVar> 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<QString> getOutputFormats() const;
QString getString(const QString& var) const;
QString getPath(const QString& var) const;
QSet<QString> 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<QRegExp> getRegExpList(const QString& var) const;
QSet<QString> 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<QString> &excludedDirs = QSet<QString>(),
@ -146,12 +154,7 @@ private:
QString prog;
Location loc;
Location lastLocation_;
QMap<QString, Location> locMap;
QMap<QString, QString> stringValueMap;
QMap<QString, QStringList> stringListValueMap;
QStringPairMap stringPairMap;
QStringListPairMap stringListPairMap;
ConfigVarMultimap configVars_;
static QMap<QString, QString> uncompressedFiles;
static QMap<QString, QString> extractedDirs;

View File

@ -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_;
}
/*!

View File

@ -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<QXmlStreamWriter*> xmlWriterStack;
QStack<DitaTag> tagStack;
QStringPairMap metadataDefaults;
ConfigVarMultimap metadataDefaults;
QVector<NodeMultiMap*> nodeTypeMaps;
QVector<NodeMultiMap*> nodeSubtypeMaps;
QVector<NodeMultiMap*> pageTypeMaps;

View File

@ -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.

View File

@ -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 };