Chromium Code Reviews| Index: tools/json_schema_compiler/model.py | 
| diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py | 
| index d839ab977be318601d9a79232b1903271fc3ad2e..86bae1bfd4119d3e1dc68d495b40a91c692f4b97 100644 | 
| --- a/tools/json_schema_compiler/model.py | 
| +++ b/tools/json_schema_compiler/model.py | 
| @@ -40,17 +40,22 @@ class Namespace(object): | 
| """ | 
| def __init__(self, json, source_file): | 
| self.name = json['namespace'] | 
| - self.unix_name = _UnixName(self.name) | 
| + self.unix_name = UnixName(self.name) | 
| self.source_file = source_file | 
| self.source_file_dir, self.source_file_filename = os.path.split(source_file) | 
| self.types = {} | 
| self.functions = {} | 
| + self.parent = None | 
| + # TODO(calamity): Implement properties on namespaces for shared structures | 
| + # or constants across a namespace (e.g Windows::WINDOW_ID_NONE). | 
| + for property_json in json.get('properties', []): | 
| + pass | 
| for type_json in json.get('types', []): | 
| - type_ = Type(type_json) | 
| + type_ = Type(self, type_json['id'], type_json) | 
| self.types[type_.name] = type_ | 
| for function_json in json.get('functions', []): | 
| if not function_json.get('nocompile', False): | 
| - function = Function(function_json) | 
| + function = Function(self, function_json) | 
| self.functions[function.name] = function | 
| class Type(object): | 
| @@ -59,23 +64,58 @@ class Type(object): | 
| Properties: | 
| - |name| the type name | 
| - |description| the description of the type (if provided) | 
| - - |properties| a map of property names to their model.Property | 
| + - |properties| a map of property unix_names to their model.Property | 
| + - |functions| a map of function names to their model.Function | 
| - |from_client| indicates that instances of the Type can originate from the | 
| users of generated code, such as top-level types and function results | 
| - |from_json| indicates that instances of the Type can originate from the | 
| JSON (as described by the schema), such as top-level types and function | 
| parameters | 
| """ | 
| - def __init__(self, json): | 
| - self.name = json['id'] | 
| + def __init__(self, parent, name, json): | 
| + if not ( | 
| + 'properties' in json or | 
| + 'additionalProperties' in json or | 
| + 'functions' in json): | 
| + raise ParseException(name + " has no properties or functions") | 
| + self.name = name | 
| self.description = json.get('description') | 
| self.from_json = True | 
| self.from_client = True | 
| self.properties = {} | 
| - for prop_name, prop_json in json['properties'].items(): | 
| - self.properties[prop_name] = Property(prop_name, prop_json, | 
| + self.functions = {} | 
| + self.parent = parent | 
| + for function_json in json.get('functions', []): | 
| + if not function_json.get('nocompile', False): | 
| + function = Function(self, function_json) | 
| + self.functions[function.name] = function | 
| + props = [] | 
| + for prop_name, prop_json in json.get('properties', {}).items(): | 
| + # TODO(calamity): support functions (callbacks) as properties. The model | 
| + # doesn't support it yet because to h/cc generators don't -- this is | 
| + # because we'd need to hook it into a base::Callback or something. | 
| + # | 
| + # However, pragmatically it's not necessary to support them anyway, since | 
| + # the instances of functions-on-properties in the extension APIs are all | 
| + # handled in pure Javascript on the render process (and .: never reach | 
| + # C++ let alone the browser). | 
| + if prop_json.get('type') == 'function': | 
| + continue | 
| + props.append(Property(self, prop_name, prop_json, | 
| from_json=True, | 
| - from_client=True) | 
| + from_client=True)) | 
| + additional_properties = json.get('additionalProperties') | 
| + if additional_properties: | 
| + props.append(Property(self, 'additionalProperties', additional_properties, | 
| + is_additional_properties=True)) | 
| + | 
| + for prop in props: | 
| + if prop.unix_name in self.properties: | 
| + raise ParseException( | 
| + self.properties[prop.unix_name].name + ' and ' + prop.name + | 
| + ' are both named ' + prop.unix_name) | 
| + self.properties[prop.unix_name] = prop | 
| + self.properties[prop.unix_name] = prop | 
| 
 
not at google - send to devlin
2012/03/01 07:09:37
umm duplicated code?
 
calamity
2012/03/01 11:05:52
Done.
 
 | 
