Home | History | Annotate | Download | only in build
      1 #!/usr/bin/env python
      2 # Copyright (c) 2011 Google Inc. All rights reserved.
      3 #
      4 # Redistribution and use in source and binary forms, with or without
      5 # modification, are permitted provided that the following conditions are
      6 # met:
      7 #
      8 #     * Redistributions of source code must retain the above copyright
      9 # notice, this list of conditions and the following disclaimer.
     10 #     * Redistributions in binary form must reproduce the above
     11 # copyright notice, this list of conditions and the following disclaimer
     12 # in the documentation and/or other materials provided with the
     13 # distribution.
     14 #     * Neither the name of Google Inc. nor the names of its
     15 # contributors may be used to endorse or promote products derived from
     16 # this software without specific prior written permission.
     17 #
     18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 import os
     31 import re
     32 import json
     33 
     34 type_traits = {
     35     "any": "*",
     36     "string": "string",
     37     "integer": "number",
     38     "number": "number",
     39     "boolean": "boolean",
     40     "array": "!Array.<*>",
     41     "object": "!Object",
     42 }
     43 
     44 promisified_domains = {
     45     "Accessibility",
     46     "Animation",
     47     "CSS",
     48     "Emulation",
     49     "Profiler"
     50 }
     51 
     52 ref_types = {}
     53 
     54 def full_qualified_type_id(domain_name, type_id):
     55     if type_id.find(".") == -1:
     56         return "%s.%s" % (domain_name, type_id)
     57     return type_id
     58 
     59 
     60 def fix_camel_case(name):
     61     prefix = ""
     62     if name[0] == "-":
     63         prefix = "Negative"
     64         name = name[1:]
     65     refined = re.sub(r'-(\w)', lambda pat: pat.group(1).upper(), name)
     66     refined = to_title_case(refined)
     67     return prefix + re.sub(r'(?i)HTML|XML|WML|API', lambda pat: pat.group(0).upper(), refined)
     68 
     69 
     70 def to_title_case(name):
     71     return name[:1].upper() + name[1:]
     72 
     73 
     74 def generate_enum(name, json):
     75     enum_members = []
     76     for member in json["enum"]:
     77         enum_members.append("    %s: \"%s\"" % (fix_camel_case(member), member))
     78     return "\n/** @enum {string} */\n%s = {\n%s\n};\n" % (name, (",\n".join(enum_members)))
     79 
     80 
     81 def param_type(domain_name, param):
     82     if "type" in param:
     83         if param["type"] == "array":
     84             items = param["items"]
     85             return "!Array.<%s>" % param_type(domain_name, items)
     86         else:
     87             return type_traits[param["type"]]
     88     if "$ref" in param:
     89         type_id = full_qualified_type_id(domain_name, param["$ref"])
     90         if type_id in ref_types:
     91             return ref_types[type_id]
     92         else:
     93             print "Type not found: " + type_id
     94             return "!! Type not found: " + type_id
     95 
     96 
     97 def load_schema(file, domains):
     98     input_file = open(file, "r")
     99     json_string = input_file.read()
    100     parsed_json = json.loads(json_string)
    101     domains.extend(parsed_json["domains"])
    102 
    103 
    104 def generate_protocol_externs(output_path, file1):
    105     domains = []
    106     load_schema(file1, domains)
    107     output_file = open(output_path, "w")
    108 
    109     output_file.write(
    110 """
    111 var InspectorBackend = {}
    112 
    113 var Protocol = {};
    114 /** @typedef {string}*/
    115 Protocol.Error;
    116 """)
    117 
    118     for domain in domains:
    119         domain_name = domain["domain"]
    120         if "types" in domain:
    121             for type in domain["types"]:
    122                 type_id = full_qualified_type_id(domain_name, type["id"])
    123                 ref_types[type_id] = "%sAgent.%s" % (domain_name, type["id"])
    124 
    125     for domain in domains:
    126         domain_name = domain["domain"]
    127         promisified = domain_name in promisified_domains
    128 
    129         output_file.write("\n\n/**\n * @constructor\n*/\n")
    130         output_file.write("Protocol.%sAgent = function(){};\n" % domain_name)
    131 
    132         if "commands" in domain:
    133             for command in domain["commands"]:
    134                 output_file.write("\n/**\n")
    135                 params = []
    136                 has_return_value = "returns" in command
    137                 explicit_parameters = promisified and has_return_value
    138                 if ("parameters" in command):
    139                     for in_param in command["parameters"]:
    140                         # All parameters are not optional in case of promisified domain with return value.
    141                         if (not explicit_parameters and "optional" in in_param):
    142                             params.append("opt_%s" % in_param["name"])
    143                             output_file.write(" * @param {%s=} opt_%s\n" % (param_type(domain_name, in_param), in_param["name"]))
    144                         else:
    145                             params.append(in_param["name"])
    146                             output_file.write(" * @param {%s} %s\n" % (param_type(domain_name, in_param), in_param["name"]))
    147                 returns = []
    148                 returns.append("?Protocol.Error")
    149                 if ("error" in command):
    150                     returns.append("%s=" % param_type(domain_name, command["error"]))
    151                 if (has_return_value):
    152                     for out_param in command["returns"]:
    153                         if ("optional" in out_param):
    154                             returns.append("%s=" % param_type(domain_name, out_param))
    155                         else:
    156                             returns.append("%s" % param_type(domain_name, out_param))
    157                 callback_return_type = "void="
    158                 if explicit_parameters:
    159                     callback_return_type = "T"
    160                 elif promisified:
    161                     callback_return_type = "T="
    162                 output_file.write(" * @param {function(%s):%s} opt_callback\n" % (", ".join(returns), callback_return_type))
    163                 if (promisified):
    164                     output_file.write(" * @return {!Promise.<T>}\n")
    165                     output_file.write(" * @template T\n")
    166                 params.append("opt_callback")
    167 
    168                 output_file.write(" */\n")
    169                 output_file.write("Protocol.%sAgent.prototype.%s = function(%s) {}\n" % (domain_name, command["name"], ", ".join(params)))
    170                 output_file.write("/** @param {function(%s):void=} opt_callback */\n" % ", ".join(returns))
    171                 output_file.write("Protocol.%sAgent.prototype.invoke_%s = function(obj, opt_callback) {}\n" % (domain_name, command["name"]))
    172 
    173         output_file.write("\n\n\nvar %sAgent = function(){};\n" % domain_name)
    174 
    175         if "types" in domain:
    176             for type in domain["types"]:
    177                 if type["type"] == "object":
    178                     typedef_args = []
    179                     if "properties" in type:
    180                         for property in type["properties"]:
    181                             suffix = ""
    182                             if ("optional" in property):
    183                                 suffix = "|undefined"
    184                             if "enum" in property:
    185                                 enum_name = "%sAgent.%s%s" % (domain_name, type["id"], to_title_case(property["name"]))
    186                                 output_file.write(generate_enum(enum_name, property))
    187                                 typedef_args.append("%s:(%s%s)" % (property["name"], enum_name, suffix))
    188                             else:
    189                                 typedef_args.append("%s:(%s%s)" % (property["name"], param_type(domain_name, property), suffix))
    190                     if (typedef_args):
    191                         output_file.write("\n/** @typedef {!{%s}} */\n%sAgent.%s;\n" % (", ".join(typedef_args), domain_name, type["id"]))
    192                     else:
    193                         output_file.write("\n/** @typedef {!Object} */\n%sAgent.%s;\n" % (domain_name, type["id"]))
    194                 elif type["type"] == "string" and "enum" in type:
    195                     output_file.write(generate_enum("%sAgent.%s" % (domain_name, type["id"]), type))
    196                 elif type["type"] == "array":
    197                     output_file.write("\n/** @typedef {!Array.<!%s>} */\n%sAgent.%s;\n" % (param_type(domain_name, type["items"]), domain_name, type["id"]))
    198                 else:
    199                     output_file.write("\n/** @typedef {%s} */\n%sAgent.%s;\n" % (type_traits[type["type"]], domain_name, type["id"]))
    200 
    201         output_file.write("/** @interface */\n")
    202         output_file.write("%sAgent.Dispatcher = function() {};\n" % domain_name)
    203         if "events" in domain:
    204             for event in domain["events"]:
    205                 params = []
    206                 if ("parameters" in event):
    207                     output_file.write("/**\n")
    208                     for param in event["parameters"]:
    209                         if ("optional" in param):
    210                             params.append("opt_%s" % param["name"])
    211                             output_file.write(" * @param {%s=} opt_%s\n" % (param_type(domain_name, param), param["name"]))
    212                         else:
    213                             params.append(param["name"])
    214                             output_file.write(" * @param {%s} %s\n" % (param_type(domain_name, param), param["name"]))
    215                     output_file.write(" */\n")
    216                 output_file.write("%sAgent.Dispatcher.prototype.%s = function(%s) {};\n" % (domain_name, event["name"], ", ".join(params)))
    217 
    218     output_file.write("\n/** @constructor\n * @param {!Object.<string, !Object>} agentsMap\n */\n")
    219     output_file.write("Protocol.Agents = function(agentsMap){this._agentsMap;};\n")
    220     output_file.write("/**\n * @param {string} domain\n * @param {!Object} dispatcher\n */\n")
    221     output_file.write("Protocol.Agents.prototype.registerDispatcher = function(domain, dispatcher){};\n")
    222     for domain in domains:
    223         domain_name = domain["domain"]
    224         uppercase_length = 0
    225         while uppercase_length < len(domain_name) and domain_name[uppercase_length].isupper():
    226             uppercase_length += 1
    227 
    228         output_file.write("/** @return {!Protocol.%sAgent}*/\n" % domain_name)
    229         output_file.write("Protocol.Agents.prototype.%s = function(){};\n" % (domain_name[:uppercase_length].lower() + domain_name[uppercase_length:] + "Agent"))
    230 
    231         output_file.write("/**\n * @param {!%sAgent.Dispatcher} dispatcher\n */\n" % domain_name)
    232         output_file.write("Protocol.Agents.prototype.register%sDispatcher = function(dispatcher) {}\n" % domain_name)
    233 
    234 
    235     output_file.close()
    236 
    237 if __name__ == "__main__":
    238     import sys
    239     import os.path
    240     program_name = os.path.basename(__file__)
    241     if len(sys.argv) < 4 or sys.argv[1] != "-o":
    242         sys.stderr.write("Usage: %s -o OUTPUT_FILE INPUT_FILE\n" % program_name)
    243         exit(1)
    244     output_path = sys.argv[2]
    245     input_path = sys.argv[3]
    246     generate_protocol_externs(output_path, input_path)
    247