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

Side by Side Diff: tools/json_schema_compiler/model.py

Issue 9491002: json_schema_compiler: any, additionalProperties, functions on types (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: add any in arrays to util.h Created 8 years, 9 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
« no previous file with comments | « tools/json_schema_compiler/h_generator.py ('k') | tools/json_schema_compiler/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
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
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
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
OLDNEW
« no previous file with comments | « tools/json_schema_compiler/h_generator.py ('k') | tools/json_schema_compiler/model_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698