1<?php 2 3/** 4 * DokuWiki Plugin doxycode (Parser Helper Component) 5 * 6 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 7 * @author Lukas Probsthain <lukas.probsthain@gmail.com> 8 */ 9 10use dokuwiki\Extension\Plugin; 11 12class helper_plugin_doxycode_parser extends Plugin 13{ 14 /** 15 * @var Array $mapping Associative array that maps certain highlight classes in the 16 * XML file to their corresponding DokuWiki CSS classes. 17 * 18 * This mapping is used in the `renderXMLToDokuWikiCode()` method to convert the XML code to DokuWiki syntax. 19 */ 20 private $mapping = array( 21 'comment' => 'co1', 22 'keywordtype' => 'kw0', 23 'keywordflow' => 'kw1', 24 'preprocessor' => 'co2', 25 'stringliteral' => 'st0' 26 ); 27 28 /** 29 * The function `renderXMLToDokuWikiCode` takes an XML string, line number setting, and tag 30 * configuration as input and returns DokuWiki code. 31 * 32 * @param string A string containing XML code. 33 * @param boolean A boolean value indicating whether line numbers should be included in the output 34 * or not. 35 * @param array The `tag_conf` parameter is an optional parameter that allows you to specify a 36 * configuration for parsing specific XML tags. It is used in the `_parseDoxygenXMLElement` function, 37 * which is called for each codeline element in the XML. 38 * 39 * @return string output string, which contains the DokuWiki code generated from the XML input. 40 */ 41 public function renderXMLToDokuWikiCode($xmlString, $line_numbers, $tag_conf = null) 42 { 43 $output = ''; 44 45 46 $dom = new DOMDocument(); 47 $dom->loadXML($xmlString); 48 49 // find the programlisting element inside the doxygen XML 50 $xpath = new DOMXPath($dom); 51 $programListing = $xpath->query('//programlisting')->item(0); 52 53 // if linenumber setting is present output list elements around the codelines! 54 if ($line_numbers) { 55 $output .= '<ol>'; 56 57 // $this->doc = str_replace("\n", "", $this->doc); 58 } 59 60 // loop over the codeline elements 61 foreach ($programListing->childNodes as $codeline) { 62 if ($codeline->hasChildNodes()) { 63 if ($line_numbers) { 64 $output .= '<li class=\"li1\"><div>'; 65 } 66 67 $this->parseDoxygenXMLElement($codeline, $output, $tag_conf); 68 69 if ($line_numbers) { 70 $output .= '</div></li>'; 71 } else { 72 $output .= DOKU_LF; 73 } 74 } 75 } 76 77 if ($line_numbers) { 78 $output .= '</ol>'; 79 } 80 81 return $output; 82 } 83 84 /** 85 * Parse the children of codeline elements of a doxygen XML output and their children. 86 * 87 * Individual lines from a source file are converted to <codeline>...</codeline> elements by doxygen. 88 * Here we parse the children of codeline elements and convert them to HTML elements that correspond 89 * to the elements of a default dokuwiki code snippet. 90 * 91 * Some of the elements also contain children (e.g. <highlight ...><ref ...>...</ref>...</highlight>). 92 * In those cases we recursivly call this function until no children are found. 93 * 94 * @param DOMElement $element Child element from the doxygen XML we want to parse 95 * @param String &$output Reference to the output string we append the generated HTML to 96 * @param Array $tag_conf Tag configuration used for generating the reference URLS 97 */ 98 private function parseDoxygenXMLElement($element, &$output, $tag_conf = null) 99 { 100 global $conf; 101 102 // helper: 103 // highlight = <span> element 104 // sp = ' ' (space) 105 // ref = <a href="..."> 106 107 foreach ($element->childNodes as $node) { 108 if ($node->nodeType === XML_ELEMENT_NODE) { 109 switch ($node->nodeName) { 110 /** 111 * The `case 'highlight'` matches the syntax highlighting elements inside 112 * the XML and matches these to dokuwiki CSS classes for code blocks. 113 */ 114 case 'highlight': 115 $output .= '<span'; 116 if ($node->hasAttribute('class')) { 117 $output .= ' class="'; 118 if ($this->mapping[$node->getAttribute('class')] !== '') { 119 $output .= $this->mapping[$node->getAttribute('class')]; 120 } else { 121 // if we cannot map a class from dokuwiki - just use the doxygen class for now 122 $output .= $node->getAttribute('class'); 123 } 124 $output .= '"'; 125 } 126 $output .= '>'; 127 // check if $element has children or content 128 if ($node->hasChildNodes()) { 129 // parse the elements inside the span element 130 $this->parseDoxygenXMLElement($node, $output, $tag_conf); 131 } 132 $output .= '</span>'; 133 break; 134 case 'sp': 135 // sp is just converted to space 136 $output .= ' '; 137 break; 138 case 'ref': 139 $output .= "<a"; 140 if ($node->hasAttribute('external') && $node->hasAttribute('refid')) { 141 $output .= ' href="'; 142 $output .= $this->convertRefToURL($node, $tag_conf); 143 $output .= '" target="' . $conf['target']['extern']; 144 $output .= '"'; 145 } 146 $output .= ">"; 147 $output .= $node->nodeValue; 148 $output .= "</a>"; 149 break; 150 default: 151 break; 152 } 153 } 154 155 // plain text inside an element is just appended to the document output 156 if ($node->nodeType === XML_TEXT_NODE) { 157 $output .= $node->nodeValue; 158 } 159 } 160 } 161 162 /** 163 * Convert the external reference from a doxygen XML to the documentation URL. 164 * 165 * The <ref...> element in the doxygen XML output includes the following elements: 166 * - refid: page identifier + anchor to the element in the documentation 167 * - external: name of the tag file of the documentation this reference points to 168 * 169 * The external attribute should match one of the tag file names we used when building the 170 * documentation. We can use this attribute to find the tag file configuration, which in turn 171 * includes the documentation base URL. 172 * 173 * We then convert the refid to a doxygen documentation html file name and append the anchor if 174 * ther is one. 175 * 176 * @param DOMElement &$node reference to the XML reference element 177 * @param Array $tag_conf Tag file configuration 178 * @return String URL to the doxygen documentation for this reference 179 */ 180 private function convertRefToURL(&$node, $tag_conf = null) 181 { 182 $output = ''; 183 184 $external = $node->getAttribute('external'); 185 $ref = $node->getAttribute('refid'); 186 187 /** @var helper_plugin_doxycode_tagmanager $tagmanager */ 188 $tagmanager = plugin_load('helper', 'doxycode_tagmanager'); 189 190 // match the external attribute to the tag file and extract the documentation URL 191 foreach ($tag_conf as $key => $conf) { 192 if (realpath($tagmanager->getTagFileDir() . $key . '.xml') === $external) { 193 $output .= $conf['docu_url']; 194 break; 195 } 196 } 197 198 $kindref = ''; 199 200 if ($node->hasAttribute('kindref')) { 201 $kindref = $node->getAttribute('kindref'); 202 } 203 204 if ($kindref === 'member') { 205 $lastUnderscorePos = strrpos($ref, '_'); 206 207 $first = substr($ref, 0, $lastUnderscorePos); 208 // we omit the underscore and the first character to get the anchor 209 $last = substr($ref, $lastUnderscorePos + 2); 210 211 $output .= $first; 212 $output .= ".html#"; 213 $output .= $last; 214 } else { 215 // probably 'compound' 216 $output .= $ref; 217 218 // some refs are directly the wanted page (includes, ...) 219 if (substr($output, -5) !== '.html') { 220 $output .= ".html"; 221 } 222 } 223 224 return $output; 225 } 226} 227