Code generation for extensions api

This is a preliminary code review for a code generator. The tool's purpose is to generate the tedious serialization code that needs to be written when defining a new extensions api. It generates from the json files in chrome/common/extensions/api.

As an example usage, chrome/browser/extensions/extension_permissions_api.cc has been changed to use a class generated from permissions.json.

The tool has been integrated into the build system and generates compiling and working code (for permissions.json at least)

BUG=
TEST=


Review URL: http://codereview.chromium.org/9114036

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@119405 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py
new file mode 100644
index 0000000..2c356fff
--- /dev/null
+++ b/tools/json_schema_compiler/model.py
@@ -0,0 +1,134 @@
+# Copyright (c) 2012 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.
+
+import os.path
+
+class Model(object):
+  """Model of all namespaces that comprise an API.
+  """
+  def __init__(self):
+    self.namespaces = {}
+
+  def AddNamespace(self, json, source_file):
+    """Add a namespace's json to the model if it has a "compile" property set
+    to true. Returns the new namespace or None if a namespace wasn't added.
+    """
+    if not json.get('compile'):
+      return None
+    namespace = Namespace(json, source_file)
+    self.namespaces[namespace.name] = namespace
+    return namespace
+
+class Namespace(object):
+  """An API namespace.
+  """
+  def __init__(self, json, source_file):
+    self.name = json['namespace']
+    self.source_file = source_file
+    self.source_file_dir, self.source_file_filename = os.path.split(source_file)
+    self.type_dependencies = {}
+    self.types = {}
+    self.functions = {}
+    for type_json in json['types']:
+      type_ = Type(type_json)
+      self.types[type_.name] = type_
+    for function_json in json['functions']:
+      if not function_json.get('nocompile'):
+        function = Function(function_json)
+        self.functions[function.name] = function
+
+class Type(object):
+  """A Type defined in the json.
+  """
+  def __init__(self, json):
+    self.name = json['id']
+    self.description = json.get('description')
+    self.properties = {}
+    for prop_name, prop_json in json['properties'].items():
+      self.properties[prop_name] = Property(prop_name, prop_json)
+
+class Callback(object):
+  """A callback parameter to a Function.
+  """
+  def __init__(self, json):
+    params = json['parameters']
+    if len(params) == 0:
+      self.param = None
+    elif len(params) == 1:
+      param = params[0]
+      self.param = Property(param['name'], param)
+    else:
+      raise AssertionError("Callbacks can have at most a single parameter")
+
+class Function(object):
+  """A Function defined in the API.
+  """
+  def __init__(self, json):
+    self.name = json['name']
+    self.params = []
+    self.description = json['description']
+    self.callback = None
+    self.type_dependencies = {}
+    for param in json['parameters']:
+      if param.get('type') == 'function':
+        assert (not self.callback), "Function has more than one callback"
+        self.callback = Callback(param)
+      else:
+        self.params.append(Property(param['name'], param))
+    assert (self.callback), "Function does not support callback"
+
+# TODO(calamity): handle Enum/choices
+class Property(object):
+  """A property of a type OR a parameter to a function.
+
+  Members will change based on PropertyType. Check self.type_ to determine which
+  members actually exist.
+  """
+  def __init__(self, name, json):
+    self.name = name
+    self.optional = json.get('optional', False)
+    self.description = json.get('description')
+    # TODO(calamity) maybe check for circular refs? could that be a problem?
+    if '$ref' in json:
+      self.ref_type = json['$ref']
+      self.type_ = PropertyType.REF
+    elif 'type' in json:
+      json_type = json['type']
+      if json_type == 'string':
+        self.type_ = PropertyType.STRING
+      elif json_type ==  'boolean':
+        self.type_ = PropertyType.BOOLEAN
+      elif json_type == 'integer':
+        self.type_ = PropertyType.INTEGER
+      elif json_type == 'double':
+        self.type_ = PropertyType.DOUBLE
+      elif json_type == 'array':
+        self.item_type = Property(name + "_inner", json['items'])
+        self.type_ = PropertyType.ARRAY
+      elif json_type == 'object':
+        self.properties = {}
+        self.type_ = PropertyType.OBJECT
+        for key, val in json['properties'].items():
+          self.properties[key] = Property(key, val)
+      else:
+        raise NotImplementedError(json_type)
+    elif 'choices' in json:
+      self.type_ = PropertyType.CHOICES
+      self.choices = {}
+
+class PropertyType(object):
+  """Enum of different types of properties/parameters.
+  """
+  class _Info(object):
+    def __init__(self, is_fundamental):
+      self.is_fundamental = is_fundamental
+
+  INTEGER = _Info(True)
+  DOUBLE = _Info(True)
+  BOOLEAN = _Info(True)
+  STRING = _Info(True)
+  ARRAY = _Info(False)
+  REF = _Info(False)
+  CHOICES = _Info(False)
+  OBJECT = _Info(False)