• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pcolby / doxlee / 9043275636

11 May 2024 11:16AM UTC coverage: 74.783% (+0.4%) from 74.336%
9043275636

push

github

pcolby
Being implementing the compound parsing

14 of 14 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

344 of 460 relevant lines covered (74.78%)

1178.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.09
/src/doxml.cpp
1
// SPDX-FileCopyrightText: 2021-2024 Paul Colby <git@colby.id.au>
2
// SPDX-License-Identifier: LGPL-3.0-or-later
3

4
#include "doxml.h"
5

6
#include "variant.h"
7

8
#include <QCoreApplication>
9
#include <QDebug>
10
#include <QJsonDocument> // \todo Remove; only for early dev / debugging.
11
#include <QLoggingCategory>
12
#include <QXmlStreamReader>
13

14
/// Shorten the QStringLiteral macro for readability.
15
#define QSL(str) QStringLiteral(str)
16

17
/// Shorten QCoreApplication::translate calls for readability.
18
#define QTR(str) QCoreApplication::translate("doxml", str)
19

20
namespace doxlee {
21

22
namespace doxml {
23

24
static Q_LOGGING_CATEGORY(lc, "doxlee.doxml", QtInfoMsg);
240✔
25

26
QPair<QStringList,QStringList> kinds(const QDir &doxmlDir)
24✔
27
{
28
    return kinds(doxmlDir.absoluteFilePath(QSL("index.xsd")));
42✔
29
}
30

31
QPair<QStringList,QStringList> kinds(const QString &indexXsdPath)
24✔
32
{
33
    // Open the file for reading.
34
    QFile file(indexXsdPath);
24✔
35
    if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
24✔
36
        qCWarning(lc).noquote() << QTR("Error opening file for reading: %1").arg(indexXsdPath);
×
37
        return { };
38
    }
39

40
    QStringList compoundKinds, memberKinds;
18✔
41

42
    // Parse the opening <schema> element.
43
    QXmlStreamReader xml(&file);
24✔
44
    if (!xml.readNextStartElement()) {
24✔
45
        qCWarning(lc).noquote() << QTR("Invalid XML file: %1 - %2").arg(indexXsdPath, xml.errorString());
×
46
        return { };
47
    }
48
    if (xml.name() != QSL("schema")) {
30✔
49
        qCWarning(lc).noquote() << QTR("File is not a Doxygen XML index schema: %1 - %2")
×
50
            .arg(indexXsdPath, xml.name().toString());
×
51
        return { };
52
    }
53

54
    // Parse the contained 'kind' elements.
55
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
192✔
56
        if (xml.name() == QSL("simpleType")) {
336✔
57
            const QString nameAttribute = xml.attributes().value(QSL("name")).toString();
84✔
58
            if ((nameAttribute == QSL("CompoundKind")) || (nameAttribute == QSL("MemberKind"))) {
84✔
59
                while ((!xml.atEnd()) && (xml.readNextStartElement())) {
96✔
60
                    if (xml.name() == QSL("restriction")) {
96✔
61
                        QStringList &kindsList = (nameAttribute == QLatin1String("CompoundKind"))
84✔
62
                            ? compoundKinds : memberKinds;
48✔
63
                        while ((!xml.atEnd()) && (xml.readNextStartElement())) {
696✔
64
                            if (xml.name() == QSL("enumeration")) {
1,296✔
65
                                kindsList.append(xml.attributes().value(QSL("value")).toString());
1,134✔
66
                            }
67
                            xml.skipCurrentElement();
648✔
68
                        }
69
                    } else xml.skipCurrentElement();
×
70
                }
71
            } else xml.skipCurrentElement();
×
72
        } else xml.skipCurrentElement();
132✔
73
    }
74
    qCInfo(lc).noquote() << QTR("Parsed %1 compound kind(s), and %2 member kind(s) from %3")
60✔
75
        .arg(compoundKinds.size()).arg(memberKinds.size()).arg(indexXsdPath);
42✔
76
    return { compoundKinds, memberKinds };
24✔
77
}
48✔
78

79
QVariantMap parseIndex(const QDir &doxmlDir, const bool extraIndexes)
24✔
80
{
81
    return parseIndex(doxmlDir.absoluteFilePath(QSL("index.xml")), extraIndexes);
42✔
82
}
83

84
QVariantMap parseIndex(const QString &indexXmlPath, const bool extraIndexes)
24✔
85
{
86
    // Open the file for reading.
87
    QFile file(indexXmlPath);
24✔
88
    if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
24✔
89
        qCWarning(lc).noquote() << QTR("Error opening file for reading: %1").arg(indexXmlPath);
×
90
        return QVariantMap();
91
    }
