blob: e0147b5b4889fb2a86b59a74648a4deb24316051 [file] [log] [blame]
[email protected]15f08dd2012-01-27 07:29:481# 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
5import os.path
6
[email protected]b2fad3f2012-11-20 02:13:247from json_parse import OrderedDict
[email protected]cb5670c2013-04-10 06:31:088from memoize import memoize
[email protected]b2fad3f2012-11-20 02:13:249
[email protected]0853bcf2013-11-06 05:14:3310
[email protected]116f0a3a2012-04-19 04:22:3811class 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
[email protected]0853bcf2013-11-06 05:14:3320
[email protected]15f08dd2012-01-27 07:29:4821class Model(object):
22 """Model of all namespaces that comprise an API.
[email protected]cfe484902012-02-15 14:52:3223
24 Properties:
25 - |namespaces| a map of a namespace name to its model.Namespace
[email protected]15f08dd2012-01-27 07:29:4826 """
rdevlin.cronin00f1fc22015-04-06 17:19:1827 def __init__(self, allow_inline_enums=True):
28 self._allow_inline_enums = allow_inline_enums
[email protected]15f08dd2012-01-27 07:29:4829 self.namespaces = {}
30
lfg8a1bee32014-08-29 03:56:2831 def AddNamespace(self,
32 json,
33 source_file,
34 include_compiler_options=False,
35 environment=None):
[email protected]a1f774972012-04-17 02:11:0936 """Add a namespace's json to the model and returns the namespace.
[email protected]15f08dd2012-01-27 07:29:4837 """
[email protected]4636c832013-01-11 02:10:1138 namespace = Namespace(json,
39 source_file,
lfg8a1bee32014-08-29 03:56:2840 include_compiler_options=include_compiler_options,
rdevlin.cronin00f1fc22015-04-06 17:19:1841 environment=environment,
42 allow_inline_enums=self._allow_inline_enums)
[email protected]15f08dd2012-01-27 07:29:4843 self.namespaces[namespace.name] = namespace
44 return namespace
45
[email protected]0853bcf2013-11-06 05:14:3346
47def CreateFeature(name, model):
48 if isinstance(model, dict):
49 return SimpleFeature(name, model)
50 return ComplexFeature(name, [SimpleFeature(name, child) for child in model])
51
52
53class ComplexFeature(object):
54 """A complex feature which may be made of several simple features.
55
56 Properties:
57 - |name| the name of the feature
58 - |unix_name| the unix_name of the feature
59 - |feature_list| a list of simple features which make up the feature
60 """
61 def __init__(self, feature_name, features):
62 self.name = feature_name
63 self.unix_name = UnixName(self.name)
64 self.feature_list = features
65
66class SimpleFeature(object):
67 """A simple feature, which can make up a complex feature, as specified in
68 files such as chrome/common/extensions/api/_permission_features.json.
69
70 Properties:
71 - |name| the name of the feature
72 - |unix_name| the unix_name of the feature
73 - |channel| the channel where the feature is released
74 - |extension_types| the types which can use the feature
75 - |whitelist| a list of extensions allowed to use the feature
76 """
77 def __init__(self, feature_name, feature_def):
78 self.name = feature_name
79 self.unix_name = UnixName(self.name)
80 self.channel = feature_def['channel']
81 self.extension_types = feature_def['extension_types']
82 self.whitelist = feature_def.get('whitelist')
83
84
[email protected]15f08dd2012-01-27 07:29:4885class Namespace(object):
86 """An API namespace.
[email protected]cfe484902012-02-15 14:52:3287
88 Properties:
89 - |name| the name of the namespace
[email protected]8426f8d72013-06-23 04:24:5590 - |description| the description of the namespace
[email protected]29163bc2014-01-27 21:50:5991 - |deprecated| a reason and possible alternative for a deprecated api
[email protected]712eca0f2012-02-21 01:13:0792 - |unix_name| the unix_name of the namespace
93 - |source_file| the file that contained the namespace definition
94 - |source_file_dir| the directory component of |source_file|
95 - |source_file_filename| the filename component of |source_file|
[email protected]4636c832013-01-11 02:10:1196 - |platforms| if not None, the list of platforms that the namespace is
97 available to
[email protected]cfe484902012-02-15 14:52:3298 - |types| a map of type names to their model.Type
99 - |functions| a map of function names to their model.Function
[email protected]b741e8f2012-07-16 21:47:24100 - |events| a map of event names to their model.Function
[email protected]116f0a3a2012-04-19 04:22:38101 - |properties| a map of property names to their model.Property
[email protected]bee7a7932013-08-12 23:45:38102 - |compiler_options| the compiler_options dict, only not empty if
[email protected]4636c832013-01-11 02:10:11103 |include_compiler_options| is True
[email protected]15f08dd2012-01-27 07:29:48104 """
lfg8a1bee32014-08-29 03:56:28105 def __init__(self,
106 json,
107 source_file,
108 include_compiler_options=False,
rdevlin.cronin00f1fc22015-04-06 17:19:18109 environment=None,
110 allow_inline_enums=True):
[email protected]15f08dd2012-01-27 07:29:48111 self.name = json['namespace']
[email protected]8426f8d72013-06-23 04:24:55112 if 'description' not in json:
[email protected]a081aeb2013-07-09 20:52:47113 # TODO(kalman): Go back to throwing an error here.
114 print('%s must have a "description" field. This will appear '
[email protected]8426f8d72013-06-23 04:24:55115 'on the API summary page.' % self.name)
[email protected]a081aeb2013-07-09 20:52:47116 json['description'] = ''
117 self.description = json['description']
[email protected]29163bc2014-01-27 21:50:59118 self.deprecated = json.get('deprecated', None)
[email protected]4ba6bdc2012-06-18 20:40:14119 self.unix_name = UnixName(self.name)
[email protected]15f08dd2012-01-27 07:29:48120 self.source_file = source_file
121 self.source_file_dir, self.source_file_filename = os.path.split(source_file)
[email protected]70e263e2014-02-09 10:45:09122 self.short_filename = os.path.basename(source_file).split('.')[0]
[email protected]feba21e2012-03-02 15:05:27123 self.parent = None
rdevlin.cronin00f1fc22015-04-06 17:19:18124 self.allow_inline_enums = allow_inline_enums
[email protected]4636c832013-01-11 02:10:11125 self.platforms = _GetPlatforms(json)
[email protected]242d5e7a2013-01-17 06:50:31126 toplevel_origin = Origin(from_client=True, from_json=True)
127 self.types = _GetTypes(self, json, self, toplevel_origin)
128 self.functions = _GetFunctions(self, json, self)
129 self.events = _GetEvents(self, json, self)
130 self.properties = _GetProperties(self, json, self, toplevel_origin)
[email protected]03e5ffe2013-11-07 01:54:53131 if include_compiler_options:
132 self.compiler_options = json.get('compiler_options', {})
133 else:
134 self.compiler_options = {}
lfg8a1bee32014-08-29 03:56:28135 self.environment = environment
[email protected]0b022482013-10-03 19:38:05136 self.documentation_options = json.get('documentation_options', {})
[email protected]15f08dd2012-01-27 07:29:48137
[email protected]0853bcf2013-11-06 05:14:33138
[email protected]242d5e7a2013-01-17 06:50:31139class Origin(object):
140 """Stores the possible origin of model object as a pair of bools. These are:
141
142 |from_client| indicating that instances can originate from users of
143 generated code (for example, function results), or
144 |from_json| indicating that instances can originate from the JSON (for
145 example, function parameters)
146
147 It is possible for model objects to originate from both the client and json,
148 for example Types defined in the top-level schema, in which case both
149 |from_client| and |from_json| would be True.
150 """
151 def __init__(self, from_client=False, from_json=False):
152 if not from_client and not from_json:
153 raise ValueError('One of from_client or from_json must be true')
154 self.from_client = from_client
155 self.from_json = from_json
156
[email protected]0853bcf2013-11-06 05:14:33157
[email protected]15f08dd2012-01-27 07:29:48158class Type(object):
159 """A Type defined in the json.
[email protected]cfe484902012-02-15 14:52:32160
161 Properties:
162 - |name| the type name
[email protected]242d5e7a2013-01-17 06:50:31163 - |namespace| the Type's namespace
[email protected]cfe484902012-02-15 14:52:32164 - |description| the description of the type (if provided)
[email protected]feba21e2012-03-02 15:05:27165 - |properties| a map of property unix_names to their model.Property
166 - |functions| a map of function names to their model.Function
[email protected]d32b8e52012-07-23 18:40:08167 - |events| a map of event names to their model.Event
[email protected]242d5e7a2013-01-17 06:50:31168 - |origin| the Origin of the type
169 - |property_type| the PropertyType of this Type
[email protected]cf6d0b32012-04-12 04:30:22170 - |item_type| if this is an array, the type of items in the array
[email protected]0b255f002012-10-05 01:58:47171 - |simple_name| the name of this Type without a namespace
[email protected]242d5e7a2013-01-17 06:50:31172 - |additional_properties| the type of the additional properties, if any is
173 specified
[email protected]15f08dd2012-01-27 07:29:48174 """
[email protected]242d5e7a2013-01-17 06:50:31175 def __init__(self,
176 parent,
177 name,
178 json,
179 namespace,
180 origin):
181 self.name = name
182 self.namespace = namespace
183 self.simple_name = _StripNamespace(self.name, namespace)
184 self.unix_name = UnixName(self.name)
185 self.description = json.get('description', None)
186 self.origin = origin
187 self.parent = parent
188 self.instance_of = json.get('isInstanceOf', None)
189
190 # TODO(kalman): Only objects need functions/events/properties, but callers
191 # assume that all types have them. Fix this.
192 self.functions = _GetFunctions(self, json, namespace)
193 self.events = _GetEvents(self, json, namespace)
194 self.properties = _GetProperties(self, json, namespace, origin)
195
196 json_type = json.get('type', None)
197 if json_type == 'array':
198 self.property_type = PropertyType.ARRAY
199 self.item_type = Type(
200 self, '%sType' % name, json['items'], namespace, origin)
201 elif '$ref' in json:
202 self.property_type = PropertyType.REF
203 self.ref_type = json['$ref']
204 elif 'enum' in json and json_type == 'string':
rdevlin.cronin00f1fc22015-04-06 17:19:18205 if not namespace.allow_inline_enums and not isinstance(parent, Namespace):
206 raise ParseException(
207 self,
208 'Inline enum "%s" found in namespace "%s". These are not allowed. '
209 'See crbug.com/472279' % (name, namespace.name))
[email protected]242d5e7a2013-01-17 06:50:31210 self.property_type = PropertyType.ENUM
[email protected]7f138d42013-11-01 05:27:27211 self.enum_values = [EnumValue(value) for value in json['enum']]
[email protected]fad5da262014-05-15 08:04:00212 self.cpp_enum_prefix_override = json.get('cpp_enum_prefix_override', None)
[email protected]242d5e7a2013-01-17 06:50:31213 elif json_type == 'any':
214 self.property_type = PropertyType.ANY
215 elif json_type == 'binary':
216 self.property_type = PropertyType.BINARY
217 elif json_type == 'boolean':
218 self.property_type = PropertyType.BOOLEAN
219 elif json_type == 'integer':
220 self.property_type = PropertyType.INTEGER
221 elif (json_type == 'double' or
222 json_type == 'number'):
223 self.property_type = PropertyType.DOUBLE
224 elif json_type == 'string':
225 self.property_type = PropertyType.STRING
226 elif 'choices' in json:
227 self.property_type = PropertyType.CHOICES
[email protected]e11b9aa2013-07-13 00:05:42228 def generate_type_name(type_json):
229 if 'items' in type_json:
230 return '%ss' % generate_type_name(type_json['items'])
231 if '$ref' in type_json:
232 return type_json['$ref']
233 if 'type' in type_json:
234 return type_json['type']
235 return None
236 self.choices = [
237 Type(self,
238 generate_type_name(choice) or 'choice%s' % i,
239 choice,
240 namespace,
241 origin)
242 for i, choice in enumerate(json['choices'])]
[email protected]242d5e7a2013-01-17 06:50:31243 elif json_type == 'object':
[email protected]cf6d0b32012-04-12 04:30:22244 if not (
[email protected]117afcf2013-08-30 02:38:09245 'isInstanceOf' in json or
[email protected]cf6d0b32012-04-12 04:30:22246 'properties' in json or
247 'additionalProperties' in json or
[email protected]e975d112012-07-31 22:12:43248 'functions' in json or
249 'events' in json):
[email protected]116f0a3a2012-04-19 04:22:38250 raise ParseException(self, name + " has no properties or functions")
[email protected]242d5e7a2013-01-17 06:50:31251 self.property_type = PropertyType.OBJECT
252 additional_properties_json = json.get('additionalProperties', None)
253 if additional_properties_json is not None:
254 self.additional_properties = Type(self,
255 'additionalProperties',
256 additional_properties_json,
257 namespace,
258 origin)
259 else:
260 self.additional_properties = None
261 elif json_type == 'function':
262 self.property_type = PropertyType.FUNCTION
263 # Sometimes we might have an unnamed function, e.g. if it's a property
264 # of an object. Use the name of the property in that case.
265 function_name = json.get('name', name)
266 self.function = Function(self, function_name, json, namespace, origin)
267 else:
268 raise ParseException(self, 'Unsupported JSON type %s' % json_type)
[email protected]15f08dd2012-01-27 07:29:48269
[email protected]0853bcf2013-11-06 05:14:33270
[email protected]15f08dd2012-01-27 07:29:48271class Function(object):
272 """A Function defined in the API.
[email protected]cfe484902012-02-15 14:52:32273
274 Properties:
275 - |name| the function name
[email protected]4636c832013-01-11 02:10:11276 - |platforms| if not None, the list of platforms that the function is
277 available to
[email protected]cfe484902012-02-15 14:52:32278 - |params| a list of parameters to the function (order matters). A separate
[email protected]4636c832013-01-11 02:10:11279 parameter is used for each choice of a 'choices' parameter
[email protected]9c7830ba2013-10-17 19:28:46280 - |deprecated| a reason and possible alternative for a deprecated function
[email protected]cfe484902012-02-15 14:52:32281 - |description| a description of the function (if provided)
282 - |callback| the callback parameter to the function. There should be exactly
[email protected]4636c832013-01-11 02:10:11283 one
[email protected]4b3f7852012-07-17 06:33:30284 - |optional| whether the Function is "optional"; this only makes sense to be
[email protected]4636c832013-01-11 02:10:11285 present when the Function is representing a callback property
[email protected]0b255f002012-10-05 01:58:47286 - |simple_name| the name of this Function without a namespace
[email protected]32096af2013-02-06 01:29:31287 - |returns| the return type of the function; None if the function does not
288 return a value
[email protected]15f08dd2012-01-27 07:29:48289 """
[email protected]0b255f002012-10-05 01:58:47290 def __init__(self,
291 parent,
[email protected]242d5e7a2013-01-17 06:50:31292 name,
[email protected]0b255f002012-10-05 01:58:47293 json,
294 namespace,
[email protected]242d5e7a2013-01-17 06:50:31295 origin):
296 self.name = name
[email protected]0b255f002012-10-05 01:58:47297 self.simple_name = _StripNamespace(self.name, namespace)
[email protected]4636c832013-01-11 02:10:11298 self.platforms = _GetPlatforms(json)
[email protected]15f08dd2012-01-27 07:29:48299 self.params = []
[email protected]feba21e2012-03-02 15:05:27300 self.description = json.get('description')
[email protected]9c7830ba2013-10-17 19:28:46301 self.deprecated = json.get('deprecated')
[email protected]15f08dd2012-01-27 07:29:48302 self.callback = None
[email protected]4b3f7852012-07-17 06:33:30303 self.optional = json.get('optional', False)
[email protected]feba21e2012-03-02 15:05:27304 self.parent = parent
[email protected]a9ead752012-05-24 02:08:45305 self.nocompile = json.get('nocompile')
tommyclid3f292022016-03-02 00:23:10306 self.nodefine = json.get('nodefine')
[email protected]c0718352012-08-21 02:34:24307 options = json.get('options', {})
308 self.conditions = options.get('conditions', [])
309 self.actions = options.get('actions', [])
310 self.supports_listeners = options.get('supportsListeners', True)
311 self.supports_rules = options.get('supportsRules', False)
[email protected]c18d6712013-10-03 18:39:30312 self.supports_dom = options.get('supportsDom', False)
[email protected]242d5e7a2013-01-17 06:50:31313
[email protected]c0718352012-08-21 02:34:24314 def GeneratePropertyFromParam(p):
[email protected]627b54d2013-02-04 23:53:59315 return Property(self, p['name'], p, namespace, origin)
[email protected]db943992012-08-02 14:02:54316
thestigb179975132015-01-15 23:19:23317 self.filters = [GeneratePropertyFromParam(filter_instance)
318 for filter_instance in json.get('filters', [])]
[email protected]db943992012-08-02 14:02:54319 callback_param = None
[email protected]b741e8f2012-07-16 21:47:24320 for param in json.get('parameters', []):
[email protected]15f08dd2012-01-27 07:29:48321 if param.get('type') == 'function':
[email protected]db943992012-08-02 14:02:54322 if callback_param:
323 # No ParseException because the webstore has this.
324 # Instead, pretend all intermediate callbacks are properties.
325 self.params.append(GeneratePropertyFromParam(callback_param))
326 callback_param = param
[email protected]15f08dd2012-01-27 07:29:48327 else:
[email protected]db943992012-08-02 14:02:54328 self.params.append(GeneratePropertyFromParam(param))
329
330 if callback_param:
[email protected]0b255f002012-10-05 01:58:47331 self.callback = Function(self,
[email protected]242d5e7a2013-01-17 06:50:31332 callback_param['name'],
[email protected]0b255f002012-10-05 01:58:47333 callback_param,
334 namespace,
[email protected]242d5e7a2013-01-17 06:50:31335 Origin(from_client=True))
[email protected]db943992012-08-02 14:02:54336
[email protected]491e60d32012-07-20 01:03:13337 self.returns = None
338 if 'returns' in json:
[email protected]627b54d2013-02-04 23:53:59339 self.returns = Type(self,
340 '%sReturnType' % name,
341 json['returns'],
342 namespace,
343 origin)
[email protected]15f08dd2012-01-27 07:29:48344
[email protected]0853bcf2013-11-06 05:14:33345
[email protected]15f08dd2012-01-27 07:29:48346class Property(object):
347 """A property of a type OR a parameter to a function.
[email protected]cfe484902012-02-15 14:52:32348 Properties:
349 - |name| name of the property as in the json. This shouldn't change since
350 it is the key used to access DictionaryValues
351 - |unix_name| the unix_style_name of the property. Used as variable name
352 - |optional| a boolean representing whether the property is optional
353 - |description| a description of the property (if provided)
[email protected]242d5e7a2013-01-17 06:50:31354 - |type_| the model.Type of this property
[email protected]0b255f002012-10-05 01:58:47355 - |simple_name| the name of this Property without a namespace
[email protected]04afd692013-11-14 19:35:04356 - |deprecated| a reason and possible alternative for a deprecated property
[email protected]15f08dd2012-01-27 07:29:48357 """
[email protected]627b54d2013-02-04 23:53:59358 def __init__(self, parent, name, json, namespace, origin):
[email protected]242d5e7a2013-01-17 06:50:31359 """Creates a Property from JSON.
360 """
[email protected]627b54d2013-02-04 23:53:59361 self.parent = parent
362 self.name = name
363 self._unix_name = UnixName(self.name)
364 self._unix_name_used = False
365 self.origin = origin
366 self.simple_name = _StripNamespace(self.name, namespace)
367 self.description = json.get('description', None)
368 self.optional = json.get('optional', None)
369 self.instance_of = json.get('isInstanceOf', None)
[email protected]04afd692013-11-14 19:35:04370 self.deprecated = json.get('deprecated')
[email protected]242d5e7a2013-01-17 06:50:31371
372 # HACK: only support very specific value types.
373 is_allowed_value = (
374 '$ref' not in json and
375 ('type' not in json or json['type'] == 'integer'
376 or json['type'] == 'string'))
377
[email protected]627b54d2013-02-04 23:53:59378 self.value = None
[email protected]242d5e7a2013-01-17 06:50:31379 if 'value' in json and is_allowed_value:
[email protected]627b54d2013-02-04 23:53:59380 self.value = json['value']
[email protected]242d5e7a2013-01-17 06:50:31381 if 'type' not in json:
382 # Sometimes the type of the value is left out, and we need to figure
383 # it out for ourselves.
[email protected]627b54d2013-02-04 23:53:59384 if isinstance(self.value, int):
[email protected]242d5e7a2013-01-17 06:50:31385 json['type'] = 'integer'
[email protected]627b54d2013-02-04 23:53:59386 elif isinstance(self.value, basestring):
[email protected]242d5e7a2013-01-17 06:50:31387 json['type'] = 'string'
388 else:
389 # TODO(kalman): support more types as necessary.
390 raise ParseException(
[email protected]627b54d2013-02-04 23:53:59391 parent,
392 '"%s" is not a supported type for "value"' % type(self.value))
[email protected]242d5e7a2013-01-17 06:50:31393
[email protected]627b54d2013-02-04 23:53:59394 self.type_ = Type(parent, name, json, namespace, origin)
[email protected]cfe484902012-02-15 14:52:32395
396 def GetUnixName(self):
397 """Gets the property's unix_name. Raises AttributeError if not set.
398 """
[email protected]116f0a3a2012-04-19 04:22:38399 if not self._unix_name:
[email protected]cfe484902012-02-15 14:52:32400 raise AttributeError('No unix_name set on %s' % self.name)
401 self._unix_name_used = True
402 return self._unix_name
403
404 def SetUnixName(self, unix_name):
405 """Set the property's unix_name. Raises AttributeError if the unix_name has
406 already been used (GetUnixName has been called).
407 """
408 if unix_name == self._unix_name:
409 return
410 if self._unix_name_used:
411 raise AttributeError(
412 'Cannot set the unix_name on %s; '
413 'it is already used elsewhere as %s' %
414 (self.name, self._unix_name))
415 self._unix_name = unix_name
416
417 unix_name = property(GetUnixName, SetUnixName)
[email protected]15f08dd2012-01-27 07:29:48418
[email protected]7f138d42013-11-01 05:27:27419class EnumValue(object):
420 """A single value from an enum.
421 Properties:
422 - |name| name of the property as in the json.
423 - |description| a description of the property (if provided)
424 """
425 def __init__(self, json):
426 if isinstance(json, dict):
427 self.name = json['name']
428 self.description = json.get('description')
429 else:
430 self.name = json
431 self.description = None
432
[email protected]fad5da262014-05-15 08:04:00433 def CamelName(self):
434 return CamelName(self.name)
435
[email protected]4636c832013-01-11 02:10:11436class _Enum(object):
437 """Superclass for enum types with a "name" field, setting up repr/eq/ne.
438 Enums need to do this so that equality/non-equality work over pickling.
[email protected]fe027682012-08-21 01:51:34439 """
[email protected]4636c832013-01-11 02:10:11440 @staticmethod
441 def GetAll(cls):
442 """Yields all _Enum objects declared in |cls|.
443 """
444 for prop_key in dir(cls):
445 prop_value = getattr(cls, prop_key)
446 if isinstance(prop_value, _Enum):
447 yield prop_value
448
449 def __init__(self, name):
[email protected]fe027682012-08-21 01:51:34450 self.name = name
451
[email protected]dc5f2fecf2012-08-31 01:55:51452 def __eq__(self, other):
[email protected]4636c832013-01-11 02:10:11453 return type(other) == type(self) and other.name == self.name
[email protected]e15dccb2012-10-05 06:02:25454 def __ne__(self, other):
[email protected]e15dccb2012-10-05 06:02:25455 return not (self == other)
456
[email protected]e11b9aa2013-07-13 00:05:42457 def __repr__(self):
458 return self.name
459
460 def __str__(self):
461 return repr(self)
462
[email protected]0853bcf2013-11-06 05:14:33463
[email protected]4636c832013-01-11 02:10:11464class _PropertyTypeInfo(_Enum):
465 def __init__(self, is_fundamental, name):
466 _Enum.__init__(self, name)
467 self.is_fundamental = is_fundamental
468
rdevlin.cronin8c3a458e2015-03-24 17:07:28469 def __repr__(self):
470 return self.name
[email protected]0853bcf2013-11-06 05:14:33471
[email protected]15f08dd2012-01-27 07:29:48472class PropertyType(object):
473 """Enum of different types of properties/parameters.
474 """
[email protected]242d5e7a2013-01-17 06:50:31475 ANY = _PropertyTypeInfo(False, "any")
[email protected]e11b9aa2013-07-13 00:05:42476 ARRAY = _PropertyTypeInfo(False, "array")
477 BINARY = _PropertyTypeInfo(False, "binary")
478 BOOLEAN = _PropertyTypeInfo(True, "boolean")
479 CHOICES = _PropertyTypeInfo(False, "choices")
480 DOUBLE = _PropertyTypeInfo(True, "double")
481 ENUM = _PropertyTypeInfo(False, "enum")
482 FUNCTION = _PropertyTypeInfo(False, "function")
483 INT64 = _PropertyTypeInfo(True, "int64")
484 INTEGER = _PropertyTypeInfo(True, "integer")
485 OBJECT = _PropertyTypeInfo(False, "object")
486 REF = _PropertyTypeInfo(False, "ref")
487 STRING = _PropertyTypeInfo(True, "string")
[email protected]cfe484902012-02-15 14:52:32488
[email protected]0853bcf2013-11-06 05:14:33489
[email protected]cb5670c2013-04-10 06:31:08490@memoize
[email protected]4ba6bdc2012-06-18 20:40:14491def UnixName(name):
[email protected]cb5670c2013-04-10 06:31:08492 '''Returns the unix_style name for a given lowerCamelCase string.
493 '''
494 unix_name = []
495 for i, c in enumerate(name):
[email protected]e11b9aa2013-07-13 00:05:42496 if c.isupper() and i > 0 and name[i - 1] != '_':
[email protected]cb5670c2013-04-10 06:31:08497 # Replace lowerUpper with lower_Upper.
498 if name[i - 1].islower():
499 unix_name.append('_')
500 # Replace ACMEWidgets with ACME_Widgets
501 elif i + 1 < len(name) and name[i + 1].islower():
502 unix_name.append('_')
503 if c == '.':
504 # Replace hello.world with hello_world.
505 unix_name.append('_')
506 else:
507 # Everything is lowercase.
508 unix_name.append(c.lower())
509 return ''.join(unix_name)
[email protected]cfe484902012-02-15 14:52:32510
[email protected]0853bcf2013-11-06 05:14:33511
[email protected]fad5da262014-05-15 08:04:00512@memoize
513def CamelName(snake):
514 ''' Converts a snake_cased_string to a camelCasedOne. '''
515 pieces = snake.split('_')
516 camel = []
517 for i, piece in enumerate(pieces):
518 if i == 0:
519 camel.append(piece)
520 else:
521 camel.append(piece.capitalize())
522 return ''.join(camel)
523
524
[email protected]0b255f002012-10-05 01:58:47525def _StripNamespace(name, namespace):
526 if name.startswith(namespace.name + '.'):
527 return name[len(namespace.name + '.'):]
528 return name
529
[email protected]0853bcf2013-11-06 05:14:33530
[email protected]116f0a3a2012-04-19 04:22:38531def _GetModelHierarchy(entity):
[email protected]feba21e2012-03-02 15:05:27532 """Returns the hierarchy of the given model entity."""
533 hierarchy = []
[email protected]242d5e7a2013-01-17 06:50:31534 while entity is not None:
535 hierarchy.append(getattr(entity, 'name', repr(entity)))
536 if isinstance(entity, Namespace):
537 hierarchy.insert(0, ' in %s' % entity.source_file)
538 entity = getattr(entity, 'parent', None)
[email protected]feba21e2012-03-02 15:05:27539 hierarchy.reverse()
540 return hierarchy
[email protected]116f0a3a2012-04-19 04:22:38541
[email protected]0853bcf2013-11-06 05:14:33542
[email protected]242d5e7a2013-01-17 06:50:31543def _GetTypes(parent, json, namespace, origin):
544 """Creates Type objects extracted from |json|.
[email protected]116f0a3a2012-04-19 04:22:38545 """
[email protected]242d5e7a2013-01-17 06:50:31546 types = OrderedDict()
[email protected]116f0a3a2012-04-19 04:22:38547 for type_json in json.get('types', []):
[email protected]242d5e7a2013-01-17 06:50:31548 type_ = Type(parent, type_json['id'], type_json, namespace, origin)
549 types[type_.name] = type_
550 return types
[email protected]116f0a3a2012-04-19 04:22:38551
[email protected]0853bcf2013-11-06 05:14:33552
[email protected]242d5e7a2013-01-17 06:50:31553def _GetFunctions(parent, json, namespace):
554 """Creates Function objects extracted from |json|.
[email protected]116f0a3a2012-04-19 04:22:38555 """
[email protected]242d5e7a2013-01-17 06:50:31556 functions = OrderedDict()
[email protected]116f0a3a2012-04-19 04:22:38557 for function_json in json.get('functions', []):
[email protected]242d5e7a2013-01-17 06:50:31558 function = Function(parent,
559 function_json['name'],
560 function_json,
561 namespace,
562 Origin(from_json=True))
563 functions[function.name] = function
564 return functions
[email protected]116f0a3a2012-04-19 04:22:38565
[email protected]0853bcf2013-11-06 05:14:33566
[email protected]242d5e7a2013-01-17 06:50:31567def _GetEvents(parent, json, namespace):
568 """Creates Function objects generated from the events in |json|.
[email protected]b741e8f2012-07-16 21:47:24569 """
[email protected]242d5e7a2013-01-17 06:50:31570 events = OrderedDict()
[email protected]b741e8f2012-07-16 21:47:24571 for event_json in json.get('events', []):
[email protected]242d5e7a2013-01-17 06:50:31572 event = Function(parent,
573 event_json['name'],
574 event_json,
575 namespace,
576 Origin(from_client=True))
577 events[event.name] = event
578 return events
[email protected]b741e8f2012-07-16 21:47:24579
[email protected]0853bcf2013-11-06 05:14:33580
[email protected]242d5e7a2013-01-17 06:50:31581def _GetProperties(parent, json, namespace, origin):
582 """Generates Property objects extracted from |json|.
[email protected]116f0a3a2012-04-19 04:22:38583 """
[email protected]242d5e7a2013-01-17 06:50:31584 properties = OrderedDict()
[email protected]116f0a3a2012-04-19 04:22:38585 for name, property_json in json.get('properties', {}).items():
[email protected]627b54d2013-02-04 23:53:59586 properties[name] = Property(parent, name, property_json, namespace, origin)
[email protected]242d5e7a2013-01-17 06:50:31587 return properties
[email protected]4636c832013-01-11 02:10:11588
[email protected]0853bcf2013-11-06 05:14:33589
[email protected]4636c832013-01-11 02:10:11590class _PlatformInfo(_Enum):
591 def __init__(self, name):
592 _Enum.__init__(self, name)
593
[email protected]0853bcf2013-11-06 05:14:33594
[email protected]4636c832013-01-11 02:10:11595class Platforms(object):
596 """Enum of the possible platforms.
597 """
598 CHROMEOS = _PlatformInfo("chromeos")
599 CHROMEOS_TOUCH = _PlatformInfo("chromeos_touch")
600 LINUX = _PlatformInfo("linux")
601 MAC = _PlatformInfo("mac")
602 WIN = _PlatformInfo("win")
603
[email protected]0853bcf2013-11-06 05:14:33604
[email protected]4636c832013-01-11 02:10:11605def _GetPlatforms(json):
[email protected]e0eec63f2013-10-30 05:28:58606 if 'platforms' not in json or json['platforms'] == None:
[email protected]4636c832013-01-11 02:10:11607 return None
[email protected]e0eec63f2013-10-30 05:28:58608 # Sanity check: platforms should not be an empty list.
609 if not json['platforms']:
610 raise ValueError('"platforms" cannot be an empty list')
[email protected]4636c832013-01-11 02:10:11611 platforms = []
612 for platform_name in json['platforms']:
613 for platform_enum in _Enum.GetAll(Platforms):
614 if platform_name == platform_enum.name:
615 platforms.append(platform_enum)
616 break
617 return platforms