| class Callback(object): | 
| """A callback parameter to a Function. | 
| @@ -83,17 +123,18 @@ class Callback(object): | 
| Properties: | 
| - |params| the parameters to this callback. | 
| """ | 
| - def __init__(self, json): | 
| + def __init__(self, parent, json): | 
| params = json['parameters'] | 
| + self.parent = parent | 
| self.params = [] | 
| if len(params) == 0: | 
| return | 
| elif len(params) == 1: | 
| param = params[0] | 
| - self.params.append(Property(param['name'], param, | 
| + self.params.append(Property(self, param['name'], param, | 
| from_client=True)) | 
| else: | 
| - raise AssertionError("Callbacks can have at most a single parameter") | 
| + raise ParseException("Callbacks can have at most a single parameter") | 
| class Function(object): | 
| """A Function defined in the API. | 
| @@ -106,17 +147,19 @@ class Function(object): | 
| - |callback| the callback parameter to the function. There should be exactly | 
| one | 
| """ | 
| - def __init__(self, json): | 
| + def __init__(self, parent, json): | 
| self.name = json['name'] | 
| self.params = [] | 
| - self.description = json['description'] | 
| + self.description = json.get('description') | 
| self.callback = None | 
| + self.parent = parent | 
| for param in json['parameters']: | 
| if param.get('type') == 'function': | 
| - assert (not self.callback), self.name + " has more than one callback" | 
| - self.callback = Callback(param) | 
| + if self.callback: | 
| + raise ParseException(self.name + " has more than one callback") | 
| + self.callback = Callback(self, param) | 
| else: | 
| - self.params.append(Property(param['name'], param, | 
| + self.params.append(Property(self, param['name'], param, | 
| from_json=True)) | 
| class Property(object): | 
| @@ -135,9 +178,9 @@ class Property(object): | 
| ARRAY | 
| - |properties| the properties of an OBJECT parameter | 
| """ | 
| - def __init__(self, name, json, | 
| - from_json=False, | 
| - from_client=False): | 
| + | 
| + def __init__(self, parent, name, json, is_additional_properties=False, | 
| + from_json=False, from_client=False): | 
| """ | 
| Parameters: | 
| - |from_json| indicates that instances of the Type can originate from the | 
| @@ -147,11 +190,14 @@ class Property(object): | 
| users of generated code, such as top-level types and function results | 
| """ | 
| self.name = name | 
| - self._unix_name = _UnixName(self.name) | 
| + self._unix_name = UnixName(self.name) | 
| self._unix_name_used = False | 
| self.optional = json.get('optional', False) | 
| self.description = json.get('description') | 
| - if '$ref' in json: | 
| + self.parent = parent | 
| + if is_additional_properties: | 
| + self.type_ = PropertyType.ADDITIONAL_PROPERTIES | 
| + elif '$ref' in json: | 
| self.ref_type = json['$ref'] | 
| self.type_ = PropertyType.REF | 
| elif 'enum' in json: | 
| @@ -172,9 +218,9 @@ class Property(object): | 
| elif json_type == 'number': | 
| self.type_ = PropertyType.DOUBLE | 
| elif json_type == 'array': | 
| - self.item_type = Property(name + "Element", json['items'], | 
| - from_json, | 
| - from_client) | 
| + self.item_type = Property(self, name + "Element", json['items'], | 
| + from_json=from_json, | 
| + from_client=from_client) | 
| self.type_ = PropertyType.ARRAY | 
| elif json_type == 'object': | 
| self.type_ = PropertyType.OBJECT | 
| @@ -182,20 +228,20 @@ class Property(object): | 
| self.properties = {} | 
| self.from_json = from_json | 
| self.from_client = from_client | 
| - for key, val in json.get('properties', {}).items(): | 
| - self.properties[key] = Property(key, val, | 
| - from_json, | 
| - from_client) | 
| + type_ = Type(self, self.name, json) | 
| + self.properties = type_.properties | 
| + self.functions = type_.functions | 
| else: | 
| - raise NotImplementedError(json_type) | 
| + raise ParseException(self, 'type ' + json_type + ' not recognized') | 
| elif 'choices' in json: | 
| - assert len(json['choices']), 'Choices has no choices\n%s' % json | 
| + if not json['choices']: | 
| + raise ParseException('Choices has no choices') | 
| self.choices = {} | 
| self.type_ = PropertyType.CHOICES | 
| for choice_json in json['choices']: | 
| - choice = Property(self.name, choice_json, | 
| - from_json, | 
| - from_client) | 
| + choice = Property(self, self.name, choice_json, | 
| + from_json=from_json, | 
| + from_client=from_client) | 
| # A choice gets its unix_name set in | 
| # cpp_type_generator.GetExpandedChoicesInParams | 
| choice._unix_name = None | 
| @@ -203,7 +249,7 @@ class Property(object): | 
| choice.optional = True | 
| self.choices[choice.type_] = choice | 
| else: | 
| - raise NotImplementedError(json) | 
| + raise ParseException('Property has no type, $ref or choices') | 
| def GetUnixName(self): | 
| """Gets the property's unix_name. Raises AttributeError if not set. | 
| @@ -257,10 +303,30 @@ class PropertyType(object): | 
| CHOICES = _Info(False, "CHOICES") | 
| OBJECT = _Info(False, "OBJECT") | 
| ANY = _Info(False, "ANY") | 
| + ADDITIONAL_PROPERTIES = _Info(False, "ADDITIONAL_PROPERTIES") | 
| -def _UnixName(name): | 
| +def UnixName(name): | 
| """Returns the unix_style name for a given lowerCamelCase string. | 
| """ | 
| return '_'.join([x.lower() | 
| for x in re.findall('[A-Z][a-z_]*', name[0].upper() + name[1:])]) | 
| +class ParseException(Exception): | 
| 
 
not at google - send to devlin
2012/03/01 07:09:37
docs
 
calamity
2012/03/01 11:05:52
Done.
 
 | 
| + def __init__(self, parent, message): | 
| + hierarchy = HierarchyOf(parent) | 
| + hierarchy.append(message) | 
| + Exception.__init__( | 
| + self, 'Model parse exception at: \n' + '\n'.join(hierarchy)) | 
| 
 
not at google - send to devlin
2012/03/01 07:09:37
unnecessary space in the middle of "at: \n"
cos n
 
calamity
2012/03/01 11:05:52
Done.
 
 | 
| + | 
| +def HierarchyOf(prop): | 
| 
 
not at google - send to devlin
2012/03/01 07:09:37
write ALL the docs
call it GetModelHierarchy... w
 
calamity
2012/03/01 11:05:52
Done.
 
 | 
| + hierarchy = [] | 
| + entity = prop | 
| + while entity: | 
| + try: | 
| + hierarchy.append(entity.name) | 
| + except AttributeError: | 
| + hierarchy.append(repr(entity)) | 
| + entity = entity.parent | 
| + hierarchy.reverse() | 
| + return hierarchy | 
| + |