From 7506404e646f1fcc5a26ca6fca91a7f65154f05a Mon Sep 17 00:00:00 2001 From: Dimitri van Heesch Date: Sun, 10 Aug 2014 20:09:33 +0200 Subject: [PATCH] Bug 731947 - Support for PlantUML --- Doxyfile | 48 +++++++++++--------- doc/commands.doc | 61 ++++++++++++++++++++++++++ src/cmdmapper.cpp | 2 + src/cmdmapper.h | 4 +- src/commentcnv.l | 6 +-- src/commentscan.l | 16 +++++-- src/compound.xsd | 2 +- src/config.l | 31 +++++++++++++ src/config.xml | 10 +++++ src/docbookvisitor.cpp | 94 +++++++++++++++++++++++++++------------ src/docbookvisitor.h | 1 + src/docparser.cpp | 18 ++++++++ src/docparser.h | 2 +- src/doctokenizer.h | 1 + src/doctokenizer.l | 32 +++++++++++++- src/htmldocvisitor.cpp | 48 +++++++++++++++++++- src/htmldocvisitor.h | 1 + src/htmlentity.cpp | 2 +- src/latexdocvisitor.cpp | 29 ++++++++++++ src/latexdocvisitor.h | 1 + src/libdoxygen.pro.in | 2 + src/mandocvisitor.cpp | 1 + src/markdown.cpp | 4 ++ src/perlmodgen.cpp | 3 +- src/plantuml.cpp | 97 +++++++++++++++++++++++++++++++++++++++++ src/plantuml.h | 40 +++++++++++++++++ src/printdocvisitor.h | 2 + src/rtfdocvisitor.cpp | 34 ++++++++++++++- src/rtfdocvisitor.h | 1 + src/xmldocvisitor.cpp | 5 +++ winbuild/Doxygen.vcproj | 8 ++++ 31 files changed, 544 insertions(+), 62 deletions(-) create mode 100644 src/plantuml.cpp create mode 100644 src/plantuml.h diff --git a/Doxyfile b/Doxyfile index bc302710e94..325a875c861 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.3.1 +# Doxyfile 1.8.7 #--------------------------------------------------------------------------- # Project related configuration options @@ -10,6 +10,7 @@ PROJECT_BRIEF = PROJECT_LOGO = OUTPUT_DIRECTORY = doxygen_docs CREATE_SUBDIRS = YES +ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES @@ -63,6 +64,7 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = NO HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES @@ -84,7 +86,7 @@ FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- -# configuration options related to warning and progress messages +# Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES @@ -94,9 +96,10 @@ WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text " WARN_LOGFILE = #--------------------------------------------------------------------------- -# configuration options related to the input files +# Configuration options related to the input files #--------------------------------------------------------------------------- -INPUT = src +INPUT = src \ + vhdlparser INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.h \ *.cpp \ @@ -105,6 +108,7 @@ RECURSIVE = NO EXCLUDE = src/logos.cpp \ src/lodepng.cpp EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = @@ -116,7 +120,7 @@ FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- -# configuration options related to source browsing +# Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO @@ -124,18 +128,17 @@ STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES -CLANG_ASSISTED_PARSING = YES -CLANG_OPTIONS = #--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index +# Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- -# configuration options related to the HTML output +# Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = @@ -184,6 +187,7 @@ USE_MATHJAX = NO MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = SEARCHENGINE = YES SERVER_BASED_SEARCH = YES EXTERNAL_SEARCH = NO @@ -192,7 +196,7 @@ SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- -# configuration options related to the LaTeX output +# Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = @@ -211,7 +215,7 @@ LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- -# configuration options related to the RTF output +# Configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = @@ -220,31 +224,31 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- -# configuration options related to the man page output +# Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = MAN_EXTENSION = .3 +MAN_SUBDIR = MAN_LINKS = NO #--------------------------------------------------------------------------- -# configuration options related to the XML output +# Configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml -XML_SCHEMA = -XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- -# configuration options related to the DOCBOOK output +# Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook +DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output +# Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- -# configuration options related to the Perl module output +# Configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO @@ -257,13 +261,14 @@ ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES -INCLUDE_PATH = qtools libmd5 +INCLUDE_PATH = qtools \ + libmd5 INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- -# Configuration::additions related to external references +# Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = qtools_docs/qtools.tag=../../qtools_docs/html GENERATE_TAGFILE = doxygen.tag @@ -276,6 +281,7 @@ PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- CLASS_DIAGRAMS = NO MSCGEN_PATH = +DIA_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES DOT_NUM_THREADS = 0 @@ -299,6 +305,8 @@ INTERACTIVE_SVG = YES DOT_PATH = DOTFILE_DIRS = MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO diff --git a/doc/commands.doc b/doc/commands.doc index 91758565e57..73eb842c651 100644 --- a/doc/commands.doc +++ b/doc/commands.doc @@ -90,6 +90,7 @@ documentation: \refitem cmdendrtfonly \\endrtfonly \refitem cmdendsecreflist \\endsecreflist \refitem cmdendverbatim \\endverbatim +\refitem cmdenduml \\enduml \refitem cmdendxmlonly \\endxmlonly \refitem cmdenum \\enum \refitem cmdexample \\example @@ -174,6 +175,7 @@ documentation: \refitem cmdskip \\skip \refitem cmdskipline \\skipline \refitem cmdsnippet \\snippet +\refitem cmdstartuml \\startuml \refitem cmdstruct \\struct \refitem cmdsubpage \\subpage \refitem cmdsubsection \\subsection @@ -2530,6 +2532,59 @@ class Receiver \sa section \ref cmdmscfile "\\mscfile". +
+\section cmdstartuml \\startuml + + \addindex \\startuml + Starts a text fragment which should contain a valid description of a + PlantUML diagram. See http://plantuml.sourceforge.net/ for examples. + The text fragment ends with \ref cmdenduml "\\enduml". + \note You need to install Java and the PlantUML's jar file, + if you want to use this command. The location of the jar file should be specified + using \ref cfg_plantuml_jar_path "PLANTUML_JAR_PATH". + +Here is an example of the use of the \c \\startuml command. +\code +/** Sender class. Can be used to send a command to the server. + * The receiver will acknowledge the command by calling Ack(). + * \startuml + * Sender->Receiver : Command() + * Sender<--Receiver : Ack() + * \enduml + */ +class Sender +{ + public: + /** Acknowledgment from server */ + void Ack(bool ok); +}; + +/** Receiver class. Can be used to receive and execute commands. + * After execution of a command, the receiver will send an acknowledgment + * \startuml + * Receiver<-Sender : Command() + * Receiver-->Sender : Ack() + * \enduml + */ +class Receiver +{ + public: + /** Executable a command on the server */ + void Command(int commandId); +}; +\endcode + +\note For compatibility with running PlantUML as a preprocessing step before +running doxygen, you can also add the name of the image file after \c \\startuml +and inside curly brackets, i.e. +\verbatim +@startuml{myimage.png} +Alice -> Bob : Hello +@enduml +\endverbatim +When the name of the image is specified, doxygen will generate an image with that name. +Without the name doxygen will choose a name automatically. +
\section cmddotfile \\dotfile ["caption"] @@ -2650,6 +2705,12 @@ class Receiver \addindex \\endmsc Ends a block that was started with \ref cmdmsc "\\msc". +
+\section cmdenduml \\enduml + + \addindex \\enduml + Ends a block that was started with \ref cmdstartuml "\\startuml". +
\section cmdendhtmlonly \\endhtmlonly diff --git a/src/cmdmapper.cpp b/src/cmdmapper.cpp index 45469f278f0..b0ca85f22d8 100644 --- a/src/cmdmapper.cpp +++ b/src/cmdmapper.cpp @@ -120,8 +120,10 @@ CommandMap cmdMap[] = { "_internalref", CMD_INTERNALREF }, { "dot", CMD_DOT }, { "msc", CMD_MSC }, + { "startuml", CMD_STARTUML }, { "enddot", CMD_ENDDOT }, { "endmsc", CMD_ENDMSC }, + { "enduml", CMD_ENDUML }, { "manonly", CMD_MANONLY }, { "endmanonly", CMD_ENDMANONLY }, { "includelineno", CMD_INCWITHLINES }, diff --git a/src/cmdmapper.h b/src/cmdmapper.h index d89e368f7b3..75fd8ec7138 100644 --- a/src/cmdmapper.h +++ b/src/cmdmapper.h @@ -127,7 +127,9 @@ enum CommandType CMD_DIAFILE = 97, CMD_LATEXINCLUDE = 98, CMD_NDASH = 99, - CMD_MDASH = 100 + CMD_MDASH = 100, + CMD_STARTUML = 101, + CMD_ENDUML = 102 }; enum HtmlTagType diff --git a/src/commentcnv.l b/src/commentcnv.l index 4cc9da9d9ee..ec56b902495 100644 --- a/src/commentcnv.l +++ b/src/commentcnv.l @@ -446,7 +446,7 @@ void replaceComment(int offset); g_blockName=&yytext[1]; BEGIN(VerbatimCode); } -[\\@]("dot"|"code"|"msc")/[^a-z_A-Z0-9] { /* start of a verbatim block */ +[\\@]("dot"|"code"|"msc"|"startuml")/[^a-z_A-Z0-9] { /* start of a verbatim block */ copyToOutput(yytext,(int)yyleng); g_lastCommentContext = YY_START; g_javaBlock=0; @@ -517,7 +517,7 @@ void replaceComment(int offset); } } } -[\\@]("enddot"|"endcode"|"endmsc") { /* end of verbatim block */ +[\\@]("enddot"|"endcode"|"endmsc"|"enduml") { /* end of verbatim block */ copyToOutput(yytext,(int)yyleng); if (&yytext[4]==g_blockName) { @@ -554,7 +554,7 @@ void replaceComment(int offset); copyToOutput(yytext,(int)yyleng); } ^[ \t]*"///" { - if (g_blockName=="dot" || g_blockName=="msc" || g_blockName.at(0)=='f') + if (g_blockName=="dot" || g_blockName=="msc" || g_blockName=="startuml" || g_blockName.at(0)=='f') { // see bug 487871, strip /// from dot images and formulas. int l=0; diff --git a/src/commentscan.l b/src/commentscan.l index a42f6ce8bc1..369349bb026 100644 --- a/src/commentscan.l +++ b/src/commentscan.l @@ -191,6 +191,7 @@ static DocCmdMap docCmdMap[] = { "manonly", &handleFormatBlock, FALSE }, { "dot", &handleFormatBlock, TRUE }, { "msc", &handleFormatBlock, TRUE }, + { "startuml", &handleFormatBlock, TRUE }, { "code", &handleFormatBlock, TRUE }, { "addindex", &handleAddIndex, FALSE }, { "if", &handleIf, FALSE }, @@ -877,7 +878,7 @@ static int yyread(char *buf,int max_size) /* start command character */ CMD ("\\"|"@") DCMD1 ("arg"|"attention"|"author"|"cite"|"code") -DCMD2 ("date"|"dot"|"msc"|"dotfile"|"example") +DCMD2 ("date"|"dot"|"msc"|"dotfile"|"example"|"startuml") DCMD3 ("htmlinclude"|"htmlonly"|"image"|"include") DCMD4 ("includelineno"|"internal"|"invariant") DCMD5 ("latexinclude"|"latexonly"|"li"|"line"|"manonly"|"name") @@ -1785,6 +1786,13 @@ RCSTAG "$"{ID}":"[^\n$]+"$" BEGIN(Comment); } } +{CMD}"enduml" { + addOutput(yytext); + if (blockName=="startuml") // found end of the block + { + BEGIN(Comment); + } + } [^ \@\*\/\\\n]* { // some word addOutput(yytext); } @@ -1809,9 +1817,11 @@ RCSTAG "$"{ID}":"[^\n$]+"$" addOutput(*yytext); } <> { + QCString endTag = "@end"+blockName; + if (blockName=="startuml") endTag="enduml"; warn(yyFileName,yyLineNr, - "reached end of comment while inside a @%s block; check for missing @end%s tag!", - blockName.data(),blockName.data() + "reached end of comment while inside a @%s block; check for missing @%s tag!", + blockName.data(),endTag.data() ); yyterminate(); } diff --git a/src/compound.xsd b/src/compound.xsd index dec596b2d3d..be897c368c3 100644 --- a/src/compound.xsd +++ b/src/compound.xsd @@ -386,7 +386,7 @@ - + diff --git a/src/config.l b/src/config.l index 133bc111fc1..a237fafea64 100644 --- a/src/config.l +++ b/src/config.l @@ -1329,6 +1329,37 @@ void Config::check() mscgenPath=""; } + // check plantuml path + QCString &plantumlJarPath = Config_getString("PLANTUML_JAR_PATH"); + if (!plantumlJarPath.isEmpty()) + { + QFileInfo pu(plantumlJarPath); + if (pu.exists() && pu.isDir()) // PLANTUML_JAR_PATH is directory + { + QFileInfo jar(plantumlJarPath+portable_pathSeparator()+"plantuml.jar"); + if (jar.exists() && jar.isFile()) + { + plantumlJarPath = jar.dirPath(TRUE).utf8()+portable_pathSeparator(); + } + else + { + config_err("Jar file plantuml.jar not found at location " + "specified via PLANTUML_JAR_PATH: '%s'\n",plantumlJarPath.data()); + plantumlJarPath=""; + } + } + else if (pu.exists() && pu.isFile() && plantumlJarPath.right(4)==".jar") // PLANTUML_JAR_PATH is file + { + plantumlJarPath = pu.dirPath(TRUE).utf8()+portable_pathSeparator(); + } + else + { + config_err("path specified via PLANTUML_JAR_PATH does not exist or not a directory: %s\n", + plantumlJarPath.data()); + plantumlJarPath=""; + } + } + // check dia path QCString &diaPath = Config_getString("DIA_PATH"); if (!diaPath.isEmpty()) diff --git a/src/config.xml b/src/config.xml index 5a0af591978..9020d3047cf 100644 --- a/src/config.xml +++ b/src/config.xml @@ -3292,6 +3292,16 @@ to be found in the default search path. The \c DIAFILE_DIRS tag can be used to specify one or more directories that contain dia files that are included in the documentation (see the \ref cmdmscfile "\\diafile" command). +]]> + + + diff --git a/src/docbookvisitor.cpp b/src/docbookvisitor.cpp index 5d7aafd439a..9f2dbe3a5c7 100644 --- a/src/docbookvisitor.cpp +++ b/src/docbookvisitor.cpp @@ -34,6 +34,7 @@ #include "msc.h" #include "dia.h" #include "htmlentity.h" +#include "plantuml.h" DocbookDocVisitor::DocbookDocVisitor(FTextStream &t,CodeOutputInterface &ci) : DocVisitor(DocVisitor_Docbook), m_t(t), m_ci(ci), m_insidePre(FALSE), m_hide(FALSE) @@ -216,35 +217,58 @@ void DocbookDocVisitor::visit(DocVerbatim *s) } break; case DocVerbatim::Msc: - static int mscindex = 1; - QCString baseName(4096); - QCString name; - QCString stext = s->text(); - m_t << "" << endl; - name.sprintf("%s%d", "msc_inline_mscgraph_", mscindex); - baseName.sprintf("%s%d", - (Config_getString("DOCBOOK_OUTPUT")+"/inline_mscgraph_").data(), - mscindex++ - ); - QFile file(baseName+".msc"); - if (!file.open(IO_WriteOnly)) { - err("Could not open file %s.msc for writing\n",baseName.data()); + static int mscindex = 1; + QCString baseName(4096); + QCString name; + QCString stext = s->text(); + m_t << "" << endl; + name.sprintf("%s%d", "msc_inline_mscgraph_", mscindex); + baseName.sprintf("%s%d", + (Config_getString("DOCBOOK_OUTPUT")+"/inline_mscgraph_").data(), + mscindex++ + ); + QFile file(baseName+".msc"); + if (!file.open(IO_WriteOnly)) + { + err("Could not open file %s.msc for writing\n",baseName.data()); + } + QCString text = "msc {"; + text+=stext; + text+="}"; + file.writeBlock( text, text.length() ); + file.close(); + m_t << "
" << endl; + m_t << " " << name << "" << endl; + m_t << " " << endl; + m_t << " " << endl; + writeMscFile(baseName); + m_t << " " << endl; + m_t << " " << endl; + m_t << "
" << endl; + m_t << "
" << endl; + } + break; + case DocVerbatim::PlantUML: + { + static QCString docbookOutput = Config_getString("DOCBOOK_OUTPUT"); + QCString baseName = writePlantUMLSource(docbookOutput,s->exampleFile(),s->text()); + QCString shortName = baseName; + int i; + if ((i=shortName.findRev('/'))!=-1) + { + shortName=shortName.right(shortName.length()-i-1); + } + m_t << "
" << endl; + m_t << " " << shortName << "" << endl; + m_t << " " << endl; + m_t << " " << endl; + writePlantUMLFile(baseName); + m_t << " " << endl; + m_t << " " << endl; + m_t << "
" << endl; + m_t << "
" << endl; } - QCString text = "msc {"; - text+=stext; - text+="}"; - file.writeBlock( text, text.length() ); - file.close(); - m_t << "
" << endl; - m_t << " " << name << "" << endl; - m_t << " " << endl; - m_t << " " << endl; - writeMscFile(baseName); - m_t << " " << endl; - m_t << " " << endl; - m_t << "
" << endl; - m_t << "" << endl; break; } } @@ -1195,6 +1219,22 @@ void DocbookDocVisitor::writeMscFile(const QCString &baseName) m_t << "" << endl; } +void DocbookDocVisitor::writePlantUMLFile(const QCString &baseName) +{ + QCString shortName = baseName; + int i; + if ((i=shortName.findRev('/'))!=-1) + { + shortName=shortName.right(shortName.length()-i-1); + } + QCString outDir = Config_getString("DOCBOOK_OUTPUT"); + generatePlantUMLOutput(baseName,outDir,PUML_BITMAP); + m_t << " "; + m_t << "" << endl; +} + void DocbookDocVisitor::startMscFile(const QCString &fileName, const QCString &width, const QCString &height, diff --git a/src/docbookvisitor.h b/src/docbookvisitor.h index 20d424e6170..dd67abaa734 100644 --- a/src/docbookvisitor.h +++ b/src/docbookvisitor.h @@ -156,6 +156,7 @@ class DocbookDocVisitor : public DocVisitor const QCString &height, bool hasCaption); void endDotFile(bool hasCaption); void writeDotFile(const QCString &fileName); + void writePlantUMLFile(const QCString &fileName); //-------------------------------------- // state variables //-------------------------------------- diff --git a/src/docparser.cpp b/src/docparser.cpp index 6d788fdf964..bab0c75fe00 100644 --- a/src/docparser.cpp +++ b/src/docparser.cpp @@ -5569,6 +5569,23 @@ int DocPara::handleCommand(const QCString &cmdName) doctokenizerYYsetStatePara(); } break; + case CMD_STARTUML: + { + static QCString jarPath = Config_getString("PLANTUML_JAR_PATH"); + doctokenizerYYsetStatePlantUML(); + retval = doctokenizerYYlex(); + if (jarPath.isEmpty()) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"ignoring startuml command because PLANTUML_JAR_PATH is not set"); + } + else + { + m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::PlantUML,FALSE,g_token->sectionId)); + } + if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"startuml section ended without end marker"); + doctokenizerYYsetStatePara(); + } + break; case CMD_ENDPARBLOCK: retval=RetVal_EndParBlock; break; @@ -5583,6 +5600,7 @@ int DocPara::handleCommand(const QCString &cmdName) case CMD_ENDVERBATIM: case CMD_ENDDOT: case CMD_ENDMSC: + case CMD_ENDUML: warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected command %s",qPrint(g_token->name)); break; case CMD_PARAM: diff --git a/src/docparser.h b/src/docparser.h index 17f296bbe86..3dc3d843598 100644 --- a/src/docparser.h +++ b/src/docparser.h @@ -433,7 +433,7 @@ class DocWhiteSpace : public DocNode class DocVerbatim : public DocNode { public: - enum Type { Code, HtmlOnly, ManOnly, LatexOnly, RtfOnly, XmlOnly, Verbatim, Dot, Msc, DocbookOnly }; + enum Type { Code, HtmlOnly, ManOnly, LatexOnly, RtfOnly, XmlOnly, Verbatim, Dot, Msc, DocbookOnly, PlantUML }; DocVerbatim(DocNode *parent,const QCString &context, const QCString &text, Type t,bool isExample, const QCString &exampleFile,bool isBlock=FALSE,const QCString &lang=QCString()); diff --git a/src/doctokenizer.h b/src/doctokenizer.h index 1db764efaa8..c95230b7974 100644 --- a/src/doctokenizer.h +++ b/src/doctokenizer.h @@ -160,5 +160,6 @@ void doctokenizerYYpushBackHtmlTag(const char *tag); void doctokenizerYYsetStateSnippet(); void doctokenizerYYstartAutoList(); void doctokenizerYYendAutoList(); +void doctokenizerYYsetStatePlantUML(); #endif diff --git a/src/doctokenizer.l b/src/doctokenizer.l index 3bd20585a73..ea4a1a439cc 100644 --- a/src/doctokenizer.l +++ b/src/doctokenizer.l @@ -417,6 +417,8 @@ REFWORD {LABELID}|{REFWORD2}|{REFWORD3}|{LNKWORD2} %x St_Verbatim %x St_Dot %x St_Msc +%x St_PlantUMLOpt +%x St_PlantUML %x St_Param %x St_XRefItem %x St_XRefItem2 @@ -843,7 +845,7 @@ REFWORD {LABELID}|{REFWORD2}|{REFWORD3}|{LNKWORD2} . { /* dot text */ g_token->verb+=yytext; } -{CMD}("endmsc"|"endvhdlflow") { +{CMD}("endmsc") { return RetVal_OK; } [^\\@\n]+ | @@ -851,6 +853,23 @@ REFWORD {LABELID}|{REFWORD2}|{REFWORD3}|{LNKWORD2} . { /* msc text */ g_token->verb+=yytext; } +\n { + g_token->sectionId=g_token->sectionId.stripWhiteSpace(); + BEGIN(St_PlantUML); + } +["{}] { // skip curly brackets or quotes around the optional image name + } +. { + g_token->sectionId += yytext; + } +{CMD}"enduml" { + return RetVal_OK; + } +[^\\@\n]+ | +\n | +. { /* plantuml text */ + g_token->verb+=yytext; + } "\"" { // quoted title BEGIN(St_TitleQ); } @@ -1121,6 +1140,10 @@ REFWORD {LABELID}|{REFWORD2}|{REFWORD3}|{LNKWORD2} g_endMarker="endmsc"; BEGIN(St_SecSkip); } +{CMD}"startuml"/[^a-z_A-Z0-9] { + g_endMarker="enduml"; + BEGIN(St_SecSkip); + } {CMD}"htmlonly"/[^a-z_A-Z0-9] { g_endMarker="endhtmlonly"; BEGIN(St_SecSkip); @@ -1318,6 +1341,13 @@ void doctokenizerYYsetStateMsc() BEGIN(St_Msc); } +void doctokenizerYYsetStatePlantUML() +{ + g_token->verb=""; + g_token->sectionId=""; + BEGIN(St_PlantUMLOpt); +} + void doctokenizerYYsetStateParam() { BEGIN(St_Param); diff --git a/src/htmldocvisitor.cpp b/src/htmldocvisitor.cpp index bd94232a9c3..e319a1fef42 100644 --- a/src/htmldocvisitor.cpp +++ b/src/htmldocvisitor.cpp @@ -34,6 +34,7 @@ #include "filedef.h" #include "memberdef.h" #include "htmlentity.h" +#include "plantuml.h" static const int NUM_HTML_LIST_TYPES = 4; static const char types[][NUM_HTML_LIST_TYPES] = {"1", "a", "i", "A"}; @@ -429,9 +430,21 @@ void HtmlDocVisitor::visit(DocVerbatim *s) m_t << "
" << endl; writeMscFile(baseName+".msc",s->relPath(),s->context()); if (Config_getBool("DOT_CLEANUP")) file.remove(); + m_t << "
" << endl; + forceStartParagraph(s); + } + break; + case DocVerbatim::PlantUML: + { + forceEndParagraph(s); + + static QCString htmlOutput = Config_getString("HTML_OUTPUT"); + QCString baseName = writePlantUMLSource(htmlOutput,s->exampleFile(),s->text()); + m_t << "
" << endl; + writePlantUMLFile(baseName,s->relPath(),s->context()); + m_t << "
" << endl; + forceStartParagraph(s); } - m_t << "" << endl; - forceStartParagraph(s); break; } } @@ -1976,6 +1989,37 @@ void HtmlDocVisitor::writeDiaFile(const QCString &fileName, m_t << "" << endl; } +void HtmlDocVisitor::writePlantUMLFile(const QCString &fileName, + const QCString &relPath, + const QCString &) +{ + QCString baseName=fileName; + int i; + if ((i=baseName.findRev('/'))!=-1) // strip path + { + baseName=baseName.right(baseName.length()-i-1); + } + if ((i=baseName.findRev('.'))!=-1) // strip extension + { + baseName=baseName.left(i); + } + static QCString outDir = Config_getString("HTML_OUTPUT"); + static QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT"); + if (imgExt=="svg") + { + generatePlantUMLOutput(fileName,outDir,PUML_SVG); + //m_t << "" << endl; + m_t << "" << endl; + } + else + { + generatePlantUMLOutput(fileName,outDir,PUML_BITMAP); + m_t << "" << endl; + } +} + /** Used for items found inside a paragraph, which due to XHTML restrictions * have to be outside of the paragraph. This method will forcefully end * the current paragraph and forceStartParagraph() will restart it. diff --git a/src/htmldocvisitor.h b/src/htmldocvisitor.h index ddefecd14b6..00ae09e0f75 100644 --- a/src/htmldocvisitor.h +++ b/src/htmldocvisitor.h @@ -153,6 +153,7 @@ class HtmlDocVisitor : public DocVisitor void writeDotFile(const QCString &fileName,const QCString &relPath,const QCString &context); void writeMscFile(const QCString &fileName,const QCString &relPath,const QCString &context); void writeDiaFile(const QCString &fileName,const QCString &relPath,const QCString &context); + void writePlantUMLFile(const QCString &fileName,const QCString &relPath,const QCString &context); void pushEnabled(); void popEnabled(); diff --git a/src/htmlentity.cpp b/src/htmlentity.cpp index c49491e0054..216b65c477c 100644 --- a/src/htmlentity.cpp +++ b/src/htmlentity.cpp @@ -462,7 +462,7 @@ const DocSymbol::PerlSymb *HtmlEntityMapper::perl(DocSymbol::SymType symb) const /*! * @brief Give code of the requested HTML entity name - * @param symName HTML entity name without \c & and \c; + * @param symName HTML entity name without \c & and \c ; * @return the code for the requested HTML entity name, * in case the requested HTML item does not exist `DocSymbol::Sym_unknown` is returned. */ diff --git a/src/latexdocvisitor.cpp b/src/latexdocvisitor.cpp index dd23d6024ab..af1a5fd7dd8 100644 --- a/src/latexdocvisitor.cpp +++ b/src/latexdocvisitor.cpp @@ -32,6 +32,7 @@ #include "filedef.h" #include "config.h" #include "htmlentity.h" +#include "plantuml.h" static QCString escapeLabelName(const char *s) { @@ -321,6 +322,16 @@ void LatexDocVisitor::visit(DocVerbatim *s) if (Config_getBool("DOT_CLEANUP")) file.remove(); } break; + case DocVerbatim::PlantUML: + { + QCString latexOutput = Config_getString("LATEX_OUTPUT"); + QCString baseName = writePlantUMLSource(latexOutput,s->exampleFile(),s->text()); + + m_t << "\\begin{center}\n"; + writePlantUMLFile(baseName); + m_t << "\\end{center}\n"; + } + break; } } @@ -1743,6 +1754,7 @@ void LatexDocVisitor::writeMscFile(const QCString &baseName) m_t << "\\end{DoxyImageNoCaption}\n"; } + void LatexDocVisitor::startDiaFile(const QCString &fileName, const QCString &width, const QCString &height, @@ -1834,3 +1846,20 @@ void LatexDocVisitor::writeDiaFile(const QCString &baseName) m_t << "\\end{DoxyImageNoCaption}\n"; } +void LatexDocVisitor::writePlantUMLFile(const QCString &baseName) +{ + QCString shortName = baseName; + int i; + if ((i=shortName.findRev('/'))!=-1) + { + shortName=shortName.right(shortName.length()-i-1); + } + QCString outDir = Config_getString("LATEX_OUTPUT"); + generatePlantUMLOutput(baseName,outDir,PUML_EPS); + m_t << "\n\\begin{DoxyImageNoCaption}" + " \\mbox{\\includegraphics"; + m_t << "{" << shortName << "}"; + m_t << "}\n"; // end mbox + m_t << "\\end{DoxyImageNoCaption}\n"; +} + diff --git a/src/latexdocvisitor.h b/src/latexdocvisitor.h index d386569410c..64560b9e939 100644 --- a/src/latexdocvisitor.h +++ b/src/latexdocvisitor.h @@ -176,6 +176,7 @@ class LatexDocVisitor : public DocVisitor const QCString &height, bool hasCaption); void endDiaFile(bool hasCaption); void writeDiaFile(const QCString &fileName); + void writePlantUMLFile(const QCString &fileName); void pushEnabled(); void popEnabled(); diff --git a/src/libdoxygen.pro.in b/src/libdoxygen.pro.in index f50f987d5d8..703b885e3a0 100644 --- a/src/libdoxygen.pro.in +++ b/src/libdoxygen.pro.in @@ -87,6 +87,7 @@ HEADERS = arguments.h \ pagedef.h \ perlmodgen.h \ lodepng.h \ + plantuml.h \ pre.h \ printdocvisitor.h \ pycode.h \ @@ -165,6 +166,7 @@ SOURCES = arguments.cpp \ layout.cpp \ lodepng.cpp \ logos.cpp \ + plantuml.cpp \ mandocvisitor.cpp \ mangen.cpp \ sqlite3gen.cpp \ diff --git a/src/mandocvisitor.cpp b/src/mandocvisitor.cpp index 540332423e7..1fe5409443f 100644 --- a/src/mandocvisitor.cpp +++ b/src/mandocvisitor.cpp @@ -208,6 +208,7 @@ void ManDocVisitor::visit(DocVerbatim *s) case DocVerbatim::DocbookOnly: case DocVerbatim::Dot: case DocVerbatim::Msc: + case DocVerbatim::PlantUML: /* nothing */ break; } diff --git a/src/markdown.cpp b/src/markdown.cpp index 291e1dcc2fa..11b01ea2d13 100644 --- a/src/markdown.cpp +++ b/src/markdown.cpp @@ -208,6 +208,10 @@ static QCString isBlockCommand(const char *data,int offset,int size) { return "end"+blockName; } + else if (blockName=="startuml") + { + return "enduml"; + } else if (blockName=="f" && endtext()); diff --git a/src/plantuml.cpp b/src/plantuml.cpp new file mode 100644 index 00000000000..18f028b8422 --- /dev/null +++ b/src/plantuml.cpp @@ -0,0 +1,97 @@ +/****************************************************************************** + * + * Copyright (C) 1997-2014 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +#include "plantuml.h" +#include "portable.h" +#include "config.h" +#include "message.h" + +#include + +//static const int maxCmdLine = 40960; + +QCString writePlantUMLSource(const QCString &outDir,const QCString &fileName,const QCString &content) +{ + QCString baseName(4096); + static int umlindex=1; + + if (fileName.isEmpty()) // generate name + { + baseName = outDir+"/inline_umlgraph_"+QCString().setNum(umlindex++); + } + else // user specified name + { + baseName = fileName; + int i=baseName.findRev('.'); + if (i!=-1) baseName = baseName.left(i); + baseName.prepend(outDir+"/"); + } + QFile file(baseName+".pu"); + if (!file.open(IO_WriteOnly)) + { + err("Could not open file %s for writing\n",baseName.data()); + } + QCString text = "@startuml\n"; + text+=content; + text+="@enduml\n"; + file.writeBlock( text, text.length() ); + file.close(); + return baseName; +} + +void generatePlantUMLOutput(const char *baseName,const char *outDir,PlantUMLOutputFormat format) +{ + static QCString plantumlJarPath = Config_getString("PLANTUML_JAR_PATH"); + + QCString pumlExe = "java"; + QCString pumlArgs = "-Djava.awt.headless=true -jar \""+plantumlJarPath+"plantuml.jar\" "; + pumlArgs+="-o \""; + pumlArgs+=outDir; + pumlArgs+="\" "; + QCString extension; + switch (format) + { + case PUML_BITMAP: + pumlArgs+="-tpng"; + extension=".png"; + break; + case PUML_EPS: + pumlArgs+="-teps"; + extension=".eps"; + break; + case PUML_SVG: + pumlArgs+="-tsvg"; + extension=".svg"; + break; + } + pumlArgs+=" \""; + pumlArgs+=baseName; + pumlArgs+=".pu\" "; + int exitCode; + //printf("*** running: %s %s outDir:%s %s\n",pumlExe.data(),pumlArgs.data(),outDir,outFile); + msg("Running PlantUML on generated file %s.pu\n",baseName); + portable_sysTimerStart(); + if ((exitCode=portable_system(pumlExe,pumlArgs,FALSE))!=0) + { + err("Problems running PlantUML. Verify that the command 'java -jar \"%splantuml.jar\" -h' works from the command line\n", + plantumlJarPath.data()); + } + else if (Config_getBool("DOT_CLEANUP")) + { + QFile(QCString(baseName)+".pu").remove(); + } + portable_sysTimerStop(); +} + diff --git a/src/plantuml.h b/src/plantuml.h new file mode 100644 index 00000000000..27626d1b6ff --- /dev/null +++ b/src/plantuml.h @@ -0,0 +1,40 @@ +/****************************************************************************** + * + * Copyright (C) 1997-2014 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +#ifndef PLANTUML_H +#define PLANTUML_H + +class QCString; + +/** Plant UML output image formats */ +enum PlantUMLOutputFormat { PUML_BITMAP, PUML_EPS, PUML_SVG }; + +/** Write a PlantUML compatible file. + * @param[in] outDir the output directory to write the file to. + * @param[in] fileName the name of the file. If empty a name will be chosen automatically. + * @param[in] content the contents of the PlantUML file. + * @returns The name of the generated file. + */ +QCString writePlantUMLSource(const QCString &outDir,const QCString &fileName,const QCString &content); + +/** Convert a PlantUML file to an image. + * @param[in] baseName the name of the generated file (as returned by writePlantUMLSource()) + * @param[in] outDir the directory to write the resulting image into. + * @param[in] format the image format to generate. + */ +void generatePlantUMLOutput(const char *baseName,const char *outDir,PlantUMLOutputFormat format); + +#endif + diff --git a/src/printdocvisitor.h b/src/printdocvisitor.h index ee2ae3ffab9..1d384b19148 100644 --- a/src/printdocvisitor.h +++ b/src/printdocvisitor.h @@ -134,6 +134,7 @@ class PrintDocVisitor : public DocVisitor case DocVerbatim::DocbookOnly: printf(""); break; case DocVerbatim::Dot: printf(""); break; case DocVerbatim::Msc: printf(""); break; + case DocVerbatim::PlantUML: printf(""); break; } printf("%s",s->text().data()); switch(s->type()) @@ -148,6 +149,7 @@ class PrintDocVisitor : public DocVisitor case DocVerbatim::DocbookOnly: printf(""); break; case DocVerbatim::Dot: printf(""); break; case DocVerbatim::Msc: printf(""); break; + case DocVerbatim::PlantUML: printf(""); break; } } void visit(DocAnchor *a) diff --git a/src/rtfdocvisitor.cpp b/src/rtfdocvisitor.cpp index 0d172080ca0..5e271374d02 100644 --- a/src/rtfdocvisitor.cpp +++ b/src/rtfdocvisitor.cpp @@ -16,6 +16,8 @@ * */ +#include + #include "rtfdocvisitor.h" #include "docparser.h" #include "language.h" @@ -26,13 +28,13 @@ #include "util.h" #include "rtfstyle.h" #include "message.h" -#include #include "parserintf.h" #include "msc.h" #include "dia.h" #include "filedef.h" #include "config.h" #include "htmlentity.h" +#include "plantuml.h" //#define DBG_RTF(x) m_t << x #define DBG_RTF(x) do {} while(0) @@ -317,6 +319,16 @@ void RTFDocVisitor::visit(DocVerbatim *s) if (Config_getBool("DOT_CLEANUP")) file.remove(); } break; + case DocVerbatim::PlantUML: + { + static QCString rtfOutput = Config_getString("RTF_OUTPUT"); + QCString baseName = writePlantUMLSource(rtfOutput,s->exampleFile(),s->text()); + + m_t << "\\par{\\qc "; // center picture + writePlantUMLFile(baseName); + m_t << "} "; + } + break; } m_lastIsPara=FALSE; } @@ -1704,3 +1716,23 @@ void RTFDocVisitor::writeDiaFile(const QCString &fileName) m_lastIsPara=TRUE; } +void RTFDocVisitor::writePlantUMLFile(const QCString &fileName) +{ + QCString baseName=fileName; + int i; + if ((i=baseName.findRev('/'))!=-1) + { + baseName=baseName.right(baseName.length()-i-1); + } + QCString outDir = Config_getString("RTF_OUTPUT"); + generatePlantUMLOutput(fileName,outDir,PUML_BITMAP); + if (!m_lastIsPara) m_t << "\\par" << endl; + m_t << "{" << endl; + m_t << rtf_Style_Reset; + m_t << "\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \""; + m_t << baseName << ".png"; + m_t << "\" \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par" << endl; + m_t << "}" << endl; + m_lastIsPara=TRUE; +} + diff --git a/src/rtfdocvisitor.h b/src/rtfdocvisitor.h index efc9d213cb5..c50802dd11a 100644 --- a/src/rtfdocvisitor.h +++ b/src/rtfdocvisitor.h @@ -155,6 +155,7 @@ class RTFDocVisitor : public DocVisitor void writeDotFile(const QCString &fileName); void writeMscFile(const QCString &fileName); void writeDiaFile(const QCString &fileName); + void writePlantUMLFile(const QCString &fileName); //-------------------------------------- // state variables diff --git a/src/xmldocvisitor.cpp b/src/xmldocvisitor.cpp index 343926c9f6b..f2da28c5456 100644 --- a/src/xmldocvisitor.cpp +++ b/src/xmldocvisitor.cpp @@ -209,6 +209,11 @@ void XmlDocVisitor::visit(DocVerbatim *s) filter(s->text()); m_t << ""; break; + case DocVerbatim::PlantUML: + m_t << ""; + filter(s->text()); + m_t << ""; + break; } } diff --git a/winbuild/Doxygen.vcproj b/winbuild/Doxygen.vcproj index f53951136ac..882e5149b5c 100644 --- a/winbuild/Doxygen.vcproj +++ b/winbuild/Doxygen.vcproj @@ -842,6 +842,10 @@ RelativePath="..\src\perlmodgen.cpp" >
+ + @@ -2614,6 +2618,10 @@ RelativePath="..\src\perlmodgen.h" > + +