summaryrefslogtreecommitdiff
path: root/common/scangobj-merge.py
diff options
context:
space:
mode:
Diffstat (limited to 'common/scangobj-merge.py')
-rwxr-xr-xcommon/scangobj-merge.py311
1 files changed, 311 insertions, 0 deletions
diff --git a/common/scangobj-merge.py b/common/scangobj-merge.py
new file mode 100755
index 0000000..4a9f1fc
--- /dev/null
+++ b/common/scangobj-merge.py
@@ -0,0 +1,311 @@
+#!/usr/bin/python
+# -*- Mode: Python -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+"""
+parse, merge and write gstdoc-scanobj files
+"""
+
+from __future__ import print_function, unicode_literals
+
+import sys
+import os
+
+def debug(*args):
+ pass
+
+# OrderedDict class based on
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
+# Licensed under the Python License
+class OrderedDict(dict):
+ def __init__(self):
+ self._keys = []
+ dict.__init__(self)
+
+ def __delitem__(self, key):
+ dict.__delitem__(self, key)
+ self._keys.remove(key)
+
+ def __setitem__(self, key, item):
+ dict.__setitem__(self, key, item)
+ if key not in self._keys: self._keys.append(key)
+
+ def clear(self):
+ dict.clear(self)
+ self._keys = []
+
+ def copy(self):
+ dict = dict.copy(self)
+ dict._keys = self._keys[:]
+ return dict
+
+ def items(self):
+ return zip(self._keys, self.values())
+
+ def keys(self):
+ return self._keys
+
+ def popitem(self):
+ try:
+ key = self._keys[-1]
+ except IndexError:
+ raise KeyError('dictionary is empty')
+
+ val = self[key]
+ del self[key]
+
+ return (key, val)
+
+ def setdefault(self, key, failobj = None):
+ dict.setdefault(self, key, failobj)
+ if key not in self._keys: self._keys.append(key)
+
+ def update(self, dict):
+ dict.update(self, dict)
+ for key in dict.keys():
+ if key not in self._keys: self._keys.append(key)
+
+ def values(self):
+ return map(self.get, self._keys)
+
+class Object:
+ def __init__(self, name):
+ self._signals = OrderedDict()
+ self._args = OrderedDict()
+ self.name = name
+
+ def __repr__(self):
+ return "<Object %s>" % self.name
+
+ def add_signal(self, signal, overwrite=True):
+ if not overwrite and signal.name in self._signals:
+ raise IndexError("signal %s already in %r" % (signal.name, self))
+ self._signals[signal.name] = signal
+
+ def add_arg(self, arg, overwrite=True):
+ if not overwrite and arg.name in self._args:
+ raise IndexError("arg %s already in %r" % (arg.name, self))
+ self._args[arg.name] = arg
+
+class Docable:
+ def __init__(self, **kwargs):
+ for key in self.attrs:
+ setattr(self, key, kwargs[key])
+ self.dict = kwargs
+
+ def __repr__(self):
+ return "<%r %s>" % (str(self.__class__), self.name)
+
+class Signal(Docable):
+ attrs = ['name', 'returns', 'args']
+
+class Arg(Docable):
+ attrs = ['name', 'type', 'range', 'flags', 'nick', 'blurb', 'default']
+
+class GDoc:
+ def load_file(self, filename):
+ try:
+ lines = open(filename).readlines()
+ self.load_data("".join(lines))
+ except IOError:
+ print ("WARNING - could not read from %s" % filename)
+
+ def save_file(self, filename, backup=False):
+ """
+ Save the information to the given file if the file content changed.
+ """
+ olddata = None
+ try:
+ lines = open(filename).readlines()
+ olddata = "".join(lines)
+ except IOError:
+ print ("WARNING - could not read from %s" % filename)
+ newdata = self.get_data()
+ if olddata and olddata == newdata:
+ return
+
+ if olddata:
+ if backup:
+ os.rename(filename, filename + '.bak')
+
+ handle = open(filename, "w")
+ handle.write(newdata)
+ handle.close()
+
+class Signals(GDoc):
+ def __init__(self):
+ self._objects = OrderedDict()
+
+ def load_data(self, data):
+ """
+ Load the .signals lines, creating our list of objects and signals.
+ """
+ import re
+ smatcher = re.compile(
+ '(?s)' # make . match \n
+ '<SIGNAL>\n(.*?)</SIGNAL>\n'
+ )
+ nmatcher = re.compile(
+ '<NAME>'
+ '(?P<object>\S*)' # store object
+ '::'
+ '(?P<signal>\S*)' # store signal
+ '</NAME>'
+ )
+ rmatcher = re.compile(
+ '(?s)' # make . match \n
+ '<RETURNS>(?P<returns>\S*)</RETURNS>\n' # store returns
+ '(?P<args>.*)' # store args
+ )
+ for block in smatcher.findall(data):
+ nmatch = nmatcher.search(block)
+ if nmatch:
+ o = nmatch.group('object')
+ debug("Found object", o)
+ debug("Found signal", nmatch.group('signal'))
+ if o not in self._objects:
+ object = Object(o)
+ self._objects[o] = object
+
+ rmatch = rmatcher.search(block)
+ if rmatch:
+ dict = rmatch.groupdict().copy()
+ dict['name'] = nmatch.group('signal')
+ signal = Signal(**dict)
+ self._objects[o].add_signal(signal)
+
+ def get_data(self):
+ lines = []
+ for o in self._objects.values():
+ for s in o._signals.values():
+ block = """<SIGNAL>
+<NAME>%(object)s::%(name)s</NAME>
+<RETURNS>%(returns)s</RETURNS>
+%(args)s</SIGNAL>
+"""
+ d = s.dict.copy()
+ d['object'] = o.name
+ lines.append(block % d)
+
+ return "\n".join(lines) + '\n'
+
+class Args(GDoc):
+ def __init__(self):
+ self._objects = OrderedDict()
+
+ def load_data(self, data):
+ """
+ Load the .args lines, creating our list of objects and args.
+ """
+ import re
+ amatcher = re.compile(
+ '(?s)' # make . match \n
+ '<ARG>\n(.*?)</ARG>\n'
+ )
+ nmatcher = re.compile(
+ '<NAME>'
+ '(?P<object>\S*)' # store object
+ '::'
+ '(?P<arg>\S*)' # store arg
+ '</NAME>'
+ )
+ rmatcher = re.compile(
+ '(?s)' # make . match \n
+ '<TYPE>(?P<type>\S*)</TYPE>\n' # store type
+ '<RANGE>(?P<range>.*?)</RANGE>\n' # store range
+ '<FLAGS>(?P<flags>\S*)</FLAGS>\n' # store flags
+ '<NICK>(?P<nick>.*?)</NICK>\n' # store nick
+ '<BLURB>(?P<blurb>.*?)</BLURB>\n' # store blurb
+ '<DEFAULT>(?P<default>.*?)</DEFAULT>\n' # store default
+ )
+ for block in amatcher.findall(data):
+ nmatch = nmatcher.search(block)
+ if nmatch:
+ o = nmatch.group('object')
+ debug("Found object", o)
+ debug("Found arg", nmatch.group('arg'))
+ if o not in self._objects:
+ object = Object(o)
+ self._objects[o] = object
+
+ rmatch = rmatcher.search(block)
+ if rmatch:
+ dict = rmatch.groupdict().copy()
+ dict['name'] = nmatch.group('arg')
+ arg = Arg(**dict)
+ self._objects[o].add_arg(arg)
+ else:
+ print ("ERROR: could not match arg from block %s" % block)
+
+ def get_data(self):
+ lines = []
+ for o in self._objects.values():
+ for a in o._args.values():
+ block = """<ARG>
+<NAME>%(object)s::%(name)s</NAME>
+<TYPE>%(type)s</TYPE>
+<RANGE>%(range)s</RANGE>
+<FLAGS>%(flags)s</FLAGS>
+<NICK>%(nick)s</NICK>
+<BLURB>%(blurb)s</BLURB>
+<DEFAULT>%(default)s</DEFAULT>
+</ARG>
+"""
+ d = a.dict.copy()
+ d['object'] = o.name
+ lines.append(block % d)
+
+ return "\n".join(lines) + '\n'
+
+class SingleLine(GDoc):
+ def __init__(self):
+ self._objects = []
+
+ def load_data(self, data):
+ """
+ Load the .interfaces/.prerequisites lines, merge duplicates
+ """
+ # split data on '\n'
+ lines = data.splitlines();
+ # merge them into self._objects
+ for line in lines:
+ if line not in self._objects:
+ self._objects.append(line)
+
+ def get_data(self):
+ lines = sorted(self._objects)
+ return "\n".join(lines) + '\n'
+
+def main(argv):
+ modulename = None
+ try:
+ modulename = argv[1]
+ except IndexError:
+ sys.stderr.write('Please provide a documentation module name\n')
+ sys.exit(1)
+
+ signals = Signals()
+ signals.load_file(modulename + '.signals')
+ signals.load_file(modulename + '.signals.new')
+ signals.save_file(modulename + '.signals', backup=True)
+ os.unlink(modulename + '.signals.new')
+
+ args = Args()
+ args.load_file(modulename + '.args')
+ args.load_file(modulename + '.args.new')
+ args.save_file(modulename + '.args', backup=True)
+ os.unlink(modulename + '.args.new')
+
+ ifaces = SingleLine()
+ ifaces.load_file(modulename + '.interfaces')
+ ifaces.load_file(modulename + '.interfaces.new')
+ ifaces.save_file(modulename + '.interfaces', backup=True)
+ os.unlink(modulename + '.interfaces.new')
+
+ prereq = SingleLine()
+ prereq.load_file(modulename + '.prerequisites')
+ prereq.load_file(modulename + '.prerequisites.new')
+ prereq.save_file(modulename + '.prerequisites', backup=True)
+ os.unlink(modulename + '.prerequisites.new')
+
+main(sys.argv)