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""" 5Generator language component for compiler.py that adds Dart language support. 6""" 7 8from code import Code 9from model import Function, PropertyType 10from schema_util import StripNamespace 11 12import os 13from datetime import datetime 14 15LICENSE = ( 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 21class 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 29class _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(type_, 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, type_, prop): 245 """Given a Type and 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 764