92

93
    // Parse the opening <doxygenindex> element.
94
    QXmlStreamReader xml(&file);
24✔
95
    if (!xml.readNextStartElement()) {
24✔
96
        qCWarning(lc).noquote() << QTR("Invalid XML file: %1 - %2").arg(indexXmlPath, xml.errorString());
×
97
        return QVariantMap();
98
    }
99
    qCDebug(lc) << xml.name() << "version" << xml.attributes().value(QSL("version"))
30✔
100
             << xml.attributes().value(QSL("xml:lang")).toString();
×
101
    if (xml.name() != QSL("doxygenindex")) {
30✔
102
        qCWarning(lc).noquote() << QTR("File is not a Doxygen XML index: %1 - %2")
×
103
                                .arg(indexXmlPath, xml.name().toString());
×
104
        return QVariantMap();
105
    }
106
    QVariantMap indexMap;
18✔
107
    indexMap.insert(QSL("doxygenVersion"), xml.attributes().value(QSL("version")).toString());
60✔
108
    indexMap.insert(QSL("doxygenLanguage"), xml.attributes().value(QSL("xml:lang")).toString());
60✔
109

110
    // Parse the contained <compound> elements.
111
    QVariantList compounds;
18✔
112
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
192✔
113
        if (xml.name() == QSL("compound")) {
336✔
114
            QVariantMap compound;
126✔
115
            compound.insert(QSL("refid"), xml.attributes().value(QSL("refid")).toString());
420✔
116
            compound.insert(QSL("kind"), xml.attributes().value(QSL("kind")).toString());
420✔
117
            if ((!xml.readNextStartElement()) || (xml.name() != QSL("name"))) {
336✔
118
                qCWarning(lc).noquote() << QTR(" %1:%2:%3 <compound> does not begin with <name>")
×
119
                    .arg(indexXmlPath).arg(xml.lineNumber()).arg(xml.columnNumber());
×
120
                return QVariantMap();
121
            }
122
            compound.insert(QSL("name"), xml.readElementText());
294✔
123
            //qCDebug(lc) << __func__ << "compound" << compound;
124
            QVariantList members;
126✔
125
            while ((!xml.atEnd()) && (xml.readNextStartElement())) {
576✔
126
                if (xml.name() == QSL("member")) {
816✔
127
                    QVariantMap member;
306✔
128
                    member.insert(QSL("refid"), xml.attributes().value(QSL("refid")).toString());
1,020✔
129
                    member.insert(QSL("kind"), xml.attributes().value(QSL("kind")).toString());
1,020✔
130
                    if ((!xml.readNextStartElement()) || (xml.name() != QSL("name"))) {
816✔
131
                        qCWarning(lc).noquote() << QTR("%1:%2:%3 <member> does not begin with <name>")
×
132
                            .arg(indexXmlPath).arg(xml.lineNumber()).arg(xml.columnNumber());
×
133
                        return QVariantMap();
134
                    }
135
                    member.insert(QSL("name"), xml.readElementText());
714✔
136
                    //qCDebug(lc) << __func__ << "member" << member;
137
                    members.append(member);
714✔
138
                    xml.skipCurrentElement();
408✔
139
                } else {
102✔
140
                    qCWarning(lc).noquote() << QTR("Skipping unknown <%1> element at %2:%3:%4")
×
141
                        .arg(xml.name().toString(), indexXmlPath).arg(xml.lineNumber()).arg(xml.columnNumber());
×
142
                    xml.skipCurrentElement();
×
143
                }
144
            }
145
            compound.insert(QSL("members"), members);
294✔
146
            compounds.append(compound);
294✔
147
        } else {
42✔
148
            qCWarning(lc).noquote() << QTR("Skipping unknown <%1> element at %2:%3:%4")
×
149
                .arg(xml.name().toString(), indexXmlPath).arg(xml.lineNumber()).arg(xml.columnNumber());
×
150
            xml.skipCurrentElement();
×
151
        }
152
    }
153
    qCInfo(lc).noquote() << QTR("Parsed %1 compound(s) from %2").arg(compounds.size()).arg(indexXmlPath);
60✔
154
    indexMap.insert(QSL("compoundsList"), compounds);
42✔
155
    if (extraIndexes) {
24✔
156
        #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
157
        indexMap.insert(doxml::extraIndexes(compounds));
42✔
158
        #else
159
        const QVariantMap extra = doxml::extraIndexes(compounds);
160
        for (auto iter = extra.constBegin(); iter != extra.constEnd(); ++iter) {
161
            indexMap.insert(iter.key(), iter.value());
162
        }
163
        #endif
164
    }
