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)