[dali_1.2.40] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / plugins / dali-swig / SWIG / doxygen / scripts / doxy2swig_class.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
374                     s = "Dali::Toolkit::"
375                     s += name
376                     b = "Dali::"
377                     b += name
378                     #print "himanshu ::::::   do_memberdef...class/struct %s" %b
379                     if cname == s or cname == b:
380                          #print("Inside %s "%s)
381                          return
382                     else:
383                          self.add_text('%csmethodmodifiers ')
384                          self.add_text([' %s::%s'%(cname, name)])
385                          self.add_text(['\n','"\n/**\n'])
386
387                 for n in node.childNodes:
388                     if n not in first.values():
389                         self.parse(n)
390                 self.add_text(['\n','*/','\n','%s ";'%prot,'\n'])
391
392     def do_definition(self, node):
393         #print("himanshu ::::::   do_definition")
394         data = node.firstChild.data
395         self.add_text('%s "\n%s'%(data, data))
396
397     def do_sectiondef(self, node):
398         #print('Himanshu : ........SectionDef ........')
399         kind = node.attributes['kind'].value
400         #print('kind = %s'%kind)
401         if kind in ('public-func', 'func', 'user-defined', 'public-type', ''):
402             self.generic_parse(node)
403
404     def do_header(self, node):
405         """For a user defined section def a header field is present
406         which should not be printed as such, so we comment it in the
407         output."""
408         data = node.firstChild.data
409         self.add_text('\n/*\n %s \n*/\n'%data)
410         # If our immediate sibling is a 'description' node then we
411         # should comment that out also and remove it from the parent
412         # node's children.
413         parent = node.parentNode
414         idx = parent.childNodes.index(node)
415         if len(parent.childNodes) >= idx + 2:
416             nd = parent.childNodes[idx+2]
417             if nd.nodeName == 'description':
418                 nd = parent.removeChild(nd)
419                 self.add_text('\n/*')
420                 self.generic_parse(nd)
421                 self.add_text('\n*/\n')
422
423     def do_parse_sect(self, node, kind):
424         if kind in ('date', 'rcs', 'version'):
425             pass
426         elif kind == 'warning':
427             self.add_text(['\n', '* @warning '])
428             self.generic_parse(node,pad=0)
429         elif kind == 'see':
430             self.add_text('\n')
431             self.add_text('* @see ')
432             self.generic_parse(node,pad=0)
433         elif kind == 'return':
434             self.add_text('\n')
435             self.add_text('* @return ')
436             self.generic_parse(node,pad=0)
437         elif kind == 'pre':
438             self.add_text('\n')
439             self.add_text('* @pre ')
440             self.generic_parse(node,pad=0)
441         elif kind == 'note':
442             self.add_text('\n')
443             self.add_text('* @note ')
444             self.generic_parse(node,pad=0)
445         elif kind == 'post':
446             self.add_text('\n')
447             self.add_text('* @post ')
448             self.generic_parse(node,pad=0)
449         elif kind == 'since':
450             self.add_text('\n')
451             self.add_text('* @SINCE_')
452             self.generic_parse(node,pad=0)
453         else:
454             self.add_text('\n')
455             self.generic_parse(node,pad=0)
456
457     def do_simplesect(self, node):
458         kind = node.attributes['kind'].value
459         self.simplesect_kind = kind
460         self.do_parse_sect(node, kind)
461         self.simplesect_kind = ''
462
463     def do_simplesectsep(self, node):
464         #tmp = node.parentnode
465         self.do_parse_sect(node, self.simplesect_kind)
466
467     def do_argsstring(self, node):
468         #self.generic_parse(node, pad=1)
469         x = 0
470
471     def do_member(self, node):
472         kind = node.attributes['kind'].value
473         refid = node.attributes['refid'].value
474         if kind == 'function' and refid[:9] == 'namespace':
475             self.generic_parse(node)
476
477     def do_doxygenindex(self, node):
478         self.multi = 1
479         comps = node.getElementsByTagName('compound')
480         for c in comps:
481             refid = c.attributes['refid'].value
482             fname = refid + '.xml'
483             if not os.path.exists(fname):
484                 fname = os.path.join(self.my_dir,  fname)
485             #if not self.quiet:
486                 #print "parsing file: %s"%fname
487             p = Doxy2SWIG(fname, self.include_function_definition, self.quiet)
488             p.generate()
489             self.pieces.extend(self.clean_pieces(p.pieces))
490
491     def do_emphasis(self,node):
492         self.add_text('\n* <i> ')
493         self.generic_parse(node,pad=0)
494         self.add_text(' </i>')
495
496     def do_heading(self,node):
497         level = node.attributes['level'].value
498         self.add_text('\n* <h%s> '%level)
499         self.generic_parse(node,pad=0)
500         self.add_text(' </h%s>\n* '%level)
501
502     def do_itemizedlist(self, node):
503         self.add_text(['\n* '])
504         self.list_ctr = self.list_ctr + 2
505         #self.firstListItem = self.firstListItem + 1
506         self.generic_parse(node, pad=0)
507         self.list_ctr = self.list_ctr - 2
508
509     def do_listitem(self, node):
510         #self.add_text('\n'* (self.firstListItem-1))
511         #self.firstlistItem = self.firstListItem - 1
512         self.add_text(' ' * self.list_ctr)
513         self.add_text('- ')
514         self.generic_parse(node, pad=0)
515
516     def do_programlisting(self, node):
517         self.add_text(['\n* '])
518         self.add_text(' ' * (self.list_ctr+2))
519         self.add_text('@code\n*')
520         self.generic_parse(node, pad=0)
521         self.add_text(' ' * (self.list_ctr+2))
522         self.add_text('@endcode\n*')
523
524     def do_codeline(self, node):
525         self.add_text(' ' * (self.list_ctr+2))
526         self.generic_parse(node, pad=1)
527
528     def do_highlight(self, node):
529         cl = node.attributes['class'].value
530         self.add_text(' ')
531         #if cl == 'normal':
532         self.generic_parse(node, pad=0)
533
534     def do_sp(self, node):
535         self.add_text(' ')
536
537     """def do_table(self, node);
538         rows = node.attributes['rows'].value
539         cols = node.attributes['cols'].value"""
540
541     def do_enumvalue(self, node):
542         self.generic_parse(node, pad=0)
543
544     def write(self, fname):
545         o = my_open_write(fname)
546         if self.multi or 1:
547             o.write(u"".join(self.pieces).encode('utf-8'))
548         else:
549             o.write("".join(self.clean_pieces(self.pieces)))
550         o.close()
551
552     def remove_trailing_spaces(self, fname):
553         clean_lines = []
554         with open(fname) as o:
555             line = o.readlines()
556             clean_lines = [l.strip() for l in line if l.strip()]
557
558         with open('temp','w+') as f:
559             f.writelines('\n'.join(clean_lines))
560
561         f.close()
562         """with open('temp','r+') as f:
563             text = f.read()
564         f.close()
565         t = textwrap.fill(text, 100, break_long_words=False)
566         t = t.replace('\n','\n* '+' '*(self.list_ctr+2))
567         #t = t.replace('1234',' '*self.list_ctr)
568         with open('temp','w+') as f:
569             f.write(t)
570         """
571         os.rename('temp',fname)
572         f.close()
573
574     def clean_pieces(self, pieces):
575         """Cleans the list of strings given as `pieces`.  It replaces
576         multiple newlines by a maximum of 2 and returns a new list.
577         It also wraps the paragraphs nicely.
578         """
579         ret = []
580         count = 0
581         for i in pieces:
582             if i == '\n':
583                 count = count + 1
584             else:
585                 if i == '";':
586                     if count:
587                         ret.append('\n')
588                 elif count > 2:
589                     ret.append('\n\n')
590                 elif count:
591                     ret.append('\n'*count)
592                 count = 0
593                 ret.append(i)
594
595         _data = "".join(ret)
596         ret = []
597         for i in _data.split('\n\n'):
598             if i == 'Parameters:' or i == 'Exceptions:':
599                 ret.extend([i, '\n-----------', '\n\n'])
600             elif i.find('// File:') > -1: # leave comments alone.
601                 ret.extend([i, '\n'])
602             else:
603                 _tmp = textwrap.fill(i.strip(), break_long_words=False)
604                 _tmp = self.lead_spc.sub(r'\1"\2', _tmp)
605                 ret.extend([_tmp, '\n\n'])
606         return ret
607
608
609 def convert(input, output, include_function_definition=True, quiet=False):
610     p = Doxy2SWIG(input, include_function_definition, quiet)
611     p.generate()
612     #p.pieces=[str(i) for i in p.pieces]
613     #print p.pieces
614     p.write(output)
615     p.remove_trailing_spaces(output)
616
617 def main():
618     usage = __doc__
619     parser = optparse.OptionParser(usage)
620     parser.add_option("-n", '--no-function-definition',
621                       action='store_true',
622                       default=False,
623                       dest='func_def',
624                       help='do not include doxygen function definitions')
625     parser.add_option("-q", '--quiet',
626                       action='store_true',
627                       default=False,
628                       dest='quiet',
629                       help='be quiet and minimize output')
630
631     options, args = parser.parse_args()
632     if len(args) != 2:
633         parser.error("error: no input and output specified")
634
635     convert(args[0], args[1], not options.func_def, options.quiet)
636
637
638 if __name__ == '__main__':
639     main()