Merge "Doxygen Comment Generation" into devel/master
[platform/core/uifw/dali-toolkit.git] / plugins / dali-swig / SWIG / doxygen / scripts / doxy2swig_namespace.py_
1 #!/usr/bin/env python
2 """Doxygen XML to SWIG docstring converter.
3
4 Usage:
5
6   doxy2swig.py [options] input.xml output.i
7
8 Converts Doxygen generated XML files into a file containing docstrings
9 that can be used by SWIG-1.3.x.  Note that you need to get SWIG
10 version > 1.3.23 or use Robin Dunn's docstring patch to be able to use
11 the resulting output.
12
13 input.xml is your doxygen generated XML file and output.i is where the
14 output will be written (the file will be clobbered).
15
16 """
17 ######################################################################
18 #
19 # This code is implemented using Mark Pilgrim's code as a guideline:
20 #   http://www.faqs.org/docs/diveintopython/kgp_divein.html
21 #
22 # Author: Prabhu Ramachandran
23 # License: BSD style
24 #
25 # Thanks:
26 #   Johan Hake:  the include_function_definition feature
27 #   Bill Spotz:  bug reports and testing.
28 #   Sebastian Henschel:   Misc. enhancements.
29 #
30 ######################################################################
31
32 #_*_ coding:utf-8 _*_
33 from xml.dom import minidom
34 import re
35 import textwrap
36 import sys
37 import types
38 import os.path
39 import optparse
40 import os
41
42 def my_open_read(source):
43     if hasattr(source, "read"):
44         return source
45     else:
46         return open(source)
47
48 def my_open_write(dest):
49     if hasattr(dest, "write"):
50         return dest
51     else:
52         return open(dest, 'w')
53
54
55 class Doxy2SWIG:
56     """Converts Doxygen generated XML files into a file containing
57     docstrings that can be used by SWIG-1.3.x that have support for
58     feature("docstring").  Once the data is parsed it is stored in
59     self.pieces.
60
61     """
62
63     def __init__(self, src, include_function_definition=True, quiet=False):
64         """Initialize the instance given a source object.  `src` can
65         be a file or filename.  If you do not want to include function
66         definitions from doxygen then set
67         `include_function_definition` to `False`.  This is handy since
68         this allows you to use the swig generated function definition
69         using %feature("autodoc", [0,1]).
70
71         """
72         f = my_open_read(src)
73         self.my_dir = os.path.dirname(f.name)
74         self.xmldoc = minidom.parse(f).documentElement
75         f.close()
76
77         self.pieces = []
78         self.pieces.append('\n// File: %s\n'%\
79                            os.path.basename(f.name))
80
81         self.space_re = re.compile(r'\s+')
82         self.lead_spc = re.compile(r'^(%feature\S+\s+\S+\s*?)"\s+(\S)')
83         self.multi = 0
84         self.ignores = ['inheritancegraph', 'param', 'listofallmembers',
85                         'innerclass', 'name', 'declname', 'incdepgraph',
86                         'invincdepgraph', 'type',
87                         'references', 'referencedby', 'location',
88                         'collaborationgraph', 'reimplements',
89                         'reimplementedby', 'derivedcompoundref',
90                         'basecompoundref']
91         #self.generics = []
92         self.include_function_definition = include_function_definition
93         if not include_function_definition:
94             self.ignores.append('argsstring')
95
96         self.quiet = quiet
97         self.list_ctr = 1  #counts the number of spaces to be displayed before displaying a list item
98         self.simplesect_kind = ''
99         self.para_kind = ''
100
101     def generate(self):
102         """Parses the file set in the initialization.  The resulting
103         data is stored in `self.pieces`.
104
105         """
106         self.parse(self.xmldoc)
107
108     def parse(self, node):
109         """Parse a given node.  This function in turn calls the
110         `parse_<nodeType>` functions which handle the respective
111         nodes.
112
113         """
114         pm = getattr(self, "parse_%s"%node.__class__.__name__)
115         pm(node)
116
117     def parse_Document(self, node):
118         #print("himanshu ::::::: parse Document... ")
119         self.parse(node.documentElement)
120
121     def parse_Text(self, node):
122         #print("himanshu ::::::: parse Text... ")
123         txt = node.data
124         #txt = txt.replace('\\', r'\\\\')
125         txt = txt.replace('"', r'\"')
126         #print '--------------------------------------'
127         #print '--------------------------------------'
128         #print txt
129         # ignore pure whitespace
130         m = self.space_re.match(txt)
131         if m and len(m.group()) == len(txt):
132             pass
133         else:
134             #self.add_text(txt)
135             t = textwrap.fill(txt, 100, break_long_words=False)
136             #print 'HIMANSHU ---------- >>>>>>>>>>>>>>>>>>>>> '
137             #print t
138             t = t.replace('\n','\n* '+' '*(self.list_ctr+2))
139             #t = t.replace('1234',' '*self.list_ctr)
140             if t:
141                 self.add_line = 1
142             self.add_text(t)
143
144     def parse_Element(self, node):
145         """Parse an `ELEMENT_NODE`.  This calls specific
146         `do_<tagName>` handers for different elements.  If no handler
147         is available the `generic_parse` method is called.  All
148         tagNames specified in `self.ignores` are simply ignored.
149
150         """
151         #print("himanshu ::::::: parse Element... ")
152         name = node.tagName
153         ignores = self.ignores
154         if name in ignores:
155             return
156         attr = "do_%s" % name
157         if hasattr(self, attr):
158             handlerMethod = getattr(self, attr)
159             handlerMethod(node)
160         else:
161             self.generic_parse(node)
162             #if name not in self.generics: self.generics.append(name)
163
164     def parse_Comment(self, node):
165         """Parse a `COMMENT_NODE`.  This does nothing for now."""
166         return
167
168     def add_text(self, value):
169         """Adds text corresponding to `value` into `self.pieces`."""
170         #print value
171         if type(value) in (types.ListType, types.TupleType):
172             self.pieces.extend(value)
173         else:
174             self.pieces.append(value)
175
176     def get_specific_nodes(self, node, names):
177         """Given a node and a sequence of strings in `names`, return a
178         dictionary containing the names as keys and child
179         `ELEMENT_NODEs`, that have a `tagName` equal to the name.
180
181         """
182         nodes = [(x.tagName, x) for x in node.childNodes \
183                  if x.nodeType == x.ELEMENT_NODE and \
184                  x.tagName in names]
185         return dict(nodes)
186
187     def generic_parse(self, node, pad=0):
188         """A Generic parser for arbitrary tags in a node.
189
190         Parameters:
191
192          - node:  A node in the DOM.
193          - pad: `int` (default: 0)
194
195            If 0 the node data is not padded with newlines.  If 1 it
196            appends a newline after parsing the childNodes.  If 2 it
197            pads before and after the nodes are processed.  Defaults to
198            0.
199
200         """
201         npiece = 0
202         if pad:
203             npiece = len(self.pieces)
204         if pad == 2:
205             self.add_text('\n* ')
206         for n in node.childNodes:
207             self.parse(n)
208         if pad:
209             #if len(self.pieces) > npiece:
210             self.add_text('\n* ')
211
212     def space_parse(self, node):
213         self.add_text(' ')
214         self.generic_parse(node)
215
216     def do_compoundname(self, node):
217         self.add_text('\n\n')
218         data = node.firstChild.data
219         #self.add_text('%feature("docstring") %s "\n'%data)
220         self.add_text('%typemap(csclassmodifiers) ')
221         self.add_text('%s\n"\n/**\n'%data)
222
223     def do_compounddef(self, node):
224         kind = node.attributes['kind'].value
225         if kind in ('class', 'struct'):
226             prot = node.attributes['prot'].value
227             if prot <> 'public':
228                 return
229             names = ('compoundname', 'briefdescription',
230                      'detaileddescription', 'includes')
231             first = self.get_specific_nodes(node, names)
232             for n in names:
233                 if first.has_key(n):
234                     self.parse(first[n])
235             #self.add_text(['";','\n'])
236             self.add_text(['*/','\n','%s %s ";'%(prot,'class'),'\n'])
237             for n in node.childNodes:
238                 if n not in first.values():
239                     self.parse(n)
240         elif kind in ('file', 'namespace'):
241             nodes = node.getElementsByTagName('sectiondef')
242             for n in nodes:
243                 self.parse(n)
244
245     def do_includes(self, node):
246         self.add_text('\n* @include ')
247         self.generic_parse(node, pad=1)
248
249     def do_parameterlist(self, node):
250
251         #print("himanshu ::::::::::  do_parameterlist")
252         text='unknown'
253         for key, val in node.attributes.items():
254             """if key == 'kind':
255                 if val == 'param': text = 'Parameters'
256                 elif val == 'exception': text = 'Exceptions'
257                 else: text = val
258                 break"""
259             if key == 'kind':
260                 if val == 'param': text = '@param'
261                 elif val == 'exception': text = '@exception'
262                 else: text = val
263                 break
264         #self.add_text(['\n', '\n', text, ':', '\n'])
265         #self.add_text(['\n', '* ', text])
266         self.para_kind = text
267         self.generic_parse(node, pad=0)
268
269     def do_para(self, node):
270         #print("himanshu :::::::: do_para ")
271         #self.add_text(['\n'])
272         self.generic_parse(node, pad=0)
273
274     def do_parametername(self, node):
275         #print("himanshu :::::::: do_parametername")
276         self.add_text(['\n', '* ', self.para_kind])
277
278         try:
279             data=node.firstChild.data
280         except AttributeError: # perhaps a <ref> tag in it
281             data=node.firstChild.firstChild.data
282         if data.find('Exception') != -1:
283             #print("himanshu :::::::: Pronting DAta1")
284             #print data
285             self.add_text(data)
286         else:
287             #print("himanshu :::::::: Pronting DAta2")
288             #print data
289             for key, val in node.attributes.items():
290                 if key == 'direction':
291                     self.add_text('[%s] '%val)
292             self.add_text("%s "%data)
293
294     def do_parameterdefinition(self, node):
295         self.generic_parse(node, pad=1)
296
297     def do_detaileddescription(self, node):
298         #self.add_text('')
299         self.generic_parse(node, pad=0)
300
301     def do_briefdescription(self, node):
302         self.add_text("* @brief ")
303         self.generic_parse(node, pad=1)
304
305     def do_memberdef(self, node):
306         prot = node.attributes['prot'].value
307         id = node.attributes['id'].value
308         kind = node.attributes['kind'].value
309         tmp = node.parentNode.parentNode.parentNode
310         compdef = tmp.getElementsByTagName('compounddef')[0]
311         cdef_kind = compdef.attributes['kind'].value
312         #print('Himanshu :: ...... Memberdef........')
313         #print('prot= %s ....., id= %s ....., kind= %s..... ,cdef_kind= %s'%(prot,id,kind,cdef_kind))
314
315         if prot == 'public':
316             #print('Entering here')
317             first = self.get_specific_nodes(node, ('definition', 'name'))
318             #print first
319             name = first['name'].firstChild.data
320             #print name
321             if name[:8] == 'operator': # Don't handle operators yet.
322                 return
323             #print('Entering here2')
324
325             # For ENUMS
326             """if kind == 'enum':
327                 #print('himanshu is in enum now')
328                 self.add_text('\n\n')
329                 self.add_text('%typemap(csclassmodifiers) ')
330                 self.add_text('%s\n"\n/**\n'%data)
331                 self.generic_parse(node, pad=0)
332             """
333             ##################################################
334             # For Classes & Functions
335             if not first.has_key('definition') or \
336                    kind in ['variable', 'typedef']:
337                 return
338             #print('Entering here3')
339
340             if self.include_function_definition:
341                 defn = first['definition'].firstChild.data
342             else:
343                 defn = ""
344             self.add_text('\n')
345             briefd = node.getElementsByTagName('briefdescription');
346             if kind == 'function' and briefd[0].firstChild.nodeValue == '\n':  # first node value if briefdescription exists will be always \n
347                 #print('Entering here4')
348                 self.add_text('%csmethodmodifiers ')
349
350                 anc = node.parentNode.parentNode
351                 if cdef_kind in ('file', 'namespace'):
352                     ns_node = anc.getElementsByTagName('innernamespace')
353                     if not ns_node and cdef_kind == 'namespace':
354                         ns_node = anc.getElementsByTagName('compoundname')
355                     if ns_node:
356                         ns = ns_node[0].firstChild.data
357                         #print("himanshu ::::::   do_memberdef....ns_node")
358                         self.add_text(' %s::%s "\n%s'%(ns, name, defn))
359                     else:
360                         #print("himanshu ::::::   do_memberdef....else")
361                         #print name
362                         #print("++++++++++++++++++++++++++++")
363                         #print defn
364                         self.add_text(name)
365                         self.add_text(' \"')
366                         self.add_text('\n')
367                         self.add_text('/**\n')
368                 elif cdef_kind in ('class', 'struct'):
369                     # Get the full function name.
370                     anc_node = anc.getElementsByTagName('compoundname')
371                     cname = anc_node[0].firstChild.data
372                     #print("himanshu ::::::   do_memberdef...class/struct")
373                     self.add_text([' %s::%s'%(cname, name)])
374                     self.add_text(['\n','"\n/**\n'])
375
376                 for n in node.childNodes:
377                     if n not in first.values():
378                         self.parse(n)
379                 self.add_text(['\n','*/','\n','%s ";'%prot,'\n'])
380
381     def do_definition(self, node):
382         #print("himanshu ::::::   do_definition")
383         data = node.firstChild.data
384         self.add_text('%s "\n%s'%(data, data))
385
386     def do_sectiondef(self, node):
387         #print('Himanshu : ........SectionDef ........')
388         kind = node.attributes['kind'].value
389         #print('kind = %s'%kind)
390         if kind in ('public-func', 'func', 'user-defined', 'public-type', ''):
391             self.generic_parse(node)
392
393     def do_header(self, node):
394         """For a user defined section def a header field is present
395         which should not be printed as such, so we comment it in the
396         output."""
397         data = node.firstChild.data
398         self.add_text('\n/*\n %s \n*/\n'%data)
399         # If our immediate sibling is a 'description' node then we
400         # should comment that out also and remove it from the parent
401         # node's children.
402         parent = node.parentNode
403         idx = parent.childNodes.index(node)
404         if len(parent.childNodes) >= idx + 2:
405             nd = parent.childNodes[idx+2]
406             if nd.nodeName == 'description':
407                 nd = parent.removeChild(nd)
408                 self.add_text('\n/*')
409                 self.generic_parse(nd)
410                 self.add_text('\n*/\n')
411
412     def do_parse_sect(self, node, kind):
413         if kind in ('date', 'rcs', 'version'):
414             pass
415         elif kind == 'warning':
416             self.add_text(['\n', '* @warning '])
417             self.generic_parse(node,pad=0)
418         elif kind == 'see':
419             self.add_text('\n')
420             self.add_text('* @see ')
421             self.generic_parse(node,pad=0)
422         elif kind == 'return':
423             self.add_text('\n')
424             self.add_text('* @return ')
425             self.generic_parse(node,pad=0)
426         elif kind == 'pre':
427             self.add_text('\n')
428             self.add_text('* @pre ')
429             self.generic_parse(node,pad=0)
430         elif kind == 'note':
431             self.add_text('\n')
432             self.add_text('* @note ')
433             self.generic_parse(node,pad=0)
434         elif kind == 'post':
435             self.add_text('\n')
436             self.add_text('* @post ')
437             self.generic_parse(node,pad=0)
438         else:
439             self.add_text('\n')
440             self.generic_parse(node,pad=0)
441
442     def do_simplesect(self, node):
443         kind = node.attributes['kind'].value
444         self.simplesect_kind = kind
445         self.do_parse_sect(node, kind)
446         self.simplesect_kind = ''
447
448     def do_simplesectsep(self, node):
449         #tmp = node.parentnode
450         self.do_parse_sect(node, self.simplesect_kind)
451
452     def do_argsstring(self, node):
453         #self.generic_parse(node, pad=1)
454         x = 0
455
456     def do_member(self, node):
457         kind = node.attributes['kind'].value
458         refid = node.attributes['refid'].value
459         if kind == 'function' and refid[:9] == 'namespace':
460             self.generic_parse(node)
461
462     def do_doxygenindex(self, node):
463         self.multi = 1
464         comps = node.getElementsByTagName('compound')
465         for c in comps:
466             refid = c.attributes['refid'].value
467             fname = refid + '.xml'
468             if not os.path.exists(fname):
469                 fname = os.path.join(self.my_dir,  fname)
470             #if not self.quiet:
471                 #print "parsing file: %s"%fname
472             p = Doxy2SWIG(fname, self.include_function_definition, self.quiet)
473             p.generate()
474             self.pieces.extend(self.clean_pieces(p.pieces))
475
476     def do_emphasis(self,node):
477         self.add_text('\n* <i> ')
478         self.generic_parse(node,pad=0)
479         self.add_text(' </i>')
480
481     def do_heading(self,node):
482         level = node.attributes['level'].value
483         self.add_text('\n* <h%s> '%level)
484         self.generic_parse(node,pad=0)
485         self.add_text(' </h%s>\n* '%level)
486
487     def do_itemizedlist(self, node):
488         self.add_text(['\n* '])
489         self.list_ctr = self.list_ctr + 2
490         #self.firstListItem = self.firstListItem + 1
491         self.generic_parse(node, pad=0)
492         self.list_ctr = self.list_ctr - 2
493
494     def do_listitem(self, node):
495         #self.add_text('\n'* (self.firstListItem-1))
496         #self.firstlistItem = self.firstListItem - 1
497         self.add_text(' ' * self.list_ctr)
498         self.add_text('- ')
499         self.generic_parse(node, pad=0)
500
501     def do_programlisting(self, node):
502         self.add_text(['\n* '])
503         self.add_text(' ' * (self.list_ctr+2))
504         self.add_text('@code\n*')
505         self.generic_parse(node, pad=0)
506         self.add_text(' ' * (self.list_ctr+2))
507         self.add_text('@endcode\n*')
508
509     def do_codeline(self, node):
510         self.add_text(' ' * (self.list_ctr+2))
511         self.generic_parse(node, pad=1)
512
513     def do_highlight(self, node):
514         cl = node.attributes['class'].value
515         self.add_text(' ')
516         #if cl == 'normal':
517         self.generic_parse(node, pad=0)
518
519     def do_sp(self, node):
520         self.add_text(' ')
521
522     """def do_table(self, node);
523         rows = node.attributes['rows'].value
524         cols = node.attributes['cols'].value"""
525
526     def do_enumvalue(self, node):
527         self.generic_parse(node, pad=0)
528
529     def write(self, fname):
530         o = my_open_write(fname)
531         if self.multi or 1:
532             o.write(u"".join(self.pieces).encode('utf-8'))
533         else:
534             o.write("".join(self.clean_pieces(self.pieces)))
535         o.close()
536
537     def remove_trailing_spaces(self, fname):
538         clean_lines = []
539         with open(fname) as o:
540             line = o.readlines()
541             clean_lines = [l.strip() for l in line if l.strip()]
542
543         with open('temp','w+') as f:
544             f.writelines('\n'.join(clean_lines))
545
546         f.close()
547         """with open('temp','r+') as f:
548             text = f.read()
549         f.close()
550         t = textwrap.fill(text, 100, break_long_words=False)
551         t = t.replace('\n','\n* '+' '*(self.list_ctr+2))
552         #t = t.replace('1234',' '*self.list_ctr)
553         with open('temp','w+') as f:
554             f.write(t)
555         """
556         os.rename('temp',fname)
557         f.close()
558
559     def clean_pieces(self, pieces):
560         """Cleans the list of strings given as `pieces`.  It replaces
561         multiple newlines by a maximum of 2 and returns a new list.
562         It also wraps the paragraphs nicely.
563         """
564         ret = []
565         count = 0
566         for i in pieces:
567             if i == '\n':
568                 count = count + 1
569             else:
570                 if i == '";':
571                     if count:
572                         ret.append('\n')
573                 elif count > 2:
574                     ret.append('\n\n')
575                 elif count:
576                     ret.append('\n'*count)
577                 count = 0
578                 ret.append(i)
579
580         _data = "".join(ret)
581         ret = []
582         for i in _data.split('\n\n'):
583             if i == 'Parameters:' or i == 'Exceptions:':
584                 ret.extend([i, '\n-----------', '\n\n'])
585             elif i.find('// File:') > -1: # leave comments alone.
586                 ret.extend([i, '\n'])
587             else:
588                 _tmp = textwrap.fill(i.strip(), break_long_words=False)
589                 _tmp = self.lead_spc.sub(r'\1"\2', _tmp)
590                 ret.extend([_tmp, '\n\n'])
591         return ret
592
593
594 def convert(input, output, include_function_definition=True, quiet=False):
595     p = Doxy2SWIG(input, include_function_definition, quiet)
596     p.generate()
597     #p.pieces=[str(i) for i in p.pieces]
598     #print p.pieces
599     p.write(output)
600     p.remove_trailing_spaces(output)
601
602 def main():
603     usage = __doc__
604     parser = optparse.OptionParser(usage)
605     parser.add_option("-n", '--no-function-definition',
606                       action='store_true',
607                       default=False,
608                       dest='func_def',
609                       help='do not include doxygen function definitions')
610     parser.add_option("-q", '--quiet',
611                       action='store_true',
612                       default=False,
613                       dest='quiet',
614                       help='be quiet and minimize output')
615
616     options, args = parser.parse_args()
617     if len(args) != 2:
618         parser.error("error: no input and output specified")
619
620     convert(args[0], args[1], not options.func_def, options.quiet)
621
622
623 if __name__ == '__main__':
624     main()