1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2010-2013 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2012-2013 Adam Wulkiewicz, Lodz, Poland.
5 // Use, modification and distribution is subject to the Boost Software License,
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
10 #ifndef DOXYGEN_XML_PARSER_HPP
11 #define DOXYGEN_XML_PARSER_HPP
17 #include <boost/algorithm/string.hpp>
19 #include <rapidxml_util.hpp>
20 #include <doxygen_elements.hpp>
21 #include <parameter_predicates.hpp>
22 #include <configuration.hpp>
25 inline std::string keep_after(std::string const& input, std::string const& sig)
27 std::size_t pos = input.rfind(sig);
28 if (pos != std::string::npos)
30 std::string copy = input.substr(pos + sig.length());
37 static inline void add_or_set(std::vector<parameter>& parameters, parameter const& p)
39 std::vector<parameter>::iterator it = std::find_if(parameters.begin(), parameters.end(), par_by_name(p.name));
40 if (it != parameters.end())
42 if (it->brief_description.empty()) it->brief_description = p.brief_description;
43 if (it->type.empty()) it->type = p.type;
44 if (it->fulltype.empty()) it->fulltype = p.fulltype;
45 if (it->default_value.empty()) it->default_value = p.default_value;
49 parameters.push_back(p);
56 /// Parses a "para" element
58 This is used for different purposes within Doxygen.
59 - Either a detailed description, possibly containing several sections (para's)
60 -> so parse next siblings
61 - Or a detailed description also containing qbk records
63 So we have to list explicitly either where to recurse, or where not to...
67 // Type used to store parsing state. It indicates if QBK formatting block was opened - [*...], [^...], etc.
75 static void parse_para(rapidxml::xml_node<>* node, configuration const& config, std::string& contents, bool& skip, bool first = true, text_block tb = not_in_block)
79 if (node->type() == rapidxml::node_element)
81 //std::cout << "ELEMENT: " << node->name() << "=" << node->value() << std::endl;
82 std::string name = node->name();
83 if ( boost::equals(name, "itemizedlist") )
86 parse_para(node->first_node(), config, contents, skip, true, tb);
88 parse_para(node->next_sibling(), config, contents, skip, true, tb);
91 else if ( boost::equals(name, "listitem") )
94 parse_para(node->first_node(), config, contents, skip, true, tb);
96 parse_para(node->next_sibling(), config, contents, skip, true, tb);
99 else if ( boost::equals(name, "verbatim") )
101 contents += "\n``\n";
102 parse_para(node->first_node(), config, contents, skip, false, tb);
104 parse_para(node->next_sibling(), config, contents, skip, false, tb);
107 else if ( boost::equals(name, "bold") )
110 parse_para(node->first_node(), config, contents, skip, false, in_block);
112 parse_para(node->next_sibling(), config, contents, skip, false, tb);
115 else if ( boost::equals(name, "emphasis") )
118 parse_para(node->first_node(), config, contents, skip, false, in_block);
120 parse_para(node->next_sibling(), config, contents, skip, false, tb);
123 else if ( boost::equals(name, "computeroutput") )
126 parse_para(node->first_node(), config, contents, skip, false, tb == in_block ? in_block : in_code_block);
128 parse_para(node->next_sibling(), config, contents, skip, false, tb);
131 else if ( boost::equals(name, "ref") )
133 // If alternative output is used - insert links
134 if ( configuration::alt == config.output_style )
136 std::string refid = node->first_attribute("refid")->value();
137 if ( !refid.empty() )
139 contents += std::string("[link ") + refid + " ";
140 parse_para(node->first_node(), config, contents, skip, false, in_block);
142 parse_para(node->next_sibling(), config, contents, skip, false, tb);
148 (boost::equals(name, "para") && first)
149 || boost::equals(name, "defval")
150 || boost::equals(name, "linebreak")
156 else if (node->type() == rapidxml::node_data)
158 std::string str = node->value();
159 if ( tb == in_block )
161 boost::replace_all(str, "\\", "\\\\");
162 boost::replace_all(str, "[", "\\[");
163 boost::replace_all(str, "]", "\\]");
165 else if ( tb == in_code_block )
167 if ( str.find('`') == std::string::npos )
168 str = std::string("`") + str + "`";
171 //std::cout << "DATA: " << node->name() << "=" << node->value() << std::endl;
175 //std::cout << "OTHER: " << node->name() << "=" << node->value() << std::endl;
178 parse_para(node->first_node(), config, contents, skip, false, tb);
179 parse_para(node->next_sibling(), config, contents, skip, false, tb);
184 static void parse_parameter(rapidxml::xml_node<>* node, configuration const& config, parameter& p)
186 // #define: <param><defname>Point</defname></param>
187 // template: <param><type>typename</type><declname>CoordinateType</declname><defname>CoordinateType</defname></param>
188 // template with default: <param><type>typename</type><declname>CoordinateSystem</declname><defname>CoordinateSystem</defname><defval><ref ....>cs::cartesian</ref></defval></param>
189 // with enum: <type><ref refid="group__enum_1ga7d33eca9a5389952bdf719972eb802b6" kindref="member">closure_selector</ref></type>
192 std::string name = node->name();
195 get_contents(node->first_node(), p.fulltype);
197 boost::replace_all(p.type, " const", "");
199 boost::replace_all(p.type, "&", "");
200 boost::replace_all(p.type, "*", "");
203 // If alt output is used retrieve type with QBK links
204 if ( configuration::alt == config.output_style )
206 p.fulltype_without_links = p.fulltype;
208 parse_para(node->first_node(), config, p.fulltype, p.skip);
211 else if (name == "declname") p.name = node->value();
212 else if (name == "parametername") p.name = node->value();
213 else if (name == "defname") p.name = node->value();
214 else if (name == "defval")
216 parse_para(node, config, p.default_value, p.skip);
218 else if (name == "para")
220 parse_para(node, config, p.brief_description, p.skip);
223 parse_parameter(node->first_node(), config, p);
224 parse_parameter(node->next_sibling(), config, p);
228 static void parse_enumeration_value(rapidxml::xml_node<>* node, configuration const& config, enumeration_value& value)
230 // <enumvalue><name>green</name><initializer> 2</initializer>
231 // <briefdescription><para>...</para></briefdescription>
232 // <detaileddescription><para>...</para></detaileddescription>
236 std::string node_name = node->name();
238 if (node_name == "name") value.name = node->value();
239 else if (node_name == "para")
241 // Parses both brief AND detailed into this description
242 parse_para(node, config, value.brief_description, value.skip);
244 else if (node_name == "initializer")
246 value.initializer = node->value();
249 parse_enumeration_value(node->first_node(), config, value);
250 parse_enumeration_value(node->next_sibling(), config, value);
254 // Definition is a function or a class/struct
255 template <typename Parameters>
256 static void parse_parameter_list(rapidxml::xml_node<>* node, configuration const& config, Parameters& parameters)
260 std::string name = node->name();
262 if (name == "parameteritem")
265 parse_parameter(node->first_node(), config, p);
266 if (! p.name.empty())
268 // Copy its description
269 std::vector<parameter>::iterator it = std::find_if(parameters.begin(),
270 parameters.end(), par_by_name(p.name));
271 if (it != parameters.end())
273 it->brief_description = p.brief_description;
277 parameters.push_back(p);
281 else if (name == "param")
283 // Element of 'templateparamlist.param (.type,.declname,.defname)'
285 parse_parameter(node->first_node(), config, p);
287 // Doxygen handles templateparamlist param's differently:
290 // <param><type>typename T</type></param>
291 // -> no name, assign type to name, replace typename
294 // <type>typename</type><declname>T</declname><defname>T</defname>
300 boost::replace_all(p.name, "typename", "");
306 p.fulltype = p.type + " " + p.name;
309 add_or_set(parameters, p);
312 parse_parameter_list(node->first_node(), config, parameters);
313 parse_parameter_list(node->next_sibling(), config, parameters);
317 static void copy_string_property(std::string const& source, std::string& target)
326 template <typename Parameters>
327 static void copy_parameter_properties(parameter const& source, Parameters& target)
329 BOOST_FOREACH(parameter& t, target)
331 if (source.name == t.name)
333 t.skip = source.skip;
334 copy_string_property(source.brief_description, t.brief_description);
335 copy_string_property(source.type, t.type);
336 copy_string_property(source.default_value, t.default_value);
337 copy_string_property(source.fulltype, t.fulltype);
342 // If not found, write a warning
343 std::cerr << "Parameter not found: " << source.name << std::endl;
347 template <typename Parameters>
348 static void copy_parameters_properties(Parameters const& source, Parameters& target)
350 BOOST_FOREACH(parameter const& s, source)
352 copy_parameter_properties(s, target);
358 template <typename Element>
359 static void parse_element(rapidxml::xml_node<>* node, configuration const& config, std::string const& parent, Element& el)
363 std::string name = node->name();
364 std::string full = parent + "." + name;
366 if (full == ".briefdescription.para")
368 parse_para(node, config, el.brief_description, el.skip);
370 else if (full == ".detaileddescription.para")
373 parse_para(node, config, para, el.skip);
374 if (!para.empty() && !el.detailed_description.empty())
376 el.detailed_description += "\n\n";
378 el.detailed_description += para;
380 else if (full == ".location")
382 std::string loc = get_attribute(node, "file");
383 // Location of (header)file. It is a FULL path, so find the start
384 // and strip the rest
385 std::size_t pos = loc.rfind(config.start_include);
386 if (pos != std::string::npos)
388 loc = loc.substr(pos);
391 el.line = atol(get_attribute(node, "line").c_str());
393 else if (full == ".detaileddescription.para.qbk")
395 el.qbk_markup.push_back(markup(node->value()));
397 else if (full == ".detaileddescription.para.qbk.after.synopsis")
399 el.qbk_markup.push_back(markup(markup_after, markup_synopsis, node->value()));
401 else if (full == ".detaileddescription.para.qbk.before.synopsis")
403 el.qbk_markup.push_back(markup(markup_before, markup_synopsis, node->value()));
405 else if (full == ".detaileddescription.para.qbk.distinguish")
407 el.additional_description = node->value();
408 boost::trim(el.additional_description);
410 else if (full == ".templateparamlist")
412 parse_parameter_list(node->first_node(), config, el.template_parameters);
414 else if (full == ".detaileddescription.para.parameterlist")
416 std::string kind = get_attribute(node, "kind");
419 // Parse parameters and their descriptions.
420 // NOTE: they are listed here, but the order might not be the order in the function call
421 std::vector<parameter> parameters;
422 parse_parameter_list(node->first_node(), config, parameters);
423 copy_parameters_properties(parameters, el.parameters);
425 else if (kind == "templateparam")
427 parse_parameter_list(node->first_node(), config, el.template_parameters);
430 else if (full == ".detaileddescription.para.simplesect")
432 std::string kind = get_attribute(node, "kind");
437 rapidxml::xml_node<> * title_node = node->first_node("title");
439 p.title = title_node->value();
441 parse_para(node->first_node("para"), config, p.text, el.skip);
443 el.paragraphs.push_back(p);
445 else if (kind == "warning")
447 parse_para(node->first_node("para"), config, el.warning, el.skip);
449 else if (kind == "note")
451 parse_para(node->first_node("para"), config, el.note, el.skip);
454 else if (full == ".param")
456 // Parse one parameter, and add it to el.parameters
458 parse_parameter(node->first_node(), config, p);
459 el.parameters.push_back(p);
463 parse_element(node->first_node(), config, full, el);
464 parse_element(node->next_sibling(), config, parent, el);
468 static void parse_function(rapidxml::xml_node<>* node, configuration const& config, std::string const& parent, function& f)
472 std::string name = node->name();
473 std::string full = parent + "." + name;
475 if (full == ".name") f.name = node->value();
476 else if (full == ".argsstring") f.argsstring = node->value();
477 else if (full == ".definition")
479 f.definition = node->value();
480 if (! config.skip_namespace.empty())
482 boost::replace_all(f.definition, config.skip_namespace, "");
485 else if (full == ".param")
488 parse_parameter(node->first_node(), config, p);
489 add_or_set(f.parameters, p);
491 else if (full == ".type")
493 get_contents(node->first_node(), f.return_type);
495 // If alt output is used, retrieve return type with links
496 if ( configuration::alt == config.output_style )
498 f.return_type_without_links = f.return_type;
500 f.return_type.clear();
501 parse_para(node->first_node(), config, f.return_type, dummy_skip);
504 else if (full == ".detaileddescription.para.simplesect")
506 std::string kind = get_attribute(node, "kind");
507 if (kind == "return")
509 parse_para(node->first_node(), config, f.return_description, f.skip);
511 /*else if (kind == "param")
513 get_contents(node->first_node(), f.paragraphs);
515 else if (kind == "pre")
517 parse_para(node->first_node(), config, f.precondition, f.skip);
520 else if (full == ".detaileddescription.para.image")
524 parse_function(node->first_node(), config, full, f);
525 parse_function(node->next_sibling(), config, parent, f);
529 static void parse_enumeration(rapidxml::xml_node<>* node, configuration const& config, std::string const& parent, enumeration& e)
533 std::string name = node->name();
534 std::string full = parent + "." + name;
536 if (full == ".name") e.name = node->value();
537 else if (full == ".enumvalue")
539 enumeration_value value;
540 parse_enumeration_value(node->first_node(), config, value);
541 e.enumeration_values.push_back(value);
544 parse_enumeration(node->first_node(), config, full, e);
545 parse_enumeration(node->next_sibling(), config, parent, e);
550 static std::string parse_named_node(rapidxml::xml_node<>* node, std::string const& look_for_name)
554 std::string node_name = node->name();
555 std::string contents;
557 if (boost::equals(node_name, look_for_name))
559 contents = node->value();
563 + parse_named_node(node->first_node(), look_for_name)
564 + parse_named_node(node->next_sibling(), look_for_name);
572 static void parse(rapidxml::xml_node<>* node, configuration const& config, documentation& doc, bool member = false)
576 bool recurse = false;
577 bool is_member = member;
579 std::string nodename = node->name();
581 if (nodename == "doxygen")
585 else if (nodename == "sectiondef")
587 std::string kind = get_attribute(node, "kind");
596 else if (boost::starts_with(kind, "public"))
602 else if (nodename == "compounddef")
604 std::string kind = get_attribute(node, "kind");
605 std::string id = get_attribute(node, "id");
610 rapidxml::xml_node<> * n = node->first_node("title");
612 doc.group_title = n->value();
614 else if (kind == "struct")
617 doc.cos.is_class = false;
619 parse_element(node->first_node(), config, "", doc.cos);
621 else if (kind == "class")
624 doc.cos.is_class = true;
626 parse_element(node->first_node(), config, "", doc.cos);
629 else if (nodename == "memberdef")
631 std::string kind = get_attribute(node, "kind");
632 std::string id = get_attribute(node, "id");
634 if (kind == "function")
638 f.is_static = get_attribute(node, "static") == "yes" ? true : false;
639 f.is_const = get_attribute(node, "const") == "yes" ? true : false;
640 f.is_explicit = get_attribute(node, "explicit") == "yes" ? true : false;
641 f.is_virtual = get_attribute(node, "virt") == "virtual" ? true : false;
643 parse_element(node->first_node(), config, "", f);
644 parse_function(node->first_node(), config, "", f);
648 bool c_or_d = boost::equals(f.name, doc.cos.name) ||
649 boost::equals(f.name, std::string("~") + doc.cos.name);
652 ? function_constructor_destructor
654 doc.cos.functions.push_back(f);
658 f.type = function_free;
659 doc.functions.push_back(f);
662 else if (kind == "define")
666 f.type = function_define;
667 parse_element(node->first_node(), config, "", f);
668 parse_function(node->first_node(), config, "", f);
669 doc.defines.push_back(f);
671 else if (kind == "enum")
675 parse_element(node->first_node(), config, "", e);
676 parse_enumeration(node->first_node(), config, "", e);
677 doc.enumerations.push_back(e);
679 else if (kind == "typedef")
681 if (boost::equals(get_attribute(node, "prot"), "public"))
683 std::string name = parse_named_node(node->first_node(), "name");
684 doc.cos.typedefs.push_back(base_element(name));
685 doc.cos.typedefs.back().id = id;
688 parse_element(node->first_node(), config, "", dummy);
689 doc.cos.typedefs.back().brief_description = dummy.brief_description;
692 else if (kind == "variable")
694 if (boost::equals(get_attribute(node, "prot"), "public"))
698 for(rapidxml::xml_node<>* var_node = node->first_node(); var_node; var_node=var_node->next_sibling())
700 if(boost::equals(var_node->name(), "name"))
702 p.name = var_node->value();
704 else if(boost::equals(var_node->name(), "type"))
706 get_contents(var_node->first_node(), p.fulltype);
708 //boost::replace_all(p.type, " const", "");
709 //boost::trim(p.type);
710 //boost::replace_all(p.type, "&", "");
711 //boost::replace_all(p.type, "*", "");
714 // If alt output is used retrieve type with QBK links
715 if ( configuration::alt == config.output_style )
717 p.fulltype_without_links = p.fulltype;
719 parse_para(var_node->first_node(), config, p.fulltype, p.skip);
722 else if(boost::equals(var_node->name(), "briefdescription"))
724 parse_para(var_node->first_node(), config, p.brief_description, p.skip);
726 else if(p.brief_description.empty() && boost::equals(var_node->name(), "detaileddescription"))
728 parse_para(var_node->first_node(), config, p.brief_description, p.skip);
731 doc.cos.variables.push_back(p);
736 else if (nodename == "compoundname")
738 std::string name = node->value();
739 if (name.find("::") != std::string::npos)
741 doc.cos.fullname = name;
743 // For a class, it should have "boost::something::" before
744 // set its name without namespace
745 doc.cos.name = keep_after(name, "::");
748 else if (nodename == "basecompoundref")
751 bc.name = node->value();
752 bc.derivation = get_attribute(node, "prot");
753 bc.virtuality = get_attribute(node, "virt");
754 doc.cos.base_classes.push_back(bc);
758 //std::cout << nodename << " ignored." << std::endl;
764 // First recurse into childnodes, then handle next siblings
765 parse(node->first_node(), config, doc, is_member);
767 parse(node->next_sibling(), config, doc, is_member);
771 #endif // DOXYGEN_XML_PARSER_HPP