Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(367)

Unified Diff: components/wug/generator/declaration.py

Issue 928163002: Initial implementation of WebUI generator (WUG) toolkit. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed problems with component build. Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..88f4abbd612003aa4947906d5155020983416add
--- /dev/null
+++ b/components/wug/generator/declaration.py
@@ -0,0 +1,268 @@
+# 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
+
+# Schema is described as follows:
+# * for simple type:
+# %simple_type%
+# * for list:
+# (list, %items_schema%)
+# * for dict:
+# (dict, { '%key_name%': (%is_key_required%, %value_schema%),
+# ...
+# })
+DECLARATION_SCHEMA = (dict, {
Nikita (slow) 2015/02/25 18:21:59 As discussed, add optional comment field (or add T
dzhioev (left Google) 2015/02/26 14:01:39 Done.
+ '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)),
Nikita (slow) 2015/02/25 18:21:59 As discussed, make this list of dicts.
dzhioev (left Google) 2015/02/26 14:01:39 Done.
+ 'strings': (False, (list, unicode))
Nikita (slow) 2015/02/25 18:21:59 As discussed, make this list of dicts.
dzhioev (left Google) 2015/02/26 14:01:39 Done.
+})
+
+# 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']:
Nikita (slow) 2015/02/25 18:21:59 Add TODO to eliminate possibility of circular refe
dzhioev (left Google) 2015/02/26 14:01:39 Done.
+ 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'])
Nikita (slow) 2015/02/25 18:21:59 nit: Check that child declaration is known.
dzhioev (left Google) 2015/02/26 14:01:39 Done.
+ if 'strings' not in self.data:
+ self.data['strings'] = {}
+ if 'context' not in self.data:
+ self.data['context'] = []
+ if 'namespace' not in self.data:
Nikita (slow) 2015/02/25 18:21:59 As discussed, remove namespace.
dzhioev (left Google) 2015/02/26 14:01:39 Done.
+ 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):
Nikita (slow) 2015/02/25 18:21:59 nit: drop this one
dzhioev (left Google) 2015/02/26 14:01:39 Done.
+ 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 export_h_name(self):
+ return self.type + '_export.h'
+
+ @property
+ def component_export_macro(self):
+ return "WUG_" + self.type.upper() + "_EXPORT"
+
+ @property
+ def component_impl_macro(self):
+ return "WUG_" + self.type.upper() + "_IMPLEMENTATION"
+
+ @property
+ def export_h_include_path(self):
+ return os.path.join(os.path.dirname(self.path), self.export_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('_', '-')
Nikita (slow) 2015/02/25 18:21:59 Please use single naming guideline.
dzhioev (left Google) 2015/02/26 14:01:39 I started discussion on chromium-polymer@chromium.
+
+ @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 self.type + "_wug_generated"
+
+ @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
+
+
+

Powered by Google App Engine
This is Rietveld 408576698