blob: 562a65ddb10afc8eb1d27cc50e4967e21cc8d522 [file] [log] [blame]
// Boost.Geometry (aka GGL, Generic Geometry Library)
//
// Copyright (c) 2010-2011 Barend Gehrels, Amsterdam, the Netherlands.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
//
#ifndef DOXYGEN_XML_PARSER_HPP
#define DOXYGEN_XML_PARSER_HPP
#include <string>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <rapidxml_util.hpp>
#include <doxygen_elements.hpp>
#include <parameter_predicates.hpp>
#include <configuration.hpp>
inline std::string keep_after(std::string const& input, std::string const& sig)
{
std::size_t pos = input.rfind(sig);
if (pos != std::string::npos)
{
std::string copy = input.substr(pos + sig.length());
return copy;
}
return input;
}
static inline void add_or_set(std::vector<parameter>& parameters, parameter const& p)
{
std::vector<parameter>::iterator it = std::find_if(parameters.begin(), parameters.end(), par_by_name(p.name));
if (it != parameters.end())
{
if (it->brief_description.empty()) it->brief_description = p.brief_description;
if (it->type.empty()) it->type = p.type;
if (it->fulltype.empty()) it->fulltype = p.fulltype;
if (it->default_value.empty()) it->default_value = p.default_value;
}
else
{
parameters.push_back(p);
}
}
/// Parses a "para" element
/*
This is used for different purposes within Doxygen.
- Either a detailed description, possibly containing several sections (para's)
-> so parse next siblings
- Or a detailed description also containing qbk records
So we have to list explicitly either where to recurse, or where not to...
*/
static void parse_para(rapidxml::xml_node<>* node, std::string& contents, bool& skip, bool first = true)
{
if (node != NULL)
{
if (node->type() == rapidxml::node_element)
{
//std::cout << "ELEMENT: " << node->name() << "=" << node->value() << std::endl;
std::string name = node->name();
if (boost::equals(name, "qbk.skip"))
{
skip = true;
return;
}
else if (! (
(boost::equals(name, "para") && first)
|| boost::equals(name, "ref")
|| boost::equals(name, "defval")
|| boost::equals(name, "verbatim")
|| boost::equals(name, "bold")
|| boost::equals(name, "emphasis")
|| boost::equals(name, "linebreak")
))
{
return;
}
}
else if (node->type() == rapidxml::node_data)
{
contents += node->value();
//std::cout << "DATA: " << node->name() << "=" << node->value() << std::endl;
}
else
{
//std::cout << "OTHER: " << node->name() << "=" << node->value() << std::endl;
}
parse_para(node->first_node(), contents, skip, false);
parse_para(node->next_sibling(), contents, skip, false);
}
}
static void parse_parameter(rapidxml::xml_node<>* node, parameter& p)
{
// #define: <param><defname>Point</defname></param>
// template: <param><type>typename</type><declname>CoordinateType</declname><defname>CoordinateType</defname></param>
// template with default: <param><type>typename</type><declname>CoordinateSystem</declname><defname>CoordinateSystem</defname><defval><ref ....>cs::cartesian</ref></defval></param>
// with enum: <type><ref refid="group__enum_1ga7d33eca9a5389952bdf719972eb802b6" kindref="member">closure_selector</ref></type>
if (node != NULL)
{
std::string name = node->name();
if (name == "type")
{
get_contents(node->first_node(), p.fulltype);
p.type = p.fulltype;
boost::replace_all(p.type, " const", "");
boost::trim(p.type);
boost::replace_all(p.type, "&", "");
boost::replace_all(p.type, "*", "");
boost::trim(p.type);
}
else if (name == "declname") p.name = node->value();
else if (name == "parametername") p.name = node->value();
else if (name == "defname") p.name = node->value();
else if (name == "defval")
{
parse_para(node, p.default_value, p.skip);
}
else if (name == "para")
{
parse_para(node, p.brief_description, p.skip);
}
parse_parameter(node->first_node(), p);
parse_parameter(node->next_sibling(), p);
}
}
static void parse_enumeration_value(rapidxml::xml_node<>* node, enumeration_value& value)
{
// <enumvalue><name>green</name><initializer> 2</initializer>
// <briefdescription><para>...</para></briefdescription>
// <detaileddescription><para>...</para></detaileddescription>
// </enumvalue>
if (node != NULL)
{
std::string node_name = node->name();
if (node_name == "name") value.name = node->value();
else if (node_name == "para")
{
// Parses both brief AND detailed into this description
parse_para(node, value.brief_description, value.skip);
}
else if (node_name == "initializer")
{
value.initializer = node->value();
}
parse_enumeration_value(node->first_node(), value);
parse_enumeration_value(node->next_sibling(), value);
}
}
// Definition is a function or a class/struct
template <typename Parameters>
static void parse_parameter_list(rapidxml::xml_node<>* node, Parameters& parameters)
{
if (node != NULL)
{
std::string name = node->name();
if (name == "parameteritem")
{
parameter p;
parse_parameter(node->first_node(), p);
if (! p.name.empty())
{
// Copy its description
std::vector<parameter>::iterator it = std::find_if(parameters.begin(),
parameters.end(), par_by_name(p.name));
if (it != parameters.end())
{
it->brief_description = p.brief_description;
}
else
{
parameters.push_back(p);
}
}
}
else if (name == "param")
{
// Element of 'templateparamlist.param (.type,.declname,.defname)'
parameter p;
parse_parameter(node->first_node(), p);
// Doxygen handles templateparamlist param's differently:
//
// Case 1:
// <param><type>typename T</type></param>
// -> no name, assign type to name, replace typename
//
// Case 2:
// <type>typename</type><declname>T</declname><defname>T</defname>
// -> set full type
if (p.name.empty())
{
// Case 1
p.name = p.type;
boost::replace_all(p.name, "typename", "");
boost::trim(p.name);
}
else
{
// Case 2
p.fulltype = p.type + " " + p.name;
}
add_or_set(parameters, p);
}
parse_parameter_list(node->first_node(), parameters);
parse_parameter_list(node->next_sibling(), parameters);
}
}
template <typename Element>
static void parse_element(rapidxml::xml_node<>* node, configuration const& config, std::string const& parent, Element& el)
{
if (node != NULL)
{
std::string name = node->name();
std::string full = parent + "." + name;
if (full == ".briefdescription.para")
{
parse_para(node, el.brief_description, el.skip);
}
else if (full == ".detaileddescription.para")
{
std::string para;
parse_para(node, para, el.skip);
if (!para.empty() && !el.detailed_description.empty())
{
el.detailed_description += "\n\n";
}
el.detailed_description += para;
}
else if (full == ".location")
{
std::string loc = get_attribute(node, "file");
// Location of (header)file. It is a FULL path, so find the start
// and strip the rest
std::size_t pos = loc.rfind(config.start_include);
if (pos != std::string::npos)
{
loc = loc.substr(pos);
}
el.location = loc;
el.line = atol(get_attribute(node, "line").c_str());
}
else if (full == ".detaileddescription.para.qbk")
{
el.qbk_markup.push_back(markup(node->value()));
}
else if (full == ".detaileddescription.para.qbk.after.synopsis")
{
el.qbk_markup.push_back(markup(markup_after, markup_synopsis, node->value()));
}
else if (full == ".detaileddescription.para.qbk.before.synopsis")
{
el.qbk_markup.push_back(markup(markup_before, markup_synopsis, node->value()));
}
else if (full == ".detaileddescription.para.qbk.distinguish")
{
el.additional_description = node->value();
boost::trim(el.additional_description);
}
else if (full == ".templateparamlist")
{
parse_parameter_list(node->first_node(), el.template_parameters);
}
else if (full == ".detaileddescription.para.parameterlist")
{
std::string kind = get_attribute(node, "kind");
if (kind == "param")
{
parse_parameter_list(node->first_node(), el.parameters);
}
else if (kind == "templateparam")
{
parse_parameter_list(node->first_node(), el.template_parameters);
}
}
parse_element(node->first_node(), config, full, el);
parse_element(node->next_sibling(), config, parent, el);
}
}
static void parse_function(rapidxml::xml_node<>* node, configuration const& config, std::string const& parent, function& f)
{
if (node != NULL)
{
std::string name = node->name();
std::string full = parent + "." + name;
if (full == ".name") f.name = node->value();
else if (full == ".argsstring") f.argsstring = node->value();
else if (full == ".definition")
{
f.definition = node->value();
if (! config.skip_namespace.empty())
{
boost::replace_all(f.definition, config.skip_namespace, "");
}
}
else if (full == ".param")
{
parameter p;
parse_parameter(node->first_node(), p);
add_or_set(f.parameters, p);
}
else if (full == ".type")
{
get_contents(node->first_node(), f.return_type);
}
else if (full == ".detaileddescription.para.simplesect")
{
std::string kind = get_attribute(node, "kind");
if (kind == "return")
{
get_contents(node->first_node(), f.return_description);
}
/*else if (kind == "param")
{
get_contents(node->first_node(), f.paragraphs);
}*/
}
else if (full == ".detaileddescription.para.image")
{
}
parse_function(node->first_node(), config, full, f);
parse_function(node->next_sibling(), config, parent, f);
}
}
static void parse_enumeration(rapidxml::xml_node<>* node, configuration const& config, std::string const& parent, enumeration& e)
{
if (node != NULL)
{
std::string name = node->name();
std::string full = parent + "." + name;
if (full == ".name") e.name = node->value();
else if (full == ".enumvalue")
{
enumeration_value value;
parse_enumeration_value(node->first_node(), value);
e.enumeration_values.push_back(value);
}
parse_enumeration(node->first_node(), config, full, e);
parse_enumeration(node->next_sibling(), config, parent, e);
}
}
static std::string parse_named_node(rapidxml::xml_node<>* node, std::string const& look_for_name)
{
if (node != NULL)
{
std::string node_name = node->name();
std::string contents;
if (boost::equals(node_name, look_for_name))
{
contents = node->value();
}
return contents
+ parse_named_node(node->first_node(), look_for_name)
+ parse_named_node(node->next_sibling(), look_for_name);
}
return "";
}
static void parse(rapidxml::xml_node<>* node, configuration const& config, documentation& doc, bool member = false)
{
if (node != NULL)
{
bool recurse = false;
bool is_member = member;
std::string nodename = node->name();
if (nodename == "doxygen")
{
recurse = true;
}
else if (nodename == "sectiondef")
{
std::string kind = get_attribute(node, "kind");
if (kind == "func"
|| kind == "define"
|| kind == "enum"
)
{
recurse = true;
}
else if (boost::starts_with(kind, "public"))
{
recurse = true;
is_member = true;
}
}
else if (nodename == "compounddef")
{
std::string kind = get_attribute(node, "kind");
if (kind == "group")
{
recurse = true;
}
else if (kind == "struct")
{
recurse = true;
doc.cos.is_class = false;
parse_element(node->first_node(), config, "", doc.cos);
}
else if (kind == "class")
{
recurse = true;
doc.cos.is_class = true;
parse_element(node->first_node(), config, "", doc.cos);
}
}
else if (nodename == "memberdef")
{
std::string kind = get_attribute(node, "kind");
if (kind == "function")
{
function f;
parse_element(node->first_node(), config, "", f);
parse_function(node->first_node(), config, "", f);
if (member)
{
f.type = boost::equals(f.name, doc.cos.name)
? function_constructor
: function_member;
doc.cos.functions.push_back(f);
}
else
{
f.type = function_free;
doc.functions.push_back(f);
}
}
else if (kind == "define")
{
function f;
f.type = function_define;
parse_element(node->first_node(), config, "", f);
parse_function(node->first_node(), config, "", f);
doc.functions.push_back(f);
}
else if (kind == "enum")
{
enumeration e;
parse_element(node->first_node(), config, "", e);
parse_enumeration(node->first_node(), config, "", e);
doc.enumerations.push_back(e);
}
else if (kind == "typedef")
{
if (boost::equals(get_attribute(node, "prot"), "public"))
{
std::string name = parse_named_node(node->first_node(), "name");
doc.cos.typedefs.push_back(base_element(name));
}
}
else if (kind == "variable")
{
if (boost::equals(get_attribute(node, "static"), "yes")
&& boost::equals(get_attribute(node, "mutable"), "no")
&& boost::equals(get_attribute(node, "prot"), "public"))
{
std::string name = parse_named_node(node->first_node(), "name");
doc.cos.variables.push_back(base_element(name));
}
}
}
else if (nodename == "compoundname")
{
std::string name = node->value();
if (name.find("::") != std::string::npos)
{
doc.cos.fullname = name;
// For a class, it should have "boost::something::" before
// set its name without namespace
doc.cos.name = keep_after(name, "::");
}
}
else if (nodename == "basecompoundref")
{
base_class bc;
bc.name = node->value();
bc.derivation = get_attribute(node, "prot");
bc.virtuality = get_attribute(node, "virt");
doc.cos.base_classes.push_back(bc);
}
else
{
//std::cout << nodename << " ignored." << std::endl;
}
if (recurse)
{
// First recurse into childnodes, then handle next siblings
parse(node->first_node(), config, doc, is_member);
}
parse(node->next_sibling(), config, doc, is_member);
}
}
#endif // DOXYGEN_XML_PARSER_HPP