| Index: components/wug/generator/declaration.py
|
| diff --git a/components/wug/generator/declaration.py b/components/wug/generator/declaration.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c04bfe05b0a1f4363738fb1f1ccf15b911fd4b27
|
| --- /dev/null
|
| +++ b/components/wug/generator/declaration.py
|
| @@ -0,0 +1,243 @@
|
| +# Copyright 2015 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +import json
|
| +import os.path
|
| +import re
|
| +import util
|
| +
|
| +DECLARATION_SCHEMA = (dict, {
|
| + 'imports': (False, (list, unicode)),
|
| + 'type': (True, unicode),
|
| + 'children': (False, (list, (dict, {
|
| + 'id': (True, unicode),
|
| + 'type': (True, unicode)
|
| + }))),
|
| + 'context': (False, (list, (dict, {
|
| + 'name': (True, unicode),
|
| + 'type': (False, unicode),
|
| + 'default': (False, object)
|
| + }))),
|
| + 'events': (False, (list, unicode)),
|
| + 'strings': (False, (list, unicode))
|
| +})
|
| +
|
| +# Returns (True,) if |obj| matches |schema|.
|
| +# Otherwise returns (False, msg), where |msg| is a diagnostic message.
|
| +def MatchSchema(obj, schema):
|
| + expected_type = schema[0] if isinstance(schema, tuple) else schema
|
| + if not isinstance(obj, expected_type):
|
| + return (False, 'Wrong type, expected %s, got %s.' %
|
| + (expected_type, type(obj)))
|
| + if expected_type == dict:
|
| + obj_keys = set(obj.iterkeys())
|
| + allowed_keys = set(schema[1].iterkeys())
|
| + required_keys = set(kv[0] for kv in schema[1].iteritems() if kv[1][0])
|
| + if not obj_keys.issuperset(required_keys):
|
| + missing_keys = required_keys.difference(obj_keys)
|
| + return (False, 'Missing required key%s %s.' %
|
| + ('s' if len(missing_keys) > 1 else '',
|
| + ', '.join('\'%s\'' % k for k in missing_keys)))
|
| + if not obj_keys.issubset(allowed_keys):
|
| + unknown_keys = obj_keys.difference(allowed_keys)
|
| + return (False, 'Unknown key%s %s.' %
|
| + ('s' if len(unknown_keys) > 1 else '',
|
| + ', '.join('\'%s\'' % k for k in unknown_keys)))
|
| + for key in obj:
|
| + match = MatchSchema(obj[key], schema[1][key][1])
|
| + if not match[0]:
|
| + return (False, ('[\'%s\'] ' % key) + match[1])
|
| + elif expected_type == list:
|
| + for i, item in enumerate(obj):
|
| + match = MatchSchema(item, schema[1])
|
| + if not match[0]:
|
| + return (False, ('[%d] ' % i) + match[1])
|
| + return (True,)
|
| +
|
| +def CheckDeclarationIsValid(declaration):
|
| + match = MatchSchema(declaration, DECLARATION_SCHEMA)
|
| + if not match[0]:
|
| + raise Exception('Declaration is not valid: ' + match[1])
|
| +
|
| +
|
| +class FieldDeclaration(object):
|
| + ALLOWED_TYPES = [
|
| + 'boolean',
|
| + 'integer',
|
| + 'double',
|
| + 'string',
|
| + 'string_list'
|
| + ]
|
| +
|
| + DEFAULTS = {
|
| + 'boolean': False,
|
| + 'integer': 0,
|
| + 'double': 0.0,
|
| + 'string': u'',
|
| + 'string_list': []
|
| + }
|
| +
|
| + def __init__(self, declaration):
|
| + self.name = declaration['name']
|
| + self.id = util.ToLowerCamelCase(self.name)
|
| + self.type = declaration['type'] if 'type' in declaration else 'string'
|
| + if self.type not in self.ALLOWED_TYPES:
|
| + raise Exception('Unknown type of context field "%s": "%s"' %
|
| + (self.name, self.type))
|
| + self.default_value = declaration['default'] if 'default' in declaration \
|
| + else self.DEFAULTS[self.type]
|
| + if type(self.default_value) != type(self.DEFAULTS[self.type]):
|
| + raise Exception('Wrong type of default for field "%s": '
|
| + 'expected "%s", got "%s"' %
|
| + (self.name,
|
| + type(self.DEFAULTS[self.type]),
|
| + type(self.default_value)))
|
| +
|
| +class Declaration(object):
|
| + class DeclarationsStorage(object):
|
| + def __init__(self):
|
| + self.by_path = {}
|
| + self.by_type = {}
|
| +
|
| + def Add(self, declaration):
|
| + assert declaration.path not in self.by_path
|
| + self.by_path[declaration.path] = declaration
|
| + if declaration.type in self.by_type:
|
| + raise Exception(
|
| + 'Redefinition of "%s" in "%s". Previous definition was in "%s".' % \
|
| + (declaration.type, declaration.path,
|
| + self.by_type[declaration.type].path))
|
| + self.by_type[declaration.type] = declaration
|
| +
|
| + def HasPath(self, path):
|
| + return path in self.by_path
|
| +
|
| + def GetByPath(self, path):
|
| + return self.by_path[path]
|
| +
|
| + def GetByType(self, type):
|
| + return self.by_type[type]
|
| +
|
| + def GetKnownPathes(self):
|
| + return self.by_path.keys()
|
| +
|
| +
|
| + def __init__(self, path, known_declarations=None):
|
| + if known_declarations is None:
|
| + known_declarations = Declaration.DeclarationsStorage()
|
| + self.path = path
|
| + self.data = json.load(open(path, 'r'))
|
| + CheckDeclarationIsValid(self.data)
|
| + known_declarations.Add(self)
|
| + if 'imports' in self.data:
|
| + for import_path in self.data['imports']:
|
| + if not known_declarations.HasPath(import_path):
|
| + Declaration(import_path, known_declarations)
|
| + self.children = {}
|
| + if 'children' in self.data:
|
| + for child_data in self.data['children']:
|
| + self.children[child_data['id']] = \
|
| + known_declarations.GetByType(child_data['type'])
|
| + if 'strings' not in self.data:
|
| + self.data['strings'] = {}
|
| + if 'context' not in self.data:
|
| + self.data['context'] = []
|
| + if 'namespace' not in self.data:
|
| + self.data['namespace'] = 'gen'
|
| + if 'events' not in self.data:
|
| + self.data['events'] = []
|
| + self.fields = [FieldDeclaration(d) for d in self.data['context']]
|
| + fields_names = [field.name for field in self.fields]
|
| + if len(fields_names) > len(set(fields_names)):
|
| + raise Exception('Duplicate fields in declaration.')
|
| + self.known_declarations = known_declarations
|
| +
|
| + @property
|
| + def type(self):
|
| + return self.data['type']
|
| +
|
| + @property
|
| + def namespace(self):
|
| + return self.data['namespace']
|
| +
|
| + @property
|
| + def strings(self):
|
| + return self.data['strings']
|
| +
|
| + @property
|
| + def events(self):
|
| + return self.data['events']
|
| +
|
| + @property
|
| + def webui_view_basename(self):
|
| + return self.type + '_web_ui_view'
|
| +
|
| + @property
|
| + def webui_view_h_name(self):
|
| + return self.webui_view_basename + '.h'
|
| +
|
| + @property
|
| + def webui_view_cc_name(self):
|
| + return self.webui_view_basename + '.cc'
|
| +
|
| + @property
|
| + def webui_view_include_path(self):
|
| + return os.path.join(os.path.dirname(self.path), self.webui_view_h_name)
|
| +
|
| + @property
|
| + def view_model_basename(self):
|
| + return self.type + '_view_model'
|
| +
|
| + @property
|
| + def view_model_h_name(self):
|
| + return self.view_model_basename + '.h'
|
| +
|
| + @property
|
| + def view_model_cc_name(self):
|
| + return self.view_model_basename + '.cc'
|
| +
|
| + @property
|
| + def view_model_include_path(self):
|
| + return os.path.join(os.path.dirname(self.path), self.view_model_h_name)
|
| +
|
| + @property
|
| + def html_view_basename(self):
|
| + return '%s-view' % self.type.replace('_', '-')
|
| +
|
| + @property
|
| + def html_view_html_name(self):
|
| + return self.html_view_basename + '.html'
|
| +
|
| + @property
|
| + def html_view_js_name(self):
|
| + return self.html_view_basename + '.js'
|
| +
|
| + @property
|
| + def html_view_html_include_path(self):
|
| + return os.path.join(os.path.dirname(self.path), self.html_view_html_name)
|
| +
|
| + @property
|
| + def html_view_js_include_path(self):
|
| + return os.path.join(os.path.dirname(self.path), self.html_view_js_name)
|
| +
|
| + @property
|
| + def build_target(self):
|
| + return re.sub(r'[/.\\-]', '_', self.path)
|
| +
|
| + @property
|
| + def webui_view_class(self):
|
| + return util.ToUpperCamelCase(self.type) + 'WebUIView'
|
| +
|
| + @property
|
| + def view_model_class(self):
|
| + return util.ToUpperCamelCase(self.type) + 'ViewModel'
|
| +
|
| + @property
|
| + def imports(self):
|
| + res = set(self.known_declarations.GetKnownPathes())
|
| + res.remove(self.path)
|
| + return res
|
| +
|
| +
|
| +
|
|
|