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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import json
6 import os.path
7 import re
8 import util
9
10 # Schema is described as follows:
11 # * for simple type:
12 # %simple_type%
13 # * for list:
14 # (list, %items_schema%)
15 # * for dict:
16 # (dict, { '%key_name%': (%is_key_required%, %value_schema%),
17 # ...
18 # })
19 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.
20 'imports': (False, (list, unicode)),
21 'type': (True, unicode),
22 'children': (False, (list, (dict, {
23 'id': (True, unicode),
24 'type': (True, unicode)
25 }))),
26 'context': (False, (list, (dict, {
27 'name': (True, unicode),
28 'type': (False, unicode),
29 'default': (False, object)
30 }))),
31 '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.
32 '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.
33 })
34
35 # Returns (True,) if |obj| matches |schema|.
36 # Otherwise returns (False, msg), where |msg| is a diagnostic message.
37 def MatchSchema(obj, schema):
38 expected_type = schema[0] if isinstance(schema, tuple) else schema
39 if not isinstance(obj, expected_type):
40 return (False, 'Wrong type, expected %s, got %s.' %
41 (expected_type, type(obj)))
42 if expected_type == dict:
43 obj_keys = set(obj.iterkeys())
44 allowed_keys = set(schema[1].iterkeys())
45 required_keys = set(kv[0] for kv in schema[1].iteritems() if kv[1][0])
46 if not obj_keys.issuperset(required_keys):
47 missing_keys = required_keys.difference(obj_keys)
48 return (False, 'Missing required key%s %s.' %
49 ('s' if len(missing_keys) > 1 else '',
50 ', '.join('\'%s\'' % k for k in missing_keys)))
51 if not obj_keys.issubset(allowed_keys):
52 unknown_keys = obj_keys.difference(allowed_keys)
53 return (False, 'Unknown key%s %s.' %
54 ('s' if len(unknown_keys) > 1 else '',
55 ', '.join('\'%s\'' % k for k in unknown_keys)))
56 for key in obj:
57 match = MatchSchema(obj[key], schema[1][key][1])
58 if not match[0]:
59 return (False, ('[\'%s\'] ' % key) + match[1])
60 elif expected_type == list:
61 for i, item in enumerate(obj):
62 match = MatchSchema(item, schema[1])
63 if not match[0]:
64 return (False, ('[%d] ' % i) + match[1])
65 return (True,)
66
67 def CheckDeclarationIsValid(declaration):
68 match = MatchSchema(declaration, DECLARATION_SCHEMA)
69 if not match[0]:
70 raise Exception('Declaration is not valid: ' + match[1])
71
72
73 class FieldDeclaration(object):
74 ALLOWED_TYPES = [
75 'boolean',
76 'integer',
77 'double',
78 'string',
79 'string_list'
80 ]
81
82 DEFAULTS = {
83 'boolean': False,
84 'integer': 0,
85 'double': 0.0,
86 'string': u'',
87 'string_list': []
88 }
89
90 def __init__(self, declaration):
91 self.name = declaration['name']
92 self.id = util.ToLowerCamelCase(self.name)
93 self.type = declaration['type'] if 'type' in declaration else 'string'
94 if self.type not in self.ALLOWED_TYPES:
95 raise Exception('Unknown type of context field "%s": "%s"' %
96 (self.name, self.type))
97 self.default_value = declaration['default'] if 'default' in declaration \
98 else self.DEFAULTS[self.type]
99 if type(self.default_value) != type(self.DEFAULTS[self.type]):
100 raise Exception('Wrong type of default for field "%s": '
101 'expected "%s", got "%s"' %
102 (self.name,
103 type(self.DEFAULTS[self.type]),
104 type(self.default_value)))
105
106 class Declaration(object):
107 class DeclarationsStorage(object):
108 def __init__(self):
109 self.by_path = {}
110 self.by_type = {}
111
112 def Add(self, declaration):
113 assert declaration.path not in self.by_path
114 self.by_path[declaration.path] = declaration
115 if declaration.type in self.by_type:
116 raise Exception(
117 'Redefinition of "%s" in "%s". Previous definition was in "%s".' % \
118 (declaration.type, declaration.path,
119 self.by_type[declaration.type].path))
120 self.by_type[declaration.type] = declaration
121
122 def HasPath(self, path):
123 return path in self.by_path
124
125 def GetByPath(self, path):
126 return self.by_path[path]
127
128 def GetByType(self, type):
129 return self.by_type[type]
130
131 def GetKnownPathes(self):
132 return self.by_path.keys()
133
134
135 def __init__(self, path, known_declarations=None):
136 if known_declarations is None:
137 known_declarations = Declaration.DeclarationsStorage()
138 self.path = path
139 self.data = json.load(open(path, 'r'))
140 CheckDeclarationIsValid(self.data)
141 known_declarations.Add(self)
142 if 'imports' in self.data:
143 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.
144 if not known_declarations.HasPath(import_path):
145 Declaration(import_path, known_declarations)
146 self.children = {}
147 if 'children' in self.data:
148 for child_data in self.data['children']:
149 self.children[child_data['id']] = \
150 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.
151 if 'strings' not in self.data:
152 self.data['strings'] = {}
153 if 'context' not in self.data:
154 self.data['context'] = []
155 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.
156 self.data['namespace'] = 'gen'
157 if 'events' not in self.data:
158 self.data['events'] = []
159 self.fields = [FieldDeclaration(d) for d in self.data['context']]
160 fields_names = [field.name for field in self.fields]
161 if len(fields_names) > len(set(fields_names)):
162 raise Exception('Duplicate fields in declaration.')
163 self.known_declarations = known_declarations
164
165 @property
166 def type(self):
167 return self.data['type']
168
169 @property
170 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.
171 return self.data['namespace']
172
173 @property
174 def strings(self):
175 return self.data['strings']
176
177 @property
178 def events(self):
179 return self.data['events']
180
181 @property
182 def webui_view_basename(self):
183 return self.type + '_web_ui_view'
184
185 @property
186 def webui_view_h_name(self):
187 return self.webui_view_basename + '.h'
188
189 @property
190 def webui_view_cc_name(self):
191 return self.webui_view_basename + '.cc'
192
193 @property
194 def webui_view_include_path(self):
195 return os.path.join(os.path.dirname(self.path), self.webui_view_h_name)
196
197 @property
198 def export_h_name(self):
199 return self.type + '_export.h'
200
201 @property
202 def component_export_macro(self):
203 return "WUG_" + self.type.upper() + "_EXPORT"
204
205 @property
206 def component_impl_macro(self):
207 return "WUG_" + self.type.upper() + "_IMPLEMENTATION"
208
209 @property
210 def export_h_include_path(self):
211 return os.path.join(os.path.dirname(self.path), self.export_h_name)
212
213 @property
214 def view_model_basename(self):
215 return self.type + '_view_model'
216
217 @property
218 def view_model_h_name(self):
219 return self.view_model_basename + '.h'
220
221 @property
222 def view_model_cc_name(self):
223 return self.view_model_basename + '.cc'
224
225 @property
226 def view_model_include_path(self):
227 return os.path.join(os.path.dirname(self.path), self.view_model_h_name)
228
229 @property
230 def html_view_basename(self):
231 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.
232
233 @property
234 def html_view_html_name(self):
235 return self.html_view_basename + '.html'
236
237 @property
238 def html_view_js_name(self):
239 return self.html_view_basename + '.js'
240
241 @property
242 def html_view_html_include_path(self):
243 return os.path.join(os.path.dirname(self.path), self.html_view_html_name)
244
245 @property
246 def html_view_js_include_path(self):
247 return os.path.join(os.path.dirname(self.path), self.html_view_js_name)
248
249 @property
250 def build_target(self):
251 return self.type + "_wug_generated"
252
253 @property
254 def webui_view_class(self):
255 return util.ToUpperCamelCase(self.type) + 'WebUIView'
256
257 @property
258 def view_model_class(self):
259 return util.ToUpperCamelCase(self.type) + 'ViewModel'
260
261 @property
262 def imports(self):
263 res = set(self.known_declarations.GetKnownPathes())
264 res.remove(self.path)
265 return res
266
267
268
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698