Chromium Code Reviews| Index: tools/json_schema_compiler/js_interface_generator.py |
| diff --git a/tools/json_schema_compiler/js_interface_generator.py b/tools/json_schema_compiler/js_interface_generator.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..97f0da9626d8c3a27380ebd50bccc55cc557b953 |
| --- /dev/null |
| +++ b/tools/json_schema_compiler/js_interface_generator.py |
| @@ -0,0 +1,185 @@ |
| +# Copyright 2015 The Chromium Authors. All rights reserved. |
|
Devlin
2015/12/02 17:58:28
Do we really need a new generator, rather than jus
stevenjb
2015/12/02 18:07:40
See below :)
I started on that path, but there ar
|
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| +""" |
| +Generator that produces an interface file for the Closure Compiler. |
| +Note: This is a work in progress, and generated externs may require tweaking. |
| +""" |
| + |
| +from code import Code |
| +from model import * |
| +from schema_util import * |
| + |
| +import os |
| +import sys |
| +from datetime import datetime |
| +import re |
| + |
| +LICENSE = ("""// Copyright %s The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| +""" % datetime.now().year) |
| + |
| +INFO = """// This file was generated by: |
| +// %s. |
| +""" |
| + |
| +class JsInterfaceGenerator(object): |
| + def Generate(self, namespace): |
| + return _Generator(namespace).Generate() |
| + |
| + def GetHeader(tool, namespace): |
| + """Returns the file header text. |
| + """ |
| + return (LICENSE + '\n' + (INFO % tool) + '\n' + |
| + ('/** @fileoverview Interface for %s that can be overriden. */' % |
| + namespace)) |
| + GetHeader = staticmethod(GetHeader) |
|
michaelpg
2015/12/02 19:39:45
i'm pretty sure python 2.3 came out before i was b
stevenjb
2015/12/02 20:30:06
apparantly.
|
| + |
| +class _Generator(object): |
| + def __init__(self, namespace): |
| + self._namespace = namespace |
| + first = namespace.name[0].upper() |
| + rest = namespace.name[1:] |
| + self._interface = first + rest |
| + |
| + def Generate(self): |
| + """Generates a Code object with the schema for the entire namespace. |
| + """ |
| + c = Code() |
| + c.Append(JsInterfaceGenerator.GetHeader( |
| + (sys.argv[0]), self._namespace.name)).Append() |
|
michaelpg
2015/12/02 19:39:45
4-space indent
stevenjb
2015/12/02 20:30:06
Done.
|
| + |
| + c.Cblock(self._GenerateInterfaceObject()) |
| + |
| + c.Sblock('%s.prototype = {' % self._interface) |
| + |
| + for function in self._namespace.functions.values(): |
| + c.Cblock(self._GenerateFunction(function)) |
| + |
| + for event in self._namespace.events.values(): |
| + c.Cblock(self._GenerateEvent(event)) |
| + |
| + c.Eblock('};') |
| + |
| + return c |
| + |
| + def _GenerateInterfaceObject(self): |
| + """Generates the code creating the interface object. |
| + For example: |
| + /** @interface */ |
| + function SettingsPrivate() {} |
| + """ |
| + c = Code() |
| + (c.Append('/** @interface */') |
| + .Append('function %s() {}' % self._interface)) |
| + return c |
| + |
| + def _GenerateFunctionJsDoc(self, function): |
| + """Generates the documentation for a function as a Code. |
| + """ |
| + c = Code() |
| + |
| + c.Sblock(line='/**', line_prefix=' * ') |
| + |
| + if function.description: |
| + c.Comment(function.description, comment_prefix='') |
| + |
| + def append_field(c, tag, js_type, name, optional, description): |
| + c.Append('@%s {' % tag) |
| + c.Concat(js_type, new_line=False) |
| + if optional: |
| + c.Append('=', new_line=False) |
| + c.Append('} %s' % name, new_line=False) |
| + if description: |
| + c.Comment(' %s' % description, comment_prefix='', |
| + wrap_indent=4, new_line=False) |
| + |
| + for param in function.params: |
| + append_field(c, 'param', self._TypeToJsType(param.type_), param.name, |
| + param.optional, param.description) |
| + |
| + if function.callback: |
| + append_field(c, 'param', self._FunctionToJsFunction(function.callback), |
| + function.callback.name, function.callback.optional, |
| + function.callback.description) |
| + |
| + if function.returns: |
| + append_field(c, 'return', self._TypeToJsType(function.returns), |
| + '', False, function.returns.description) |
| + |
| + c.Eblock(' */') |
| + return c |
| + |
| + def _GenerateFunction(self, function): |
| + """Generates the inteface for a function, including a JSDoc comment. |
| + """ |
| + c = Code() |
| + if function.deprecated: |
| + return c |
| + |
| + (c.Concat(self._GenerateFunctionJsDoc(function)) |
| + .Append('%s: assertNotReached,' % (function.name))) |
| + |
| + return c |
| + |
| + def _GenerateEvent(self, event): |
| + """Generates the interface for an event. |
| + """ |
| + c = Code() |
| + c.Sblock(line='/**', line_prefix=' * ') |
| + if (event.description): |
| + c.Comment(event.description, comment_prefix='') |
| + c.Append('@type {!ChromeEvent}') |
| + c.Eblock(' */') |
| + c.Append('%s: new ChromeEvent(),' % (event.name)) |
| + return c |
| + |
| + def _FunctionToJsFunction(self, function): |
| + """Converts a model.Function to a JS type (i.e., function([params])...)""" |
| + c = Code() |
| + c.Append('function(') |
| + for i, param in enumerate(function.params): |
| + c.Concat(self._TypeToJsType(param.type_), new_line=False) |
| + if i is not len(function.params) - 1: |
| + c.Append(', ', new_line=False, strip_right=False) |
| + c.Append('):', new_line=False) |
| + |
| + if function.returns: |
| + c.Concat(self._TypeToJsType(function.returns), new_line=False) |
| + else: |
| + c.Append('void', new_line=False) |
| + |
| + return c |
| + |
| + def _TypeToJsType(self, js_type): |
| + """Converts a model.Type to a JS type (number, Array, etc.)""" |
| + if js_type.property_type in (PropertyType.INTEGER, PropertyType.DOUBLE): |
| + return Code().Append('number') |
| + if js_type.property_type is PropertyType.OBJECT: |
| + if js_type.properties: |
| + return self._GenerateObjectDefinition(js_type.properties) |
| + return Code().Append('Object') |
| + if js_type.property_type is PropertyType.ARRAY: |
| + return (Code().Append('!Array<'). |
| + Concat(self._TypeToJsType(js_type.item_type), new_line=False). |
| + Append('>', new_line=False)) |
| + if js_type.property_type is PropertyType.REF: |
| + ref_type = '!chrome.%s.%s' % (self._namespace.name, js_type.ref_type) |
| + return Code().Append(ref_type) |
| + if js_type.property_type is PropertyType.CHOICES: |
| + c = Code() |
| + c.Append('(') |
| + for i, choice in enumerate(js_type.choices): |
| + c.Concat(self._TypeToJsType(choice), new_line=False) |
| + if i is not len(js_type.choices) - 1: |
| + c.Append('|', new_line=False) |
| + c.Append(')', new_line=False) |
| + return c |
| + if js_type.property_type is PropertyType.FUNCTION: |
| + return self._FunctionToJsFunction(js_type.function) |
| + if js_type.property_type is PropertyType.ANY: |
| + return Code().Append('*') |
| + if js_type.property_type.is_fundamental: |
| + return Code().Append(js_type.property_type.name) |
| + return Code().Append('?') # TODO(tbreisacher): Make this more specific. |