summaryrefslogtreecommitdiffstats
path: root/tools/json_schema_compiler
diff options
context:
space:
mode:
authorcalamity@chromium.org <calamity@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-02 15:05:27 +0000
committercalamity@chromium.org <calamity@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-02 15:05:27 +0000
commitfeba21e3dd6f25b0927817bdad749e47490e2798 (patch)
treeda16581c8c1658b385d6230423cc726df04cf4b7 /tools/json_schema_compiler
parent7ae87817552c6e8608f62304354fd88cb921a31c (diff)
downloadchromium_src-feba21e3dd6f25b0927817bdad749e47490e2798.zip
chromium_src-feba21e3dd6f25b0927817bdad749e47490e2798.tar.gz
chromium_src-feba21e3dd6f25b0927817bdad749e47490e2798.tar.bz2
json_schema_compiler: any, additionalProperties, functions on types
Add support and tests for more json types. Also fixed a number of API jsons. BUG= TEST= Review URL: http://codereview.chromium.org/9491002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@124643 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/json_schema_compiler')
-rw-r--r--tools/json_schema_compiler/any.cc28
-rw-r--r--tools/json_schema_compiler/any.h40
-rw-r--r--tools/json_schema_compiler/any_helper.py32
-rw-r--r--tools/json_schema_compiler/api_gen_util.gyp1
-rw-r--r--tools/json_schema_compiler/cc_generator.py162
-rw-r--r--tools/json_schema_compiler/code.py49
-rw-r--r--tools/json_schema_compiler/code_test.py12
-rw-r--r--tools/json_schema_compiler/compiler.py4
-rw-r--r--tools/json_schema_compiler/cpp_type_generator.py9
-rw-r--r--tools/json_schema_compiler/cpp_type_generator_test.py6
-rw-r--r--tools/json_schema_compiler/cpp_util.py1
-rw-r--r--tools/json_schema_compiler/h_generator.py76
-rw-r--r--tools/json_schema_compiler/model.py139
-rw-r--r--tools/json_schema_compiler/model_test.py13
-rwxr-xr-xtools/json_schema_compiler/previewserver.py2
-rw-r--r--tools/json_schema_compiler/test/additionalProperties.json55
-rw-r--r--tools/json_schema_compiler/test/additional_properties_unittest.cc70
-rw-r--r--tools/json_schema_compiler/test/any.json54
-rw-r--r--tools/json_schema_compiler/test/any_unittest.cc59
-rw-r--r--tools/json_schema_compiler/test/arrays.json17
-rw-r--r--tools/json_schema_compiler/test/arrays_unittest.cc16
-rw-r--r--tools/json_schema_compiler/test/functionsOnTypes.json74
-rw-r--r--tools/json_schema_compiler/test/functions_on_types_unittest.cc68
-rw-r--r--tools/json_schema_compiler/test/json_schema_compiler_tests.gyp3
-rw-r--r--tools/json_schema_compiler/util.cc19
-rw-r--r--tools/json_schema_compiler/util.h13
26 files changed, 868 insertions, 154 deletions
diff --git a/tools/json_schema_compiler/any.cc b/tools/json_schema_compiler/any.cc
new file mode 100644
index 0000000..b429567
--- /dev/null
+++ b/tools/json_schema_compiler/any.cc
@@ -0,0 +1,28 @@
+// 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.
+
+#include "tools/json_schema_compiler/any.h"
+
+#include "base/logging.h"
+#include "base/values.h"
+
+namespace json_schema_compiler {
+namespace any {
+
+Any::Any() {}
+
+Any::~Any() {}
+
+void Any::Init(const base::Value& from_value) {
+ CHECK(!value_.get());
+ value_.reset(from_value.DeepCopy());
+}
+
+const base::Value& Any::value() const {
+ CHECK(value_.get());
+ return *value_;
+}
+
+} // namespace any
+} // namespace json_schema_compiler
diff --git a/tools/json_schema_compiler/any.h b/tools/json_schema_compiler/any.h
new file mode 100644
index 0000000..8169313
--- /dev/null
+++ b/tools/json_schema_compiler/any.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef TOOLS_JSON_SCHEMA_COMPILER_ANY_H__
+#define TOOLS_JSON_SCHEMA_COMPILER_ANY_H__
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+class Value;
+}
+
+namespace json_schema_compiler {
+namespace any {
+
+// Represents an "any" type in JSON schema as a wrapped Value.
+class Any {
+ public:
+ Any();
+ ~Any();
+
+ // Initializes the Value in this Any. Fails if already initialized.
+ void Init(const base::Value& from_value);
+
+ // Get the Value from this Any.
+ const base::Value& value() const;
+
+ private:
+ scoped_ptr<base::Value> value_;
+
+ DISALLOW_COPY_AND_ASSIGN(Any);
+};
+
+} // namespace any
+} // namespace json_schema_compiler
+
+#endif // TOOLS_JSON_SCHEMA_COMPILER_ANY_H__
diff --git a/tools/json_schema_compiler/any_helper.py b/tools/json_schema_compiler/any_helper.py
new file mode 100644
index 0000000..4502dab
--- /dev/null
+++ b/tools/json_schema_compiler/any_helper.py
@@ -0,0 +1,32 @@
+# 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.
+
+ANY_NAMESPACE = 'json_schema_compiler::any'
+ANY_CLASS = ANY_NAMESPACE + '::Any'
+
+class AnyHelper(object):
+ """A util class that generates code that uses
+ tools/json_schema_compiler/any.cc.
+ """
+ def Init(self, any_prop, src, dst):
+ """Initialize |dst|.|any_prop| to |src|.
+
+ src: Value*
+ dst: Type*
+ """
+ if any_prop.optional:
+ return '%s->%s->Init(*%s)' % (dst, any_prop.name, src)
+ else:
+ return '%s->%s.Init(*%s)' % (dst, any_prop.name, src)
+
+ def GetValue(self, any_prop, var):
+ """Get |var| as a const Value&.
+
+ var: Any* or Any
+ """
+ if any_prop.optional:
+ return '%s->value()' % var
+ else:
+ return '%s.value()' % var
+
diff --git a/tools/json_schema_compiler/api_gen_util.gyp b/tools/json_schema_compiler/api_gen_util.gyp
index 54966cc..b1c220d 100644
--- a/tools/json_schema_compiler/api_gen_util.gyp
+++ b/tools/json_schema_compiler/api_gen_util.gyp
@@ -10,6 +10,7 @@
'target_name': 'api_gen_util',
'type': 'static_library',
'sources': [
+ 'any.cc',
'util.cc',
],
'dependencies': ['<(DEPTH)/base/base.gyp:base'],
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py
index bbda061..ce65ed2 100644
--- a/tools/json_schema_compiler/cc_generator.py
+++ b/tools/json_schema_compiler/cc_generator.py
@@ -3,8 +3,10 @@
# found in the LICENSE file.
from model import PropertyType
+import any_helper
import code
import cpp_util
+import model
import util_cc_helper
class CCGenerator(object):
@@ -17,6 +19,7 @@ class CCGenerator(object):
self._cpp_type_generator.GetCppNamespaceName(self._namespace))
self._util_cc_helper = (
util_cc_helper.UtilCCHelper(self._cpp_type_generator))
+ self._any_helper = any_helper.AnyHelper()
def Generate(self):
"""Generates a code.Code object with the .cc for a single namespace.
@@ -40,6 +43,7 @@ class CCGenerator(object):
.Append('using base::Value;')
.Append('using base::DictionaryValue;')
.Append('using base::ListValue;')
+ .Append('using %s;' % any_helper.ANY_CLASS)
.Append()
.Concat(self._cpp_type_generator.GetRootNamespaceStart())
.Concat(self._cpp_type_generator.GetNamespaceStart())
@@ -62,7 +66,8 @@ class CCGenerator(object):
.Append()
)
for function in self._namespace.functions.values():
- (c.Concat(self._GenerateFunction(function))
+ (c.Concat(self._GenerateFunction(
+ cpp_util.Classname(function.name), function))
.Append()
)
(c.Concat(self._cpp_type_generator.GetNamespaceEnd())
@@ -78,25 +83,43 @@ class CCGenerator(object):
classname = cpp_util.Classname(type_.name)
c = code.Code()
- (c.Concat(self._GeneratePropertyFunctions(
- cpp_namespace, type_.properties.values()))
- .Append('%(namespace)s::%(classname)s() {}')
- .Append('%(namespace)s::~%(classname)s() {}')
- .Append()
- )
- if type_.from_json:
- (c.Concat(self._GenerateTypePopulate(cpp_namespace, type_))
+ if type_.functions:
+ # Types with functions are not instantiable in C++ because they are
+ # handled in pure Javascript and hence have no properties or
+ # additionalProperties.
+ if type_.properties:
+ raise NotImplementedError('\n'.join(model.GetModelHierarchy(type_)) +
+ '\nCannot generate both functions and properties on a type')
+ for function in type_.functions.values():
+ (c.Concat(
+ self._GenerateFunction(
+ cpp_namespace + '::' + cpp_util.Classname(function.name),
+ function))
+ .Append()
+ )
+ else:
+ (c.Concat(self._GeneratePropertyFunctions(
+ cpp_namespace, type_.properties.values()))
+ .Append('%(namespace)s::%(classname)s() {}')
+ .Append('%(namespace)s::~%(classname)s() {}')
.Append()
)
- if type_.from_client:
- c.Concat(self._GenerateTypeToValue(cpp_namespace, type_))
- c.Append()
+ if type_.from_json:
+ (c.Concat(self._GenerateTypePopulate(cpp_namespace, type_))
+ .Append()
+ )
+ if type_.from_client:
+ (c.Concat(self._GenerateTypeToValue(cpp_namespace, type_))
+ .Append()
+ )
c.Substitute({'classname': classname, 'namespace': cpp_namespace})
return c
def _GenerateTypePopulate(self, cpp_namespace, type_):
"""Generates the function for populating a type given a pointer to it.
+
+ E.g for type "Foo", generates Foo::Populate()
"""
classname = cpp_util.Classname(type_.name)
c = code.Code()
@@ -112,7 +135,16 @@ class CCGenerator(object):
for prop in type_.properties.values():
c.Concat(self._InitializePropertyToDefault(prop, 'out'))
for prop in type_.properties.values():
- c.Concat(self._GenerateTypePopulateProperty(prop, 'dict', 'out'))
+ if prop.type_ == PropertyType.ADDITIONAL_PROPERTIES:
+ c.Append('out->additional_properties.MergeDictionary(dict);')
+ # remove all keys that are actual properties
+ for cur_prop in type_.properties.values():
+ if prop != cur_prop:
+ c.Append('out->additional_properties'
+ '.RemoveWithoutPathExpansion("%s", NULL);' % cur_prop.name)
+ c.Append()
+ else:
+ c.Concat(self._GenerateTypePopulateProperty(prop, 'dict', 'out'))
(c.Append('return true;')
.Eblock('}')
)
@@ -149,6 +181,8 @@ class CCGenerator(object):
def _GenerateTypeToValue(self, cpp_namespace, type_):
"""Generates a function that serializes the type into a |DictionaryValue|.
+
+ E.g. for type "Foo" generates Foo::ToValue()
"""
c = code.Code()
(c.Sblock('scoped_ptr<DictionaryValue> %s::ToValue() const {' %
@@ -157,51 +191,54 @@ class CCGenerator(object):
.Append()
)
for prop in type_.properties.values():
- if prop.optional:
- if prop.type_ == PropertyType.ENUM:
- c.Sblock('if (%s != %s)' %
- (prop.unix_name, self._cpp_type_generator.GetEnumNoneValue(prop)))
- else:
- c.Sblock('if (%s.get())' % prop.unix_name)
- c.Append('value->SetWithoutPathExpansion("%s", %s);' % (
- prop.name,
- self._CreateValueFromProperty(prop, prop.unix_name)))
- if prop.optional:
- c.Eblock();
+ if prop.type_ == PropertyType.ADDITIONAL_PROPERTIES:
+ c.Append('value->MergeDictionary(&%s);' % prop.unix_name)
+ else:
+ if prop.optional:
+ if prop.type_ == PropertyType.ENUM:
+ c.Sblock('if (%s != %s)' %
+ (prop.unix_name,
+ self._cpp_type_generator.GetEnumNoneValue(prop)))
+ else:
+ c.Sblock('if (%s.get())' % prop.unix_name)
+ c.Append('value->SetWithoutPathExpansion("%s", %s);' % (
+ prop.name,
+ self._CreateValueFromProperty(prop, prop.unix_name)))
+ if prop.optional:
+ c.Eblock();
(c.Append()
.Append('return value.Pass();')
.Eblock('}')
)
return c
- def _GenerateFunction(self, function):
+ def _GenerateFunction(self, cpp_namespace, function):
"""Generates the definitions for function structs.
"""
- classname = cpp_util.Classname(function.name)
c = code.Code()
# Params::Populate function
if function.params:
- c.Concat(self._GeneratePropertyFunctions(classname + '::Params',
+ c.Concat(self._GeneratePropertyFunctions(cpp_namespace + '::Params',
function.params))
- (c.Append('%(name)s::Params::Params() {}')
- .Append('%(name)s::Params::~Params() {}')
+ (c.Append('%(cpp_namespace)s::Params::Params() {}')
+ .Append('%(cpp_namespace)s::Params::~Params() {}')
.Append()
- .Concat(self._GenerateFunctionParamsCreate(function))
+ .Concat(self._GenerateFunctionParamsCreate(cpp_namespace, function))
.Append()
)
# Result::Create function
if function.callback:
- c.Concat(self._GenerateFunctionResultCreate(function))
+ c.Concat(self._GenerateFunctionResultCreate(cpp_namespace, function))
- c.Substitute({'name': classname})
+ c.Substitute({'cpp_namespace': cpp_namespace})
return c
def _GenerateCreateEnumValue(self, cpp_namespace, prop):
- """Generates a function that returns the |StringValue| representation of an
- enum.
+ """Generates CreateEnumValue() that returns the |StringValue|
+ representation of an enum.
"""
c = code.Code()
c.Append('// static')
@@ -233,15 +270,18 @@ class CCGenerator(object):
return c
def _CreateValueFromProperty(self, prop, var):
- """Creates a Value given a single property. Generated code passes ownership
+ """Creates a Value given a property. Generated code passes ownership
to caller.
var: variable or variable*
+
+ E.g for std::string, generate Value::CreateStringValue(var)
"""
if prop.type_ == PropertyType.CHOICES:
- # CHOICES conversion not implemented because it's not used. If needed,
- # write something to generate a function that returns a scoped_ptr<Value>
- # and put it in _GeneratePropertyFunctions.
+ # CHOICES conversion not implemented. If needed, write something to
+ # generate a function that returns a scoped_ptr<Value> and put it in
+ # _GeneratePropertyFunctions, then use it here. Look at CreateEnumValue()
+ # for reference.
raise NotImplementedError(
'Conversion of CHOICES to Value not implemented')
if prop.type_ in (PropertyType.REF, PropertyType.OBJECT):
@@ -249,6 +289,10 @@ class CCGenerator(object):
return '%s->ToValue().release()' % var
else:
return '%s.ToValue().release()' % var
+ elif prop.type_ == PropertyType.ANY:
+ return '%s.DeepCopy()' % self._any_helper.GetValue(prop, var)
+ elif prop.type_ == PropertyType.ADDITIONAL_PROPERTIES:
+ return '%s.DeepCopy()' % var
elif prop.type_ == PropertyType.ENUM:
return 'CreateEnumValue(%s).release()' % var
elif prop.type_ == PropertyType.ARRAY:
@@ -291,19 +335,20 @@ class CCGenerator(object):
})
return c
- def _GenerateFunctionParamsCreate(self, function):
+ def _GenerateFunctionParamsCreate(self, cpp_namespace, function):
"""Generate function to create an instance of Params. The generated
function takes a ListValue of arguments.
+
+ E.g for function "Bar", generate Bar::Params::Create()
"""
- classname = cpp_util.Classname(function.name)
c = code.Code()
(c.Append('// static')
- .Sblock('scoped_ptr<%(classname)s::Params> %(classname)s::Params::Create'
- '(const ListValue& args) {')
+ .Sblock('scoped_ptr<%(cpp_namespace)s::Params> '
+ '%(cpp_namespace)s::Params::Create(const ListValue& args) {')
.Concat(self._GenerateParamsCheck(function, 'args'))
.Append('scoped_ptr<Params> params(new Params());')
)
- c.Substitute({'classname': classname})
+ c.Substitute({'cpp_namespace': cpp_namespace})
for param in function.params:
c.Concat(self._InitializePropertyToDefault(param, 'params'))
@@ -353,7 +398,8 @@ class CCGenerator(object):
c = code.Code()
c.Sblock('{')
- if check_type and prop.type_ != PropertyType.CHOICES:
+ if check_type and prop.type_ not in (
+ PropertyType.CHOICES, PropertyType.ANY):
(c.Append('if (!%(value_var)s->IsType(%(value_type)s))')
.Append(' return %(failure_value)s;')
)
@@ -389,6 +435,10 @@ class CCGenerator(object):
'if (!%(ctype)s::Populate(*dictionary, &%(dst)s->%(name)s))')
.Append(' return %(failure_value)s;')
)
+ elif prop.type_ == PropertyType.ANY:
+ if prop.optional:
+ c.Append('%(dst)s->%(name)s.reset(new Any());')
+ c.Append(self._any_helper.Init(prop, value_var, dst) + ';')
elif prop.type_ == PropertyType.ARRAY:
# util_cc_helper deals with optional and required arrays
(c.Append('ListValue* list = NULL;')
@@ -442,7 +492,7 @@ class CCGenerator(object):
'dst': dst,
'failure_value': failure_value,
}
- if prop.type_ != PropertyType.CHOICES:
+ if prop.type_ not in (PropertyType.CHOICES, PropertyType.ANY):
sub['ctype'] = self._cpp_type_generator.GetType(prop)
sub['value_type'] = cpp_util.GetValueType(prop)
c.Substitute(sub)
@@ -459,20 +509,24 @@ class CCGenerator(object):
param_namespace + '::' + cpp_util.Classname(param.name),
param))
c.Append()
+ elif param.type_ == PropertyType.CHOICES:
+ c.Concat(self._GeneratePropertyFunctions(
+ param_namespace, param.choices.values()))
elif param.type_ == PropertyType.ENUM:
c.Concat(self._GenerateCreateEnumValue(param_namespace, param))
c.Append()
return c
- def _GenerateFunctionResultCreate(self, function):
+ def _GenerateFunctionResultCreate(self, cpp_namespace, function):
"""Generate function to create a Result given the return value.
+
+ E.g for function "Bar", generate Bar::Result::Create
"""
- classname = cpp_util.Classname(function.name)
c = code.Code()
params = function.callback.params
if not params:
- (c.Append('Value* %s::Result::Create() {' % classname)
+ (c.Append('Value* %s::Result::Create() {' % cpp_namespace)
.Append(' return Value::CreateNullValue();')
.Append('}')
)
@@ -480,22 +534,26 @@ class CCGenerator(object):
expanded_params = self._cpp_type_generator.GetExpandedChoicesInParams(
params)
c.Concat(self._GeneratePropertyFunctions(
- classname + '::Result', expanded_params))
+ cpp_namespace + '::Result', expanded_params))
# If there is a single parameter, this is straightforward. However, if
# the callback parameter is of 'choices', this generates a Create method
# for each choice. This works because only 1 choice can be returned at a
# time.
for param in expanded_params:
+ if param.type_ == PropertyType.ANY:
+ # Generation of Value* Create(Value*) is redundant.
+ continue
# We treat this argument as 'required' to avoid wrapping it in a
# scoped_ptr if it's optional.
param_copy = param.Copy()
param_copy.optional = False
- c.Sblock('Value* %(classname)s::Result::Create(const %(arg)s) {')
+ c.Sblock('Value* %(cpp_namespace)s::Result::Create(const %(arg)s) {')
c.Append('return %s;' %
self._CreateValueFromProperty(param_copy, param_copy.unix_name))
c.Eblock('}')
- c.Substitute({'classname': classname,
+ c.Substitute({
+ 'cpp_namespace': cpp_namespace,
'arg': cpp_util.GetParameterDeclaration(
param_copy, self._cpp_type_generator.GetType(param_copy))
})
@@ -505,6 +563,8 @@ class CCGenerator(object):
def _InitializePropertyToDefault(self, prop, dst):
"""Initialize a model.Property to its default value inside an object.
+ E.g for optional enum "state", generate dst->state = STATE_NONE;
+
dst: Type*
"""
c = code.Code()
diff --git a/tools/json_schema_compiler/code.py b/tools/json_schema_compiler/code.py
index c19029a..07f6574 100644
--- a/tools/json_schema_compiler/code.py
+++ b/tools/json_schema_compiler/code.py
@@ -14,11 +14,15 @@ class Code(object):
self._indent_size = indent_size
self._comment_length = comment_length
- def Append(self, line=''):
+ def Append(self, line='', substitute=True):
"""Appends a line of code at the current indent level or just a newline if
line is not specified. Trailing whitespace is stripped.
+
+ substitute: indicated whether this line should be affected by
+ code.Substitute().
"""
- self._code.append(((' ' * self._indent_level) + line).rstrip())
+ self._code.append(Line(((' ' * self._indent_level) + line).rstrip(),
+ substitute=substitute))
return self
def IsEmpty(self):
@@ -40,9 +44,13 @@ class Code(object):
for line in obj._code:
try:
# line % () will fail if any substitution tokens are left in line
- self._code.append(((' ' * self._indent_level) + line % ()).rstrip())
+ if line.substitute:
+ line.value %= ()
except TypeError:
raise TypeError('Unsubstituted value when concatting\n' + line)
+ except ValueError:
+ raise ValueError('Stray % character when concatting\n' + line)
+ self.Append(line.value, line.substitute)
return self
@@ -66,16 +74,15 @@ class Code(object):
self.Append(line)
return self
- # TODO(calamity): Make comment its own class or something and Render at
- # self.Render() time
- def Comment(self, comment):
+ def Comment(self, comment, comment_prefix='// '):
"""Adds the given string as a comment.
Will split the comment if it's too long. Use mainly for variable length
comments. Otherwise just use code.Append('// ...') for comments.
+
+ Unaffected by code.Substitute().
"""
- comment_symbol = '// '
- max_len = self._comment_length - self._indent_level - len(comment_symbol)
+ max_len = self._comment_length - self._indent_level - len(comment_prefix)
while len(comment) >= max_len:
line = comment[0:max_len]
last_space = line.rfind(' ')
@@ -84,8 +91,8 @@ class Code(object):
comment = comment[last_space + 1:]
else:
comment = comment[max_len:]
- self.Append(comment_symbol + line)
- self.Append(comment_symbol + comment)
+ self.Append(comment_prefix + line, substitute=False)
+ self.Append(comment_prefix + comment, substitute=False)
return self
def Substitute(self, d):
@@ -100,16 +107,24 @@ class Code(object):
if not isinstance(d, dict):
raise TypeError('Passed argument is not a dictionary: ' + d)
for i, line in enumerate(self._code):
- # Only need to check %s because arg is a dict and python will allow
- # '%s %(named)s' but just about nothing else
- if '%s' in self._code[i] or '%r' in self._code[i]:
- raise TypeError('"%s" or "%r" found in substitution. '
- 'Named arguments only. Use "%" to escape')
- self._code[i] = line % d
+ if self._code[i].substitute:
+ # Only need to check %s because arg is a dict and python will allow
+ # '%s %(named)s' but just about nothing else
+ if '%s' in self._code[i].value or '%r' in self._code[i].value:
+ raise TypeError('"%s" or "%r" found in substitution. '
+ 'Named arguments only. Use "%" to escape')
+ self._code[i].value = line.value % d
+ self._code[i].substitute = False
return self
def Render(self):
"""Renders Code as a string.
"""
- return '\n'.join(self._code)
+ return '\n'.join([l.value for l in self._code])
+class Line(object):
+ """A line of code.
+ """
+ def __init__(self, value, substitute=True):
+ self.value = value
+ self.substitute = substitute
diff --git a/tools/json_schema_compiler/code_test.py b/tools/json_schema_compiler/code_test.py
index 8431d95..a089f7fb 100644
--- a/tools/json_schema_compiler/code_test.py
+++ b/tools/json_schema_compiler/code_test.py
@@ -148,5 +148,17 @@ class CodeTest(unittest.TestCase):
'// ' + 'x' * 23,
c.Render())
+ def testCommentWithSpecialCharacters(self):
+ c = Code()
+ c.Comment('20% of 80%s')
+ c.Substitute({})
+ self.assertEquals('// 20% of 80%s', c.Render())
+ d = Code()
+ d.Append('90')
+ d.Concat(c)
+ self.assertEquals('90\n'
+ '// 20% of 80%s',
+ d.Render())
+
if __name__ == '__main__':
unittest.main()
diff --git a/tools/json_schema_compiler/compiler.py b/tools/json_schema_compiler/compiler.py
index 7965d18..54ab62f 100644
--- a/tools/json_schema_compiler/compiler.py
+++ b/tools/json_schema_compiler/compiler.py
@@ -75,8 +75,8 @@ if __name__ == '__main__':
# The output filename must match the input filename for gyp to deal with it
# properly.
out_file = namespace.name
- type_generator = cpp_type_generator.CppTypeGenerator(root_namespace,
- namespace, out_file)
+ type_generator = cpp_type_generator.CppTypeGenerator(
+ root_namespace, namespace, namespace.unix_name)
for referenced_namespace in api_model.namespaces.values():
type_generator.AddNamespace(
referenced_namespace,
diff --git a/tools/json_schema_compiler/cpp_type_generator.py b/tools/json_schema_compiler/cpp_type_generator.py
index cad64ae..44e7b59 100644
--- a/tools/json_schema_compiler/cpp_type_generator.py
+++ b/tools/json_schema_compiler/cpp_type_generator.py
@@ -4,6 +4,7 @@
from code import Code
from model import PropertyType
+import any_helper
import cpp_util
class CppTypeGenerator(object):
@@ -134,8 +135,12 @@ class CppTypeGenerator(object):
cpp_type = 'std::string'
elif prop.type_ == PropertyType.ENUM:
cpp_type = cpp_util.Classname(prop.name)
- elif prop.type_ == PropertyType.ANY:
+ elif prop.type_ == PropertyType.ADDITIONAL_PROPERTIES:
cpp_type = 'DictionaryValue'
+ elif prop.type_ == PropertyType.ANY:
+ cpp_type = any_helper.ANY_CLASS
+ elif prop.type_ == PropertyType.OBJECT:
+ cpp_type = cpp_util.Classname(prop.name)
elif prop.type_ == PropertyType.ARRAY:
if prop.item_type.type_ in (
PropertyType.REF, PropertyType.ANY, PropertyType.OBJECT):
@@ -144,8 +149,6 @@ class CppTypeGenerator(object):
cpp_type = 'std::vector<%s> '
cpp_type = cpp_type % self.GetType(
prop.item_type, pad_for_generics=True)
- elif prop.type_ == PropertyType.OBJECT:
- cpp_type = cpp_util.Classname(prop.name)
else:
raise NotImplementedError(prop.type_)
diff --git a/tools/json_schema_compiler/cpp_type_generator_test.py b/tools/json_schema_compiler/cpp_type_generator_test.py
index 81ee04d..ac16262 100644
--- a/tools/json_schema_compiler/cpp_type_generator_test.py
+++ b/tools/json_schema_compiler/cpp_type_generator_test.py
@@ -62,11 +62,11 @@ class CppTypeGeneratorTest(unittest.TestCase):
manager = CppTypeGenerator('', self.tabs, 'tabs_api')
prop = self.tabs.functions['move'].params[0]
self.assertEquals('TAB_IDS_ARRAY',
- manager.GetEnumValue(prop, model.PropertyType.ARRAY))
+ manager.GetEnumValue(prop, model.PropertyType.ARRAY.name))
self.assertEquals('TAB_IDS_INTEGER',
- manager.GetEnumValue(prop, model.PropertyType.INTEGER))
+ manager.GetEnumValue(prop, model.PropertyType.INTEGER.name))
self.assertEquals('TabIdsType',
- manager.GetEnumType(prop))
+ manager.GetChoicesEnumType(prop))
def testGetTypeSimple(self):
manager = CppTypeGenerator('', self.tabs, 'tabs_api')
diff --git a/tools/json_schema_compiler/cpp_util.py b/tools/json_schema_compiler/cpp_util.py
index fafc155..3b65f5d 100644
--- a/tools/json_schema_compiler/cpp_util.py
+++ b/tools/json_schema_compiler/cpp_util.py
@@ -52,7 +52,6 @@ def GetValueType(prop):
PropertyType.REF: 'Value::TYPE_DICTIONARY',
PropertyType.OBJECT: 'Value::TYPE_DICTIONARY',
PropertyType.ARRAY: 'Value::TYPE_LIST',
- PropertyType.ANY: 'Value::TYPE_DICTIONARY',
}[prop.type_]
def GetParameterDeclaration(param, type_):
diff --git a/tools/json_schema_compiler/h_generator.py b/tools/json_schema_compiler/h_generator.py
index 3af7ccf..3c97bc6 100644
--- a/tools/json_schema_compiler/h_generator.py
+++ b/tools/json_schema_compiler/h_generator.py
@@ -5,6 +5,7 @@
from model import PropertyType
import code
import cpp_util
+import model
import os
class HGenerator(object):
@@ -38,6 +39,7 @@ class HGenerator(object):
.Append('#include "base/memory/linked_ptr.h"')
.Append('#include "base/memory/scoped_ptr.h"')
.Append('#include "base/values.h"')
+ .Append('#include "tools/json_schema_compiler/any.h"')
.Append()
)
@@ -76,8 +78,7 @@ class HGenerator(object):
(c.Concat(self._GenerateFunction(function))
.Append()
)
- (c.Append()
- .Concat(self._cpp_type_generator.GetNamespaceEnd())
+ (c.Concat(self._cpp_type_generator.GetNamespaceEnd())
.Concat(self._cpp_type_generator.GetRootNamespaceEnd())
.Append()
.Append('#endif // %s' % ifndef_name)
@@ -109,6 +110,7 @@ class HGenerator(object):
if prop.type_ == PropertyType.CHOICES:
enum_name = self._cpp_type_generator.GetChoicesEnumType(prop)
c.Append('%s %s_type;' % (enum_name, prop.unix_name))
+ c.Append()
for prop in self._cpp_type_generator.GetExpandedChoicesInParams(props):
if prop.description:
c.Comment(prop.description)
@@ -123,33 +125,50 @@ class HGenerator(object):
"""
classname = cpp_util.Classname(type_.name)
c = code.Code()
- if type_.description:
- c.Comment(type_.description)
- (c.Sblock('struct %(classname)s {')
- .Append('~%(classname)s();')
- .Append('%(classname)s();')
- .Append()
- .Concat(self._GeneratePropertyStructures(type_.properties.values()))
- .Concat(self._GenerateFields(type_.properties.values()))
- )
- if type_.from_json:
- (c.Comment('Populates a %s object from a Value. Returns'
- ' whether |out| was successfully populated.' % classname)
- .Append('static bool Populate(const Value& value, %(classname)s* out);')
- .Append()
+
+ if type_.functions:
+ # Types with functions are not instantiable in C++ because they are
+ # handled in pure Javascript and hence have no properties or
+ # additionalProperties.
+ if type_.properties:
+ raise NotImplementedError('\n'.join(model.GetModelHierarchy(type_)) +
+ '\nCannot generate both functions and properties on a type')
+ c.Sblock('namespace %(classname)s {')
+ for function in type_.functions.values():
+ (c.Concat(self._GenerateFunction(function))
+ .Append()
+ )
+ c.Eblock('}')
+ else:
+ if type_.description:
+ c.Comment(type_.description)
+ (c.Sblock('struct %(classname)s {')
+ .Append('~%(classname)s();')
+ .Append('%(classname)s();')
+ .Append()
+ .Concat(self._GeneratePropertyStructures(type_.properties.values()))
+ .Concat(self._GenerateFields(type_.properties.values()))
)
+ if type_.from_json:
+ (c.Comment('Populates a %s object from a Value. Returns'
+ ' whether |out| was successfully populated.' % classname)
+ .Append(
+ 'static bool Populate(const Value& value, %(classname)s* out);')
+ .Append()
+ )
+
+ if type_.from_client:
+ (c.Comment('Returns a new DictionaryValue representing the'
+ ' serialized form of this %s object. Passes '
+ 'ownership to caller.' % classname)
+ .Append('scoped_ptr<DictionaryValue> ToValue() const;')
+ )
- if type_.from_client:
- (c.Comment('Returns a new DictionaryValue representing the'
- ' serialized form of this %s object. Passes '
- 'ownership to caller.' % classname)
- .Append('scoped_ptr<DictionaryValue> ToValue() const;')
+ (c.Eblock()
+ .Sblock(' private:')
+ .Append('DISALLOW_COPY_AND_ASSIGN(%(classname)s);')
+ .Eblock('};')
)
- (c.Eblock()
- .Sblock(' private:')
- .Append('DISALLOW_COPY_AND_ASSIGN(%(classname)s);')
- .Eblock('};')
- )
c.Substitute({'classname': classname})
return c
@@ -205,6 +224,7 @@ class HGenerator(object):
self._cpp_type_generator.GetChoicesEnumType(prop),
prop,
[choice.type_.name for choice in prop.choices.values()]))
+ c.Concat(self._GeneratePropertyStructures(prop.choices.values()))
elif prop.type_ == PropertyType.ENUM:
enum_name = self._cpp_type_generator.GetType(prop)
c.Concat(self._GenerateEnumDeclaration(
@@ -234,6 +254,10 @@ class HGenerator(object):
for param in self._cpp_type_generator.GetExpandedChoicesInParams(params):
if param.description:
c.Comment(param.description)
+ if param.type_ == PropertyType.ANY:
+ c.Comment("Value* Result::Create(Value*) not generated "
+ "because it's redundant.")
+ continue
c.Append('Value* Create(const %s);' % cpp_util.GetParameterDeclaration(
param, self._cpp_type_generator.GetType(param)))
c.Eblock('};')
diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py
index d839ab9..de392d2 100644
--- a/tools/json_schema_compiler/model.py
+++ b/tools/json_schema_compiler/model.py
@@ -40,18 +40,22 @@ class Namespace(object):
"""
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(type_json)
+ type_ = Type(self, type_json['id'], type_json)
self.types[type_.name] = type_
for function_json in json.get('functions', []):
if not function_json.get('nocompile', False):
- function = Function(function_json)
- self.functions[function.name] = function
+ self.functions[function_json['name']] = Function(self, function_json)
class Type(object):
"""A Type defined in the json.
@@ -59,23 +63,57 @@ class Type(object):
Properties:
- |name| the type name
- |description| the description of the type (if provided)
- - |properties| a map of property names to their model.Property
+ - |properties| a map of property unix_names to their model.Property
+ - |functions| a map of function names to their model.Function
- |from_client| indicates that instances of the Type can originate from the
users of generated code, such as top-level types and function results
- |from_json| indicates that instances of the Type can originate from the
JSON (as described by the schema), such as top-level types and function
parameters
"""
- def __init__(self, json):
- self.name = json['id']
+ def __init__(self, parent, name, json):
+ if not (
+ 'properties' in json or
+ 'additionalProperties' in json or
+ 'functions' in json):
+ raise ParseException(name + " has no properties or functions")
+ self.name = name
self.description = json.get('description')
self.from_json = True
self.from_client = True
self.properties = {}
- for prop_name, prop_json in json['properties'].items():
- self.properties[prop_name] = Property(prop_name, prop_json,
+ self.functions = {}
+ self.parent = parent
+ for function_json in json.get('functions', []):
+ if not function_json.get('nocompile', False):
+ 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 to 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)
+ from_client=True))
+
+ additional_properties = json.get('additionalProperties')
+ 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
class Callback(object):
"""A callback parameter to a Function.
@@ -83,17 +121,18 @@ class Callback(object):
Properties:
- |params| the parameters to this callback.
"""
- def __init__(self, json):
+ def __init__(self, parent, json):
params = json['parameters']
+ self.parent = parent
self.params = []
if len(params) == 0:
return
elif len(params) == 1:
param = params[0]
- self.params.append(Property(param['name'], param,
+ self.params.append(Property(self, param['name'], param,
from_client=True))
else:
- raise AssertionError("Callbacks can have at most a single parameter")
+ raise ParseException("Callbacks can have at most a single parameter")
class Function(object):
"""A Function defined in the API.
@@ -106,17 +145,19 @@ class Function(object):
- |callback| the callback parameter to the function. There should be exactly
one
"""
- def __init__(self, json):
+ def __init__(self, parent, json):
self.name = json['name']
self.params = []
- self.description = json['description']
+ self.description = json.get('description')
self.callback = None
+ self.parent = parent
for param in json['parameters']:
if param.get('type') == 'function':
- assert (not self.callback), self.name + " has more than one callback"
- self.callback = Callback(param)
+ if self.callback:
+ raise ParseException(self.name + " has more than one callback")
+ self.callback = Callback(self, param)
else:
- self.params.append(Property(param['name'], param,
+ self.params.append(Property(self, param['name'], param,
from_json=True))
class Property(object):
@@ -135,9 +176,9 @@ class Property(object):
ARRAY
- |properties| the properties of an OBJECT parameter
"""
- def __init__(self, name, json,
- from_json=False,
- from_client=False):
+
+ def __init__(self, parent, name, json, is_additional_properties=False,
+ from_json=False, from_client=False):
"""
Parameters:
- |from_json| indicates that instances of the Type can originate from the
@@ -147,11 +188,14 @@ 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.description = json.get('description')
- if '$ref' in json:
+ self.parent = parent
+ if is_additional_properties:
+ self.type_ = PropertyType.ADDITIONAL_PROPERTIES
+ elif '$ref' in json:
self.ref_type = json['$ref']
self.type_ = PropertyType.REF
elif 'enum' in json:
@@ -172,9 +216,9 @@ class Property(object):
elif json_type == 'number':
self.type_ = PropertyType.DOUBLE
elif json_type == 'array':
- self.item_type = Property(name + "Element", json['items'],
- from_json,
- from_client)
+ self.item_type = Property(self, name + "Element", json['items'],
+ from_json=from_json,
+ from_client=from_client)
self.type_ = PropertyType.ARRAY
elif json_type == 'object':
self.type_ = PropertyType.OBJECT
@@ -182,20 +226,20 @@ class Property(object):
self.properties = {}
self.from_json = from_json
self.from_client = from_client
- for key, val in json.get('properties', {}).items():
- self.properties[key] = Property(key, val,
- from_json,
- from_client)
+ type_ = Type(self, self.name, json)
+ self.properties = type_.properties
+ self.functions = type_.functions
else:
- raise NotImplementedError(json_type)
+ raise ParseException(self, 'type ' + json_type + ' not recognized')
elif 'choices' in json:
- assert len(json['choices']), 'Choices has no choices\n%s' % json
+ if not json['choices']:
+ raise ParseException('Choices has no choices')
self.choices = {}
self.type_ = PropertyType.CHOICES
for choice_json in json['choices']:
- choice = Property(self.name, choice_json,
- from_json,
- from_client)
+ choice = Property(self, self.name, choice_json,
+ from_json=from_json,
+ from_client=from_client)
# A choice gets its unix_name set in
# cpp_type_generator.GetExpandedChoicesInParams
choice._unix_name = None
@@ -203,7 +247,7 @@ class Property(object):
choice.optional = True
self.choices[choice.type_] = choice
else:
- raise NotImplementedError(json)
+ raise ParseException('Property has no type, $ref or choices')
def GetUnixName(self):
"""Gets the property's unix_name. Raises AttributeError if not set.
@@ -257,10 +301,31 @@ class PropertyType(object):
CHOICES = _Info(False, "CHOICES")
OBJECT = _Info(False, "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.
"""
return '_'.join([x.lower()
for x in re.findall('[A-Z][a-z_]*', name[0].upper() + name[1:])])
+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):
+ """Returns the hierarchy of the given model entity."""
+ hierarchy = []
+ while entity:
+ try:
+ hierarchy.append(entity.name)
+ except AttributeError:
+ hierarchy.append(repr(entity))
+ entity = entity.parent
+ hierarchy.reverse()
+ return hierarchy
+
diff --git a/tools/json_schema_compiler/model_test.py b/tools/json_schema_compiler/model_test.py
index 824e26a..3217498 100644
--- a/tools/json_schema_compiler/model_test.py
+++ b/tools/json_schema_compiler/model_test.py
@@ -37,11 +37,6 @@ class ModelTest(unittest.TestCase):
self.assertEquals(["contains", "getAll", "remove", "request"],
sorted(self.permissions.functions.keys()))
- def testFunctionNoCallback(self):
- del (self.permissions_json[0]['functions'][0]['parameters'][0])
- self.assertRaises(AssertionError, self.model.AddNamespace,
- self.permissions_json[0], 'path/to/something.json')
-
def testFunctionNoCompile(self):
# tabs.json has 2 functions marked as nocompile (connect, sendRequest)
self.assertEquals(["captureVisibleTab", "create", "detectLanguage",
@@ -57,9 +52,9 @@ class ModelTest(unittest.TestCase):
self.assertEquals(['Window'], self.windows.types.keys())
def testHasProperties(self):
- self.assertEquals(["active", "favIconUrl", "highlighted", "id",
+ self.assertEquals(["active", "fav_icon_url", "highlighted", "id",
"incognito", "index", "pinned", "selected", "status", "title", "url",
- "windowId"],
+ "window_id"],
sorted(self.tabs.types['Tab'].properties.keys()))
def testProperties(self):
@@ -75,7 +70,7 @@ class ModelTest(unittest.TestCase):
self.assertEquals(model.PropertyType.OBJECT, object_prop.type_)
self.assertEquals(
["active", "highlighted", "pinned", "status", "title", "url",
- "windowId", "windowType"],
+ "window_id", "window_type"],
sorted(object_prop.properties.keys()))
def testChoices(self):
@@ -85,7 +80,7 @@ class ModelTest(unittest.TestCase):
def testPropertyNotImplemented(self):
(self.permissions_json[0]['types'][0]
['properties']['permissions']['type']) = 'something'
- self.assertRaises(NotImplementedError, self.model.AddNamespace,
+ self.assertRaises(model.ParseException, self.model.AddNamespace,
self.permissions_json[0], 'path/to/something.json')
def testDescription(self):
diff --git a/tools/json_schema_compiler/previewserver.py b/tools/json_schema_compiler/previewserver.py
index c7ab262..384171e 100755
--- a/tools/json_schema_compiler/previewserver.py
+++ b/tools/json_schema_compiler/previewserver.py
@@ -196,7 +196,7 @@ updateEverything();
json_file_path)
return
type_generator = cpp_type_generator.CppTypeGenerator(
- 'preview::api', namespace, cpp_util.Classname(filename).lower())
+ 'preview::api', namespace, namespace.unix_name)
# Get json file depedencies
for dependency in api_defs[0].get('dependencies', []):
diff --git a/tools/json_schema_compiler/test/additionalProperties.json b/tools/json_schema_compiler/test/additionalProperties.json
new file mode 100644
index 0000000..03bbef8
--- /dev/null
+++ b/tools/json_schema_compiler/test/additionalProperties.json
@@ -0,0 +1,55 @@
+[
+ {
+ "namespace": "additionalProperties",
+ "types": [
+ {
+ "id": "AdditionalPropertiesType",
+ "type": "object",
+ "properties": {
+ "string": {
+ "type": "string",
+ "description": "Some string."
+ }
+ },
+ "additionalProperties": { "type": "any" }
+ }
+ ],
+ "functions": [
+ {
+ "name": "additionalProperties",
+ "type": "function",
+ "description": "Takes an object with additionalProperties",
+ "parameters": [
+ {
+ "name": "paramObject",
+ "type": "object",
+ "properties": {},
+ "additionalProperties": {"type": "any"}
+ }
+ ]
+ },
+ {
+ "name": "returnAdditionalProperties",
+ "type": "function",
+ "description": "Returns an object with additionalProperties.",
+ "nodoc": "true",
+ "parameters": [
+ {
+ "type": "function",
+ "name": "callback",
+ "parameters": [
+ {
+ "name": "resultObject",
+ "type": "object",
+ "properties": {
+ "integer": {"type": "integer"}
+ },
+ "additionalProperties": {"type": "any"}
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/tools/json_schema_compiler/test/additional_properties_unittest.cc b/tools/json_schema_compiler/test/additional_properties_unittest.cc
new file mode 100644
index 0000000..b00836f
--- /dev/null
+++ b/tools/json_schema_compiler/test/additional_properties_unittest.cc
@@ -0,0 +1,70 @@
+// 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.
+
+#include "tools/json_schema_compiler/test/additionalProperties.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace test::api::additional_properties;
+
+TEST(JsonSchemaCompilerAdditionalPropertiesTest,
+ AdditionalPropertiesTypePopulate) {
+ {
+ scoped_ptr<ListValue> list_value(new ListValue());
+ list_value->Append(Value::CreateStringValue("asdf"));
+ list_value->Append(Value::CreateIntegerValue(4));
+ scoped_ptr<DictionaryValue> type_value(new DictionaryValue());
+ type_value->SetString("string", "value");
+ type_value->SetInteger("other", 9);
+ type_value->Set("another", list_value.release());
+ scoped_ptr<AdditionalPropertiesType> type(new AdditionalPropertiesType());
+ EXPECT_TRUE(AdditionalPropertiesType::Populate(*type_value, type.get()));
+ EXPECT_EQ("value", type->string);
+ EXPECT_TRUE(type_value->Remove("string", NULL));
+ EXPECT_TRUE(type->additional_properties.Equals(type_value.get()));
+ }
+ {
+ scoped_ptr<DictionaryValue> type_value(new DictionaryValue());
+ type_value->SetInteger("string", 3);
+ scoped_ptr<AdditionalPropertiesType> type(new AdditionalPropertiesType());
+ EXPECT_FALSE(AdditionalPropertiesType::Populate(*type_value, type.get()));
+ }
+}
+
+TEST(JsonSchemaCompilerAdditionalPropertiesTest,
+ AdditionalPropertiesParamsCreate) {
+ scoped_ptr<DictionaryValue> param_object_value(new DictionaryValue());
+ param_object_value->SetString("str", "a");
+ param_object_value->SetInteger("num", 1);
+ scoped_ptr<ListValue> params_value(new ListValue());
+ params_value->Append(param_object_value->DeepCopy());
+ scoped_ptr<AdditionalProperties::Params> params(
+ AdditionalProperties::Params::Create(*params_value));
+ EXPECT_TRUE(params.get());
+ EXPECT_TRUE(params->param_object.additional_properties.Equals(
+ param_object_value.get()));
+}
+
+TEST(JsonSchemaCompilerAdditionalPropertiesTest,
+ ReturnAdditionalPropertiesResultCreate) {
+ scoped_ptr<DictionaryValue> result_object_value(new DictionaryValue());
+ result_object_value->SetString("key", "value");
+ scoped_ptr<ReturnAdditionalProperties::Result::ResultObject> result_object(
+ new ReturnAdditionalProperties::Result::ResultObject());
+ result_object->integer = 5;
+ result_object->additional_properties.MergeDictionary(
+ result_object_value.get());
+ scoped_ptr<Value> result(
+ ReturnAdditionalProperties::Result::Create(*result_object));
+ DictionaryValue* result_dict = NULL;
+ EXPECT_TRUE(result->GetAsDictionary(&result_dict));
+
+ Value* int_temp_value = NULL;
+ int int_temp = 0;
+ EXPECT_TRUE(result_dict->Remove("integer", &int_temp_value));
+ EXPECT_TRUE(int_temp_value->GetAsInteger(&int_temp));
+ EXPECT_EQ(5, int_temp);
+
+ EXPECT_TRUE(result_dict->Equals(result_object_value.get()));
+}
diff --git a/tools/json_schema_compiler/test/any.json b/tools/json_schema_compiler/test/any.json
new file mode 100644
index 0000000..b19db82
--- /dev/null
+++ b/tools/json_schema_compiler/test/any.json
@@ -0,0 +1,54 @@
+[
+ {
+ "namespace": "any",
+ "types": [
+ {
+ "id": "AnyType",
+ "type": "object",
+ "properties": {
+ "any": {
+ "type": "any",
+ "description": "Any way you want it, that's the way you need it."
+ }
+ }
+ }
+ ],
+ "functions": [
+ {
+ "name": "optionalAny",
+ "type": "function",
+ "description": "Takes an optional any param.",
+ "parameters": [
+ {
+ "type": "any",
+ "name": "any",
+ "optional": true
+ },
+ {
+ "type": "function",
+ "name": "callback",
+ "parameters": []
+ }
+ ]
+ },
+ {
+ "name": "returnAny",
+ "type": "function",
+ "description": "Returns any.",
+ "nodoc": "true",
+ "parameters": [
+ {
+ "type": "function",
+ "name": "callback",
+ "parameters": [
+ {
+ "name": "result",
+ "type": "any"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/tools/json_schema_compiler/test/any_unittest.cc b/tools/json_schema_compiler/test/any_unittest.cc
new file mode 100644
index 0000000..fc6ae4a
--- /dev/null
+++ b/tools/json_schema_compiler/test/any_unittest.cc
@@ -0,0 +1,59 @@
+// 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.
+
+#include "tools/json_schema_compiler/test/any.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace test::api::any;
+
+TEST(JsonSchemaCompilerAnyTest, AnyTypePopulate) {
+ {
+ AnyType any_type;
+ scoped_ptr<DictionaryValue> any_type_value(new DictionaryValue());
+ any_type_value->SetString("any", "value");
+ EXPECT_TRUE(AnyType::Populate(*any_type_value, &any_type));
+ scoped_ptr<Value> any_type_to_value(any_type.ToValue());
+ EXPECT_TRUE(any_type_value->Equals(any_type_to_value.get()));
+ }
+ {
+ AnyType any_type;
+ scoped_ptr<DictionaryValue> any_type_value(new DictionaryValue());
+ any_type_value->SetInteger("any", 5);
+ EXPECT_TRUE(AnyType::Populate(*any_type_value, &any_type));
+ scoped_ptr<Value> any_type_to_value(any_type.ToValue());
+ EXPECT_TRUE(any_type_value->Equals(any_type_to_value.get()));
+ }
+}
+
+TEST(JsonSchemaCompilerAnyTest, OptionalAnyParamsCreate) {
+ {
+ scoped_ptr<ListValue> params_value(new ListValue());
+ scoped_ptr<OptionalAny::Params> params(
+ OptionalAny::Params::Create(*params_value));
+ EXPECT_TRUE(params.get());
+ EXPECT_FALSE(params->any.get());
+ }
+ {
+ scoped_ptr<ListValue> params_value(new ListValue());
+ scoped_ptr<Value> param(Value::CreateStringValue("asdf"));
+ params_value->Append(param->DeepCopy());
+ scoped_ptr<OptionalAny::Params> params(
+ OptionalAny::Params::Create(*params_value));
+ EXPECT_TRUE(params.get());
+ EXPECT_TRUE(params->any.get());
+ EXPECT_TRUE(params->any->value().Equals(param.get()));
+ }
+ {
+ scoped_ptr<ListValue> params_value(new ListValue());
+ scoped_ptr<Value> param(Value::CreateBooleanValue(true));
+ params_value->Append(param->DeepCopy());
+ scoped_ptr<OptionalAny::Params> params(
+ OptionalAny::Params::Create(*params_value));
+ EXPECT_TRUE(params.get());
+ EXPECT_TRUE(params->any.get());
+ EXPECT_TRUE(params->any.get());
+ EXPECT_TRUE(params->any->value().Equals(param.get()));
+ }
+}
diff --git a/tools/json_schema_compiler/test/arrays.json b/tools/json_schema_compiler/test/arrays.json
index 26e5825..9734ff8 100644
--- a/tools/json_schema_compiler/test/arrays.json
+++ b/tools/json_schema_compiler/test/arrays.json
@@ -63,6 +63,23 @@
]
},
{
+ "name": "anyArray",
+ "type": "function",
+ "description": "Takes some Items.",
+ "parameters": [
+ {
+ "name": "anys",
+ "type": "array",
+ "items": {"type": "any"}
+ },
+ {
+ "name": "callback",
+ "type": "function",
+ "parameters": []
+ }
+ ]
+ },
+ {
"name": "refArray",
"type": "function",
"description": "Takes some Items.",
diff --git a/tools/json_schema_compiler/test/arrays_unittest.cc b/tools/json_schema_compiler/test/arrays_unittest.cc
index 405fbb1..5676e25 100644
--- a/tools/json_schema_compiler/test/arrays_unittest.cc
+++ b/tools/json_schema_compiler/test/arrays_unittest.cc
@@ -93,6 +93,22 @@ TEST(JsonSchemaCompilerArrayTest, IntegerArrayParamsCreate) {
EXPECT_EQ(8, params->nums[2]);
}
+TEST(JsonSchemaCompilerArrayTest, AnyArrayParamsCreate) {
+ scoped_ptr<ListValue> params_value(new ListValue());
+ scoped_ptr<ListValue> any_array(new ListValue());
+ any_array->Append(Value::CreateIntegerValue(1));
+ any_array->Append(Value::CreateStringValue("test"));
+ any_array->Append(CreateItemValue(2));
+ params_value->Append(any_array.release());
+ scoped_ptr<AnyArray::Params> params(
+ AnyArray::Params::Create(*params_value));
+ EXPECT_TRUE(params.get());
+ EXPECT_EQ((size_t) 3, params->anys.size());
+ int int_temp = 0;
+ EXPECT_TRUE(params->anys[0]->value().GetAsInteger(&int_temp));
+ EXPECT_EQ(1, int_temp);
+}
+
TEST(JsonSchemaCompilerArrayTest, RefArrayParamsCreate) {
scoped_ptr<ListValue> params_value(new ListValue());
scoped_ptr<ListValue> item_array(new ListValue());
diff --git a/tools/json_schema_compiler/test/functionsOnTypes.json b/tools/json_schema_compiler/test/functionsOnTypes.json
new file mode 100644
index 0000000..e8c8220
--- /dev/null
+++ b/tools/json_schema_compiler/test/functionsOnTypes.json
@@ -0,0 +1,74 @@
+[
+ {
+ "namespace": "functionsOnTypes",
+ "types": [
+ {
+ "id": "StorageArea",
+ "type": "object",
+ "functions": [
+ {
+ "name": "get",
+ "type": "function",
+ "description": "Gets one or more items from storage.",
+ "parameters": [
+ {
+ "name": "keys",
+ "choices": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "description": "Storage items to return in the callback, where the values are replaced with those from storage if they exist.",
+ "properties": {},
+ "additionalProperties": { "type": "any" }
+ }
+ ],
+ "description": "A single key to get, list of keys to get, or a dictionary specifying default values (see description of the object). An empty list or object will return an empty result object. Pass in <code>null</code> to get the entire contents of storage.",
+ "optional": true
+ },
+ {
+ "name": "callback",
+ "type": "function",
+ "description": "Callback with storage items, or on failure (in which case lastError will be set).",
+ "parameters": [
+ {
+ "name": "items",
+ "type": "object",
+ "properties": {},
+ "additionalProperties": { "type": "any" },
+ "description": "Object with items in their key-value mappings."
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "ChromeSetting",
+ "type": "object",
+ "description": "An interface which allows access to a Chrome browser setting.",
+ "functions": [
+ {
+ "name": "get",
+ "type": "function",
+ "description": "Gets the value of a setting.",
+ "parameters": [
+ {
+ "name": "details",
+ "type": "object",
+ "description": "What setting to consider.",
+ "properties": {
+ "incognito": {
+ "type": "boolean",
+ "optional": true,
+ "description": "Whether to return the setting that applies to the incognito session (default false)."
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/tools/json_schema_compiler/test/functions_on_types_unittest.cc b/tools/json_schema_compiler/test/functions_on_types_unittest.cc
new file mode 100644
index 0000000..41a276c
--- /dev/null
+++ b/tools/json_schema_compiler/test/functions_on_types_unittest.cc
@@ -0,0 +1,68 @@
+// 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.
+
+#include "tools/json_schema_compiler/test/functionsOnTypes.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace test::api::functions_on_types;
+
+TEST(JsonSchemaCompilerFunctionsOnTypesTest, StorageAreaGetParamsCreate) {
+ {
+ scoped_ptr<ListValue> params_value(new ListValue());
+ scoped_ptr<StorageArea::Get::Params> params(
+ StorageArea::Get::Params::Create(*params_value));
+ EXPECT_TRUE(params.get());
+ EXPECT_EQ(StorageArea::Get::Params::KEYS_NONE, params->keys_type);
+ }
+ {
+ scoped_ptr<ListValue> params_value(new ListValue());
+ params_value->Append(Value::CreateIntegerValue(9));
+ scoped_ptr<StorageArea::Get::Params> params(
+ StorageArea::Get::Params::Create(*params_value));
+ EXPECT_FALSE(params.get());
+ }
+ {
+ scoped_ptr<ListValue> params_value(new ListValue());
+ params_value->Append(Value::CreateStringValue("test"));
+ scoped_ptr<StorageArea::Get::Params> params(
+ StorageArea::Get::Params::Create(*params_value));
+ EXPECT_TRUE(params.get());
+ EXPECT_EQ(StorageArea::Get::Params::KEYS_STRING, params->keys_type);
+ EXPECT_EQ("test", *params->keys_string);
+ }
+ {
+ scoped_ptr<DictionaryValue> keys_object_value(new DictionaryValue());
+ keys_object_value->SetInteger("integer", 5);
+ keys_object_value->SetString("string", "string");
+ scoped_ptr<ListValue> params_value(new ListValue());
+ params_value->Append(keys_object_value->DeepCopy());
+ scoped_ptr<StorageArea::Get::Params> params(
+ StorageArea::Get::Params::Create(*params_value));
+ EXPECT_TRUE(params.get());
+ EXPECT_EQ(StorageArea::Get::Params::KEYS_OBJECT, params->keys_type);
+ EXPECT_TRUE(
+ keys_object_value->Equals(&params->keys_object->additional_properties));
+ }
+}
+
+TEST(JsonSchemaCompilerFunctionsOnTypesTest, StorageAreaGetResultCreate) {
+ scoped_ptr<StorageArea::Get::Result::Items> items(
+ new StorageArea::Get::Result::Items());
+ items->additional_properties.SetDouble("asdf", 0.1);
+ items->additional_properties.SetString("sdfg", "zxcv");
+ scoped_ptr<Value> result_value(StorageArea::Get::Result::Create(*items));
+ EXPECT_TRUE(result_value->Equals(&items->additional_properties));
+}
+
+TEST(JsonSchemaCompilerFunctionsOnTypesTest, ChromeSettingGetParamsCreate) {
+ scoped_ptr<DictionaryValue> details_value(new DictionaryValue());
+ details_value->SetBoolean("incognito", true);
+ scoped_ptr<ListValue> params_value(new ListValue());
+ params_value->Append(details_value.release());
+ scoped_ptr<ChromeSetting::Get::Params> params(
+ ChromeSetting::Get::Params::Create(*params_value));
+ EXPECT_TRUE(params.get());
+ EXPECT_TRUE(*params->details.incognito);
+}
diff --git a/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp b/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp
index 8f32acb..58e50b7 100644
--- a/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp
+++ b/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp
@@ -10,10 +10,13 @@
'variables': {
'chromium_code': 1,
'json_schema_files': [
+ 'any.json',
+ 'additionalProperties.json',
'arrays.json',
'choices.json',
'crossref.json',
'enums.json',
+ 'functionsOnTypes.json',
'objects.json',
'simple_api.json',
],
diff --git a/tools/json_schema_compiler/util.cc b/tools/json_schema_compiler/util.cc
index 468df83..9d8abd5 100644
--- a/tools/json_schema_compiler/util.cc
+++ b/tools/json_schema_compiler/util.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "tools/json_schema_compiler/any.h"
#include "tools/json_schema_compiler/util.h"
#include "base/values.h"
@@ -26,11 +27,21 @@ bool GetItemFromList(const ListValue& from, int index, std::string* out) {
}
bool GetItemFromList(const ListValue& from, int index,
+ linked_ptr<any::Any>* out) {
+ Value* value = NULL;
+ if (!from.Get(index, &value))
+ return false;
+ scoped_ptr<any::Any> any_object(new any::Any());
+ any_object->Init(*value);
+ *out = linked_ptr<any::Any>(any_object.release());
+ return true;
+}
+
+bool GetItemFromList(const ListValue& from, int index,
linked_ptr<base::DictionaryValue>* out) {
DictionaryValue* dict = NULL;
- if (!from.GetDictionary(index, &dict)) {
+ if (!from.GetDictionary(index, &dict))
return false;
- }
*out = linked_ptr<DictionaryValue>(dict->DeepCopy());
return true;
}
@@ -51,6 +62,10 @@ void AddItemToList(const linked_ptr<base::DictionaryValue>& from,
base::ListValue* out) {
out->Append(static_cast<Value*>(from->DeepCopy()));
}
+void AddItemToList(const linked_ptr<any::Any>& from,
+ base::ListValue* out) {
+ out->Append(from->value().DeepCopy());
+}
} // namespace api_util
} // namespace extensions
diff --git a/tools/json_schema_compiler/util.h b/tools/json_schema_compiler/util.h
index f71c93c..c3e3b1d 100644
--- a/tools/json_schema_compiler/util.h
+++ b/tools/json_schema_compiler/util.h
@@ -14,6 +14,11 @@
#include "base/values.h"
namespace json_schema_compiler {
+
+namespace any {
+class Any;
+}
+
namespace util {
// Creates a new item at |out| from |from|[|index|]. These are used by template
@@ -24,6 +29,8 @@ bool GetItemFromList(const ListValue& from, int index, double* out);
bool GetItemFromList(const ListValue& from, int index, std::string* out);
bool GetItemFromList(const ListValue& from, int index,
linked_ptr<base::DictionaryValue>* out);
+bool GetItemFromList(const ListValue& from, int index,
+ linked_ptr<any::Any>* out);
// This template is used for types generated by tools/json_schema_compiler.
template<class T>
@@ -119,6 +126,8 @@ void AddItemToList(const double from, base::ListValue* out);
void AddItemToList(const std::string& from, base::ListValue* out);
void AddItemToList(const linked_ptr<base::DictionaryValue>& from,
base::ListValue* out);
+void AddItemToList(const linked_ptr<any::Any>& from,
+ base::ListValue* out);
// This template is used for types generated by tools/json_schema_compiler.
template<class T>
@@ -166,7 +175,7 @@ scoped_ptr<Value> CreateValueFromOptionalArray(
return scoped_ptr<Value>();
}
-} // namespace api_util
-} // namespace extensions
+} // namespace util
+} // namespace json_schema_compiler
#endif // TOOLS_JSON_SCHEMA_COMPILER_UTIL_H__