//
// DocWriter.cpp
//
// Copyright (c) 2005-2014, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "DocWriter.h"
#include "Poco/NumberFormatter.h"
#include "Poco/Path.h"
#include "Poco/String.h"
#include "Poco/DateTime.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/RegularExpression.h"
#include "Poco/Exception.h"
#include "Poco/Format.h"
#include "Poco/Util/Application.h"
#include "Poco/CppParser/Struct.h"
#include "Poco/CppParser/Enum.h"
#include "Poco/CppParser/EnumValue.h"
#include "Poco/CppParser/Function.h"
#include "Poco/CppParser/Parameter.h"
#include "Poco/CppParser/TypeDef.h"
#include "Poco/CppParser/Variable.h"
#include
\n\n- ";
writeOrderedListItem(ostr, text);
state = TEXT_OLIST;
break;
case TEXT_LIST:
case TEXT_OLIST:
ostr << "
\n- ";
writeOrderedListItem(ostr, text);
state = TEXT_OLIST;
break;
case TEXT_LITERAL:
writeLiteral(ostr, text);
break;
default:
break;
}
break;
case TEXT_LITERAL:
switch (state)
{
case TEXT_PARAGRAPH:
ostr << "\n
";
writeLiteral(ostr, text);
state = TEXT_LITERAL;
break;
case TEXT_LIST:
writeText(ostr, text);
state = TEXT_LIST;
break;
case TEXT_OLIST:
writeText(ostr, text);
state = TEXT_OLIST;
break;
case TEXT_LITERAL:
writeLiteral(ostr, text);
break;
default:
break;
}
break;
case TEXT_WHITESPACE:
switch (state)
{
case TEXT_PARAGRAPH:
ostr << "\n";
break;
case TEXT_LIST:
ostr << "
\n\n";
state = TEXT_PARAGRAPH;
break;
case TEXT_OLIST:
ostr << "\n
\n";
state = TEXT_PARAGRAPH;
break;
case TEXT_LITERAL:
writeLiteral(ostr, text);
break;
default:
break;
}
break;
}
}
}
void DocWriter::writeSummary(std::ostream& ostr, const std::string& text, const std::string& uri)
{
ostr << "
";
std::string::const_iterator beg = text.begin();
std::string::const_iterator it = beg;
std::string::const_iterator end = text.end();
while (it != end && *it != '.') ++it;
if (it != end) ++it;
writeText(ostr, beg, it);
if (!uri.empty())
{
ostr << " ";
writeImageLink(ostr, uri, "arrow.png", tr("more"));
}
ostr << "
\n";
}
DocWriter::TextState DocWriter::analyzeLine(const std::string& line)
{
int nSpaces = 0;
std::string::const_iterator it = line.begin();
std::string::const_iterator end = line.end();
while (it != end && std::isspace(*it)) { ++it; ++nSpaces; }
if (it == end)
return TEXT_WHITESPACE;
else if (nSpaces < 3)
return TEXT_PARAGRAPH;
else if (*it == '-' || *it == '*')
return TEXT_LIST;
else if (std::isdigit(*it))
{
++it;
if (it != end && *it == '.')
return TEXT_OLIST;
}
return TEXT_LITERAL;
}
std::string DocWriter::htmlizeName(const std::string& name)
{
std::string result;
for (std::string::const_iterator it = name.begin(); it != name.end(); ++it)
{
if (*it == ' ')
result += " ";
else
result += htmlize(*it);
}
return result;
}
void DocWriter::writeText(std::ostream& ostr, const std::string& text)
{
std::string::const_iterator it(text.begin());
std::string::const_iterator end(text.end());
while (it != end && std::isspace(*it)) ++it;
if (it != end)
{
if (*it == '!')
{
std::string heading("h4");
++it;
if (it != end && *it == '!')
{
heading = "h3";
++it;
}
if (it != end && *it == '!')
{
heading = "h2";
++it;
}
while (it != end && std::isspace(*it)) ++it;
ostr << "<" << heading << ">" << format("", _titleId++) << htmlize(std::string(it, end)) << "" << heading << ">" << std::endl;
return;
}
}
writeText(ostr, it, end);
ostr << ' ';
}
void DocWriter::writeText(std::ostream& ostr, std::string::const_iterator begin, const std::string::const_iterator& end)
{
std::string token;
nextToken(begin, end, token);
while (!token.empty())
{
if (!writeSymbol(ostr, token, begin, end) && !writeSpecial(ostr, token, begin, end))
{
if (token == "[[")
{
std::string uri;
std::string text;
std::string::const_iterator it(begin);
while (it != end && std::isspace(*it)) ++it;
while (it != end && !std::isspace(*it)) uri += *it++;
while (it != end && std::isspace(*it)) ++it;
while (it != end && *it != ']') text += *it++;
while (it != end && *it == ']') ++it;
if (uri.compare(0, 6, "image:") == 0)
{
uri.erase(0, 6);
writeImage(ostr, uri, text);
}
else
{
std::string target;
if (uri.compare(0, 7, "http://") == 0 || uri.compare(0, 8, "https://") == 0)
target = "_blank";
writeTargetLink(ostr, uri, text, target);
}
begin = it;
nextToken(begin, end, token);
continue;
}
if (token == "RFC")
{
std::string::const_iterator it(begin);
std::string spc;
nextToken(begin, end, spc);
if (spc == " ")
{
std::string n;
nextToken(begin, end, n);
if (!n.empty() && std::isdigit(n[0]))
{
std::string uri(RFC_URI);
uri += n;
uri += ".txt";
writeTargetLink(ostr, uri, token + " " + n, "_blank");
nextToken(begin, end, token);
continue;
}
}
begin = it;
}
if (token == "GH")
{
std::string uri(GITHUB_POCO_URI);
std::string::const_iterator it(begin);
std::string spc;
nextToken(begin, end, spc);
if (spc == ":")
{
std::string proj;
nextToken(begin, end, proj);
uri = projectURI(proj);
nextToken(begin, end, spc);
}
if (spc == " ")
{
std::string hash;
nextToken(begin, end, hash);
if (hash == "#")
{
std::string n;
nextToken(begin, end, n);
if (!n.empty() && std::isdigit(n[0]))
{
uri += "/issues/";
uri += n;
writeTargetLink(ostr, uri, token + " #" + n, "_blank");
nextToken(begin, end, token);
continue;
}
}
}
begin = it;
}
if (token == "http")
{
std::string::const_iterator it(begin);
std::string css;
nextToken(begin, end, css);
if (css == "://")
{
std::string uri(token);
uri += css;
while (begin != end && !std::isspace(*begin) && *begin != '>' && *begin != ')') uri += *begin++;
if (uri[uri.length() - 1] == '.')
{
uri.resize(uri.length() - 1);
writeTargetLink(ostr, uri, uri, "_blank");
ostr << '.';
}
else writeTargetLink(ostr, uri, uri, "_blank");
nextToken(begin, end, token);
continue;
}
else
ostr << htmlize(token);
begin = it;
}
else
{
ostr << htmlize(token);
}
nextToken(begin, end, token);
}
}
}
void DocWriter::writeDecl(std::ostream& ostr, const std::string& decl)
{
writeDecl(ostr, decl.begin(), decl.end());
}
void DocWriter::writeDecl(std::ostream& ostr, std::string::const_iterator begin, const std::string::const_iterator& end)
{
std::string token;
nextToken(begin, end, token);
while (!token.empty())
{
if (!writeSymbol(ostr, token, begin, end))
{
ostr << htmlize(token);
nextToken(begin, end, token);
}
}
}
bool DocWriter::writeSymbol(std::ostream& ostr, std::string& token, std::string::const_iterator& begin, const std::string::const_iterator& end)
{
if (std::isalnum(token[0]) && _pNameSpace)
{
std::string id(token);
std::string next;
std::string::const_iterator it(begin);
nextToken(begin, end, next);
begin = it;
if (std::isupper(id[0]) || (!next.empty() && next[0] == '('))
{
std::string::const_iterator it2(begin);
while (next == "::")
{
nextToken(begin, end, next); // ::
id += next;
nextToken(begin, end, next); // id
id += next;
it2 = begin;
nextToken(begin, end, next);
begin = it2;
}
Symbol* pSym = _pNameSpace->lookup(id);
if (pSym)
{
writeLink(ostr, pSym, id);
nextToken(begin, end, token);
return true;
}
begin = it;
}
}
return false;
}
bool DocWriter::writeSpecial(std::ostream& ostr, std::string& token, std::string::const_iterator& begin, const std::string::const_iterator& end)
{
if (token == "<%")
{
_htmlMode = true;
ostr << "
";
}
else if (token == "<{")
{
_htmlMode = true;
}
else if (token == "%>")
{
if (_htmlMode)
{
_htmlMode = false;
ostr << "";
}
else
{
ostr << htmlize(token);
}
}
else if (token == "}>")
{
_htmlMode = false;
}
else if (token == "" || token == "=")
{
std::string prop;
nextToken(begin, end, token);
while (!token.empty() && token != "?>")
{
prop.append(token);
nextToken(begin, end, token);
}
Poco::trimInPlace(prop);
Application& app = Application::instance();
ostr << htmlize(app.config().getString(prop, std::string("NOT FOUND: ") + prop));
}
else if (token == "-")
{
std::string prop;
nextToken(begin, end, token);
while (!token.empty() && token != "?>")
{
prop.append(token);
nextToken(begin, end, token);
}
Poco::trimInPlace(prop);
Application& app = Application::instance();
ostr << app.config().getString(prop, "");
}
else if (_htmlMode)
{
ostr << token;
}
else if (token == "<*")
{
ostr << "";
}
else if (token == "*>")
{
ostr << "";
}
else if (token == "";
}
else if (token == "!>")
{
ostr << "";
}
else if (token == "<[")
{
ostr << "";
_literalMode = true;
}
else if (token == "]>")
{
ostr << "";
_literalMode = false;
}
else if (token == "`")
{
if (!_literalMode)
{
ostr << "";
_literalMode = true;
}
else
{
ostr << "";
_literalMode = false;
}
}
else if (token == "--" && !_literalMode)
{
ostr << "—";
}
else if (token == "iff" && !_literalMode)
{
ostr << tr("iff");
}
else if (token != "----")
{
return false;
}
nextToken(begin, end, token);
return true;
}
void DocWriter::nextToken(std::string::const_iterator& it, const std::string::const_iterator& end, std::string& token)
{
token.clear();
if (it != end && (std::isalnum(*it) || *it == '_'))
{
while (it != end && (std::isalnum(*it) || *it == '_')) token += *it++;
}
else if (it != end && std::isspace(*it))
{
while (it != end && std::isspace(*it)) token += *it++;
}
else if (it != end && *it == '<')
{
token += *it++;
if (it != end && std::ispunct(*it)) token += *it++;
if (it != end && std::ispunct(*it)) token += *it++;
}
else if (it != end && *it == '[')
{
token += *it++;
if (it != end && *it == '[') token += *it++;
}
else if (it != end && (*it == ']' || *it == '*' || *it == '!' || *it == '%' || *it == '}' || *it == '?'))
{
token += *it++;
if (it != end && *it == '>') token += *it++;
}
else if (it != end && *it == '-')
{
while (it != end && *it == '-') token += *it++;
}
else if (it != end && *it == ':')
{
while (it != end && (*it == ':' || *it == '/')) token += *it++;
}
else if (it != end)
{
token += *it++;
}
}
void DocWriter::writeListItem(std::ostream& ostr, const std::string& text)
{
std::string::const_iterator it = text.begin();
std::string::const_iterator end = text.end();
while (it != end && std::isspace(*it)) ++it;
if ((it != end && *it == '-') || *it == '*')
{
++it;
while (it != end && std::isspace(*it)) ++it;
}
writeText(ostr, it, end);
ostr << ' ';
}
void DocWriter::writeOrderedListItem(std::ostream& ostr, const std::string& text)
{
std::string::const_iterator it = text.begin();
std::string::const_iterator end = text.end();
while (it != end && std::isspace(*it)) ++it;
if (it != end && std::isdigit(*it))
{
while (it != end && std::isdigit(*it)) ++it;
if (it != end && *it == '.') ++it;
while (it != end && std::isspace(*it)) ++it;
}
writeText(ostr, it, end);
ostr << ' ';
}
void DocWriter::writeLiteral(std::ostream& ostr, const std::string& text)
{
if (_pendingLine)
{
ostr << "\n";
_pendingLine = false;
}
std::string::const_iterator it(text.begin());
std::string::const_iterator end(text.end());
if (_indent == 0)
{
while (it != end && std::isspace(*it))
{
++it;
++_indent;
}
}
else
{
int i = 0;
while (it != end && i < _indent)
{
++it;
++i;
}
}
std::string line(it, end);
Poco::trimRightInPlace(line);
if (line.empty())
_pendingLine = true;
else
ostr << htmlize(std::string(it, end)) << "\n";
}
void DocWriter::writeFileInfo(std::ostream& ostr, const Symbol* pSymbol)
{
std::string library(pSymbol->getLibrary());
std::string package(pSymbol->getPackage());
if (library.empty() || library == "ChangeThis")
logger().notice(std::string("No library name specified in ") + pSymbol->getFile());
if (package.empty() || package == "ChangeThis")
logger().notice(std::string("No package name specified in ") + pSymbol->getFile());
ostr << "
\n";
ostr << "" << tr("Library") << ": " << library << "
\n"
<< "" << tr("Package") << ": " << package << "
\n"
<< "" << tr("Header") << ": " << headerFor(pSymbol);
ostr << "
\n";
}
void DocWriter::writeInheritance(std::ostream& ostr, const Struct* pStruct)
{
std::set bases;
pStruct->bases(bases);
if (!bases.empty() || pStruct->derivedBegin() != pStruct->derivedEnd())
{
writeSubTitle(ostr, tr("Inheritance"));
if (pStruct->baseBegin() != pStruct->baseEnd())
{
ostr << "" << tr("Direct_Base_Classes") << ": ";
bool first = true;
for (Struct::BaseIterator it = pStruct->baseBegin(); it != pStruct->baseEnd(); ++it)
{
std::string base;
if (it->pClass)
{
if (it->pClass->nameSpace() == pStruct->nameSpace())
base = it->pClass->name();
else
base = it->pClass->fullName();
}
else base = it->name;
writeNameListItem(ostr, base, it->pClass, pStruct->nameSpace(), first);
}
ostr << "
\n";
}
if (!bases.empty())
{
ostr << "" << tr("All_Base_Classes") << ": ";
bool first = true;
for (std::set::const_iterator it = bases.begin(); it != bases.end(); ++it)
{
std::string base;
Symbol* pBase = pStruct->nameSpace()->lookup(*it);
if (pBase)
{
if (pBase->nameSpace() == pStruct->nameSpace())
base = pBase->name();
else
base = pBase->fullName();
}
else base = *it;
writeNameListItem(ostr, base, pBase, pStruct->nameSpace(), first);
}
ostr << "
\n";
}
Struct::StructSet derived;
pStruct->derived(derived);
if (!derived.empty())
{
ostr << "" << tr("Known_Derived_Classes") << ": ";
bool first = true;
for (Struct::StructSet::const_iterator it = derived.begin(); it != derived.end(); ++it)
{
std::string derived;
if ((*it)->nameSpace() == pStruct->nameSpace())
derived = (*it)->name();
else
derived = (*it)->fullName();
writeNameListItem(ostr, derived, *it, pStruct->nameSpace(), first);
}
ostr << "
\n";
}
}
}
void DocWriter::writeMethodSummary(std::ostream& ostr, const Struct* pStruct)
{
bool titleWritten = false;
MethodMap methods;
Struct::Functions functions;
pStruct->methods(Symbol::ACC_PUBLIC, functions);
for (Struct::Functions::const_iterator it = functions.begin(); it != functions.end(); ++it)
{
if (methods.find((*it)->name()) == methods.end())
methods[(*it)->name()] = *it;
}
pStruct->methods(Symbol::ACC_PROTECTED, functions);
for (Struct::Functions::const_iterator it = functions.begin(); it != functions.end(); ++it)
{
if (methods.find((*it)->name()) == methods.end())
methods[(*it)->name()] = *it;
}
if (!methods.empty())
{
writeSubTitle(ostr, tr("Member_Summary"));
titleWritten = true;
ostr << "" << tr("Member_Functions") << ": ";
bool first = true;
for (MethodMap::const_iterator it = methods.begin(); it != methods.end(); ++it)
{
writeNameListItem(ostr, it->first, it->second, pStruct, first);
}
ostr << "
\n";
}
methods.clear();
Struct::FunctionSet inhFunctions;
pStruct->inheritedMethods(inhFunctions);
for (Struct::FunctionSet::const_iterator it = inhFunctions.begin(); it != inhFunctions.end(); ++it)
{
if (methods.find((*it)->name()) == methods.end())
methods[(*it)->name()] = *it;
}
if (!methods.empty())
{
if (!titleWritten)
writeSubTitle(ostr, tr("Member_Summary"));
ostr << "" << tr("Inherited_Functions") << ": ";
bool first = true;
for (MethodMap::const_iterator it = methods.begin(); it != methods.end(); ++it)
{
writeNameListItem(ostr, it->first, it->second, pStruct, first);
}
ostr << "
\n";
}
}
void DocWriter::writeNestedClasses(std::ostream& ostr, const Struct* pStruct)
{
NameSpace::SymbolTable classes;
pStruct->classes(classes);
bool hasNested = false;
for (NameSpace::Iterator it = classes.begin(); it != classes.end(); ++it)
{
if (it->second->getAccess() != Symbol::ACC_PRIVATE)
hasNested = true;
}
if (hasNested)
{
writeSubTitle(ostr, tr("Nested_Classes"));
for (NameSpace::Iterator it = classes.begin(); it != classes.end(); ++it)
{
if (it->second->getAccess() != Symbol::ACC_PRIVATE)
writeClassSummary(ostr, static_cast(it->second));
}
}
}
void DocWriter::writeNameSpacesSummary(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable nameSpaces;
pNameSpace->nameSpaces(nameSpaces);
if (!nameSpaces.empty())
{
ostr << "" << tr("Namespaces") << ": " << std::endl;
bool first = true;
for (NameSpace::Iterator it = nameSpaces.begin(); it != nameSpaces.end(); ++it)
{
writeNameListItem(ostr, it->second->name(), it->second, pNameSpace, first);
}
ostr << "
" << std::endl;
}
}
void DocWriter::writeNameSpaces(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable nameSpaces;
pNameSpace->nameSpaces(nameSpaces);
if (!nameSpaces.empty())
{
writeSubTitle(ostr, tr("Namespaces"));
for (NameSpace::Iterator it = nameSpaces.begin(); it != nameSpaces.end(); ++it)
{
ostr << "";
std::string what("namespace ");
what += it->second->name();
writeLink(ostr, uriFor(it->second), what, "class");
ostr << "
\n";
}
}
}
void DocWriter::writeClassesSummary(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable classes;
pNameSpace->classes(classes);
if (!classes.empty())
{
ostr << "" << tr("Classes") << ": " << std::endl;
bool first = true;
for (NameSpace::Iterator it = classes.begin(); it != classes.end(); ++it)
{
writeNameListItem(ostr, it->second->name(), it->second, pNameSpace, first);
}
ostr << "
" << std::endl;
}
}
void DocWriter::writeClasses(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable classes;
pNameSpace->classes(classes);
if (!classes.empty())
{
writeSubTitle(ostr, tr("Classes"));
for (NameSpace::Iterator it = classes.begin(); it != classes.end(); ++it)
{
writeClassSummary(ostr, static_cast(it->second));
}
}
}
void DocWriter::writeClassSummary(std::ostream& ostr, const Struct* pStruct)
{
ostr << "";
std::string what;
if (pStruct->isClass())
what += "class ";
else
what += "struct ";
what += pStruct->name();
writeLink(ostr, uriFor(pStruct), what, "class");
if (pStruct->getAccess() != Symbol::ACC_PUBLIC)
writeIcon(ostr, "protected");
ostr << "
\n";
writeSummary(ostr, pStruct->getDocumentation(), uriFor(pStruct));
}
void DocWriter::writeTypesSummary(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable types;
pNameSpace->typeDefs(types);
if (!types.empty())
{
ostr << "" << tr("Types") << ": " << std::endl;
bool first = true;
for (NameSpace::Iterator it = types.begin(); it != types.end(); ++it)
{
writeNameListItem(ostr, it->second->name(), it->second, pNameSpace, first);
}
ostr << "
" << std::endl;
}
}
void DocWriter::writeAliasesSummary(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable types;
pNameSpace->typeAliases(types);
if (!types.empty())
{
ostr << "" << tr("Types Aliases") << ": " << std::endl;
bool first = true;
for (NameSpace::Iterator it = types.begin(); it != types.end(); ++it)
{
writeNameListItem(ostr, it->second->name(), it->second, pNameSpace, first);
}
ostr << "
" << std::endl;
}
}
void DocWriter::writeTypes(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable types;
pNameSpace->typeDefs(types);
bool hasTypes = false;
for (NameSpace::Iterator it = types.begin(); !hasTypes && it != types.end(); ++it)
{
if (it->second->getAccess() != Symbol::ACC_PRIVATE)
hasTypes = true;
}
if (hasTypes)
{
writeSubTitle(ostr, tr("Types"));
for (NameSpace::Iterator it = types.begin(); it != types.end(); ++it)
{
if (it->second->getAccess() != Symbol::ACC_PRIVATE)
writeType(ostr, static_cast(it->second));
}
}
}
void DocWriter::writeAliases(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable types;
pNameSpace->typeAliases(types);
bool hasTypes = false;
for (NameSpace::Iterator it = types.begin(); !hasTypes && it != types.end(); ++it)
{
if (it->second->getAccess() != Symbol::ACC_PRIVATE)
hasTypes = true;
}
if (hasTypes)
{
writeSubTitle(ostr, tr("Types Aliases"));
for (NameSpace::Iterator it = types.begin(); it != types.end(); ++it)
{
if (it->second->getAccess() != Symbol::ACC_PRIVATE)
writeType(ostr, static_cast(it->second));
}
}
}
void DocWriter::writeType(std::ostream& ostr, const TypeDef* pType)
{
ostr << "";
writeAnchor(ostr, pType->name(), pType);
if (pType->getAccess() != Symbol::ACC_PUBLIC)
writeIcon(ostr, "protected");
ostr << "
\n";
ostr << "";
writeDecl(ostr, pType->declaration());
ostr << ";
\n";
writeDescription(ostr, pType->getDocumentation());
}
void DocWriter::writeEnums(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable enums;
pNameSpace->enums(enums);
bool hasEnums = false;
for (NameSpace::Iterator it = enums.begin(); !hasEnums && it != enums.end(); ++it)
{
if (it->second->getAccess() != Symbol::ACC_PRIVATE)
hasEnums = true;
}
if (hasEnums)
{
writeSubTitle(ostr, tr("Enumerations"));
for (NameSpace::Iterator it = enums.begin(); it != enums.end(); ++it)
{
if (it->second->getAccess() != Symbol::ACC_PRIVATE)
writeEnum(ostr, static_cast(it->second));
}
}
}
void DocWriter::writeEnum(std::ostream& ostr, const Enum* pEnum)
{
ostr << "";
const std::string& name = pEnum->name();
if (name[0] == '#')
ostr << "" << tr("Anonymous") << "";
else
writeAnchor(ostr, name, pEnum);
if (pEnum->getAccess() != Symbol::ACC_PUBLIC)
writeIcon(ostr, "protected");
ostr << "
\n";
writeDescription(ostr, pEnum->getDocumentation());
for (Enum::Iterator it = pEnum->begin(); it != pEnum->end(); ++it)
{
const std::string& name = (*it)->name();
const std::string& value = (*it)->value();
ostr << "";
writeAnchor(ostr, name, *it);
if (!value.empty())
ostr << " = " << htmlize(value);
ostr << "
\n";
writeDescription(ostr, (*it)->getDocumentation());
}
}
void DocWriter::writeConstructors(std::ostream& ostr, const Struct* pStruct)
{
Struct::Functions ctors;
pStruct->constructors(ctors);
if (!ctors.empty())
{
writeSubTitle(ostr, tr("Constructors"));
for (Struct::Functions::const_iterator it = ctors.begin(); it != ctors.end(); ++it)
{
if ((*it)->getAccess() == Symbol::ACC_PUBLIC)
writeFunction(ostr, *it);
}
for (Struct::Functions::const_iterator it = ctors.begin(); it != ctors.end(); ++it)
{
if ((*it)->getAccess() == Symbol::ACC_PROTECTED)
writeFunction(ostr, *it);
}
}
}
void DocWriter::writeDestructor(std::ostream& ostr, const Struct* pStruct)
{
if (pStruct->destructor())
{
writeSubTitle(ostr, tr("Destructor"));
writeFunction(ostr, pStruct->destructor());
}
}
void DocWriter::writeMethods(std::ostream& ostr, const Struct* pStruct)
{
Struct::Functions methods;
pStruct->methods(Symbol::ACC_PUBLIC, methods);
pStruct->methods(Symbol::ACC_PROTECTED, methods);
if (!methods.empty())
{
writeSubTitle(ostr, tr("Member_Functions"));
for (Struct::Functions::const_iterator it = methods.begin(); it != methods.end(); ++it)
{
writeFunction(ostr, *it);
}
}
}
void DocWriter::writeFunctionsSummary(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable funcs;
pNameSpace->functions(funcs);
if (!funcs.empty())
{
ostr << "" << tr("Functions") << ": " << std::endl;
std::string lastName;
bool first = true;
for (NameSpace::Iterator it = funcs.begin(); it != funcs.end(); ++it)
{
if (it->second->name() != lastName)
{
writeNameListItem(ostr, it->second->name(), it->second, pNameSpace, first);
lastName = it->second->name();
}
}
ostr << "
" << std::endl;
}
}
void DocWriter::writeFunctions(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable funcs;
pNameSpace->functions(funcs);
if (!funcs.empty())
{
writeSubTitle(ostr, tr("Functions"));
for (NameSpace::Iterator it = funcs.begin(); it != funcs.end(); ++it)
{
writeFunction(ostr, static_cast(it->second));
}
}
}
void DocWriter::writeFunction(std::ostream& ostr, const Function* pFunc)
{
ostr << "";
writeAnchor(ostr, pFunc->name(), pFunc);
if (pFunc->getAccess() != Symbol::ACC_PUBLIC)
writeIcon(ostr, "protected");
if (pFunc->isVirtual())
writeIcon(ostr, "virtual");
else if (pFunc->flags() & Function::FN_STATIC)
writeIcon(ostr, "static");
if (pFunc->flags() & Function::FN_INLINE)
writeIcon(ostr, "inline");
ostr << "
\n";
ostr << "";
const std::string& decl = pFunc->declaration();
writeDecl(ostr, decl);
if (!std::isalnum(decl[decl.length() - 1]))
ostr << " ";
ostr << "(";
bool hasArgs = false;
for (Function::Iterator it = pFunc->begin(); it != pFunc->end(); ++it)
{
hasArgs = true;
if (it != pFunc->begin())
ostr << ",";
ostr << "
";
writeDecl(ostr, (*it)->declaration());
if ((*it)->hasDefaultValue())
{
ostr << " = " << (*it)->defaultDecl();
}
}
if (hasArgs)
ostr << "
";
ostr << ")";
if (pFunc->flags() & Function::FN_CONST)
ostr << " const";
if (pFunc->flags() & Function::FN_OVERRIDE)
ostr << " override";
else if (pFunc->flags() & Function::FN_FINAL)
ostr << " final";
if (pFunc->flags() & Function::FN_NOEXCEPT)
ostr << " noexcept";
if (pFunc->flags() & Function::FN_DELETE)
ostr << " = delete";
else if (pFunc->flags() & Function::FN_DEFAULT)
ostr << " = default";
else if (pFunc->flags() & Function::FN_PURE_VIRTUAL)
ostr << " = 0";
ostr << ";
\n";
if (pFunc->attrs().has("deprecated"))
{
writeDeprecated(ostr, "function");
}
Function* pOverridden = pFunc->getOverridden();
const std::string& doc(pFunc->getDocumentation());
if (doc.empty() && pFunc->isPublic() && !pOverridden && !pFunc->isConstructor() && !pFunc->isDestructor())
logger().notice(std::string("Undocumented public function: ") + pFunc->fullName());
writeDescription(ostr, doc);
if (pOverridden)
{
ostr << "" << tr("See_also") << ": ";
writeLink(ostr, pOverridden, pOverridden->fullName() + "()");
ostr << "
\n";
}
}
void DocWriter::writeVariables(std::ostream& ostr, const NameSpace* pNameSpace)
{
NameSpace::SymbolTable vars;
pNameSpace->variables(vars);
bool hasVars = false;
for (NameSpace::Iterator it = vars.begin(); !hasVars && it != vars.end(); ++it)
{
if (it->second->getAccess() != Symbol::ACC_PRIVATE)
hasVars = true;
}
if (hasVars)
{
writeSubTitle(ostr, tr("Variables"));
for (NameSpace::Iterator it = vars.begin(); it != vars.end(); ++it)
{
if (it->second->getAccess() == Symbol::ACC_PUBLIC)
writeVariable(ostr, static_cast(it->second));
}
for (NameSpace::Iterator it = vars.begin(); it != vars.end(); ++it)
{
if (it->second->getAccess() == Symbol::ACC_PROTECTED)
writeVariable(ostr, static_cast(it->second));
}
}
}
void DocWriter::writeVariable(std::ostream& ostr, const Variable* pVar)
{
ostr << "";
writeAnchor(ostr, pVar->name(), pVar);
if (pVar->getAccess() != Symbol::ACC_PUBLIC)
writeIcon(ostr, "protected");
if (pVar->flags() & Function::FN_STATIC)
writeIcon(ostr, "static");
ostr << "
\n";
ostr << "";
writeDecl(ostr, pVar->declaration());
ostr << ";
\n";
writeDescription(ostr, pVar->getDocumentation());
}
void DocWriter::writeNameListItem(std::ostream& ostr, const std::string& str, const Symbol* pSymbol, const NameSpace* pNameSpace, bool& first)
{
if (first)
first = false;
else
ostr << ", ";
if (pSymbol)
writeLink(ostr, pSymbol, str);
else
ostr << htmlizeName(str);
}
void DocWriter::writeLink(std::ostream& ostr, const std::string& uri, const std::string& text)
{
ostr << "" << htmlize(text) << "";
}
void DocWriter::writeLink(std::ostream& ostr, const Symbol* pSymbol, const std::string& name)
{
ostr << "" << htmlizeName(name) << "";
}
void DocWriter::writeLink(std::ostream& ostr, const std::string& uri, const std::string& text, const std::string& linkClass)
{
ostr << "" << htmlize(text) << "";
}
void DocWriter::writeTargetLink(std::ostream& ostr, const std::string& uri, const std::string& text, const std::string& target)
{
if (_noFrames && target != "_blank")
ostr << "" << text << "";
else if (!target.empty())
ostr << "" << text << "";
else
ostr << "" << htmlize(text) << "";
}
void DocWriter::writeImageLink(std::ostream& ostr, const std::string& uri, const std::string& image, const std::string& alt)
{
ostr << "";
ostr << "
";
ostr << "";
}
void DocWriter::writeImage(std::ostream& ostr, const std::string& uri, const std::string& caption)
{
ostr << "" << std::endl;
ostr << "
![\"" \""](\"")
" << std::endl;
if (!caption.empty())
{
ostr << "
" << htmlize(caption) << "
";
}
ostr << "
" << std::endl;
}
void DocWriter::writeIcon(std::ostream& ostr, const std::string& icon)
{
ostr << "
";
}
void DocWriter::writeAnchor(std::ostream& ostr, const std::string& text, const Symbol* pSymbol)
{
ostr << "id() << "\">" << htmlize(text) << "";
}
void DocWriter::writeDeprecated(std::ostream& ostr, const std::string& what)
{
ostr << "" << std::endl;
ostr << "
" << tr("Deprecated") << ". " << tr("This") << " " << what << " " << tr("is_deprecated") << ".
" << std::endl;
ostr << "
" << std::endl;
}
std::string DocWriter::headerFor(const Symbol* pSymbol)
{
Path path(pSymbol->getFile());
std::string header;
int i = 0;
while (i < path.depth() - 1 && path[i] != "include") ++i;
for (++i; i < path.depth(); ++i)
{
header += path[i];
header += "/";
}
header += path.getFileName();
return header;
}
std::string DocWriter::titleFor(const Symbol* pSymbol)
{
std::string title;
switch (pSymbol->kind())
{
case Symbol::SYM_NAMESPACE:
title += "namespace";
break;
case Symbol::SYM_STRUCT:
title += (static_cast(pSymbol)->isClass() ? "class" : "struct");
break;
case Symbol::SYM_ENUM:
title += "enum ";
break;
default:
break;
}
if (!title.empty()) title += " ";
const Function* pFunc = dynamic_cast(pSymbol);
if (pFunc && pFunc->isConstructor())
{
title += "class " + pSymbol->nameSpace()->fullName();
}
else
{
title += pSymbol->fullName();
if (pFunc) title += "()";
}
return title;
}
std::string DocWriter::htmlize(const std::string& str)
{
std::string result;
for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
result += htmlize(*it);
return result;
}
std::string DocWriter::htmlize(char c)
{
std::string result;
switch (c)
{
case '"':
result += """;
break;
case '<':
result += "<";
break;
case '>':
result += ">";
break;
case '&':
result += "&";
break;
default:
result += c;
}
return result;
}
void DocWriter::libraries(std::set& libs)
{
for (NameSpace::SymbolTable::const_iterator it = _symbols.begin(); it != _symbols.end(); ++it)
{
const std::string& lib = it->second->getLibrary();
if (!lib.empty())
libs.insert(lib);
}
}
void DocWriter::packages(const std::string& lib, std::set& packages)
{
for (NameSpace::SymbolTable::const_iterator it = _symbols.begin(); it != _symbols.end(); ++it)
{
const std::string& plib = it->second->getLibrary();
const std::string& pkg = it->second->getPackage();
if (!pkg.empty() && lib == plib)
packages.insert(pkg);
}
}
void DocWriter::writePages()
{
_pNameSpace = rootNameSpace();
for (PageMap::iterator it = _pages.begin(); it != _pages.end(); ++it)
{
writePage(it->second);
}
}
void DocWriter::writePage(Page& page)
{
std::ifstream istr(page.path.c_str());
if (!istr.good()) throw Poco::OpenFileException(page.path);
std::string title;
std::string category;
std::string text;
int ch = istr.get();
while (ch != -1 && ch != '\n') { title += (char) ch; ch = istr.get(); }
ch = istr.get();
while (ch != -1 && ch != '\n') { category += (char) ch; ch = istr.get(); }
while (std::isspace(ch)) ch = istr.get();
while (ch != -1)
{
text += (char) ch;
if (ch == '\n') text += ' ';
ch = istr.get();
}
page.title = title;
page.category = category;
TOC toc;
scanTOC(text, toc);
std::string path(pathFor(page.fileName));
std::ofstream ostr(path.c_str());
if (!ostr.good()) throw Poco::CreateFileException(path);
writeHeader(ostr, title, "js/iframeResizer.min.js");
writeTitle(ostr, tr(category), title);
beginBody(ostr);
writeNavigationFrame(ostr, "category", category);
beginContent(ostr);
if (!toc.empty())
{
writeTOC(ostr, toc);
}
writeDescription(ostr, text);
writeCopyright(ostr);
endContent(ostr);
endBody(ostr);
ostr << "" << std::endl;
writeFooter(ostr);
}
void DocWriter::scanTOC(const std::string& text, TOC& toc)
{
int titleId = 0;
std::istringstream istr(text);
while (!istr.eof())
{
std::string line;
std::getline(istr, line);
std::string::const_iterator it(line.begin());
std::string::const_iterator end(line.end());
while (it != end && std::isspace(*it)) ++it;
if (it != end && *it == '!')
{
++it;
int level = MAX_TITLE_LEVEL;
while (it != end && *it == '!')
{
level--;
++it;
}
while (it != end && std::isspace(*it)) ++it;
TOCEntry entry;
entry.id = titleId++;
entry.level = level;
entry.title.assign(it, end);
toc.push_back(entry);
}
}
}
void DocWriter::writeTOC(std::ostream& ostr, const TOC& toc)
{
ostr << "" << std::endl;
ostr << "
- " << tr("TOC") << std::endl;
int lastLevel = 0;
std::vector levelStack;
levelStack.push_back(0);
for (TOC::const_iterator it = toc.begin(); it != toc.end(); ++it)
{
int level = it->level;
if (level > levelStack.back())
{
levelStack.push_back(level);
ostr << "
" << std::endl;
}
else if (level < levelStack.back())
{
ostr << "
" << std::endl;
while (level < levelStack.back())
{
levelStack.pop_back();
ostr << "
" << std::endl;
}
}
else
{
ostr << "" << std::endl;
}
ostr << "
id << "\">" << htmlize(it->title) << "" << std::endl;
lastLevel = level;
}
while (!levelStack.empty())
{
ostr << "" << std::endl;
levelStack.pop_back();
}
ostr << "
" << std::endl;
}
void DocWriter::writeCategoryIndex(const std::string& category, const std::string& fileName)
{
std::ofstream ostr(pathFor(fileName).c_str());
if (!ostr.good()) throw Poco::CreateFileException(fileName);
writeHeader(ostr, tr(category));
beginBody(ostr);
ostr << "" << htmlize(tr(category)) << "
";
writeCategoryIndex(ostr, category, "_top");
endBody(ostr);
writeFooter(ostr);
}
void DocWriter::writeCategoryIndex(std::ostream& ostr, const std::string& category, const std::string& target)
{
ostr << "\n";
for (PageMap::const_iterator it = _pages.begin(); it != _pages.end(); ++it)
{
if (it->second.category == category)
{
ostr << "- ";
writeTargetLink(ostr, it->second.fileName, it->second.title, target);
ostr << "
\n";
}
}
ostr << "
\n";
}
NameSpace* DocWriter::rootNameSpace() const
{
for (NameSpace::SymbolTable::const_iterator it = _symbols.begin(); it != _symbols.end(); ++it)
{
if (it->second->kind() == Symbol::SYM_NAMESPACE)
{
NameSpace* pNS = static_cast(it->second);
while (pNS->nameSpace()) pNS = pNS->nameSpace();
return pNS;
}
}
return 0;
}
const std::string& DocWriter::tr(const std::string& id)
{
StringMap::const_iterator it = _strings.find(id);
if (it == _strings.end())
{
loadString(id, id, _language);
it = _strings.find(id);
}
if (it != _strings.end())
return it->second;
else
return id;
}
void DocWriter::loadString(const std::string& id, const std::string& def, const std::string& language)
{
Application& app = Application::instance();
std::pair p(id, app.config().getString("Translations." + language + "." + id, def));
_strings.insert(p);
}
void DocWriter::loadStrings(const std::string& language)
{
_strings.clear();
loadString("AAAIntroduction", "Introduction", language);
loadString("All_Base_Classes", "All Base Classes", language);
loadString("All_Symbols", "All Symbols", language);
loadString("Anonymous", "Anonymous", language);
loadString("Constructors", "Constructors", language);
loadString("Class", "Class", language);
loadString("Deprecated", "Deprecated", language);
loadString("Description", "Description", language);
loadString("Destructor", "Destructor", language);
loadString("Direct_Base_Classes", "Direct Base Classes", language);
loadString("Enumerations", "Enumerations", language);
loadString("Functions", "Functions", language);
loadString("Header", "Header", language);
loadString("iff", "if and only if", language);
loadString("Inheritance", "Inheritance", language);
loadString("Inherited_Functions", "Inherited Functions", language);
loadString("is_deprecated", "is deprecated and should no longer be used", language);
loadString("Known_Derived_Classes", "Known Derived Classes", language);
loadString("Library", "Library", language);
loadString("License", "License", language);
loadString("Member_Functions", "Member Functions", language);
loadString("Member_Summary", "Member Summary", language);
loadString("more", "more...", language);
loadString("Namespaces", "Namespaces", language);
loadString("Namespace", "Namespace", language);
loadString("Nested_Classes", "Nested Classes", language);
loadString("Package", "Package", language);
loadString("Packages", "Packages", language);
loadString("Package_Index", "Package Index", language);
loadString("Reference", "Reference", language);
loadString("See_also", "See also", language);
loadString("Struct", "Struct", language);
loadString("Symbol_Index", "Symbol Index", language);
loadString("This", "This", language);
loadString("Types", "Types", language);
loadString("Variables", "Variables", language);
}
std::string DocWriter::projectURI(const std::string& proj)
{
Application& app = Application::instance();
std::string key("PocoDoc.projects.");
key += proj;
std::string uri = app.config().getString(key, "");
if (uri.empty())
{
app.logger().warning("No project URI found for %s", proj);
uri = GITHUB_POCO_URI;
}
return uri;
}