Adding gst-python package
[platform/upstream/gst-python.git] / common / scangobj-merge.py
1 #!/usr/bin/python
2 # -*- Mode: Python -*-
3 # vi:si:et:sw=4:sts=4:ts=4
4
5 """
6 parse, merge and write gstdoc-scanobj files
7 """
8
9 from __future__ import print_function, unicode_literals
10
11 import sys
12 import os
13
14 def debug(*args):
15     pass
16
17 # OrderedDict class based on
18 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
19 # Licensed under the Python License
20 class OrderedDict(dict):
21     def __init__(self):
22         self._keys = []
23         dict.__init__(self)
24
25     def __delitem__(self, key):
26         dict.__delitem__(self, key)
27         self._keys.remove(key)
28
29     def __setitem__(self, key, item):
30         dict.__setitem__(self, key, item)
31         if key not in self._keys: self._keys.append(key)
32
33     def clear(self):
34         dict.clear(self)
35         self._keys = []
36
37     def copy(self):
38         dict = dict.copy(self)
39         dict._keys = self._keys[:]
40         return dict
41
42     def items(self):
43         return zip(self._keys, self.values())
44
45     def keys(self):
46         return self._keys
47
48     def popitem(self):
49         try:
50             key = self._keys[-1]
51         except IndexError:
52             raise KeyError('dictionary is empty')
53
54         val = self[key]
55         del self[key]
56
57         return (key, val)
58
59     def setdefault(self, key, failobj = None):
60         dict.setdefault(self, key, failobj)
61         if key not in self._keys: self._keys.append(key)
62
63     def update(self, dict):
64         dict.update(self, dict)
65         for key in dict.keys():
66             if key not in self._keys: self._keys.append(key)
67
68     def values(self):
69         return map(self.get, self._keys)
70
71 class Object:
72     def __init__(self, name):
73         self._signals = OrderedDict()
74         self._args = OrderedDict()
75         self.name = name
76
77     def __repr__(self):
78         return "<Object %s>" % self.name
79
80     def add_signal(self, signal, overwrite=True):
81         if not overwrite and signal.name in self._signals:
82             raise IndexError("signal %s already in %r" % (signal.name, self))
83         self._signals[signal.name] = signal
84
85     def add_arg(self, arg, overwrite=True):
86         if not overwrite and arg.name in self._args:
87             raise IndexError("arg %s already in %r" % (arg.name, self))
88         self._args[arg.name] = arg
89
90 class Docable:
91     def __init__(self, **kwargs):
92         for key in self.attrs:
93             setattr(self, key, kwargs[key])
94         self.dict = kwargs
95
96     def __repr__(self):
97         return "<%r %s>" % (str(self.__class__), self.name)
98
99 class Signal(Docable):
100     attrs = ['name', 'returns', 'args']
101
102 class Arg(Docable):
103     attrs = ['name', 'type', 'range', 'flags', 'nick', 'blurb', 'default']
104
105 class GDoc:
106     def load_file(self, filename):
107         try:
108             lines = open(filename).readlines()
109             self.load_data("".join(lines))
110         except IOError:
111             print ("WARNING - could not read from %s" % filename)
112
113     def save_file(self, filename, backup=False):
114         """
115         Save the information to the given file if the file content changed.
116         """
117         olddata = None
118         try:
119             lines = open(filename).readlines()
120             olddata = "".join(lines)
121         except IOError:
122             print ("WARNING - could not read from %s" % filename)
123         newdata = self.get_data()
124         if olddata and olddata == newdata:
125             return
126
127         if olddata:
128             if backup:
129                 os.rename(filename, filename + '.bak')
130
131         handle = open(filename, "w")
132         handle.write(newdata)
133         handle.close()
134
135 class Signals(GDoc):
136     def __init__(self):
137         self._objects = OrderedDict()
138
139     def load_data(self, data):
140         """
141         Load the .signals lines, creating our list of objects and signals.
142         """
143         import re
144         smatcher = re.compile(
145             '(?s)'                                      # make . match \n
146             '<SIGNAL>\n(.*?)</SIGNAL>\n'
147             )
148         nmatcher = re.compile(
149             '<NAME>'
150             '(?P<object>\S*)'                           # store object
151             '::'
152             '(?P<signal>\S*)'                           # store signal
153             '</NAME>'
154         )
155         rmatcher = re.compile(
156             '(?s)'                                      # make . match \n
157             '<RETURNS>(?P<returns>\S*)</RETURNS>\n'     # store returns
158             '(?P<args>.*)'                              # store args
159         )
160         for block in smatcher.findall(data):
161             nmatch = nmatcher.search(block)
162             if nmatch:
163                 o = nmatch.group('object')
164                 debug("Found object", o)
165                 debug("Found signal", nmatch.group('signal'))
166                 if o not in self._objects:
167                     object = Object(o)
168                     self._objects[o] = object
169
170                 rmatch = rmatcher.search(block)
171                 if rmatch:
172                     dict = rmatch.groupdict().copy()
173                     dict['name'] = nmatch.group('signal')
174                     signal = Signal(**dict)
175                     self._objects[o].add_signal(signal)
176
177     def get_data(self):
178         lines = []
179         for o in self._objects.values():
180             for s in o._signals.values():
181                 block = """<SIGNAL>
182 <NAME>%(object)s::%(name)s</NAME>
183 <RETURNS>%(returns)s</RETURNS>
184 %(args)s</SIGNAL>
185 """
186                 d = s.dict.copy()
187                 d['object'] = o.name
188                 lines.append(block % d)
189
190         return "\n".join(lines) + '\n'
191
192 class Args(GDoc):
193     def __init__(self):
194         self._objects = OrderedDict()
195
196     def load_data(self, data):
197         """
198         Load the .args lines, creating our list of objects and args.
199         """
200         import re
201         amatcher = re.compile(
202             '(?s)'                                      # make . match \n
203             '<ARG>\n(.*?)</ARG>\n'
204             )
205         nmatcher = re.compile(
206             '<NAME>'
207             '(?P<object>\S*)'                           # store object
208             '::'
209             '(?P<arg>\S*)'                              # store arg
210             '</NAME>'
211         )
212         rmatcher = re.compile(
213             '(?s)'                                      # make . match \n
214             '<TYPE>(?P<type>\S*)</TYPE>\n'              # store type
215             '<RANGE>(?P<range>.*?)</RANGE>\n'           # store range
216             '<FLAGS>(?P<flags>\S*)</FLAGS>\n'           # store flags
217             '<NICK>(?P<nick>.*?)</NICK>\n'              # store nick
218             '<BLURB>(?P<blurb>.*?)</BLURB>\n'           # store blurb
219             '<DEFAULT>(?P<default>.*?)</DEFAULT>\n'     # store default
220         )
221         for block in amatcher.findall(data):
222             nmatch = nmatcher.search(block)
223             if nmatch:
224                 o = nmatch.group('object')
225                 debug("Found object", o)
226                 debug("Found arg", nmatch.group('arg'))
227                 if o not in self._objects:
228                     object = Object(o)
229                     self._objects[o] = object
230
231                 rmatch = rmatcher.search(block)
232                 if rmatch:
233                     dict = rmatch.groupdict().copy()
234                     dict['name'] = nmatch.group('arg')
235                     arg = Arg(**dict)
236                     self._objects[o].add_arg(arg)
237                 else:
238                     print ("ERROR: could not match arg from block %s" % block)
239
240     def get_data(self):
241         lines = []
242         for o in self._objects.values():
243             for a in o._args.values():
244                 block = """<ARG>
245 <NAME>%(object)s::%(name)s</NAME>
246 <TYPE>%(type)s</TYPE>
247 <RANGE>%(range)s</RANGE>
248 <FLAGS>%(flags)s</FLAGS>
249 <NICK>%(nick)s</NICK>
250 <BLURB>%(blurb)s</BLURB>
251 <DEFAULT>%(default)s</DEFAULT>
252 </ARG>
253 """
254                 d = a.dict.copy()
255                 d['object'] = o.name
256                 lines.append(block % d)
257
258         return "\n".join(lines) + '\n'
259
260 class SingleLine(GDoc):
261     def __init__(self):
262         self._objects = []
263
264     def load_data(self, data):
265         """
266         Load the .interfaces/.prerequisites lines, merge duplicates
267         """
268         # split data on '\n'
269         lines = data.splitlines();
270         # merge them into self._objects
271         for line in lines:
272             if line not in self._objects:
273                 self._objects.append(line)
274
275     def get_data(self):
276         lines = sorted(self._objects)
277         return "\n".join(lines) + '\n'
278
279 def main(argv):
280     modulename = None
281     try:
282         modulename = argv[1]
283     except IndexError:
284         sys.stderr.write('Please provide a documentation module name\n')
285         sys.exit(1)
286
287     signals = Signals()
288     signals.load_file(modulename + '.signals')
289     signals.load_file(modulename + '.signals.new')
290     signals.save_file(modulename + '.signals', backup=True)
291     os.unlink(modulename + '.signals.new')
292
293     args = Args()
294     args.load_file(modulename + '.args')
295     args.load_file(modulename + '.args.new')
296     args.save_file(modulename + '.args', backup=True)
297     os.unlink(modulename + '.args.new')
298
299     ifaces = SingleLine()
300     ifaces.load_file(modulename + '.interfaces')
301     ifaces.load_file(modulename + '.interfaces.new')
302     ifaces.save_file(modulename + '.interfaces', backup=True)
303     os.unlink(modulename + '.interfaces.new')
304
305     prereq = SingleLine()
306     prereq.load_file(modulename + '.prerequisites')
307     prereq.load_file(modulename + '.prerequisites.new')
308     prereq.save_file(modulename + '.prerequisites', backup=True)
309     os.unlink(modulename + '.prerequisites.new')
310
311 main(sys.argv)