*/ use dokuwiki\Extension\Plugin; class helper_plugin_doxycode_parser extends Plugin { /** * @var Array $mapping Associative array that maps certain highlight classes in the * XML file to their corresponding DokuWiki CSS classes. * * This mapping is used in the `renderXMLToDokuWikiCode()` method to convert the XML code to DokuWiki syntax. */ private $mapping = array( 'comment' => 'co1', 'keywordtype' => 'kw0', 'keywordflow' => 'kw1', 'preprocessor' => 'co2', 'stringliteral' => 'st0' ); /** * The function `renderXMLToDokuWikiCode` takes an XML string, line number setting, and tag * configuration as input and returns DokuWiki code. * * @param string A string containing XML code. * @param boolean A boolean value indicating whether line numbers should be included in the output * or not. * @param array The `tag_conf` parameter is an optional parameter that allows you to specify a * configuration for parsing specific XML tags. It is used in the `_parseDoxygenXMLElement` function, * which is called for each codeline element in the XML. * * @return string output string, which contains the DokuWiki code generated from the XML input. */ public function renderXMLToDokuWikiCode($xmlString, $line_numbers, $tag_conf = null) { $output = ''; $dom = new DOMDocument(); $dom->loadXML($xmlString); // find the programlisting element inside the doxygen XML $xpath = new DOMXPath($dom); $programListing = $xpath->query('//programlisting')->item(0); // if linenumber setting is present output list elements around the codelines! if ($line_numbers) { $output .= '
    '; // $this->doc = str_replace("\n", "", $this->doc); } // loop over the codeline elements foreach ($programListing->childNodes as $codeline) { if ($codeline->hasChildNodes()) { if ($line_numbers) { $output .= '
  1. '; } $this->parseDoxygenXMLElement($codeline, $output, $tag_conf); if ($line_numbers) { $output .= '
  2. '; } else { $output .= DOKU_LF; } } } if ($line_numbers) { $output .= '
'; } return $output; } /** * Parse the children of codeline elements of a doxygen XML output and their children. * * Individual lines from a source file are converted to ... elements by doxygen. * Here we parse the children of codeline elements and convert them to HTML elements that correspond * to the elements of a default dokuwiki code snippet. * * Some of the elements also contain children (e.g. ......). * In those cases we recursivly call this function until no children are found. * * @param DOMElement $element Child element from the doxygen XML we want to parse * @param String &$output Reference to the output string we append the generated HTML to * @param Array $tag_conf Tag configuration used for generating the reference URLS */ private function parseDoxygenXMLElement($element, &$output, $tag_conf = null) { global $conf; // helper: // highlight = element // sp = ' ' (space) // ref = foreach ($element->childNodes as $node) { if ($node->nodeType === XML_ELEMENT_NODE) { switch ($node->nodeName) { /** * The `case 'highlight'` matches the syntax highlighting elements inside * the XML and matches these to dokuwiki CSS classes for code blocks. */ case 'highlight': $output .= 'hasAttribute('class')) { $output .= ' class="'; if ($this->mapping[$node->getAttribute('class')] !== '') { $output .= $this->mapping[$node->getAttribute('class')]; } else { // if we cannot map a class from dokuwiki - just use the doxygen class for now $output .= $node->getAttribute('class'); } $output .= '"'; } $output .= '>'; // check if $element has children or content if ($node->hasChildNodes()) { // parse the elements inside the span element $this->parseDoxygenXMLElement($node, $output, $tag_conf); } $output .= ''; break; case 'sp': // sp is just converted to space $output .= ' '; break; case 'ref': $output .= "hasAttribute('external') && $node->hasAttribute('refid')) { $output .= ' href="'; $output .= $this->convertRefToURL($node, $tag_conf); $output .= '" target="' . $conf['target']['extern']; $output .= '"'; } $output .= ">"; $output .= $node->nodeValue; $output .= ""; break; default: break; } } // plain text inside an element is just appended to the document output if ($node->nodeType === XML_TEXT_NODE) { $output .= $node->nodeValue; } } } /** * Convert the external reference from a doxygen XML to the documentation URL. * * The element in the doxygen XML output includes the following elements: * - refid: page identifier + anchor to the element in the documentation * - external: name of the tag file of the documentation this reference points to * * The external attribute should match one of the tag file names we used when building the * documentation. We can use this attribute to find the tag file configuration, which in turn * includes the documentation base URL. * * We then convert the refid to a doxygen documentation html file name and append the anchor if * ther is one. * * @param DOMElement &$node reference to the XML reference element * @param Array $tag_conf Tag file configuration * @return String URL to the doxygen documentation for this reference */ private function convertRefToURL(&$node, $tag_conf = null) { $output = ''; $external = $node->getAttribute('external'); $ref = $node->getAttribute('refid'); /** @var helper_plugin_doxycode_tagmanager $tagmanager */ $tagmanager = plugin_load('helper', 'doxycode_tagmanager'); // match the external attribute to the tag file and extract the documentation URL foreach ($tag_conf as $key => $conf) { if (realpath($tagmanager->getTagFileDir() . $key . '.xml') === $external) { $output .= $conf['docu_url']; break; } } $kindref = ''; if ($node->hasAttribute('kindref')) { $kindref = $node->getAttribute('kindref'); } if ($kindref === 'member') { $lastUnderscorePos = strrpos($ref, '_'); $first = substr($ref, 0, $lastUnderscorePos); // we omit the underscore and the first character to get the anchor $last = substr($ref, $lastUnderscorePos + 2); $output .= $first; $output .= ".html#"; $output .= $last; } else { // probably 'compound' $output .= $ref; // some refs are directly the wanted page (includes, ...) if (substr($output, -5) !== '.html') { $output .= ".html"; } } return $output; } }