From aac33fb2a807e4d5b0baa45d7a1c638911761a97 Mon Sep 17 00:00:00 2001 From: Vincent Untz Date: Sat, 17 Apr 2010 11:57:06 -0400 Subject: [PATCH] Make the gconf schema parser a class --- gio/gsettings-schema-convert | 341 ++++++++++++++++++----------------- 1 file changed, 177 insertions(+), 164 deletions(-) diff --git a/gio/gsettings-schema-convert b/gio/gsettings-schema-convert index d16c1cc14..54c15e96e 100755 --- a/gio/gsettings-schema-convert +++ b/gio/gsettings-schema-convert @@ -22,6 +22,8 @@ # TODO: add alias support for choices # choices: 'this-is-an-alias' = 'real', 'other', 'real' +# TODO: we don't support migrating a pair from a gconf schema. It has yet to be +# seen in real-world usage, though. import os import sys @@ -348,106 +350,6 @@ class GSettingsSchemaKey: ###################################### -def map_gconf_type_to_variant_type(gconftype, gconfsubtype): - typemap = { 'string': 's', 'int': 'i', 'float': 'f', 'bool': 'b', 'list': 'a' } - result = typemap[gconftype] - if gconftype == 'list': - result = result + typemap[gconfsubtype] - return result - - -class GConfSchema: - - def __init__(self, node): - locale_node = node.find('locale') - - self.key = node.find('key').text - self.type = node.find('type').text - if self.type == 'list': - self.list_type = node.find('list_type').text - else: - self.list_type = None - self.varianttype = map_gconf_type_to_variant_type(self.type, self.list_type) - - applyto_node = node.find('applyto') - if applyto_node is not None: - self.applyto = node.find('applyto').text - self.applyto.strip() - self.keyname = self.applyto[self.applyto.rfind('/')+1:] - self.prefix = self.applyto[:self.applyto.rfind('/')+1] - else: - self.applyto = None - self.key.strip() - self.keyname = self.key[self.key.rfind('/')+1:] - self.prefix = self.key[:self.key.rfind('/')+1] - self.prefix = os.path.normpath(self.prefix) - - try: - self.default = locale_node.find('default').text - self.localized = 'messages' - except: - try: - self.default = node.find('default').text - self.localized = None - except: - raise GSettingsSchemaConvertException('No default value for key \'%s\'. A default value is always required in GSettings schemas.' % self.applyto or self.key) - self.typed_default = None - - self.short = self._get_value_with_locale(node, locale_node, 'short') - self.long = self._get_value_with_locale(node, locale_node, 'long') - - self.short = self._oneline(self.short) - self.long = self._oneline(self.long) - - # Fix the default to be parsable by GVariant - if self.type == 'string': - if not self.default: - self.default = '\'\'' - else: - self.default.replace('\'', '\\\'') - self.default = '\'%s\'' % self.default - elif self.type == 'bool': - self.default = self.default.lower() - elif self.type == 'list': - l = self.default.strip() - if not (l[0] == '[' and l[-1] == ']'): - raise GSettingsSchemaConvertException('Cannot parse default list value \'%s\' for key \'%s\'.' % (self.default, self.applyto or self.key)) - values = l[1:-1].strip() - if not values: - self.typed_default = '@%s []' % self.varianttype - elif self.list_type == 'string': - items = [ item.strip() for item in values.split(',') ] - items = [ item.replace('\'', '\\\'') for item in items ] - values = ', '.join([ '\'%s\'' % item for item in items ]) - self.default = '[ %s ]' % values - - def _get_value_with_locale(self, node, locale_node, element): - element_node = None - if locale_node is not None: - element_node = locale_node.find(element) - if element_node is None: - element_node = node.find(element) - if element_node is not None: - return element_node.text - else: - return None - - def _oneline(self, s): - lines = s.splitlines() - result = '' - for line in lines: - result += ' ' + line.lstrip() - return result.strip() - - def get_gsettings_schema_key(self): - key = GSettingsSchemaKey() - key.fill(self.keyname, self.varianttype, self.default, self.typed_default, self.localized, self.keyname, self.short, self.long, None, None) - return key - - -###################################### - - class SimpleSchemaParser: allowed_tokens = { @@ -733,82 +635,192 @@ class SimpleSchemaParser: ###################################### -def read_gconf_schema(gconf_schema_file, default_schema_id): - gsettings_schema_root = GSettingsSchemaRoot() +def map_gconf_type_to_variant_type(gconftype, gconfsubtype): + typemap = { 'string': 's', 'int': 'i', 'float': 'f', 'bool': 'b', 'list': 'a' } + result = typemap[gconftype] + if gconftype == 'list': + result = result + typemap[gconfsubtype] + return result - default_schema_id_count = 0 - gconfschemafile_node = ET.parse(gconf_schema_file).getroot() - for schemalist_node in gconfschemafile_node.findall('schemalist'): - for schema_node in schemalist_node.findall('schema'): - gconf_schema = GConfSchema(schema_node) +class GConfSchema: - schemas_only = (gconf_schema.applyto is not None) + def __init__(self, node): + locale_node = node.find('locale') - dirpath = gconf_schema.prefix - if dirpath[0] != '/': - raise GSettingsSchemaConvertException('Key \'%s\' has a relative path. There is no relative path in GSettings schemas.' % gconf_schema.applyto or gconf_schema.key) + self.key = node.find('key').text + self.type = node.find('type').text + if self.type == 'list': + self.list_type = node.find('list_type').text + else: + self.list_type = None + self.varianttype = map_gconf_type_to_variant_type(self.type, self.list_type) - # remove leading 'schemas/' for schemas-only keys - if schemas_only and dirpath.startswith('/schemas/'): - dirpath = dirpath[len('/schemas'):] + applyto_node = node.find('applyto') + if applyto_node is not None: + self.applyto = node.find('applyto').text + self.applyto.strip() + self.keyname = self.applyto[self.applyto.rfind('/')+1:] + self.prefix = self.applyto[:self.applyto.rfind('/')+1] + else: + self.applyto = None + self.key.strip() + self.keyname = self.key[self.key.rfind('/')+1:] + self.prefix = self.key[:self.key.rfind('/')+1] + self.prefix = os.path.normpath(self.prefix) - if len(dirpath) == 1: - raise GSettingsSchemaConvertException('Key \'%s\' is a toplevel key. Toplevel keys are not accepted in GSettings schemas.' % gconf_schema.applyto or gconf_schema.key) + try: + self.default = locale_node.find('default').text + self.localized = 'messages' + except: + try: + self.default = node.find('default').text + self.localized = None + except: + raise GSettingsSchemaConvertException('No default value for key \'%s\'. A default value is always required in GSettings schemas.' % self.applyto or self.key) + self.typed_default = None - # remove trailing slash because we'll split the string - if dirpath[-1] == '/': - dirpath = dirpath[:-1] - # and also remove leading slash when splitting - hierarchy = dirpath[1:].split('/') + self.short = self._get_value_with_locale(node, locale_node, 'short') + self.long = self._get_value_with_locale(node, locale_node, 'long') - # we don't want to put apps/ and desktop/ keys in the same schema, - # so we have a first step where we make sure to create a new schema - # to avoid this case if necessary - gsettings_schema = None - for schema in gsettings_schema_root.schemas: - if schemas_only: - schema_path = schema.path - else: - schema_path = schema._hacky_path - if dirpath.startswith(schema_path): - gsettings_schema = schema + self.short = self._oneline(self.short) + self.long = self._oneline(self.long) + + # Fix the default to be parsable by GVariant + if self.type == 'string': + if not self.default: + self.default = '\'\'' + else: + self.default.replace('\'', '\\\'') + self.default = '\'%s\'' % self.default + elif self.type == 'bool': + self.default = self.default.lower() + elif self.type == 'list': + l = self.default.strip() + if not (l[0] == '[' and l[-1] == ']'): + raise GSettingsSchemaConvertException('Cannot parse default list value \'%s\' for key \'%s\'.' % (self.default, self.applyto or self.key)) + values = l[1:-1].strip() + if not values: + self.typed_default = '@%s []' % self.varianttype + elif self.list_type == 'string': + items = [ item.strip() for item in values.split(',') ] + items = [ item.replace('\'', '\\\'') for item in items ] + values = ', '.join([ '\'%s\'' % item for item in items ]) + self.default = '[ %s ]' % values + + def _get_value_with_locale(self, node, locale_node, element): + element_node = None + if locale_node is not None: + element_node = locale_node.find(element) + if element_node is None: + element_node = node.find(element) + if element_node is not None: + return element_node.text + else: + return None + + def _oneline(self, s): + lines = s.splitlines() + result = '' + for line in lines: + result += ' ' + line.lstrip() + return result.strip() + + def get_gsettings_schema_key(self): + key = GSettingsSchemaKey() + key.fill(self.keyname, self.varianttype, self.default, self.typed_default, self.localized, self.keyname, self.short, self.long, None, None) + return key + + +###################################### + + +class GConfSchemaParser: + + def __init__(self, file, default_schema_id): + self.file = file + self.default_schema_id = default_schema_id + + self.root = None + self.default_schema_id_count = 0 + + def _insert_schema(self, gconf_schema): + schemas_only = (gconf_schema.applyto is not None) + + dirpath = gconf_schema.prefix + if dirpath[0] != '/': + raise GSettingsSchemaConvertException('Key \'%s\' has a relative path. There is no relative path in GSettings schemas.' % gconf_schema.applyto or gconf_schema.key) + + # remove leading 'schemas/' for schemas-only keys + if schemas_only and dirpath.startswith('/schemas/'): + dirpath = dirpath[len('/schemas'):] + + if len(dirpath) == 1: + raise GSettingsSchemaConvertException('Key \'%s\' is a toplevel key. Toplevel keys are not accepted in GSettings schemas.' % gconf_schema.applyto or gconf_schema.key) + + # remove trailing slash because we'll split the string + if dirpath[-1] == '/': + dirpath = dirpath[:-1] + # and also remove leading slash when splitting + hierarchy = dirpath[1:].split('/') + + # we don't want to put apps/ and desktop/ keys in the same schema, + # so we have a first step where we make sure to create a new schema + # to avoid this case if necessary + gsettings_schema = None + for schema in self.root.schemas: + if schemas_only: + schema_path = schema.path + else: + schema_path = schema._hacky_path + if dirpath.startswith(schema_path): + gsettings_schema = schema + break + if not gsettings_schema: + gsettings_schema = GSettingsSchema() + if self.default_schema_id: + gsettings_schema.id = self.default_schema_id + if self.default_schema_id_count > 0: + gsettings_schema.id += '.FIXME-%s' % self.default_schema_id_count + self.default_schema_id_count += 1 + else: + gsettings_schema.id = 'FIXME' + if schemas_only: + gsettings_schema.path = '/' + hierarchy[0] + '/' + else: + gsettings_schema._hacky_path = '/' + hierarchy[0] + '/' + self.root.schemas.append(gsettings_schema) + + # we create all the subdirs that lead to this key + gsettings_dir = gsettings_schema + for item in hierarchy[1:]: + subdir = None + for dir in gsettings_dir.dirs: + if dir.name == item: + subdir = dir break - if not gsettings_schema: - gsettings_schema = GSettingsSchema() - if default_schema_id: - gsettings_schema.id = default_schema_id - if default_schema_id_count > 0: - gsettings_schema.id += '.FIXME-%s' % default_schema_id_count - default_schema_id_count += 1 - else: - gsettings_schema.id = 'FIXME' - if schemas_only: - gsettings_schema.path = '/' + hierarchy[0] + '/' - else: - gsettings_schema._hacky_path = '/' + hierarchy[0] + '/' - gsettings_schema_root.schemas.append(gsettings_schema) + if not subdir: + subdir = GSettingsSchemaDir() + subdir.name = item + gsettings_dir.dirs.append(subdir) + gsettings_dir = subdir - # we create all the subdirs that lead to this key - gsettings_dir = gsettings_schema - for item in hierarchy[1:]: - subdir = None - for dir in gsettings_dir.dirs: - if dir.name == item: - subdir = dir - break - if not subdir: - subdir = GSettingsSchemaDir() - subdir.name = item - gsettings_dir.dirs.append(subdir) - gsettings_dir = subdir + # we have the final directory, so we can put the key there + gsettings_dir.keys.append(gconf_schema.get_gsettings_schema_key()) - # we have the final directory, so we can put the key there - gsettings_dir.keys.append(gconf_schema.get_gsettings_schema_key()) + def parse(self): + # reset the state of the parser + self.root = GSettingsSchemaRoot() + self.default_schema_id_count = 0 - gsettings_schema_root.simplify() + gconfschemafile_node = ET.parse(self.file).getroot() + for schemalist_node in gconfschemafile_node.findall('schemalist'): + for schema_node in schemalist_node.findall('schema'): + self._insert_schema(GConfSchema(schema_node)) - return gsettings_schema_root + self.root.simplify() + + return self.root ###################################### @@ -866,7 +878,8 @@ def main(args): if options.gconf: try: - schema_root = read_gconf_schema(argfile, options.schema_id) + parser = GConfSchemaParser(argfile, options.schema_id) + schema_root = parser.parse() except SyntaxError, e: raise GSettingsSchemaConvertException('\'%s\' does not look like a valid gconf schema file: %s' % (argfile, e)) else: