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 |