Doxygen
Loading...
Searching...
No Matches
plantuml.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 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 */
15
16#include <mutex>
17#include "plantuml.h"
18#include "util.h"
19#include "portable.h"
20#include "config.h"
21#include "doxygen.h"
22#include "message.h"
23#include "debug.h"
24#include "fileinfo.h"
25#include "dir.h"
26#include "indexlist.h"
27#include "stringutil.h"
28
29static std::mutex g_PlantUmlMutex;
30
32 const DString &content,OutputFormat format, const DString &engine,
33 const DString &srcFile,int srcLine,bool inlineCode)
34{
35 StringVector baseNameVector;
36 DString baseName;
37 DString puName;
38 DString imgName;
39 DString outDir(outDirArg);
40
41 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource fileName: {}\n",fileName);
42 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource outDir: {}\n",outDir);
43
44 // strip any trailing slashes and backslashes
45 size_t l = 0;
46 while ((l=outDir.length())>0 && (outDir.at(l-1)=='/' || outDir.at(l-1)=='\\'))
47 {
48 outDir = outDir.left(l-1);
49 }
50
51 generatePlantUmlFileNames(fileName,format,outDir,baseName,puName,imgName);
52
53 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSourcebaseName: {}\n",baseName);
54 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSourcebaseName puName: {}\n",puName);
55 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSourcebaseName imgName: {}\n",imgName);
56
57 DString text;
58 if (inlineCode) text = "@start"+engine+" "+imgName+"\n";
59 text.reserve(text.length()+content.length()+100); // add room for image name and end marker
60 const char *p = content.data();
61 if (p)
62 {
63 char c = 0;
64 bool insideComment = false;
65 DString locEngine;
66 while ((c=*p++))
67 {
68 text+=c;
69 switch (c)
70 {
71 case '\'': insideComment=true; break;
72 case '\n': insideComment=false; break;
73 case '\t': break;
74 case ' ': break;
75 case '@':
76 if (!insideComment && literal_at(p,"start")) // @start...
77 {
78 locEngine.clear();
79 p+=5;
80 text += "start";
81 while ((c=*p++) && isId(c))
82 {
83 locEngine += c;
84 text+=c;
85 }
86 DString inpName;
87 DString rest;
88
89 // skip leading whitespace
90 if (*p && (c==' ' || c=='\t'))
91 {
92 while ((c=*p++) && (c==' ' || c=='\t')) {}
93 }
94 // get everything till end or endOfLine, and split into inpName (without extension) and rest
95 enum State { InName, InExt, InRest };
96 State state = InName;
97 while (*p && c!='\n')
98 {
99 switch (state)
100 {
101 case InName: // looking for the name part
102 if (isId(c) || c=='-') { inpName+=c; }
103 else if (c=='.') { state=InExt; }
104 else { rest+=c; state=InRest; }
105 break;
106 case InExt: // skipping over extension part
107 if (!isId(c) && c!='-') { rest+=c; state=InRest; }
108 break;
109 case InRest: // gather rest until new line
110 rest+=c;
111 break;
112 }
113 c = *p++;
114 }
115 //printf("inpName='%s' rest='%s'\n",qPrint(inpName),qPrint(rest));
116 generatePlantUmlFileNames(inpName,format,outDir,baseName,puName,imgName);
117
118 // insert the image name
119 text+=' ';
120 text+=imgName;
121
122 if (!rest.empty())
123 {
124 text += '\n';
125 text += rest;
126 }
127 if (c) text+=c;
128 }
129 else if (!insideComment && strncmp(p,("end"+locEngine).data(), 3+strlen(engine.data()))==0) // @end...
130 {
131 text += "end"+locEngine+"\n";
132 p+=3+locEngine.length();
133 if (!inlineCode)
134 {
135 DString qcOutDir(substitute(outDir,"\\","/"));
136 size_t pos = qcOutDir.rfind('/');
137 DString generateType(pos!=DString::npos ? qcOutDir.mid(pos+1) : qcOutDir);
138 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource generateType: {}\n",generateType);
139 PlantumlManager::instance().insert(generateType.str(),puName.str(),outDir,format,text,srcFile,srcLine);
140 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource generateType: {}\n",generateType);
141 baseNameVector.push_back(baseName.str());
142 text.clear();
143 }
144 }
145 break;
146 default:
147 break;
148 }
149 }
150 text+='\n';
151 }
152 if (inlineCode)
153 {
154 text +="@end"+engine+"\n";
155 //printf("content\n====\n%s\n=====\n->\n-----\n%s\n------\n",qPrint(content),qPrint(text));
156 DString qcOutDir(substitute(outDir,"\\","/"));
157 size_t pos = qcOutDir.rfind("/");
158 DString generateType(pos!=DString::npos ? qcOutDir.mid(pos+1) : qcOutDir);
159 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource generateType: {}\n",generateType);
160 PlantumlManager::instance().insert(generateType.str(),puName.str(),outDir,format,text,srcFile,srcLine);
161 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource generateType: {}\n",generateType);
162 baseNameVector.push_back(baseName.str());
163 }
164
165 return baseNameVector;
166}
167
169 DString &baseName,DString &puName,DString &imgName)
170{
171 static int umlindex=1;
172
173 if (fileName.empty()) // generate name
174 {
175 std::lock_guard<std::mutex> lock(g_PlantUmlMutex);
176 puName = "inline_umlgraph_"+DString().setNum(umlindex);
177 baseName = outDir+"/inline_umlgraph_"+DString().setNum(umlindex++);
178 }
179 else // user specified name
180 {
181 baseName = fileName;
182 if (size_t i=baseName.rfind('.'); i!=DString::npos) baseName = baseName.left(i);
183 puName = baseName;
184 baseName.prepend(outDir+"/");
185 }
186
187 switch (format)
188 {
189 case PUML_BITMAP:
190 imgName =puName+".png";
191 break;
192 case PUML_EPS:
193 imgName =puName+".eps";
194 break;
195 case PUML_SVG:
196 imgName =puName+".svg";
197 break;
198 }
199}
200
201void PlantumlManager::generatePlantUMLOutput(const DString &baseName,const DString &/* outDir */,OutputFormat format,bool toIndex)
202{
203 if (!toIndex) return;
204 DString imgName = baseName;
205 // The basename contains path, we need to strip the path from the filename in order
206 // to create the image file name which should be included in the index.qhp (Qt help index file).
207 if (size_t i = imgName.rfind('/'); i!=DString::npos) // strip path
208 {
209 imgName=imgName.mid(i+1);
210 }
211 switch (format)
212 {
213 case PUML_BITMAP:
214 imgName+=".png";
215 break;
216 case PUML_EPS:
217 imgName+=".eps";
218 break;
219 case PUML_SVG:
220 imgName+=".svg";
221 break;
222 }
223
225}
226
227//--------------------------------------------------------------------
228
229
231{
232 static PlantumlManager theInstance;
233 return theInstance;
234}
235
239
240static void runPlantumlContent(const PlantumlManager::FilesMap &plantumlFiles,
241 const PlantumlManager::ContentMap &plantumlContent,
243{
244 /* example : running: java -Djava.awt.headless=true
245 -jar "/usr/local/bin/plantuml.jar"
246 -o "test_doxygen/DOXYGEN_OUTPUT/html"
247 -tpng
248 "test_doxygen/DOXYGEN_OUTPUT/html/A.pu"
249 -charset UTF-8
250 outDir:test_doxygen/DOXYGEN_OUTPUT/html
251 test_doxygen/DOXYGEN_OUTPUT/html/A
252 */
253 int exitCode = 0;
254 DString plantumlJarPath = Config_getString(PLANTUML_JAR_PATH);
255 DString plantumlConfigFile = Config_getString(PLANTUML_CFG_FILE);
256
257 DString pumlExe = "java";
258 DString pumlArgs = "";
259 DString pumlType = "";
260 DString pumlOutDir = "";
261
262 const StringVector &pumlIncludePathList = Config_getList(PLANTUML_INCLUDE_PATH);
263 {
264 auto it = pumlIncludePathList.begin();
265 if (it!=pumlIncludePathList.end())
266 {
267 pumlArgs += "-Dplantuml.include.path=\"";
268 pumlArgs += it->c_str();
269 ++it;
270 }
271 while (it!=pumlIncludePathList.end())
272 {
273 pumlArgs += Portable::pathListSeparator();
274 pumlArgs += it->c_str();
275 ++it;
276 }
277 }
278 if (!pumlIncludePathList.empty()) pumlArgs += "\" ";
279 pumlArgs += "-Djava.awt.headless=true -jar \""+plantumlJarPath+"\" ";
280 if (!plantumlConfigFile.empty())
281 {
282 pumlArgs += "-config \"";
283 pumlArgs += plantumlConfigFile;
284 pumlArgs += "\" ";
285 }
286 // the -graphvizdot option expects a relative or absolute path to the dot executable, so
287 // we need to use the unverified DOT_PATH option and check if it points to an existing file.
288 DString dotPath = Config_getString(DOT_PATH);
289 FileInfo dp(dotPath.str());
290 if (Config_getBool(HAVE_DOT) && dp.exists() && dp.isFile())
291 {
292 pumlArgs += "-graphvizdot \"";
293 pumlArgs += dotPath;
294 pumlArgs += "\" ";
295 }
296 switch (format)
297 {
299 pumlType="png";
300 break;
302 pumlType="eps";
303 break;
305 pumlType="svg";
306 break;
307 }
308
309 {
310 for (const auto &[name,nb] : plantumlContent)
311 {
312 if (nb.content.empty()) continue;
313
314 DString pumlArguments = pumlArgs;
315 msg("Generating PlantUML {} Files in {}\n",pumlType,name);
316 pumlArguments+="-o \"";
317 pumlArguments+=nb.outDir;
318 pumlArguments+="\" ";
319 pumlArguments+="-charset UTF-8 -t";
320 pumlArguments+=pumlType;
321 pumlArguments+=" ";
322
323 DString puFileName("");
324 puFileName+=nb.outDir;
325 puFileName+="/";
326 pumlOutDir=puFileName;
327 puFileName+="inline_umlgraph_";
328 puFileName+=pumlType;
329 puFileName+=name.c_str();
330 puFileName+=".pu";
331
332 pumlArguments+="\"";
333 pumlArguments+=puFileName;
334 pumlArguments+="\" ";
335
336
337 DString cachedContent;
338 FileInfo fi(puFileName.str());
339 if (fi.exists())
340 {
341 cachedContent = fileToString(puFileName);
342 }
343
344 std::ofstream file = Portable::openOutputStream(puFileName);
345 if (!file.is_open())
346 {
347 err_full(nb.srcFile,nb.srcLine,"Could not open file {} for writing",puFileName);
348 }
349 file.write( nb.content.data(), nb.content.length() );
350 file.close();
351 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::runPlantumlContent Running Plantuml arguments:{}\n",pumlArguments);
352
353 if (cachedContent == nb.content) continue;
354
355 if ((exitCode=Portable::system(pumlExe.data(),pumlArguments.data(),true))!=0)
356 {
357 err_full(nb.srcFile,nb.srcLine,"Problems running PlantUML. Verify that the command 'java -jar \"{}\" -h' works from the command line. Exit code: {}.",
358 plantumlJarPath,exitCode);
359 }
360
361 if ( (format==PlantumlManager::PUML_EPS) && (Config_getBool(USE_PDFLATEX)) )
362 {
363 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::runPlantumlContent Running epstopdf\n");
364 auto files_kv = plantumlFiles.find(name);
365 if (files_kv!=plantumlFiles.end())
366 {
367 for (const auto &str : files_kv->second)
368 {
369 const int maxCmdLine = 40960;
370 DString epstopdfArgs(maxCmdLine, DString::ExplicitSize);
371 epstopdfArgs.sprintf("\"%s%s.eps\" --outfile=\"%s%s.pdf\"",
372 pumlOutDir.data(),str.c_str(), pumlOutDir.data(),str.c_str());
373 if ((exitCode=Portable::system("epstopdf",epstopdfArgs.data()))!=0)
374 {
375 err_full(nb.srcFile,nb.srcLine,"Problems running epstopdf. Check your TeX installation! Exit code: {}.",exitCode);
376 }
377 else
378 {
379 Dir().remove(pumlOutDir.str()+str+".eps");
380 }
381 }
382 }
383 }
384 }
385 }
386}
387
395
396static void print(const PlantumlManager::FilesMap &plantumlFiles)
397{
399 {
400 for (const auto &[key,list] : plantumlFiles)
401 {
402 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Files PlantumlFiles key:{} size:{}\n",key,list.size());
403 for (const auto &s : list)
404 {
405 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print list:{}\n",s);
406 }
407 }
408 }
409}
410
411static void print(const PlantumlManager::ContentMap &plantumlContent)
412{
414 {
415 for (const auto &[key,content] : plantumlContent)
416 {
417 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Content PlantumlContent key: {}\n",key);
418 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Content:\n{}\n",content.content);
419 }
420 }
421}
422
424 const std::string &key, const std::string &value)
425{
426 auto kv = plantumlFiles.find(key);
427 if (kv==plantumlFiles.end())
428 {
429 kv = plantumlFiles.emplace(key,StringVector()).first;
430 }
431 kv->second.push_back(value);
432}
433
435 const std::string &key, const DString &outDir, const DString &puContent,
436 const DString &srcFile,int srcLine)
437{
438 auto kv = plantumlContent.find(key);
439 if (kv==plantumlContent.end())
440 {
441 kv = plantumlContent.emplace(key,PlantumlContent("",outDir,srcFile,srcLine)).first;
442 }
443 kv->second.content+=puContent;
444}
445
446void PlantumlManager::insert(const std::string &key, const std::string &value,
447 const DString &outDir,OutputFormat format,const DString &puContent,
448 const DString &srcFile,int srcLine)
449{
450 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::insert key:{} ,value:{}\n",key,value);
451
452 switch (format)
453 {
454 case PUML_BITMAP:
457 addPlantumlContent(m_pngPlantumlContent,key,outDir,puContent,srcFile,srcLine);
459 break;
460 case PUML_EPS:
463 addPlantumlContent(m_epsPlantumlContent,key,outDir,puContent,srcFile,srcLine);
465 break;
466 case PUML_SVG:
469 addPlantumlContent(m_svgPlantumlContent,key,outDir,puContent,srcFile,srcLine);
471 break;
472 }
473}
474
475//--------------------------------------------------------------------
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:89
void clear()
Definition dstring.h:219
DString & setNum(short n)
Definition dstring.h:541
DString()=default
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:249
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:323
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:153
const char * c_str() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:165
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:183
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:675
DString & prepend(const char *s)
Definition dstring.h:504
DString & sprintf(const char *format,...)
Definition dstring.cpp:29
void reserve(size_t size)
Reserve space for size bytes without changing the string contents.
Definition dstring.h:222
@ ExplicitSize
Definition dstring.h:136
DString left(size_t len) const
Definition dstring.h:311
const std::string & str() const
Definition dstring.h:634
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:162
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:156
@ Plantuml
Definition debug.h:39
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:133
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:77
Dir()
Definition dir.cpp:189
static IndexList * indexList
Definition doxygen.h:132
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
bool exists() const
Definition fileinfo.cpp:30
bool isFile() const
Definition fileinfo.cpp:63
void addImageFile(const DString &name)
Definition indexlist.h:123
ContentMap m_pngPlantumlContent
Definition plantuml.h:100
void generatePlantUMLOutput(const DString &baseName, const DString &outDir, OutputFormat format, bool toIndex)
Convert a PlantUML file to an image.
Definition plantuml.cpp:201
std::map< std::string, PlantumlContent > ContentMap
Definition plantuml.h:84
void insert(const std::string &key, const std::string &value, const DString &outDir, OutputFormat format, const DString &puContent, const DString &srcFile, int srcLine)
Definition plantuml.cpp:446
OutputFormat
Plant UML output image formats.
Definition plantuml.h:44
ContentMap m_epsPlantumlContent
Definition plantuml.h:102
std::map< std::string, StringVector > FilesMap
Definition plantuml.h:83
StringVector writePlantUMLSource(const DString &outDirArg, const DString &fileName, const DString &content, OutputFormat format, const DString &engine, const DString &srcFile, int srcLine, bool inlineCode)
Write a PlantUML compatible file.
Definition plantuml.cpp:31
void generatePlantUmlFileNames(const DString &fileName, OutputFormat format, const DString &outDir, DString &baseName, DString &puName, DString &imgName)
Definition plantuml.cpp:168
ContentMap m_svgPlantumlContent
Definition plantuml.h:101
FilesMap m_pngPlantumlFiles
Definition plantuml.h:97
static PlantumlManager & instance()
Definition plantuml.cpp:230
FilesMap m_svgPlantumlFiles
Definition plantuml.h:98
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:388
FilesMap m_epsPlantumlFiles
Definition plantuml.h:99
#define Config_getList(name)
Definition config.h:38
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
std::vector< std::string > StringVector
Definition containers.h:33
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:476
#define msg(fmt,...)
Definition message.h:94
#define err_full(file, line, fmt,...)
Definition message.h:132
int system(const DString &command, const DString &args, bool commandHasConsole=true)
Definition portable.cpp:105
DString pathListSeparator()
Definition portable.cpp:383
std::ofstream openOutputStream(const DString &name, bool append=false)
Definition portable.cpp:648
static void runPlantumlContent(const PlantumlManager::FilesMap &plantumlFiles, const PlantumlManager::ContentMap &plantumlContent, PlantumlManager::OutputFormat format)
Definition plantuml.cpp:240
static std::mutex g_PlantUmlMutex
Definition plantuml.cpp:29
static void addPlantumlContent(PlantumlManager::ContentMap &plantumlContent, const std::string &key, const DString &outDir, const DString &puContent, const DString &srcFile, int srcLine)
Definition plantuml.cpp:434
static void print(const PlantumlManager::FilesMap &plantumlFiles)
Definition plantuml.cpp:396
static void addPlantumlFiles(PlantumlManager::FilesMap &plantumlFiles, const std::string &key, const std::string &value)
Definition plantuml.cpp:423
Portable versions of functions that are platform dependent.
Some helper functions for std::string.
bool literal_at(const char *data, const char(&str)[N])
returns true iff data points to a substring that matches string literal str
Definition stringutil.h:98
PlantumlContent(const DString &content_, const DString &outDir_, const DString &srcFile_, int srcLine_)
Definition plantuml.h:31
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1491
A bunch of utility functions.
bool isId(int c)
Definition util.h:257