OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import os.path | 5 import os.path |
6 import re | 6 import re |
7 import copy | 7 import copy |
8 | 8 |
9 class Model(object): | 9 class Model(object): |
10 """Model of all namespaces that comprise an API. | 10 """Model of all namespaces that comprise an API. |
(...skipping 22 matching lines...) Expand all Loading... |
33 - |name| the name of the namespace | 33 - |name| the name of the namespace |
34 - |unix_name| the unix_name of the namespace | 34 - |unix_name| the unix_name of the namespace |
35 - |source_file| the file that contained the namespace definition | 35 - |source_file| the file that contained the namespace definition |
36 - |source_file_dir| the directory component of |source_file| | 36 - |source_file_dir| the directory component of |source_file| |
37 - |source_file_filename| the filename component of |source_file| | 37 - |source_file_filename| the filename component of |source_file| |
38 - |types| a map of type names to their model.Type | 38 - |types| a map of type names to their model.Type |
39 - |functions| a map of function names to their model.Function | 39 - |functions| a map of function names to their model.Function |
40 """ | 40 """ |
41 def __init__(self, json, source_file): | 41 def __init__(self, json, source_file): |
42 self.name = json['namespace'] | 42 self.name = json['namespace'] |
43 self.unix_name = _UnixName(self.name) | 43 self.unix_name = UnixName(self.name) |
44 self.source_file = source_file | 44 self.source_file = source_file |
45 self.source_file_dir, self.source_file_filename = os.path.split(source_file) | 45 self.source_file_dir, self.source_file_filename = os.path.split(source_file) |
46 self.types = {} | 46 self.types = {} |
47 self.functions = {} | 47 self.functions = {} |
| 48 self.parent = None |
| 49 # TODO(calamity): Implement properties on namespaces for shared structures |
| 50 # or constants across a namespace (e.g Windows::WINDOW_ID_NONE). |
| 51 for property_json in json.get('properties', []): |
| 52 pass |
48 for type_json in json.get('types', []): | 53 for type_json in json.get('types', []): |
49 type_ = Type(type_json) | 54 type_ = Type(self, type_json['id'], type_json) |
50 self.types[type_.name] = type_ | 55 self.types[type_.name] = type_ |
51 for function_json in json.get('functions', []): | 56 for function_json in json.get('functions', []): |
52 if not function_json.get('nocompile', False): | 57 if not function_json.get('nocompile', False): |
53 function = Function(function_json) | 58 self.functions[function_json['name']] = Function(self, function_json) |
54 self.functions[function.name] = function | |
55 | 59 |
56 class Type(object): | 60 class Type(object): |
57 """A Type defined in the json. | 61 """A Type defined in the json. |
58 | 62 |
59 Properties: | 63 Properties: |
60 - |name| the type name | 64 - |name| the type name |
61 - |description| the description of the type (if provided) | 65 - |description| the description of the type (if provided) |
62 - |properties| a map of property names to their model.Property | 66 - |properties| a map of property unix_names to their model.Property |
| 67 - |functions| a map of function names to their model.Function |
63 - |from_client| indicates that instances of the Type can originate from the | 68 - |from_client| indicates that instances of the Type can originate from the |
64 users of generated code, such as top-level types and function results | 69 users of generated code, such as top-level types and function results |
65 - |from_json| indicates that instances of the Type can originate from the | 70 - |from_json| indicates that instances of the Type can originate from the |
66 JSON (as described by the schema), such as top-level types and function | 71 JSON (as described by the schema), such as top-level types and function |
67 parameters | 72 parameters |
68 """ | 73 """ |
69 def __init__(self, json): | 74 def __init__(self, parent, name, json): |
70 self.name = json['id'] | 75 if not ( |
| 76 'properties' in json or |
| 77 'additionalProperties' in json or |
| 78 'functions' in json): |
| 79 raise ParseException(name + " has no properties or functions") |
| 80 self.name = name |
71 self.description = json.get('description') | 81 self.description = json.get('description') |
72 self.from_json = True | 82 self.from_json = True |
73 self.from_client = True | 83 self.from_client = True |
74 self.properties = {} | 84 self.properties = {} |
75 for prop_name, prop_json in json['properties'].items(): | 85 self.functions = {} |
76 self.properties[prop_name] = Property(prop_name, prop_json, | 86 self.parent = parent |
| 87 for function_json in json.get('functions', []): |
| 88 if not function_json.get('nocompile', False): |
| 89 self.functions[function_json['name']] = Function(self, function_json) |
| 90 props = [] |
| 91 for prop_name, prop_json in json.get('properties', {}).items(): |
| 92 # TODO(calamity): support functions (callbacks) as properties. The model |
| 93 # doesn't support it yet because to h/cc generators don't -- this is |
| 94 # because we'd need to hook it into a base::Callback or something. |
| 95 # |
| 96 # However, pragmatically it's not necessary to support them anyway, since |
| 97 # the instances of functions-on-properties in the extension APIs are all |
| 98 # handled in pure Javascript on the render process (and .: never reach |
| 99 # C++ let alone the browser). |
| 100 if prop_json.get('type') == 'function': |
| 101 continue |
| 102 props.append(Property(self, prop_name, prop_json, |
77 from_json=True, | 103 from_json=True, |
78 from_client=True) | 104 from_client=True)) |
| 105 |
| 106 additional_properties = json.get('additionalProperties') |
| 107 if additional_properties: |
| 108 props.append(Property(self, 'additionalProperties', additional_properties, |
| 109 is_additional_properties=True)) |
| 110 |
| 111 for prop in props: |
| 112 if prop.unix_name in self.properties: |
| 113 raise ParseException( |
| 114 self.properties[prop.unix_name].name + ' and ' + prop.name + |
| 115 ' are both named ' + prop.unix_name) |
| 116 self.properties[prop.unix_name] = prop |
79 | 117 |
80 class Callback(object): | 118 class Callback(object): |
81 """A callback parameter to a Function. | 119 """A callback parameter to a Function. |
82 | 120 |
83 Properties: | 121 Properties: |
84 - |params| the parameters to this callback. | 122 - |params| the parameters to this callback. |
85 """ | 123 """ |
86 def __init__(self, json): | 124 def __init__(self, parent, json): |
87 params = json['parameters'] | 125 params = json['parameters'] |
| 126 self.parent = parent |
88 self.params = [] | 127 self.params = [] |
89 if len(params) == 0: | 128 if len(params) == 0: |
90 return | 129 return |
91 elif len(params) == 1: | 130 elif len(params) == 1: |
92 param = params[0] | 131 param = params[0] |
93 self.params.append(Property(param['name'], param, | 132 self.params.append(Property(self, param['name'], param, |
94 from_client=True)) | 133 from_client=True)) |
95 else: | 134 else: |
96 raise AssertionError("Callbacks can have at most a single parameter") | 135 raise ParseException("Callbacks can have at most a single parameter") |
97 | 136 |
98 class Function(object): | 137 class Function(object): |
99 """A Function defined in the API. | 138 """A Function defined in the API. |
100 | 139 |
101 Properties: | 140 Properties: |
102 - |name| the function name | 141 - |name| the function name |
103 - |params| a list of parameters to the function (order matters). A separate | 142 - |params| a list of parameters to the function (order matters). A separate |
104 parameter is used for each choice of a 'choices' parameter. | 143 parameter is used for each choice of a 'choices' parameter. |
105 - |description| a description of the function (if provided) | 144 - |description| a description of the function (if provided) |
106 - |callback| the callback parameter to the function. There should be exactly | 145 - |callback| the callback parameter to the function. There should be exactly |
107 one | 146 one |
108 """ | 147 """ |
109 def __init__(self, json): | 148 def __init__(self, parent, json): |
110 self.name = json['name'] | 149 self.name = json['name'] |
111 self.params = [] | 150 self.params = [] |
112 self.description = json['description'] | 151 self.description = json.get('description') |
113 self.callback = None | 152 self.callback = None |
| 153 self.parent = parent |
114 for param in json['parameters']: | 154 for param in json['parameters']: |
115 if param.get('type') == 'function': | 155 if param.get('type') == 'function': |
116 assert (not self.callback), self.name + " has more than one callback" | 156 if self.callback: |
117 self.callback = Callback(param) | 157 raise ParseException(self.name + " has more than one callback") |
| 158 self.callback = Callback(self, param) |
118 else: | 159 else: |
119 self.params.append(Property(param['name'], param, | 160 self.params.append(Property(self, param['name'], param, |
120 from_json=True)) | 161 from_json=True)) |
121 | 162 |
122 class Property(object): | 163 class Property(object): |
123 """A property of a type OR a parameter to a function. | 164 """A property of a type OR a parameter to a function. |
124 | 165 |
125 Properties: | 166 Properties: |
126 - |name| name of the property as in the json. This shouldn't change since | 167 - |name| name of the property as in the json. This shouldn't change since |
127 it is the key used to access DictionaryValues | 168 it is the key used to access DictionaryValues |
128 - |unix_name| the unix_style_name of the property. Used as variable name | 169 - |unix_name| the unix_style_name of the property. Used as variable name |
129 - |optional| a boolean representing whether the property is optional | 170 - |optional| a boolean representing whether the property is optional |
130 - |description| a description of the property (if provided) | 171 - |description| a description of the property (if provided) |
131 - |type_| the model.PropertyType of this property | 172 - |type_| the model.PropertyType of this property |
132 - |ref_type| the type that the REF property is referencing. Can be used to | 173 - |ref_type| the type that the REF property is referencing. Can be used to |
133 map to its model.Type | 174 map to its model.Type |
134 - |item_type| a model.Property representing the type of each element in an | 175 - |item_type| a model.Property representing the type of each element in an |
135 ARRAY | 176 ARRAY |
136 - |properties| the properties of an OBJECT parameter | 177 - |properties| the properties of an OBJECT parameter |
137 """ | 178 """ |
138 def __init__(self, name, json, | 179 |
139 from_json=False, | 180 def __init__(self, parent, name, json, is_additional_properties=False, |
140 from_client=False): | 181 from_json=False, from_client=False): |
141 """ | 182 """ |
142 Parameters: | 183 Parameters: |
143 - |from_json| indicates that instances of the Type can originate from the | 184 - |from_json| indicates that instances of the Type can originate from the |
144 JSON (as described by the schema), such as top-level types and function | 185 JSON (as described by the schema), such as top-level types and function |
145 parameters | 186 parameters |
146 - |from_client| indicates that instances of the Type can originate from the | 187 - |from_client| indicates that instances of the Type can originate from the |
147 users of generated code, such as top-level types and function results | 188 users of generated code, such as top-level types and function results |
148 """ | 189 """ |
149 self.name = name | 190 self.name = name |
150 self._unix_name = _UnixName(self.name) | 191 self._unix_name = UnixName(self.name) |
151 self._unix_name_used = False | 192 self._unix_name_used = False |
152 self.optional = json.get('optional', False) | 193 self.optional = json.get('optional', False) |
153 self.description = json.get('description') | 194 self.description = json.get('description') |
154 if '$ref' in json: | 195 self.parent = parent |
| 196 if is_additional_properties: |
| 197 self.type_ = PropertyType.ADDITIONAL_PROPERTIES |
| 198 elif '$ref' in json: |
155 self.ref_type = json['$ref'] | 199 self.ref_type = json['$ref'] |
156 self.type_ = PropertyType.REF | 200 self.type_ = PropertyType.REF |
157 elif 'enum' in json: | 201 elif 'enum' in json: |
158 self.enum_values = [] | 202 self.enum_values = [] |
159 for value in json['enum']: | 203 for value in json['enum']: |
160 self.enum_values.append(value) | 204 self.enum_values.append(value) |
161 self.type_ = PropertyType.ENUM | 205 self.type_ = PropertyType.ENUM |
162 elif 'type' in json: | 206 elif 'type' in json: |
163 json_type = json['type'] | 207 json_type = json['type'] |
164 if json_type == 'string': | 208 if json_type == 'string': |
165 self.type_ = PropertyType.STRING | 209 self.type_ = PropertyType.STRING |
166 elif json_type == 'any': | 210 elif json_type == 'any': |
167 self.type_ = PropertyType.ANY | 211 self.type_ = PropertyType.ANY |
168 elif json_type == 'boolean': | 212 elif json_type == 'boolean': |
169 self.type_ = PropertyType.BOOLEAN | 213 self.type_ = PropertyType.BOOLEAN |
170 elif json_type == 'integer': | 214 elif json_type == 'integer': |
171 self.type_ = PropertyType.INTEGER | 215 self.type_ = PropertyType.INTEGER |
172 elif json_type == 'number': | 216 elif json_type == 'number': |
173 self.type_ = PropertyType.DOUBLE | 217 self.type_ = PropertyType.DOUBLE |
174 elif json_type == 'array': | 218 elif json_type == 'array': |
175 self.item_type = Property(name + "Element", json['items'], | 219 self.item_type = Property(self, name + "Element", json['items'], |
176 from_json, | 220 from_json=from_json, |
177 from_client) | 221 from_client=from_client) |
178 self.type_ = PropertyType.ARRAY | 222 self.type_ = PropertyType.ARRAY |
179 elif json_type == 'object': | 223 elif json_type == 'object': |
180 self.type_ = PropertyType.OBJECT | 224 self.type_ = PropertyType.OBJECT |
181 # These members are read when this OBJECT Property is used as a Type | 225 # These members are read when this OBJECT Property is used as a Type |
182 self.properties = {} | 226 self.properties = {} |
183 self.from_json = from_json | 227 self.from_json = from_json |
184 self.from_client = from_client | 228 self.from_client = from_client |
185 for key, val in json.get('properties', {}).items(): | 229 type_ = Type(self, self.name, json) |
186 self.properties[key] = Property(key, val, | 230 self.properties = type_.properties |
187 from_json, | 231 self.functions = type_.functions |
188 from_client) | |
189 else: | 232 else: |
190 raise NotImplementedError(json_type) | 233 raise ParseException(self, 'type ' + json_type + ' not recognized') |
191 elif 'choices' in json: | 234 elif 'choices' in json: |
192 assert len(json['choices']), 'Choices has no choices\n%s' % json | 235 if not json['choices']: |
| 236 raise ParseException('Choices has no choices') |
193 self.choices = {} | 237 self.choices = {} |
194 self.type_ = PropertyType.CHOICES | 238 self.type_ = PropertyType.CHOICES |
195 for choice_json in json['choices']: | 239 for choice_json in json['choices']: |
196 choice = Property(self.name, choice_json, | 240 choice = Property(self, self.name, choice_json, |
197 from_json, | 241 from_json=from_json, |
198 from_client) | 242 from_client=from_client) |
199 # A choice gets its unix_name set in | 243 # A choice gets its unix_name set in |
200 # cpp_type_generator.GetExpandedChoicesInParams | 244 # cpp_type_generator.GetExpandedChoicesInParams |
201 choice._unix_name = None | 245 choice._unix_name = None |
202 # The existence of any single choice is optional | 246 # The existence of any single choice is optional |
203 choice.optional = True | 247 choice.optional = True |
204 self.choices[choice.type_] = choice | 248 self.choices[choice.type_] = choice |
205 else: | 249 else: |
206 raise NotImplementedError(json) | 250 raise ParseException('Property has no type, $ref or choices') |
207 | 251 |
208 def GetUnixName(self): | 252 def GetUnixName(self): |
209 """Gets the property's unix_name. Raises AttributeError if not set. | 253 """Gets the property's unix_name. Raises AttributeError if not set. |
210 """ | 254 """ |
211 if self._unix_name is None: | 255 if self._unix_name is None: |
212 raise AttributeError('No unix_name set on %s' % self.name) | 256 raise AttributeError('No unix_name set on %s' % self.name) |
213 self._unix_name_used = True | 257 self._unix_name_used = True |
214 return self._unix_name | 258 return self._unix_name |
215 | 259 |
216 def SetUnixName(self, unix_name): | 260 def SetUnixName(self, unix_name): |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
250 INTEGER = _Info(True, "INTEGER") | 294 INTEGER = _Info(True, "INTEGER") |
251 DOUBLE = _Info(True, "DOUBLE") | 295 DOUBLE = _Info(True, "DOUBLE") |
252 BOOLEAN = _Info(True, "BOOLEAN") | 296 BOOLEAN = _Info(True, "BOOLEAN") |
253 STRING = _Info(True, "STRING") | 297 STRING = _Info(True, "STRING") |
254 ENUM = _Info(False, "ENUM") | 298 ENUM = _Info(False, "ENUM") |
255 ARRAY = _Info(False, "ARRAY") | 299 ARRAY = _Info(False, "ARRAY") |
256 REF = _Info(False, "REF") | 300 REF = _Info(False, "REF") |
257 CHOICES = _Info(False, "CHOICES") | 301 CHOICES = _Info(False, "CHOICES") |
258 OBJECT = _Info(False, "OBJECT") | 302 OBJECT = _Info(False, "OBJECT") |
259 ANY = _Info(False, "ANY") | 303 ANY = _Info(False, "ANY") |
| 304 ADDITIONAL_PROPERTIES = _Info(False, "ADDITIONAL_PROPERTIES") |
260 | 305 |
261 def _UnixName(name): | 306 def UnixName(name): |
262 """Returns the unix_style name for a given lowerCamelCase string. | 307 """Returns the unix_style name for a given lowerCamelCase string. |
263 """ | 308 """ |
264 return '_'.join([x.lower() | 309 return '_'.join([x.lower() |
265 for x in re.findall('[A-Z][a-z_]*', name[0].upper() + name[1:])]) | 310 for x in re.findall('[A-Z][a-z_]*', name[0].upper() + name[1:])]) |
266 | 311 |
| 312 class ParseException(Exception): |
| 313 """Thrown when data in the model is invalid.""" |
| 314 def __init__(self, parent, message): |
| 315 hierarchy = GetModelHierarchy(parent) |
| 316 hierarchy.append(message) |
| 317 Exception.__init__( |
| 318 self, 'Model parse exception at:\n' + '\n'.join(hierarchy)) |
| 319 |
| 320 def GetModelHierarchy(entity): |
| 321 """Returns the hierarchy of the given model entity.""" |
| 322 hierarchy = [] |
| 323 while entity: |
| 324 try: |
| 325 hierarchy.append(entity.name) |
| 326 except AttributeError: |
| 327 hierarchy.append(repr(entity)) |
| 328 entity = entity.parent |
| 329 hierarchy.reverse() |
| 330 return hierarchy |
| 331 |
OLD | NEW |