4e5580b8ed4132344e5138e9a0ffff97a335bbf2
[platform/core/uifw/dali-toolkit.git] / plugins / dali-swig / SWIG / doxygen / scripts / doxy2swig_struct.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 from xml.dom import minidom
33 import re
34 import textwrap
35 import sys
36 import types
37 import os.path
38 import optparse
39 import os
40
41 def my_open_read(source):
42     if hasattr(source, "read"):
43         return source
44     else:
45         return open(source)
46
47 def my_open_write(dest):
48     if hasattr(dest, "write"):
49         return dest
50     else:
51         return open(dest, 'w')
52
53
54 class Doxy2SWIG:
55     """Converts Doxygen generated XML files into a file containing
56     docstrings that can be used by SWIG-1.3.x that have support for
57     feature("docstring").  Once the data is parsed it is stored in
58     self.pieces.
59
60     """
61
62     def __init__(self, src, include_function_definition=True, quiet=False):
63         """Initialize the instance given a source object.  `src` can
64         be a file or filename.  If you do not want to include function
65         definitions from doxygen then set
66         `include_function_definition` to `False`.  This is handy since
67         this allows you to use the swig generated function definition
68         using %feature("autodoc", [0,1]).
69
70         """
71         f = my_open_read(src)
72         self.my_dir = os.path.dirname(f.name)
73         self.xmldoc = minidom.parse(f).documentElement
74         f.close()
75
76         self.pieces = []
77         self.pieces.append('\n// File: %s\n'%\
78                            os.path.basename(f.name))
79
80         self.space_re = re.compile(r'\s+')
81         self.lead_spc = re.compile(r'^(%feature\S+\s+\S+\s*?)"\s+(\S)')
82         self.multi = 0
83         self.ignores = ['inheritancegraph', 'param', 'listofallmembers',
84                         'innerclass', 'name', 'declname', 'incdepgraph',
85                         'invincdepgraph', 'type',
86                         'references', 'referencedby', 'location',
87                         'collaborationgraph', 'reimplements',
88                         'reimplementedby', 'derivedcompoundref',
89                         'basecompoundref', 'initializer']
90         #self.generics = []
91         self.include_function_definition = include_function_definition
92         if not include_function_definition:
93             self.ignores.append('argsstring')
94
95         self.quiet = quiet
96         self.list_ctr = 1  #counts the number of spaces to be displayed before displaying a list item
97         self.simplesect_kind = ''
98         self.para_kind = ''
99         self.cdef_data = ''
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         self.cdef_data = data
223
224     def do_compounddef(self, node):
225         kind = node.attributes['kind'].value
226         if kind in ('class', 'struct'):
227             prot = node.attributes['prot'].value
228             if prot <> 'public':
229                 return
230             names = ('compoundname', 'briefdescription',
231                      'detaileddescription', 'includes')
232             first = self.get_specific_nodes(node, names)
233             for n in names:
234                 if first.has_key(n):
235                     self.parse(first[n])
236             #self.add_text(['";','\n'])
237             self.add_text(['*/','\n','%s %s ";'%(prot,'class'),'\n'])
238             for n in node.childNodes:
239                 if n not in first.values():
240                     self.parse(n)
241         elif kind in ('file', 'namespace'):
242             nodes = node.getElementsByTagName('sectiondef')
243             for n in nodes:
244                 self.parse(n)
245
246     def do_includes(self, node):
247         self.add_text('\n* @include ')
248         self.generic_parse(node, pad=1)
249
250     def do_parameterlist(self, node):
251
252         print("himanshu ::::::::::  do_parameterlist")
253         text='unknown'
254         for key, val in node.attributes.items():
255             """if key == 'kind':
256                 if val == 'param': text = 'Parameters'
257                 elif val == 'exception': text = 'Exceptions'
258                 else: text = val
259                 break"""
260             if key == 'kind':
261                 if val == 'param': text = '@param'
262                 elif val == 'exception': text = '@exception'
263                 else: text = val
264                 break
265         #self.add_text(['\n', '\n', text, ':', '\n'])
266         #self.add_text(['\n', '* ', text])
267         self.para_kind = text
268         self.generic_parse(node, pad=0)
269
270     def do_para(self, node):
271         print("himanshu :::::::: do_para ")
272         #self.add_text(['\n'])
273         self.generic_parse(node, pad=0)
274
275     def do_parametername(self, node):
276         print("himanshu :::::::: do_parametername")
277         self.add_text(['\n', '* ', self.para_kind])
278
279         try:
280             data=node.firstChild.data
281         except AttributeError: # perhaps a <ref> tag in it
282             data=node.firstChild.firstChild.data
283         if data.find('Exception') != -1:
284             print("himanshu :::::::: Pronting DAta1")
285             print data
286             self.add_text(data)
287         else:
288             print("himanshu :::::::: Pronting DAta2")
289             print data
290             for key, val in node.attributes.items():
291                 if key == 'direction':
292                     self.add_text('[%s] '%val)
293             self.add_text("%s "%data)
294
295     def do_parameterdefinition(self, node):
296         self.generic_parse(node, pad=1)
297
298     def do_detaileddescription(self, node):
299         #self.add_text('')
300         self.generic_parse(node, pad=0)
301
302     def do_briefdescription(self, node):
303         #self.add_text("* @brief ")
304         self.generic_parse(node, pad=0)
305
306     def do_memberdef(self, node):
307         prot = node.attributes['prot'].value
308         id = node.attributes['id'].value
309         kind = node.attributes['kind'].value
310         tmp = node.parentNode.parentNode.parentNode
311         compdef = tmp.getElementsByTagName('compounddef')[0]
312         cdef_kind = compdef.attributes['kind'].value
313         print('Himanshu :: ...... Memberdef........')
314         print('prot= %s ....., id= %s ....., kind= %s..... ,cdef_kind= %s'%(prot,id,kind,cdef_kind))
315
316         if prot == 'public':
317             print('Entering here')
318             first = self.get_specific_nodes(node, ('definition', 'name'))
319             print first
320             name = first['name'].firstChild.data
321             print name
322             if name[:8] == 'operator': # Don't handle operators yet.
323                 return
324             print('Entering here2')
325
326             # For ENUMS
327             if kind == 'enum':
328                 #print('himanshu is in enum now')
329                 """self.add_text('\n\n')
330                 data = node.firstChild.data
331                 self.add_text('%csattributes ')
332                 self.add_text('%s\n"\n/**\n'%data)"""
333                 self.generic_parse(node, pad=0)
334
335             ##################################################
336             # For Classes & Functions
337             if not first.has_key('definition') or \
338                    kind in ['variable', 'typedef']:
339                 return
340             print('Entering here3')
341
342             if self.include_function_definition:
343                 defn = first['definition'].firstChild.data
344             else:
345                 defn = ""
346             self.add_text('\n')
347             briefd = node.getElementsByTagName('briefdescription');
348             if kind == 'function' and briefd[0].firstChild.nodeValue == '\n':  # first node value if briefdescription exists will be always \n
349                 print('Entering here4')
350                 self.add_text('%csmethodmodifiers ')
351
352                 anc = node.parentNode.parentNode
353                 if cdef_kind in ('file', 'namespace'):
354                     ns_node = anc.getElementsByTagName('innernamespace')
355                     if not ns_node and cdef_kind == 'namespace':
356                         ns_node = anc.getElementsByTagName('compoundname')
357                     if ns_node:
358                         ns = ns_node[0].firstChild.data
359                         print("himanshu ::::::   do_memberdef....ns_node")
360                         self.add_text(' %s::%s "\n%s'%(ns, name, defn))
361                     else:
362                         print("himanshu ::::::   do_memberdef....else")
363                         print name
364                         print("++++++++++++++++++++++++++++")
365                         print defn
366                         self.add_text(name)
367                         self.add_text(' \"')
368                         self.add_text('\n')
369                         self.add_text('/**\n')
370                 elif cdef_kind in ('class', 'struct'):
371                     # Get the full function name.
372                     anc_node = anc.getElementsByTagName('compoundname')
373                     cname = anc_node[0].firstChild.data
374                     print("himanshu ::::::   do_memberdef...class/struct")
375                     self.add_text([' %s::%s'%(cname, name)])
376                     self.add_text(['\n','"\n/**\n'])
377
378                 for n in node.childNodes:
379                     if n not in first.values():
380                         self.parse(n)
381                 self.add_text(['\n','*/','\n','%s ";'%prot,'\n'])
382
383     def do_definition(self, node):
384         print("himanshu ::::::   do_definition")
385         data = node.firstChild.data
386         self.add_text('%s "\n%s'%(data, data))
387
388     def do_sectiondef(self, node):
389         print('Himanshu : ........SectionDef ........')
390         kind = node.attributes['kind'].value
391         print('kind = %s'%kind)
392         if kind in ('public-func', 'func', 'user-defined', 'public-type', ''):
393             self.generic_parse(node)
394
395     def do_header(self, node):
396         """For a user defined section def a header field is present
397         which should not be printed as such, so we comment it in the
398         output."""
399         data = node.firstChild.data
400         self.add_text('\n/*\n %s \n*/\n'%data)
401         # If our immediate sibling is a 'description' node then we
402         # should comment that out also and remove it from the parent
403         # node's children.
404         parent = node.parentNode
405         idx = parent.childNodes.index(node)
406         if len(parent.childNodes) >= idx + 2:
407             nd = parent.childNodes[idx+2]
408             if nd.nodeName == 'description':
409                 nd = parent.removeChild(nd)
410                 self.add_text('\n/*')
411                 self.generic_parse(nd)
412                 self.add_text('\n*/\n')
413
414     def do_parse_sect(self, node, kind):
415         if kind in ('date', 'rcs', 'version'):
416             pass
417         elif kind == 'warning':
418             self.add_text(['\n', '* @warning '])
419             self.generic_parse(node,pad=0)
420         elif kind == 'see':
421             self.add_text('\n')
422             self.add_text('* @see ')
423             self.generic_parse(node,pad=0)
424         elif kind == 'return':
425             self.add_text('\n')
426             self.add_text('* @return ')
427             self.generic_parse(node,pad=0)
428         elif kind == 'pre':
429             self.add_text('\n')
430             self.add_text('* @pre ')
431             self.generic_parse(node,pad=0)
432         elif kind == 'note':
433             self.add_text('\n')
434             self.add_text('* @note ')
435             self.generic_parse(node,pad=0)
436         elif kind == 'post':
437             self.add_text('\n')
438             self.add_text('* @post ')
439             self.generic_parse(node,pad=0)
440         else:
441             self.add_text('\n')
442             self.generic_parse(node,pad=0)
443
444     def do_simplesect(self, node):
445         kind = node.attributes['kind'].value
446         self.simplesect_kind = kind
447         self.do_parse_sect(node, kind)
448         self.simplesect_kind = ''
449
450     def do_simplesectsep(self, node):
451         #tmp = node.parentnode
452         self.do_parse_sect(node, self.simplesect_kind)
453
454     def do_argsstring(self, node):
455         #self.generic_parse(node, pad=1)
456         x = 0
457
458     def do_member(self, node):
459         kind = node.attributes['kind'].value
460         refid = node.attributes['refid'].value
461         if kind == 'function' and refid[:9] == 'namespace':
462             self.generic_parse(node)
463
464     def do_doxygenindex(self, node):
465         self.multi = 1
466         comps = node.getElementsByTagName('compound')
467         for c in comps:
468             refid = c.attributes['refid'].value
469             fname = refid + '.xml'
470             if not os.path.exists(fname):
471                 fname = os.path.join(self.my_dir,  fname)
472             if not self.quiet:
473                 print "parsing file: %s"%fname
474             p = Doxy2SWIG(fname, self.include_function_definition, self.quiet)
475             p.generate()
476             self.pieces.extend(self.clean_pieces(p.pieces))
477
478     def do_emphasis(self,node):
479         self.add_text('\n* <i> ')
480         self.generic_parse(node,pad=0)
481         self.add_text(' </i>')
482
483     def do_heading(self,node):
484         level = node.attributes['level'].value
485         self.add_text('\n* <h%s> '%level)
486         self.generic_parse(node,pad=0)
487         self.add_text(' </h%s>\n* '%level)
488
489     def do_itemizedlist(self, node):
490         self.add_text(['\n* '])
491         self.list_ctr = self.list_ctr + 2
492         #self.firstListItem = self.firstListItem + 1
493         self.generic_parse(node, pad=0)
494         self.list_ctr = self.list_ctr - 2
495
496     def do_listitem(self, node):
497         #self.add_text('\n'* (self.firstListItem-1))
498         #self.firstlistItem = self.firstListItem - 1
499         self.add_text(' ' * self.list_ctr)
500         self.add_text('- ')
501         self.generic_parse(node, pad=0)
502
503     def do_programlisting(self, node):
504         self.add_text(['\n* '])
505         self.add_text(' ' * (self.list_ctr+2))
506         self.add_text('@code\n*')
507         self.generic_parse(node, pad=0)
508         self.add_text(' ' * (self.list_ctr+2))
509         self.add_text('@endcode\n*')
510
511     def do_codeline(self, node):
512         self.add_text(' ' * (self.list_ctr+2))
513         self.generic_parse(node, pad=1)
514
515     def do_highlight(self, node):
516         cl = node.attributes['class'].value
517         self.add_text(' ')
518         #if cl == 'normal':
519         self.generic_parse(node, pad=0)
520
521     def do_sp(self, node):
522         self.add_text(' ')
523
524     """def do_table(self, node);
525         rows = node.attributes['rows'].value
526         cols = node.attributes['cols'].value"""
527
528     def do_enumvalue(self, node):
529         self.add_text('\n\n')
530         #data = node.firstChild.nodeValue
531         name = node.getElementsByTagName('name')
532         data = name[0].firstChild.data
533         print('Entering ENUM VALUE')
534         print(data)
535         self.add_text('%csattributes ')
536         self.add_text('%s::%s\n"\n///< '%(self.cdef_data,data))
537         self.generic_parse(node, pad=0)
538         self.add_text('\n"')
539
540     def write(self, fname):
541         o = my_open_write(fname)
542         if self.multi or 1:
543             o.write("".join(self.pieces))
544         else:
545             o.write("".join(self.clean_pieces(self.pieces)))
546         o.close()
547
548     def remove_trailing_spaces(self, fname):
549         clean_lines = []
550         with open(fname) as o:
551             line = o.readlines()
552             clean_lines = [l.strip() for l in line if l.strip()]
553
554         with open('temp','w+') as f:
555             f.writelines('\n'.join(clean_lines))
556
557         f.close()
558         """with open('temp','r+') as f:
559             text = f.read()
560         f.close()
561         t = textwrap.fill(text, 100, break_long_words=False)
562         t = t.replace('\n','\n* '+' '*(self.list_ctr+2))
563         #t = t.replace('1234',' '*self.list_ctr)
564         with open('temp','w+') as f:
565             f.write(t)
566         """
567         os.rename('temp',fname)
568         f.close()
569
570     def clean_pieces(self, pieces):
571         """Cleans the list of strings given as `pieces`.  It replaces
572         multiple newlines by a maximum of 2 and returns a new list.
573         It also wraps the paragraphs nicely.
574         """
575         ret = []
576         count = 0
577         for i in pieces:
578             if i == '\n':
579                 count = count + 1
580             else:
581                 if i == '";':
582                     if count:
583                         ret.append('\n')
584                 elif count > 2:
585                     ret.append('\n\n')
586                 elif count:
587                     ret.append('\n'*count)
588                 count = 0
589                 ret.append(i)
590
591         _data = "".join(ret)
592         ret = []
593         for i in _data.split('\n\n'):
594             if i == 'Parameters:' or i == 'Exceptions:':
595                 ret.extend([i, '\n-----------', '\n\n'])
596             elif i.find('// File:') > -1: # leave comments alone.
597                 ret.extend([i, '\n'])
598             else:
599                 _tmp = textwrap.fill(i.strip(), break_long_words=False)
600                 _tmp = self.lead_spc.sub(r'\1"\2', _tmp)
601                 ret.extend([_tmp, '\n\n'])
602         return ret
603
604
605 def convert(input, output, include_function_definition=True, quiet=False):
606     p = Doxy2SWIG(input, include_function_definition, quiet)
607     p.generate()
608     p.pieces=[str(i) for i in p.pieces]
609     print p.pieces
610     p.write(output)
611     p.remove_trailing_spaces(output)
612
613 def main():
614     usage = __doc__
615     parser = optparse.OptionParser(usage)
616     parser.add_option("-n", '--no-function-definition',
617                       action='store_true',
618                       default=False,
619                       dest='func_def',
620                       help='do not include doxygen function definitions')
621     parser.add_option("-q", '--quiet',
622                       action='store_true',
623                       default=False,
624                       dest='quiet',
625                       help='be quiet and minimize output')
626
627     options, args = parser.parse_args()
628     if len(args) != 2:
629         parser.error("error: no input and output specified")
630
631     convert(args[0], args[1], not options.func_def, options.quiet)
632
633
634 if __name__ == '__main__':
635     main()