summaryrefslogtreecommitdiffstats
path: root/tools/json_schema_compiler/model.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/json_schema_compiler/model.py')
-rw-r--r--tools/json_schema_compiler/model.py147
1 files changed, 87 insertions, 60 deletions
diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py
index 9a5c55f..6dc20fe 100644
--- a/tools/json_schema_compiler/model.py
+++ b/tools/json_schema_compiler/model.py
@@ -6,6 +6,15 @@ import copy
import os.path
import re
+class ParseException(Exception):
+ """Thrown when data in the model is invalid.
+ """
+ def __init__(self, parent, message):
+ hierarchy = _GetModelHierarchy(parent)
+ hierarchy.append(message)
+ Exception.__init__(
+ self, 'Model parse exception at:\n' + '\n'.join(hierarchy))
+
class Model(object):
"""Model of all namespaces that comprise an API.
@@ -33,24 +42,17 @@ class Namespace(object):
- |source_file_filename| the filename component of |source_file|
- |types| a map of type names to their model.Type
- |functions| a map of function names to their model.Function
+ - |properties| a map of property names to their model.Property
"""
def __init__(self, json, source_file):
self.name = json['namespace']
- self.unix_name = UnixName(self.name)
+ self.unix_name = _UnixName(self.name)
self.source_file = source_file
self.source_file_dir, self.source_file_filename = os.path.split(source_file)
- self.types = {}
- self.functions = {}
self.parent = None
- # TODO(calamity): Implement properties on namespaces for shared structures
- # or constants across a namespace (e.g Windows::WINDOW_ID_NONE).
- for property_json in json.get('properties', []):
- pass
- for type_json in json.get('types', []):
- type_ = Type(self, type_json['id'], type_json)
- self.types[type_.name] = type_
- for function_json in json.get('functions', []):
- self.functions[function_json['name']] = Function(self, function_json)
+ _AddTypes(self, json)
+ _AddFunctions(self, json)
+ _AddProperties(self, json)
class Type(object):
"""A Type defined in the json.
@@ -79,44 +81,24 @@ class Type(object):
'properties' in json or
'additionalProperties' in json or
'functions' in json):
- raise ParseException(name + " has no properties or functions")
+ raise ParseException(self, name + " has no properties or functions")
self.type_ = PropertyType.OBJECT
self.name = name
self.description = json.get('description')
self.from_json = True
self.from_client = True
- self.properties = {}
- self.functions = {}
self.parent = parent
- for function_json in json.get('functions', []):
- self.functions[function_json['name']] = Function(self, function_json)
- props = []
- for prop_name, prop_json in json.get('properties', {}).items():
- # TODO(calamity): support functions (callbacks) as properties. The model
- # doesn't support it yet because the h/cc generators don't -- this is
- # because we'd need to hook it into a base::Callback or something.
- #
- # However, pragmatically it's not necessary to support them anyway, since
- # the instances of functions-on-properties in the extension APIs are all
- # handled in pure Javascript on the render process (and .: never reach
- # C++ let alone the browser).
- if prop_json.get('type') == 'function':
- continue
- props.append(Property(self, prop_name, prop_json,
- from_json=True,
- from_client=True))
+ _AddFunctions(self, json)
+ _AddProperties(self, json, from_json=True, from_client=True)
- additional_properties = json.get('additionalProperties')
+ additional_properties_key = 'additionalProperties'
+ additional_properties = json.get(additional_properties_key)
if additional_properties:
- props.append(Property(self, 'additionalProperties', additional_properties,
- is_additional_properties=True))
-
- for prop in props:
- if prop.unix_name in self.properties:
- raise ParseException(
- self.properties[prop.unix_name].name + ' and ' + prop.name +
- ' are both named ' + prop.unix_name)
- self.properties[prop.unix_name] = prop
+ self.properties[additional_properties_key] = Property(
+ self,
+ additional_properties_key,
+ additional_properties,
+ is_additional_properties=True)
class Callback(object):
"""A callback parameter to a Function.
@@ -135,7 +117,9 @@ class Callback(object):
self.params.append(Property(self, param['name'], param,
from_client=True))
else:
- raise ParseException("Callbacks can have at most a single parameter")
+ raise ParseException(
+ self,
+ "Callbacks can have at most a single parameter")
class Function(object):
"""A Function defined in the API.
@@ -157,7 +141,7 @@ class Function(object):
for param in json['parameters']:
if param.get('type') == 'function':
if self.callback:
- raise ParseException(self.name + " has more than one callback")
+ raise ParseException(self, self.name + " has more than one callback")
self.callback = Callback(self, param)
else:
self.params.append(Property(self, param['name'], param,
@@ -191,11 +175,13 @@ class Property(object):
users of generated code, such as top-level types and function results
"""
self.name = name
- self._unix_name = UnixName(self.name)
+ self._unix_name = _UnixName(self.name)
self._unix_name_used = False
self.optional = json.get('optional', False)
+ self.has_value = False
self.description = json.get('description')
self.parent = parent
+ _AddProperties(self, json)
if is_additional_properties:
self.type_ = PropertyType.ADDITIONAL_PROPERTIES
elif '$ref' in json:
@@ -226,17 +212,17 @@ class Property(object):
elif json_type == 'object':
self.type_ = PropertyType.OBJECT
# These members are read when this OBJECT Property is used as a Type
- self.properties = {}
self.from_json = from_json
self.from_client = from_client
type_ = Type(self, self.name, json)
- self.properties = type_.properties
+ # self.properties will already have some value from |_AddProperties|.
+ self.properties.update(type_.properties)
self.functions = type_.functions
else:
raise ParseException(self, 'type ' + json_type + ' not recognized')
elif 'choices' in json:
if not json['choices']:
- raise ParseException('Choices has no choices')
+ raise ParseException(self, 'Choices has no choices')
self.choices = {}
self.type_ = PropertyType.CHOICES
for choice_json in json['choices']:
@@ -249,13 +235,23 @@ class Property(object):
# The existence of any single choice is optional
choice.optional = True
self.choices[choice.type_] = choice
+ elif 'value' in json:
+ self.has_value = True
+ self.value = json['value']
+ if type(self.value) == int:
+ self.type_ = PropertyType.INTEGER
+ else:
+ # TODO(kalman): support more types as necessary.
+ raise ParseException(
+ self, '"%s" is not a supported type' % type(self.value))
else:
- raise ParseException('Property has no type, $ref or choices')
+ raise ParseException(
+ self, 'Property has no type, $ref, choices, or value')
def GetUnixName(self):
"""Gets the property's unix_name. Raises AttributeError if not set.
"""
- if self._unix_name is None:
+ if not self._unix_name:
raise AttributeError('No unix_name set on %s' % self.name)
self._unix_name_used = True
return self._unix_name
@@ -306,7 +302,7 @@ class PropertyType(object):
ANY = _Info(False, "ANY")
ADDITIONAL_PROPERTIES = _Info(False, "ADDITIONAL_PROPERTIES")
-def UnixName(name):
+def _UnixName(name):
"""Returns the unix_style name for a given lowerCamelCase string.
"""
# First replace any lowerUpper patterns with lower_Upper.
@@ -316,15 +312,7 @@ def UnixName(name):
# Finally, replace any remaining periods, and make lowercase.
return s2.replace('.', '_').lower()
-class ParseException(Exception):
- """Thrown when data in the model is invalid."""
- def __init__(self, parent, message):
- hierarchy = GetModelHierarchy(parent)
- hierarchy.append(message)
- Exception.__init__(
- self, 'Model parse exception at:\n' + '\n'.join(hierarchy))
-
-def GetModelHierarchy(entity):
+def _GetModelHierarchy(entity):
"""Returns the hierarchy of the given model entity."""
hierarchy = []
while entity:
@@ -335,3 +323,42 @@ def GetModelHierarchy(entity):
entity = entity.parent
hierarchy.reverse()
return hierarchy
+
+def _AddTypes(model, json):
+ """Adds Type objects to |model| contained in the 'types' field of |json|.
+ """
+ model.types = {}
+ for type_json in json.get('types', []):
+ type_ = Type(model, type_json['id'], type_json)
+ model.types[type_.name] = type_
+
+def _AddFunctions(model, json):
+ """Adds Function objects to |model| contained in the 'types' field of |json|.
+ """
+ model.functions = {}
+ for function_json in json.get('functions', []):
+ function = Function(model, function_json)
+ model.functions[function.name] = function
+
+def _AddProperties(model, json, from_json=False, from_client=False):
+ """Adds model.Property objects to |model| contained in the 'properties' field
+ of |json|.
+ """
+ model.properties = {}
+ for name, property_json in json.get('properties', {}).items():
+ # TODO(calamity): support functions (callbacks) as properties. The model
+ # doesn't support it yet because the h/cc generators don't -- this is
+ # because we'd need to hook it into a base::Callback or something.
+ #
+ # However, pragmatically it's not necessary to support them anyway, since
+ # the instances of functions-on-properties in the extension APIs are all
+ # handled in pure Javascript on the render process (and .: never reach
+ # C++ let alone the browser).
+ if property_json.get('type') == 'function':
+ continue
+ model.properties[name] = Property(
+ model,
+ name,
+ property_json,
+ from_json=from_json,
+ from_client=from_client)