diff options
author | kalman <kalman@chromium.org> | 2015-04-01 19:04:10 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-02 02:05:38 +0000 |
commit | d94f954f5e1c32e7be85e2ba835a143fd388a0d8 (patch) | |
tree | 5d9c34720d21f1b40282213e9e8738e56cc43427 /tools/json_schema_compiler | |
parent | d205eb31669df40e3564071c600adc377036bad4 (diff) | |
download | chromium_src-d94f954f5e1c32e7be85e2ba835a143fd388a0d8.zip chromium_src-d94f954f5e1c32e7be85e2ba835a143fd388a0d8.tar.gz chromium_src-d94f954f5e1c32e7be85e2ba835a143fd388a0d8.tar.bz2 |
Support JSON Schema properties with values in the JSON Schema Compiler.
This brings the Extension IDL Schema feature set closer to the JSON Schema
feature set. In particular, the MDNS API needs this functionality.
Unfortunately the PPAPI IDL parser doesn't support WebIDL's "const" keyword,
which would be the right way to do this. Instead we fake it (though, consistent
with functions and events); to define a JSON Schema property, IDL Schemas
declare a 'Properties' namespace populated with no-argument functions,
annotating each function with the value they should be mapped to.
For example, this IDL:
namespace myApi {
interface Properties {
// A constant.
[value=42] static long someConstant();
};
};
will generate this JSON Schema:
{
"namespace": "myApi",
"properties": {
"someConstant": {
"value": "42",
"description": "A constant",
"type": "integer",
}
}
}
and finally, an integer myApi::some_constant equal to 42 in C++.
R=rdevlin.cronin@chromium.org
Review URL: https://codereview.chromium.org/1051563002
Cr-Commit-Position: refs/heads/master@{#323401}
Diffstat (limited to 'tools/json_schema_compiler')
-rw-r--r-- | tools/json_schema_compiler/cpp_type_generator.py | 12 | ||||
-rwxr-xr-x | tools/json_schema_compiler/idl_schema.py | 81 | ||||
-rwxr-xr-x | tools/json_schema_compiler/idl_schema_test.py | 24 | ||||
-rw-r--r-- | tools/json_schema_compiler/test/idl_properties.idl | 20 |
4 files changed, 118 insertions, 19 deletions
diff --git a/tools/json_schema_compiler/cpp_type_generator.py b/tools/json_schema_compiler/cpp_type_generator.py index 5a573b6..944dba9 100644 --- a/tools/json_schema_compiler/cpp_type_generator.py +++ b/tools/json_schema_compiler/cpp_type_generator.py @@ -252,11 +252,15 @@ class CppTypeGenerator(object): c.Comment(prop.description) if prop.value is not None: + cpp_type = self.GetCppType(prop.type_) + cpp_value = prop.value + if cpp_type == 'std::string': + cpp_value = '"%s"' % cpp_type c.Append(line % { - "type": self.GetCppType(prop.type_), - "name": prop.name, - "value": prop.value - }) + "type": cpp_type, + "name": prop.name, + "value": cpp_value + }) else: has_child_code = False c.Sblock('namespace %s {' % prop.name) diff --git a/tools/json_schema_compiler/idl_schema.py b/tools/json_schema_compiler/idl_schema.py index 7364800..65d7bae 100755 --- a/tools/json_schema_compiler/idl_schema.py +++ b/tools/json_schema_compiler/idl_schema.py @@ -6,6 +6,7 @@ import itertools import json import os.path +import pprint import re import sys @@ -167,7 +168,7 @@ class Member(object): def __init__(self, member_node): self.node = member_node - def process(self, callbacks): + def process(self, callbacks, functions_are_properties=False): properties = OrderedDict() name = self.node.GetName() if self.node.GetProperty('deprecated'): @@ -187,34 +188,69 @@ class Member(object): properties['options'] = {} properties['options'][option_name] = sanitizer(self.node.GetProperty( option_name)) - is_function = False + type_override = None parameter_comments = OrderedDict() for node in self.node.GetChildren(): if node.cls == 'Comment': (parent_comment, parameter_comments) = ProcessComment(node.GetName()) properties['description'] = parent_comment elif node.cls == 'Callspec': - is_function = True name, parameters, return_type = (Callspec(node, parameter_comments) .process(callbacks)) - properties['parameters'] = parameters - if return_type is not None: - properties['returns'] = return_type + if functions_are_properties: + # If functions are treated as properties (which will happen if the + # interface is named Properties) then this isn't a function, it's a + # property which is encoded as a function with no arguments. The + # property type is the return type. This is an egregious hack in lieu + # of the IDL parser supporting 'const'. + assert parameters == [], ( + 'Property "%s" must be no-argument functions ' + 'with a non-void return type' % name) + assert return_type is not None, ( + 'Property "%s" must be no-argument functions ' + 'with a non-void return type' % name) + assert 'type' in return_type, ( + 'Property return type "%s" from "%s" must specify a ' + 'fundamental IDL type.' % (pprint.pformat(return_type), name)) + type_override = return_type['type'] + else: + type_override = 'function' + properties['parameters'] = parameters + if return_type is not None: + properties['returns'] = return_type properties['name'] = name - if is_function: - properties['type'] = 'function' + if type_override is not None: + properties['type'] = type_override else: properties = Typeref(self.node.GetProperty('TYPEREF'), self.node, properties).process(callbacks) + value = self.node.GetProperty('value') + if value is not None: + # IDL always returns values as strings, so cast to their real type. + properties['value'] = self.cast_from_json_type(properties['type'], value) enum_values = self.node.GetProperty('legalValues') if enum_values: - if properties['type'] == 'integer': - enum_values = map(int, enum_values) - elif properties['type'] == 'double': - enum_values = map(float, enum_values) - properties['enum'] = enum_values + # IDL always returns enum values as strings, so cast to their real type. + properties['enum'] = [self.cast_from_json_type(properties['type'], enum) + for enum in enum_values] return name, properties + def cast_from_json_type(self, json_type, string_value): + '''Casts from string |string_value| to a real Python type based on a JSON + Schema type |json_type|. For example, a string value of '42' and a JSON + Schema type 'integer' will cast to int('42') ==> 42. + ''' + if json_type == 'integer': + return int(string_value) + if json_type == 'number': + return float(string_value) + # Add more as necessary. + assert json_type == 'string', ( + 'No rule exists to cast JSON Schema type "%s" to its equivalent ' + 'Python type for value "%s". You must add a new rule here.' % + (json_type, string_value)) + return string_value + class Typeref(object): ''' @@ -352,6 +388,7 @@ class Namespace(object): self.compiler_options = compiler_options self.events = [] self.functions = [] + self.properties = OrderedDict() self.types = [] self.callbacks = OrderedDict() self.description = description @@ -369,6 +406,17 @@ class Namespace(object): self.functions = self.process_interface(node) elif node.cls == 'Interface' and node.GetName() == 'Events': self.events = self.process_interface(node) + elif node.cls == 'Interface' and node.GetName() == 'Properties': + properties_as_list = self.process_interface( + node, functions_are_properties=True) + for prop in properties_as_list: + # Properties are given as key-value pairs, but IDL will parse + # it as a list. Convert back to key-value pairs. + prop_name = prop.pop('name') + assert not self.properties.has_key(prop_name), ( + 'Property "%s" cannot be specified more than once.' % + prop_name) + self.properties[prop_name] = prop elif node.cls == 'Enum': self.types.append(Enum(node).process()) else: @@ -380,6 +428,7 @@ class Namespace(object): 'nodoc': self.nodoc, 'types': self.types, 'functions': self.functions, + 'properties': self.properties, 'internal': self.internal, 'events': self.events, 'platforms': self.platforms, @@ -387,11 +436,13 @@ class Namespace(object): 'deprecated': self.deprecated, 'documentation_options': documentation_options} - def process_interface(self, node): + def process_interface(self, node, functions_are_properties=False): members = [] for member in node.GetChildren(): if member.cls == 'Member': - _, properties = Member(member).process(self.callbacks) + _, properties = Member(member).process( + self.callbacks, + functions_are_properties=functions_are_properties) members.append(properties) return members diff --git a/tools/json_schema_compiler/idl_schema_test.py b/tools/json_schema_compiler/idl_schema_test.py index a045716..03960c3 100755 --- a/tools/json_schema_compiler/idl_schema_test.py +++ b/tools/json_schema_compiler/idl_schema_test.py @@ -391,6 +391,30 @@ class IdlSchemaTest(unittest.TestCase): self.assertEquals(expected, badabish_params) + def testProperties(self): + schema = idl_schema.Load('test/idl_properties.idl')[0] + self.assertEquals(OrderedDict([ + ('first', OrderedDict([ + ('description', 'Integer property.'), + ('type', 'integer'), + ('value', 42), + ])), + ('second', OrderedDict([ + ('description', 'Double property.'), + ('type', 'number'), + ('value', 42.0), + ])), + ('third', OrderedDict([ + ('description', 'String property.'), + ('type', 'string'), + ('value', 'hello world'), + ])), + ('fourth', OrderedDict([ + ('description', 'Unvalued property.'), + ('type', 'integer'), + ])), + ]), schema.get('properties')) + if __name__ == '__main__': unittest.main() diff --git a/tools/json_schema_compiler/test/idl_properties.idl b/tools/json_schema_compiler/test/idl_properties.idl new file mode 100644 index 0000000..5501602 --- /dev/null +++ b/tools/json_schema_compiler/test/idl_properties.idl @@ -0,0 +1,20 @@ +// Copyright 2015 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. + +// Tests parsing the Properties interface. +namespace idl_properties { + interface Properties { + // Integer property. + [value=42] static long first(); + + // Double property. + [value=42.0] static double second(); + + // String property. + [value="hello world"] static DOMString third(); + + // Unvalued property. + static long fourth(); + }; +}; |