Doxygen
Loading...
Searching...
No Matches
requirement.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2026 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 * The implementation of the requirements feature is loosely based on
15 * https://github.com/doxygen/doxygen/pull/11839 provided by
16 * Rob Mellor (https://github.com/dropbearrob)
17 */
18
19#include <algorithm>
20
21#include "requirement.h"
22#include "entry.h"
23#include "util.h"
24#include "linkedmap.h"
25#include "message.h"
26#include "outputlist.h"
27#include "language.h"
28#include "section.h"
29#include "pagedef.h"
30#include "config.h"
31#include "trace.h"
32
34{
35 public:
36 Requirement(const DString &id_,const DString &file_,int line_,const DString &title_,const DString &doc_,const DString &tagFile_,const DString &extPage_) :
37 m_id(id_), m_file(file_), m_line(line_), m_title(title_), m_doc(doc_), m_tagFile(tagFile_), m_extPage(extPage_) {}
38 DString id() const override { return m_id; }
39 DString file() const override { return m_file; }
40 int line() const override { return m_line; }
41 DString title() const override { return m_title; }
42 DString doc() const override { return m_doc; }
43 DString getTagFile() const override { return m_tagFile; }
44 DString getExtPage() const override { return m_extPage; }
45 DString getOutputFileBase() const override { return ::convertNameToFile("requirements",false,true); }
46 const DefinitionVector &satisfiedBy() const override { return m_satisfiedBy; }
47 const DefinitionVector &verifiedBy() const override { return m_verifiedBy; }
48
50 {
51 auto comp = [](const Definition *c1,const Definition *c2)
52 {
53 return Config_getBool(SORT_BY_SCOPE_NAME) ?
54 dstricmp_sort(c1->qualifiedName(), c2->qualifiedName())<0 :
55 dstricmp_sort(c1->name(), c2->name())<0;
56 };
57 std::stable_sort(m_satisfiedBy.begin(), m_satisfiedBy.end(), comp);
58 std::stable_sort(m_verifiedBy.begin(), m_verifiedBy.end(), comp);
59 }
60
61 void addSatisfiedBy(const Definition *def)
62 {
63 if (auto it = std::find(m_satisfiedBy.begin(),m_satisfiedBy.end(),def); it==m_satisfiedBy.end())
64 {
65 m_satisfiedBy.push_back(def);
66 }
67 }
68
69 void addVerifiedBy(const Definition *def)
70 {
71 if (auto it = std::find(m_verifiedBy.begin(),m_verifiedBy.end(),def); it==m_verifiedBy.end())
72 {
73 m_verifiedBy.push_back(def);
74 }
75 }
76
77 private:
80 int m_line;
87};
88
95
97{
98 static RequirementManager s_instance;
99 return s_instance;
100}
101
105
107{
108 return p->reqPageDef;
109}
110
112{
113 DString tagFile;
114 DString extPage;
115 if (!Config_getBool(GENERATE_REQUIREMENTS)) // still record the requirement, so we can refer to it.
116 {
117 if (e->tagInfo())
118 {
119 //printf("External requirement %s title=%s fileName=%s tagName=%s anchor=%s\n",
120 // qPrint(e->name),qPrint(e->type),qPrint(e->tagInfo()->fileName),qPrint(e->tagInfo()->tagName),qPrint(e->tagInfo()->anchor));
121 tagFile = e->tagInfo()->tagName;
122 extPage = e->tagInfo()->fileName;
123 // register requirement id as anchor; for non-external links this is
124 // done in commentscan.l in the comment block containing the @requirement command
126 }
127 p->requirements.add(e->name, e->fileName, e->startLine, e->type, e->doc, tagFile, extPage);
128 }
129 else
130 {
131 if (p->reqPageDef==nullptr)
132 {
133 p->reqPageDef = addRelatedPage("requirements", // name
134 theTranslator->trRequirements(), // ptitle
135 DString(), // doc
136 "requirements", // fileName
137 1, // docLine
138 1 // startLine
139 );
140 }
141 //printf("requirement ID=%s title='%s' file=%s line=%d brief='%s' doc='%s'\n",
142 // qPrint(e->name), qPrint(e->type), qPrint(e->fileName), e->startLine, qPrint(e->brief), qPrint(e->doc));
143 DString title = parseCommentAsText(p->reqPageDef,nullptr,e->type,e->fileName,e->startLine);
144 DString doc = e->doc;
145 if (e->tagInfo())
146 {
147 //printf("External requirement %s title=%s fileName=%s tagName=%s anchor=%s\n",
148 // qPrint(e->name),qPrint(e->type),qPrint(e->tagInfo()->fileName),qPrint(e->tagInfo()->tagName),qPrint(e->tagInfo()->anchor));
149 tagFile = e->tagInfo()->tagName;
150 extPage = e->tagInfo()->fileName;
151 // register requirement id as anchor; for non-external links this is
152 // done in commentscan.l in the comment block containing the @requirement command
153 SectionManager::instance().add(e->name,"requirements",1,title,SectionType::Anchor,1);
154 }
155 if (!e->brief.empty())
156 {
157 doc.prepend(e->brief+"\n<p>");
158 }
159 p->requirements.add(e->name, e->fileName, e->startLine, title, doc, tagFile, extPage);
160
161 p->reqPageDef->setRefItems(e->sli);
162 }
163}
164
166{
167 return !p->requirements.empty();
168}
169
171{
172 RequirementIntfList result;
173 for (const auto &req : p->requirements)
174 {
175 result.push_back(req.get());
176 }
177 return result;
178}
179
181{
182 AUTO_TRACE("#requirements={}",p->requirements.size());
183 if (!Config_getBool(GENERATE_REQUIREMENTS) || p->requirements.empty()) return;
184 std::vector<const SectionInfo*> anchors;
185 std::stable_sort(p->requirements.begin(),p->requirements.end(),
186 [](const auto &left,const auto &right) { return dstricmp(left->id(),right->id()) < 0; });
187 DString doc = "<table class=\"doxtable reqlist\">";
188 doc.reserve(10*1024); // prevent too many reallocs
189 doc += "<tr><th>";
191 doc += "</th><th>";
192 doc += "</th>";
193 using RequirementPtrVector = std::vector<const Requirement*>;
194 RequirementPtrVector missingSatisfiedRef, missingVerifiedRef;
195 for (const auto &req : p->requirements) // TODO: filter out external references?
196 {
197 if (const SectionInfo *si = SectionManager::instance().find(req->id()); si!=nullptr)
198 {
199 anchors.push_back(si);
200 }
201 doc += "<tr><td valign=\"top\">";
202 doc += " \\ifile \""+req->file()+"\" \\iline "+DString().setNum(req->line())+" ";
203 doc += "\\anchor ";
204 doc += req->id();
205 doc += " ";
206 doc += "<span class=\"req_id\">";
207 if (DString tagFile = req->getTagFile(); !tagFile.empty())
208 {
209 //printf("tagFile=%s extPage=%s\n",qPrint(tagFile),qPrint(req->getExtPage()));
210 doc += "<a href=\"";
211 doc += createHtmlUrl(DString(),tagFile,true,false,req->getExtPage(),req->id());
212 doc +="\">"+req->id()+"</a>";
213 }
214 else
215 {
216 doc += req->id();
217 }
218 doc += "</span> ";
219 doc += "</td><td>";
220 doc += "<div class=\"req_title\">"+req->title()+"</div>";
221 doc += "<p/><div class=\"req_docs\">";
222 doc += req->doc();
223 req->sortReferences();
224 auto symToString = [](const Definition *sym)
225 {
226 DString symName = sym->qualifiedName();
227 if (sym->definitionType()==Definition::TypeMember)
228 {
229 const MemberDef *md = toMemberDef(sym);
230 if (!md->isObjCMethod() && md->isFunctionOrSignalSlot()) symName += "()";
231 }
232 return symName;
233 };
234 int numSatisfiedBy = static_cast<int>(req->satisfiedBy().size());
235 if (numSatisfiedBy>0)
236 {
237 doc += "<p><div class=\"satisfiedby\">";
239 writeMarkerList(theTranslator->trWriteList(numSatisfiedBy).str(),
240 numSatisfiedBy,
241 [&symToString, &refs = req->satisfiedBy()](size_t entryIndex) {
242 return symToString(refs[entryIndex]);
243 }));
244 doc += "</div></p>";
245 }
246 else
247 {
248 missingSatisfiedRef.push_back(req.get());
249 }
250 int numVerifiedBy = static_cast<int>(req->verifiedBy().size());
251 if (numVerifiedBy>0)
252 {
253 doc += "<p><div class=\"verifiedby\">";
256 numVerifiedBy,
257 [&symToString, &refs = req->verifiedBy()](size_t entryIndex) {
258 return symToString(refs[entryIndex]);
259 }));
260 doc += "</div></p>";
261 }
262 else
263 {
264 missingVerifiedRef.push_back(req.get());
265 }
266 doc += "\n</div></td></tr>\n";
267 }
268 doc += "</table>\n";
269
270 //------------
271 doc += " \\ifile \"requirements\" \\iline 1\n";
272
273 auto writeMissingRef = [&doc,&anchors](const RequirementPtrVector &reqs,
274 const char *label,const DString &section,const DString &text)
275 {
276 if (!reqs.empty())
277 {
279 const SectionInfo *si = sm.add(DString("missing_")+label,"requirements",1,section,SectionType::Section,1);
280 anchors.push_back(si);
281 doc += "\\htmlonly <div class=\"missing_";
282 doc += label;
283 doc += "\">\\endhtmlonly\n";
284 doc += DString("@section missing_")+label+" "+section+"\n"+text+"\n";
285 doc += "\\htmlonly </div>\\endhtmlonly\n";
286 }
287 };
288
289 // write list of requirements that do not have a satisfies relation
290 auto traceInfo = Config_getEnum(REQ_TRACEABILITY_INFO);
291 int numMissingSatisfied = static_cast<int>(missingSatisfiedRef.size());
292 if ((traceInfo==REQ_TRACEABILITY_INFO_t::YES || traceInfo==REQ_TRACEABILITY_INFO_t::UNSATISFIED_ONLY) && numMissingSatisfied>0)
293 {
294 writeMissingRef(missingSatisfiedRef,
295 "satisfies",
297 theTranslator->trUnsatisfiedRequirementsText(numMissingSatisfied==1,
298 writeMarkerList(theTranslator->trWriteList(numMissingSatisfied).str(),
299 numMissingSatisfied,
300 [&missingSatisfiedRef](size_t entryIndex) {
301 DString id = missingSatisfiedRef[entryIndex]->id();
302 return "@ref "+id + " \""+id+"\"";
303 })));
304 }
305
306 // write list of requirements that do not have a verifies relation
307 int numMissingVerified = static_cast<int>(missingVerifiedRef.size());
308 if ((traceInfo==REQ_TRACEABILITY_INFO_t::YES || traceInfo==REQ_TRACEABILITY_INFO_t::UNVERIFIED_ONLY) && numMissingVerified>0)
309 {
310 writeMissingRef(missingVerifiedRef,
311 "verifies",
313 theTranslator->trUnverifiedRequirementsText(numMissingVerified==1,
314 writeMarkerList(theTranslator->trWriteList(numMissingVerified).str(),
315 numMissingVerified,
316 [&missingVerifiedRef](size_t entryIndex) {
317 DString id = missingVerifiedRef[entryIndex]->id();
318 return "@ref "+id + " \""+id+"\"";
319 })));
320 }
321
322 AUTO_TRACE_ADD("doc=[[\n{}\n]]\n",doc);
323 p->reqPageDef->setDocumentation(doc,"requirements",1,false);
324 p->reqPageDef->addSectionsToDefinition(anchors);
325}
326
328{
329 return p->requirements.find(reqId);
330}
331
333{
334 for (const auto &ref : symbol->requirementReferences())
335 {
336 Requirement *req = p->requirements.find(ref.reqId());
337 if (req)
338 {
339 //printf("adding reference from %s to requirement %s\n",qPrint(symbol->name()),qPrint(ref.reqId()));
340 switch (ref.type())
341 {
342 case RequirementRefType::Satisfies: req->addSatisfiedBy(symbol); break;
343 case RequirementRefType::Verifies: req->addVerifiedBy(symbol); break;
344 }
345 }
346 else
347 {
348 warn(ref.file(),ref.line(),"Reference to unknown requirement '{}' found",ref.reqId());
349 // invalid reference (file, and line needed)
350 }
351 }
352}
353
354
356{
357 if (!Config_getBool(GENERATE_REQUIREMENTS)) return;
358 if (const RequirementIntf *req = RequirementManager::instance().find(ref.reqId()); req!=nullptr)
359 {
360 DString title = ref.reqId();
361 if (!ref.title().empty())
362 {
363 title +=" (";
364 title += parseCommentAsText(p->reqPageDef,nullptr,ref.title(),ref.file(),ref.line());
365 title +=")";
366 }
367 else if (!req->title().empty())
368 {
369 title += " (";
370 title += req->title();
371 title += ")";
372 }
373 else
374 {
375 title = ref.reqId();
376 }
377 ol.writeObjectLink(DString(),req->getOutputFileBase(),ref.reqId(),title);
378 }
379 else
380 {
381 ol.docify(ref.reqId());
382 }
383}
384
386{
387 if (!Config_getBool(GENERATE_REQUIREMENTS)) return;
388 if (!p->requirements.empty())
389 {
390 for (const auto &req : p->requirements)
391 {
392 tagFile << " <compound kind=\"requirement\">\n";
393 tagFile << " <id>" << req->id() << "</id>\n";
394 tagFile << " <title>" << convertToXML(req->title()) << "</title>\n";
395 DString fn = req->getOutputFileBase();
397 tagFile << " <filename>" << fn << "</filename>\n";
398 tagFile << " </compound>\n";
399 }
400 }
401}
402
403void splitRequirementRefs(const RequirementRefs &inputReqRefs,RequirementRefs &satisfiesRefs,RequirementRefs &verifiesRefs)
404{
405 auto makeUnique = [](RequirementRefs &uniqueRefs)
406 {
407 // sort results on itemId
408 std::stable_sort(uniqueRefs.begin(),uniqueRefs.end(),
409 [](const auto &left,const auto &right)
410 { return left.reqId()< right.reqId() ||
411 (left.reqId()==right.reqId() &&
412 dstricmp(left.title(),right.title())<0);
413 });
414
415 // filter out duplicates
416 auto last = std::unique(uniqueRefs.begin(),uniqueRefs.end(),
417 [](const auto &left,const auto &right)
418 { return left.reqId()==right.reqId() &&
419 dstricmp(left.title(),right.title())==0;
420 });
421
422 // remove unused part
423 uniqueRefs.erase(last, uniqueRefs.end());
424 };
425
426 // split into satisfied and verifies references
427 std::partition_copy(
428 inputReqRefs.begin(),
429 inputReqRefs.end(),
430 std::back_inserter(satisfiesRefs),
431 std::back_inserter(verifiesRefs),
432 [](const auto &ref) { return ref.type()==RequirementRefType::Satisfies; }
433 );
434
435 // remove duplicates
436 makeUnique(satisfiesRefs);
437 makeUnique(verifiesRefs);
438}
439
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:89
DString & setNum(short n)
Definition dstring.h:541
DString()=default
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:153
DString & prepend(const char *s)
Definition dstring.h:504
void reserve(size_t size)
Reserve space for size bytes without changing the string contents.
Definition dstring.h:222
const std::string & str() const
Definition dstring.h:634
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual const DString & name() const =0
virtual const RequirementRefs & requirementReferences() const =0
virtual DString qualifiedName() const =0
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:117
const TagInfo * tagInfo() const
Definition entry.h:178
DString fileName
file this entry was extracted from
Definition entry.h:225
DString doc
documentation block (partly parsed)
Definition entry.h:202
DString brief
brief description (doc block)
Definition entry.h:205
int startLine
start line of entry in the source
Definition entry.h:226
DString type
member type
Definition entry.h:174
DString name
member name
Definition entry.h:175
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:228
Container class representing a vector of objects with keys.
Definition linkedmap.h:36
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual bool isObjCMethod() const =0
virtual bool isFunctionOrSignalSlot() const =0
Class representing a list of output generators that are written to in parallel.
Definition outputlist.h:315
void writeObjectLink(const DString &ref, const DString &file, const DString &anchor, const DString &name)
Definition outputlist.h:439
void docify(const DString &s)
Definition outputlist.h:437
A model of a page symbol.
Definition pagedef.h:26
std::vector< const Definition * > DefinitionVector
Definition requirement.h:63
LinkedMap< Requirement > requirements
static RequirementManager & instance()
std::unique_ptr< Private > p
Definition requirement.h:95
bool hasRequirements() const
const RequirementIntf * find(const DString &reqId) const
void writeTagFile(TextStream &tagFile)
void writeRef(OutputList &ol, const RequirementRef &ref)
void addRequirement(Entry *e)
const PageDef * requirementsPage() const
void addRequirementRefsForSymbol(const Definition *symbol)
RequirementIntfList requirements() const
Class to hold requirement reference information.
Definition requirement.h:37
DString title() const
Definition requirement.h:43
DString reqId() const
Definition requirement.h:42
DString file() const
Definition requirement.h:44
int line() const
Definition requirement.h:45
class that provide information about a section.
Definition section.h:58
singleton class that owns the list of all sections
Definition section.h:135
SectionInfo * add(const SectionInfo &si)
Definition section.h:139
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:179
static constexpr int Anchor
Definition section.h:40
static constexpr int Section
Definition section.h:33
static constexpr int Requirement
Definition section.h:42
Text streaming class that buffers data.
Definition textstream.h:36
virtual DString trUnsatisfiedRequirements()=0
virtual DString trRequirements()=0
virtual DString trRequirementID()=0
virtual DString trUnverifiedRequirementsText(bool singular, const DString &list)=0
virtual DString trSatisfiedBy(const DString &list)=0
virtual DString trUnsatisfiedRequirementsText(bool singular, const DString &list)=0
virtual DString trVerifiedBy(const DString &list)=0
virtual DString trWriteList(int numEntries)=0
virtual DString trUnverifiedRequirements()=0
#define Config_getBool(name)
Definition config.h:33
#define Config_getEnum(name)
Definition config.h:35
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:49
#define AUTO_TRACE(...)
Definition docnode.cpp:48
int dstricmp_sort(const char *str1, const char *str2)
Definition dstring.h:72
Translator * theTranslator
Definition language.cpp:71
MemberDef * toMemberDef(Definition *d)
#define warn(file, line, fmt,...)
Definition message.h:97
Definition dstring.h:879
void splitRequirementRefs(const RequirementRefs &inputReqRefs, RequirementRefs &satisfiesRefs, RequirementRefs &verifiesRefs)
std::vector< const RequirementIntf * > RequirementIntfList
Definition requirement.h:77
std::vector< RequirementRef > RequirementRefs
List of requirement references.
Definition requirement.h:56
Requirement(const DString &id_, const DString &file_, int line_, const DString &title_, const DString &doc_, const DString &tagFile_, const DString &extPage_)
DString getTagFile() const override
DString m_file
DString m_extPage
void sortReferences()
const DefinitionVector & satisfiedBy() const override
void addSatisfiedBy(const Definition *def)
DString getExtPage() const override
DString m_tagFile
void addVerifiedBy(const Definition *def)
int line() const override
DString file() const override
DString m_doc
DString doc() const override
DString id() const override
DefinitionVector m_verifiedBy
DString title() const override
const DefinitionVector & verifiedBy() const override
DString m_title
DString getOutputFileBase() const override
DefinitionVector m_satisfiedBy
DString tagName
Definition entry.h:105
DString fileName
Definition entry.h:106
DString parseCommentAsText(const Definition *scope, const MemberDef *md, const DString &doc, const DString &fileName, int lineNr)
Definition util.cpp:5415
PageDef * addRelatedPage(const DString &name, const DString &ptitle, const DString &doc, const DString &fileName, int docLine, int startLine, const RefItemVector &sli, GroupDef *gd, const TagInfo *tagInfo, bool xref, SrcLangExt lang)
Definition util.cpp:4742
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:4969
DString createHtmlUrl(const DString &relPath, const DString &ref, bool href, bool isLocalFile, const DString &targetFileName, const DString &anchor)
Definition util.cpp:5782
DString convertToXML(const DString &s, bool keepEntities, const bool citeEntry)
Definition util.cpp:3956
void writeMarkerList(OutputList &ol, const std::string &markerText, size_t numMarkers, std::function< void(size_t)> replaceFunc)
Definition util.cpp:1128
A bunch of utility functions.