From 02d8d9e0bc26d869e828da1cb81428894a445e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 19 Mar 2013 17:44:25 +0100 Subject: [PATCH] Support nested options objects This commit extends the generated Export constructors to support nested options objects. For example, this is now supported: @exportObjectLiteralProperty ol.MapOptions.view ol.View|ol.View2DOptions|undefined This specifies that the "view" property in the map options can reference an ol.View instance, an ol.View2DOptions literal object, or undefined. If the "view" property references an ol.View2DOptions literal object the ol.MapExport constructor will create an ol.View2DExport instance, and pass it to the parent constructor, ol.Map. In this way, extern types never cross the external/internal boundary. In other words, translations from non-renamed to renamed objects remain confined to the generated Export constructors. --- bin/generate-exports.py | 59 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/bin/generate-exports.py b/bin/generate-exports.py index 6e98035ee2..cdd0ea4124 100755 --- a/bin/generate-exports.py +++ b/bin/generate-exports.py @@ -33,13 +33,52 @@ class Exportable(object): class Class(Exportable): - def __init__(self, name, object_literal): + def __init__(self, name, object_literal, objects): Exportable.__init__(self, name) self.object_literal = object_literal + self.objects = objects self.props = set() __repr__ = simplerepr + def nested_options(self): + def get_class_by_object_literal_name(name): + for k, o in self.objects.iteritems(): + if isinstance(o, Class) and o.object_literal.name == name: + return o + return None + + for option in sorted(self.object_literal.prop_types.keys()): + types = self.object_literal.prop_types[option].split('|') + base, object_literal = None, None + for t in types: + if t in self.objects: + o = self.objects[t] + if isinstance(o, (Class, Symbol)): + if base: + raise RuntimeError('Multiple "class" types found for ' + 'option %s.%s: %s, %s.' % + (self.object_literal.name, option, + base.name, o.name)) + base = o + elif isinstance(o, ObjectLiteral): + if object_literal: + raise RuntimeError('Multiple "literal" types found for ' + 'option %s.%s: %s, %s.' % + (self.object_literal.name, option, + object_literal.name, o.name)) + object_literal = o + if object_literal: + if not base: + raise RuntimeError('%s "literal" type found for option %s.%s, ' + 'but no "class" type.' % + (object_literal.name, self.object_literal.name, option)) + klass = get_class_by_object_literal_name(object_literal.name) + if not klass: + raise RuntimeError('No constructor taking a %s found.' % + object_literal.name) + yield option, object_literal, klass, base + def export(self): lines = [] if self.object_literal is None: @@ -51,10 +90,19 @@ class Class(Exportable): lines.append(' * @extends {%s}\n' % (self.name,)) lines.append(' * @param {%s} options Options.\n' % (self.object_literal.extern_name(),)) lines.append(' */\n') - lines.append('%sExport = function(options) {\n' % (self.name,)) + lines.append('%s = function(options) {\n' % (self.export_name(),)) lines.append(' /** @type {%s} */\n' % (self.object_literal.name,)) lines.append(' var arg;\n'); lines.append(' if (goog.isDefAndNotNull(options)) {\n') + # FIXME: we modify the user's options object + lines.append(''.join( + ' if (!(options.%(o)s instanceof %(base)s)) {\n' + ' options.%(o)s = new %(ctor)s(\n' + ' /** @type {%(extern)s} */ (options.%(o)s));\n' + ' }\n' % + {'o': o, 'base': b.name, 'ctor': k.export_name(), + 'extern': ol.extern_name()} \ + for o, ol, k, b in self.nested_options())) lines.append(' arg = {') lines.extend(','.join('\n %s: options.%s' % (key, key) for key in sorted(self.object_literal.prop_types.keys()))) lines.append('\n };\n') @@ -66,10 +114,13 @@ class Class(Exportable): lines.append('goog.inherits(%sExport, %s);\n' % (self.name, self.name)) lines.append('goog.exportSymbol(\n') lines.append(' \'%s\',\n' % (self.name,)) - lines.append(' %sExport);\n' % (self.name,)) + lines.append(' %s);\n' % (self.export_name(),)) lines.extend('goog.exportProperty(\n %s,\n \'%s\',\n %s.%s);\n' % (self.name, prop, self.name, prop) for prop in sorted(self.props)) return ''.join(lines) + def export_name(self): + return '%sExport' % self.name + class ObjectLiteral(Exportable): @@ -161,7 +212,7 @@ def main(argv): object_literal = objects[object_literal_name] if not isinstance(object_literal, ObjectLiteral): raise RuntimeError(line) # Undefined object literal - klass = Class(name, object_literal) + klass = Class(name, object_literal, objects) objects[name] = klass continue m = re.match(r'@exportObjectLiteral\s+(?P\S+)\Z', line)