Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(479)

Unified Diff: third_party/chrome/tools/model.py

Issue 12261015: Import chrome idl into third_party (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/chrome/tools/json_schema_test.py ('k') | third_party/chrome/tools/model_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/chrome/tools/model.py
diff --git a/third_party/chrome/tools/model.py b/third_party/chrome/tools/model.py
new file mode 100644
index 0000000000000000000000000000000000000000..637942790825b1e76a9ac4033777589be57680e7
--- /dev/null
+++ b/third_party/chrome/tools/model.py
@@ -0,0 +1,497 @@
+# 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 copy
+import os.path
+import re
+
+from json_parse import OrderedDict
+
+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.
+
+ Properties:
+ - |namespaces| a map of a namespace name to its model.Namespace
+ """
+ def __init__(self):
+ self.namespaces = {}
+
+ def AddNamespace(self, json, source_file, include_compiler_options=False):
+ """Add a namespace's json to the model and returns the namespace.
+ """
+ namespace = Namespace(json,
+ source_file,
+ include_compiler_options=include_compiler_options)
+ self.namespaces[namespace.name] = namespace
+ return namespace
+
+class Namespace(object):
+ """An API namespace.
+
+ Properties:
+ - |name| the name of the namespace
+ - |unix_name| the unix_name of the namespace
+ - |source_file| the file that contained the namespace definition
+ - |source_file_dir| the directory component of |source_file|
+ - |source_file_filename| the filename component of |source_file|
+ - |platforms| if not None, the list of platforms that the namespace is
+ available to
+ - |types| a map of type names to their model.Type
+ - |functions| a map of function names to their model.Function
+ - |events| a map of event names to their model.Function
+ - |properties| a map of property names to their model.Property
+ - |compiler_options| the compiler_options dict, only present if
+ |include_compiler_options| is True
+ """
+ def __init__(self, json, source_file, include_compiler_options=False):
+ self.name = json['namespace']
+ 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.parent = None
+ self.platforms = _GetPlatforms(json)
+ toplevel_origin = Origin(from_client=True, from_json=True)
+ self.types = _GetTypes(self, json, self, toplevel_origin)
+ self.functions = _GetFunctions(self, json, self)
+ self.events = _GetEvents(self, json, self)
+ self.properties = _GetProperties(self, json, self, toplevel_origin)
+ if include_compiler_options:
+ self.compiler_options = json.get('compiler_options', {})
+
+class Origin(object):
+ """Stores the possible origin of model object as a pair of bools. These are:
+
+ |from_client| indicating that instances can originate from users of
+ generated code (for example, function results), or
+ |from_json| indicating that instances can originate from the JSON (for
+ example, function parameters)
+
+ It is possible for model objects to originate from both the client and json,
+ for example Types defined in the top-level schema, in which case both
+ |from_client| and |from_json| would be True.
+ """
+ def __init__(self, from_client=False, from_json=False):
+ if not from_client and not from_json:
+ raise ValueError('One of from_client or from_json must be true')
+ self.from_client = from_client
+ self.from_json = from_json
+
+class Type(object):
+ """A Type defined in the json.
+
+ Properties:
+ - |name| the type name
+ - |namespace| the Type's namespace
+ - |description| the description of the type (if provided)
+ - |properties| a map of property unix_names to their model.Property
+ - |functions| a map of function names to their model.Function
+ - |events| a map of event names to their model.Event
+ - |origin| the Origin of the type
+ - |property_type| the PropertyType of this Type
+ - |item_type| if this is an array, the type of items in the array
+ - |simple_name| the name of this Type without a namespace
+ - |additional_properties| the type of the additional properties, if any is
+ specified
+ """
+ def __init__(self,
+ parent,
+ name,
+ json,
+ namespace,
+ origin):
+ self.name = name
+ self.namespace = namespace
+ self.simple_name = _StripNamespace(self.name, namespace)
+ self.unix_name = UnixName(self.name)
+ self.description = json.get('description', None)
+ self.origin = origin
+ self.parent = parent
+ self.instance_of = json.get('isInstanceOf', None)
+
+ # TODO(kalman): Only objects need functions/events/properties, but callers
+ # assume that all types have them. Fix this.
+ self.functions = _GetFunctions(self, json, namespace)
+ self.events = _GetEvents(self, json, namespace)
+ self.properties = _GetProperties(self, json, namespace, origin)
+
+ json_type = json.get('type', None)
+ if json_type == 'array':
+ self.property_type = PropertyType.ARRAY
+ self.item_type = Type(
+ self, '%sType' % name, json['items'], namespace, origin)
+ elif '$ref' in json:
+ self.property_type = PropertyType.REF
+ self.ref_type = json['$ref']
+ elif 'enum' in json and json_type == 'string':
+ self.property_type = PropertyType.ENUM
+ self.enum_values = [value for value in json['enum']]
+ elif json_type == 'any':
+ self.property_type = PropertyType.ANY
+ elif json_type == 'binary':
+ self.property_type = PropertyType.BINARY
+ elif json_type == 'boolean':
+ self.property_type = PropertyType.BOOLEAN
+ elif json_type == 'integer':
+ self.property_type = PropertyType.INTEGER
+ elif (json_type == 'double' or
+ json_type == 'number'):
+ self.property_type = PropertyType.DOUBLE
+ elif json_type == 'string':
+ self.property_type = PropertyType.STRING
+ elif 'choices' in json:
+ self.property_type = PropertyType.CHOICES
+ self.choices = [Type(self,
+ # The name of the choice type - there had better be
+ # either a type or a $ref specified for the choice.
+ json.get('type', json.get('$ref')),
+ json,
+ namespace,
+ origin)
+ for json in json['choices']]
+ elif json_type == 'object':
+ if not (
+ 'properties' in json or
+ 'additionalProperties' in json or
+ 'functions' in json or
+ 'events' in json):
+ raise ParseException(self, name + " has no properties or functions")
+ self.property_type = PropertyType.OBJECT
+ additional_properties_json = json.get('additionalProperties', None)
+ if additional_properties_json is not None:
+ self.additional_properties = Type(self,
+ 'additionalProperties',
+ additional_properties_json,
+ namespace,
+ origin)
+ else:
+ self.additional_properties = None
+ elif json_type == 'function':
+ self.property_type = PropertyType.FUNCTION
+ # Sometimes we might have an unnamed function, e.g. if it's a property
+ # of an object. Use the name of the property in that case.
+ function_name = json.get('name', name)
+ self.function = Function(self, function_name, json, namespace, origin)
+ else:
+ raise ParseException(self, 'Unsupported JSON type %s' % json_type)
+
+class Function(object):
+ """A Function defined in the API.
+
+ Properties:
+ - |name| the function name
+ - |platforms| if not None, the list of platforms that the function is
+ available to
+ - |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
+ - |optional| whether the Function is "optional"; this only makes sense to be
+ present when the Function is representing a callback property
+ - |simple_name| the name of this Function without a namespace
+ """
+ def __init__(self,
+ parent,
+ name,
+ json,
+ namespace,
+ origin):
+ self.name = name
+ self.simple_name = _StripNamespace(self.name, namespace)
+ self.platforms = _GetPlatforms(json)
+ self.params = []
+ self.description = json.get('description')
+ self.callback = None
+ self.optional = json.get('optional', False)
+ self.parent = parent
+ self.nocompile = json.get('nocompile')
+ options = json.get('options', {})
+ self.conditions = options.get('conditions', [])
+ self.actions = options.get('actions', [])
+ self.supports_listeners = options.get('supportsListeners', True)
+ self.supports_rules = options.get('supportsRules', False)
+
+ def GeneratePropertyFromParam(p):
+ return Property.FromJSON(self, p['name'], p, namespace, origin)
+
+ self.filters = [GeneratePropertyFromParam(filter)
+ for filter in json.get('filters', [])]
+ callback_param = None
+ for param in json.get('parameters', []):
+ if param.get('type') == 'function':
+ if callback_param:
+ # No ParseException because the webstore has this.
+ # Instead, pretend all intermediate callbacks are properties.
+ self.params.append(GeneratePropertyFromParam(callback_param))
+ callback_param = param
+ else:
+ self.params.append(GeneratePropertyFromParam(param))
+
+ if callback_param:
+ self.callback = Function(self,
+ callback_param['name'],
+ callback_param,
+ namespace,
+ Origin(from_client=True))
+
+ self.returns = None
+ if 'returns' in json:
+ self.returns = Property.FromJSON(
+ self, 'return', json['returns'], namespace, origin)
+
+class Property(object):
+ """A property of a type OR a parameter to a function.
+ 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.Type of this property
+ - |simple_name| the name of this Property without a namespace
+ """
+
+ @staticmethod
+ def FromJSON(parent, name, json, namespace, origin):
+ """Creates a Property from JSON.
+ """
+ opt_args = {}
+ if 'description' in json:
+ opt_args['description'] = json['description']
+ if 'optional' in json:
+ opt_args['optional'] = json.get('optional')
+ if 'isInstanceOf' in json:
+ opt_args['instance_of'] = json.get('isInstanceOf')
+
+ # HACK: only support very specific value types.
+ is_allowed_value = (
+ '$ref' not in json and
+ ('type' not in json or json['type'] == 'integer'
+ or json['type'] == 'string'))
+
+ if 'value' in json and is_allowed_value:
+ value = json['value']
+ opt_args['value'] = value
+ if 'type' not in json:
+ # Sometimes the type of the value is left out, and we need to figure
+ # it out for ourselves.
+ if isinstance(value, int):
+ json['type'] = 'integer'
+ elif isinstance(value, basestring):
+ json['type'] = 'string'
+ else:
+ # TODO(kalman): support more types as necessary.
+ raise ParseException(
+ parent, '"%s" is not a supported type for "value"' % type(value))
+
+ type_ = Type(parent, name, json, namespace, origin)
+ return Property(parent,
+ name,
+ namespace,
+ type_,
+ origin,
+ **opt_args);
+
+ def __init__(self,
+ parent,
+ name,
+ namespace,
+ type_,
+ origin,
+ description=None,
+ optional=False,
+ returns=None,
+ instance_of=None,
+ value=None):
+ """Directly initializes the fields of the Property.
+ """
+ self.name = name
+ self.simple_name = _StripNamespace(self.name, namespace)
+ self._unix_name = UnixName(self.name)
+ self._unix_name_used = False
+ self.optional = optional
+ self.description = description
+ self.parent = parent
+ self.origin = origin
+ if not isinstance(type_, Type):
+ raise ValueError("not Type: %s" % type_)
+ self.type_ = type_
+ self.returns = returns
+ if instance_of is not None:
+ self.instance_of = instance_of
+ self.value = value
+
+ def GetUnixName(self):
+ """Gets the property's unix_name. Raises AttributeError if not set.
+ """
+ if not self._unix_name:
+ 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 _Enum(object):
+ """Superclass for enum types with a "name" field, setting up repr/eq/ne.
+ Enums need to do this so that equality/non-equality work over pickling.
+ """
+
+ @staticmethod
+ def GetAll(cls):
+ """Yields all _Enum objects declared in |cls|.
+ """
+ for prop_key in dir(cls):
+ prop_value = getattr(cls, prop_key)
+ if isinstance(prop_value, _Enum):
+ yield prop_value
+
+ def __init__(self, name):
+ self.name = name
+
+ def __repr(self):
+ return self.name
+
+ def __eq__(self, other):
+ return type(other) == type(self) and other.name == self.name
+
+ def __ne__(self, other):
+ return not (self == other)
+
+class _PropertyTypeInfo(_Enum):
+ def __init__(self, is_fundamental, name):
+ _Enum.__init__(self, name)
+ self.is_fundamental = is_fundamental
+
+class PropertyType(object):
+ """Enum of different types of properties/parameters.
+ """
+ INTEGER = _PropertyTypeInfo(True, "integer")
+ INT64 = _PropertyTypeInfo(True, "int64")
+ DOUBLE = _PropertyTypeInfo(True, "double")
+ BOOLEAN = _PropertyTypeInfo(True, "boolean")
+ STRING = _PropertyTypeInfo(True, "string")
+ ENUM = _PropertyTypeInfo(False, "enum")
+ ARRAY = _PropertyTypeInfo(False, "array")
+ REF = _PropertyTypeInfo(False, "ref")
+ CHOICES = _PropertyTypeInfo(False, "choices")
+ OBJECT = _PropertyTypeInfo(False, "object")
+ FUNCTION = _PropertyTypeInfo(False, "function")
+ BINARY = _PropertyTypeInfo(False, "binary")
+ ANY = _PropertyTypeInfo(False, "any")
+
+def UnixName(name):
+ """Returns the unix_style name for a given lowerCamelCase string.
+ """
+ # First replace any lowerUpper patterns with lower_Upper.
+ s1 = re.sub('([a-z])([A-Z])', r'\1_\2', name)
+ # Now replace any ACMEWidgets patterns with ACME_Widgets
+ s2 = re.sub('([A-Z]+)([A-Z][a-z])', r'\1_\2', s1)
+ # Finally, replace any remaining periods, and make lowercase.
+ return s2.replace('.', '_').lower()
+
+def _StripNamespace(name, namespace):
+ if name.startswith(namespace.name + '.'):
+ return name[len(namespace.name + '.'):]
+ return name
+
+def _GetModelHierarchy(entity):
+ """Returns the hierarchy of the given model entity."""
+ hierarchy = []
+ while entity is not None:
+ hierarchy.append(getattr(entity, 'name', repr(entity)))
+ if isinstance(entity, Namespace):
+ hierarchy.insert(0, ' in %s' % entity.source_file)
+ entity = getattr(entity, 'parent', None)
+ hierarchy.reverse()
+ return hierarchy
+
+def _GetTypes(parent, json, namespace, origin):
+ """Creates Type objects extracted from |json|.
+ """
+ types = OrderedDict()
+ for type_json in json.get('types', []):
+ type_ = Type(parent, type_json['id'], type_json, namespace, origin)
+ types[type_.name] = type_
+ return types
+
+def _GetFunctions(parent, json, namespace):
+ """Creates Function objects extracted from |json|.
+ """
+ functions = OrderedDict()
+ for function_json in json.get('functions', []):
+ function = Function(parent,
+ function_json['name'],
+ function_json,
+ namespace,
+ Origin(from_json=True))
+ functions[function.name] = function
+ return functions
+
+def _GetEvents(parent, json, namespace):
+ """Creates Function objects generated from the events in |json|.
+ """
+ events = OrderedDict()
+ for event_json in json.get('events', []):
+ event = Function(parent,
+ event_json['name'],
+ event_json,
+ namespace,
+ Origin(from_client=True))
+ events[event.name] = event
+ return events
+
+def _GetProperties(parent, json, namespace, origin):
+ """Generates Property objects extracted from |json|.
+ """
+ properties = OrderedDict()
+ for name, property_json in json.get('properties', {}).items():
+ properties[name] = Property.FromJSON(
+ parent, name, property_json, namespace, origin)
+ return properties
+
+class _PlatformInfo(_Enum):
+ def __init__(self, name):
+ _Enum.__init__(self, name)
+
+class Platforms(object):
+ """Enum of the possible platforms.
+ """
+ CHROMEOS = _PlatformInfo("chromeos")
+ CHROMEOS_TOUCH = _PlatformInfo("chromeos_touch")
+ LINUX = _PlatformInfo("linux")
+ MAC = _PlatformInfo("mac")
+ WIN = _PlatformInfo("win")
+
+def _GetPlatforms(json):
+ if 'platforms' not in json:
+ return None
+ platforms = []
+ for platform_name in json['platforms']:
+ for platform_enum in _Enum.GetAll(Platforms):
+ if platform_name == platform_enum.name:
+ platforms.append(platform_enum)
+ break
+ return platforms
« no previous file with comments | « third_party/chrome/tools/json_schema_test.py ('k') | third_party/chrome/tools/model_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698