- add sources.
[platform/framework/web/crosswalk.git] / src / tools / json_schema_compiler / dart_generator.py
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(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