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 "util.h"
32
34{
35 public:
36 Requirement(const QCString &id_,const QCString &file_,int line_,const QCString &title_,const QCString &doc_,const QCString &tagFile_,const QCString &extPage_) :
37 m_id(id_), m_file(file_), m_line(line_), m_title(title_), m_doc(doc_), m_tagFile(tagFile_), m_extPage(extPage_) {}
38 QCString id() const override { return m_id; }
39 QCString file() const override { return m_file; }
40 int line() const override { return m_line; }
41 QCString title() const override { return m_title; }
42 QCString doc() const override { return m_doc; }
43 QCString getTagFile() const override { return m_tagFile; }
44 QCString getExtPage() const override { return m_extPage; }
45 QCString getOutputFileBase() const override { return ::convertNameToFile("requirements",false,true); }
46
47 using DefinitionVector = std::vector<const Definition *>;
48 const DefinitionVector &satisfiedBy() const { return m_satisfiedBy; }
49 const DefinitionVector &verifiedBy() const { return m_verifiedBy; }
50
52 {
53 auto comp = [](const Definition *c1,const Definition *c2)
54 {
55 return Config_getBool(SORT_BY_SCOPE_NAME) ?
56 qstricmp_sort(c1->qualifiedName(), c2->qualifiedName())<0 :
57 qstricmp_sort(c1->name(), c2->name())<0;
58 };
59 std::stable_sort(m_satisfiedBy.begin(), m_satisfiedBy.end(), comp);
60 std::stable_sort(m_verifiedBy.begin(), m_verifiedBy.end(), comp);
61 }
62
63 void addSatisfiedBy(const Definition *def)
64 {
65 if (auto it = std::find(m_satisfiedBy.begin(),m_satisfiedBy.end(),def); it==m_satisfiedBy.end())
66 {
67 m_satisfiedBy.push_back(def);
68 }
69 }
70
71 void addVerifiedBy(const Definition *def)
72 {
73 if (auto it = std::find(m_verifiedBy.begin(),m_verifiedBy.end(),def); it==m_verifiedBy.end())
74 {
75 m_verifiedBy.push_back(def);
76 }
77 }
78
79 private:
82 int m_line;
89};
90
97
99{
100 static RequirementManager s_instance;
101 return s_instance;
102}
103
105{
106}
107
109{
110 return p->reqPageDef;
111}
112
114{
115 if (p->reqPageDef==nullptr)
116 {
117 p->reqPageDef = addRelatedPage("requirements", // name
118 theTranslator->trRequirements(), // ptitle
119 QCString(), // doc
120 "requirements", // fileName
121 1, // docLine
122 1 // startLine
123 );
124 }
125 //printf("requirement ID=%s title='%s' file=%s line=%d brief='%s' doc='%s'\n",
126 // qPrint(e->name), qPrint(e->type), qPrint(e->fileName), e->startLine, qPrint(e->brief), qPrint(e->doc));
127 QCString tagFile;
128 QCString extPage;
129 QCString title = parseCommentAsText(p->reqPageDef,nullptr,e->type,e->fileName,e->startLine);
130 QCString doc = e->doc;
131 if (e->tagInfo())
132 {
133 //printf("External requirement %s title=%s fileName=%s tagName=%s anchor=%s\n",
134 // qPrint(e->name),qPrint(e->type),qPrint(e->tagInfo()->fileName),qPrint(e->tagInfo()->tagName),qPrint(e->tagInfo()->anchor));
135 tagFile = e->tagInfo()->tagName;
136 extPage = e->tagInfo()->fileName;
137 // register requirement id as anchor; for non-external links this is
138 // done in commentscan.l in the comment block containing the @requirement command
139 SectionManager::instance().add(e->name,"requirements",1,title,SectionType::Anchor,1);
140 }
141 if (!e->brief.isEmpty())
142 {
143 doc.prepend(e->brief+"\n<p>");
144 }
145 p->requirements.add(e->name, e->fileName, e->startLine, title, doc, tagFile, extPage);
146
147 p->reqPageDef->setRefItems(e->sli);
148}
149
151{
152 RequirementIntfList result;
153 for (const auto &req : p->requirements)
154 {
155 result.push_back(req.get());
156 }
157 return result;
158}
159
161{
162 if (p->requirements.empty()) return;
163 std::vector<const SectionInfo*> anchors;
164 std::stable_sort(p->requirements.begin(),p->requirements.end(),
165 [](const auto &left,const auto &right) { return qstricmp(left->id(),right->id()) < 0; });
166 QCString doc = "<table class=\"doxtable reqlist\">";
167 doc.reserve(10*1024); // prevent too many reallocs
168 doc += "<tr><th>";
169 doc += theTranslator->trRequirementID();
170 doc += "</th><th>";
171 doc += "</th>";
172 using RequirementPtrVector = std::vector<const Requirement*>;
173 RequirementPtrVector missingSatisfiedRef, missingVerifiedRef;
174 for (const auto &req : p->requirements) // TODO: filter out external references?
175 {
176 if (const SectionInfo *si = SectionManager::instance().find(req->id()); si!=nullptr)
177 {
178 anchors.push_back(si);
179 }
180 doc += "<tr><td valign=\"top\">";
181 doc += " \\ifile \""+req->file()+"\" \\iline "+QCString().setNum(req->line())+" ";
182 doc += "\\anchor ";
183 doc += req->id();
184 doc += " ";
185 doc += "<span class=\"req_id\">";
186 if (QCString tagFile = req->getTagFile(); !tagFile.isEmpty())
187 {
188 //printf("tagFile=%s extPage=%s\n",qPrint(tagFile),qPrint(req->getExtPage()));
189 doc += "<a href=\"";
190 doc += createHtmlUrl(QCString(),tagFile,true,false,req->getExtPage(),req->id());
191 doc +="\">"+req->id()+"</a>";
192 }
193 else
194 {
195 doc += req->id();
196 }
197 doc += "</span> ";
198 doc += "</td><td>";
199 doc += "<div class=\"req_title\">"+req->title()+"</div>";
200 doc += "<div class=\"req_docs\">";
201 doc += req->doc();
202 req->sortReferences();
203 auto symToString = [](const Definition *sym)
204 {
205 QCString symName = sym->qualifiedName();
206 if (sym->definitionType()==Definition::TypeMember)
207 {
208 const MemberDef *md = toMemberDef(sym);
209 if (!md->isObjCMethod() && md->isFunctionOrSignalSlot()) symName += "()";
210 }
211 return symName;
212 };
213 int numSatisfiedBy = static_cast<int>(req->satisfiedBy().size());
214 if (numSatisfiedBy>0)
215 {
216 doc += "<p><div class=\"satisfiedby\">";
217 doc += theTranslator->trSatisfiedBy(
218 writeMarkerList(theTranslator->trWriteList(numSatisfiedBy).str(),
219 numSatisfiedBy,
220 [&symToString, &refs = req->satisfiedBy()](size_t entryIndex) {
221 return symToString(refs[entryIndex]);
222 }));
223 doc += "</div></p>";
224 }
225 else
226 {
227 missingSatisfiedRef.push_back(req.get());
228 }
229 int numVerifiedBy = static_cast<int>(req->verifiedBy().size());
230 if (numVerifiedBy>0)
231 {
232 doc += "<p><div class=\"verifiedby\">";
233 doc += theTranslator->trVerifiedBy(
234 writeMarkerList(theTranslator->trWriteList(numVerifiedBy).str(),
235 numVerifiedBy,
236 [&symToString, &refs = req->verifiedBy()](size_t entryIndex) {
237 return symToString(refs[entryIndex]);
238 }));
239 doc += "</div></p>";
240 }
241 else
242 {
243 missingVerifiedRef.push_back(req.get());
244 }
245 doc += "</div></td></tr>\n";
246 }
247 doc += "</table>\n";
248
249 //------------
250 doc += " \\ifile \"requirements\" \\iline 1\n";
251
252 auto writeMissingRef = [&doc,&anchors](const RequirementPtrVector &reqs,
253 const char *label,const QCString &section,const QCString &text)
254 {
255 if (!reqs.empty())
256 {
258 const SectionInfo *si = sm.add(QCString("missing_")+label,"requirements",1,section,SectionType::Section,1);
259 anchors.push_back(si);
260 doc += "\\htmlonly <div class=\"missing_req\">\\endhtmlonly\n";
261 doc += QCString("@section missing_")+label+" "+section+"\n"+text+"\n";
262 doc += "\\htmlonly </div>\\endhtmlonly\n";
263 }
264 };
265
266 // write list of requirements that do not have a satisfies relation
267 auto traceInfo = Config_getEnum(REQ_TRACEABILITY_INFO);
268 int numMissingSatisfied = static_cast<int>(missingSatisfiedRef.size());
269 if ((traceInfo==REQ_TRACEABILITY_INFO_t::YES || traceInfo==REQ_TRACEABILITY_INFO_t::SATISFIES_ONLY) && numMissingSatisfied>0)
270 {
271 writeMissingRef(missingSatisfiedRef,
272 "satisfies",
273 theTranslator->trUnsatisfiedRequirements(),
274 theTranslator->trUnsatisfiedRequirementsText(numMissingSatisfied==1,
275 writeMarkerList(theTranslator->trWriteList(numMissingSatisfied).str(),
276 numMissingSatisfied,
277 [&missingSatisfiedRef](size_t entryIndex) {
278 QCString id = missingSatisfiedRef[entryIndex]->id();
279 return "@ref "+id + " \""+id+"\"";
280 })));
281 }
282
283 // write list of requirements that do not have a verifies relation
284 int numMissingVerified = static_cast<int>(missingVerifiedRef.size());
285 if ((traceInfo==REQ_TRACEABILITY_INFO_t::YES || traceInfo==REQ_TRACEABILITY_INFO_t::VERIFIES_ONLY) && numMissingVerified>0)
286 {
287 writeMissingRef(missingVerifiedRef,
288 "verifies",
289 theTranslator->trUnverifiedRequirements(),
290 theTranslator->trUnverifiedRequirementsText(numMissingVerified==1,
291 writeMarkerList(theTranslator->trWriteList(numMissingVerified).str(),
292 numMissingVerified,
293 [&missingVerifiedRef](size_t entryIndex) {
294 QCString id = missingVerifiedRef[entryIndex]->id();
295 return "@ref "+id + " \""+id+"\"";
296 })));
297 }
298
299 //printf("doc=[[\n%s\n]]\n",qPrint(doc));
300 p->reqPageDef->setDocumentation(doc,"requirements",1,false);
301 p->reqPageDef->addSectionsToDefinition(anchors);
302}
303
305{
306 return p->requirements.find(reqId);
307}
308
310{
311 for (const auto &ref : symbol->requirementReferences())
312 {
313 Requirement *req = p->requirements.find(ref.reqId());
314 if (req)
315 {
316 //printf("adding reference from %s to requirement %s\n",qPrint(symbol->name()),qPrint(ref.reqId()));
317 switch (ref.type())
318 {
319 case RequirementRefType::Satisfies: req->addSatisfiedBy(symbol); break;
320 case RequirementRefType::Verifies: req->addVerifiedBy(symbol); break;
321 }
322 }
323 else
324 {
325 warn(ref.file(),ref.line(),"Reference to unknown requirement '{}' found",ref.reqId());
326 // invalid reference (file, and line needed)
327 }
328 }
329}
330
331
333{
334 if (const RequirementIntf *req = RequirementManager::instance().find(ref.reqId()); req!=nullptr)
335 {
336 QCString title = ref.reqId();
337 if (!ref.title().isEmpty())
338 {
339 title +=" (";
340 title += parseCommentAsText(p->reqPageDef,nullptr,ref.title(),ref.file(),ref.line());
341 title +=")";
342 }
343 else if (!req->title().isEmpty())
344 {
345 title += " (";
346 title += req->title();
347 title += ")";
348 }
349 else
350 {
351 title = ref.reqId();
352 }
353 ol.writeObjectLink(QCString(),req->getOutputFileBase(),ref.reqId(),title);
354 }
355 else
356 {
357 ol.docify(ref.reqId());
358 }
359}
360
362{
363 if (!p->requirements.empty())
364 {
365 for (const auto &req : p->requirements)
366 {
367 tagFile << " <compound kind=\"requirement\">\n";
368 tagFile << " <id>" << req->id() << "</id>\n";
369 tagFile << " <title>" << convertToXML(req->title()) << "</title>\n";
370 QCString fn = req->getOutputFileBase();
372 tagFile << " <filename>" << fn << "</filename>\n";
373 tagFile << " </compound>\n";
374 }
375 }
376}
377
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual const RequirementRefs & requirementReferences() const =0
virtual QCString qualifiedName() const =0
virtual const QCString & name() 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
QCString fileName
file this entry was extracted from
Definition entry.h:224
QCString type
member type
Definition entry.h:174
int startLine
start line of entry in the source
Definition entry.h:225
QCString name
member name
Definition entry.h:175
QCString doc
documentation block (partly parsed)
Definition entry.h:201
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:227
QCString brief
brief description (doc block)
Definition entry.h:204
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 QCString &ref, const QCString &file, const QCString &anchor, const QCString &name)
Definition outputlist.h:439
void docify(const QCString &s)
Definition outputlist.h:437
A model of a page symbol.
Definition pagedef.h:26
This is an alternative implementation of QCString.
Definition qcstring.h:101
QCString & prepend(const char *s)
Definition qcstring.h:422
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
QCString & setNum(short n)
Definition qcstring.h:459
void reserve(size_t size)
Reserve space for size bytes without changing the string contents.
Definition qcstring.h:185
LinkedMap< Requirement > requirements
static RequirementManager & instance()
std::unique_ptr< Private > p
Definition requirement.h:89
void writeTagFile(TextStream &tagFile)
const RequirementIntf * find(const QCString &reqId) const
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
QCString title() const
Definition requirement.h:43
QCString reqId() const
Definition requirement.h:42
QCString file() const
Definition requirement.h:44
int line() const
Definition requirement.h:45
class that provide information about a section.
Definition section.h:57
singleton class that owns the list of all sections
Definition section.h:134
SectionInfo * add(const SectionInfo &si)
Definition section.h:138
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:178
static constexpr int Anchor
Definition section.h:40
static constexpr int Section
Definition section.h:33
Text streaming class that buffers data.
Definition textstream.h:36
#define Config_getBool(name)
Definition config.h:33
#define Config_getEnum(name)
Definition config.h:35
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:329
Translator * theTranslator
Definition language.cpp:71
MemberDef * toMemberDef(Definition *d)
#define warn(file, line, fmt,...)
Definition message.h:97
int qstricmp_sort(const char *str1, const char *str2)
Definition qcstring.h:86
std::vector< const RequirementIntf * > RequirementIntfList
Definition requirement.h:72
const DefinitionVector & satisfiedBy() const
QCString title() const override
void sortReferences()
QCString getExtPage() const override
QCString getOutputFileBase() const override
void addSatisfiedBy(const Definition *def)
const DefinitionVector & verifiedBy() const
QCString m_file
void addVerifiedBy(const Definition *def)
int line() const override
QCString m_extPage
QCString getTagFile() const override
QCString doc() const override
QCString file() const override
QCString m_doc
DefinitionVector m_verifiedBy
std::vector< const Definition * > DefinitionVector
QCString m_tagFile
QCString m_id
QCString id() const override
Requirement(const QCString &id_, const QCString &file_, int line_, const QCString &title_, const QCString &doc_, const QCString &tagFile_, const QCString &extPage_)
QCString m_title
DefinitionVector m_satisfiedBy
QCString fileName
Definition entry.h:106
QCString tagName
Definition entry.h:105
QCString parseCommentAsText(const Definition *scope, const MemberDef *md, const QCString &doc, const QCString &fileName, int lineNr)
Definition util.cpp:5349
QCString convertToXML(const QCString &s, bool keepEntities)
Definition util.cpp:3893
void writeMarkerList(OutputList &ol, const std::string &markerText, size_t numMarkers, std::function< void(size_t)> replaceFunc)
Definition util.cpp:1106
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:4902
QCString createHtmlUrl(const QCString &relPath, const QCString &ref, bool href, bool isLocalFile, const QCString &targetFileName, const QCString &anchor)
Definition util.cpp:5716
A bunch of utility functions.