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

Side by Side Diff: third_party/chrome/tools/model.py

Issue 12261015: Import chrome idl into third_party (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 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 | Annotate | Revision Log
« no previous file with comments | « third_party/chrome/tools/json_schema_test.py ('k') | third_party/chrome/tools/model_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2012 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 copy
6 import os.path
7 import re
8
9 from json_parse import OrderedDict
10
11 class ParseException(Exception):
12 """Thrown when data in the model is invalid.
13 """
14 def __init__(self, parent, message):
15 hierarchy = _GetModelHierarchy(parent)
16 hierarchy.append(message)
17 Exception.__init__(
18 self, 'Model parse exception at:\n' + '\n'.join(hierarchy))
19
20 class Model(object):
21 """Model of all namespaces that comprise an API.
22
23 Properties:
24 - |namespaces| a map of a namespace name to its model.Namespace
25 """
26 def __init__(self):
27 self.namespaces = {}
28
29 def AddNamespace(self, json, source_file, include_compiler_options=False):
30 """Add a namespace's json to the model and returns the namespace.
31 """
32 namespace = Namespace(json,
33 source_file,
34 include_compiler_options=include_compiler_options)
35 self.namespaces[namespace.name] = namespace
36 return namespace
37
38 class Namespace(object):
39 """An API namespace.
40
41 Properties:
42 - |name| the name of the namespace
43 - |unix_name| the unix_name of the namespace
44 - |source_file| the file that contained the namespace definition
45 - |source_file_dir| the directory component of |source_file|
46 - |source_file_filename| the filename component of |source_file|
47 - |platforms| if not None, the list of platforms that the namespace is
48 available to
49 - |types| a map of type names to their model.Type
50 - |functions| a map of function names to their model.Function
51 - |events| a map of event names to their model.Function
52 - |properties| a map of property names to their model.Property
53 - |compiler_options| the compiler_options dict, only present if
54 |include_compiler_options| is True
55 """
56 def __init__(self, json, source_file, include_compiler_options=False):
57 self.name = json['namespace']
58 self.unix_name = UnixName(self.name)
59 self.source_file = source_file
60 self.source_file_dir, self.source_file_filename = os.path.split(source_file)
61 self.parent = None
62 self.platforms = _GetPlatforms(json)
63 toplevel_origin = Origin(from_client=True, from_json=True)
64 self.types = _GetTypes(self, json, self, toplevel_origin)
65 self.functions = _GetFunctions(self, json, self)
66 self.events = _GetEvents(self, json, self)
67 self.properties = _GetProperties(self, json, self, toplevel_origin)
68 if include_compiler_options:
69 self.compiler_options = json.get('compiler_options', {})
70
71 class Origin(object):
72 """Stores the possible origin of model object as a pair of bools. These are:
73
74 |from_client| indicating that instances can originate from users of
75 generated code (for example, function results), or
76 |from_json| indicating that instances can originate from the JSON (for
77 example, function parameters)
78
79 It is possible for model objects to originate from both the client and json,
80 for example Types defined in the top-level schema, in which case both
81 |from_client| and |from_json| would be True.
82 """
83 def __init__(self, from_client=False, from_json=False):
84 if not from_client and not from_json:
85 raise ValueError('One of from_client or from_json must be true')
86 self.from_client = from_client
87 self.from_json = from_json
88
89 class Type(object):
90 """A Type defined in the json.
91
92 Properties:
93 - |name| the type name
94 - |namespace| the Type's namespace
95 - |description| the description of the type (if provided)
96 - |properties| a map of property unix_names to their model.Property
97 - |functions| a map of function names to their model.Function
98 - |events| a map of event names to their model.Event
99 - |origin| the Origin of the type
100 - |property_type| the PropertyType of this Type
101 - |item_type| if this is an array, the type of items in the array
102 - |simple_name| the name of this Type without a namespace
103 - |additional_properties| the type of the additional properties, if any is
104 specified
105 """
106 def __init__(self,
107 parent,
108 name,
109 json,
110 namespace,
111 origin):
112 self.name = name
113 self.namespace = namespace
114 self.simple_name = _StripNamespace(self.name, namespace)
115 self.unix_name = UnixName(self.name)
116 self.description = json.get('description', None)
117 self.origin = origin
118 self.parent = parent
119 self.instance_of = json.get('isInstanceOf', None)
120
121 # TODO(kalman): Only objects need functions/events/properties, but callers
122 # assume that all types have them. Fix this.
123 self.functions = _GetFunctions(self, json, namespace)
124 self.events = _GetEvents(self, json, namespace)
125 self.properties = _GetProperties(self, json, namespace, origin)
126
127 json_type = json.get('type', None)
128 if json_type == 'array':
129 self.property_type = PropertyType.ARRAY
130 self.item_type = Type(
131 self, '%sType' % name, json['items'], namespace, origin)
132 elif '$ref' in json:
133 self.property_type = PropertyType.REF
134 self.ref_type = json['$ref']
135 elif 'enum' in json and json_type == 'string':
136 self.property_type = PropertyType.ENUM
137 self.enum_values = [value for value in json['enum']]
138 elif json_type == 'any':
139 self.property_type = PropertyType.ANY
140 elif json_type == 'binary':
141 self.property_type = PropertyType.BINARY
142 elif json_type == 'boolean':
143 self.property_type = PropertyType.BOOLEAN
144 elif json_type == 'integer':
145 self.property_type = PropertyType.INTEGER
146 elif (json_type == 'double' or
147 json_type == 'number'):
148 self.property_type = PropertyType.DOUBLE
149 elif json_type == 'string':
150 self.property_type = PropertyType.STRING
151 elif 'choices' in json:
152 self.property_type = PropertyType.CHOICES
153 self.choices = [Type(self,
154 # The name of the choice type - there had better be
155 # either a type or a $ref specified for the choice.
156 json.get('type', json.get('$ref')),
157 json,
158 namespace,
159 origin)
160 for json in json['choices']]
161 elif json_type == 'object':
162 if not (
163 'properties' in json or
164 'additionalProperties' in json or
165 'functions' in json or
166 'events' in json):
167 raise ParseException(self, name + " has no properties or functions")
168 self.property_type = PropertyType.OBJECT
169 additional_properties_json = json.get('additionalProperties', None)
170 if additional_properties_json is not None:
171 self.additional_properties = Type(self,
172 'additionalProperties',
173 additional_properties_json,
174 namespace,
175 origin)
176 else:
177 self.additional_properties = None
178 elif json_type == 'function':
179 self.property_type = PropertyType.FUNCTION
180 # Sometimes we might have an unnamed function, e.g. if it's a property
181 # of an object. Use the name of the property in that case.
182 function_name = json.get('name', name)
183 self.function = Function(self, function_name, json, namespace, origin)
184 else:
185 raise ParseException(self, 'Unsupported JSON type %s' % json_type)
186
187 class Function(object):
188 """A Function defined in the API.
189
190 Properties:
191 - |name| the function name
192 - |platforms| if not None, the list of platforms that the function is
193 available to
194 - |params| a list of parameters to the function (order matters). A separate
195 parameter is used for each choice of a 'choices' parameter
196 - |description| a description of the function (if provided)
197 - |callback| the callback parameter to the function. There should be exactly
198 one
199 - |optional| whether the Function is "optional"; this only makes sense to be
200 present when the Function is representing a callback property
201 - |simple_name| the name of this Function without a namespace
202 """
203 def __init__(self,
204 parent,
205 name,
206 json,
207 namespace,
208 origin):
209 self.name = name
210 self.simple_name = _StripNamespace(self.name, namespace)
211 self.platforms = _GetPlatforms(json)
212 self.params = []
213 self.description = json.get('description')
214 self.callback = None
215 self.optional = json.get('optional', False)
216 self.parent = parent
217 self.nocompile = json.get('nocompile')
218 options = json.get('options', {})
219 self.conditions = options.get('conditions', [])
220 self.actions = options.get('actions', [])
221 self.supports_listeners = options.get('supportsListeners', True)
222 self.supports_rules = options.get('supportsRules', False)
223
224 def GeneratePropertyFromParam(p):
225 return Property.FromJSON(self, p['name'], p, namespace, origin)
226
227 self.filters = [GeneratePropertyFromParam(filter)
228 for filter in json.get('filters', [])]
229 callback_param = None
230 for param in json.get('parameters', []):
231 if param.get('type') == 'function':
232 if callback_param:
233 # No ParseException because the webstore has this.
234 # Instead, pretend all intermediate callbacks are properties.
235 self.params.append(GeneratePropertyFromParam(callback_param))
236 callback_param = param
237 else:
238 self.params.append(GeneratePropertyFromParam(param))
239
240 if callback_param:
241 self.callback = Function(self,
242 callback_param['name'],
243 callback_param,
244 namespace,
245 Origin(from_client=True))
246
247 self.returns = None
248 if 'returns' in json:
249 self.returns = Property.FromJSON(
250 self, 'return', json['returns'], namespace, origin)
251
252 class Property(object):
253 """A property of a type OR a parameter to a function.
254 Properties:
255 - |name| name of the property as in the json. This shouldn't change since
256 it is the key used to access DictionaryValues
257 - |unix_name| the unix_style_name of the property. Used as variable name
258 - |optional| a boolean representing whether the property is optional
259 - |description| a description of the property (if provided)
260 - |type_| the model.Type of this property
261 - |simple_name| the name of this Property without a namespace
262 """
263
264 @staticmethod
265 def FromJSON(parent, name, json, namespace, origin):
266 """Creates a Property from JSON.
267 """
268 opt_args = {}
269 if 'description' in json:
270 opt_args['description'] = json['description']
271 if 'optional' in json:
272 opt_args['optional'] = json.get('optional')
273 if 'isInstanceOf' in json:
274 opt_args['instance_of'] = json.get('isInstanceOf')
275
276 # HACK: only support very specific value types.
277 is_allowed_value = (
278 '$ref' not in json and
279 ('type' not in json or json['type'] == 'integer'
280 or json['type'] == 'string'))
281
282 if 'value' in json and is_allowed_value:
283 value = json['value']
284 opt_args['value'] = value
285 if 'type' not in json:
286 # Sometimes the type of the value is left out, and we need to figure
287 # it out for ourselves.
288 if isinstance(value, int):
289 json['type'] = 'integer'
290 elif isinstance(value, basestring):
291 json['type'] = 'string'
292 else:
293 # TODO(kalman): support more types as necessary.
294 raise ParseException(
295 parent, '"%s" is not a supported type for "value"' % type(value))
296
297 type_ = Type(parent, name, json, namespace, origin)
298 return Property(parent,
299 name,
300 namespace,
301 type_,
302 origin,
303 **opt_args);
304
305 def __init__(self,
306 parent,
307 name,
308 namespace,
309 type_,
310 origin,
311 description=None,
312 optional=False,
313 returns=None,
314 instance_of=None,
315 value=None):
316 """Directly initializes the fields of the Property.
317 """
318 self.name = name
319 self.simple_name = _StripNamespace(self.name, namespace)
320 self._unix_name = UnixName(self.name)
321 self._unix_name_used = False
322 self.optional = optional
323 self.description = description
324 self.parent = parent
325 self.origin = origin
326 if not isinstance(type_, Type):
327 raise ValueError("not Type: %s" % type_)
328 self.type_ = type_
329 self.returns = returns
330 if instance_of is not None:
331 self.instance_of = instance_of
332 self.value = value
333
334 def GetUnixName(self):
335 """Gets the property's unix_name. Raises AttributeError if not set.
336 """
337 if not self._unix_name:
338 raise AttributeError('No unix_name set on %s' % self.name)
339 self._unix_name_used = True
340 return self._unix_name
341
342 def SetUnixName(self, unix_name):
343 """Set the property's unix_name. Raises AttributeError if the unix_name has
344 already been used (GetUnixName has been called).
345 """
346 if unix_name == self._unix_name:
347 return
348 if self._unix_name_used:
349 raise AttributeError(
350 'Cannot set the unix_name on %s; '
351 'it is already used elsewhere as %s' %
352 (self.name, self._unix_name))
353 self._unix_name = unix_name
354
355 unix_name = property(GetUnixName, SetUnixName)
356
357 class _Enum(object):
358 """Superclass for enum types with a "name" field, setting up repr/eq/ne.
359 Enums need to do this so that equality/non-equality work over pickling.
360 """
361
362 @staticmethod
363 def GetAll(cls):
364 """Yields all _Enum objects declared in |cls|.
365 """
366 for prop_key in dir(cls):
367 prop_value = getattr(cls, prop_key)
368 if isinstance(prop_value, _Enum):
369 yield prop_value
370
371 def __init__(self, name):
372 self.name = name
373
374 def __repr(self):
375 return self.name
376
377 def __eq__(self, other):
378 return type(other) == type(self) and other.name == self.name
379
380 def __ne__(self, other):
381 return not (self == other)
382
383 class _PropertyTypeInfo(_Enum):
384 def __init__(self, is_fundamental, name):
385 _Enum.__init__(self, name)
386 self.is_fundamental = is_fundamental
387
388 class PropertyType(object):
389 """Enum of different types of properties/parameters.
390 """
391 INTEGER = _PropertyTypeInfo(True, "integer")
392 INT64 = _PropertyTypeInfo(True, "int64")
393 DOUBLE = _PropertyTypeInfo(True, "double")
394 BOOLEAN = _PropertyTypeInfo(True, "boolean")
395 STRING = _PropertyTypeInfo(True, "string")
396 ENUM = _PropertyTypeInfo(False, "enum")
397 ARRAY = _PropertyTypeInfo(False, "array")
398 REF = _PropertyTypeInfo(False, "ref")
399 CHOICES = _PropertyTypeInfo(False, "choices")
400 OBJECT = _PropertyTypeInfo(False, "object")
401 FUNCTION = _PropertyTypeInfo(False, "function")
402 BINARY = _PropertyTypeInfo(False, "binary")
403 ANY = _PropertyTypeInfo(False, "any")
404
405 def UnixName(name):
406 """Returns the unix_style name for a given lowerCamelCase string.
407 """
408 # First replace any lowerUpper patterns with lower_Upper.
409 s1 = re.sub('([a-z])([A-Z])', r'\1_\2', name)
410 # Now replace any ACMEWidgets patterns with ACME_Widgets
411 s2 = re.sub('([A-Z]+)([A-Z][a-z])', r'\1_\2', s1)
412 # Finally, replace any remaining periods, and make lowercase.
413 return s2.replace('.', '_').lower()
414
415 def _StripNamespace(name, namespace):
416 if name.startswith(namespace.name + '.'):
417 return name[len(namespace.name + '.'):]
418 return name
419
420 def _GetModelHierarchy(entity):
421 """Returns the hierarchy of the given model entity."""
422 hierarchy = []
423 while entity is not None:
424 hierarchy.append(getattr(entity, 'name', repr(entity)))
425 if isinstance(entity, Namespace):
426 hierarchy.insert(0, ' in %s' % entity.source_file)
427 entity = getattr(entity, 'parent', None)
428 hierarchy.reverse()
429 return hierarchy
430
431 def _GetTypes(parent, json, namespace, origin):
432 """Creates Type objects extracted from |json|.
433 """
434 types = OrderedDict()
435 for type_json in json.get('types', []):
436 type_ = Type(parent, type_json['id'], type_json, namespace, origin)
437 types[type_.name] = type_
438 return types
439
440 def _GetFunctions(parent, json, namespace):
441 """Creates Function objects extracted from |json|.
442 """
443 functions = OrderedDict()
444 for function_json in json.get('functions', []):
445 function = Function(parent,
446 function_json['name'],
447 function_json,
448 namespace,
449 Origin(from_json=True))
450 functions[function.name] = function
451 return functions
452
453 def _GetEvents(parent, json, namespace):
454 """Creates Function objects generated from the events in |json|.
455 """
456 events = OrderedDict()
457 for event_json in json.get('events', []):
458 event = Function(parent,
459 event_json['name'],
460 event_json,
461 namespace,
462 Origin(from_client=True))
463 events[event.name] = event
464 return events
465
466 def _GetProperties(parent, json, namespace, origin):
467 """Generates Property objects extracted from |json|.
468 """
469 properties = OrderedDict()
470 for name, property_json in json.get('properties', {}).items():
471 properties[name] = Property.FromJSON(
472 parent, name, property_json, namespace, origin)
473 return properties
474
475 class _PlatformInfo(_Enum):
476 def __init__(self, name):
477 _Enum.__init__(self, name)
478
479 class Platforms(object):
480 """Enum of the possible platforms.
481 """
482 CHROMEOS = _PlatformInfo("chromeos")
483 CHROMEOS_TOUCH = _PlatformInfo("chromeos_touch")
484 LINUX = _PlatformInfo("linux")
485 MAC = _PlatformInfo("mac")
486 WIN = _PlatformInfo("win")
487
488 def _GetPlatforms(json):
489 if 'platforms' not in json:
490 return None
491 platforms = []
492 for platform_name in json['platforms']:
493 for platform_enum in _Enum.GetAll(Platforms):
494 if platform_name == platform_enum.name:
495 platforms.append(platform_enum)
496 break
497 return platforms
OLDNEW
« no previous file with comments | « third_party/chrome/tools/json_schema_test.py ('k') | third_party/chrome/tools/model_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698