165
    return indexMap;
166
}
24✔
167

168
QVariantMap extraIndexes(const QVariantList &compounds)
24✔
169
{
170
    QHash<QString,QVariantList> compoundsByKind, membersByKind;
18✔
171
    QVariantMap compoundsByRefId, membersByRefId;
18✔
172
    for (const QVariant &compound: compounds) {
192✔
173
        const QVariantMap compoundMap = compound.toMap();
168✔
174
        {
175
            const QString kind = compoundMap.value(QSL("kind")).toString();
336✔
176
            const QString refid = compoundMap.value(QSL("refid")).toString();
336✔
177
            compoundsByKind[kind].append(compound);
168✔
178
            compoundsByRefId.insert(refid, compound);
168✔
179
        }
42✔
180

181
        const QVariantList members = compoundMap.value(QSL("members")).toList();
336✔
182
        for (const QVariant &member: members) {
576✔
183
            const QVariantMap memberMap = member.toMap();
408✔
184
            const QString kind = memberMap.value(QSL("kind")).toString();
816✔
185
            const QString refid = memberMap.value(QSL("refid")).toString();
816✔
186
            membersByKind[kind].append(member);
408✔
187
            membersByRefId.insert(refid, member);
408✔
188
        }
102✔
189
    }
42✔
190
    for (QVariantList &compoundsList: compoundsByKind) sortBy(compoundsList, QSL("name"));
198✔
191
    for (QVariantList &membersList: membersByKind)     sortBy(membersList,   QSL("name"));
114✔
192
    return QVariantMap{
193
        { QSL("compoundsByKind" ), toVariant(compoundsByKind) },
48✔
194
        { QSL("compoundsByRefId"), compoundsByRefId           },
24✔
195
        { QSL("membersByKind"   ), toVariant(membersByKind)   },
48✔
196
        { QSL("membersByRefId"  ), membersByRefId             },
24✔
197
    };
186✔
198
}
24✔
199

200
QVariantMap parseCompound(const QDir &doxmlDir, const QString &refId)
72✔
201
{
202
    return parseCompound(doxmlDir.absoluteFilePath(QSL("%1.xml").arg(refId)));
126✔
203
}
204

205
QVariantMap parseCompound(const QString &compoundXmlPath)
72✔
206
{
207
    // Open the compound XML file.
208
    QFile file(compoundXmlPath);
72✔
209
    if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
72✔
210
        qCWarning(lc).noquote() << QTR("Error opening file for reading: %1").arg(compoundXmlPath);
×
211
        return QVariantMap();
212
    }
213

214
    // Parse, and fetch the compoundDef element.
215
    QXmlStreamReader xml(&file);
72✔
216
    const QVariantMap compoundDef = toVariant(xml).value(QSL("doxygen")).toMap().value(QSL("compounddef")).toMap();
144✔
217
    if (compoundDef.isEmpty()) {
72✔
UNCOV
218
        qCWarning(lc).noquote() << QTR("Error reading compond defintion: %1").arg(compoundXmlPath);
×
219
        return QVariantMap();
220
    }
221
    qCDebug(lc).noquote() << QJsonDocument::fromVariant(compoundDef).toJson();
90✔
222

223
    // Map the compoundDef properties to our own compound map.
224
    QVariantMap compound{
225
        { QSL("name"), compoundDef.value(QSL("compoundname")).toMap().value(QSL(".Characters")) },
144✔
226
        /// \todo Provide stand way of fetch, and trimming, all text from each of these...
227
        // { QSL("brief"),       compoundDef.value(QSL("briefdescription")).toMap().value(QSL(".Characters")) },
228
        // { QSL("description"), compoundDef.value(QSL("fulldescription" )).toMap().value(QSL(".Characters")) },
229
    };
216✔
230

231
    // Copy call compoundDef attributes to top-level compound properties.
232
    for (const QVariant &attribute: compoundDef) {
1,056✔
233
        const auto map = attribute.toMap();
984✔
234
        const auto name = map.find(QSL("QualifiedName"));
984✔
235
        const auto value = map.find(QSL("Value"));
984✔
236
        if ((name != map.constEnd()) && (value != map.constEnd())) {
984✔
237
            compound.insert(name->toString(), *value);
546✔
238
        }
239
    }
246✔
240

241
    /// \todo More parsing here...
242

243
    qCDebug(lc).noquote() << QJsonDocument::fromVariant(compound).toJson();
90✔
244
    return compound;
245
}
72✔
246

247
} // namespace doxml
248

249
} // namespace doxlee
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc