diff options
author | calamity@chromium.org <calamity@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-15 14:52:32 +0000 |
---|---|---|
committer | calamity@chromium.org <calamity@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-15 14:52:32 +0000 |
commit | cfe484905085bc55742da39f792a571615cb419b (patch) | |
tree | d655642171deba8dcbb57d5cdbe9693ee2e42dd8 /tools/json_schema_compiler/model.py | |
parent | 498e0a6eb258f10ad76039c3028d0f7a4ad365e4 (diff) | |
download | chromium_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.py | 144 |
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) |