1#!/usr/local/bin/python3.8 2# Copyright 2006 Rene Rivera 3# Distributed under the Boost Software License, Version 1.0. 4# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 5 6''' 7Processing of Doxygen generated XML. 8''' 9 10import os 11import os.path 12import sys 13import time 14import string 15import getopt 16import glob 17import re 18import xml.dom.minidom 19 20 21def usage(): 22 print ''' 23Usage: 24 %s options 25 26Options: 27 --xmldir Directory with the Doxygen xml result files. 28 --output Write the output BoostBook to the given location. 29 --id The ID of the top level BoostBook section. 30 --title The title of the top level BoostBook section. 31 --enable-index Generate additional index sections for classes and 32 types. 33''' % ( sys.argv[0] ) 34 35 36def get_args( argv = sys.argv[1:] ): 37 spec = [ 38 'xmldir=', 39 'output=', 40 'id=', 41 'title=', 42 'enable-index', 43 'help' ] 44 options = { 45 '--xmldir' : 'xml', 46 '--output' : None, 47 '--id' : 'dox', 48 '--title' : 'Doxygen' 49 } 50 ( option_pairs, other ) = getopt.getopt( argv, '', spec ) 51 map( lambda x: options.__setitem__( x[0], x[1] ), option_pairs ) 52 53 if '--help' in options: 54 usage() 55 sys.exit(1) 56 57 return { 58 'xmldir' : options['--xmldir'], 59 'output' : options['--output'], 60 'id' : options['--id'], 61 'title' : options['--title'], 62 'index' : '--enable-index' in options 63 } 64 65def if_attribute(node, attribute, true_value, false_value=None): 66 if node.getAttribute(attribute) == 'yes': 67 return true_value 68 else: 69 return false_value 70 71class Doxygen2BoostBook: 72 73 def __init__( self, **kwargs ): 74 ## 75 self.args = kwargs 76 self.args.setdefault('id','') 77 self.args.setdefault('title','') 78 self.args.setdefault('last_revision', time.asctime()) 79 self.args.setdefault('index', False) 80 self.id = '%(id)s.reference' % self.args 81 self.args['id'] = self.id 82 #~ This is our template BoostBook document we insert the generated content into. 83 self.boostbook = xml.dom.minidom.parseString('''<?xml version="1.0" encoding="UTF-8"?> 84<section id="%(id)s" name="%(title)s" last-revision="%(last_revision)s"> 85 <title>%(title)s</title> 86 <library-reference id="%(id)s.headers"> 87 <title>Headers</title> 88 </library-reference> 89 <index id="%(id)s.classes"> 90 <title>Classes</title> 91 </index> 92 <index id="%(id)s.index"> 93 <title>Index</title> 94 </index> 95</section> 96''' % self.args ) 97 self.section = { 98 'headers' : self._getChild('library-reference',id='%(id)s.headers' % self.args), 99 'classes' : self._getChild('index',id='%(id)s.classes' % self.args), 100 'index' : self._getChild('index',id='%(id)s.index' % self.args) 101 } 102 #~ Remove the index sections if we aren't generating it. 103 if not self.args['index']: 104 self.section['classes'].parentNode.removeChild(self.section['classes']) 105 self.section['classes'].unlink() 106 del self.section['classes'] 107 self.section['index'].parentNode.removeChild(self.section['index']) 108 self.section['index'].unlink() 109 del self.section['index'] 110 #~ The symbols, per Doxygen notion, that we translated. 111 self.symbols = {} 112 #~ Map of Doxygen IDs and BoostBook IDs, so we can translate as needed. 113 self.idmap = {} 114 #~ Marks generation, to prevent redoing it. 115 self.generated = False 116 117 #~ Add an Doxygen generated XML document to the content we are translating. 118 def addDox( self, document ): 119 self._translateNode(document.documentElement) 120 121 #~ Turns the internal XML tree into an output UTF-8 string. 122 def tostring( self ): 123 self._generate() 124 #~ return self.boostbook.toprettyxml(' ') 125 return self.boostbook.toxml('utf-8') 126 127 #~ Does post-processing on the partial generated content to generate additional info 128 #~ now that we have the complete source documents. 129 def _generate( self ): 130 if not self.generated: 131 self.generated = True 132 symbols = self.symbols.keys() 133 symbols.sort() 134 #~ Populate the header section. 135 for symbol in symbols: 136 if self.symbols[symbol]['kind'] in ('header'): 137 self.section['headers'].appendChild(self.symbols[symbol]['dom']) 138 for symbol in symbols: 139 if self.symbols[symbol]['kind'] not in ('namespace', 'header'): 140 container = self._resolveContainer(self.symbols[symbol], 141 self.symbols[self.symbols[symbol]['header']]['dom']) 142 if container.nodeName != 'namespace': 143 ## The current BoostBook to Docbook translation doesn't 144 ## respect, nor assign, IDs to inner types of any kind. 145 ## So nuke the ID entry so as not create bogus links. 146 del self.idmap[self.symbols[symbol]['id']] 147 container.appendChild(self.symbols[symbol]['dom']) 148 self._rewriteIDs(self.boostbook.documentElement) 149 150 #~ Rewrite the various IDs from Doxygen references to the newly created 151 #~ BoostBook references. 152 def _rewriteIDs( self, node ): 153 if node.nodeName in ('link'): 154 if node.getAttribute('linkend') in self.idmap: 155 #~ A link, and we have someplace to repoint it at. 156 node.setAttribute('linkend',self.idmap[node.getAttribute('linkend')]) 157 else: 158 #~ A link, but we don't have a generated target for it. 159 node.removeAttribute('linkend') 160 elif hasattr(node,'hasAttribute') and node.hasAttribute('id') and node.getAttribute('id') in self.idmap: 161 #~ Simple ID, and we have a translation. 162 node.setAttribute('id',self.idmap[node.getAttribute('id')]) 163 #~ Recurse, and iterate, depth-first traversal which turns out to be 164 #~ left-to-right and top-to-bottom for the document. 165 if node.firstChild: 166 self._rewriteIDs(node.firstChild) 167 if node.nextSibling: 168 self._rewriteIDs(node.nextSibling) 169 170 def _resolveContainer( self, cpp, root ): 171 container = root 172 for ns in cpp['namespace']: 173 node = self._getChild('namespace',name=ns,root=container) 174 if not node: 175 node = container.appendChild( 176 self._createNode('namespace',name=ns)) 177 container = node 178 for inner in cpp['name'].split('::'): 179 node = self._getChild(name=inner,root=container) 180 if not node: 181 break 182 container = node 183 return container 184 185 def _setID( self, id, name ): 186 self.idmap[id] = name.replace('::','.').replace('/','.') 187 #~ print '--| setID:',id,'::',self.idmap[id] 188 189 #~ Translate a given node within a given context. 190 #~ The translation dispatches to a local method of the form 191 #~ "_translate[_context0,...,_contextN]", and the keyword args are 192 #~ passed along. If there is no translation handling method we 193 #~ return None. 194 def _translateNode( self, *context, **kwargs ): 195 node = None 196 names = [ ] 197 for c in context: 198 if c: 199 if not isinstance(c,xml.dom.Node): 200 suffix = '_'+c.replace('-','_') 201 else: 202 suffix = '_'+c.nodeName.replace('-','_') 203 node = c 204 names.append('_translate') 205 names = map(lambda x: x+suffix,names) 206 if node: 207 for name in names: 208 if hasattr(self,name): 209 return getattr(self,name)(node,**kwargs) 210 return None 211 212 #~ Translates the children of the given parent node, appending the results 213 #~ to the indicated target. For nodes not translated by the translation method 214 #~ it copies the child over and recurses on that child to translate any 215 #~ possible interior nodes. Hence this will translate the entire subtree. 216 def _translateChildren( self, parent, **kwargs ): 217 target = kwargs['target'] 218 for n in parent.childNodes: 219 child = self._translateNode(n,target=target) 220 if child: 221 target.appendChild(child) 222 else: 223 child = n.cloneNode(False) 224 if hasattr(child,'data'): 225 child.data = re.sub(r'\s+',' ',child.data) 226 target.appendChild(child) 227 self._translateChildren(n,target=child) 228 229 #~ Translate the given node as a description, into the description subnode 230 #~ of the target. If no description subnode is present in the target it 231 #~ is created. 232 def _translateDescription( self, node, target=None, tag='description', **kwargs ): 233 description = self._getChild(tag,root=target) 234 if not description: 235 description = target.appendChild(self._createNode(tag)) 236 self._translateChildren(node,target=description) 237 return description 238 239 #~ Top level translation of: <doxygen ...>...</doxygen>, 240 #~ translates the children. 241 def _translate_doxygen( self, node ): 242 #~ print '_translate_doxygen:', node.nodeName 243 result = [] 244 for n in node.childNodes: 245 newNode = self._translateNode(n) 246 if newNode: 247 result.append(newNode) 248 return result 249 250 #~ Top level translation of: 251 #~ <doxygenindex ...> 252 #~ <compound ...> 253 #~ <member ...> 254 #~ <name>...</name> 255 #~ </member> 256 #~ ... 257 #~ </compound> 258 #~ ... 259 #~ </doxygenindex> 260 #~ builds the class and symbol sections, if requested. 261 def _translate_doxygenindex( self, node ): 262 #~ print '_translate_doxygenindex:', node.nodeName 263 if self.args['index']: 264 entries = [] 265 classes = [] 266 #~ Accumulate all the index entries we care about. 267 for n in node.childNodes: 268 if n.nodeName == 'compound': 269 if n.getAttribute('kind') not in ('file','dir','define'): 270 cpp = self._cppName(self._getChildData('name',root=n)) 271 entry = { 272 'name' : cpp['name'], 273 'compoundname' : cpp['compoundname'], 274 'id' : n.getAttribute('refid') 275 } 276 if n.getAttribute('kind') in ('class','struct'): 277 classes.append(entry) 278 entries.append(entry) 279 for m in n.childNodes: 280 if m.nodeName == 'member': 281 cpp = self._cppName(self._getChildData('name',root=m)) 282 entry = { 283 'name' : cpp['name'], 284 'compoundname' : cpp['compoundname'], 285 'id' : n.getAttribute('refid') 286 } 287 if hasattr(m,'getAttribute') and m.getAttribute('kind') in ('class','struct'): 288 classes.append(entry) 289 entries.append(entry) 290 #~ Put them in a sensible order. 291 entries.sort(lambda x,y: cmp(x['name'].lower(),y['name'].lower())) 292 classes.sort(lambda x,y: cmp(x['name'].lower(),y['name'].lower())) 293 #~ And generate the BoostBook for them. 294 self._translate_index_(entries,target=self.section['index']) 295 self._translate_index_(classes,target=self.section['classes']) 296 return None 297 298 #~ Translate a set of index entries in the BoostBook output. The output 299 #~ is grouped into groups of the first letter of the entry names. 300 def _translate_index_(self, entries, target=None, **kwargs ): 301 i = 0 302 targetID = target.getAttribute('id') 303 while i < len(entries): 304 dividerKey = entries[i]['name'][0].upper() 305 divider = target.appendChild(self._createNode('indexdiv',id=targetID+'.'+dividerKey)) 306 divider.appendChild(self._createText('title',dividerKey)) 307 while i < len(entries) and dividerKey == entries[i]['name'][0].upper(): 308 iename = entries[i]['name'] 309 ie = divider.appendChild(self._createNode('indexentry')) 310 ie = ie.appendChild(self._createText('primaryie',iename)) 311 while i < len(entries) and entries[i]['name'] == iename: 312 ie.appendChild(self.boostbook.createTextNode(' (')) 313 ie.appendChild(self._createText( 314 'link',entries[i]['compoundname'],linkend=entries[i]['id'])) 315 ie.appendChild(self.boostbook.createTextNode(')')) 316 i += 1 317 318 #~ Translate a <compounddef ...>...</compounddef>, 319 #~ by retranslating with the "kind" of compounddef. 320 def _translate_compounddef( self, node, target=None, **kwargs ): 321 return self._translateNode(node,node.getAttribute('kind')) 322 323 #~ Translate a <compounddef kind="namespace"...>...</compounddef>. For 324 #~ namespaces we just collect the information for later use as there is no 325 #~ currently namespaces are not included in the BoostBook format. In the future 326 #~ it might be good to generate a namespace index. 327 def _translate_compounddef_namespace( self, node, target=None, **kwargs ): 328 namespace = { 329 'id' : node.getAttribute('id'), 330 'kind' : 'namespace', 331 'name' : self._getChildData('compoundname',root=node), 332 'brief' : self._getChildData('briefdescription',root=node), 333 'detailed' : self._getChildData('detaileddescription',root=node), 334 'parsed' : False 335 } 336 if namespace['name'] in self.symbols: 337 if not self.symbols[namespace['name']]['parsed']: 338 self.symbols[namespace['name']]['parsed'] = True 339 #~ for n in node.childNodes: 340 #~ if hasattr(n,'getAttribute'): 341 #~ self._translateNode(n,n.getAttribute('kind'),target=target,**kwargs) 342 else: 343 self.symbols[namespace['name']] = namespace 344 #~ self._setID(namespace['id'],namespace['name']) 345 return None 346 347 #~ Translate a <compounddef kind="class"...>...</compounddef>, which 348 #~ forwards to the kind=struct as they are the same. 349 def _translate_compounddef_class( self, node, target=None, **kwargs ): 350 return self._translate_compounddef_struct(node,tag='class',target=target,**kwargs) 351 352 #~ Translate a <compounddef kind="struct"...>...</compounddef> into: 353 #~ <header id="?" name="?"> 354 #~ <struct name="?"> 355 #~ ... 356 #~ </struct> 357 #~ </header> 358 def _translate_compounddef_struct( self, node, tag='struct', target=None, **kwargs ): 359 result = None 360 includes = self._getChild('includes',root=node) 361 if includes: 362 ## Add the header into the output table. 363 self._translate_compounddef_includes_(includes,includes,**kwargs) 364 ## Compounds are the declared symbols, classes, types, etc. 365 ## We add them to the symbol table, along with the partial DOM for them 366 ## so that they can be organized into the output later. 367 compoundname = self._getChildData('compoundname',root=node) 368 compoundname = self._cppName(compoundname) 369 self._setID(node.getAttribute('id'),compoundname['compoundname']) 370 struct = self._createNode(tag,name=compoundname['name'].split('::')[-1]) 371 self.symbols[compoundname['compoundname']] = { 372 'header' : includes.firstChild.data, 373 'namespace' : compoundname['namespace'], 374 'id' : node.getAttribute('id'), 375 'kind' : tag, 376 'name' : compoundname['name'], 377 'dom' : struct 378 } 379 ## Add the children which will be the members of the struct. 380 for n in node.childNodes: 381 self._translateNode(n,target=struct,scope=compoundname['compoundname']) 382 result = struct 383 return result 384 385 #~ Translate a <compounddef ...><includes ...>...</includes></compounddef>, 386 def _translate_compounddef_includes_( self, node, target=None, **kwargs ): 387 name = node.firstChild.data 388 if name not in self.symbols: 389 self._setID(node.getAttribute('refid'),name) 390 self.symbols[name] = { 391 'kind' : 'header', 392 'id' : node.getAttribute('refid'), 393 'dom' : self._createNode('header', 394 id=node.getAttribute('refid'), 395 name=name) 396 } 397 return None 398 399 #~ Translate a <basecompoundref...>...</basecompoundref> into: 400 #~ <inherit access="?"> 401 #~ ... 402 #~ </inherit> 403 def _translate_basecompoundref( self, ref, target=None, **kwargs ): 404 inherit = target.appendChild(self._createNode('inherit', 405 access=ref.getAttribute('prot'))) 406 self._translateChildren(ref,target=inherit) 407 return 408 409 #~ Translate: 410 #~ <templateparamlist> 411 #~ <param> 412 #~ <type>...</type> 413 #~ <declname>...</declname> 414 #~ <defname>...</defname> 415 #~ <defval>...</defval> 416 #~ </param> 417 #~ ... 418 #~ </templateparamlist> 419 #~ Into: 420 #~ <template> 421 #~ <template-type-parameter name="?" /> 422 #~ <template-nontype-parameter name="?"> 423 #~ <type>?</type> 424 #~ <default>?</default> 425 #~ </template-nontype-parameter> 426 #~ </template> 427 def _translate_templateparamlist( self, templateparamlist, target=None, **kwargs ): 428 template = target.appendChild(self._createNode('template')) 429 for param in templateparamlist.childNodes: 430 if param.nodeName == 'param': 431 type = self._getChildData('type',root=param) 432 defval = self._getChild('defval',root=param) 433 paramKind = None 434 if type in ('class','typename'): 435 paramKind = 'template-type-parameter' 436 else: 437 paramKind = 'template-nontype-parameter' 438 templateParam = template.appendChild( 439 self._createNode(paramKind, 440 name=self._getChildData('declname',root=param))) 441 if paramKind == 'template-nontype-parameter': 442 template_type = templateParam.appendChild(self._createNode('type')) 443 self._translate_type( 444 self._getChild('type',root=param),target=template_type) 445 if defval: 446 value = self._getChildData('ref',root=defval.firstChild) 447 if not value: 448 value = self._getData(defval) 449 templateParam.appendChild(self._createText('default',value)) 450 return template 451 452 #~ Translate: 453 #~ <briefdescription>...</briefdescription> 454 #~ Into: 455 #~ <purpose>...</purpose> 456 def _translate_briefdescription( self, brief, target=None, **kwargs ): 457 self._translateDescription(brief,target=target,**kwargs) 458 return self._translateDescription(brief,target=target,tag='purpose',**kwargs) 459 460 #~ Translate: 461 #~ <detaileddescription>...</detaileddescription> 462 #~ Into: 463 #~ <description>...</description> 464 def _translate_detaileddescription( self, detailed, target=None, **kwargs ): 465 return self._translateDescription(detailed,target=target,**kwargs) 466 467 #~ Translate: 468 #~ <sectiondef kind="?">...</sectiondef> 469 #~ With kind specific translation. 470 def _translate_sectiondef( self, sectiondef, target=None, **kwargs ): 471 self._translateNode(sectiondef,sectiondef.getAttribute('kind'),target=target,**kwargs) 472 473 #~ Translate non-function sections. 474 def _translate_sectiondef_x_( self, sectiondef, target=None, **kwargs ): 475 for n in sectiondef.childNodes: 476 if hasattr(n,'getAttribute'): 477 self._translateNode(n,n.getAttribute('kind'),target=target,**kwargs) 478 return None 479 480 #~ Translate: 481 #~ <sectiondef kind="public-type">...</sectiondef> 482 def _translate_sectiondef_public_type( self, sectiondef, target=None, **kwargs ): 483 return self._translate_sectiondef_x_(sectiondef,target=target,**kwargs) 484 485 #~ Translate: 486 #~ <sectiondef kind="public-sttrib">...</sectiondef> 487 def _translate_sectiondef_public_attrib( self, sectiondef, target=None, **kwargs): 488 return self._translate_sectiondef_x_(sectiondef,target=target,**kwargs) 489 490 #~ Translate: 491 #~ <sectiondef kind="?-func">...</sectiondef> 492 #~ All the various function group translations end up here for which 493 #~ they are translated into: 494 #~ <method-group name="?"> 495 #~ ... 496 #~ </method-group> 497 def _translate_sectiondef_func_( self, sectiondef, name='functions', target=None, **kwargs ): 498 members = target.appendChild(self._createNode('method-group',name=name)) 499 for n in sectiondef.childNodes: 500 if hasattr(n,'getAttribute'): 501 self._translateNode(n,n.getAttribute('kind'),target=members,**kwargs) 502 return members 503 504 #~ Translate: 505 #~ <sectiondef kind="public-func">...</sectiondef> 506 def _translate_sectiondef_public_func( self, sectiondef, target=None, **kwargs ): 507 return self._translate_sectiondef_func_(sectiondef, 508 name='public member functions',target=target,**kwargs) 509 510 #~ Translate: 511 #~ <sectiondef kind="public-static-func">...</sectiondef> 512 def _translate_sectiondef_public_static_func( self, sectiondef, target=None, **kwargs): 513 return self._translate_sectiondef_func_(sectiondef, 514 name='public static functions',target=target,**kwargs) 515 516 #~ Translate: 517 #~ <sectiondef kind="protected-func">...</sectiondef> 518 def _translate_sectiondef_protected_func( self, sectiondef, target=None, **kwargs ): 519 return self._translate_sectiondef_func_(sectiondef, 520 name='protected member functions',target=target,**kwargs) 521 522 #~ Translate: 523 #~ <sectiondef kind="private-static-func">...</sectiondef> 524 def _translate_sectiondef_private_static_func( self, sectiondef, target=None, **kwargs): 525 return self._translate_sectiondef_func_(sectiondef, 526 name='private static functions',target=target,**kwargs) 527 528 #~ Translate: 529 #~ <sectiondef kind="public-func">...</sectiondef> 530 def _translate_sectiondef_private_func( self, sectiondef, target=None, **kwargs ): 531 return self._translate_sectiondef_func_(sectiondef, 532 name='private member functions',target=target,**kwargs) 533 534 #~ Translate: 535 #~ <sectiondef kind="user-defined"><header>...</header>...</sectiondef> 536 def _translate_sectiondef_user_defined( self, sectiondef, target=None, **kwargs ): 537 return self._translate_sectiondef_func_(sectiondef, 538 name=self._getChildData('header', root=sectiondef),target=target,**kwargs) 539 540 #~ Translate: 541 #~ <memberdef kind="typedef" id="?"> 542 #~ <name>...</name> 543 #~ </memberdef> 544 #~ To: 545 #~ <typedef id="?" name="?"> 546 #~ <type>...</type> 547 #~ </typedef> 548 def _translate_memberdef_typedef( self, memberdef, target=None, scope=None, **kwargs ): 549 self._setID(memberdef.getAttribute('id'), 550 scope+'::'+self._getChildData('name',root=memberdef)) 551 typedef = target.appendChild(self._createNode('typedef', 552 id=memberdef.getAttribute('id'), 553 name=self._getChildData('name',root=memberdef))) 554 typedef_type = typedef.appendChild(self._createNode('type')) 555 self._translate_type(self._getChild('type',root=memberdef),target=typedef_type) 556 return typedef 557 558 #~ Translate: 559 #~ <memberdef kind="function" id="?" const="?" static="?" explicit="?" inline="?"> 560 #~ <name>...</name> 561 #~ </memberdef> 562 #~ To: 563 #~ <method name="?" cv="?" specifiers="?"> 564 #~ ... 565 #~ </method> 566 def _translate_memberdef_function( self, memberdef, target=None, scope=None, **kwargs ): 567 name = self._getChildData('name',root=memberdef) 568 self._setID(memberdef.getAttribute('id'),scope+'::'+name) 569 ## Check if we have some specific kind of method. 570 if name == scope.split('::')[-1]: 571 kind = 'constructor' 572 target = target.parentNode 573 elif name == '~'+scope.split('::')[-1]: 574 kind = 'destructor' 575 target = target.parentNode 576 elif name == 'operator=': 577 kind = 'copy-assignment' 578 target = target.parentNode 579 else: 580 kind = 'method' 581 method = target.appendChild(self._createNode(kind, 582 # id=memberdef.getAttribute('id'), 583 name=name, 584 cv=' '.join([ 585 if_attribute(memberdef,'const','const','').strip() 586 ]), 587 specifiers=' '.join([ 588 if_attribute(memberdef,'static','static',''), 589 if_attribute(memberdef,'explicit','explicit',''), 590 if_attribute(memberdef,'inline','inline','') 591 ]).strip() 592 )) 593 ## We iterate the children to translate each part of the function. 594 for n in memberdef.childNodes: 595 self._translateNode(memberdef,'function',n,target=method) 596 return method 597 598 #~ Translate: 599 #~ <memberdef kind="function"...><templateparamlist>...</templateparamlist></memberdef> 600 def _translate_memberdef_function_templateparamlist( 601 self, templateparamlist, target=None, **kwargs ): 602 return self._translate_templateparamlist(templateparamlist,target=target,**kwargs) 603 604 #~ Translate: 605 #~ <memberdef kind="function"...><type>...</type></memberdef> 606 #~ To: 607 #~ ...<type>?</type> 608 def _translate_memberdef_function_type( self, resultType, target=None, **kwargs ): 609 methodType = self._createNode('type') 610 self._translate_type(resultType,target=methodType) 611 if methodType.hasChildNodes(): 612 target.appendChild(methodType) 613 return methodType 614 615 #~ Translate: 616 #~ <memberdef kind="function"...><briefdescription>...</briefdescription></memberdef> 617 def _translate_memberdef_function_briefdescription( self, description, target=None, **kwargs ): 618 result = self._translateDescription(description,target=target,**kwargs) 619 ## For functions if we translate the brief docs to the purpose they end up 620 ## right above the regular description. And since we just added the brief to that 621 ## on the previous line, don't bother with the repetition. 622 # result = self._translateDescription(description,target=target,tag='purpose',**kwargs) 623 return result 624 625 #~ Translate: 626 #~ <memberdef kind="function"...><detaileddescription>...</detaileddescription></memberdef> 627 def _translate_memberdef_function_detaileddescription( self, description, target=None, **kwargs ): 628 return self._translateDescription(description,target=target,**kwargs) 629 630 #~ Translate: 631 #~ <memberdef kind="function"...><inbodydescription>...</inbodydescription></memberdef> 632 def _translate_memberdef_function_inbodydescription( self, description, target=None, **kwargs ): 633 return self._translateDescription(description,target=target,**kwargs) 634 635 #~ Translate: 636 #~ <memberdef kind="function"...><param>...</param></memberdef> 637 def _translate_memberdef_function_param( self, param, target=None, **kwargs ): 638 return self._translate_param(param,target=target,**kwargs) 639 640 #~ Translate: 641 #~ <memberdef kind="variable" id="?"> 642 #~ <name>...</name> 643 #~ <type>...</type> 644 #~ </memberdef> 645 #~ To: 646 #~ <data-member id="?" name="?"> 647 #~ <type>...</type> 648 #~ </data-member> 649 def _translate_memberdef_variable( self, memberdef, target=None, scope=None, **kwargs ): 650 self._setID(memberdef.getAttribute('id'), 651 scope+'::'+self._getChildData('name',root=memberdef)) 652 data_member = target.appendChild(self._createNode('data-member', 653 id=memberdef.getAttribute('id'), 654 name=self._getChildData('name',root=memberdef))) 655 data_member_type = data_member.appendChild(self._createNode('type')) 656 self._translate_type(self._getChild('type',root=memberdef),target=data_member_type) 657 658 #~ Translate: 659 #~ <memberdef kind="enum" id="?"> 660 #~ <name>...</name> 661 #~ ... 662 #~ </memberdef> 663 #~ To: 664 #~ <enum id="?" name="?"> 665 #~ ... 666 #~ </enum> 667 def _translate_memberdef_enum( self, memberdef, target=None, scope=None, **kwargs ): 668 self._setID(memberdef.getAttribute('id'), 669 scope+'::'+self._getChildData('name',root=memberdef)) 670 enum = target.appendChild(self._createNode('enum', 671 id=memberdef.getAttribute('id'), 672 name=self._getChildData('name',root=memberdef))) 673 for n in memberdef.childNodes: 674 self._translateNode(memberdef,'enum',n,target=enum,scope=scope,**kwargs) 675 return enum 676 677 #~ Translate: 678 #~ <memberdef kind="enum"...> 679 #~ <enumvalue id="?"> 680 #~ <name>...</name> 681 #~ <initializer>...</initializer> 682 #~ </enumvalue> 683 #~ </memberdef> 684 #~ To: 685 #~ <enumvalue id="?" name="?"> 686 #~ <default>...</default> 687 #~ </enumvalue> 688 def _translate_memberdef_enum_enumvalue( self, enumvalue, target=None, scope=None, **kwargs ): 689 self._setID(enumvalue.getAttribute('id'), 690 scope+'::'+self._getChildData('name',root=enumvalue)) 691 value = target.appendChild(self._createNode('enumvalue', 692 id=enumvalue.getAttribute('id'), 693 name=self._getChildData('name',root=enumvalue))) 694 initializer = self._getChild('initializer',root=enumvalue) 695 if initializer: 696 self._translateChildren(initializer, 697 target=target.appendChild(self._createNode('default'))) 698 return value 699 700 #~ Translate: 701 #~ <param> 702 #~ <type>...</type> 703 #~ <declname>...</declname> 704 #~ <defval>...</defval> 705 #~ </param> 706 #~ To: 707 #~ <parameter name="?"> 708 #~ <paramtype>...</paramtype> 709 #~ ... 710 #~ </parameter> 711 def _translate_param( self, param, target=None, **kwargs): 712 parameter = target.appendChild(self._createNode('parameter', 713 name=self._getChildData('declname',root=param))) 714 paramtype = parameter.appendChild(self._createNode('paramtype')) 715 self._translate_type(self._getChild('type',root=param),target=paramtype) 716 defval = self._getChild('defval',root=param) 717 if defval: 718 self._translateChildren(self._getChild('defval',root=param),target=parameter) 719 return parameter 720 721 #~ Translate: 722 #~ <ref kindref="?" ...>...</ref> 723 def _translate_ref( self, ref, **kwargs ): 724 return self._translateNode(ref,ref.getAttribute('kindref')) 725 726 #~ Translate: 727 #~ <ref refid="?" kindref="compound">...</ref> 728 #~ To: 729 #~ <link linkend="?"><classname>...</classname></link> 730 def _translate_ref_compound( self, ref, **kwargs ): 731 result = self._createNode('link',linkend=ref.getAttribute('refid')) 732 classname = result.appendChild(self._createNode('classname')) 733 self._translateChildren(ref,target=classname) 734 return result 735 736 #~ Translate: 737 #~ <ref refid="?" kindref="member">...</ref> 738 #~ To: 739 #~ <link linkend="?">...</link> 740 def _translate_ref_member( self, ref, **kwargs ): 741 result = self._createNode('link',linkend=ref.getAttribute('refid')) 742 self._translateChildren(ref,target=result) 743 return result 744 745 #~ Translate: 746 #~ <type>...</type> 747 def _translate_type( self, type, target=None, **kwargs ): 748 result = self._translateChildren(type,target=target,**kwargs) 749 #~ Filter types to clean up various readability problems, most notably 750 #~ with really long types. 751 xml = target.toxml('utf-8'); 752 if ( 753 xml.startswith('<type>boost::mpl::') or 754 xml.startswith('<type>BOOST_PP_') or 755 re.match('<type>boost::(lazy_)?(enable|disable)_if',xml) 756 ): 757 while target.firstChild: 758 target.removeChild(target.firstChild) 759 target.appendChild(self._createText('emphasis','unspecified')) 760 return result 761 762 def _getChild( self, tag = None, id = None, name = None, root = None ): 763 if not root: 764 root = self.boostbook.documentElement 765 for n in root.childNodes: 766 found = True 767 if tag and found: 768 found = found and tag == n.nodeName 769 if id and found: 770 if n.hasAttribute('id'): 771 found = found and n.getAttribute('id') == id 772 else: 773 found = found and n.hasAttribute('id') and n.getAttribute('id') == id 774 if name and found: 775 found = found and n.hasAttribute('name') and n.getAttribute('name') == name 776 if found: 777 #~ print '--|', n 778 return n 779 return None 780 781 def _getChildData( self, tag, **kwargs ): 782 return self._getData(self._getChild(tag,**kwargs),**kwargs) 783 784 def _getData( self, node, **kwargs ): 785 if node: 786 text = self._getChild('#text',root=node) 787 if text: 788 return text.data.strip() 789 return '' 790 791 def _cppName( self, type ): 792 parts = re.search('^([^<]+)[<]?(.*)[>]?$',type.strip().strip(':')) 793 result = { 794 'compoundname' : parts.group(1), 795 'namespace' : parts.group(1).split('::')[0:-1], 796 'name' : parts.group(1).split('::')[-1], 797 'specialization' : parts.group(2) 798 } 799 if result['namespace'] and len(result['namespace']) > 0: 800 namespace = '::'.join(result['namespace']) 801 while ( 802 len(result['namespace']) > 0 and ( 803 namespace not in self.symbols or 804 self.symbols[namespace]['kind'] != 'namespace') 805 ): 806 result['name'] = result['namespace'].pop()+'::'+result['name'] 807 namespace = '::'.join(result['namespace']) 808 return result 809 810 def _createNode( self, tag, **kwargs ): 811 result = self.boostbook.createElement(tag) 812 for k in kwargs.keys(): 813 if kwargs[k] != '': 814 if k == 'id': 815 result.setAttribute('id',kwargs[k]) 816 else: 817 result.setAttribute(k,kwargs[k]) 818 return result 819 820 def _createText( self, tag, data, **kwargs ): 821 result = self._createNode(tag,**kwargs) 822 data = data.strip() 823 if len(data) > 0: 824 result.appendChild(self.boostbook.createTextNode(data)) 825 return result 826 827 828def main( xmldir=None, output=None, id=None, title=None, index=False ): 829 #~ print '--- main: xmldir = %s, output = %s' % (xmldir,output) 830 831 input = glob.glob( os.path.abspath( os.path.join( xmldir, "*.xml" ) ) ) 832 input.sort 833 translator = Doxygen2BoostBook(id=id, title=title, index=index) 834 #~ Feed in the namespaces first to build up the set of namespaces 835 #~ and definitions so that lookup is unambiguous when reading in the definitions. 836 namespace_files = filter( 837 lambda x: 838 os.path.basename(x).startswith('namespace'), 839 input) 840 decl_files = filter( 841 lambda x: 842 not os.path.basename(x).startswith('namespace') and not os.path.basename(x).startswith('_'), 843 input) 844 for dox in namespace_files: 845 #~ print '--|',os.path.basename(dox) 846 translator.addDox(xml.dom.minidom.parse(dox)) 847 for dox in decl_files: 848 #~ print '--|',os.path.basename(dox) 849 translator.addDox(xml.dom.minidom.parse(dox)) 850 851 if output: 852 output = open(output,'w') 853 else: 854 output = sys.stdout 855 if output: 856 output.write(translator.tostring()) 857 858 859main( **get_args() ) 860