| OLD | NEW |
| (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 Generator language component for compiler.py that adds Dart language support. | |
| 6 """ | |
| 7 | |
| 8 from code import Code | |
| 9 from model import Function, PropertyType | |
| 10 from schema_util import StripNamespace | |
| 11 | |
| 12 import os | |
| 13 from datetime import datetime | |
| 14 | |
| 15 LICENSE = ( | |
| 16 """// Copyright (c) %s, the Dart project authors. Please see the AUTHORS file | |
| 17 // for details. All rights reserved. Use of this source code is governed by a | |
| 18 // BSD-style license that can be found in the LICENSE file.""" % | |
| 19 datetime.now().year) | |
| 20 | |
| 21 class DartGenerator(object): | |
| 22 def __init__(self, dart_overrides_dir=None): | |
| 23 self._dart_overrides_dir = dart_overrides_dir | |
| 24 | |
| 25 def Generate(self, namespace): | |
| 26 return _Generator(namespace, self._dart_overrides_dir).Generate() | |
| 27 | |
| 28 | |
| 29 class _Generator(object): | |
| 30 """A .dart generator for a namespace. | |
| 31 """ | |
| 32 | |
| 33 def __init__(self, namespace, dart_overrides_dir=None): | |
| 34 self._namespace = namespace | |
| 35 # TODO(sashab): Once inline type definitions start being added to | |
| 36 # self._types, make a _FindType(self, type_) function that looks at | |
| 37 # self._namespace.types. | |
| 38 self._types = namespace.types | |
| 39 | |
| 40 # Build a dictionary of Type Name --> Custom Dart code. | |
| 41 self._type_overrides = {} | |
| 42 if dart_overrides_dir is not None: | |
| 43 for filename in os.listdir(dart_overrides_dir): | |
| 44 if filename.startswith(namespace.unix_name): | |
| 45 with open(os.path.join(dart_overrides_dir, filename)) as f: | |
| 46 # Split off the namespace and file extension, leaving just the type. | |
| 47 type_path = '.'.join(filename.split('.')[1:-1]) | |
| 48 self._type_overrides[type_path] = f.read() | |
| 49 | |
| 50 # TODO(sashab): Add all inline type definitions to the global Types | |
| 51 # dictionary here, so they have proper names, and are implemented along with | |
| 52 # all other types. Also update the parameters/members with these types | |
| 53 # to reference these new types instead. | |
| 54 | |
| 55 def Generate(self): | |
| 56 """Generates a Code object with the .dart for the entire namespace. | |
| 57 """ | |
| 58 c = Code() | |
| 59 (c.Append(LICENSE) | |
| 60 .Append() | |
| 61 .Append('// Generated from namespace: %s' % self._namespace.name) | |
| 62 .Append() | |
| 63 .Append('part of chrome;')) | |
| 64 | |
| 65 if self._types: | |
| 66 (c.Append() | |
| 67 .Append('/**') | |
| 68 .Append(' * Types') | |
| 69 .Append(' */') | |
| 70 .Append() | |
| 71 ) | |
| 72 for type_ in self._types.values(): | |
| 73 # Check for custom dart for this whole type. | |
| 74 override = self._GetOverride([type_.name], document_with=type_) | |
| 75 c.Cblock(override if override is not None else self._GenerateType(type_)) | |
| 76 | |
| 77 if self._namespace.events: | |
| 78 (c.Append('/**') | |
| 79 .Append(' * Events') | |
| 80 .Append(' */') | |
| 81 .Append() | |
| 82 ) | |
| 83 for event_name in self._namespace.events: | |
| 84 c.Cblock(self._GenerateEvent(self._namespace.events[event_name])) | |
| 85 | |
| 86 (c.Append('/**') | |
| 87 .Append(' * Functions') | |
| 88 .Append(' */') | |
| 89 .Append() | |
| 90 ) | |
| 91 c.Cblock(self._GenerateMainClass()) | |
| 92 | |
| 93 return c | |
| 94 | |
| 95 def _GenerateType(self, type_): | |
| 96 """Given a Type object, returns the Code with the .dart for this | |
| 97 type's definition. | |
| 98 | |
| 99 Assumes this type is a Parameter Type (creatable by user), and creates an | |
| 100 object that extends ChromeObject. All parameters are specifiable as named | |
| 101 arguments in the constructor, and all methods are wrapped with getters and | |
| 102 setters that hide the JS() implementation. | |
| 103 """ | |
| 104 c = Code() | |
| 105 | |
| 106 # Since enums are just treated as strings for now, don't generate their | |
| 107 # type. | |
| 108 # TODO(sashab): Find a nice way to wrap enum objects. | |
| 109 if type_.property_type is PropertyType.ENUM: | |
| 110 return c | |
| 111 | |
| 112 (c.Concat(self._GenerateDocumentation(type_)) | |
| 113 .Sblock('class %(type_name)s extends ChromeObject {') | |
| 114 ) | |
| 115 | |
| 116 # Check whether this type has function members. If it does, don't allow | |
| 117 # public construction. | |
| 118 add_public_constructor = all(not self._IsFunction(p.type_) | |
| 119 for p in type_.properties.values()) | |
| 120 constructor_fields = [self._GeneratePropertySignature(p) | |
| 121 for p in type_.properties.values()] | |
| 122 | |
| 123 if add_public_constructor: | |
| 124 (c.Append('/*') | |
| 125 .Append(' * Public constructor') | |
| 126 .Append(' */') | |
| 127 .Sblock('%(type_name)s({%(constructor_fields)s}) {') | |
| 128 ) | |
| 129 | |
| 130 for prop_name in type_.properties: | |
| 131 (c.Sblock('if (%s != null)' % prop_name) | |
| 132 .Append('this.%s = %s;' % (prop_name, prop_name)) | |
| 133 .Eblock() | |
| 134 ) | |
| 135 (c.Eblock('}') | |
| 136 .Append() | |
| 137 ) | |
| 138 | |
| 139 (c.Append('/*') | |
| 140 .Append(' * Private constructor') | |
| 141 .Append(' */') | |
| 142 .Append('%(type_name)s._proxy(_jsObject) : super._proxy(_jsObject);') | |
| 143 ) | |
| 144 | |
| 145 # Add an accessor (getter & setter) for each property. | |
| 146 properties = [p for p in type_.properties.values() | |
| 147 if not self._IsFunction(p.type_)] | |
| 148 if properties: | |
| 149 (c.Append() | |
| 150 .Append('/*') | |
| 151 .Append(' * Public accessors') | |
| 152 .Append(' */') | |
| 153 ) | |
| 154 for prop in properties: | |
| 155 override = self._GetOverride([type_.name, prop.name], document_with=prop) | |
| 156 c.Concat(override if override is not None | |
| 157 else self._GenerateGetterAndSetter(type_, prop)) | |
| 158 | |
| 159 # Now add all the methods. | |
| 160 methods = [t for t in type_.properties.values() | |
| 161 if self._IsFunction(t.type_)] | |
| 162 if methods: | |
| 163 (c.Append() | |
| 164 .Append('/*') | |
| 165 .Append(' * Methods') | |
| 166 .Append(' */') | |
| 167 ) | |
| 168 for prop in methods: | |
| 169 # Check if there's an override for this method. | |
| 170 override = self._GetOverride([type_.name, prop.name], document_with=prop) | |
| 171 c.Cblock(override if override is not None | |
| 172 else self._GenerateFunction(prop.type_.function)) | |
| 173 | |
| 174 (c.Eblock('}') | |
| 175 .Substitute({ | |
| 176 'type_name': self._AddPrefix(type_.simple_name), | |
| 177 'constructor_fields': ', '.join(constructor_fields) | |
| 178 }) | |
| 179 ) | |
| 180 | |
| 181 return c | |
| 182 | |
| 183 def _GenerateGetterAndSetter(self, type_, prop): | |
| 184 """Given a Type and Property, returns the Code object for the getter and | |
| 185 setter for that property. | |
| 186 """ | |
| 187 c = Code() | |
| 188 override = self._GetOverride([type_.name, prop.name, '.get'], | |
| 189 document_with=prop) | |
| 190 c.Cblock(override if override is not None | |
| 191 else self._GenerateGetter(type_, prop)) | |
| 192 override = self._GetOverride([type_.name, prop.name, '.set']) | |
| 193 c.Cblock(override if override is not None | |
| 194 else self._GenerateSetter(prop)) | |
| 195 return c | |
| 196 | |
| 197 def _GenerateGetter(self, type_, prop): | |
| 198 """Given a Type and Property, returns the Code object for the getter for | |
| 199 that property. | |
| 200 | |
| 201 Also adds the documentation for this property before the method. | |
| 202 """ | |
| 203 c = Code() | |
| 204 c.Concat(self._GenerateDocumentation(prop)) | |
| 205 | |
| 206 type_name = self._GetDartType(prop.type_) | |
| 207 if (self._IsBaseType(prop.type_)): | |
| 208 c.Append("%s get %s => JS('%s', '#.%s', this._jsObject);" % | |
| 209 (type_name, prop.name, type_name, prop.name)) | |
| 210 elif self._IsSerializableObjectType(prop.type_): | |
| 211 c.Append("%s get %s => new %s._proxy(JS('', '#.%s', " | |
| 212 "this._jsObject));" % | |
| 213 (type_name, prop.name, type_name, prop.name)) | |
| 214 elif self._IsListOfSerializableObjects(prop.type_): | |
| 215 (c.Sblock('%s get %s {' % (type_name, prop.name)) | |
| 216 .Append('%s __proxy_%s = new %s();' % (type_name, prop.name, | |
| 217 type_name)) | |
| 218 .Append("int count = JS('int', '#.%s.length', this._jsObject);" % | |
| 219 prop.name) | |
| 220 .Sblock("for (int i = 0; i < count; i++) {") | |
| 221 .Append("var item = JS('', '#.%s[#]', this._jsObject, i);" % prop.name) | |
| 222 .Append('__proxy_%s.add(new %s._proxy(item));' % (prop.name, | |
| 223 self._GetDartType(prop.type_.item_type))) | |
| 224 .Eblock('}') | |
| 225 .Append('return __proxy_%s;' % prop.name) | |
| 226 .Eblock('}') | |
| 227 ) | |
| 228 elif self._IsObjectType(prop.type_): | |
| 229 # TODO(sashab): Think of a way to serialize generic Dart objects. | |
| 230 if type_name in self._types: | |
| 231 c.Append("%s get %s => new %s._proxy(JS('%s', '#.%s', " | |
| 232 "this._jsObject));" % | |
| 233 (type_name, prop.name, type_name, type_name, prop.name)) | |
| 234 else: | |
| 235 c.Append("%s get %s => JS('%s', '#.%s', this._jsObject);" % | |
| 236 (type_name, prop.name, type_name, prop.name)) | |
| 237 else: | |
| 238 raise Exception( | |
| 239 "Could not generate wrapper for %s.%s: unserializable type %s" % | |
| 240 (type_.name, prop.name, type_name) | |
| 241 ) | |
| 242 return c | |
| 243 | |
| 244 def _GenerateSetter(self, prop): | |
| 245 """Given a Property, returns the Code object for the setter for | |
| 246 that property. | |
| 247 """ | |
| 248 c = Code() | |
| 249 type_name = self._GetDartType(prop.type_) | |
| 250 wrapped_name = prop.name | |
| 251 if not self._IsBaseType(prop.type_): | |
| 252 wrapped_name = 'convertArgument(%s)' % prop.name | |
| 253 | |
| 254 (c.Sblock("void set %s(%s %s) {" % (prop.name, type_name, prop.name)) | |
| 255 .Append("JS('void', '#.%s = #', this._jsObject, %s);" % | |
| 256 (prop.name, wrapped_name)) | |
| 257 .Eblock("}") | |
| 258 ) | |
| 259 return c | |
| 260 | |
| 261 def _GenerateDocumentation(self, prop): | |
| 262 """Given an object, generates the documentation for this object (as a | |
| 263 code string) and returns the Code object. | |
| 264 | |
| 265 Returns an empty code object if the object has no documentation. | |
| 266 | |
| 267 Uses triple-quotes for the string. | |
| 268 """ | |
| 269 c = Code() | |
| 270 if prop.description is not None: | |
| 271 for line in prop.description.split('\n'): | |
| 272 c.Comment(line, comment_prefix='/// ') | |
| 273 return c | |
| 274 | |
| 275 def _GenerateFunction(self, f): | |
| 276 """Returns the Code object for the given function. | |
| 277 """ | |
| 278 c = Code() | |
| 279 c.Concat(self._GenerateDocumentation(f)) | |
| 280 | |
| 281 if not self._NeedsProxiedCallback(f): | |
| 282 c.Append("%s => %s;" % (self._GenerateFunctionSignature(f), | |
| 283 self._GenerateProxyCall(f))) | |
| 284 return c | |
| 285 | |
| 286 (c.Sblock("%s {" % self._GenerateFunctionSignature(f)) | |
| 287 .Concat(self._GenerateProxiedFunction(f.callback, f.callback.name)) | |
| 288 .Append('%s;' % self._GenerateProxyCall(f)) | |
| 289 .Eblock('}') | |
| 290 ) | |
| 291 | |
| 292 return c | |
| 293 | |
| 294 def _GenerateProxiedFunction(self, f, callback_name): | |
| 295 """Given a function (assumed to be a callback), generates the proxied | |
| 296 version of this function, which calls |callback_name| if it is defined. | |
| 297 | |
| 298 Returns a Code object. | |
| 299 """ | |
| 300 c = Code() | |
| 301 proxied_params = [] | |
| 302 # A list of Properties, containing List<*> objects that need proxying for | |
| 303 # their members (by copying out each member and proxying it). | |
| 304 lists_to_proxy = [] | |
| 305 for p in f.params: | |
| 306 if self._IsBaseType(p.type_): | |
| 307 proxied_params.append(p.name) | |
| 308 elif self._IsSerializableObjectType(p.type_): | |
| 309 proxied_params.append('new %s._proxy(%s)' % ( | |
| 310 self._GetDartType(p.type_), p.name)) | |
| 311 elif self._IsListOfSerializableObjects(p.type_): | |
| 312 proxied_params.append('__proxy_%s' % p.name) | |
| 313 lists_to_proxy.append(p) | |
| 314 elif self._IsObjectType(p.type_): | |
| 315 # TODO(sashab): Find a way to build generic JS objects back in Dart. | |
| 316 proxied_params.append('%s' % p.name) | |
| 317 elif p.type_.property_type is PropertyType.ARRAY: | |
| 318 # TODO(sashab): This might be okay - what if this is a list of | |
| 319 # FileEntry elements? In this case, a basic list will proxy the objects | |
| 320 # fine. | |
| 321 proxied_params.append('%s' % p.name) | |
| 322 else: | |
| 323 raise Exception( | |
| 324 "Cannot automatically create proxy; can't wrap %s, type %s" % ( | |
| 325 self._GenerateFunctionSignature(f), self._GetDartType(p.type_))) | |
| 326 | |
| 327 (c.Sblock("void __proxy_callback(%s) {" % ', '.join(p.name for p in | |
| 328 f.params)) | |
| 329 .Sblock('if (%s != null) {' % callback_name) | |
| 330 ) | |
| 331 | |
| 332 # Add the proxied lists. | |
| 333 for list_to_proxy in lists_to_proxy: | |
| 334 (c.Append("%s __proxy_%s = new %s();" % ( | |
| 335 self._GetDartType(list_to_proxy.type_), | |
| 336 list_to_proxy.name, | |
| 337 self._GetDartType(list_to_proxy.type_))) | |
| 338 .Sblock("for (var o in %s) {" % list_to_proxy.name) | |
| 339 .Append('__proxy_%s.add(new %s._proxy(o));' % (list_to_proxy.name, | |
| 340 self._GetDartType(list_to_proxy.type_.item_type))) | |
| 341 .Eblock("}") | |
| 342 ) | |
| 343 | |
| 344 (c.Append("%s(%s);" % (callback_name, ', '.join(proxied_params))) | |
| 345 .Eblock('}') | |
| 346 .Eblock('}') | |
| 347 ) | |
| 348 return c | |
| 349 | |
| 350 def _NeedsProxiedCallback(self, f): | |
| 351 """Given a function, returns True if this function's callback needs to be | |
| 352 proxied, False if not. | |
| 353 | |
| 354 Function callbacks need to be proxied if they have at least one | |
| 355 non-base-type parameter. | |
| 356 """ | |
| 357 return f.callback and self._NeedsProxy(f.callback) | |
| 358 | |
| 359 def _NeedsProxy(self, f): | |
| 360 """Given a function, returns True if it needs to be proxied, False if not. | |
| 361 | |
| 362 A function needs to be proxied if any of its members are non-base types. | |
| 363 This means that, when the function object is passed to Javascript, it | |
| 364 needs to be wrapped in a "proxied" call that converts the JS inputs to Dart | |
| 365 objects explicitly, before calling the real function with these new objects. | |
| 366 """ | |
| 367 return any(not self._IsBaseType(p.type_) for p in f.params) | |
| 368 | |
| 369 def _GenerateProxyCall(self, function, call_target='this._jsObject'): | |
| 370 """Given a function, generates the code to call that function via JS(). | |
| 371 Returns a string. | |
| 372 | |
| 373 |call_target| is the name of the object to call the function on. The default | |
| 374 is this._jsObject. | |
| 375 | |
| 376 e.g. | |
| 377 JS('void', '#.resizeTo(#, #)', this._jsObject, width, height) | |
| 378 JS('void', '#.setBounds(#)', this._jsObject, convertArgument(bounds)) | |
| 379 """ | |
| 380 n_params = len(function.params) | |
| 381 if function.callback: | |
| 382 n_params += 1 | |
| 383 | |
| 384 return_type_str = self._GetDartType(function.returns) | |
| 385 params = [] | |
| 386 | |
| 387 # If this object is serializable, don't convert the type from JS - pass the | |
| 388 # JS object straight into the proxy. | |
| 389 if self._IsSerializableObjectType(function.returns): | |
| 390 params.append("''") | |
| 391 else: | |
| 392 params.append("'%s'" % return_type_str) | |
| 393 | |
| 394 params.append("'#.%s(%s)'" % (function.name, ', '.join(['#'] * n_params))) | |
| 395 params.append(call_target) | |
| 396 | |
| 397 for param in function.params: | |
| 398 if not self._IsBaseType(param.type_): | |
| 399 params.append('convertArgument(%s)' % param.name) | |
| 400 else: | |
| 401 params.append(param.name) | |
| 402 if function.callback: | |
| 403 # If this isn't a base type, we need a proxied callback. | |
| 404 callback_name = function.callback.name | |
| 405 if self._NeedsProxiedCallback(function): | |
| 406 callback_name = "__proxy_callback" | |
| 407 params.append('convertDartClosureToJS(%s, %s)' % (callback_name, | |
| 408 len(function.callback.params))) | |
| 409 | |
| 410 # If the object is serializable, call the proxy constructor for this type. | |
| 411 proxy_call = 'JS(%s)' % ', '.join(params) | |
| 412 if self._IsSerializableObjectType(function.returns): | |
| 413 proxy_call = 'new %s._proxy(%s)' % (return_type_str, proxy_call) | |
| 414 | |
| 415 return proxy_call | |
| 416 | |
| 417 def _GenerateEvent(self, event): | |
| 418 """Given a Function object, returns the Code with the .dart for this event, | |
| 419 represented by the function. | |
| 420 | |
| 421 All events extend the Event base type. | |
| 422 """ | |
| 423 c = Code() | |
| 424 | |
| 425 # Add documentation for this event. | |
| 426 (c.Concat(self._GenerateDocumentation(event)) | |
| 427 .Sblock('class Event_%(event_name)s extends Event {') | |
| 428 ) | |
| 429 | |
| 430 # If this event needs a proxy, all calls need to be proxied. | |
| 431 needs_proxy = self._NeedsProxy(event) | |
| 432 | |
| 433 # Override Event callback type definitions. | |
| 434 for ret_type, event_func in (('void', 'addListener'), | |
| 435 ('void', 'removeListener'), | |
| 436 ('bool', 'hasListener')): | |
| 437 param_list = self._GenerateParameterList(event.params, event.callback, | |
| 438 convert_optional=True) | |
| 439 if needs_proxy: | |
| 440 (c.Sblock('%s %s(void callback(%s)) {' % (ret_type, event_func, | |
| 441 param_list)) | |
| 442 .Concat(self._GenerateProxiedFunction(event, 'callback')) | |
| 443 .Append('super.%s(__proxy_callback);' % event_func) | |
| 444 .Eblock('}') | |
| 445 ) | |
| 446 else: | |
| 447 c.Append('%s %s(void callback(%s)) => super.%s(callback);' % | |
| 448 (ret_type, event_func, param_list, event_func)) | |
| 449 c.Append() | |
| 450 | |
| 451 # Generate the constructor. | |
| 452 (c.Append('Event_%(event_name)s(jsObject) : ' | |
| 453 'super._(jsObject, %(param_num)d);') | |
| 454 .Eblock('}') | |
| 455 .Substitute({ | |
| 456 'event_name': self._namespace.unix_name + '_' + event.name, | |
| 457 'param_num': len(event.params) | |
| 458 }) | |
| 459 ) | |
| 460 | |
| 461 return c | |
| 462 | |
| 463 def _GenerateMainClass(self): | |
| 464 """Generates the main class for this file, which links to all functions | |
| 465 and events. | |
| 466 | |
| 467 Returns a code object. | |
| 468 """ | |
| 469 c = Code() | |
| 470 (c.Sblock('class API_%s {' % self._namespace.unix_name) | |
| 471 .Append('/*') | |
| 472 .Append(' * API connection') | |
| 473 .Append(' */') | |
| 474 .Append('Object _jsObject;') | |
| 475 ) | |
| 476 | |
| 477 # Add events. | |
| 478 if self._namespace.events: | |
| 479 (c.Append() | |
| 480 .Append('/*') | |
| 481 .Append(' * Events') | |
| 482 .Append(' */') | |
| 483 ) | |
| 484 for event_name in self._namespace.events: | |
| 485 c.Append('Event_%s_%s %s;' % (self._namespace.unix_name, event_name, | |
| 486 event_name)) | |
| 487 | |
| 488 # Add functions. | |
| 489 if self._namespace.functions: | |
| 490 (c.Append() | |
| 491 .Append('/*') | |
| 492 .Append(' * Functions') | |
| 493 .Append(' */') | |
| 494 ) | |
| 495 for function in self._namespace.functions.values(): | |
| 496 # Check for custom dart for this whole property. | |
| 497 override = self._GetOverride([function.name], document_with=function) | |
| 498 c.Cblock(override if override is not None | |
| 499 else self._GenerateFunction(function)) | |
| 500 | |
| 501 # Add the constructor. | |
| 502 c.Sblock('API_%s(this._jsObject) {' % self._namespace.unix_name) | |
| 503 | |
| 504 # Add events to constructor. | |
| 505 for event_name in self._namespace.events: | |
| 506 c.Append("%s = new Event_%s_%s(JS('', '#.%s', this._jsObject));" % | |
| 507 (event_name, self._namespace.unix_name, event_name, event_name)) | |
| 508 | |
| 509 (c.Eblock('}') | |
| 510 .Eblock('}') | |
| 511 ) | |
| 512 return c | |
| 513 | |
| 514 def _GeneratePropertySignature(self, prop): | |
| 515 """Given a property, returns a signature for that property. | |
| 516 Recursively generates the signature for callbacks. | |
| 517 Returns a String for the given property. | |
| 518 | |
| 519 e.g. | |
| 520 bool x | |
| 521 void onClosed() | |
| 522 void doSomething(bool x, void callback([String x])) | |
| 523 """ | |
| 524 if self._IsFunction(prop.type_): | |
| 525 return self._GenerateFunctionSignature(prop.type_.function) | |
| 526 return '%(type)s %(name)s' % { | |
| 527 'type': self._GetDartType(prop.type_), | |
| 528 'name': prop.simple_name | |
| 529 } | |
| 530 | |
| 531 def _GenerateFunctionSignature(self, function, convert_optional=False): | |
| 532 """Given a function object, returns the signature for that function. | |
| 533 Recursively generates the signature for callbacks. | |
| 534 Returns a String for the given function. | |
| 535 | |
| 536 If convert_optional is True, changes optional parameters to be required. | |
| 537 | |
| 538 e.g. | |
| 539 void onClosed() | |
| 540 bool isOpen([String type]) | |
| 541 void doSomething(bool x, void callback([String x])) | |
| 542 """ | |
| 543 sig = '%(return_type)s %(name)s(%(params)s)' | |
| 544 | |
| 545 if function.returns: | |
| 546 return_type = self._GetDartType(function.returns) | |
| 547 else: | |
| 548 return_type = 'void' | |
| 549 | |
| 550 return sig % { | |
| 551 'return_type': return_type, | |
| 552 'name': function.simple_name, | |
| 553 'params': self._GenerateParameterList(function.params, | |
| 554 function.callback, | |
| 555 convert_optional=convert_optional) | |
| 556 } | |
| 557 | |
| 558 def _GenerateParameterList(self, | |
| 559 params, | |
| 560 callback=None, | |
| 561 convert_optional=False): | |
| 562 """Given a list of function parameters, generates their signature (as a | |
| 563 string). | |
| 564 | |
| 565 e.g. | |
| 566 [String type] | |
| 567 bool x, void callback([String x]) | |
| 568 | |
| 569 If convert_optional is True, changes optional parameters to be required. | |
| 570 Useful for callbacks, where optional parameters are treated as required. | |
| 571 """ | |
| 572 # Params lists (required & optional), to be joined with commas. | |
| 573 # TODO(sashab): Don't assume optional params always come after required | |
| 574 # ones. | |
| 575 params_req = [] | |
| 576 params_opt = [] | |
| 577 for param in params: | |
| 578 p_sig = self._GeneratePropertySignature(param) | |
| 579 if param.optional and not convert_optional: | |
| 580 params_opt.append(p_sig) | |
| 581 else: | |
| 582 params_req.append(p_sig) | |
| 583 | |
| 584 # Add the callback, if it exists. | |
| 585 if callback: | |
| 586 c_sig = self._GenerateFunctionSignature(callback, convert_optional=True) | |
| 587 if callback.optional: | |
| 588 params_opt.append(c_sig) | |
| 589 else: | |
| 590 params_req.append(c_sig) | |
| 591 | |
| 592 # Join the parameters with commas. | |
| 593 # Optional parameters have to be in square brackets, e.g.: | |
| 594 # | |
| 595 # required params | optional params | output | |
| 596 # [] | [] | '' | |
| 597 # [x, y] | [] | 'x, y' | |
| 598 # [] | [a, b] | '[a, b]' | |
| 599 # [x, y] | [a, b] | 'x, y, [a, b]' | |
| 600 if params_opt: | |
| 601 params_opt[0] = '[%s' % params_opt[0] | |
| 602 params_opt[-1] = '%s]' % params_opt[-1] | |
| 603 param_sets = [', '.join(params_req), ', '.join(params_opt)] | |
| 604 | |
| 605 # The 'if p' part here is needed to prevent commas where there are no | |
| 606 # parameters of a certain type. | |
| 607 # If there are no optional parameters, this prevents a _trailing_ comma, | |
| 608 # e.g. '(x, y,)'. Similarly, if there are no required parameters, this | |
| 609 # prevents a leading comma, e.g. '(, [a, b])'. | |
| 610 return ', '.join(p for p in param_sets if p) | |
| 611 | |
| 612 def _GetOverride(self, key_chain, document_with=None): | |
| 613 """Given a list of keys, joins them with periods and searches for them in | |
| 614 the custom dart overrides. | |
| 615 If there is an override for that key, finds the override code and returns | |
| 616 the Code object. If not, returns None. | |
| 617 | |
| 618 If document_with is not None, adds the documentation for this property | |
| 619 before the override code. | |
| 620 """ | |
| 621 c = Code() | |
| 622 contents = self._type_overrides.get('.'.join(key_chain)) | |
| 623 if contents is None: | |
| 624 return None | |
| 625 | |
| 626 if document_with is not None: | |
| 627 c.Concat(self._GenerateDocumentation(document_with)) | |
| 628 for line in contents.strip('\n').split('\n'): | |
| 629 c.Append(line) | |
| 630 return c | |
| 631 | |
| 632 def _AddPrefix(self, name): | |
| 633 """Given the name of a type, prefixes the namespace (as camelcase) and | |
| 634 returns the new name. | |
| 635 """ | |
| 636 # TODO(sashab): Split the dart library into multiple files, avoiding the | |
| 637 # need for this prefixing. | |
| 638 return ('%s%s' % ( | |
| 639 ''.join(s.capitalize() for s in self._namespace.name.split('.')), | |
| 640 name)) | |
| 641 | |
| 642 def _IsFunction(self, type_): | |
| 643 """Given a model.Type, returns whether this type is a function. | |
| 644 """ | |
| 645 return type_.property_type == PropertyType.FUNCTION | |
| 646 | |
| 647 def _IsSerializableObjectType(self, type_): | |
| 648 """Given a model.Type, returns whether this type is a serializable object. | |
| 649 Serializable objects are custom types defined in this namespace. | |
| 650 | |
| 651 If this object is a reference to something not in this namespace, assumes | |
| 652 its a serializable object. | |
| 653 """ | |
| 654 if type_ is None: | |
| 655 return False | |
| 656 if type_.property_type is PropertyType.CHOICES: | |
| 657 return all(self._IsSerializableObjectType(c) for c in type_.choices) | |
| 658 if type_.property_type is PropertyType.REF: | |
| 659 if type_.ref_type in self._types: | |
| 660 return self._IsObjectType(self._types[type_.ref_type]) | |
| 661 return True | |
| 662 if (type_.property_type == PropertyType.OBJECT | |
| 663 and type_.instance_of in self._types): | |
| 664 return self._IsObjectType(self._types[type_.instance_of]) | |
| 665 return False | |
| 666 | |
| 667 def _IsObjectType(self, type_): | |
| 668 """Given a model.Type, returns whether this type is an object. | |
| 669 """ | |
| 670 return (self._IsSerializableObjectType(type_) | |
| 671 or type_.property_type in [PropertyType.OBJECT, PropertyType.ANY]) | |
| 672 | |
| 673 def _IsListOfSerializableObjects(self, type_): | |
| 674 """Given a model.Type, returns whether this type is a list of serializable | |
| 675 objects (or regular objects, if this list is treated as a type - in this | |
| 676 case, the item type was defined inline). | |
| 677 | |
| 678 If this type is a reference to something not in this namespace, assumes | |
| 679 it is not a list of serializable objects. | |
| 680 """ | |
| 681 if type_.property_type is PropertyType.CHOICES: | |
| 682 return all(self._IsListOfSerializableObjects(c) for c in type_.choices) | |
| 683 if type_.property_type is PropertyType.REF: | |
| 684 if type_.ref_type in self._types: | |
| 685 return self._IsListOfSerializableObjects(self._types[type_.ref_type]) | |
| 686 return False | |
| 687 return (type_.property_type is PropertyType.ARRAY and | |
| 688 (self._IsSerializableObjectType(type_.item_type))) | |
| 689 | |
| 690 def _IsListOfBaseTypes(self, type_): | |
| 691 """Given a model.Type, returns whether this type is a list of base type | |
| 692 objects (PropertyType.REF types). | |
| 693 """ | |
| 694 if type_.property_type is PropertyType.CHOICES: | |
| 695 return all(self._IsListOfBaseTypes(c) for c in type_.choices) | |
| 696 return (type_.property_type is PropertyType.ARRAY and | |
| 697 self._IsBaseType(type_.item_type)) | |
| 698 | |
| 699 def _IsBaseType(self, type_): | |
| 700 """Given a model.type_, returns whether this type is a base type | |
| 701 (string, number, boolean, or a list of these). | |
| 702 | |
| 703 If type_ is a Choices object, returns True if all possible choices are base | |
| 704 types. | |
| 705 """ | |
| 706 # TODO(sashab): Remove 'Choices' as a base type once they are wrapped in | |
| 707 # native Dart classes. | |
| 708 if type_.property_type is PropertyType.CHOICES: | |
| 709 return all(self._IsBaseType(c) for c in type_.choices) | |
| 710 return ( | |
| 711 (self._GetDartType(type_) in ['bool', 'num', 'int', 'double', 'String']) | |
| 712 or (type_.property_type is PropertyType.ARRAY | |
| 713 and self._IsBaseType(type_.item_type)) | |
| 714 ) | |
| 715 | |
| 716 def _GetDartType(self, type_): | |
| 717 """Given a model.Type object, returns its type as a Dart string. | |
| 718 """ | |
| 719 if type_ is None: | |
| 720 return 'void' | |
| 721 | |
| 722 prop_type = type_.property_type | |
| 723 if prop_type is PropertyType.REF: | |
| 724 if type_.ref_type in self._types: | |
| 725 return self._GetDartType(self._types[type_.ref_type]) | |
| 726 # TODO(sashab): If the type is foreign, it might have to be imported. | |
| 727 return StripNamespace(type_.ref_type) | |
| 728 elif prop_type is PropertyType.BOOLEAN: | |
| 729 return 'bool' | |
| 730 elif prop_type is PropertyType.INTEGER: | |
| 731 return 'int' | |
| 732 elif prop_type is PropertyType.INT64: | |
| 733 return 'num' | |
| 734 elif prop_type is PropertyType.DOUBLE: | |
| 735 return 'double' | |
| 736 elif prop_type is PropertyType.STRING: | |
| 737 return 'String' | |
| 738 elif prop_type is PropertyType.ENUM: | |
| 739 return 'String' | |
| 740 elif prop_type is PropertyType.CHOICES: | |
| 741 # TODO(sashab): Think of a nice way to generate code for Choices objects | |
| 742 # in Dart. | |
| 743 return 'Object' | |
| 744 elif prop_type is PropertyType.ANY: | |
| 745 return 'Object' | |
| 746 elif prop_type is PropertyType.OBJECT: | |
| 747 # TODO(sashab): type_.name is the name of the function's parameter for | |
| 748 # inline types defined in functions. Think of a way to generate names | |
| 749 # for this, or remove all inline type definitions at the start. | |
| 750 if type_.instance_of is not None: | |
| 751 return type_.instance_of | |
| 752 if not isinstance(type_.parent, Function): | |
| 753 return self._AddPrefix(type_.name) | |
| 754 return 'Object' | |
| 755 elif prop_type is PropertyType.FUNCTION: | |
| 756 return 'Function' | |
| 757 elif prop_type is PropertyType.ARRAY: | |
| 758 return 'List<%s>' % self._GetDartType(type_.item_type) | |
| 759 elif prop_type is PropertyType.BINARY: | |
| 760 return 'String' | |
| 761 else: | |
| 762 raise NotImplementedError(prop_type) | |
| 763 | |
| OLD | NEW |