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)