summaryrefslogtreecommitdiffstats
path: root/tools/json_schema_compiler/model.py
diff options
context:
space:
mode:
authorcalamity@chromium.org <calamity@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-15 14:52:32 +0000
committercalamity@chromium.org <calamity@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-15 14:52:32 +0000
commitcfe484905085bc55742da39f792a571615cb419b (patch)
treed655642171deba8dcbb57d5cdbe9693ee2e42dd8 /tools/json_schema_compiler/model.py
parent498e0a6eb258f10ad76039c3028d0f7a4ad365e4 (diff)
downloadchromium_src-cfe484905085bc55742da39f792a571615cb419b.zip
chromium_src-cfe484905085bc55742da39f792a571615cb419b.tar.gz
chromium_src-cfe484905085bc55742da39f792a571615cb419b.tar.bz2
Adds support for the "choices" and "any" types to json_schema_compiler, as well
as miscellaneous improvements to get at least 3 more schema files compiling: windows, tabs, and the in-progress experimental.declarative. For description of the generator, see http://codereview.chromium.org/9114036/ BUG= TEST= Review URL: http://codereview.chromium.org/9309044 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@122082 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/json_schema_compiler/model.py')
-rw-r--r--tools/json_schema_compiler/model.py144
1 files changed, 117 insertions, 27 deletions
diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py
index 2c356ff..9ac7dfd 100644
--- a/tools/json_schema_compiler/model.py
+++ b/tools/json_schema_compiler/model.py
@@ -3,18 +3,23 @@
# found in the LICENSE file.
import os.path
+import re
class Model(object):
"""Model of all namespaces that comprise an API.
+
+ Properties:
+ - |namespaces| a map of a namespace name to its model.Namespace
"""
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.
+ """Add a namespace's json to the model if it doesn't have "nocompile"
+ property set to true. Returns the new namespace or None if a namespace
+ wasn't added.
"""
- if not json.get('compile'):
+ if json.get('nocompile', False):
return None
namespace = Namespace(json, source_file)
self.namespaces[namespace.name] = namespace
@@ -22,24 +27,37 @@ class Model(object):
class Namespace(object):
"""An API namespace.
+
+ Properties:
+ - |name| the name of the namespace
+ - |source_file_dir| the directory component of the file that contained the
+ namespace definition
+ - |source_file_filename| the filename component of the file that contained
+ the namespace definition
+ - |types| a map of type names to their model.Type
+ - |functions| a map of function names to their model.Function
"""
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'):
+ if not function_json.get('nocompile', False):
function = Function(function_json)
self.functions[function.name] = function
class Type(object):
"""A Type defined in the json.
+
+ Properties:
+ - |name| the type name
+ - |description| the description of the type (if provided)
+ - |properties| a map of property names to their model.Property
"""
def __init__(self, json):
self.name = json['id']
@@ -50,46 +68,70 @@ class Type(object):
class Callback(object):
"""A callback parameter to a Function.
+
+ Properties:
+ - |params| the parameters to this callback.
"""
def __init__(self, json):
params = json['parameters']
+ self.params = []
if len(params) == 0:
- self.param = None
+ return
elif len(params) == 1:
param = params[0]
- self.param = Property(param['name'], param)
+ self.params.append(Property(param['name'], param))
else:
raise AssertionError("Callbacks can have at most a single parameter")
class Function(object):
"""A Function defined in the API.
+
+ Properties:
+ - |name| the function name
+ - |params| a list of parameters to the function (order matters). A separate
+ parameter is used for each choice of a 'choices' parameter.
+ - |description| a description of the function (if provided)
+ - |callback| the callback parameter to the function. There should be exactly
+ one
"""
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"
+ assert (not self.callback), self.name + " 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"
+ assert (self.callback), self.name + " does not support callback"
-# TODO(calamity): handle Enum/choices
+# TODO(calamity): handle Enum
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.
+ Properties:
+ - |name| name of the property as in the json. This shouldn't change since
+ it is the key used to access DictionaryValues
+ - |unix_name| the unix_style_name of the property. Used as variable name
+ - |optional| a boolean representing whether the property is optional
+ - |description| a description of the property (if provided)
+ - |type_| the model.PropertyType of this property
+ - |ref_type| the type that the REF property is referencing. Can be used to
+ map to its model.Type
+ - |item_type| a model.Property representing the type of each element in an
+ ARRAY
+ - |properties| the properties of an OBJECT parameter
"""
def __init__(self, name, json):
+ if not re.match('^[a-z][a-zA-Z0-9]*$', name):
+ raise AssertionError('Name %s must be lowerCamelCase' % name)
self.name = name
+ self._unix_name = _UnixName(self.name)
+ self._unix_name_used = False
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
@@ -97,14 +139,16 @@ class Property(object):
json_type = json['type']
if json_type == 'string':
self.type_ = PropertyType.STRING
- elif json_type == 'boolean':
+ elif json_type == 'any':
+ self.type_ = PropertyType.ANY
+ elif json_type == 'boolean':
self.type_ = PropertyType.BOOLEAN
elif json_type == 'integer':
self.type_ = PropertyType.INTEGER
- elif json_type == 'double':
+ elif json_type == 'number':
self.type_ = PropertyType.DOUBLE
elif json_type == 'array':
- self.item_type = Property(name + "_inner", json['items'])
+ self.item_type = Property(name + "Element", json['items'])
self.type_ = PropertyType.ARRAY
elif json_type == 'object':
self.properties = {}
@@ -114,21 +158,67 @@ class Property(object):
else:
raise NotImplementedError(json_type)
elif 'choices' in json:
- self.type_ = PropertyType.CHOICES
+ assert len(json['choices']), 'Choices has no choices\n%s' % json
self.choices = {}
+ self.type_ = PropertyType.CHOICES
+ for choice_json in json['choices']:
+ choice = Property(self.name, choice_json)
+ # A choice gets its unix_name set in
+ # cpp_type_generator.GetExpandedChoicesInParams
+ choice._unix_name = None
+ # The existence of any single choice is optional
+ choice.optional = True
+ self.choices[choice.type_] = choice
+ else:
+ raise NotImplementedError(json)
+
+ def GetUnixName(self):
+ """Gets the property's unix_name. Raises AttributeError if not set.
+ """
+ if self._unix_name is None:
+ raise AttributeError('No unix_name set on %s' % self.name)
+ self._unix_name_used = True
+ return self._unix_name
+
+ def SetUnixName(self, unix_name):
+ """Set the property's unix_name. Raises AttributeError if the unix_name has
+ already been used (GetUnixName has been called).
+ """
+ if unix_name == self._unix_name:
+ return
+ if self._unix_name_used:
+ raise AttributeError(
+ 'Cannot set the unix_name on %s; '
+ 'it is already used elsewhere as %s' %
+ (self.name, self._unix_name))
+ self._unix_name = unix_name
+
+ unix_name = property(GetUnixName, SetUnixName)
class PropertyType(object):
"""Enum of different types of properties/parameters.
"""
class _Info(object):
- def __init__(self, is_fundamental):
+ def __init__(self, is_fundamental, name):
self.is_fundamental = is_fundamental
+ self.name = name
+
+ def __repr__(self):
+ return self.name
+
+ INTEGER = _Info(True, "INTEGER")
+ DOUBLE = _Info(True, "DOUBLE")
+ BOOLEAN = _Info(True, "BOOLEAN")
+ STRING = _Info(True, "STRING")
+ ARRAY = _Info(False, "ARRAY")
+ REF = _Info(False, "REF")
+ CHOICES = _Info(False, "CHOICES")
+ OBJECT = _Info(False, "OBJECT")
+ ANY = _Info(False, "ANY")
+
+def _UnixName(name):
+ """Returns the unix_style name for a given string in lowerCamelCase format.
+ """
+ return '_'.join([x.lower()
+ for x in re.findall('[A-Z][a-z_]*', name[0].upper() + name[1:])])
- INTEGER = _Info(True)
- DOUBLE = _Info(True)
- BOOLEAN = _Info(True)
- STRING = _Info(True)
- ARRAY = _Info(False)
- REF = _Info(False)
- CHOICES = _Info(False)
- OBJECT = _Info(False)