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