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