summaryrefslogtreecommitdiff
path: root/chromium/tools/json_schema_compiler
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/tools/json_schema_compiler
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/tools/json_schema_compiler')
-rw-r--r--chromium/tools/json_schema_compiler/PRESUBMIT.py20
-rw-r--r--chromium/tools/json_schema_compiler/api_gen_util.gyp20
-rw-r--r--chromium/tools/json_schema_compiler/cc_generator.py943
-rw-r--r--chromium/tools/json_schema_compiler/code.py141
-rwxr-xr-xchromium/tools/json_schema_compiler/code_test.py165
-rwxr-xr-xchromium/tools/json_schema_compiler/compiler.py159
-rw-r--r--chromium/tools/json_schema_compiler/cpp_bundle_generator.py291
-rw-r--r--chromium/tools/json_schema_compiler/cpp_generator.py11
-rw-r--r--chromium/tools/json_schema_compiler/cpp_type_generator.py276
-rwxr-xr-xchromium/tools/json_schema_compiler/cpp_type_generator_test.py167
-rw-r--r--chromium/tools/json_schema_compiler/cpp_util.py113
-rwxr-xr-xchromium/tools/json_schema_compiler/cpp_util_test.py31
-rw-r--r--chromium/tools/json_schema_compiler/dart_generator.py762
-rwxr-xr-xchromium/tools/json_schema_compiler/dart_generator_test.py85
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/comments.dart31
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/comments.idl32
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/dictionaries.dart235
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/dictionaries.idl61
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/empty_namespace.dart19
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/empty_namespace.idl7
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/empty_type.dart37
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/empty_type.idl10
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/enums.idl12
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/events.dart282
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/events.idl56
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/functions.dart93
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/functions.idl55
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/operatable_type.dart94
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/operatable_type.idl32
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/tags.dart116
-rw-r--r--chromium/tools/json_schema_compiler/dart_test/tags.idl33
-rw-r--r--chromium/tools/json_schema_compiler/h_generator.py397
-rw-r--r--chromium/tools/json_schema_compiler/highlighters/__init__.py0
-rw-r--r--chromium/tools/json_schema_compiler/highlighters/hilite_me_highlighter.py30
-rw-r--r--chromium/tools/json_schema_compiler/highlighters/none_highlighter.py20
-rw-r--r--chromium/tools/json_schema_compiler/highlighters/pygments_highlighter.py37
-rw-r--r--chromium/tools/json_schema_compiler/idl_schema.py417
-rwxr-xr-xchromium/tools/json_schema_compiler/idl_schema_test.py159
-rw-r--r--chromium/tools/json_schema_compiler/json_parse.py61
-rw-r--r--chromium/tools/json_schema_compiler/json_schema.py50
-rwxr-xr-xchromium/tools/json_schema_compiler/json_schema_test.py83
-rw-r--r--chromium/tools/json_schema_compiler/memoize.py13
-rw-r--r--chromium/tools/json_schema_compiler/model.py492
-rwxr-xr-xchromium/tools/json_schema_compiler/model_test.py104
-rwxr-xr-xchromium/tools/json_schema_compiler/preview.py366
-rw-r--r--chromium/tools/json_schema_compiler/schema_loader.py53
-rw-r--r--chromium/tools/json_schema_compiler/schema_util.py37
-rwxr-xr-xchromium/tools/json_schema_compiler/schema_util_test.py25
-rw-r--r--chromium/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp44
-rw-r--r--chromium/tools/json_schema_compiler/util.cc97
-rw-r--r--chromium/tools/json_schema_compiler/util.h181
-rw-r--r--chromium/tools/json_schema_compiler/util_cc_helper.py85
52 files changed, 7140 insertions, 0 deletions
diff --git a/chromium/tools/json_schema_compiler/PRESUBMIT.py b/chromium/tools/json_schema_compiler/PRESUBMIT.py
new file mode 100644
index 00000000000..b98649b3b4b
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/PRESUBMIT.py
@@ -0,0 +1,20 @@
+# 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.
+
+"""Presubmit script for changes affecting tools/json_schema_compiler/
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into gcl.
+"""
+
+WHITELIST = [ r'.+_test.py$' ]
+
+def CheckChangeOnUpload(input_api, output_api):
+ return input_api.canned_checks.RunUnitTestsInDirectory(
+ input_api, output_api, '.', whitelist=WHITELIST)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ return input_api.canned_checks.RunUnitTestsInDirectory(
+ input_api, output_api, '.', whitelist=WHITELIST)
diff --git a/chromium/tools/json_schema_compiler/api_gen_util.gyp b/chromium/tools/json_schema_compiler/api_gen_util.gyp
new file mode 100644
index 00000000000..54966cc6030
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/api_gen_util.gyp
@@ -0,0 +1,20 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [{
+ 'target_name': 'api_gen_util',
+ 'type': 'static_library',
+ 'sources': [
+ 'util.cc',
+ ],
+ 'dependencies': ['<(DEPTH)/base/base.gyp:base'],
+ 'include_dirs': [
+ '<(DEPTH)',
+ ],
+ }],
+}
diff --git a/chromium/tools/json_schema_compiler/cc_generator.py b/chromium/tools/json_schema_compiler/cc_generator.py
new file mode 100644
index 00000000000..d7d565e3846
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/cc_generator.py
@@ -0,0 +1,943 @@
+# 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.
+
+from code import Code
+from model import PropertyType, Type
+import cpp_util
+import model
+import schema_util
+import sys
+import util_cc_helper
+
+class CCGenerator(object):
+ def __init__(self, type_generator, cpp_namespace):
+ self._type_generator = type_generator
+ self._cpp_namespace = cpp_namespace
+
+ def Generate(self, namespace):
+ return _Generator(namespace,
+ self._type_generator,
+ self._cpp_namespace).Generate()
+
+class _Generator(object):
+ """A .cc generator for a namespace.
+ """
+ def __init__(self, namespace, cpp_type_generator, cpp_namespace):
+ self._namespace = namespace
+ self._type_helper = cpp_type_generator
+ self._cpp_namespace = cpp_namespace
+ self._target_namespace = (
+ self._type_helper.GetCppNamespaceName(self._namespace))
+ self._util_cc_helper = (
+ util_cc_helper.UtilCCHelper(self._type_helper))
+ self._generate_error_messages = namespace.compiler_options.get(
+ 'generate_error_messages', False)
+
+ def Generate(self):
+ """Generates a Code object with the .cc for a single namespace.
+ """
+ c = Code()
+ (c.Append(cpp_util.CHROMIUM_LICENSE)
+ .Append()
+ .Append(cpp_util.GENERATED_FILE_MESSAGE % self._namespace.source_file)
+ .Append()
+ .Append(self._util_cc_helper.GetIncludePath())
+ .Append('#include "base/logging.h"')
+ .Append('#include "base/strings/string_number_conversions.h"')
+ .Append('#include "%s/%s.h"' %
+ (self._namespace.source_file_dir, self._namespace.unix_name))
+ .Cblock(self._type_helper.GenerateIncludes(include_soft=True))
+ .Append()
+ .Concat(cpp_util.OpenNamespace(self._cpp_namespace))
+ .Cblock(self._type_helper.GetNamespaceStart())
+ )
+ if self._namespace.properties:
+ (c.Append('//')
+ .Append('// Properties')
+ .Append('//')
+ .Append()
+ )
+ for property in self._namespace.properties.values():
+ property_code = self._type_helper.GeneratePropertyValues(
+ property,
+ 'const %(type)s %(name)s = %(value)s;',
+ nodoc=True)
+ if property_code:
+ c.Cblock(property_code)
+ if self._namespace.types:
+ (c.Append('//')
+ .Append('// Types')
+ .Append('//')
+ .Append()
+ .Cblock(self._GenerateTypes(None, self._namespace.types.values()))
+ )
+ if self._namespace.functions:
+ (c.Append('//')
+ .Append('// Functions')
+ .Append('//')
+ .Append()
+ )
+ for function in self._namespace.functions.values():
+ c.Cblock(self._GenerateFunction(function))
+ if self._namespace.events:
+ (c.Append('//')
+ .Append('// Events')
+ .Append('//')
+ .Append()
+ )
+ for event in self._namespace.events.values():
+ c.Cblock(self._GenerateEvent(event))
+ (c.Concat(self._type_helper.GetNamespaceEnd())
+ .Cblock(cpp_util.CloseNamespace(self._cpp_namespace))
+ )
+ return c
+
+ def _GenerateType(self, cpp_namespace, type_):
+ """Generates the function definitions for a type.
+ """
+ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name))
+ c = Code()
+
+ if type_.functions:
+ # Wrap functions within types in the type's namespace.
+ (c.Append('namespace %s {' % classname)
+ .Append())
+ for function in type_.functions.values():
+ c.Cblock(self._GenerateFunction(function))
+ c.Append('} // namespace %s' % classname)
+ elif type_.property_type == PropertyType.ARRAY:
+ c.Cblock(self._GenerateType(cpp_namespace, type_.item_type))
+ elif type_.property_type in (PropertyType.CHOICES,
+ PropertyType.OBJECT):
+ if cpp_namespace is None:
+ classname_in_namespace = classname
+ else:
+ classname_in_namespace = '%s::%s' % (cpp_namespace, classname)
+
+ if type_.property_type == PropertyType.OBJECT:
+ c.Cblock(self._GeneratePropertyFunctions(classname_in_namespace,
+ type_.properties.values()))
+ else:
+ c.Cblock(self._GenerateTypes(classname_in_namespace, type_.choices))
+
+ (c.Append('%s::%s()' % (classname_in_namespace, classname))
+ .Cblock(self._GenerateInitializersAndBody(type_))
+ .Append('%s::~%s() {}' % (classname_in_namespace, classname))
+ .Append()
+ )
+ if type_.origin.from_json:
+ c.Cblock(self._GenerateTypePopulate(classname_in_namespace, type_))
+ if cpp_namespace is None: # only generate for top-level types
+ c.Cblock(self._GenerateTypeFromValue(classname_in_namespace, type_))
+ if type_.origin.from_client:
+ c.Cblock(self._GenerateTypeToValue(classname_in_namespace, type_))
+ elif type_.property_type == PropertyType.ENUM:
+ (c.Cblock(self._GenerateEnumToString(cpp_namespace, type_))
+ .Cblock(self._GenerateEnumFromString(cpp_namespace, type_))
+ )
+
+ return c
+
+ def _GenerateInitializersAndBody(self, type_):
+ items = []
+ for prop in type_.properties.values():
+ if prop.optional:
+ continue
+
+ t = prop.type_
+ if t.property_type == PropertyType.INTEGER:
+ items.append('%s(0)' % prop.unix_name)
+ elif t.property_type == PropertyType.DOUBLE:
+ items.append('%s(0.0)' % prop.unix_name)
+ elif t.property_type == PropertyType.BOOLEAN:
+ items.append('%s(false)' % prop.unix_name)
+ elif (t.property_type == PropertyType.ANY or
+ t.property_type == PropertyType.ARRAY or
+ t.property_type == PropertyType.BINARY or # mapped to std::string
+ t.property_type == PropertyType.CHOICES or
+ t.property_type == PropertyType.ENUM or
+ t.property_type == PropertyType.OBJECT or
+ t.property_type == PropertyType.FUNCTION or
+ t.property_type == PropertyType.REF or
+ t.property_type == PropertyType.STRING):
+ # TODO(miket): It would be nice to initialize CHOICES and ENUM, but we
+ # don't presently have the semantics to indicate which one of a set
+ # should be the default.
+ continue
+ else:
+ raise TypeError(t)
+
+ if items:
+ s = ': %s' % (', '.join(items))
+ else:
+ s = ''
+ s = s + ' {}'
+ return Code().Append(s)
+
+ 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(schema_util.StripNamespace(type_.name))
+ c = Code()
+ (c.Append('// static')
+ .Append('bool %(namespace)s::Populate(')
+ .Sblock(' %s) {' % self._GenerateParams(
+ ('const base::Value& value', '%(name)s* out'))))
+
+ if type_.property_type == PropertyType.CHOICES:
+ for choice in type_.choices:
+ (c.Sblock('if (%s) {' % self._GenerateValueIsTypeExpression('value',
+ choice))
+ .Concat(self._GeneratePopulateVariableFromValue(
+ choice,
+ '(&value)',
+ 'out->as_%s' % choice.unix_name,
+ 'false',
+ is_ptr=True))
+ .Append('return true;')
+ .Eblock('}')
+ )
+ (c.Concat(self._GenerateError(
+ '"expected %s, got " + %s' %
+ (" or ".join(choice.name for choice in type_.choices),
+ self._util_cc_helper.GetValueTypeString('value'))))
+ .Append('return false;'))
+ elif type_.property_type == PropertyType.OBJECT:
+ (c.Sblock('if (!value.IsType(base::Value::TYPE_DICTIONARY)) {')
+ .Concat(self._GenerateError(
+ '"expected dictionary, got " + ' +
+ self._util_cc_helper.GetValueTypeString('value')))
+ .Append('return false;')
+ .Eblock('}'))
+
+ if type_.properties or type_.additional_properties is not None:
+ c.Append('const base::DictionaryValue* dict = '
+ 'static_cast<const base::DictionaryValue*>(&value);')
+ 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 type_.additional_properties is not None:
+ if type_.additional_properties.property_type == PropertyType.ANY:
+ c.Append('out->additional_properties.MergeDictionary(dict);')
+ else:
+ cpp_type = self._type_helper.GetCppType(type_.additional_properties,
+ is_in_container=True)
+ (c.Append('for (base::DictionaryValue::Iterator it(*dict);')
+ .Sblock(' !it.IsAtEnd(); it.Advance()) {')
+ .Append('%s tmp;' % cpp_type)
+ .Concat(self._GeneratePopulateVariableFromValue(
+ type_.additional_properties,
+ '(&it.value())',
+ 'tmp',
+ 'false'))
+ .Append('out->additional_properties[it.key()] = tmp;')
+ .Eblock('}')
+ )
+ c.Append('return true;')
+ (c.Eblock('}')
+ .Substitute({'namespace': cpp_namespace, 'name': classname}))
+ return c
+
+ def _GenerateValueIsTypeExpression(self, var, type_):
+ real_type = self._type_helper.FollowRef(type_)
+ if real_type.property_type is PropertyType.CHOICES:
+ return '(%s)' % ' || '.join(self._GenerateValueIsTypeExpression(var,
+ choice)
+ for choice in real_type.choices)
+ return '%s.IsType(%s)' % (var, cpp_util.GetValueType(real_type))
+
+ def _GenerateTypePopulateProperty(self, prop, src, dst):
+ """Generate the code to populate a single property in a type.
+
+ src: base::DictionaryValue*
+ dst: Type*
+ """
+ c = Code()
+ value_var = prop.unix_name + '_value'
+ c.Append('const base::Value* %(value_var)s = NULL;')
+ if prop.optional:
+ (c.Sblock(
+ 'if (%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s)) {')
+ .Concat(self._GeneratePopulatePropertyFromValue(
+ prop, value_var, dst, 'false')))
+ underlying_type = self._type_helper.FollowRef(prop.type_)
+ if underlying_type.property_type == PropertyType.ENUM:
+ (c.Append('} else {')
+ .Append('%%(dst)s->%%(name)s = %s;' %
+ self._type_helper.GetEnumNoneValue(prop.type_)))
+ c.Eblock('}')
+ else:
+ (c.Sblock(
+ 'if (!%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s)) {')
+ .Concat(self._GenerateError('"\'%%(key)s\' is required"'))
+ .Append('return false;')
+ .Eblock('}')
+ .Concat(self._GeneratePopulatePropertyFromValue(
+ prop, value_var, dst, 'false'))
+ )
+ c.Append()
+ c.Substitute({
+ 'value_var': value_var,
+ 'key': prop.name,
+ 'src': src,
+ 'dst': dst,
+ 'name': prop.unix_name
+ })
+ return c
+
+ def _GenerateTypeFromValue(self, cpp_namespace, type_):
+ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name))
+ c = Code()
+ (c.Append('// static')
+ .Append('scoped_ptr<%s> %s::FromValue(%s) {' % (classname,
+ cpp_namespace, self._GenerateParams(('const base::Value& value',))))
+ .Append(' scoped_ptr<%s> out(new %s());' % (classname, classname))
+ .Append(' if (!Populate(%s))' % self._GenerateArgs(
+ ('value', 'out.get()')))
+ .Append(' return scoped_ptr<%s>();' % classname)
+ .Append(' return out.Pass();')
+ .Append('}')
+ )
+ return c
+
+ def _GenerateTypeToValue(self, cpp_namespace, type_):
+ """Generates a function that serializes the type into a base::Value.
+ E.g. for type "Foo" generates Foo::ToValue()
+ """
+ if type_.property_type == PropertyType.OBJECT:
+ return self._GenerateObjectTypeToValue(cpp_namespace, type_)
+ elif type_.property_type == PropertyType.CHOICES:
+ return self._GenerateChoiceTypeToValue(cpp_namespace, type_)
+ else:
+ raise ValueError("Unsupported property type %s" % type_.type_)
+
+ def _GenerateObjectTypeToValue(self, cpp_namespace, type_):
+ """Generates a function that serializes an object-representing type
+ into a base::DictionaryValue.
+ """
+ c = Code()
+ (c.Sblock('scoped_ptr<base::DictionaryValue> %s::ToValue() const {' %
+ cpp_namespace)
+ .Append('scoped_ptr<base::DictionaryValue> value('
+ 'new base::DictionaryValue());')
+ .Append()
+ )
+
+ for prop in type_.properties.values():
+ if prop.optional:
+ # Optional enum values are generated with a NONE enum value.
+ underlying_type = self._type_helper.FollowRef(prop.type_)
+ if underlying_type.property_type == PropertyType.ENUM:
+ c.Sblock('if (%s != %s) {' %
+ (prop.unix_name,
+ self._type_helper.GetEnumNoneValue(prop.type_)))
+ else:
+ c.Sblock('if (%s.get()) {' % prop.unix_name)
+
+ # ANY is a base::Value which is abstract and cannot be a direct member, so
+ # it will always be a pointer.
+ is_ptr = prop.optional or prop.type_.property_type == PropertyType.ANY
+ c.Append('value->SetWithoutPathExpansion("%s", %s);' % (
+ prop.name,
+ self._CreateValueFromType(prop.type_,
+ 'this->%s' % prop.unix_name,
+ is_ptr=is_ptr)))
+
+ if prop.optional:
+ c.Eblock('}');
+
+ if type_.additional_properties is not None:
+ if type_.additional_properties.property_type == PropertyType.ANY:
+ c.Append('value->MergeDictionary(&additional_properties);')
+ else:
+ # Non-copyable types will be wrapped in a linked_ptr for inclusion in
+ # maps, so we need to unwrap them.
+ needs_unwrap = (
+ not self._type_helper.IsCopyable(type_.additional_properties))
+ cpp_type = self._type_helper.GetCppType(type_.additional_properties,
+ is_in_container=True)
+ (c.Sblock('for (std::map<std::string, %s>::const_iterator it =' %
+ cpp_util.PadForGenerics(cpp_type))
+ .Append(' additional_properties.begin();')
+ .Append(' it != additional_properties.end(); ++it) {')
+ .Append('value->SetWithoutPathExpansion(it->first, %s);' %
+ self._CreateValueFromType(
+ type_.additional_properties,
+ '%sit->second' % ('*' if needs_unwrap else '')))
+ .Eblock('}')
+ )
+
+ return (c.Append()
+ .Append('return value.Pass();')
+ .Eblock('}'))
+
+ def _GenerateChoiceTypeToValue(self, cpp_namespace, type_):
+ """Generates a function that serializes a choice-representing type
+ into a base::Value.
+ """
+ c = Code()
+ c.Sblock('scoped_ptr<base::Value> %s::ToValue() const {' % cpp_namespace)
+ c.Append('scoped_ptr<base::Value> result;');
+ for choice in type_.choices:
+ choice_var = 'as_%s' % choice.unix_name
+ (c.Sblock('if (%s) {' % choice_var)
+ .Append('DCHECK(!result) << "Cannot set multiple choices for %s";' %
+ type_.unix_name)
+ .Append('result.reset(%s);' %
+ self._CreateValueFromType(choice, '*%s' % choice_var))
+ .Eblock('}')
+ )
+ (c.Append('DCHECK(result) << "Must set at least one choice for %s";' %
+ type_.unix_name)
+ .Append('return result.Pass();')
+ .Eblock('}')
+ )
+ return c
+
+ def _GenerateFunction(self, function):
+ """Generates the definitions for function structs.
+ """
+ c = Code()
+
+ # TODO(kalman): use function.unix_name not Classname.
+ function_namespace = cpp_util.Classname(function.name)
+ """Windows has a #define for SendMessage, so to avoid any issues, we need
+ to not use the name.
+ """
+ if function_namespace == 'SendMessage':
+ function_namespace = 'PassMessage'
+ (c.Append('namespace %s {' % function_namespace)
+ .Append()
+ )
+
+ # Params::Populate function
+ if function.params:
+ c.Concat(self._GeneratePropertyFunctions('Params', function.params))
+ (c.Append('Params::Params() {}')
+ .Append('Params::~Params() {}')
+ .Append()
+ .Cblock(self._GenerateFunctionParamsCreate(function))
+ )
+
+ # Results::Create function
+ if function.callback:
+ c.Concat(self._GenerateCreateCallbackArguments('Results',
+ function.callback))
+
+ c.Append('} // namespace %s' % function_namespace)
+ return c
+
+ def _GenerateEvent(self, event):
+ # TODO(kalman): use event.unix_name not Classname.
+ c = Code()
+ event_namespace = cpp_util.Classname(event.name)
+ (c.Append('namespace %s {' % event_namespace)
+ .Append()
+ .Cblock(self._GenerateEventNameConstant(None, event))
+ .Cblock(self._GenerateCreateCallbackArguments(None, event))
+ .Append('} // namespace %s' % event_namespace)
+ )
+ return c
+
+ def _CreateValueFromType(self, type_, var, is_ptr=False):
+ """Creates a base::Value given a type. Generated code passes ownership
+ to caller.
+
+ var: variable or variable*
+
+ E.g for std::string, generate new base::StringValue(var)
+ """
+ underlying_type = self._type_helper.FollowRef(type_)
+ if (underlying_type.property_type == PropertyType.CHOICES or
+ underlying_type.property_type == PropertyType.OBJECT):
+ if is_ptr:
+ return '(%s)->ToValue().release()' % var
+ else:
+ return '(%s).ToValue().release()' % var
+ elif (underlying_type.property_type == PropertyType.ANY or
+ underlying_type.property_type == PropertyType.FUNCTION):
+ if is_ptr:
+ vardot = '(%s)->' % var
+ else:
+ vardot = '(%s).' % var
+ return '%sDeepCopy()' % vardot
+ elif underlying_type.property_type == PropertyType.ENUM:
+ return 'new base::StringValue(ToString(%s))' % var
+ elif underlying_type.property_type == PropertyType.BINARY:
+ if is_ptr:
+ vardot = var + '->'
+ else:
+ vardot = var + '.'
+ return ('base::BinaryValue::CreateWithCopiedBuffer(%sdata(), %ssize())' %
+ (vardot, vardot))
+ elif underlying_type.property_type == PropertyType.ARRAY:
+ return '%s.release()' % self._util_cc_helper.CreateValueFromArray(
+ underlying_type,
+ var,
+ is_ptr)
+ elif underlying_type.property_type.is_fundamental:
+ if is_ptr:
+ var = '*%s' % var
+ if underlying_type.property_type == PropertyType.STRING:
+ return 'new base::StringValue(%s)' % var
+ else:
+ return 'new base::FundamentalValue(%s)' % var
+ else:
+ raise NotImplementedError('Conversion of %s to base::Value not '
+ 'implemented' % repr(type_.type_))
+
+ def _GenerateParamsCheck(self, function, var):
+ """Generates a check for the correct number of arguments when creating
+ Params.
+ """
+ c = Code()
+ num_required = 0
+ for param in function.params:
+ if not param.optional:
+ num_required += 1
+ if num_required == len(function.params):
+ c.Sblock('if (%(var)s.GetSize() != %(total)d) {')
+ elif not num_required:
+ c.Sblock('if (%(var)s.GetSize() > %(total)d) {')
+ else:
+ c.Sblock('if (%(var)s.GetSize() < %(required)d'
+ ' || %(var)s.GetSize() > %(total)d) {')
+ (c.Concat(self._GenerateError(
+ '"expected %%(total)d arguments, got " '
+ '+ base::IntToString(%%(var)s.GetSize())'))
+ .Append('return scoped_ptr<Params>();')
+ .Eblock('}')
+ .Substitute({
+ 'var': var,
+ 'required': num_required,
+ 'total': len(function.params),
+ }))
+ return c
+
+ def _GenerateFunctionParamsCreate(self, function):
+ """Generate function to create an instance of Params. The generated
+ function takes a base::ListValue of arguments.
+
+ E.g for function "Bar", generate Bar::Params::Create()
+ """
+ c = Code()
+ (c.Append('// static')
+ .Sblock('scoped_ptr<Params> Params::Create(%s) {' % self._GenerateParams(
+ ['const base::ListValue& args']))
+ .Concat(self._GenerateParamsCheck(function, 'args'))
+ .Append('scoped_ptr<Params> params(new Params());'))
+
+ for param in function.params:
+ c.Concat(self._InitializePropertyToDefault(param, 'params'))
+
+ for i, param in enumerate(function.params):
+ # Any failure will cause this function to return. If any argument is
+ # incorrect or missing, those following it are not processed. Note that
+ # for optional arguments, we allow missing arguments and proceed because
+ # there may be other arguments following it.
+ failure_value = 'scoped_ptr<Params>()'
+ c.Append()
+ value_var = param.unix_name + '_value'
+ (c.Append('const base::Value* %(value_var)s = NULL;')
+ .Append('if (args.Get(%(i)s, &%(value_var)s) &&')
+ .Sblock(' !%(value_var)s->IsType(base::Value::TYPE_NULL)) {')
+ .Concat(self._GeneratePopulatePropertyFromValue(
+ param, value_var, 'params', failure_value))
+ .Eblock('}')
+ )
+ if not param.optional:
+ (c.Sblock('else {')
+ .Concat(self._GenerateError('"\'%%(key)s\' is required"'))
+ .Append('return %s;' % failure_value)
+ .Eblock('}'))
+ c.Substitute({'value_var': value_var, 'i': i, 'key': param.name})
+ (c.Append()
+ .Append('return params.Pass();')
+ .Eblock('}')
+ .Append()
+ )
+
+ return c
+
+ def _GeneratePopulatePropertyFromValue(self,
+ prop,
+ src_var,
+ dst_class_var,
+ failure_value):
+ """Generates code to populate property |prop| of |dst_class_var| (a
+ pointer) from a Value*. See |_GeneratePopulateVariableFromValue| for
+ semantics.
+ """
+ return self._GeneratePopulateVariableFromValue(prop.type_,
+ src_var,
+ '%s->%s' % (dst_class_var,
+ prop.unix_name),
+ failure_value,
+ is_ptr=prop.optional)
+
+ def _GeneratePopulateVariableFromValue(self,
+ type_,
+ src_var,
+ dst_var,
+ failure_value,
+ is_ptr=False):
+ """Generates code to populate a variable |dst_var| of type |type_| from a
+ Value* at |src_var|. The Value* is assumed to be non-NULL. In the generated
+ code, if |dst_var| fails to be populated then Populate will return
+ |failure_value|.
+ """
+ c = Code()
+ c.Sblock('{')
+
+ underlying_type = self._type_helper.FollowRef(type_)
+
+ if underlying_type.property_type.is_fundamental:
+ if is_ptr:
+ (c.Append('%(cpp_type)s temp;')
+ .Sblock('if (!%s) {' % cpp_util.GetAsFundamentalValue(
+ self._type_helper.FollowRef(type_), src_var, '&temp'))
+ .Concat(self._GenerateError(
+ '"\'%%(key)s\': expected ' + '%s, got " + %s' % (
+ type_.name,
+ self._util_cc_helper.GetValueTypeString(
+ '%%(src_var)s', True))))
+ .Append('return %(failure_value)s;')
+ .Eblock('}')
+ .Append('%(dst_var)s.reset(new %(cpp_type)s(temp));')
+ )
+ else:
+ (c.Sblock('if (!%s) {' % cpp_util.GetAsFundamentalValue(
+ self._type_helper.FollowRef(type_),
+ src_var,
+ '&%s' % dst_var))
+ .Concat(self._GenerateError(
+ '"\'%%(key)s\': expected ' + '%s, got " + %s' % (
+ type_.name,
+ self._util_cc_helper.GetValueTypeString(
+ '%%(src_var)s', True))))
+ .Append('return %(failure_value)s;')
+ .Eblock('}')
+ )
+ elif underlying_type.property_type == PropertyType.OBJECT:
+ if is_ptr:
+ (c.Append('const base::DictionaryValue* dictionary = NULL;')
+ .Sblock('if (!%(src_var)s->GetAsDictionary(&dictionary)) {')
+ .Concat(self._GenerateError(
+ '"\'%%(key)s\': expected dictionary, got " + ' +
+ self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
+ .Append('return %(failure_value)s;')
+ .Eblock('}')
+ .Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());')
+ .Append('if (!%%(cpp_type)s::Populate(%s)) {' % self._GenerateArgs(
+ ('*dictionary', 'temp.get()')))
+ .Append(' return %(failure_value)s;')
+ .Append('}')
+ .Append('%(dst_var)s = temp.Pass();')
+ )
+ else:
+ (c.Append('const base::DictionaryValue* dictionary = NULL;')
+ .Sblock('if (!%(src_var)s->GetAsDictionary(&dictionary)) {')
+ .Concat(self._GenerateError(
+ '"\'%%(key)s\': expected dictionary, got " + ' +
+ self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
+ .Append('return %(failure_value)s;')
+ .Eblock('}')
+ .Append('if (!%%(cpp_type)s::Populate(%s)) {' % self._GenerateArgs(
+ ('*dictionary', '&%(dst_var)s')))
+ .Append(' return %(failure_value)s;')
+ .Append('}')
+ )
+ elif underlying_type.property_type == PropertyType.FUNCTION:
+ if is_ptr:
+ c.Append('%(dst_var)s.reset(new base::DictionaryValue());')
+ elif underlying_type.property_type == PropertyType.ANY:
+ c.Append('%(dst_var)s.reset(%(src_var)s->DeepCopy());')
+ elif underlying_type.property_type == PropertyType.ARRAY:
+ # util_cc_helper deals with optional and required arrays
+ (c.Append('const base::ListValue* list = NULL;')
+ .Sblock('if (!%(src_var)s->GetAsList(&list)) {')
+ .Concat(self._GenerateError(
+ '"\'%%(key)s\': expected list, got " + ' +
+ self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
+ .Append('return %(failure_value)s;')
+ .Eblock('}'))
+ item_type = self._type_helper.FollowRef(underlying_type.item_type)
+ if item_type.property_type == PropertyType.ENUM:
+ c.Concat(self._GenerateListValueToEnumArrayConversion(
+ item_type,
+ 'list',
+ dst_var,
+ failure_value,
+ is_ptr=is_ptr))
+ else:
+ (c.Sblock('if (!%s) {' % self._util_cc_helper.PopulateArrayFromList(
+ underlying_type,
+ 'list',
+ dst_var,
+ is_ptr))
+ .Concat(self._GenerateError(
+ '"unable to populate array \'%%(parent_key)s\'"'))
+ .Append('return %(failure_value)s;')
+ .Eblock('}')
+ )
+ elif underlying_type.property_type == PropertyType.CHOICES:
+ if is_ptr:
+ (c.Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());')
+ .Append('if (!%%(cpp_type)s::Populate(%s))' % self._GenerateArgs(
+ ('*%(src_var)s', 'temp.get()')))
+ .Append(' return %(failure_value)s;')
+ .Append('%(dst_var)s = temp.Pass();')
+ )
+ else:
+ (c.Append('if (!%%(cpp_type)s::Populate(%s))' % self._GenerateArgs(
+ ('*%(src_var)s', '&%(dst_var)s')))
+ .Append(' return %(failure_value)s;'))
+ elif underlying_type.property_type == PropertyType.ENUM:
+ c.Concat(self._GenerateStringToEnumConversion(type_,
+ src_var,
+ dst_var,
+ failure_value))
+ elif underlying_type.property_type == PropertyType.BINARY:
+ (c.Sblock('if (!%(src_var)s->IsType(base::Value::TYPE_BINARY)) {')
+ .Concat(self._GenerateError(
+ '"\'%%(key)s\': expected binary, got " + ' +
+ self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
+ .Append('return %(failure_value)s;')
+ .Eblock('}')
+ .Append('const base::BinaryValue* binary_value =')
+ .Append(' static_cast<const base::BinaryValue*>(%(src_var)s);')
+ )
+ if is_ptr:
+ (c.Append('%(dst_var)s.reset(')
+ .Append(' new std::string(binary_value->GetBuffer(),')
+ .Append(' binary_value->GetSize()));')
+ )
+ else:
+ (c.Append('%(dst_var)s.assign(binary_value->GetBuffer(),')
+ .Append(' binary_value->GetSize());')
+ )
+ else:
+ raise NotImplementedError(type_)
+ return c.Eblock('}').Substitute({
+ 'cpp_type': self._type_helper.GetCppType(type_),
+ 'src_var': src_var,
+ 'dst_var': dst_var,
+ 'failure_value': failure_value,
+ 'key': type_.name,
+ 'parent_key': type_.parent.name
+ })
+
+ def _GenerateListValueToEnumArrayConversion(self,
+ item_type,
+ src_var,
+ dst_var,
+ failure_value,
+ is_ptr=False):
+ """Returns Code that converts a ListValue of string constants from
+ |src_var| into an array of enums of |type_| in |dst_var|. On failure,
+ returns |failure_value|.
+ """
+ c = Code()
+ accessor = '.'
+ if is_ptr:
+ accessor = '->'
+ cpp_type = self._type_helper.GetCppType(item_type, is_in_container=True)
+ c.Append('%s.reset(new std::vector<%s>);' %
+ (dst_var, cpp_util.PadForGenerics(cpp_type)))
+ (c.Sblock('for (base::ListValue::const_iterator it = %s->begin(); '
+ 'it != %s->end(); ++it) {' % (src_var, src_var))
+ .Append('%s tmp;' % self._type_helper.GetCppType(item_type))
+ .Concat(self._GenerateStringToEnumConversion(item_type,
+ '(*it)',
+ 'tmp',
+ failure_value))
+ .Append('%s%spush_back(tmp);' % (dst_var, accessor))
+ .Eblock('}')
+ )
+ return c
+
+ def _GenerateStringToEnumConversion(self,
+ type_,
+ src_var,
+ dst_var,
+ failure_value):
+ """Returns Code that converts a string type in |src_var| to an enum with
+ type |type_| in |dst_var|. In the generated code, if |src_var| is not
+ a valid enum name then the function will return |failure_value|.
+ """
+ c = Code()
+ enum_as_string = '%s_as_string' % type_.unix_name
+ (c.Append('std::string %s;' % enum_as_string)
+ .Sblock('if (!%s->GetAsString(&%s)) {' % (src_var, enum_as_string))
+ .Concat(self._GenerateError(
+ '"\'%%(key)s\': expected string, got " + ' +
+ self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
+ .Append('return %s;' % failure_value)
+ .Eblock('}')
+ .Append('%s = Parse%s(%s);' % (dst_var,
+ self._type_helper.GetCppType(type_),
+ enum_as_string))
+ .Sblock('if (%s == %s) {' % (dst_var,
+ self._type_helper.GetEnumNoneValue(type_)))
+ .Concat(self._GenerateError(
+ '\"\'%%(key)s\': expected \\"' +
+ '\\" or \\"'.join(self._type_helper.FollowRef(type_).enum_values) +
+ '\\", got \\"" + %s + "\\""' % enum_as_string))
+ .Append('return %s;' % failure_value)
+ .Eblock('}')
+ .Substitute({'src_var': src_var, 'key': type_.name})
+ )
+ return c
+
+ def _GeneratePropertyFunctions(self, namespace, params):
+ """Generates the member functions for a list of parameters.
+ """
+ return self._GenerateTypes(namespace, (param.type_ for param in params))
+
+ def _GenerateTypes(self, namespace, types):
+ """Generates the member functions for a list of types.
+ """
+ c = Code()
+ for type_ in types:
+ c.Cblock(self._GenerateType(namespace, type_))
+ return c
+
+ def _GenerateEnumToString(self, cpp_namespace, type_):
+ """Generates ToString() which gets the string representation of an enum.
+ """
+ c = Code()
+ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name))
+
+ if cpp_namespace is not None:
+ c.Append('// static')
+ maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace
+
+ c.Sblock('std::string %sToString(%s enum_param) {' %
+ (maybe_namespace, classname))
+ c.Sblock('switch (enum_param) {')
+ for enum_value in self._type_helper.FollowRef(type_).enum_values:
+ (c.Append('case %s: ' % self._type_helper.GetEnumValue(type_, enum_value))
+ .Append(' return "%s";' % enum_value))
+ (c.Append('case %s:' % self._type_helper.GetEnumNoneValue(type_))
+ .Append(' return "";')
+ .Eblock('}')
+ .Append('NOTREACHED();')
+ .Append('return "";')
+ .Eblock('}')
+ )
+ return c
+
+ def _GenerateEnumFromString(self, cpp_namespace, type_):
+ """Generates FromClassNameString() which gets an enum from its string
+ representation.
+ """
+ c = Code()
+ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name))
+
+ if cpp_namespace is not None:
+ c.Append('// static')
+ maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace
+
+ c.Sblock('%s%s %sParse%s(const std::string& enum_string) {' %
+ (maybe_namespace, classname, maybe_namespace, classname))
+ for i, enum_value in enumerate(
+ self._type_helper.FollowRef(type_).enum_values):
+ # This is broken up into all ifs with no else ifs because we get
+ # "fatal error C1061: compiler limit : blocks nested too deeply"
+ # on Windows.
+ (c.Append('if (enum_string == "%s")' % enum_value)
+ .Append(' return %s;' %
+ self._type_helper.GetEnumValue(type_, enum_value)))
+ (c.Append('return %s;' % self._type_helper.GetEnumNoneValue(type_))
+ .Eblock('}')
+ )
+ return c
+
+ def _GenerateCreateCallbackArguments(self, function_scope, callback):
+ """Generate all functions to create Value parameters for a callback.
+
+ E.g for function "Bar", generate Bar::Results::Create
+ E.g for event "Baz", generate Baz::Create
+
+ function_scope: the function scope path, e.g. Foo::Bar for the function
+ Foo::Bar::Baz(). May be None if there is no function scope.
+ callback: the Function object we are creating callback arguments for.
+ """
+ c = Code()
+ params = callback.params
+ c.Concat(self._GeneratePropertyFunctions(function_scope, params))
+
+ (c.Sblock('scoped_ptr<base::ListValue> %(function_scope)s'
+ 'Create(%(declaration_list)s) {')
+ .Append('scoped_ptr<base::ListValue> create_results('
+ 'new base::ListValue());')
+ )
+ declaration_list = []
+ for param in params:
+ declaration_list.append(cpp_util.GetParameterDeclaration(
+ param, self._type_helper.GetCppType(param.type_)))
+ c.Append('create_results->Append(%s);' %
+ self._CreateValueFromType(param.type_, param.unix_name))
+ c.Append('return create_results.Pass();')
+ c.Eblock('}')
+ c.Substitute({
+ 'function_scope': ('%s::' % function_scope) if function_scope else '',
+ 'declaration_list': ', '.join(declaration_list),
+ 'param_names': ', '.join(param.unix_name for param in params)
+ })
+ return c
+
+ def _GenerateEventNameConstant(self, function_scope, event):
+ """Generates a constant string array for the event name.
+ """
+ c = Code()
+ c.Append('const char kEventName[] = "%s.%s";' % (
+ self._namespace.name, event.name))
+ return c
+
+ 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()
+ underlying_type = self._type_helper.FollowRef(prop.type_)
+ if (underlying_type.property_type == PropertyType.ENUM and
+ prop.optional):
+ c.Append('%s->%s = %s;' % (
+ dst,
+ prop.unix_name,
+ self._type_helper.GetEnumNoneValue(prop.type_)))
+ return c
+
+ def _GenerateError(self, body):
+ """Generates an error message pertaining to population failure.
+
+ E.g 'expected bool, got int'
+ """
+ c = Code()
+ if not self._generate_error_messages:
+ return c
+ (c.Append('if (error)')
+ .Append(' *error = ' + body + ';'))
+ return c
+
+ def _GenerateParams(self, params):
+ """Builds the parameter list for a function, given an array of parameters.
+ """
+ if self._generate_error_messages:
+ params = list(params) + ['std::string* error']
+ return ', '.join(str(p) for p in params)
+
+ def _GenerateArgs(self, args):
+ """Builds the argument list for a function, given an array of arguments.
+ """
+ if self._generate_error_messages:
+ args = list(args) + ['error']
+ return ', '.join(str(a) for a in args)
diff --git a/chromium/tools/json_schema_compiler/code.py b/chromium/tools/json_schema_compiler/code.py
new file mode 100644
index 00000000000..3622237a84a
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/code.py
@@ -0,0 +1,141 @@
+# 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.
+
+class Code(object):
+ """A convenience object for constructing code.
+
+ Logically each object should be a block of code. All methods except |Render|
+ and |IsEmpty| return self.
+ """
+ def __init__(self, indent_size=2, comment_length=80):
+ self._code = []
+ self._indent_level = 0
+ self._indent_size = indent_size
+ self._comment_length = comment_length
+
+ def Append(self, line='', substitute=True, indent_level=None):
+ """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().
+ """
+ if indent_level is None:
+ indent_level = self._indent_level
+ self._code.append(Line(((' ' * indent_level) + line).rstrip(),
+ substitute=substitute))
+ return self
+
+ def IsEmpty(self):
+ """Returns True if the Code object is empty.
+ """
+ return not bool(self._code)
+
+ def Concat(self, obj):
+ """Concatenate another Code object onto this one. Trailing whitespace is
+ stripped.
+
+ Appends the code at the current indent level. Will fail if there are any
+ un-interpolated format specifiers eg %s, %(something)s which helps
+ isolate any strings that haven't been substituted.
+ """
+ if not isinstance(obj, Code):
+ raise TypeError(type(obj))
+ assert self is not obj
+ for line in obj._code:
+ try:
+ # line % () will fail if any substitution tokens are left in line
+ if line.substitute:
+ line.value %= ()
+ except TypeError:
+ raise TypeError('Unsubstituted value when concatting\n' + line.value)
+ except ValueError:
+ raise ValueError('Stray % character when concatting\n' + line.value)
+ self.Append(line.value, line.substitute)
+
+ return self
+
+ def Cblock(self, code):
+ """Concatenates another Code object |code| onto this one followed by a
+ blank line, if |code| is non-empty."""
+ if not code.IsEmpty():
+ self.Concat(code).Append()
+ return self
+
+ def Sblock(self, line=None):
+ """Starts a code block.
+
+ Appends a line of code and then increases the indent level.
+ """
+ if line is not None:
+ self.Append(line)
+ self._indent_level += self._indent_size
+ return self
+
+ def Eblock(self, line=None):
+ """Ends a code block by decreasing and then appending a line (or a blank
+ line if not given).
+ """
+ # TODO(calamity): Decide if type checking is necessary
+ #if not isinstance(line, basestring):
+ # raise TypeError
+ self._indent_level -= self._indent_size
+ if line is not None:
+ self.Append(line)
+ return self
+
+ 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().
+ """
+ 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(' ')
+ if last_space != -1:
+ line = line[0:last_space]
+ comment = comment[last_space + 1:]
+ else:
+ comment = comment[max_len:]
+ self.Append(comment_prefix + line, substitute=False)
+ self.Append(comment_prefix + comment, substitute=False)
+ return self
+
+ def Substitute(self, d):
+ """Goes through each line and interpolates using the given dict.
+
+ Raises type error if passed something that isn't a dict
+
+ Use for long pieces of code using interpolation with the same variables
+ repeatedly. This will reduce code and allow for named placeholders which
+ are more clear.
+ """
+ if not isinstance(d, dict):
+ raise TypeError('Passed argument is not a dictionary: ' + d)
+ for i, line in enumerate(self._code):
+ 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([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/chromium/tools/json_schema_compiler/code_test.py b/chromium/tools/json_schema_compiler/code_test.py
new file mode 100755
index 00000000000..ca3652420f9
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/code_test.py
@@ -0,0 +1,165 @@
+#!/usr/bin/env python
+# 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.
+
+from code import Code
+import unittest
+
+class CodeTest(unittest.TestCase):
+ def testAppend(self):
+ c = Code()
+ c.Append('line')
+ self.assertEquals('line', c.Render())
+
+ def testBlock(self):
+ c = Code()
+ (c.Append('line')
+ .Sblock('sblock')
+ .Append('inner')
+ .Append('moreinner')
+ .Sblock('moresblock')
+ .Append('inner')
+ .Eblock('out')
+ .Append('inner')
+ .Eblock('out')
+ )
+ self.assertEquals(
+ 'line\n'
+ 'sblock\n'
+ ' inner\n'
+ ' moreinner\n'
+ ' moresblock\n'
+ ' inner\n'
+ ' out\n'
+ ' inner\n'
+ 'out',
+ c.Render())
+
+ def testConcat(self):
+ b = Code()
+ (b.Sblock('2')
+ .Append('2')
+ .Eblock('2')
+ )
+ c = Code()
+ (c.Sblock('1')
+ .Concat(b)
+ .Append('1')
+ .Eblock('1')
+ )
+ self.assertEquals(
+ '1\n'
+ ' 2\n'
+ ' 2\n'
+ ' 2\n'
+ ' 1\n'
+ '1',
+ c.Render())
+ d = Code()
+ a = Code()
+ a.Concat(d)
+ self.assertEquals('', a.Render())
+ a.Concat(c)
+ self.assertEquals(
+ '1\n'
+ ' 2\n'
+ ' 2\n'
+ ' 2\n'
+ ' 1\n'
+ '1',
+ a.Render())
+
+ def testConcatErrors(self):
+ c = Code()
+ d = Code()
+ d.Append('%s')
+ self.assertRaises(TypeError, c.Concat, d)
+ d = Code()
+ d.Append('%(classname)s')
+ self.assertRaises(TypeError, c.Concat, d)
+ d = 'line of code'
+ self.assertRaises(TypeError, c.Concat, d)
+
+ def testSubstitute(self):
+ c = Code()
+ c.Append('%(var1)s %(var2)s %(var1)s')
+ c.Substitute({'var1': 'one', 'var2': 'two'})
+ self.assertEquals('one two one', c.Render())
+ c.Append('%(var1)s %(var2)s %(var3)s')
+ c.Append('%(var2)s %(var1)s %(var3)s')
+ c.Substitute({'var1': 'one', 'var2': 'two', 'var3': 'three'})
+ self.assertEquals(
+ 'one two one\n'
+ 'one two three\n'
+ 'two one three',
+ c.Render())
+
+ def testSubstituteErrors(self):
+ # No unnamed placeholders allowed when substitute is run
+ c = Code()
+ c.Append('%s %s')
+ self.assertRaises(TypeError, c.Substitute, ('var1', 'one'))
+ c = Code()
+ c.Append('%s %(var1)s')
+ self.assertRaises(TypeError, c.Substitute, {'var1': 'one'})
+ c = Code()
+ c.Append('%s %(var1)s')
+ self.assertRaises(TypeError, c.Substitute, {'var1': 'one'})
+ c = Code()
+ c.Append('%(var1)s')
+ self.assertRaises(KeyError, c.Substitute, {'clearlynotvar1': 'one'})
+
+ def testIsEmpty(self):
+ c = Code()
+ self.assertTrue(c.IsEmpty())
+ c.Append('asdf')
+ self.assertFalse(c.IsEmpty())
+
+ def testComment(self):
+ long_comment = ('This comment is eighty nine characters in longness, '
+ 'that is, to use another word, length')
+ c = Code()
+ c.Comment(long_comment)
+ self.assertEquals(
+ '// This comment is eighty nine characters '
+ 'in longness, that is, to use another\n'
+ '// word, length',
+ c.Render())
+ c = Code()
+ c.Sblock('sblock')
+ c.Comment(long_comment)
+ c.Eblock('eblock')
+ c.Comment(long_comment)
+ self.assertEquals(
+ 'sblock\n'
+ ' // This comment is eighty nine characters '
+ 'in longness, that is, to use\n'
+ ' // another word, length\n'
+ 'eblock\n'
+ '// This comment is eighty nine characters in '
+ 'longness, that is, to use another\n'
+ '// word, length',
+ c.Render())
+ long_word = 'x' * 100
+ c = Code()
+ c.Comment(long_word)
+ self.assertEquals(
+ '// ' + 'x' * 77 + '\n'
+ '// ' + '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/chromium/tools/json_schema_compiler/compiler.py b/chromium/tools/json_schema_compiler/compiler.py
new file mode 100755
index 00000000000..acf64acd91d
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/compiler.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+# 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.
+"""Generator for C++ structs from api json files.
+
+The purpose of this tool is to remove the need for hand-written code that
+converts to and from base::Value types when receiving javascript api calls.
+Originally written for generating code for extension apis. Reference schemas
+are in chrome/common/extensions/api.
+
+Usage example:
+ compiler.py --root /home/Work/src --namespace extensions windows.json
+ tabs.json
+ compiler.py --destdir gen --root /home/Work/src
+ --namespace extensions windows.json tabs.json
+"""
+
+import optparse
+import os
+import sys
+
+from cpp_bundle_generator import CppBundleGenerator
+from cpp_generator import CppGenerator
+from cpp_type_generator import CppTypeGenerator
+from dart_generator import DartGenerator
+import json_schema
+from model import Model, UnixName
+from schema_loader import SchemaLoader
+
+# Names of supported code generators, as specified on the command-line.
+# First is default.
+GENERATORS = ['cpp', 'cpp-bundle', 'dart']
+
+def GenerateSchema(generator,
+ filenames,
+ root,
+ destdir,
+ root_namespace,
+ dart_overrides_dir):
+ schema_loader = SchemaLoader(os.path.dirname(os.path.relpath(
+ os.path.normpath(filenames[0]), root)))
+ # Merge the source files into a single list of schemas.
+ api_defs = []
+ for filename in filenames:
+ schema = os.path.normpath(filename)
+ schema_filename, schema_extension = os.path.splitext(schema)
+ path, short_filename = os.path.split(schema_filename)
+ api_def = schema_loader.LoadSchema(schema)
+
+ # If compiling the C++ model code, delete 'nocompile' nodes.
+ if generator == 'cpp':
+ api_def = json_schema.DeleteNodes(api_def, 'nocompile')
+ api_defs.extend(api_def)
+
+ api_model = Model()
+
+ # For single-schema compilation make sure that the first (i.e. only) schema
+ # is the default one.
+ default_namespace = None
+
+ # Load the actual namespaces into the model.
+ for target_namespace, schema_filename in zip(api_defs, filenames):
+ relpath = os.path.relpath(os.path.normpath(schema_filename), root)
+ namespace = api_model.AddNamespace(target_namespace,
+ relpath,
+ include_compiler_options=True)
+ if default_namespace is None:
+ default_namespace = namespace
+
+ path, filename = os.path.split(schema_filename)
+ short_filename, extension = os.path.splitext(filename)
+
+ # Filenames are checked against the unix_names of the namespaces they
+ # generate because the gyp uses the names of the JSON files to generate
+ # the names of the .cc and .h files. We want these to be using unix_names.
+ if namespace.unix_name != short_filename:
+ sys.exit("Filename %s is illegal. Name files using unix_hacker style." %
+ schema_filename)
+
+ # The output filename must match the input filename for gyp to deal with it
+ # properly.
+ out_file = namespace.unix_name
+
+ # Construct the type generator with all the namespaces in this model.
+ type_generator = CppTypeGenerator(api_model,
+ schema_loader,
+ default_namespace=default_namespace)
+
+ if generator == 'cpp-bundle':
+ cpp_bundle_generator = CppBundleGenerator(root,
+ api_model,
+ api_defs,
+ type_generator,
+ root_namespace)
+ generators = [
+ ('generated_api.cc', cpp_bundle_generator.api_cc_generator),
+ ('generated_api.h', cpp_bundle_generator.api_h_generator),
+ ('generated_schemas.cc', cpp_bundle_generator.schemas_cc_generator),
+ ('generated_schemas.h', cpp_bundle_generator.schemas_h_generator)
+ ]
+ elif generator == 'cpp':
+ cpp_generator = CppGenerator(type_generator, root_namespace)
+ generators = [
+ ('%s.h' % namespace.unix_name, cpp_generator.h_generator),
+ ('%s.cc' % namespace.unix_name, cpp_generator.cc_generator)
+ ]
+ elif generator == 'dart':
+ generators = [
+ ('%s.dart' % namespace.unix_name, DartGenerator(
+ dart_overrides_dir))
+ ]
+ else:
+ raise Exception('Unrecognised generator %s' % generator)
+
+ output_code = []
+ for filename, generator in generators:
+ code = generator.Generate(namespace).Render()
+ if destdir:
+ with open(os.path.join(destdir, namespace.source_file_dir,
+ filename), 'w') as f:
+ f.write(code)
+ output_code += [filename, '', code, '']
+
+ return '\n'.join(output_code)
+
+if __name__ == '__main__':
+ parser = optparse.OptionParser(
+ description='Generates a C++ model of an API from JSON schema',
+ usage='usage: %prog [option]... schema')
+ parser.add_option('-r', '--root', default='.',
+ help='logical include root directory. Path to schema files from specified'
+ 'dir will be the include path.')
+ parser.add_option('-d', '--destdir',
+ help='root directory to output generated files.')
+ parser.add_option('-n', '--namespace', default='generated_api_schemas',
+ help='C++ namespace for generated files. e.g extensions::api.')
+ parser.add_option('-g', '--generator', default=GENERATORS[0],
+ choices=GENERATORS,
+ help='The generator to use to build the output code. Supported values are'
+ ' %s' % GENERATORS)
+ parser.add_option('-D', '--dart-overrides-dir', dest='dart_overrides_dir',
+ help='Adds custom dart from files in the given directory (Dart only).')
+
+ (opts, filenames) = parser.parse_args()
+
+ if not filenames:
+ sys.exit(0) # This is OK as a no-op
+
+ # Unless in bundle mode, only one file should be specified.
+ if opts.generator != 'cpp-bundle' and len(filenames) > 1:
+ # TODO(sashab): Could also just use filenames[0] here and not complain.
+ raise Exception(
+ "Unless in bundle mode, only one file can be specified at a time.")
+
+ result = GenerateSchema(opts.generator, filenames, opts.root, opts.destdir,
+ opts.namespace, opts.dart_overrides_dir)
+ if not opts.destdir:
+ print result
diff --git a/chromium/tools/json_schema_compiler/cpp_bundle_generator.py b/chromium/tools/json_schema_compiler/cpp_bundle_generator.py
new file mode 100644
index 00000000000..580bca85208
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/cpp_bundle_generator.py
@@ -0,0 +1,291 @@
+# 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 code
+import cpp_util
+from model import Platforms
+from schema_util import CapitalizeFirstLetter
+from schema_util import JsFunctionNameToClassName
+
+import json
+import os
+import re
+
+# TODO(miket/asargent) - parameterize this.
+SOURCE_BASE_PATH = 'chrome/common/extensions/api'
+
+def _RemoveDescriptions(node):
+ """Returns a copy of |schema| with "description" fields removed.
+ """
+ if isinstance(node, dict):
+ result = {}
+ for key, value in node.items():
+ # Some schemas actually have properties called "description", so only
+ # remove descriptions that have string values.
+ if key == 'description' and isinstance(value, basestring):
+ continue
+ result[key] = _RemoveDescriptions(value)
+ return result
+ if isinstance(node, list):
+ return [_RemoveDescriptions(v) for v in node]
+ return node
+
+class CppBundleGenerator(object):
+ """This class contains methods to generate code based on multiple schemas.
+ """
+
+ def __init__(self, root, model, api_defs, cpp_type_generator, cpp_namespace):
+ self._root = root;
+ self._model = model
+ self._api_defs = api_defs
+ self._cpp_type_generator = cpp_type_generator
+ self._cpp_namespace = cpp_namespace
+
+ self.api_cc_generator = _APICCGenerator(self)
+ self.api_h_generator = _APIHGenerator(self)
+ self.schemas_cc_generator = _SchemasCCGenerator(self)
+ self.schemas_h_generator = _SchemasHGenerator(self)
+
+ def _GenerateHeader(self, file_base, body_code):
+ """Generates a code.Code object for a header file
+
+ Parameters:
+ - |file_base| - the base of the filename, e.g. 'foo' (for 'foo.h')
+ - |body_code| - the code to put in between the multiple inclusion guards"""
+ c = code.Code()
+ c.Append(cpp_util.CHROMIUM_LICENSE)
+ c.Append()
+ c.Append(cpp_util.GENERATED_BUNDLE_FILE_MESSAGE % SOURCE_BASE_PATH)
+ ifndef_name = cpp_util.GenerateIfndefName(SOURCE_BASE_PATH, file_base)
+ c.Append()
+ c.Append('#ifndef %s' % ifndef_name)
+ c.Append('#define %s' % ifndef_name)
+ c.Append()
+ c.Concat(body_code)
+ c.Append()
+ c.Append('#endif // %s' % ifndef_name)
+ c.Append()
+ return c
+
+ def _GetPlatformIfdefs(self, model_object):
+ """Generates the "defined" conditional for an #if check if |model_object|
+ has platform restrictions. Returns None if there are no restrictions.
+ """
+ if model_object.platforms is None:
+ return None
+ ifdefs = []
+ for platform in model_object.platforms:
+ if platform == Platforms.CHROMEOS:
+ ifdefs.append('defined(OS_CHROMEOS)')
+ else:
+ raise ValueError("Unsupported platform ifdef: %s" % platform.name)
+ return ' and '.join(ifdefs)
+
+ def _GenerateRegisterFunctions(self, namespace_name, function):
+ c = code.Code()
+ function_ifdefs = self._GetPlatformIfdefs(function)
+ if function_ifdefs is not None:
+ c.Append("#if %s" % function_ifdefs, indent_level=0)
+
+ function_name = JsFunctionNameToClassName(namespace_name, function.name)
+ c.Append("registry->RegisterFunction<%sFunction>();" % (
+ function_name))
+
+ if function_ifdefs is not None:
+ c.Append("#endif // %s" % function_ifdefs, indent_level=0)
+ return c
+
+ def _GenerateFunctionRegistryRegisterAll(self):
+ c = code.Code()
+ c.Append('// static')
+ c.Sblock('void GeneratedFunctionRegistry::RegisterAll('
+ 'ExtensionFunctionRegistry* registry) {')
+ for namespace in self._model.namespaces.values():
+ namespace_ifdefs = self._GetPlatformIfdefs(namespace)
+ if namespace_ifdefs is not None:
+ c.Append("#if %s" % namespace_ifdefs, indent_level=0)
+
+ namespace_name = CapitalizeFirstLetter(namespace.name.replace(
+ "experimental.", ""))
+ for function in namespace.functions.values():
+ if function.nocompile:
+ continue
+ c.Concat(self._GenerateRegisterFunctions(namespace.name, function))
+
+ for type_ in namespace.types.values():
+ for function in type_.functions.values():
+ if function.nocompile:
+ continue
+ namespace_types_name = JsFunctionNameToClassName(
+ namespace.name, type_.name)
+ c.Concat(self._GenerateRegisterFunctions(namespace_types_name,
+ function))
+
+ if namespace_ifdefs is not None:
+ c.Append("#endif // %s" % namespace_ifdefs, indent_level=0)
+ c.Eblock("}")
+ return c
+
+class _APIHGenerator(object):
+ """Generates the header for API registration / declaration"""
+ def __init__(self, cpp_bundle):
+ self._bundle = cpp_bundle
+
+ def Generate(self, namespace):
+ c = code.Code()
+
+ c.Append('#include <string>')
+ c.Append()
+ c.Append('#include "base/basictypes.h"')
+ c.Append()
+ c.Append("class ExtensionFunctionRegistry;")
+ c.Append()
+ c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace))
+ c.Append()
+ c.Append('class GeneratedFunctionRegistry {')
+ c.Sblock(' public:')
+ c.Append('static void RegisterAll('
+ 'ExtensionFunctionRegistry* registry);')
+ c.Eblock('};');
+ c.Append()
+ c.Concat(cpp_util.CloseNamespace(self._bundle._cpp_namespace))
+ return self._bundle._GenerateHeader('generated_api', c)
+
+class _APICCGenerator(object):
+ """Generates a code.Code object for the generated API .cc file"""
+
+ def __init__(self, cpp_bundle):
+ self._bundle = cpp_bundle
+
+ def Generate(self, namespace):
+ c = code.Code()
+ c.Append(cpp_util.CHROMIUM_LICENSE)
+ c.Append()
+ c.Append('#include "%s"' % (os.path.join(SOURCE_BASE_PATH,
+ 'generated_api.h')))
+ c.Append()
+ for namespace in self._bundle._model.namespaces.values():
+ namespace_name = namespace.unix_name.replace("experimental_", "")
+ implementation_header = namespace.compiler_options.get(
+ "implemented_in",
+ "chrome/browser/extensions/api/%s/%s_api.h" % (namespace_name,
+ namespace_name))
+ if not os.path.exists(
+ os.path.join(self._bundle._root,
+ os.path.normpath(implementation_header))):
+ if "implemented_in" in namespace.compiler_options:
+ raise ValueError('Header file for namespace "%s" specified in '
+ 'compiler_options not found: %s' %
+ (namespace.unix_name, implementation_header))
+ continue
+ ifdefs = self._bundle._GetPlatformIfdefs(namespace)
+ if ifdefs is not None:
+ c.Append("#if %s" % ifdefs, indent_level=0)
+
+ c.Append('#include "%s"' % implementation_header)
+
+ if ifdefs is not None:
+ c.Append("#endif // %s" % ifdefs, indent_level=0)
+ c.Append()
+ c.Append('#include '
+ '"chrome/browser/extensions/extension_function_registry.h"')
+ c.Append()
+ c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace))
+ c.Append()
+ c.Concat(self._bundle._GenerateFunctionRegistryRegisterAll())
+ c.Append()
+ c.Concat(cpp_util.CloseNamespace(self._bundle._cpp_namespace))
+ c.Append()
+ return c
+
+class _SchemasHGenerator(object):
+ """Generates a code.Code object for the generated schemas .h file"""
+ def __init__(self, cpp_bundle):
+ self._bundle = cpp_bundle
+
+ def Generate(self, namespace):
+ c = code.Code()
+ c.Append('#include <map>')
+ c.Append('#include <string>')
+ c.Append();
+ c.Append('#include "base/strings/string_piece.h"')
+ c.Append()
+ c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace))
+ c.Append()
+ c.Append('class GeneratedSchemas {')
+ c.Sblock(' public:')
+ c.Append('// Determines if schema named |name| is generated.')
+ c.Append('static bool IsGenerated(std::string name);')
+ c.Append()
+ c.Append('// Gets the API schema named |name|.')
+ c.Append('static base::StringPiece Get(const std::string& name);')
+ c.Eblock('};');
+ c.Append()
+ c.Concat(cpp_util.CloseNamespace(self._bundle._cpp_namespace))
+ return self._bundle._GenerateHeader('generated_schemas', c)
+
+def _FormatNameAsConstant(name):
+ """Formats a name to be a C++ constant of the form kConstantName"""
+ name = '%s%s' % (name[0].upper(), name[1:])
+ return 'k%s' % re.sub('_[a-z]',
+ lambda m: m.group(0)[1].upper(),
+ name.replace('.', '_'))
+
+class _SchemasCCGenerator(object):
+ """Generates a code.Code object for the generated schemas .cc file"""
+
+ def __init__(self, cpp_bundle):
+ self._bundle = cpp_bundle
+
+ def Generate(self, namespace):
+ c = code.Code()
+ c.Append(cpp_util.CHROMIUM_LICENSE)
+ c.Append()
+ c.Append('#include "%s"' % (os.path.join(SOURCE_BASE_PATH,
+ 'generated_schemas.h')))
+ c.Append()
+ c.Append('#include "base/lazy_instance.h"')
+ c.Append()
+ c.Append('namespace {')
+ for api in self._bundle._api_defs:
+ namespace = self._bundle._model.namespaces[api.get('namespace')]
+ # JSON parsing code expects lists of schemas, so dump a singleton list.
+ json_content = json.dumps([_RemoveDescriptions(api)],
+ separators=(',', ':'))
+ # Escape all double-quotes and backslashes. For this to output a valid
+ # JSON C string, we need to escape \ and ".
+ json_content = json_content.replace('\\', '\\\\').replace('"', '\\"')
+ c.Append('const char %s[] = "%s";' %
+ (_FormatNameAsConstant(namespace.name), json_content))
+ c.Append('}')
+ c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace))
+ c.Append()
+ c.Sblock('struct Static {')
+ c.Sblock('Static() {')
+ for api in self._bundle._api_defs:
+ namespace = self._bundle._model.namespaces[api.get('namespace')]
+ c.Append('schemas["%s"] = %s;' % (namespace.name,
+ _FormatNameAsConstant(namespace.name)))
+ c.Eblock('}');
+ c.Append()
+ c.Append('std::map<std::string, const char*> schemas;')
+ c.Eblock('};');
+ c.Append()
+ c.Append('base::LazyInstance<Static> g_lazy_instance;')
+ c.Append()
+ c.Append('// static')
+ c.Sblock('base::StringPiece GeneratedSchemas::Get('
+ 'const std::string& name) {')
+ c.Append('return IsGenerated(name) ? '
+ 'g_lazy_instance.Get().schemas[name] : "";')
+ c.Eblock('}')
+ c.Append()
+ c.Append('// static')
+ c.Sblock('bool GeneratedSchemas::IsGenerated(std::string name) {')
+ c.Append('return g_lazy_instance.Get().schemas.count(name) > 0;')
+ c.Eblock('}')
+ c.Append()
+ c.Concat(cpp_util.CloseNamespace(self._bundle._cpp_namespace))
+ c.Append()
+ return c
diff --git a/chromium/tools/json_schema_compiler/cpp_generator.py b/chromium/tools/json_schema_compiler/cpp_generator.py
new file mode 100644
index 00000000000..6dc05137205
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/cpp_generator.py
@@ -0,0 +1,11 @@
+# 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.
+
+from cc_generator import CCGenerator
+from h_generator import HGenerator
+
+class CppGenerator(object):
+ def __init__(self, type_generator, cpp_namespace):
+ self.h_generator = HGenerator(type_generator, cpp_namespace)
+ self.cc_generator = CCGenerator(type_generator, cpp_namespace)
diff --git a/chromium/tools/json_schema_compiler/cpp_type_generator.py b/chromium/tools/json_schema_compiler/cpp_type_generator.py
new file mode 100644
index 00000000000..efb712ee728
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/cpp_type_generator.py
@@ -0,0 +1,276 @@
+# 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.
+
+from code import Code
+from model import Namespace, PropertyType, Type
+import cpp_util
+from json_parse import OrderedDict
+from operator import attrgetter
+import schema_util
+
+class _TypeDependency(object):
+ """Contains information about a dependency a namespace has on a type: the
+ type's model, and whether that dependency is "hard" meaning that it cannot be
+ forward declared.
+ """
+ def __init__(self, type_, hard=False):
+ self.type_ = type_
+ self.hard = hard
+
+ def GetSortKey(self):
+ return '%s.%s' % (self.type_.namespace.name, self.type_.name)
+
+class CppTypeGenerator(object):
+ """Manages the types of properties and provides utilities for getting the
+ C++ type out of a model.Property
+ """
+ def __init__(self, model, schema_loader, default_namespace=None):
+ """Creates a cpp_type_generator. The given root_namespace should be of the
+ format extensions::api::sub. The generator will generate code suitable for
+ use in the given model's namespace.
+ """
+ self._default_namespace = default_namespace
+ if self._default_namespace is None:
+ self._default_namespace = model.namespaces.values()[0]
+ self._schema_loader = schema_loader
+
+ def GetCppNamespaceName(self, namespace):
+ """Gets the mapped C++ namespace name for the given namespace relative to
+ the root namespace.
+ """
+ return namespace.unix_name
+
+ def GetNamespaceStart(self):
+ """Get opening self._default_namespace namespace declaration.
+ """
+ return Code().Append('namespace %s {' %
+ self.GetCppNamespaceName(self._default_namespace))
+
+ def GetNamespaceEnd(self):
+ """Get closing self._default_namespace namespace declaration.
+ """
+ return Code().Append('} // %s' %
+ self.GetCppNamespaceName(self._default_namespace))
+
+ def GetEnumNoneValue(self, type_):
+ """Gets the enum value in the given model.Property indicating no value has
+ been set.
+ """
+ return '%s_NONE' % self.FollowRef(type_).unix_name.upper()
+
+ def GetEnumValue(self, type_, enum_value):
+ """Gets the enum value of the given model.Property of the given type.
+
+ e.g VAR_STRING
+ """
+ value = '%s_%s' % (self.FollowRef(type_).unix_name.upper(),
+ cpp_util.Classname(enum_value.upper()))
+ # To avoid collisions with built-in OS_* preprocessor definitions, we add a
+ # trailing slash to enum names that start with OS_.
+ if value.startswith("OS_"):
+ value += "_"
+ return value
+
+ def GetCppType(self, type_, is_ptr=False, is_in_container=False):
+ """Translates a model.Property or model.Type into its C++ type.
+
+ If REF types from different namespaces are referenced, will resolve
+ using self._schema_loader.
+
+ Use |is_ptr| if the type is optional. This will wrap the type in a
+ scoped_ptr if possible (it is not possible to wrap an enum).
+
+ Use |is_in_container| if the type is appearing in a collection, e.g. a
+ std::vector or std::map. This will wrap it in the correct type with spacing.
+ """
+ cpp_type = None
+ if type_.property_type == PropertyType.REF:
+ ref_type = self._FindType(type_.ref_type)
+ if ref_type is None:
+ raise KeyError('Cannot find referenced type: %s' % type_.ref_type)
+ if self._default_namespace is ref_type.namespace:
+ cpp_type = ref_type.name
+ else:
+ cpp_type = '%s::%s' % (ref_type.namespace.unix_name, ref_type.name)
+ elif type_.property_type == PropertyType.BOOLEAN:
+ cpp_type = 'bool'
+ elif type_.property_type == PropertyType.INTEGER:
+ cpp_type = 'int'
+ elif type_.property_type == PropertyType.INT64:
+ cpp_type = 'int64'
+ elif type_.property_type == PropertyType.DOUBLE:
+ cpp_type = 'double'
+ elif type_.property_type == PropertyType.STRING:
+ cpp_type = 'std::string'
+ elif type_.property_type == PropertyType.ENUM:
+ cpp_type = cpp_util.Classname(type_.name)
+ elif type_.property_type == PropertyType.ANY:
+ cpp_type = 'base::Value'
+ elif (type_.property_type == PropertyType.OBJECT or
+ type_.property_type == PropertyType.CHOICES):
+ cpp_type = cpp_util.Classname(type_.name)
+ elif type_.property_type == PropertyType.FUNCTION:
+ # Functions come into the json schema compiler as empty objects. We can
+ # record these as empty DictionaryValues so that we know if the function
+ # was passed in or not.
+ cpp_type = 'base::DictionaryValue'
+ elif type_.property_type == PropertyType.ARRAY:
+ item_cpp_type = self.GetCppType(type_.item_type, is_in_container=True)
+ cpp_type = 'std::vector<%s>' % cpp_util.PadForGenerics(item_cpp_type)
+ elif type_.property_type == PropertyType.BINARY:
+ cpp_type = 'std::string'
+ else:
+ raise NotImplementedError('Cannot get type of %s' % type_.property_type)
+
+ # HACK: optional ENUM is represented elsewhere with a _NONE value, so it
+ # never needs to be wrapped in pointer shenanigans.
+ # TODO(kalman): change this - but it's an exceedingly far-reaching change.
+ if not self.FollowRef(type_).property_type == PropertyType.ENUM:
+ if is_in_container and (is_ptr or not self.IsCopyable(type_)):
+ cpp_type = 'linked_ptr<%s>' % cpp_util.PadForGenerics(cpp_type)
+ elif is_ptr:
+ cpp_type = 'scoped_ptr<%s>' % cpp_util.PadForGenerics(cpp_type)
+
+ return cpp_type
+
+ def IsCopyable(self, type_):
+ return not (self.FollowRef(type_).property_type in (PropertyType.ANY,
+ PropertyType.ARRAY,
+ PropertyType.OBJECT,
+ PropertyType.CHOICES))
+
+ def GenerateForwardDeclarations(self):
+ """Returns the forward declarations for self._default_namespace.
+ """
+ c = Code()
+
+ for namespace, dependencies in self._NamespaceTypeDependencies().items():
+ c.Append('namespace %s {' % namespace.unix_name)
+ for dependency in dependencies:
+ # No point forward-declaring hard dependencies.
+ if dependency.hard:
+ continue
+ # Add more ways to forward declare things as necessary.
+ if dependency.type_.property_type in (PropertyType.CHOICES,
+ PropertyType.OBJECT):
+ c.Append('struct %s;' % dependency.type_.name)
+ c.Append('}')
+
+ return c
+
+ def GenerateIncludes(self, include_soft=False):
+ """Returns the #include lines for self._default_namespace.
+ """
+ c = Code()
+ for namespace, dependencies in self._NamespaceTypeDependencies().items():
+ for dependency in dependencies:
+ if dependency.hard or include_soft:
+ c.Append('#include "%s/%s.h"' % (namespace.source_file_dir,
+ namespace.unix_name))
+ return c
+
+ def _FindType(self, full_name):
+ """Finds the model.Type with name |qualified_name|. If it's not from
+ |self._default_namespace| then it needs to be qualified.
+ """
+ namespace = self._schema_loader.ResolveType(full_name,
+ self._default_namespace)
+ if namespace is None:
+ raise KeyError('Cannot resolve type %s. Maybe it needs a prefix '
+ 'if it comes from another namespace?' % full_name)
+ return namespace.types[schema_util.StripNamespace(full_name)]
+
+ def FollowRef(self, type_):
+ """Follows $ref link of types to resolve the concrete type a ref refers to.
+
+ If the property passed in is not of type PropertyType.REF, it will be
+ returned unchanged.
+ """
+ if type_.property_type != PropertyType.REF:
+ return type_
+ return self.FollowRef(self._FindType(type_.ref_type))
+
+ def _NamespaceTypeDependencies(self):
+ """Returns a dict ordered by namespace name containing a mapping of
+ model.Namespace to every _TypeDependency for |self._default_namespace|,
+ sorted by the type's name.
+ """
+ dependencies = set()
+ for function in self._default_namespace.functions.values():
+ for param in function.params:
+ dependencies |= self._TypeDependencies(param.type_,
+ hard=not param.optional)
+ if function.callback:
+ for param in function.callback.params:
+ dependencies |= self._TypeDependencies(param.type_,
+ hard=not param.optional)
+ for type_ in self._default_namespace.types.values():
+ for prop in type_.properties.values():
+ dependencies |= self._TypeDependencies(prop.type_,
+ hard=not prop.optional)
+ for event in self._default_namespace.events.values():
+ for param in event.params:
+ dependencies |= self._TypeDependencies(param.type_,
+ hard=not param.optional)
+
+ # Make sure that the dependencies are returned in alphabetical order.
+ dependency_namespaces = OrderedDict()
+ for dependency in sorted(dependencies, key=_TypeDependency.GetSortKey):
+ namespace = dependency.type_.namespace
+ if namespace is self._default_namespace:
+ continue
+ if namespace not in dependency_namespaces:
+ dependency_namespaces[namespace] = []
+ dependency_namespaces[namespace].append(dependency)
+
+ return dependency_namespaces
+
+ def _TypeDependencies(self, type_, hard=False):
+ """Gets all the type dependencies of a property.
+ """
+ deps = set()
+ if type_.property_type == PropertyType.REF:
+ deps.add(_TypeDependency(self._FindType(type_.ref_type), hard=hard))
+ elif type_.property_type == PropertyType.ARRAY:
+ # Non-copyable types are not hard because they are wrapped in linked_ptrs
+ # when generated. Otherwise they're typedefs, so they're hard (though we
+ # could generate those typedefs in every dependent namespace, but that
+ # seems weird).
+ deps = self._TypeDependencies(type_.item_type,
+ hard=self.IsCopyable(type_.item_type))
+ elif type_.property_type == PropertyType.CHOICES:
+ for type_ in type_.choices:
+ deps |= self._TypeDependencies(type_, hard=self.IsCopyable(type_))
+ elif type_.property_type == PropertyType.OBJECT:
+ for p in type_.properties.values():
+ deps |= self._TypeDependencies(p.type_, hard=not p.optional)
+ return deps
+
+ def GeneratePropertyValues(self, property, line, nodoc=False):
+ """Generates the Code to display all value-containing properties.
+ """
+ c = Code()
+ if not nodoc:
+ c.Comment(property.description)
+
+ if property.value is not None:
+ c.Append(line % {
+ "type": self.GetCppType(property.type_),
+ "name": property.name,
+ "value": property.value
+ })
+ else:
+ has_child_code = False
+ c.Sblock('namespace %s {' % property.name)
+ for child_property in property.type_.properties.values():
+ child_code = self.GeneratePropertyValues(child_property,
+ line,
+ nodoc=nodoc)
+ if child_code:
+ has_child_code = True
+ c.Concat(child_code)
+ c.Eblock('} // namespace %s' % property.name)
+ if not has_child_code:
+ c = None
+ return c
diff --git a/chromium/tools/json_schema_compiler/cpp_type_generator_test.py b/chromium/tools/json_schema_compiler/cpp_type_generator_test.py
new file mode 100755
index 00000000000..7782b234a4c
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/cpp_type_generator_test.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python
+# 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.
+
+from cpp_type_generator import CppTypeGenerator
+from json_schema import CachedLoad
+import model
+import unittest
+
+from collections import defaultdict
+
+class _FakeSchemaLoader(object):
+ def __init__(self, model):
+ self._model = model
+
+ def ResolveType(self, type_name, default):
+ parts = type_name.rsplit('.', 1)
+ if len(parts) == 1:
+ return default if type_name in default.types else None
+ return self._model.namespaces[parts[0]]
+
+class CppTypeGeneratorTest(unittest.TestCase):
+ def setUp(self):
+ self.models = defaultdict(model.Model)
+
+ self.forbidden_json = CachedLoad('test/forbidden.json')
+ self.forbidden = self.models['forbidden'].AddNamespace(
+ self.forbidden_json[0], 'path/to/forbidden.json')
+ self.permissions_json = CachedLoad('test/permissions.json')
+ self.permissions = self.models['permissions'].AddNamespace(
+ self.permissions_json[0], 'path/to/permissions.json')
+ self.windows_json = CachedLoad('test/windows.json')
+ self.windows = self.models['windows'].AddNamespace(self.windows_json[0],
+ 'path/to/window.json')
+ self.tabs_json = CachedLoad('test/tabs.json')
+ self.tabs = self.models['tabs'].AddNamespace(self.tabs_json[0],
+ 'path/to/tabs.json')
+ self.browser_action_json = CachedLoad('test/browser_action.json')
+ self.browser_action = self.models['browser_action'].AddNamespace(
+ self.browser_action_json[0], 'path/to/browser_action.json')
+ self.font_settings_json = CachedLoad('test/font_settings.json')
+ self.font_settings = self.models['font_settings'].AddNamespace(
+ self.font_settings_json[0], 'path/to/font_settings.json')
+ self.dependency_tester_json = CachedLoad('test/dependency_tester.json')
+ self.dependency_tester = self.models['dependency_tester'].AddNamespace(
+ self.dependency_tester_json[0], 'path/to/dependency_tester.json')
+ self.content_settings_json = CachedLoad('test/content_settings.json')
+ self.content_settings = self.models['content_settings'].AddNamespace(
+ self.content_settings_json[0], 'path/to/content_settings.json')
+
+ def testGenerateIncludesAndForwardDeclarations(self):
+ m = model.Model()
+ m.AddNamespace(self.windows_json[0], 'path/to/windows.json')
+ m.AddNamespace(self.tabs_json[0], 'path/to/tabs.json')
+ manager = CppTypeGenerator(m, _FakeSchemaLoader(m))
+
+ self.assertEquals('', manager.GenerateIncludes().Render())
+ self.assertEquals('#include "path/to/tabs.h"',
+ manager.GenerateIncludes(include_soft=True).Render())
+ self.assertEquals('namespace tabs {\n'
+ 'struct Tab;\n'
+ '}',
+ manager.GenerateForwardDeclarations().Render())
+ manager = CppTypeGenerator(self.models.get('permissions'),
+ _FakeSchemaLoader(m))
+ self.assertEquals('', manager.GenerateIncludes().Render())
+ self.assertEquals('', manager.GenerateIncludes().Render())
+ self.assertEquals('', manager.GenerateForwardDeclarations().Render())
+ manager = CppTypeGenerator(self.models.get('content_settings'),
+ _FakeSchemaLoader(m))
+ self.assertEquals('', manager.GenerateIncludes().Render())
+
+ def testGenerateIncludesAndForwardDeclarationsDependencies(self):
+ m = model.Model()
+ # Insert 'font_settings' before 'browser_action' in order to test that
+ # CppTypeGenerator sorts them properly.
+ m.AddNamespace(self.font_settings_json[0], 'path/to/font_settings.json')
+ m.AddNamespace(self.browser_action_json[0], 'path/to/browser_action.json')
+ dependency_tester = m.AddNamespace(self.dependency_tester_json[0],
+ 'path/to/dependency_tester.json')
+ manager = CppTypeGenerator(m,
+ _FakeSchemaLoader(m),
+ default_namespace=dependency_tester)
+ self.assertEquals('#include "path/to/browser_action.h"\n'
+ '#include "path/to/font_settings.h"',
+ manager.GenerateIncludes().Render())
+ self.assertEquals('namespace browser_action {\n'
+ '}\n'
+ 'namespace font_settings {\n'
+ '}',
+ manager.GenerateForwardDeclarations().Render())
+
+ def testGetCppTypeSimple(self):
+ manager = CppTypeGenerator(self.models.get('tabs'), _FakeSchemaLoader(None))
+ self.assertEquals(
+ 'int',
+ manager.GetCppType(self.tabs.types['Tab'].properties['id'].type_))
+ self.assertEquals(
+ 'std::string',
+ manager.GetCppType(self.tabs.types['Tab'].properties['status'].type_))
+ self.assertEquals(
+ 'bool',
+ manager.GetCppType(self.tabs.types['Tab'].properties['selected'].type_))
+
+ def testStringAsType(self):
+ manager = CppTypeGenerator(self.models.get('font_settings'),
+ _FakeSchemaLoader(None))
+ self.assertEquals(
+ 'std::string',
+ manager.GetCppType(self.font_settings.types['FakeStringType']))
+
+ def testArrayAsType(self):
+ manager = CppTypeGenerator(self.models.get('browser_action'),
+ _FakeSchemaLoader(None))
+ self.assertEquals(
+ 'std::vector<int>',
+ manager.GetCppType(self.browser_action.types['ColorArray']))
+
+ def testGetCppTypeArray(self):
+ manager = CppTypeGenerator(self.models.get('windows'),
+ _FakeSchemaLoader(None))
+ self.assertEquals(
+ 'std::vector<linked_ptr<Window> >',
+ manager.GetCppType(
+ self.windows.functions['getAll'].callback.params[0].type_))
+ manager = CppTypeGenerator(self.models.get('permissions'),
+ _FakeSchemaLoader(None))
+ self.assertEquals(
+ 'std::vector<std::string>',
+ manager.GetCppType(
+ self.permissions.types['Permissions'].properties['origins'].type_))
+
+ def testGetCppTypeLocalRef(self):
+ manager = CppTypeGenerator(self.models.get('tabs'), _FakeSchemaLoader(None))
+ self.assertEquals(
+ 'Tab',
+ manager.GetCppType(self.tabs.functions['get'].callback.params[0].type_))
+
+ def testGetCppTypeIncludedRef(self):
+ m = model.Model()
+ m.AddNamespace(self.windows_json[0], 'path/to/windows.json')
+ m.AddNamespace(self.tabs_json[0], 'path/to/tabs.json')
+ manager = CppTypeGenerator(m, _FakeSchemaLoader(m))
+ self.assertEquals(
+ 'std::vector<linked_ptr<tabs::Tab> >',
+ manager.GetCppType(
+ self.windows.types['Window'].properties['tabs'].type_))
+
+ def testGetCppTypeWithPadForGeneric(self):
+ manager = CppTypeGenerator(self.models.get('permissions'),
+ _FakeSchemaLoader(None))
+ self.assertEquals('std::vector<std::string>',
+ manager.GetCppType(
+ self.permissions.types['Permissions'].properties['origins'].type_,
+ is_in_container=False))
+ self.assertEquals('linked_ptr<std::vector<std::string> >',
+ manager.GetCppType(
+ self.permissions.types['Permissions'].properties['origins'].type_,
+ is_in_container=True))
+ self.assertEquals('bool',
+ manager.GetCppType(
+ self.permissions.functions['contains'].callback.params[0].type_,
+ is_in_container=True))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/tools/json_schema_compiler/cpp_util.py b/chromium/tools/json_schema_compiler/cpp_util.py
new file mode 100644
index 00000000000..2de42293fba
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/cpp_util.py
@@ -0,0 +1,113 @@
+# 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.
+
+"""Utilies and constants specific to Chromium C++ code.
+"""
+
+from code import Code
+from datetime import datetime
+from model import Property, PropertyType, Type
+import os
+import re
+
+CHROMIUM_LICENSE = (
+"""// Copyright (c) %d 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.""" % datetime.now().year
+)
+GENERATED_FILE_MESSAGE = """// GENERATED FROM THE API DEFINITION IN
+// %s
+// DO NOT EDIT.
+"""
+GENERATED_BUNDLE_FILE_MESSAGE = """// GENERATED FROM THE API DEFINITIONS IN
+// %s
+// DO NOT EDIT.
+"""
+
+def Classname(s):
+ """Translates a namespace name or function name into something more
+ suited to C++.
+
+ eg experimental.downloads -> Experimental_Downloads
+ updateAll -> UpdateAll.
+ """
+ return '_'.join([x[0].upper() + x[1:] for x in re.split('\W', s)])
+
+def GetAsFundamentalValue(type_, src, dst):
+ """Returns the C++ code for retrieving a fundamental type from a
+ Value into a variable.
+
+ src: Value*
+ dst: Property*
+ """
+ return {
+ PropertyType.BOOLEAN: '%s->GetAsBoolean(%s)',
+ PropertyType.DOUBLE: '%s->GetAsDouble(%s)',
+ PropertyType.INTEGER: '%s->GetAsInteger(%s)',
+ PropertyType.STRING: '%s->GetAsString(%s)',
+ }[type_.property_type] % (src, dst)
+
+def GetValueType(type_):
+ """Returns the Value::Type corresponding to the model.Type.
+ """
+ return {
+ PropertyType.ARRAY: 'base::Value::TYPE_LIST',
+ PropertyType.BINARY: 'base::Value::TYPE_BINARY',
+ PropertyType.BOOLEAN: 'base::Value::TYPE_BOOLEAN',
+ # PropertyType.CHOICES can be any combination of types.
+ PropertyType.DOUBLE: 'base::Value::TYPE_DOUBLE',
+ PropertyType.ENUM: 'base::Value::TYPE_STRING',
+ PropertyType.FUNCTION: 'base::Value::TYPE_DICTIONARY',
+ PropertyType.INTEGER: 'base::Value::TYPE_INTEGER',
+ PropertyType.OBJECT: 'base::Value::TYPE_DICTIONARY',
+ PropertyType.STRING: 'base::Value::TYPE_STRING',
+ }[type_.property_type]
+
+def GetParameterDeclaration(param, type_):
+ """Gets a parameter declaration of a given model.Property and its C++
+ type.
+ """
+ if param.type_.property_type in (PropertyType.ANY,
+ PropertyType.ARRAY,
+ PropertyType.CHOICES,
+ PropertyType.OBJECT,
+ PropertyType.REF,
+ PropertyType.STRING):
+ arg = 'const %(type)s& %(name)s'
+ else:
+ arg = '%(type)s %(name)s'
+ return arg % {
+ 'type': type_,
+ 'name': param.unix_name,
+ }
+
+def GenerateIfndefName(path, filename):
+ """Formats a path and filename as a #define name.
+
+ e.g chrome/extensions/gen, file.h becomes CHROME_EXTENSIONS_GEN_FILE_H__.
+ """
+ return (('%s_%s_H__' % (path, filename))
+ .upper().replace(os.sep, '_').replace('/', '_'))
+
+def PadForGenerics(var):
+ """Appends a space to |var| if it ends with a >, so that it can be compiled
+ within generic types.
+ """
+ return ('%s ' % var) if var.endswith('>') else var
+
+def OpenNamespace(namespace):
+ """Get opening root namespace declarations.
+ """
+ c = Code()
+ for component in namespace.split('::'):
+ c.Append('namespace %s {' % component)
+ return c
+
+def CloseNamespace(namespace):
+ """Get closing root namespace declarations.
+ """
+ c = Code()
+ for component in reversed(namespace.split('::')):
+ c.Append('} // namespace %s' % component)
+ return c
diff --git a/chromium/tools/json_schema_compiler/cpp_util_test.py b/chromium/tools/json_schema_compiler/cpp_util_test.py
new file mode 100755
index 00000000000..ede309587f3
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/cpp_util_test.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# 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 cpp_util
+import unittest
+
+class CppUtilTest(unittest.TestCase):
+ def testClassname(self):
+ self.assertEquals('Permissions', cpp_util.Classname('permissions'))
+ self.assertEquals('UpdateAllTheThings',
+ cpp_util.Classname('updateAllTheThings'))
+ self.assertEquals('Aa_Bb_Cc', cpp_util.Classname('aa.bb.cc'))
+
+ def testNamespaceDeclaration(self):
+ self.assertEquals('namespace extensions {',
+ cpp_util.OpenNamespace('extensions').Render())
+ self.assertEquals('} // namespace extensions',
+ cpp_util.CloseNamespace('extensions').Render())
+ self.assertEquals('namespace extensions {\n'
+ 'namespace gen {\n'
+ 'namespace api {',
+ cpp_util.OpenNamespace('extensions::gen::api').Render())
+ self.assertEquals('} // namespace api\n'
+ '} // namespace gen\n'
+ '} // namespace extensions',
+ cpp_util.CloseNamespace('extensions::gen::api').Render())
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/tools/json_schema_compiler/dart_generator.py b/chromium/tools/json_schema_compiler/dart_generator.py
new file mode 100644
index 00000000000..3a4c2e33c10
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_generator.py
@@ -0,0 +1,762 @@
+# 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.
+"""
+Generator language component for compiler.py that adds Dart language support.
+"""
+
+from code import Code
+from model import *
+from schema_util import *
+
+import os
+from datetime import datetime
+
+LICENSE = (
+"""// Copyright (c) %s, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.""" %
+ datetime.now().year)
+
+class DartGenerator(object):
+ def __init__(self, dart_overrides_dir=None):
+ self._dart_overrides_dir = dart_overrides_dir
+
+ def Generate(self, namespace):
+ return _Generator(namespace, self._dart_overrides_dir).Generate()
+
+class _Generator(object):
+ """A .dart generator for a namespace.
+ """
+
+ def __init__(self, namespace, dart_overrides_dir=None):
+ self._namespace = namespace
+ # TODO(sashab): Once inline type definitions start being added to
+ # self._types, make a _FindType(self, type_) function that looks at
+ # self._namespace.types.
+ self._types = namespace.types
+
+ # Build a dictionary of Type Name --> Custom Dart code.
+ self._type_overrides = {}
+ if dart_overrides_dir is not None:
+ for filename in os.listdir(dart_overrides_dir):
+ if filename.startswith(namespace.unix_name):
+ with open(os.path.join(dart_overrides_dir, filename)) as f:
+ # Split off the namespace and file extension, leaving just the type.
+ type_path = '.'.join(filename.split('.')[1:-1])
+ self._type_overrides[type_path] = f.read()
+
+ # TODO(sashab): Add all inline type definitions to the global Types
+ # dictionary here, so they have proper names, and are implemented along with
+ # all other types. Also update the parameters/members with these types
+ # to reference these new types instead.
+
+ def Generate(self):
+ """Generates a Code object with the .dart for the entire namespace.
+ """
+ c = Code()
+ (c.Append(LICENSE)
+ .Append()
+ .Append('// Generated from namespace: %s' % self._namespace.name)
+ .Append()
+ .Append('part of chrome;'))
+
+ if self._types:
+ (c.Append()
+ .Append('/**')
+ .Append(' * Types')
+ .Append(' */')
+ .Append()
+ )
+ for type_ in self._types.values():
+ # Check for custom dart for this whole type.
+ override = self._GetOverride([type_.name], document_with=type_)
+ c.Cblock(override if override is not None else self._GenerateType(type_))
+
+ if self._namespace.events:
+ (c.Append('/**')
+ .Append(' * Events')
+ .Append(' */')
+ .Append()
+ )
+ for event_name in self._namespace.events:
+ c.Cblock(self._GenerateEvent(self._namespace.events[event_name]))
+
+ (c.Append('/**')
+ .Append(' * Functions')
+ .Append(' */')
+ .Append()
+ )
+ c.Cblock(self._GenerateMainClass())
+
+ return c
+
+ def _GenerateType(self, type_):
+ """Given a Type object, returns the Code with the .dart for this
+ type's definition.
+
+ Assumes this type is a Parameter Type (creatable by user), and creates an
+ object that extends ChromeObject. All parameters are specifiable as named
+ arguments in the constructor, and all methods are wrapped with getters and
+ setters that hide the JS() implementation.
+ """
+ c = Code()
+
+ # Since enums are just treated as strings for now, don't generate their
+ # type.
+ # TODO(sashab): Find a nice way to wrap enum objects.
+ if type_.property_type is PropertyType.ENUM:
+ return c
+
+ (c.Concat(self._GenerateDocumentation(type_))
+ .Sblock('class %(type_name)s extends ChromeObject {')
+ )
+
+ # Check whether this type has function members. If it does, don't allow
+ # public construction.
+ add_public_constructor = all(not self._IsFunction(p.type_)
+ for p in type_.properties.values())
+ constructor_fields = [self._GeneratePropertySignature(p)
+ for p in type_.properties.values()]
+
+ if add_public_constructor:
+ (c.Append('/*')
+ .Append(' * Public constructor')
+ .Append(' */')
+ .Sblock('%(type_name)s({%(constructor_fields)s}) {')
+ )
+
+ for prop_name in type_.properties:
+ (c.Sblock('if (%s != null)' % prop_name)
+ .Append('this.%s = %s;' % (prop_name, prop_name))
+ .Eblock()
+ )
+ (c.Eblock('}')
+ .Append()
+ )
+
+ (c.Append('/*')
+ .Append(' * Private constructor')
+ .Append(' */')
+ .Append('%(type_name)s._proxy(_jsObject) : super._proxy(_jsObject);')
+ )
+
+ # Add an accessor (getter & setter) for each property.
+ properties = [p for p in type_.properties.values()
+ if not self._IsFunction(p.type_)]
+ if properties:
+ (c.Append()
+ .Append('/*')
+ .Append(' * Public accessors')
+ .Append(' */')
+ )
+ for prop in properties:
+ override = self._GetOverride([type_.name, prop.name], document_with=prop)
+ c.Concat(override if override is not None
+ else self._GenerateGetterAndSetter(type_, prop))
+
+ # Now add all the methods.
+ methods = [t for t in type_.properties.values()
+ if self._IsFunction(t.type_)]
+ if methods:
+ (c.Append()
+ .Append('/*')
+ .Append(' * Methods')
+ .Append(' */')
+ )
+ for prop in methods:
+ # Check if there's an override for this method.
+ override = self._GetOverride([type_.name, prop.name], document_with=prop)
+ c.Cblock(override if override is not None
+ else self._GenerateFunction(prop.type_.function))
+
+ (c.Eblock('}')
+ .Substitute({
+ 'type_name': self._AddPrefix(type_.simple_name),
+ 'constructor_fields': ', '.join(constructor_fields)
+ })
+ )
+
+ return c
+
+ def _GenerateGetterAndSetter(self, type_, prop):
+ """Given a Type and Property, returns the Code object for the getter and
+ setter for that property.
+ """
+ c = Code()
+ override = self._GetOverride([type_.name, prop.name, '.get'],
+ document_with=prop)
+ c.Cblock(override if override is not None
+ else self._GenerateGetter(type_, prop))
+ override = self._GetOverride([type_.name, prop.name, '.set'])
+ c.Cblock(override if override is not None
+ else self._GenerateSetter(type_, prop))
+ return c
+
+ def _GenerateGetter(self, type_, prop):
+ """Given a Type and Property, returns the Code object for the getter for
+ that property.
+
+ Also adds the documentation for this property before the method.
+ """
+ c = Code()
+ c.Concat(self._GenerateDocumentation(prop))
+
+ type_name = self._GetDartType(prop.type_)
+ if (self._IsBaseType(prop.type_)):
+ c.Append("%s get %s => JS('%s', '#.%s', this._jsObject);" %
+ (type_name, prop.name, type_name, prop.name))
+ elif self._IsSerializableObjectType(prop.type_):
+ c.Append("%s get %s => new %s._proxy(JS('', '#.%s', "
+ "this._jsObject));" %
+ (type_name, prop.name, type_name, prop.name))
+ elif self._IsListOfSerializableObjects(prop.type_):
+ (c.Sblock('%s get %s {' % (type_name, prop.name))
+ .Append('%s __proxy_%s = new %s();' % (type_name, prop.name,
+ type_name))
+ .Append("int count = JS('int', '#.%s.length', this._jsObject);" %
+ prop.name)
+ .Sblock("for (int i = 0; i < count; i++) {")
+ .Append("var item = JS('', '#.%s[#]', this._jsObject, i);" % prop.name)
+ .Append('__proxy_%s.add(new %s._proxy(item));' % (prop.name,
+ self._GetDartType(prop.type_.item_type)))
+ .Eblock('}')
+ .Append('return __proxy_%s;' % prop.name)
+ .Eblock('}')
+ )
+ elif self._IsObjectType(prop.type_):
+ # TODO(sashab): Think of a way to serialize generic Dart objects.
+ if type_name in self._types:
+ c.Append("%s get %s => new %s._proxy(JS('%s', '#.%s', "
+ "this._jsObject));" %
+ (type_name, prop.name, type_name, type_name, prop.name))
+ else:
+ c.Append("%s get %s => JS('%s', '#.%s', this._jsObject);" %
+ (type_name, prop.name, type_name, prop.name))
+ else:
+ raise Exception(
+ "Could not generate wrapper for %s.%s: unserializable type %s" %
+ (type_.name, prop.name, type_name)
+ )
+ return c
+
+ def _GenerateSetter(self, type_, prop):
+ """Given a Type and Property, returns the Code object for the setter for
+ that property.
+ """
+ c = Code()
+ type_name = self._GetDartType(prop.type_)
+ wrapped_name = prop.name
+ if not self._IsBaseType(prop.type_):
+ wrapped_name = 'convertArgument(%s)' % prop.name
+
+ (c.Sblock("void set %s(%s %s) {" % (prop.name, type_name, prop.name))
+ .Append("JS('void', '#.%s = #', this._jsObject, %s);" %
+ (prop.name, wrapped_name))
+ .Eblock("}")
+ )
+ return c
+
+ def _GenerateDocumentation(self, prop):
+ """Given an object, generates the documentation for this object (as a
+ code string) and returns the Code object.
+
+ Returns an empty code object if the object has no documentation.
+
+ Uses triple-quotes for the string.
+ """
+ c = Code()
+ if prop.description is not None:
+ for line in prop.description.split('\n'):
+ c.Comment(line, comment_prefix='/// ')
+ return c
+
+ def _GenerateFunction(self, f):
+ """Returns the Code object for the given function.
+ """
+ c = Code()
+ c.Concat(self._GenerateDocumentation(f))
+
+ if not self._NeedsProxiedCallback(f):
+ c.Append("%s => %s;" % (self._GenerateFunctionSignature(f),
+ self._GenerateProxyCall(f)))
+ return c
+
+ (c.Sblock("%s {" % self._GenerateFunctionSignature(f))
+ .Concat(self._GenerateProxiedFunction(f.callback, f.callback.name))
+ .Append('%s;' % self._GenerateProxyCall(f))
+ .Eblock('}')
+ )
+
+ return c
+
+ def _GenerateProxiedFunction(self, f, callback_name):
+ """Given a function (assumed to be a callback), generates the proxied
+ version of this function, which calls |callback_name| if it is defined.
+
+ Returns a Code object.
+ """
+ c = Code()
+ proxied_params = []
+ # A list of Properties, containing List<*> objects that need proxying for
+ # their members (by copying out each member and proxying it).
+ lists_to_proxy = []
+ for p in f.params:
+ if self._IsBaseType(p.type_):
+ proxied_params.append(p.name)
+ elif self._IsSerializableObjectType(p.type_):
+ proxied_params.append('new %s._proxy(%s)' % (
+ self._GetDartType(p.type_), p.name))
+ elif self._IsListOfSerializableObjects(p.type_):
+ proxied_params.append('__proxy_%s' % p.name)
+ lists_to_proxy.append(p)
+ elif self._IsObjectType(p.type_):
+ # TODO(sashab): Find a way to build generic JS objects back in Dart.
+ proxied_params.append('%s' % p.name)
+ elif p.type_.property_type is PropertyType.ARRAY:
+ # TODO(sashab): This might be okay - what if this is a list of
+ # FileEntry elements? In this case, a basic list will proxy the objects
+ # fine.
+ proxied_params.append('%s' % p.name)
+ else:
+ raise Exception(
+ "Cannot automatically create proxy; can't wrap %s, type %s" % (
+ self._GenerateFunctionSignature(f), self._GetDartType(p.type_)))
+
+ (c.Sblock("void __proxy_callback(%s) {" % ', '.join(p.name for p in
+ f.params))
+ .Sblock('if (%s != null) {' % callback_name)
+ )
+
+ # Add the proxied lists.
+ for list_to_proxy in lists_to_proxy:
+ (c.Append("%s __proxy_%s = new %s();" % (
+ self._GetDartType(list_to_proxy.type_),
+ list_to_proxy.name,
+ self._GetDartType(list_to_proxy.type_)))
+ .Sblock("for (var o in %s) {" % list_to_proxy.name)
+ .Append('__proxy_%s.add(new %s._proxy(o));' % (list_to_proxy.name,
+ self._GetDartType(list_to_proxy.type_.item_type)))
+ .Eblock("}")
+ )
+
+ (c.Append("%s(%s);" % (callback_name, ', '.join(proxied_params)))
+ .Eblock('}')
+ .Eblock('}')
+ )
+ return c
+
+ def _NeedsProxiedCallback(self, f):
+ """Given a function, returns True if this function's callback needs to be
+ proxied, False if not.
+
+ Function callbacks need to be proxied if they have at least one
+ non-base-type parameter.
+ """
+ return f.callback and self._NeedsProxy(f.callback)
+
+ def _NeedsProxy(self, f):
+ """Given a function, returns True if it needs to be proxied, False if not.
+
+ A function needs to be proxied if any of its members are non-base types.
+ This means that, when the function object is passed to Javascript, it
+ needs to be wrapped in a "proxied" call that converts the JS inputs to Dart
+ objects explicitly, before calling the real function with these new objects.
+ """
+ return any(not self._IsBaseType(p.type_) for p in f.params)
+
+ def _GenerateProxyCall(self, function, call_target='this._jsObject'):
+ """Given a function, generates the code to call that function via JS().
+ Returns a string.
+
+ |call_target| is the name of the object to call the function on. The default
+ is this._jsObject.
+
+ e.g.
+ JS('void', '#.resizeTo(#, #)', this._jsObject, width, height)
+ JS('void', '#.setBounds(#)', this._jsObject, convertArgument(bounds))
+ """
+ n_params = len(function.params)
+ if function.callback:
+ n_params += 1
+
+ return_type_str = self._GetDartType(function.returns)
+ params = []
+
+ # If this object is serializable, don't convert the type from JS - pass the
+ # JS object straight into the proxy.
+ if self._IsSerializableObjectType(function.returns):
+ params.append("''")
+ else:
+ params.append("'%s'" % return_type_str)
+
+ params.append("'#.%s(%s)'" % (function.name, ', '.join(['#'] * n_params)))
+ params.append(call_target)
+
+ for param in function.params:
+ if not self._IsBaseType(param.type_):
+ params.append('convertArgument(%s)' % param.name)
+ else:
+ params.append(param.name)
+ if function.callback:
+ # If this isn't a base type, we need a proxied callback.
+ callback_name = function.callback.name
+ if self._NeedsProxiedCallback(function):
+ callback_name = "__proxy_callback"
+ params.append('convertDartClosureToJS(%s, %s)' % (callback_name,
+ len(function.callback.params)))
+
+ # If the object is serializable, call the proxy constructor for this type.
+ proxy_call = 'JS(%s)' % ', '.join(params)
+ if self._IsSerializableObjectType(function.returns):
+ proxy_call = 'new %s._proxy(%s)' % (return_type_str, proxy_call)
+
+ return proxy_call
+
+ def _GenerateEvent(self, event):
+ """Given a Function object, returns the Code with the .dart for this event,
+ represented by the function.
+
+ All events extend the Event base type.
+ """
+ c = Code()
+
+ # Add documentation for this event.
+ (c.Concat(self._GenerateDocumentation(event))
+ .Sblock('class Event_%(event_name)s extends Event {')
+ )
+
+ # If this event needs a proxy, all calls need to be proxied.
+ needs_proxy = self._NeedsProxy(event)
+
+ # Override Event callback type definitions.
+ for ret_type, event_func in (('void', 'addListener'),
+ ('void', 'removeListener'),
+ ('bool', 'hasListener')):
+ param_list = self._GenerateParameterList(event.params, event.callback,
+ convert_optional=True)
+ if needs_proxy:
+ (c.Sblock('%s %s(void callback(%s)) {' % (ret_type, event_func,
+ param_list))
+ .Concat(self._GenerateProxiedFunction(event, 'callback'))
+ .Append('super.%s(__proxy_callback);' % event_func)
+ .Eblock('}')
+ )
+ else:
+ c.Append('%s %s(void callback(%s)) => super.%s(callback);' %
+ (ret_type, event_func, param_list, event_func))
+ c.Append()
+
+ # Generate the constructor.
+ (c.Append('Event_%(event_name)s(jsObject) : '
+ 'super._(jsObject, %(param_num)d);')
+ .Eblock('}')
+ .Substitute({
+ 'event_name': self._namespace.unix_name + '_' + event.name,
+ 'param_num': len(event.params)
+ })
+ )
+
+ return c
+
+ def _GenerateMainClass(self):
+ """Generates the main class for this file, which links to all functions
+ and events.
+
+ Returns a code object.
+ """
+ c = Code()
+ (c.Sblock('class API_%s {' % self._namespace.unix_name)
+ .Append('/*')
+ .Append(' * API connection')
+ .Append(' */')
+ .Append('Object _jsObject;')
+ )
+
+ # Add events.
+ if self._namespace.events:
+ (c.Append()
+ .Append('/*')
+ .Append(' * Events')
+ .Append(' */')
+ )
+ for event_name in self._namespace.events:
+ c.Append('Event_%s_%s %s;' % (self._namespace.unix_name, event_name,
+ event_name))
+
+ # Add functions.
+ if self._namespace.functions:
+ (c.Append()
+ .Append('/*')
+ .Append(' * Functions')
+ .Append(' */')
+ )
+ for function in self._namespace.functions.values():
+ # Check for custom dart for this whole property.
+ override = self._GetOverride([function.name], document_with=function)
+ c.Cblock(override if override is not None
+ else self._GenerateFunction(function))
+
+ # Add the constructor.
+ c.Sblock('API_%s(this._jsObject) {' % self._namespace.unix_name)
+
+ # Add events to constructor.
+ for event_name in self._namespace.events:
+ c.Append("%s = new Event_%s_%s(JS('', '#.%s', this._jsObject));" %
+ (event_name, self._namespace.unix_name, event_name, event_name))
+
+ (c.Eblock('}')
+ .Eblock('}')
+ )
+ return c
+
+ def _GeneratePropertySignature(self, prop):
+ """Given a property, returns a signature for that property.
+ Recursively generates the signature for callbacks.
+ Returns a String for the given property.
+
+ e.g.
+ bool x
+ void onClosed()
+ void doSomething(bool x, void callback([String x]))
+ """
+ if self._IsFunction(prop.type_):
+ return self._GenerateFunctionSignature(prop.type_.function)
+ return '%(type)s %(name)s' % {
+ 'type': self._GetDartType(prop.type_),
+ 'name': prop.simple_name
+ }
+
+ def _GenerateFunctionSignature(self, function, convert_optional=False):
+ """Given a function object, returns the signature for that function.
+ Recursively generates the signature for callbacks.
+ Returns a String for the given function.
+
+ If convert_optional is True, changes optional parameters to be required.
+
+ e.g.
+ void onClosed()
+ bool isOpen([String type])
+ void doSomething(bool x, void callback([String x]))
+ """
+ sig = '%(return_type)s %(name)s(%(params)s)'
+
+ if function.returns:
+ return_type = self._GetDartType(function.returns)
+ else:
+ return_type = 'void'
+
+ return sig % {
+ 'return_type': return_type,
+ 'name': function.simple_name,
+ 'params': self._GenerateParameterList(function.params,
+ function.callback,
+ convert_optional=convert_optional)
+ }
+
+ def _GenerateParameterList(self,
+ params,
+ callback=None,
+ convert_optional=False):
+ """Given a list of function parameters, generates their signature (as a
+ string).
+
+ e.g.
+ [String type]
+ bool x, void callback([String x])
+
+ If convert_optional is True, changes optional parameters to be required.
+ Useful for callbacks, where optional parameters are treated as required.
+ """
+ # Params lists (required & optional), to be joined with commas.
+ # TODO(sashab): Don't assume optional params always come after required
+ # ones.
+ params_req = []
+ params_opt = []
+ for param in params:
+ p_sig = self._GeneratePropertySignature(param)
+ if param.optional and not convert_optional:
+ params_opt.append(p_sig)
+ else:
+ params_req.append(p_sig)
+
+ # Add the callback, if it exists.
+ if callback:
+ c_sig = self._GenerateFunctionSignature(callback, convert_optional=True)
+ if callback.optional:
+ params_opt.append(c_sig)
+ else:
+ params_req.append(c_sig)
+
+ # Join the parameters with commas.
+ # Optional parameters have to be in square brackets, e.g.:
+ #
+ # required params | optional params | output
+ # [] | [] | ''
+ # [x, y] | [] | 'x, y'
+ # [] | [a, b] | '[a, b]'
+ # [x, y] | [a, b] | 'x, y, [a, b]'
+ if params_opt:
+ params_opt[0] = '[%s' % params_opt[0]
+ params_opt[-1] = '%s]' % params_opt[-1]
+ param_sets = [', '.join(params_req), ', '.join(params_opt)]
+
+ # The 'if p' part here is needed to prevent commas where there are no
+ # parameters of a certain type.
+ # If there are no optional parameters, this prevents a _trailing_ comma,
+ # e.g. '(x, y,)'. Similarly, if there are no required parameters, this
+ # prevents a leading comma, e.g. '(, [a, b])'.
+ return ', '.join(p for p in param_sets if p)
+
+ def _GetOverride(self, key_chain, document_with=None):
+ """Given a list of keys, joins them with periods and searches for them in
+ the custom dart overrides.
+ If there is an override for that key, finds the override code and returns
+ the Code object. If not, returns None.
+
+ If document_with is not None, adds the documentation for this property
+ before the override code.
+ """
+ c = Code()
+ contents = self._type_overrides.get('.'.join(key_chain))
+ if contents is None:
+ return None
+
+ if document_with is not None:
+ c.Concat(self._GenerateDocumentation(document_with))
+ for line in contents.strip('\n').split('\n'):
+ c.Append(line)
+ return c
+
+ def _AddPrefix(self, name):
+ """Given the name of a type, prefixes the namespace (as camelcase) and
+ returns the new name.
+ """
+ # TODO(sashab): Split the dart library into multiple files, avoiding the
+ # need for this prefixing.
+ return ('%s%s' % (
+ ''.join(s.capitalize() for s in self._namespace.name.split('.')),
+ name))
+
+ def _IsFunction(self, type_):
+ """Given a model.Type, returns whether this type is a function.
+ """
+ return type_.property_type == PropertyType.FUNCTION
+
+ def _IsSerializableObjectType(self, type_):
+ """Given a model.Type, returns whether this type is a serializable object.
+ Serializable objects are custom types defined in this namespace.
+
+ If this object is a reference to something not in this namespace, assumes
+ its a serializable object.
+ """
+ if type_ is None:
+ return False
+ if type_.property_type is PropertyType.CHOICES:
+ return all(self._IsSerializableObjectType(c) for c in type_.choices)
+ if type_.property_type is PropertyType.REF:
+ if type_.ref_type in self._types:
+ return self._IsObjectType(self._types[type_.ref_type])
+ return True
+ if (type_.property_type == PropertyType.OBJECT
+ and type_.instance_of in self._types):
+ return self._IsObjectType(self._types[type_.instance_of])
+ return False
+
+ def _IsObjectType(self, type_):
+ """Given a model.Type, returns whether this type is an object.
+ """
+ return (self._IsSerializableObjectType(type_)
+ or type_.property_type in [PropertyType.OBJECT, PropertyType.ANY])
+
+ def _IsListOfSerializableObjects(self, type_):
+ """Given a model.Type, returns whether this type is a list of serializable
+ objects (or regular objects, if this list is treated as a type - in this
+ case, the item type was defined inline).
+
+ If this type is a reference to something not in this namespace, assumes
+ it is not a list of serializable objects.
+ """
+ if type_.property_type is PropertyType.CHOICES:
+ return all(self._IsListOfSerializableObjects(c) for c in type_.choices)
+ if type_.property_type is PropertyType.REF:
+ if type_.ref_type in self._types:
+ return self._IsListOfSerializableObjects(self._types[type_.ref_type])
+ return False
+ return (type_.property_type is PropertyType.ARRAY and
+ (self._IsSerializableObjectType(type_.item_type)))
+
+ def _IsListOfBaseTypes(self, type_):
+ """Given a model.Type, returns whether this type is a list of base type
+ objects (PropertyType.REF types).
+ """
+ if type_.property_type is PropertyType.CHOICES:
+ return all(self._IsListOfBaseTypes(c) for c in type_.choices)
+ return (type_.property_type is PropertyType.ARRAY and
+ self._IsBaseType(type_.item_type))
+
+ def _IsBaseType(self, type_):
+ """Given a model.type_, returns whether this type is a base type
+ (string, number, boolean, or a list of these).
+
+ If type_ is a Choices object, returns True if all possible choices are base
+ types.
+ """
+ # TODO(sashab): Remove 'Choices' as a base type once they are wrapped in
+ # native Dart classes.
+ if type_.property_type is PropertyType.CHOICES:
+ return all(self._IsBaseType(c) for c in type_.choices)
+ return (
+ (self._GetDartType(type_) in ['bool', 'num', 'int', 'double', 'String'])
+ or (type_.property_type is PropertyType.ARRAY
+ and self._IsBaseType(type_.item_type))
+ )
+
+ def _GetDartType(self, type_):
+ """Given a model.Type object, returns its type as a Dart string.
+ """
+ if type_ is None:
+ return 'void'
+
+ prop_type = type_.property_type
+ if prop_type is PropertyType.REF:
+ if type_.ref_type in self._types:
+ return self._GetDartType(self._types[type_.ref_type])
+ # TODO(sashab): If the type is foreign, it might have to be imported.
+ return StripNamespace(type_.ref_type)
+ elif prop_type is PropertyType.BOOLEAN:
+ return 'bool'
+ elif prop_type is PropertyType.INTEGER:
+ return 'int'
+ elif prop_type is PropertyType.INT64:
+ return 'num'
+ elif prop_type is PropertyType.DOUBLE:
+ return 'double'
+ elif prop_type is PropertyType.STRING:
+ return 'String'
+ elif prop_type is PropertyType.ENUM:
+ return 'String'
+ elif prop_type is PropertyType.CHOICES:
+ # TODO(sashab): Think of a nice way to generate code for Choices objects
+ # in Dart.
+ return 'Object'
+ elif prop_type is PropertyType.ANY:
+ return 'Object'
+ elif prop_type is PropertyType.OBJECT:
+ # TODO(sashab): type_.name is the name of the function's parameter for
+ # inline types defined in functions. Think of a way to generate names
+ # for this, or remove all inline type definitions at the start.
+ if type_.instance_of is not None:
+ return type_.instance_of
+ if not isinstance(type_.parent, Function):
+ return self._AddPrefix(type_.name)
+ return 'Object'
+ elif prop_type is PropertyType.FUNCTION:
+ return 'Function'
+ elif prop_type is PropertyType.ARRAY:
+ return 'List<%s>' % self._GetDartType(type_.item_type)
+ elif prop_type is PropertyType.BINARY:
+ return 'String'
+ else:
+ raise NotImplementedError(prop_type)
+
diff --git a/chromium/tools/json_schema_compiler/dart_generator_test.py b/chromium/tools/json_schema_compiler/dart_generator_test.py
new file mode 100755
index 00000000000..65ec7f1e638
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_generator_test.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+# 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 os
+import sys
+import unittest
+import glob
+
+from dart_generator import DartGenerator
+from compiler import GenerateSchema
+
+# If --rebase is passed to this test, this is set to True, indicating the test
+# output should be re-generated for each test (rather than running the tests
+# themselves).
+REBASE_MODE = False
+
+# The directory containing the input and expected output files corresponding
+# to each test name.
+TESTS_DIR = 'dart_test'
+
+class DartTest(unittest.TestCase):
+
+ def _RunTest(self, test_filename):
+ '''Given the name of a test, runs compiler.py on the file:
+ TESTS_DIR/test_filename.idl
+ and compares it to the output in the file:
+ TESTS_DIR/test_filename.dart
+ '''
+ file_rel = os.path.join(TESTS_DIR, test_filename)
+
+ output_dir = None
+ if REBASE_MODE:
+ output_dir = TESTS_DIR
+ output_code = GenerateSchema('dart', ['%s.idl' % file_rel], TESTS_DIR,
+ output_dir, None, None)
+
+ if not REBASE_MODE:
+ with open('%s.dart' % file_rel) as f:
+ expected_output = f.read()
+ # Remove the first line of the output code (as it contains the filename).
+ # Also remove all blank lines, ignoring them from the comparison.
+ # Compare with lists instead of strings for clearer diffs (especially with
+ # whitespace) when a test fails.
+ self.assertEqual([l for l in expected_output.split('\n') if l],
+ [l for l in output_code.split('\n')[1:] if l])
+
+ def setUp(self):
+ # Increase the maximum diff amount to see the full diff on a failed test.
+ self.maxDiff = 2000
+
+ def testComments(self):
+ self._RunTest('comments')
+
+ def testDictionaries(self):
+ self._RunTest('dictionaries')
+
+ def testEmptyNamespace(self):
+ self._RunTest('empty_namespace')
+
+ def testEmptyType(self):
+ self._RunTest('empty_type')
+
+ def testEvents(self):
+ self._RunTest('enums')
+
+ def testEvents(self):
+ self._RunTest('events')
+
+ def testBasicFunction(self):
+ self._RunTest('functions')
+
+ def testOpratableType(self):
+ self._RunTest('operatable_type')
+
+ def testTags(self):
+ self._RunTest('tags')
+
+if __name__ == '__main__':
+ if '--rebase' in sys.argv:
+ print "Running in rebase mode."
+ REBASE_MODE = True
+ sys.argv.remove('--rebase')
+ unittest.main()
diff --git a/chromium/tools/json_schema_compiler/dart_test/comments.dart b/chromium/tools/json_schema_compiler/dart_test/comments.dart
new file mode 100644
index 00000000000..d734d56f522
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/comments.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Generated from namespace: comments
+
+part of chrome;
+/**
+ * Functions
+ */
+
+class API_comments {
+ /*
+ * API connection
+ */
+ Object _jsObject;
+
+ /*
+ * Functions
+ */
+ /// There's a blank line at the start of this comment. Documentation for
+ /// basicFunction. BasicFunction() is a great function. There is a newline
+ /// after this.<br/><br/> It works like so: +-----+ | |
+ /// +--+ | | | | +-----+ --> +--+<br/><br/> Some other
+ /// stuff here. This paragraph starts with whitespace. Overall, its a
+ /// great function. There's also a blank line at the end of this comment.
+ void basicFunction() => JS('void', '#.basicFunction()', this._jsObject);
+
+ API_comments(this._jsObject) {
+ }
+}
diff --git a/chromium/tools/json_schema_compiler/dart_test/comments.idl b/chromium/tools/json_schema_compiler/dart_test/comments.idl
new file mode 100644
index 00000000000..867d289d264
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/comments.idl
@@ -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.
+
+// This comment is for the comments namespace.
+namespace comments {
+ // This comments the "Functions" block, and should be ignored.
+ interface Functions {
+ // This comment is separated by at least one blank line from the start of
+ // the function, and should be ignored.
+
+ //
+ // There's a blank line at the start of this comment.
+ //
+ // Documentation for basicFunction.
+ // BasicFunction() is a great function.
+ // There is a newline after this.
+ //
+ // It works like so:
+ // +-----+
+ // | | +--+
+ // | | | |
+ // +-----+ --> +--+
+ //
+ // Some other stuff here.
+ // This paragraph starts with whitespace.
+ // Overall, its a great function.
+ // There's also a blank line at the end of this comment.
+ //
+ static void basicFunction();
+ };
+};
diff --git a/chromium/tools/json_schema_compiler/dart_test/dictionaries.dart b/chromium/tools/json_schema_compiler/dart_test/dictionaries.dart
new file mode 100644
index 00000000000..ebe92a6c1eb
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/dictionaries.dart
@@ -0,0 +1,235 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Generated from namespace: dictionaries
+
+part of chrome;
+
+/**
+ * Types
+ */
+
+class DictionariesInnerType extends ChromeObject {
+ /*
+ * Public constructor
+ */
+ DictionariesInnerType({String s, int b, int i, int l, double d, FileEntry f, String os, int ob, int oi, int ol, double od, FileEntry of}) {
+ if (s != null)
+ this.s = s;
+ if (b != null)
+ this.b = b;
+ if (i != null)
+ this.i = i;
+ if (l != null)
+ this.l = l;
+ if (d != null)
+ this.d = d;
+ if (f != null)
+ this.f = f;
+ if (os != null)
+ this.os = os;
+ if (ob != null)
+ this.ob = ob;
+ if (oi != null)
+ this.oi = oi;
+ if (ol != null)
+ this.ol = ol;
+ if (od != null)
+ this.od = od;
+ if (of != null)
+ this.of = of;
+ }
+
+ /*
+ * Private constructor
+ */
+ DictionariesInnerType._proxy(_jsObject) : super._proxy(_jsObject);
+
+ /*
+ * Public accessors
+ */
+ /// Documentation for the String s.
+ String get s => JS('String', '#.s', this._jsObject);
+
+ void set s(String s) {
+ JS('void', '#.s = #', this._jsObject, s);
+ }
+
+ /// Documentation for the boolean b.
+ int get b => JS('int', '#.b', this._jsObject);
+
+ void set b(int b) {
+ JS('void', '#.b = #', this._jsObject, b);
+ }
+
+ /// Documentation for the int i.
+ int get i => JS('int', '#.i', this._jsObject);
+
+ void set i(int i) {
+ JS('void', '#.i = #', this._jsObject, i);
+ }
+
+ /// Documentation for the long l.
+ int get l => JS('int', '#.l', this._jsObject);
+
+ void set l(int l) {
+ JS('void', '#.l = #', this._jsObject, l);
+ }
+
+ /// Documentation for the double d.
+ double get d => JS('double', '#.d', this._jsObject);
+
+ void set d(double d) {
+ JS('void', '#.d = #', this._jsObject, d);
+ }
+
+ /// Documentation for the file entry f.
+ FileEntry get f => JS('FileEntry', '#.f', this._jsObject);
+
+ void set f(FileEntry f) {
+ JS('void', '#.f = #', this._jsObject, convertArgument(f));
+ }
+
+ /// Documentation for the optional String s.
+ String get os => JS('String', '#.os', this._jsObject);
+
+ void set os(String os) {
+ JS('void', '#.os = #', this._jsObject, os);
+ }
+
+ /// Documentation for the optional boolean ob.
+ int get ob => JS('int', '#.ob', this._jsObject);
+
+ void set ob(int ob) {
+ JS('void', '#.ob = #', this._jsObject, ob);
+ }
+
+ /// Documentation for the optional int i.
+ int get oi => JS('int', '#.oi', this._jsObject);
+
+ void set oi(int oi) {
+ JS('void', '#.oi = #', this._jsObject, oi);
+ }
+
+ /// Documentation for the optional long l.
+ int get ol => JS('int', '#.ol', this._jsObject);
+
+ void set ol(int ol) {
+ JS('void', '#.ol = #', this._jsObject, ol);
+ }
+
+ /// Documentation for the optional double d.
+ double get od => JS('double', '#.od', this._jsObject);
+
+ void set od(double od) {
+ JS('void', '#.od = #', this._jsObject, od);
+ }
+
+ /// Documentation for the optional file entry f.
+ FileEntry get of => JS('FileEntry', '#.of', this._jsObject);
+
+ void set of(FileEntry of) {
+ JS('void', '#.of = #', this._jsObject, convertArgument(of));
+ }
+
+}
+
+class DictionariesOuterType extends ChromeObject {
+ /*
+ * Public constructor
+ */
+ DictionariesOuterType({List<DictionariesInnerType> items, List<DictionariesInnerType> oitems}) {
+ if (items != null)
+ this.items = items;
+ if (oitems != null)
+ this.oitems = oitems;
+ }
+
+ /*
+ * Private constructor
+ */
+ DictionariesOuterType._proxy(_jsObject) : super._proxy(_jsObject);
+
+ /*
+ * Public accessors
+ */
+ /// Documentation for the array of InnerTypes items.
+ List<DictionariesInnerType> get items {
+ List<DictionariesInnerType> __proxy_items = new List<DictionariesInnerType>();
+ int count = JS('int', '#.items.length', this._jsObject);
+ for (int i = 0; i < count; i++) {
+ var item = JS('', '#.items[#]', this._jsObject, i);
+ __proxy_items.add(new DictionariesInnerType._proxy(item));
+ }
+ return __proxy_items;
+ }
+
+ void set items(List<DictionariesInnerType> items) {
+ JS('void', '#.items = #', this._jsObject, convertArgument(items));
+ }
+
+ /// Documentation for the optional array of Inner Types oitems.
+ List<DictionariesInnerType> get oitems {
+ List<DictionariesInnerType> __proxy_oitems = new List<DictionariesInnerType>();
+ int count = JS('int', '#.oitems.length', this._jsObject);
+ for (int i = 0; i < count; i++) {
+ var item = JS('', '#.oitems[#]', this._jsObject, i);
+ __proxy_oitems.add(new DictionariesInnerType._proxy(item));
+ }
+ return __proxy_oitems;
+ }
+
+ void set oitems(List<DictionariesInnerType> oitems) {
+ JS('void', '#.oitems = #', this._jsObject, convertArgument(oitems));
+ }
+
+}
+
+class DictionariesComplexType extends ChromeObject {
+ /*
+ * Public constructor
+ */
+ DictionariesComplexType({int i, DictionariesComplexType c}) {
+ if (i != null)
+ this.i = i;
+ if (c != null)
+ this.c = c;
+ }
+
+ /*
+ * Private constructor
+ */
+ DictionariesComplexType._proxy(_jsObject) : super._proxy(_jsObject);
+
+ /*
+ * Public accessors
+ */
+ /// Documentation for the int i.
+ int get i => JS('int', '#.i', this._jsObject);
+
+ void set i(int i) {
+ JS('void', '#.i = #', this._jsObject, i);
+ }
+
+ /// Documentation for the ComplexType c.
+ DictionariesComplexType get c => new DictionariesComplexType._proxy(JS('', '#.c', this._jsObject));
+
+ void set c(DictionariesComplexType c) {
+ JS('void', '#.c = #', this._jsObject, convertArgument(c));
+ }
+
+}
+
+/**
+ * Functions
+ */
+
+class API_dictionaries {
+ /*
+ * API connection
+ */
+ Object _jsObject;
+ API_dictionaries(this._jsObject) {
+ }
+}
diff --git a/chromium/tools/json_schema_compiler/dart_test/dictionaries.idl b/chromium/tools/json_schema_compiler/dart_test/dictionaries.idl
new file mode 100644
index 00000000000..1eb9e25e0a0
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/dictionaries.idl
@@ -0,0 +1,61 @@
+// 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.
+
+// This comment is for the dictionaries namespace.
+namespace dictionaries {
+ // Documentation for ComplexType.
+ dictionary InnerType {
+ // Documentation for the String s.
+ DOMString s;
+
+ // Documentation for the boolean b.
+ int b;
+
+ // Documentation for the int i.
+ int i;
+
+ // Documentation for the long l.
+ long l;
+
+ // Documentation for the double d.
+ double d;
+
+ // Documentation for the file entry f.
+ [instanceOf=FileEntry] object f;
+
+ // Documentation for the optional String s.
+ DOMString? os;
+
+ // Documentation for the optional boolean ob.
+ int ob;
+
+ // Documentation for the optional int i.
+ int? oi;
+
+ // Documentation for the optional long l.
+ long? ol;
+
+ // Documentation for the optional double d.
+ double? od;
+
+ // Documentation for the optional file entry f.
+ [instanceOf=FileEntry] object? of;
+ };
+
+ dictionary OuterType {
+ // Documentation for the array of InnerTypes items.
+ InnerType[] items;
+
+ // Documentation for the optional array of Inner Types oitems.
+ InnerType[]? oitems;
+ };
+
+ dictionary ComplexType {
+ // Documentation for the int i.
+ int i;
+
+ // Documentation for the ComplexType c.
+ ComplexType c;
+ };
+};
diff --git a/chromium/tools/json_schema_compiler/dart_test/empty_namespace.dart b/chromium/tools/json_schema_compiler/dart_test/empty_namespace.dart
new file mode 100644
index 00000000000..d2378a2860c
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/empty_namespace.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Generated from namespace: empty_namespace
+
+part of chrome;
+/**
+ * Functions
+ */
+
+class API_empty_namespace {
+ /*
+ * API connection
+ */
+ Object _jsObject;
+ API_empty_namespace(this._jsObject) {
+ }
+}
diff --git a/chromium/tools/json_schema_compiler/dart_test/empty_namespace.idl b/chromium/tools/json_schema_compiler/dart_test/empty_namespace.idl
new file mode 100644
index 00000000000..824de2d80e7
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/empty_namespace.idl
@@ -0,0 +1,7 @@
+// 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.
+
+// An empty comment is required for an empty namespace.
+namespace empty_namespace {
+};
diff --git a/chromium/tools/json_schema_compiler/dart_test/empty_type.dart b/chromium/tools/json_schema_compiler/dart_test/empty_type.dart
new file mode 100644
index 00000000000..ebe8e69659a
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/empty_type.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Generated from namespace: empty_type
+
+part of chrome;
+
+/**
+ * Types
+ */
+
+class Empty_typeEmptyType extends ChromeObject {
+ /*
+ * Public constructor
+ */
+ Empty_typeEmptyType({}) {
+ }
+
+ /*
+ * Private constructor
+ */
+ Empty_typeEmptyType._proxy(_jsObject) : super._proxy(_jsObject);
+}
+
+/**
+ * Functions
+ */
+
+class API_empty_type {
+ /*
+ * API connection
+ */
+ Object _jsObject;
+ API_empty_type(this._jsObject) {
+ }
+}
diff --git a/chromium/tools/json_schema_compiler/dart_test/empty_type.idl b/chromium/tools/json_schema_compiler/dart_test/empty_type.idl
new file mode 100644
index 00000000000..9d7de6f7a70
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/empty_type.idl
@@ -0,0 +1,10 @@
+// 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.
+
+// Namespace-level comment for EmptyType.
+namespace empty_type {
+ // Documentation for EmptyType.
+ dictionary EmptyType {
+ };
+};
diff --git a/chromium/tools/json_schema_compiler/dart_test/enums.idl b/chromium/tools/json_schema_compiler/dart_test/enums.idl
new file mode 100644
index 00000000000..1c82d00824a
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/enums.idl
@@ -0,0 +1,12 @@
+// Copyright 2013 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.
+
+// A comment for the enums namespace.
+namespace enums {
+ // A basic enumeration.
+ enum Enum1 { a, b };
+
+ // Another basic enumeration.
+ enum Enum2 { ab, bc, de, ef, fg, hi };
+};
diff --git a/chromium/tools/json_schema_compiler/dart_test/events.dart b/chromium/tools/json_schema_compiler/dart_test/events.dart
new file mode 100644
index 00000000000..d2e79ba1b31
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/events.dart
@@ -0,0 +1,282 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Generated from namespace: events
+
+part of chrome;
+
+/**
+ * Types
+ */
+
+class EventsEventArgumentElement extends ChromeObject {
+ /*
+ * Public constructor
+ */
+ EventsEventArgumentElement({String elementStringArg}) {
+ if (elementStringArg != null)
+ this.elementStringArg = elementStringArg;
+ }
+
+ /*
+ * Private constructor
+ */
+ EventsEventArgumentElement._proxy(_jsObject) : super._proxy(_jsObject);
+
+ /*
+ * Public accessors
+ */
+ String get elementStringArg => JS('String', '#.elementStringArg', this._jsObject);
+
+ void set elementStringArg(String elementStringArg) {
+ JS('void', '#.elementStringArg = #', this._jsObject, elementStringArg);
+ }
+
+}
+
+class EventsEventArgument extends ChromeObject {
+ /*
+ * Public constructor
+ */
+ EventsEventArgument({FileEntry entryArg, String stringArg, int intArg, List<EventsEventArgumentElement> elements, FileEntry optionalEntryArg, String optionalStringArg, int optionalIntArg, List<EventsEventArgumentElement> optionalElements}) {
+ if (entryArg != null)
+ this.entryArg = entryArg;
+ if (stringArg != null)
+ this.stringArg = stringArg;
+ if (intArg != null)
+ this.intArg = intArg;
+ if (elements != null)
+ this.elements = elements;
+ if (optionalEntryArg != null)
+ this.optionalEntryArg = optionalEntryArg;
+ if (optionalStringArg != null)
+ this.optionalStringArg = optionalStringArg;
+ if (optionalIntArg != null)
+ this.optionalIntArg = optionalIntArg;
+ if (optionalElements != null)
+ this.optionalElements = optionalElements;
+ }
+
+ /*
+ * Private constructor
+ */
+ EventsEventArgument._proxy(_jsObject) : super._proxy(_jsObject);
+
+ /*
+ * Public accessors
+ */
+ /// A file entry
+ FileEntry get entryArg => JS('FileEntry', '#.entryArg', this._jsObject);
+
+ void set entryArg(FileEntry entryArg) {
+ JS('void', '#.entryArg = #', this._jsObject, convertArgument(entryArg));
+ }
+
+ /// A string
+ String get stringArg => JS('String', '#.stringArg', this._jsObject);
+
+ void set stringArg(String stringArg) {
+ JS('void', '#.stringArg = #', this._jsObject, stringArg);
+ }
+
+ /// A primitive
+ int get intArg => JS('int', '#.intArg', this._jsObject);
+
+ void set intArg(int intArg) {
+ JS('void', '#.intArg = #', this._jsObject, intArg);
+ }
+
+ /// An array
+ List<EventsEventArgumentElement> get elements {
+ List<EventsEventArgumentElement> __proxy_elements = new List<EventsEventArgumentElement>();
+ int count = JS('int', '#.elements.length', this._jsObject);
+ for (int i = 0; i < count; i++) {
+ var item = JS('', '#.elements[#]', this._jsObject, i);
+ __proxy_elements.add(new EventsEventArgumentElement._proxy(item));
+ }
+ return __proxy_elements;
+ }
+
+ void set elements(List<EventsEventArgumentElement> elements) {
+ JS('void', '#.elements = #', this._jsObject, convertArgument(elements));
+ }
+
+ /// Optional file entry
+ FileEntry get optionalEntryArg => JS('FileEntry', '#.optionalEntryArg', this._jsObject);
+
+ void set optionalEntryArg(FileEntry optionalEntryArg) {
+ JS('void', '#.optionalEntryArg = #', this._jsObject, convertArgument(optionalEntryArg));
+ }
+
+ /// A string
+ String get optionalStringArg => JS('String', '#.optionalStringArg', this._jsObject);
+
+ void set optionalStringArg(String optionalStringArg) {
+ JS('void', '#.optionalStringArg = #', this._jsObject, optionalStringArg);
+ }
+
+ /// A primitive
+ int get optionalIntArg => JS('int', '#.optionalIntArg', this._jsObject);
+
+ void set optionalIntArg(int optionalIntArg) {
+ JS('void', '#.optionalIntArg = #', this._jsObject, optionalIntArg);
+ }
+
+ /// An array
+ List<EventsEventArgumentElement> get optionalElements {
+ List<EventsEventArgumentElement> __proxy_optionalElements = new List<EventsEventArgumentElement>();
+ int count = JS('int', '#.optionalElements.length', this._jsObject);
+ for (int i = 0; i < count; i++) {
+ var item = JS('', '#.optionalElements[#]', this._jsObject, i);
+ __proxy_optionalElements.add(new EventsEventArgumentElement._proxy(item));
+ }
+ return __proxy_optionalElements;
+ }
+
+ void set optionalElements(List<EventsEventArgumentElement> optionalElements) {
+ JS('void', '#.optionalElements = #', this._jsObject, convertArgument(optionalElements));
+ }
+
+}
+
+/**
+ * Events
+ */
+
+/// Documentation for the first basic event.
+class Event_events_firstBasicEvent extends Event {
+ void addListener(void callback()) => super.addListener(callback);
+
+ void removeListener(void callback()) => super.removeListener(callback);
+
+ bool hasListener(void callback()) => super.hasListener(callback);
+
+ Event_events_firstBasicEvent(jsObject) : super._(jsObject, 0);
+}
+
+/// Documentation for the second basic event.
+class Event_events_secondBasicEvent extends Event {
+ void addListener(void callback()) => super.addListener(callback);
+
+ void removeListener(void callback()) => super.removeListener(callback);
+
+ bool hasListener(void callback()) => super.hasListener(callback);
+
+ Event_events_secondBasicEvent(jsObject) : super._(jsObject, 0);
+}
+
+/// Documentation for an event with a non-optional primitive argument.
+class Event_events_nonOptionalPrimitiveArgEvent extends Event {
+ void addListener(void callback(int argument)) => super.addListener(callback);
+
+ void removeListener(void callback(int argument)) => super.removeListener(callback);
+
+ bool hasListener(void callback(int argument)) => super.hasListener(callback);
+
+ Event_events_nonOptionalPrimitiveArgEvent(jsObject) : super._(jsObject, 1);
+}
+
+/// Documentation for an event with an optional primitive argument.
+class Event_events_optionalPrimitiveArgEvent extends Event {
+ void addListener(void callback(int argument)) => super.addListener(callback);
+
+ void removeListener(void callback(int argument)) => super.removeListener(callback);
+
+ bool hasListener(void callback(int argument)) => super.hasListener(callback);
+
+ Event_events_optionalPrimitiveArgEvent(jsObject) : super._(jsObject, 1);
+}
+
+/// Documentation for an event with a non-optional dictionary argument.
+class Event_events_nonOptionalDictArgEvent extends Event {
+ void addListener(void callback(EventsEventArgument argument)) {
+ void __proxy_callback(argument) {
+ if (callback != null) {
+ callback(new EventsEventArgument._proxy(argument));
+ }
+ }
+ super.addListener(__proxy_callback);
+ }
+
+ void removeListener(void callback(EventsEventArgument argument)) {
+ void __proxy_callback(argument) {
+ if (callback != null) {
+ callback(new EventsEventArgument._proxy(argument));
+ }
+ }
+ super.removeListener(__proxy_callback);
+ }
+
+ bool hasListener(void callback(EventsEventArgument argument)) {
+ void __proxy_callback(argument) {
+ if (callback != null) {
+ callback(new EventsEventArgument._proxy(argument));
+ }
+ }
+ super.hasListener(__proxy_callback);
+ }
+
+ Event_events_nonOptionalDictArgEvent(jsObject) : super._(jsObject, 1);
+}
+
+/// Documentation for an event with a optional dictionary argument.
+class Event_events_optionalDictArgEvent extends Event {
+ void addListener(void callback(EventsEventArgument argument)) {
+ void __proxy_callback(argument) {
+ if (callback != null) {
+ callback(new EventsEventArgument._proxy(argument));
+ }
+ }
+ super.addListener(__proxy_callback);
+ }
+
+ void removeListener(void callback(EventsEventArgument argument)) {
+ void __proxy_callback(argument) {
+ if (callback != null) {
+ callback(new EventsEventArgument._proxy(argument));
+ }
+ }
+ super.removeListener(__proxy_callback);
+ }
+
+ bool hasListener(void callback(EventsEventArgument argument)) {
+ void __proxy_callback(argument) {
+ if (callback != null) {
+ callback(new EventsEventArgument._proxy(argument));
+ }
+ }
+ super.hasListener(__proxy_callback);
+ }
+
+ Event_events_optionalDictArgEvent(jsObject) : super._(jsObject, 1);
+}
+
+/**
+ * Functions
+ */
+
+class API_events {
+ /*
+ * API connection
+ */
+ Object _jsObject;
+
+ /*
+ * Events
+ */
+ Event_events_firstBasicEvent firstBasicEvent;
+ Event_events_secondBasicEvent secondBasicEvent;
+ Event_events_nonOptionalPrimitiveArgEvent nonOptionalPrimitiveArgEvent;
+ Event_events_optionalPrimitiveArgEvent optionalPrimitiveArgEvent;
+ Event_events_nonOptionalDictArgEvent nonOptionalDictArgEvent;
+ Event_events_optionalDictArgEvent optionalDictArgEvent;
+ API_events(this._jsObject) {
+ firstBasicEvent = new Event_events_firstBasicEvent(JS('', '#.firstBasicEvent', this._jsObject));
+ secondBasicEvent = new Event_events_secondBasicEvent(JS('', '#.secondBasicEvent', this._jsObject));
+ nonOptionalPrimitiveArgEvent = new Event_events_nonOptionalPrimitiveArgEvent(JS('', '#.nonOptionalPrimitiveArgEvent', this._jsObject));
+ optionalPrimitiveArgEvent = new Event_events_optionalPrimitiveArgEvent(JS('', '#.optionalPrimitiveArgEvent', this._jsObject));
+ nonOptionalDictArgEvent = new Event_events_nonOptionalDictArgEvent(JS('', '#.nonOptionalDictArgEvent', this._jsObject));
+ optionalDictArgEvent = new Event_events_optionalDictArgEvent(JS('', '#.optionalDictArgEvent', this._jsObject));
+ }
+}
diff --git a/chromium/tools/json_schema_compiler/dart_test/events.idl b/chromium/tools/json_schema_compiler/dart_test/events.idl
new file mode 100644
index 00000000000..f1fb4b67dcc
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/events.idl
@@ -0,0 +1,56 @@
+// Copyright 2013 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.
+
+// This comment is for the events namespace.
+namespace events {
+ dictionary EventArgumentElement {
+ DOMString elementStringArg;
+ };
+
+ dictionary EventArgument {
+ // A file entry
+ [instanceOf=FileEntry] object entryArg;
+
+ // A string
+ DOMString stringArg;
+
+ // A primitive
+ int intArg;
+
+ // An array
+ EventArgumentElement[] elements;
+
+ // Optional file entry
+ [instanceOf=FileEntry] object? optionalEntryArg;
+
+ // A string
+ DOMString? optionalStringArg;
+
+ // A primitive
+ int? optionalIntArg;
+
+ // An array
+ EventArgumentElement[]? optionalElements;
+ };
+
+ interface Events {
+ // Documentation for the first basic event.
+ static void firstBasicEvent();
+
+ // Documentation for the second basic event.
+ static void secondBasicEvent();
+
+ // Documentation for an event with a non-optional primitive argument.
+ static void nonOptionalPrimitiveArgEvent(int argument);
+
+ // Documentation for an event with an optional primitive argument.
+ static void optionalPrimitiveArgEvent(optional int argument);
+
+ // Documentation for an event with a non-optional dictionary argument.
+ static void nonOptionalDictArgEvent(EventArgument argument);
+
+ // Documentation for an event with a optional dictionary argument.
+ static void optionalDictArgEvent(EventArgument argument);
+ };
+};
diff --git a/chromium/tools/json_schema_compiler/dart_test/functions.dart b/chromium/tools/json_schema_compiler/dart_test/functions.dart
new file mode 100644
index 00000000000..2b4f02bc1c6
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/functions.dart
@@ -0,0 +1,93 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Generated from namespace: functions
+
+part of chrome;
+
+/**
+ * Types
+ */
+
+class FunctionsDictType extends ChromeObject {
+ /*
+ * Private constructor
+ */
+ FunctionsDictType._proxy(_jsObject) : super._proxy(_jsObject);
+
+ /*
+ * Public accessors
+ */
+ /// A field.
+ int get a => JS('int', '#.a', this._jsObject);
+
+ void set a(int a) {
+ JS('void', '#.a = #', this._jsObject, a);
+ }
+
+
+ /*
+ * Methods
+ */
+ /// A parameter.
+ void voidFunc() => JS('void', '#.voidFunc()', this._jsObject);
+
+}
+
+/**
+ * Functions
+ */
+
+class API_functions {
+ /*
+ * API connection
+ */
+ Object _jsObject;
+
+ /*
+ * Functions
+ */
+ /// Simple function.
+ void voidFunc() => JS('void', '#.voidFunc()', this._jsObject);
+
+ /// Function taking a non-optional argument.
+ void argFunc(String s) => JS('void', '#.argFunc(#)', this._jsObject, s);
+
+ /// Function taking an optional argument.
+ void optionalArgFunc([String s]) => JS('void', '#.optionalArgFunc(#)', this._jsObject, s);
+
+ /// Function taking a non-optional dictionary argument.
+ void dictArgFunc(FunctionsDictType d) => JS('void', '#.dictArgFunc(#)', this._jsObject, convertArgument(d));
+
+ /// Function taking an optional dictionary argument.
+ void optionalDictArgFunc([FunctionsDictType d]) => JS('void', '#.optionalDictArgFunc(#)', this._jsObject, convertArgument(d));
+
+ /// Function taking an entry argument.
+ void entryArgFunc(Object entry) => JS('void', '#.entryArgFunc(#)', this._jsObject, convertArgument(entry));
+
+ /// Function taking a simple callback.
+ void callbackFunc(void c()) => JS('void', '#.callbackFunc(#)', this._jsObject, convertDartClosureToJS(c, 0));
+
+ /// Function taking an optional simple callback.
+ void optionalCallbackFunc([void c()]) => JS('void', '#.optionalCallbackFunc(#)', this._jsObject, convertDartClosureToJS(c, 0));
+
+ /// Function taking a primitive callback.
+ void primitiveCallbackFunc(void c(int i)) => JS('void', '#.primitiveCallbackFunc(#)', this._jsObject, convertDartClosureToJS(c, 1));
+
+ /// Function taking a dictionary callback.
+ void dictCallbackFunc(void c(DictType dict)) {
+ void __proxy_callback(dict) {
+ if (c != null) {
+ c(new DictType._proxy(dict));
+ }
+ }
+ JS('void', '#.dictCallbackFunc(#)', this._jsObject, convertDartClosureToJS(__proxy_callback, 1));
+ }
+
+ /// Function returning a dictionary.
+ FunctionsDictType dictRetFunc() => new FunctionsDictType._proxy(JS('', '#.dictRetFunc()', this._jsObject));
+
+ API_functions(this._jsObject) {
+ }
+}
diff --git a/chromium/tools/json_schema_compiler/dart_test/functions.idl b/chromium/tools/json_schema_compiler/dart_test/functions.idl
new file mode 100644
index 00000000000..e303d0de022
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/functions.idl
@@ -0,0 +1,55 @@
+// Copyright 2013 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.
+
+// A comment for the functions namespace.
+namespace functions {
+ callback SimpleCallback = void ();
+
+ callback PrimitiveCallback = void (int i);
+
+ callback DictCallback = void ([instanceOf=DictType] object dict);
+
+ dictionary DictType {
+ // A field.
+ int a;
+
+ // A parameter.
+ static void voidFunc();
+ };
+
+ interface Functions {
+ // Simple function.
+ static void voidFunc();
+
+ // Function taking a non-optional argument.
+ static void argFunc(DOMString s);
+
+ // Function taking an optional argument.
+ static void optionalArgFunc(optional DOMString s);
+
+ // Function taking a non-optional dictionary argument.
+ static void dictArgFunc(DictType d);
+
+ // Function taking an optional dictionary argument.
+ static void optionalDictArgFunc(optional DictType d);
+
+ // Function taking an entry argument.
+ static void entryArgFunc([intanceOf=FileEntry] object entry);
+
+ // Function taking a simple callback.
+ static void callbackFunc(SimpleCallback c);
+
+ // Function taking an optional simple callback.
+ static void optionalCallbackFunc(optional SimpleCallback c);
+
+ // Function taking a primitive callback.
+ static void primitiveCallbackFunc(PrimitiveCallback c);
+
+ // Function taking a dictionary callback.
+ static void dictCallbackFunc(DictCallback c);
+
+ // Function returning a dictionary.
+ static DictType dictRetFunc();
+ };
+};
diff --git a/chromium/tools/json_schema_compiler/dart_test/operatable_type.dart b/chromium/tools/json_schema_compiler/dart_test/operatable_type.dart
new file mode 100644
index 00000000000..725d6a68882
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/operatable_type.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Generated from namespace: operatable_type
+
+part of chrome;
+
+/**
+ * Types
+ */
+
+class Operatable_typeDictType extends ChromeObject {
+ /*
+ * Public constructor
+ */
+ Operatable_typeDictType({int x, int y}) {
+ if (x != null)
+ this.x = x;
+ if (y != null)
+ this.y = y;
+ }
+
+ /*
+ * Private constructor
+ */
+ Operatable_typeDictType._proxy(_jsObject) : super._proxy(_jsObject);
+
+ /*
+ * Public accessors
+ */
+ int get x => JS('int', '#.x', this._jsObject);
+
+ void set x(int x) {
+ JS('void', '#.x = #', this._jsObject, x);
+ }
+
+ int get y => JS('int', '#.y', this._jsObject);
+
+ void set y(int y) {
+ JS('void', '#.y = #', this._jsObject, y);
+ }
+
+}
+
+class Operatable_typeOperatableType extends ChromeObject {
+ /*
+ * Private constructor
+ */
+ Operatable_typeOperatableType._proxy(_jsObject) : super._proxy(_jsObject);
+
+ /*
+ * Public accessors
+ */
+ /// Documentation for the String t.
+ String get t => JS('String', '#.t', this._jsObject);
+
+ void set t(String t) {
+ JS('void', '#.t = #', this._jsObject, t);
+ }
+
+
+ /*
+ * Methods
+ */
+ /// Function returning nothing, taking nothing.
+ void voidFunc() => JS('void', '#.voidFunc()', this._jsObject);
+
+ /// Function returning primitive type.
+ int intRetFunc() => new int._proxy(JS('', '#.intRetFunc()', this._jsObject));
+
+ /// Function returning dictionary.
+ Operatable_typeDictType dictRetFunc() => new Operatable_typeDictType._proxy(JS('', '#.dictRetFunc()', this._jsObject));
+
+ /// Function taking primitive type.
+ void intArgFunc(int i) => JS('void', '#.intArgFunc(#)', this._jsObject, i);
+
+ /// Function taking dict type.
+ void dictArgFunc(Operatable_typeDictType d) => JS('void', '#.dictArgFunc(#)', this._jsObject, convertArgument(d));
+
+}
+
+/**
+ * Functions
+ */
+
+class API_operatable_type {
+ /*
+ * API connection
+ */
+ Object _jsObject;
+ API_operatable_type(this._jsObject) {
+ }
+}
diff --git a/chromium/tools/json_schema_compiler/dart_test/operatable_type.idl b/chromium/tools/json_schema_compiler/dart_test/operatable_type.idl
new file mode 100644
index 00000000000..9c5f53cc0ef
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/operatable_type.idl
@@ -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.
+
+// Top-level namespace-comment for operatableType
+namespace operatable_type {
+ dictionary DictType {
+ int x;
+ int y;
+ };
+
+ // Documentation for OperatableType.
+ dictionary OperatableType {
+ // Documentation for the String t.
+ DOMString t;
+
+ // Function returning nothing, taking nothing.
+ static void voidFunc();
+
+ // Function returning primitive type.
+ static int intRetFunc();
+
+ // Function returning dictionary.
+ static DictType dictRetFunc();
+
+ // Function taking primitive type.
+ static void intArgFunc(int i);
+
+ // Function taking dict type.
+ static void dictArgFunc(DictType d);
+ };
+};
diff --git a/chromium/tools/json_schema_compiler/dart_test/tags.dart b/chromium/tools/json_schema_compiler/dart_test/tags.dart
new file mode 100644
index 00000000000..4c3514aeebf
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/tags.dart
@@ -0,0 +1,116 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Generated from namespace: tags
+
+part of chrome;
+
+/**
+ * Types
+ */
+
+class TagsInlineDoc extends ChromeObject {
+ /*
+ * Public constructor
+ */
+ TagsInlineDoc({}) {
+ }
+
+ /*
+ * Private constructor
+ */
+ TagsInlineDoc._proxy(_jsObject) : super._proxy(_jsObject);
+}
+
+class TagsNodoc extends ChromeObject {
+ /*
+ * Public constructor
+ */
+ TagsNodoc({}) {
+ }
+
+ /*
+ * Private constructor
+ */
+ TagsNodoc._proxy(_jsObject) : super._proxy(_jsObject);
+}
+
+class TagsNocompile extends ChromeObject {
+ /*
+ * Public constructor
+ */
+ TagsNocompile({}) {
+ }
+
+ /*
+ * Private constructor
+ */
+ TagsNocompile._proxy(_jsObject) : super._proxy(_jsObject);
+}
+
+class TagsPlainDict extends ChromeObject {
+ /*
+ * Public constructor
+ */
+ TagsPlainDict({int inline_doc, String nodoc, double nocompile, fileEntry instance_of_tag}) {
+ if (inline_doc != null)
+ this.inline_doc = inline_doc;
+ if (nodoc != null)
+ this.nodoc = nodoc;
+ if (nocompile != null)
+ this.nocompile = nocompile;
+ if (instance_of_tag != null)
+ this.instance_of_tag = instance_of_tag;
+ }
+
+ /*
+ * Private constructor
+ */
+ TagsPlainDict._proxy(_jsObject) : super._proxy(_jsObject);
+
+ /*
+ * Public accessors
+ */
+ /// This int has the property [inline_doc].
+ int get inline_doc => JS('int', '#.inline_doc', this._jsObject);
+
+ void set inline_doc(int inline_doc) {
+ JS('void', '#.inline_doc = #', this._jsObject, inline_doc);
+ }
+
+ /// This String has the property [nodoc].
+ String get nodoc => JS('String', '#.nodoc', this._jsObject);
+
+ void set nodoc(String nodoc) {
+ JS('void', '#.nodoc = #', this._jsObject, nodoc);
+ }
+
+ /// This double has the property [nocompile].
+ double get nocompile => JS('double', '#.nocompile', this._jsObject);
+
+ void set nocompile(double nocompile) {
+ JS('void', '#.nocompile = #', this._jsObject, nocompile);
+ }
+
+ /// This object has the property [instanceOf=fileEntry].
+ fileEntry get instance_of_tag => JS('fileEntry', '#.instance_of_tag', this._jsObject);
+
+ void set instance_of_tag(fileEntry instance_of_tag) {
+ JS('void', '#.instance_of_tag = #', this._jsObject, convertArgument(instance_of_tag));
+ }
+
+}
+
+/**
+ * Functions
+ */
+
+class API_tags {
+ /*
+ * API connection
+ */
+ Object _jsObject;
+ API_tags(this._jsObject) {
+ }
+}
diff --git a/chromium/tools/json_schema_compiler/dart_test/tags.idl b/chromium/tools/json_schema_compiler/dart_test/tags.idl
new file mode 100644
index 00000000000..7a029cd70e3
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/dart_test/tags.idl
@@ -0,0 +1,33 @@
+// 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.
+
+// A comment describing tags.
+namespace tags {
+ // This dictionary has the property [inline_doc].
+ [inline_doc] dictionary InlineDoc {
+ };
+
+ // This dictionary has the property [nodoc].
+ [nodoc] dictionary Nodoc {
+ };
+
+ // This dictionary has the property [nocompile].
+ [nocompile] dictionary Nocompile {
+ };
+
+ // This dictionary has no tags on the dictionary itself.
+ dictionary PlainDict {
+ // This int has the property [inline_doc].
+ [inline_doc] int inline_doc;
+
+ // This String has the property [nodoc].
+ [nodoc] String nodoc;
+
+ // This double has the property [nocompile].
+ [nocompile] double nocompile;
+
+ // This object has the property [instanceOf=fileEntry].
+ [instanceOf=fileEntry] object instance_of_tag;
+ };
+};
diff --git a/chromium/tools/json_schema_compiler/h_generator.py b/chromium/tools/json_schema_compiler/h_generator.py
new file mode 100644
index 00000000000..9a348fe51dc
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/h_generator.py
@@ -0,0 +1,397 @@
+# 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.
+
+from code import Code
+from model import PropertyType, Type
+import cpp_util
+import schema_util
+
+class HGenerator(object):
+ def __init__(self, type_generator, cpp_namespace):
+ self._type_generator = type_generator
+ self._cpp_namespace = cpp_namespace
+
+ def Generate(self, namespace):
+ return _Generator(namespace,
+ self._type_generator,
+ self._cpp_namespace).Generate()
+
+class _Generator(object):
+ """A .h generator for a namespace.
+ """
+ def __init__(self, namespace, cpp_type_generator, cpp_namespace):
+ self._namespace = namespace
+ self._type_helper = cpp_type_generator
+ self._cpp_namespace = cpp_namespace
+ self._target_namespace = (
+ self._type_helper.GetCppNamespaceName(self._namespace))
+ self._generate_error_messages = namespace.compiler_options.get(
+ 'generate_error_messages', False)
+
+ def Generate(self):
+ """Generates a Code object with the .h for a single namespace.
+ """
+ c = Code()
+ (c.Append(cpp_util.CHROMIUM_LICENSE)
+ .Append()
+ .Append(cpp_util.GENERATED_FILE_MESSAGE % self._namespace.source_file)
+ .Append()
+ )
+
+ ifndef_name = cpp_util.GenerateIfndefName(self._namespace.source_file_dir,
+ self._target_namespace)
+ (c.Append('#ifndef %s' % ifndef_name)
+ .Append('#define %s' % ifndef_name)
+ .Append()
+ .Append('#include <map>')
+ .Append('#include <string>')
+ .Append('#include <vector>')
+ .Append()
+ .Append('#include "base/basictypes.h"')
+ .Append('#include "base/logging.h"')
+ .Append('#include "base/memory/linked_ptr.h"')
+ .Append('#include "base/memory/scoped_ptr.h"')
+ .Append('#include "base/values.h"')
+ .Cblock(self._type_helper.GenerateIncludes())
+ .Append()
+ )
+
+ c.Concat(cpp_util.OpenNamespace(self._cpp_namespace))
+ # TODO(calamity): These forward declarations should be #includes to allow
+ # $ref types from other files to be used as required params. This requires
+ # some detangling of windows and tabs which will currently lead to circular
+ # #includes.
+ forward_declarations = (
+ self._type_helper.GenerateForwardDeclarations())
+ if not forward_declarations.IsEmpty():
+ (c.Append()
+ .Cblock(forward_declarations)
+ )
+
+ c.Concat(self._type_helper.GetNamespaceStart())
+ c.Append()
+ if self._namespace.properties:
+ (c.Append('//')
+ .Append('// Properties')
+ .Append('//')
+ .Append()
+ )
+ for property in self._namespace.properties.values():
+ property_code = self._type_helper.GeneratePropertyValues(
+ property,
+ 'extern const %(type)s %(name)s;')
+ if property_code:
+ c.Cblock(property_code)
+ if self._namespace.types:
+ (c.Append('//')
+ .Append('// Types')
+ .Append('//')
+ .Append()
+ .Cblock(self._GenerateTypes(self._FieldDependencyOrder(),
+ is_toplevel=True,
+ generate_typedefs=True))
+ )
+ if self._namespace.functions:
+ (c.Append('//')
+ .Append('// Functions')
+ .Append('//')
+ .Append()
+ )
+ for function in self._namespace.functions.values():
+ c.Cblock(self._GenerateFunction(function))
+ if self._namespace.events:
+ (c.Append('//')
+ .Append('// Events')
+ .Append('//')
+ .Append()
+ )
+ for event in self._namespace.events.values():
+ c.Cblock(self._GenerateEvent(event))
+ (c.Concat(self._type_helper.GetNamespaceEnd())
+ .Concat(cpp_util.CloseNamespace(self._cpp_namespace))
+ .Append('#endif // %s' % ifndef_name)
+ .Append()
+ )
+ return c
+
+ def _FieldDependencyOrder(self):
+ """Generates the list of types in the current namespace in an order in which
+ depended-upon types appear before types which depend on them.
+ """
+ dependency_order = []
+
+ def ExpandType(path, type_):
+ if type_ in path:
+ raise ValueError("Illegal circular dependency via cycle " +
+ ", ".join(map(lambda x: x.name, path + [type_])))
+ for prop in type_.properties.values():
+ if (prop.type_ == PropertyType.REF and
+ schema_util.GetNamespace(prop.ref_type) == self._namespace.name):
+ ExpandType(path + [type_], self._namespace.types[prop.ref_type])
+ if not type_ in dependency_order:
+ dependency_order.append(type_)
+
+ for type_ in self._namespace.types.values():
+ ExpandType([], type_)
+ return dependency_order
+
+ def _GenerateEnumDeclaration(self, enum_name, type_):
+ """Generate the declaration of a C++ enum.
+ """
+ c = Code()
+ c.Sblock('enum %s {' % enum_name)
+ c.Append(self._type_helper.GetEnumNoneValue(type_) + ',')
+ for value in type_.enum_values:
+ c.Append(self._type_helper.GetEnumValue(type_, value) + ',')
+ return c.Eblock('};')
+
+ def _GenerateFields(self, props):
+ """Generates the field declarations when declaring a type.
+ """
+ c = Code()
+ needs_blank_line = False
+ for prop in props:
+ if needs_blank_line:
+ c.Append()
+ needs_blank_line = True
+ if prop.description:
+ c.Comment(prop.description)
+ # ANY is a base::Value which is abstract and cannot be a direct member, so
+ # we always need to wrap it in a scoped_ptr.
+ is_ptr = prop.optional or prop.type_.property_type == PropertyType.ANY
+ (c.Append('%s %s;' % (
+ self._type_helper.GetCppType(prop.type_, is_ptr=is_ptr),
+ prop.unix_name))
+ )
+ return c
+
+ def _GenerateType(self, type_, is_toplevel=False, generate_typedefs=False):
+ """Generates a struct for |type_|.
+
+ |is_toplevel| implies that the type was declared in the "types" field
+ of an API schema. This determines the correct function
+ modifier(s).
+ |generate_typedefs| controls whether primitive types should be generated as
+ a typedef. This may not always be desired. If false,
+ primitive types are ignored.
+ """
+ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name))
+ c = Code()
+
+ if type_.functions:
+ # Wrap functions within types in the type's namespace.
+ (c.Append('namespace %s {' % classname)
+ .Append()
+ )
+ for function in type_.functions.values():
+ c.Cblock(self._GenerateFunction(function))
+ c.Append('} // namespace %s' % classname)
+ elif type_.property_type == PropertyType.ARRAY:
+ if generate_typedefs and type_.description:
+ c.Comment(type_.description)
+ c.Cblock(self._GenerateType(type_.item_type))
+ if generate_typedefs:
+ (c.Append('typedef std::vector<%s > %s;' % (
+ self._type_helper.GetCppType(type_.item_type),
+ classname))
+ )
+ elif type_.property_type == PropertyType.STRING:
+ if generate_typedefs:
+ if type_.description:
+ c.Comment(type_.description)
+ c.Append('typedef std::string %(classname)s;')
+ elif type_.property_type == PropertyType.ENUM:
+ if type_.description:
+ c.Comment(type_.description)
+ c.Sblock('enum %(classname)s {')
+ c.Append('%s,' % self._type_helper.GetEnumNoneValue(type_))
+ for value in type_.enum_values:
+ c.Append('%s,' % self._type_helper.GetEnumValue(type_, value))
+ # Top level enums are in a namespace scope so the methods shouldn't be
+ # static. On the other hand, those declared inline (e.g. in an object) do.
+ maybe_static = '' if is_toplevel else 'static '
+ (c.Eblock('};')
+ .Append()
+ .Append('%sstd::string ToString(%s as_enum);' %
+ (maybe_static, classname))
+ .Append('%s%s Parse%s(const std::string& as_string);' %
+ (maybe_static, classname, classname))
+ )
+ elif type_.property_type in (PropertyType.CHOICES,
+ PropertyType.OBJECT):
+ if type_.description:
+ c.Comment(type_.description)
+ (c.Sblock('struct %(classname)s {')
+ .Append('%(classname)s();')
+ .Append('~%(classname)s();')
+ )
+ if type_.origin.from_json:
+ (c.Append()
+ .Comment('Populates a %s object from a base::Value. Returns'
+ ' whether |out| was successfully populated.' % classname)
+ .Append('static bool Populate(%s);' % self._GenerateParams(
+ ('const base::Value& value', '%s* out' % classname)))
+ )
+ if is_toplevel:
+ (c.Append()
+ .Comment('Creates a %s object from a base::Value, or NULL on '
+ 'failure.' % classname)
+ .Append('static scoped_ptr<%s> FromValue(%s);' % (
+ classname, self._GenerateParams(('const base::Value& value',))))
+ )
+ if type_.origin.from_client:
+ value_type = ('base::Value'
+ if type_.property_type is PropertyType.CHOICES else
+ 'base::DictionaryValue')
+ (c.Append()
+ .Comment('Returns a new %s representing the serialized form of this '
+ '%s object.' % (value_type, classname))
+ .Append('scoped_ptr<%s> ToValue() const;' % value_type)
+ )
+ if type_.property_type == PropertyType.CHOICES:
+ # Choices are modelled with optional fields for each choice. Exactly one
+ # field of the choice is guaranteed to be set by the compiler.
+ c.Cblock(self._GenerateTypes(type_.choices))
+ c.Append('// Choices:')
+ for choice_type in type_.choices:
+ c.Append('%s as_%s;' % (
+ self._type_helper.GetCppType(choice_type, is_ptr=True),
+ choice_type.unix_name))
+ else:
+ properties = type_.properties.values()
+ (c.Append()
+ .Cblock(self._GenerateTypes(p.type_ for p in properties))
+ .Cblock(self._GenerateFields(properties)))
+ if type_.additional_properties is not None:
+ # Most additionalProperties actually have type "any", which is better
+ # modelled as a DictionaryValue rather than a map of string -> Value.
+ if type_.additional_properties.property_type == PropertyType.ANY:
+ c.Append('base::DictionaryValue additional_properties;')
+ else:
+ (c.Cblock(self._GenerateType(type_.additional_properties))
+ .Append('std::map<std::string, %s> additional_properties;' %
+ cpp_util.PadForGenerics(
+ self._type_helper.GetCppType(type_.additional_properties,
+ is_in_container=True)))
+ )
+ (c.Eblock()
+ .Append()
+ .Sblock(' private:')
+ .Append('DISALLOW_COPY_AND_ASSIGN(%(classname)s);')
+ .Eblock('};')
+ )
+ return c.Substitute({'classname': classname})
+
+ def _GenerateEvent(self, event):
+ """Generates the namespaces for an event.
+ """
+ c = Code()
+ # TODO(kalman): use event.unix_name not Classname.
+ event_namespace = cpp_util.Classname(event.name)
+ (c.Append('namespace %s {' % event_namespace)
+ .Append()
+ .Concat(self._GenerateEventNameConstant(event))
+ .Concat(self._GenerateCreateCallbackArguments(event))
+ .Eblock('} // namespace %s' % event_namespace)
+ )
+ return c
+
+ def _GenerateFunction(self, function):
+ """Generates the namespaces and structs for a function.
+ """
+ c = Code()
+ # TODO(kalman): Use function.unix_name not Classname here.
+ function_namespace = cpp_util.Classname(function.name)
+ """Windows has a #define for SendMessage, so to avoid any issues, we need
+ to not use the name.
+ """
+ if function_namespace == 'SendMessage':
+ function_namespace = 'PassMessage'
+ (c.Append('namespace %s {' % function_namespace)
+ .Append()
+ .Cblock(self._GenerateFunctionParams(function))
+ )
+ if function.callback:
+ c.Cblock(self._GenerateFunctionResults(function.callback))
+ c.Append('} // namespace %s' % function_namespace)
+ return c
+
+ def _GenerateFunctionParams(self, function):
+ """Generates the struct for passing parameters from JSON to a function.
+ """
+ if not function.params:
+ return Code()
+
+ c = Code()
+ (c.Sblock('struct Params {')
+ .Append('static scoped_ptr<Params> Create(%s);' % self._GenerateParams(
+ ('const base::ListValue& args',)))
+ .Append('~Params();')
+ .Append()
+ .Cblock(self._GenerateTypes(p.type_ for p in function.params))
+ .Cblock(self._GenerateFields(function.params))
+ .Eblock()
+ .Append()
+ .Sblock(' private:')
+ .Append('Params();')
+ .Append()
+ .Append('DISALLOW_COPY_AND_ASSIGN(Params);')
+ .Eblock('};')
+ )
+ return c
+
+ def _GenerateTypes(self, types, is_toplevel=False, generate_typedefs=False):
+ """Generate the structures required by a property such as OBJECT classes
+ and enums.
+ """
+ c = Code()
+ for type_ in types:
+ c.Cblock(self._GenerateType(type_,
+ is_toplevel=is_toplevel,
+ generate_typedefs=generate_typedefs))
+ return c
+
+ def _GenerateCreateCallbackArguments(self, function):
+ """Generates functions for passing parameters to a callback.
+ """
+ c = Code()
+ params = function.params
+ c.Cblock(self._GenerateTypes((p.type_ for p in params), is_toplevel=True))
+
+ declaration_list = []
+ for param in params:
+ if param.description:
+ c.Comment(param.description)
+ declaration_list.append(cpp_util.GetParameterDeclaration(
+ param, self._type_helper.GetCppType(param.type_)))
+ c.Append('scoped_ptr<base::ListValue> Create(%s);' %
+ ', '.join(declaration_list))
+ return c
+
+ def _GenerateEventNameConstant(self, event):
+ """Generates a constant string array for the event name.
+ """
+ c = Code()
+ c.Append('extern const char kEventName[]; // "%s.%s"' % (
+ self._namespace.name, event.name))
+ c.Append()
+ return c
+
+ def _GenerateFunctionResults(self, callback):
+ """Generates namespace for passing a function's result back.
+ """
+ c = Code()
+ (c.Append('namespace Results {')
+ .Append()
+ .Concat(self._GenerateCreateCallbackArguments(callback))
+ .Append('} // namespace Results')
+ )
+ return c
+
+ def _GenerateParams(self, params):
+ """Builds the parameter list for a function, given an array of parameters.
+ """
+ if self._generate_error_messages:
+ params += ('std::string* error = NULL',)
+ return ', '.join(str(p) for p in params)
diff --git a/chromium/tools/json_schema_compiler/highlighters/__init__.py b/chromium/tools/json_schema_compiler/highlighters/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/highlighters/__init__.py
diff --git a/chromium/tools/json_schema_compiler/highlighters/hilite_me_highlighter.py b/chromium/tools/json_schema_compiler/highlighters/hilite_me_highlighter.py
new file mode 100644
index 00000000000..af0484723cd
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/highlighters/hilite_me_highlighter.py
@@ -0,0 +1,30 @@
+# 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 urllib
+import urllib2
+
+class HiliteMeHighlighter(object):
+ """Highlighter that calls the http://hilite.me API to highlight code.
+ """
+ def GetCSS(self, style):
+ return ''
+
+ def GetCodeElement(self, code, style):
+ # Call hilite.me API to do syntax highlighting
+ return urllib2.urlopen('http://hilite.me/api',
+ urllib.urlencode([
+ ('code', code),
+ ('lexer', 'cpp'),
+ ('style', style),
+ ('linenos', 1)])
+ ).read()
+
+ def DisplayName(self):
+ return 'hilite.me (slow, requires internet)'
+
+ def GetStyles(self):
+ return ['monokai', 'manni', 'perldoc', 'borland', 'colorful', 'default',
+ 'murphy', 'vs', 'trac', 'tango', 'fruity', 'autumn', 'bw', 'emacs',
+ 'vim', 'pastie', 'friendly', 'native']
diff --git a/chromium/tools/json_schema_compiler/highlighters/none_highlighter.py b/chromium/tools/json_schema_compiler/highlighters/none_highlighter.py
new file mode 100644
index 00000000000..ac1cc2b4b26
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/highlighters/none_highlighter.py
@@ -0,0 +1,20 @@
+# 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 cgi
+
+class NoneHighlighter(object):
+ """Highlighter that just wraps code in a <pre>.
+ """
+ def GetCSS(self, style):
+ return ''
+
+ def GetCodeElement(self, code, style):
+ return '<pre>' + cgi.escape(code) + '</pre>'
+
+ def DisplayName(self):
+ return 'none'
+
+ def GetStyles(self):
+ return []
diff --git a/chromium/tools/json_schema_compiler/highlighters/pygments_highlighter.py b/chromium/tools/json_schema_compiler/highlighters/pygments_highlighter.py
new file mode 100644
index 00000000000..06abd33c790
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/highlighters/pygments_highlighter.py
@@ -0,0 +1,37 @@
+# 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 sys
+try:
+ import pygments
+ from pygments.lexers import CppLexer
+ from pygments.formatters import HtmlFormatter
+ PYGMENTS_IMPORTED = True
+except ImportError:
+ print('It appears that Pygments is not installed. '
+ 'Can be installed using easy_install Pygments or from http://pygments.org.')
+ PYGMENTS_IMPORTED = False
+
+class PygmentsHighlighter(object):
+ def __init__(self):
+ if not PYGMENTS_IMPORTED:
+ raise ImportError('Pygments not installed')
+
+ """Highlighter that uses the python pygments library to highlight code.
+ """
+ def GetCSS(self, style):
+ formatter = HtmlFormatter(linenos=True,
+ style=pygments.styles.get_style_by_name(style))
+ return formatter.get_style_defs('.highlight')
+
+ def GetCodeElement(self, code, style):
+ formatter = HtmlFormatter(linenos=True,
+ style=pygments.styles.get_style_by_name(style))
+ return pygments.highlight(code, CppLexer(), formatter)
+
+ def DisplayName(self):
+ return 'pygments' + ('' if PYGMENTS_IMPORTED else ' (not installed)')
+
+ def GetStyles(self):
+ return list(pygments.styles.get_all_styles())
diff --git a/chromium/tools/json_schema_compiler/idl_schema.py b/chromium/tools/json_schema_compiler/idl_schema.py
new file mode 100644
index 00000000000..0556236be4f
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/idl_schema.py
@@ -0,0 +1,417 @@
+#! /usr/bin/env python
+# 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 itertools
+import json
+import os.path
+import re
+import sys
+
+from json_parse import OrderedDict
+import schema_util
+
+# This file is a peer to json_schema.py. Each of these files understands a
+# certain format describing APIs (either JSON or IDL), reads files written
+# in that format into memory, and emits them as a Python array of objects
+# corresponding to those APIs, where the objects are formatted in a way that
+# the JSON schema compiler understands. compiler.py drives both idl_schema.py
+# and json_schema.py.
+
+# idl_parser expects to be able to import certain files in its directory,
+# so let's set things up the way it wants.
+_idl_generators_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ os.pardir, os.pardir, 'ppapi', 'generators')
+if _idl_generators_path in sys.path:
+ import idl_parser
+else:
+ sys.path.insert(0, _idl_generators_path)
+ try:
+ import idl_parser
+ finally:
+ sys.path.pop(0)
+
+def ProcessComment(comment):
+ '''
+ Convert a comment into a parent comment and a list of parameter comments.
+
+ Function comments are of the form:
+ Function documentation. May contain HTML and multiple lines.
+
+ |arg1_name|: Description of arg1. Use <var>argument</var> to refer
+ to other arguments.
+ |arg2_name|: Description of arg2...
+
+ Newlines are removed, and leading and trailing whitespace is stripped.
+
+ Args:
+ comment: The string from a Comment node.
+
+ Returns: A tuple that looks like:
+ (
+ "The processed comment, minus all |parameter| mentions.",
+ {
+ 'parameter_name_1': "The comment that followed |parameter_name_1|:",
+ ...
+ }
+ )
+ '''
+ # Find all the parameter comments of the form '|name|: comment'.
+ parameter_starts = list(re.finditer(r' *\|([^|]*)\| *: *', comment))
+
+ # Get the parent comment (everything before the first parameter comment.
+ first_parameter_location = (parameter_starts[0].start()
+ if parameter_starts else len(comment))
+ parent_comment = comment[:first_parameter_location]
+
+ # We replace \n\n with <br/><br/> here and below, because the documentation
+ # needs to know where the newlines should be, and this is easier than
+ # escaping \n.
+ parent_comment = (parent_comment.strip().replace('\n\n', '<br/><br/>')
+ .replace('\n', ''))
+
+ params = OrderedDict()
+ for (cur_param, next_param) in itertools.izip_longest(parameter_starts,
+ parameter_starts[1:]):
+ param_name = cur_param.group(1)
+
+ # A parameter's comment goes from the end of its introduction to the
+ # beginning of the next parameter's introduction.
+ param_comment_start = cur_param.end()
+ param_comment_end = next_param.start() if next_param else len(comment)
+ params[param_name] = (comment[param_comment_start:param_comment_end
+ ].strip().replace('\n\n', '<br/><br/>')
+ .replace('\n', ''))
+ return (parent_comment, params)
+
+class Callspec(object):
+ '''
+ Given a Callspec node representing an IDL function declaration, converts into
+ a tuple:
+ (name, list of function parameters, return type)
+ '''
+ def __init__(self, callspec_node, comment):
+ self.node = callspec_node
+ self.comment = comment
+
+ def process(self, callbacks):
+ parameters = []
+ return_type = None
+ if self.node.GetProperty('TYPEREF') not in ('void', None):
+ return_type = Typeref(self.node.GetProperty('TYPEREF'),
+ self.node,
+ {'name': self.node.GetName()}).process(callbacks)
+ # The IDL parser doesn't allow specifying return types as optional.
+ # Instead we infer any object return values to be optional.
+ # TODO(asargent): fix the IDL parser to support optional return types.
+ if return_type.get('type') == 'object' or '$ref' in return_type:
+ return_type['optional'] = True;
+ for node in self.node.children:
+ parameter = Param(node).process(callbacks)
+ if parameter['name'] in self.comment:
+ parameter['description'] = self.comment[parameter['name']]
+ parameters.append(parameter)
+ return (self.node.GetName(), parameters, return_type)
+
+class Param(object):
+ '''
+ Given a Param node representing a function parameter, converts into a Python
+ dictionary that the JSON schema compiler expects to see.
+ '''
+ def __init__(self, param_node):
+ self.node = param_node
+
+ def process(self, callbacks):
+ return Typeref(self.node.GetProperty('TYPEREF'),
+ self.node,
+ {'name': self.node.GetName()}).process(callbacks)
+
+class Dictionary(object):
+ '''
+ Given an IDL Dictionary node, converts into a Python dictionary that the JSON
+ schema compiler expects to see.
+ '''
+ def __init__(self, dictionary_node):
+ self.node = dictionary_node
+
+ def process(self, callbacks):
+ properties = OrderedDict()
+ for node in self.node.children:
+ if node.cls == 'Member':
+ k, v = Member(node).process(callbacks)
+ properties[k] = v
+ result = {'id': self.node.GetName(),
+ 'properties': properties,
+ 'type': 'object'}
+ if self.node.GetProperty('inline_doc'):
+ result['inline_doc'] = True
+ elif self.node.GetProperty('noinline_doc'):
+ result['noinline_doc'] = True
+ return result
+
+
+class Member(object):
+ '''
+ Given an IDL dictionary or interface member, converts into a name/value pair
+ where the value is a Python dictionary that the JSON schema compiler expects
+ to see.
+ '''
+ def __init__(self, member_node):
+ self.node = member_node
+
+ def process(self, callbacks):
+ properties = OrderedDict()
+ name = self.node.GetName()
+ for property_name in ('OPTIONAL', 'nodoc', 'nocompile', 'nodart'):
+ if self.node.GetProperty(property_name):
+ properties[property_name.lower()] = True
+ for option_name, sanitizer in [
+ ('maxListeners', int),
+ ('supportsFilters', lambda s: s == 'true'),
+ ('supportsListeners', lambda s: s == 'true'),
+ ('supportsRules', lambda s: s == 'true')]:
+ if self.node.GetProperty(option_name):
+ if 'options' not in properties:
+ properties['options'] = {}
+ properties['options'][option_name] = sanitizer(self.node.GetProperty(
+ option_name))
+ is_function = False
+ parameter_comments = OrderedDict()
+ for node in self.node.children:
+ 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
+ properties['name'] = name
+ if is_function:
+ properties['type'] = 'function'
+ else:
+ properties = Typeref(self.node.GetProperty('TYPEREF'),
+ self.node, properties).process(callbacks)
+ 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
+ return name, properties
+
+class Typeref(object):
+ '''
+ Given a TYPEREF property representing the type of dictionary member or
+ function parameter, converts into a Python dictionary that the JSON schema
+ compiler expects to see.
+ '''
+ def __init__(self, typeref, parent, additional_properties=OrderedDict()):
+ self.typeref = typeref
+ self.parent = parent
+ self.additional_properties = additional_properties
+
+ def process(self, callbacks):
+ properties = self.additional_properties
+ result = properties
+
+ if self.parent.GetProperty('OPTIONAL', False):
+ properties['optional'] = True
+
+ # The IDL parser denotes array types by adding a child 'Array' node onto
+ # the Param node in the Callspec.
+ for sibling in self.parent.GetChildren():
+ if sibling.cls == 'Array' and sibling.GetName() == self.parent.GetName():
+ properties['type'] = 'array'
+ properties['items'] = OrderedDict()
+ properties = properties['items']
+ break
+
+ if self.typeref == 'DOMString':
+ properties['type'] = 'string'
+ elif self.typeref == 'boolean':
+ properties['type'] = 'boolean'
+ elif self.typeref == 'double':
+ properties['type'] = 'number'
+ elif self.typeref == 'long':
+ properties['type'] = 'integer'
+ elif self.typeref == 'any':
+ properties['type'] = 'any'
+ elif self.typeref == 'object':
+ properties['type'] = 'object'
+ if 'additionalProperties' not in properties:
+ properties['additionalProperties'] = OrderedDict()
+ properties['additionalProperties']['type'] = 'any'
+ instance_of = self.parent.GetProperty('instanceOf')
+ if instance_of:
+ properties['isInstanceOf'] = instance_of
+ elif self.typeref == 'ArrayBuffer':
+ properties['type'] = 'binary'
+ properties['isInstanceOf'] = 'ArrayBuffer'
+ elif self.typeref == 'FileEntry':
+ properties['type'] = 'object'
+ properties['isInstanceOf'] = 'FileEntry'
+ if 'additionalProperties' not in properties:
+ properties['additionalProperties'] = OrderedDict()
+ properties['additionalProperties']['type'] = 'any'
+ elif self.typeref is None:
+ properties['type'] = 'function'
+ else:
+ if self.typeref in callbacks:
+ # Do not override name and description if they are already specified.
+ name = properties.get('name', None)
+ description = properties.get('description', None)
+ properties.update(callbacks[self.typeref])
+ if description is not None:
+ properties['description'] = description
+ if name is not None:
+ properties['name'] = name
+ else:
+ properties['$ref'] = self.typeref
+ return result
+
+
+class Enum(object):
+ '''
+ Given an IDL Enum node, converts into a Python dictionary that the JSON
+ schema compiler expects to see.
+ '''
+ def __init__(self, enum_node):
+ self.node = enum_node
+ self.description = ''
+
+ def process(self, callbacks):
+ enum = []
+ for node in self.node.children:
+ if node.cls == 'EnumItem':
+ enum.append(node.GetName())
+ elif node.cls == 'Comment':
+ self.description = ProcessComment(node.GetName())[0]
+ else:
+ sys.exit('Did not process %s %s' % (node.cls, node))
+ result = {'id' : self.node.GetName(),
+ 'description': self.description,
+ 'type': 'string',
+ 'enum': enum}
+ for property_name in ('inline_doc', 'noinline_doc', 'nodoc'):
+ if self.node.GetProperty(property_name):
+ result[property_name] = True
+ return result
+
+
+class Namespace(object):
+ '''
+ Given an IDLNode representing an IDL namespace, converts into a Python
+ dictionary that the JSON schema compiler expects to see.
+ '''
+
+ def __init__(self, namespace_node, description, nodoc=False, internal=False):
+ self.namespace = namespace_node
+ self.nodoc = nodoc
+ self.internal = internal
+ self.events = []
+ self.functions = []
+ self.types = []
+ self.callbacks = OrderedDict()
+ self.description = description
+
+ def process(self):
+ for node in self.namespace.children:
+ if node.cls == 'Dictionary':
+ self.types.append(Dictionary(node).process(self.callbacks))
+ elif node.cls == 'Callback':
+ k, v = Member(node).process(self.callbacks)
+ self.callbacks[k] = v
+ elif node.cls == 'Interface' and node.GetName() == 'Functions':
+ self.functions = self.process_interface(node)
+ elif node.cls == 'Interface' and node.GetName() == 'Events':
+ self.events = self.process_interface(node)
+ elif node.cls == 'Enum':
+ self.types.append(Enum(node).process(self.callbacks))
+ else:
+ sys.exit('Did not process %s %s' % (node.cls, node))
+ return {'namespace': self.namespace.GetName(),
+ 'description': self.description,
+ 'nodoc': self.nodoc,
+ 'types': self.types,
+ 'functions': self.functions,
+ 'internal': self.internal,
+ 'events': self.events}
+
+ def process_interface(self, node):
+ members = []
+ for member in node.children:
+ if member.cls == 'Member':
+ name, properties = Member(member).process(self.callbacks)
+ members.append(properties)
+ return members
+
+class IDLSchema(object):
+ '''
+ Given a list of IDLNodes and IDLAttributes, converts into a Python list
+ of api_defs that the JSON schema compiler expects to see.
+ '''
+
+ def __init__(self, idl):
+ self.idl = idl
+
+ def process(self):
+ namespaces = []
+ nodoc = False
+ internal = False
+ description = None
+ for node in self.idl:
+ if node.cls == 'Namespace':
+ if not description:
+ # TODO(kalman): Go back to throwing an error here.
+ print('%s must have a namespace-level comment. This will '
+ 'appear on the API summary page.' % node.GetName())
+ description = ''
+ namespace = Namespace(node, description, nodoc, internal)
+ namespaces.append(namespace.process())
+ nodoc = False
+ internal = False
+ elif node.cls == 'Copyright':
+ continue
+ elif node.cls == 'Comment':
+ description = node.GetName()
+ elif node.cls == 'ExtAttribute':
+ if node.name == 'nodoc':
+ nodoc = bool(node.value)
+ elif node.name == 'internal':
+ internal = bool(node.value)
+ else:
+ continue
+ else:
+ sys.exit('Did not process %s %s' % (node.cls, node))
+ return namespaces
+
+def Load(filename):
+ '''
+ Given the filename of an IDL file, parses it and returns an equivalent
+ Python dictionary in a format that the JSON schema compiler expects to see.
+ '''
+
+ f = open(filename, 'r')
+ contents = f.read()
+ f.close()
+
+ idl = idl_parser.IDLParser().ParseData(contents, filename)
+ idl_schema = IDLSchema(idl)
+ return idl_schema.process()
+
+def Main():
+ '''
+ Dump a json serialization of parse result for the IDL files whose names
+ were passed in on the command line.
+ '''
+ for filename in sys.argv[1:]:
+ schema = Load(filename)
+ print json.dumps(schema, indent=2)
+
+if __name__ == '__main__':
+ Main()
diff --git a/chromium/tools/json_schema_compiler/idl_schema_test.py b/chromium/tools/json_schema_compiler/idl_schema_test.py
new file mode 100755
index 00000000000..6adbbd7e288
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/idl_schema_test.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+# 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 idl_schema
+import unittest
+
+def getFunction(schema, name):
+ for item in schema['functions']:
+ if item['name'] == name:
+ return item
+ raise KeyError('Missing function %s' % name)
+
+def getParams(schema, name):
+ function = getFunction(schema, name)
+ return function['parameters']
+
+def getType(schema, id):
+ for item in schema['types']:
+ if item['id'] == id:
+ return item
+
+class IdlSchemaTest(unittest.TestCase):
+ def setUp(self):
+ loaded = idl_schema.Load('test/idl_basics.idl')
+ self.assertEquals(1, len(loaded))
+ self.assertEquals('idl_basics', loaded[0]['namespace'])
+ self.idl_basics = loaded[0]
+
+ def testSimpleCallbacks(self):
+ schema = self.idl_basics
+ expected = [{'type':'function', 'name':'cb', 'parameters':[]}]
+ self.assertEquals(expected, getParams(schema, 'function4'))
+
+ expected = [{'type':'function', 'name':'cb',
+ 'parameters':[{'name':'x', 'type':'integer'}]}]
+ self.assertEquals(expected, getParams(schema, 'function5'))
+
+ expected = [{'type':'function', 'name':'cb',
+ 'parameters':[{'name':'arg', '$ref':'MyType1'}]}]
+ self.assertEquals(expected, getParams(schema, 'function6'))
+
+ def testCallbackWithArrayArgument(self):
+ schema = self.idl_basics
+ expected = [{'type':'function', 'name':'cb',
+ 'parameters':[{'name':'arg', 'type':'array',
+ 'items':{'$ref':'MyType2'}}]}]
+ self.assertEquals(expected, getParams(schema, 'function12'))
+
+
+ def testArrayOfCallbacks(self):
+ schema = idl_schema.Load('test/idl_callback_arrays.idl')[0]
+ expected = [{'type':'array', 'name':'callbacks',
+ 'items':{'type':'function', 'name':'MyCallback',
+ 'parameters':[{'type':'integer', 'name':'x'}]}}]
+ self.assertEquals(expected, getParams(schema, 'whatever'))
+
+ def testLegalValues(self):
+ self.assertEquals({
+ 'x': {'name': 'x', 'type': 'integer', 'enum': [1,2],
+ 'description': 'This comment tests "double-quotes".'},
+ 'y': {'name': 'y', 'type': 'string'},
+ 'z': {'name': 'z', 'type': 'string'},
+ 'a': {'name': 'a', 'type': 'string'},
+ 'b': {'name': 'b', 'type': 'string'},
+ 'c': {'name': 'c', 'type': 'string'}},
+ getType(self.idl_basics, 'MyType1')['properties'])
+
+ def testMemberOrdering(self):
+ self.assertEquals(
+ ['x', 'y', 'z', 'a', 'b', 'c'],
+ getType(self.idl_basics, 'MyType1')['properties'].keys())
+
+ def testEnum(self):
+ schema = self.idl_basics
+ expected = {'enum': ['name1', 'name2'], 'description': 'Enum description',
+ 'type': 'string', 'id': 'EnumType'}
+ self.assertEquals(expected, getType(schema, expected['id']))
+
+ expected = [{'name':'type', '$ref':'EnumType'},
+ {'type':'function', 'name':'cb',
+ 'parameters':[{'name':'type', '$ref':'EnumType'}]}]
+ self.assertEquals(expected, getParams(schema, 'function13'))
+
+ expected = [{'items': {'$ref': 'EnumType'}, 'name': 'types',
+ 'type': 'array'}]
+ self.assertEquals(expected, getParams(schema, 'function14'))
+
+ def testNoCompile(self):
+ schema = self.idl_basics
+ func = getFunction(schema, 'function15')
+ self.assertTrue(func is not None)
+ self.assertTrue(func['nocompile'])
+
+ def testNoDocOnEnum(self):
+ schema = self.idl_basics
+ enum_with_nodoc = getType(schema, 'EnumTypeWithNoDoc')
+ self.assertTrue(enum_with_nodoc is not None)
+ self.assertTrue(enum_with_nodoc['nodoc'])
+
+ def testInternalNamespace(self):
+ idl_basics = self.idl_basics
+ self.assertEquals('idl_basics', idl_basics['namespace'])
+ self.assertTrue(idl_basics['internal'])
+ self.assertFalse(idl_basics['nodoc'])
+
+ def testCallbackComment(self):
+ schema = self.idl_basics
+ self.assertEquals('A comment on a callback.',
+ getParams(schema, 'function16')[0]['description'])
+ self.assertEquals(
+ 'A parameter.',
+ getParams(schema, 'function16')[0]['parameters'][0]['description'])
+ self.assertEquals(
+ 'Just a parameter comment, with no comment on the callback.',
+ getParams(schema, 'function17')[0]['parameters'][0]['description'])
+ self.assertEquals(
+ 'Override callback comment.',
+ getParams(schema, 'function18')[0]['description'])
+
+ def testFunctionComment(self):
+ schema = self.idl_basics
+ func = getFunction(schema, 'function3')
+ self.assertEquals(('This comment should appear in the documentation, '
+ 'despite occupying multiple lines.'),
+ func['description'])
+ self.assertEquals(
+ [{'description': ('So should this comment about the argument. '
+ '<em>HTML</em> is fine too.'),
+ 'name': 'arg',
+ '$ref': 'MyType1'}],
+ func['parameters'])
+ func = getFunction(schema, 'function4')
+ self.assertEquals(('This tests if "double-quotes" are escaped correctly.'
+ '<br/><br/> It also tests a comment with two newlines.'),
+ func['description'])
+
+ def testReservedWords(self):
+ schema = idl_schema.Load('test/idl_reserved_words.idl')[0]
+
+ foo_type = getType(schema, 'Foo')
+ self.assertEquals(['float', 'DOMString'], foo_type['enum'])
+
+ enum_type = getType(schema, 'enum')
+ self.assertEquals(['callback', 'namespace'], enum_type['enum'])
+
+ dictionary = getType(schema, 'dictionary');
+ self.assertEquals('integer', dictionary['properties']['long']['type'])
+
+ mytype = getType(schema, 'MyType')
+ self.assertEquals('string', mytype['properties']['interface']['type'])
+
+ params = getParams(schema, 'static')
+ self.assertEquals('Foo', params[0]['$ref'])
+ self.assertEquals('enum', params[1]['$ref'])
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/tools/json_schema_compiler/json_parse.py b/chromium/tools/json_schema_compiler/json_parse.py
new file mode 100644
index 00000000000..9502e91d5c2
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/json_parse.py
@@ -0,0 +1,61 @@
+# 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 json
+import logging
+import os
+import sys
+
+_FILE_PATH = os.path.dirname(os.path.realpath(__file__))
+_SYS_PATH = sys.path[:]
+try:
+ _COMMENT_EATER_PATH = os.path.join(
+ _FILE_PATH, os.pardir, 'json_comment_eater')
+ sys.path.insert(0, _COMMENT_EATER_PATH)
+ import json_comment_eater
+finally:
+ sys.path = _SYS_PATH
+
+try:
+ from collections import OrderedDict
+
+ # Successfully imported, so we're running Python >= 2.7, and json.loads
+ # supports object_pairs_hook.
+ def Parse(json_str):
+ return json.loads(json_comment_eater.Nom(json_str),
+ object_pairs_hook=OrderedDict)
+
+except ImportError:
+ # Failed to import, so we're running Python < 2.7, and json.loads doesn't
+ # support object_pairs_hook. simplejson however does, but it's slow.
+ #
+ # TODO(cduvall/kalman): Refuse to start the docs server in this case, but
+ # let json-schema-compiler do its thing.
+ #logging.warning('Using simplejson to parse, this might be slow! Upgrade to '
+ # 'Python 2.7.')
+
+ _SYS_PATH = sys.path[:]
+ try:
+ _SIMPLE_JSON_PATH = os.path.join(_FILE_PATH,
+ os.pardir,
+ os.pardir,
+ 'third_party')
+ sys.path.insert(0, _SIMPLE_JSON_PATH)
+ # Add this path in case this is being used in the docs server.
+ sys.path.insert(0, os.path.join(_FILE_PATH,
+ os.pardir,
+ os.pardir,
+ 'third_party',
+ 'json_schema_compiler'))
+ import simplejson
+ from simplejson import OrderedDict
+ finally:
+ sys.path = _SYS_PATH
+
+ def Parse(json_str):
+ return simplejson.loads(json_comment_eater.Nom(json_str),
+ object_pairs_hook=OrderedDict)
+
+def IsDict(item):
+ return isinstance(item, (dict, OrderedDict))
diff --git a/chromium/tools/json_schema_compiler/json_schema.py b/chromium/tools/json_schema_compiler/json_schema.py
new file mode 100644
index 00000000000..0fa1ba57e96
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/json_schema.py
@@ -0,0 +1,50 @@
+# 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
+import sys
+
+import json_parse
+import schema_util
+
+def DeleteNodes(item, delete_key):
+ """Deletes the given nodes in item, recursively, that have |delete_key| as
+ an attribute.
+ """
+ def HasKey(thing):
+ return json_parse.IsDict(thing) and thing.get(delete_key, False)
+
+ if json_parse.IsDict(item):
+ toDelete = []
+ for key, value in item.items():
+ if HasKey(value):
+ toDelete.append(key)
+ else:
+ DeleteNodes(value, delete_key)
+ for key in toDelete:
+ del item[key]
+ elif type(item) == list:
+ item[:] = [DeleteNodes(thing, delete_key)
+ for thing in item if not HasKey(thing)]
+
+ return item
+
+def Load(filename):
+ with open(filename, 'r') as handle:
+ schemas = json_parse.Parse(handle.read())
+ return schemas
+
+# A dictionary mapping |filename| to the object resulting from loading the JSON
+# at |filename|.
+_cache = {}
+
+def CachedLoad(filename):
+ """Equivalent to Load(filename), but caches results for subsequent calls"""
+ if filename not in _cache:
+ _cache[filename] = Load(filename)
+ # Return a copy of the object so that any changes a caller makes won't affect
+ # the next caller.
+ return copy.deepcopy(_cache[filename])
+
diff --git a/chromium/tools/json_schema_compiler/json_schema_test.py b/chromium/tools/json_schema_compiler/json_schema_test.py
new file mode 100755
index 00000000000..11ba61e443a
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/json_schema_test.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# 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 json_schema
+import json_schema_test
+import unittest
+
+class JsonSchemaUnittest(unittest.TestCase):
+ def testNocompile(self):
+ compiled = [
+ {
+ "namespace": "compile",
+ "description": "The compile API.",
+ "functions": [],
+ "types": {}
+ },
+
+ {
+ "namespace": "functions",
+ "description": "The functions API.",
+ "functions": [
+ {
+ "id": "two"
+ },
+ {
+ "id": "four"
+ }
+ ],
+
+ "types": {
+ "one": { "key": "value" }
+ }
+ },
+
+ {
+ "namespace": "types",
+ "description": "The types API.",
+ "functions": [
+ { "id": "one" }
+ ],
+ "types": {
+ "two": {
+ "key": "value"
+ },
+ "four": {
+ "key": "value"
+ }
+ }
+ },
+
+ {
+ "namespace": "nested",
+ "description": "The nested API.",
+ "properties": {
+ "sync": {
+ "functions": [
+ {
+ "id": "two"
+ },
+ {
+ "id": "four"
+ }
+ ],
+ "types": {
+ "two": {
+ "key": "value"
+ },
+ "four": {
+ "key": "value"
+ }
+ }
+ }
+ }
+ }
+ ]
+
+ schema = json_schema.CachedLoad('test/json_schema_test.json')
+ self.assertEquals(compiled, json_schema.DeleteNodes(schema, 'nocompile'))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/tools/json_schema_compiler/memoize.py b/chromium/tools/json_schema_compiler/memoize.py
new file mode 100644
index 00000000000..1402a6ecd80
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/memoize.py
@@ -0,0 +1,13 @@
+# Copyright 2013 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.
+
+def memoize(fn):
+ '''Decorates |fn| to memoize.
+ '''
+ memory = {}
+ def impl(*args):
+ if args not in memory:
+ memory[args] = fn(*args)
+ return memory[args]
+ return impl
diff --git a/chromium/tools/json_schema_compiler/model.py b/chromium/tools/json_schema_compiler/model.py
new file mode 100644
index 00000000000..50a9c581403
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/model.py
@@ -0,0 +1,492 @@
+# 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 os.path
+
+from json_parse import OrderedDict
+from memoize import memoize
+
+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
+ - |description| the description 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 not empty if
+ |include_compiler_options| is True
+ """
+ def __init__(self, json, source_file, include_compiler_options=False):
+ self.name = json['namespace']
+ if 'description' not in json:
+ # TODO(kalman): Go back to throwing an error here.
+ print('%s must have a "description" field. This will appear '
+ 'on the API summary page.' % self.name)
+ json['description'] = ''
+ self.description = json['description']
+ 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)
+ self.compiler_options = (json.get('compiler_options', {})
+ if include_compiler_options else {})
+
+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
+ def generate_type_name(type_json):
+ if 'items' in type_json:
+ return '%ss' % generate_type_name(type_json['items'])
+ if '$ref' in type_json:
+ return type_json['$ref']
+ if 'type' in type_json:
+ return type_json['type']
+ return None
+ self.choices = [
+ Type(self,
+ generate_type_name(choice) or 'choice%s' % i,
+ choice,
+ namespace,
+ origin)
+ for i, choice in enumerate(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
+ - |returns| the return type of the function; None if the function does not
+ return a value
+ """
+ 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(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 = Type(self,
+ '%sReturnType' % name,
+ 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
+ """
+ def __init__(self, parent, name, json, namespace, origin):
+ """Creates a Property from JSON.
+ """
+ self.parent = parent
+ self.name = name
+ self._unix_name = UnixName(self.name)
+ self._unix_name_used = False
+ self.origin = origin
+ self.simple_name = _StripNamespace(self.name, namespace)
+ self.description = json.get('description', None)
+ self.optional = json.get('optional', None)
+ self.instance_of = json.get('isInstanceOf', None)
+
+ # 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'))
+
+ self.value = None
+ if 'value' in json and is_allowed_value:
+ self.value = json['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(self.value, int):
+ json['type'] = 'integer'
+ elif isinstance(self.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(self.value))
+
+ self.type_ = Type(parent, name, json, namespace, origin)
+
+ 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 __eq__(self, other):
+ return type(other) == type(self) and other.name == self.name
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __repr__(self):
+ return self.name
+
+ def __str__(self):
+ return repr(self)
+
+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.
+ """
+ ANY = _PropertyTypeInfo(False, "any")
+ ARRAY = _PropertyTypeInfo(False, "array")
+ BINARY = _PropertyTypeInfo(False, "binary")
+ BOOLEAN = _PropertyTypeInfo(True, "boolean")
+ CHOICES = _PropertyTypeInfo(False, "choices")
+ DOUBLE = _PropertyTypeInfo(True, "double")
+ ENUM = _PropertyTypeInfo(False, "enum")
+ FUNCTION = _PropertyTypeInfo(False, "function")
+ INT64 = _PropertyTypeInfo(True, "int64")
+ INTEGER = _PropertyTypeInfo(True, "integer")
+ OBJECT = _PropertyTypeInfo(False, "object")
+ REF = _PropertyTypeInfo(False, "ref")
+ STRING = _PropertyTypeInfo(True, "string")
+
+@memoize
+def UnixName(name):
+ '''Returns the unix_style name for a given lowerCamelCase string.
+ '''
+ unix_name = []
+ for i, c in enumerate(name):
+ if c.isupper() and i > 0 and name[i - 1] != '_':
+ # Replace lowerUpper with lower_Upper.
+ if name[i - 1].islower():
+ unix_name.append('_')
+ # Replace ACMEWidgets with ACME_Widgets
+ elif i + 1 < len(name) and name[i + 1].islower():
+ unix_name.append('_')
+ if c == '.':
+ # Replace hello.world with hello_world.
+ unix_name.append('_')
+ else:
+ # Everything is lowercase.
+ unix_name.append(c.lower())
+ return ''.join(unix_name)
+
+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(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
diff --git a/chromium/tools/json_schema_compiler/model_test.py b/chromium/tools/json_schema_compiler/model_test.py
new file mode 100755
index 00000000000..5003af91e24
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/model_test.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+# 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.
+
+from json_schema import CachedLoad
+import model
+import unittest
+
+class ModelTest(unittest.TestCase):
+ def setUp(self):
+ self.model = model.Model()
+ self.permissions_json = CachedLoad('test/permissions.json')
+ self.model.AddNamespace(self.permissions_json[0],
+ 'path/to/permissions.json')
+ self.permissions = self.model.namespaces.get('permissions')
+ self.windows_json = CachedLoad('test/windows.json')
+ self.model.AddNamespace(self.windows_json[0],
+ 'path/to/window.json')
+ self.windows = self.model.namespaces.get('windows')
+ self.tabs_json = CachedLoad('test/tabs.json')
+ self.model.AddNamespace(self.tabs_json[0],
+ 'path/to/tabs.json')
+ self.tabs = self.model.namespaces.get('tabs')
+
+ def testNamespaces(self):
+ self.assertEquals(3, len(self.model.namespaces))
+ self.assertTrue(self.permissions)
+
+ def testHasFunctions(self):
+ self.assertEquals(["contains", "getAll", "remove", "request"],
+ sorted(self.permissions.functions.keys()))
+
+ def testHasTypes(self):
+ self.assertEquals(['Tab'], self.tabs.types.keys())
+ self.assertEquals(['Permissions'], self.permissions.types.keys())
+ self.assertEquals(['Window'], self.windows.types.keys())
+
+ def testHasProperties(self):
+ self.assertEquals(["active", "favIconUrl", "highlighted", "id",
+ "incognito", "index", "pinned", "selected", "status", "title", "url",
+ "windowId"],
+ sorted(self.tabs.types['Tab'].properties.keys()))
+
+ def testProperties(self):
+ string_prop = self.tabs.types['Tab'].properties['status']
+ self.assertEquals(model.PropertyType.STRING,
+ string_prop.type_.property_type)
+ integer_prop = self.tabs.types['Tab'].properties['id']
+ self.assertEquals(model.PropertyType.INTEGER,
+ integer_prop.type_.property_type)
+ array_prop = self.windows.types['Window'].properties['tabs']
+ self.assertEquals(model.PropertyType.ARRAY,
+ array_prop.type_.property_type)
+ self.assertEquals(model.PropertyType.REF,
+ array_prop.type_.item_type.property_type)
+ self.assertEquals('tabs.Tab', array_prop.type_.item_type.ref_type)
+ object_prop = self.tabs.functions['query'].params[0]
+ self.assertEquals(model.PropertyType.OBJECT,
+ object_prop.type_.property_type)
+ self.assertEquals(
+ ["active", "highlighted", "pinned", "status", "title", "url",
+ "windowId", "windowType"],
+ sorted(object_prop.type_.properties.keys()))
+
+ def testChoices(self):
+ self.assertEquals(model.PropertyType.CHOICES,
+ self.tabs.functions['move'].params[0].type_.property_type)
+
+ def testPropertyNotImplemented(self):
+ (self.permissions_json[0]['types'][0]
+ ['properties']['permissions']['type']) = 'something'
+ self.assertRaises(model.ParseException, self.model.AddNamespace,
+ self.permissions_json[0], 'path/to/something.json')
+
+ def testDescription(self):
+ self.assertFalse(
+ self.permissions.functions['contains'].params[0].description)
+ self.assertEquals('True if the extension has the specified permissions.',
+ self.permissions.functions['contains'].callback.params[0].description)
+
+ def testPropertyUnixName(self):
+ param = self.tabs.functions['move'].params[0]
+ self.assertEquals('tab_ids', param.unix_name)
+
+ def testUnixName(self):
+ expectations = {
+ 'foo': 'foo',
+ 'fooBar': 'foo_bar',
+ 'fooBarBaz': 'foo_bar_baz',
+ 'fooBARBaz': 'foo_bar_baz',
+ 'fooBAR': 'foo_bar',
+ 'FOO': 'foo',
+ 'FOOBar': 'foo_bar',
+ 'foo.bar': 'foo_bar',
+ 'foo.BAR': 'foo_bar',
+ 'foo.barBAZ': 'foo_bar_baz',
+ 'foo_Bar_Baz_box': 'foo_bar_baz_box',
+ }
+ for name in expectations:
+ self.assertEquals(expectations[name], model.UnixName(name));
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/tools/json_schema_compiler/preview.py b/chromium/tools/json_schema_compiler/preview.py
new file mode 100755
index 00000000000..22ed0f2df9f
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/preview.py
@@ -0,0 +1,366 @@
+#!/usr/bin/env python
+
+# 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.
+"""Server for viewing the compiled C++ code from tools/json_schema_compiler.
+"""
+
+import cc_generator
+import code
+import compiler
+import cpp_type_generator
+import cpp_util
+import h_generator
+import idl_schema
+import json_schema
+import model
+import optparse
+import os
+import schema_loader
+import sys
+import urlparse
+from highlighters import (
+ pygments_highlighter, none_highlighter, hilite_me_highlighter)
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+
+class CompilerHandler(BaseHTTPRequestHandler):
+ """A HTTPRequestHandler that outputs the result of tools/json_schema_compiler.
+ """
+ def do_GET(self):
+ parsed_url = urlparse.urlparse(self.path)
+ request_path = self._GetRequestPath(parsed_url)
+
+ chromium_favicon = 'http://codereview.chromium.org/static/favicon.ico'
+
+ head = code.Code()
+ head.Append('<link rel="icon" href="%s">' % chromium_favicon)
+ head.Append('<link rel="shortcut icon" href="%s">' % chromium_favicon)
+
+ body = code.Code()
+
+ try:
+ if os.path.isdir(request_path):
+ self._ShowPanels(parsed_url, head, body)
+ else:
+ self._ShowCompiledFile(parsed_url, head, body)
+ finally:
+ self.wfile.write('<html><head>')
+ self.wfile.write(head.Render())
+ self.wfile.write('</head><body>')
+ self.wfile.write(body.Render())
+ self.wfile.write('</body></html>')
+
+ def _GetRequestPath(self, parsed_url, strip_nav=False):
+ """Get the relative path from the current directory to the requested file.
+ """
+ path = parsed_url.path
+ if strip_nav:
+ path = parsed_url.path.replace('/nav', '')
+ return os.path.normpath(os.curdir + path)
+
+ def _ShowPanels(self, parsed_url, head, body):
+ """Show the previewer frame structure.
+
+ Code panes are populated via XHR after links in the nav pane are clicked.
+ """
+ (head.Append('<style>')
+ .Append('body {')
+ .Append(' margin: 0;')
+ .Append('}')
+ .Append('.pane {')
+ .Append(' height: 100%;')
+ .Append(' overflow-x: auto;')
+ .Append(' overflow-y: scroll;')
+ .Append(' display: inline-block;')
+ .Append('}')
+ .Append('#nav_pane {')
+ .Append(' width: 20%;')
+ .Append('}')
+ .Append('#nav_pane ul {')
+ .Append(' list-style-type: none;')
+ .Append(' padding: 0 0 0 1em;')
+ .Append('}')
+ .Append('#cc_pane {')
+ .Append(' width: 40%;')
+ .Append('}')
+ .Append('#h_pane {')
+ .Append(' width: 40%;')
+ .Append('}')
+ .Append('</style>')
+ )
+
+ body.Append(
+ '<div class="pane" id="nav_pane">%s</div>'
+ '<div class="pane" id="h_pane"></div>'
+ '<div class="pane" id="cc_pane"></div>' %
+ self._RenderNavPane(parsed_url.path[1:])
+ )
+
+ # The Javascript that interacts with the nav pane and panes to show the
+ # compiled files as the URL or highlighting options change.
+ body.Append('''<script type="text/javascript">
+// Calls a function for each highlighter style <select> element.
+function forEachHighlighterStyle(callback) {
+ var highlighterStyles =
+ document.getElementsByClassName('highlighter_styles');
+ for (var i = 0; i < highlighterStyles.length; ++i)
+ callback(highlighterStyles[i]);
+}
+
+// Called when anything changes, such as the highlighter or hashtag.
+function updateEverything() {
+ var highlighters = document.getElementById('highlighters');
+ var highlighterName = highlighters.value;
+
+ // Cache in localStorage for when the page loads next.
+ localStorage.highlightersValue = highlighterName;
+
+ // Show/hide the highlighter styles.
+ var highlighterStyleName = '';
+ forEachHighlighterStyle(function(highlighterStyle) {
+ if (highlighterStyle.id === highlighterName + '_styles') {
+ highlighterStyle.removeAttribute('style')
+ highlighterStyleName = highlighterStyle.value;
+ } else {
+ highlighterStyle.setAttribute('style', 'display:none')
+ }
+
+ // Cache in localStorage for when the page next loads.
+ localStorage[highlighterStyle.id + 'Value'] = highlighterStyle.value;
+ });
+
+ // Populate the code panes.
+ function populateViaXHR(elementId, requestPath) {
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState != 4)
+ return;
+ if (xhr.status != 200) {
+ alert('XHR error to ' + requestPath);
+ return;
+ }
+ document.getElementById(elementId).innerHTML = xhr.responseText;
+ };
+ xhr.open('GET', requestPath, true);
+ xhr.send();
+ }
+
+ var targetName = window.location.hash;
+ targetName = targetName.substring('#'.length);
+ targetName = targetName.split('.', 1)[0]
+
+ if (targetName !== '') {
+ var basePath = window.location.pathname;
+ var query = 'highlighter=' + highlighterName + '&' +
+ 'style=' + highlighterStyleName;
+ populateViaXHR('h_pane', basePath + '/' + targetName + '.h?' + query);
+ populateViaXHR('cc_pane', basePath + '/' + targetName + '.cc?' + query);
+ }
+}
+
+// Initial load: set the values of highlighter and highlighterStyles from
+// localStorage.
+(function() {
+var cachedValue = localStorage.highlightersValue;
+if (cachedValue)
+ document.getElementById('highlighters').value = cachedValue;
+
+forEachHighlighterStyle(function(highlighterStyle) {
+ var cachedValue = localStorage[highlighterStyle.id + 'Value'];
+ if (cachedValue)
+ highlighterStyle.value = cachedValue;
+});
+})();
+
+window.addEventListener('hashchange', updateEverything, false);
+updateEverything();
+</script>''')
+
+ def _LoadModel(self, basedir, name):
+ """Loads and returns the model for the |name| API from either its JSON or
+ IDL file, e.g.
+ name=contextMenus will be loaded from |basedir|/context_menus.json,
+ name=alarms will be loaded from |basedir|/alarms.idl.
+ """
+ loaders = {
+ 'json': json_schema.Load,
+ 'idl': idl_schema.Load
+ }
+ # APIs are referred to like "webRequest" but that's in a file
+ # "web_request.json" so we need to unixify the name.
+ unix_name = model.UnixName(name)
+ for loader_ext, loader_fn in loaders.items():
+ file_path = '%s/%s.%s' % (basedir, unix_name, loader_ext)
+ if os.path.exists(file_path):
+ # For historical reasons these files contain a singleton list with the
+ # model, so just return that single object.
+ return (loader_fn(file_path)[0], file_path)
+ raise ValueError('File for model "%s" not found' % name)
+
+ def _ShowCompiledFile(self, parsed_url, head, body):
+ """Show the compiled version of a json or idl file given the path to the
+ compiled file.
+ """
+ api_model = model.Model()
+
+ request_path = self._GetRequestPath(parsed_url)
+ (file_root, file_ext) = os.path.splitext(request_path)
+ (filedir, filename) = os.path.split(file_root)
+
+ try:
+ # Get main file.
+ (api_def, file_path) = self._LoadModel(filedir, filename)
+ namespace = api_model.AddNamespace(api_def, file_path)
+ type_generator = cpp_type_generator.CppTypeGenerator(
+ api_model,
+ schema_loader.SchemaLoader(filedir),
+ namespace)
+
+ # Get the model's dependencies.
+ for dependency in api_def.get('dependencies', []):
+ # Dependencies can contain : in which case they don't refer to APIs,
+ # rather, permissions or manifest keys.
+ if ':' in dependency:
+ continue
+ (api_def, file_path) = self._LoadModel(filedir, dependency)
+ referenced_namespace = api_model.AddNamespace(api_def, file_path)
+ if referenced_namespace:
+ type_generator.AddNamespace(referenced_namespace,
+ cpp_util.Classname(referenced_namespace.name).lower())
+
+ # Generate code
+ cpp_namespace = 'generated_api_schemas'
+ if file_ext == '.h':
+ cpp_code = (h_generator.HGenerator(type_generator, cpp_namespace)
+ .Generate(namespace).Render())
+ elif file_ext == '.cc':
+ cpp_code = (cc_generator.CCGenerator(type_generator, cpp_namespace)
+ .Generate(namespace).Render())
+ else:
+ self.send_error(404, "File not found: %s" % request_path)
+ return
+
+ # Do highlighting on the generated code
+ (highlighter_param, style_param) = self._GetHighlighterParams(parsed_url)
+ head.Append('<style>' +
+ self.server.highlighters[highlighter_param].GetCSS(style_param) +
+ '</style>')
+ body.Append(self.server.highlighters[highlighter_param]
+ .GetCodeElement(cpp_code, style_param))
+ except IOError:
+ self.send_error(404, "File not found: %s" % request_path)
+ return
+ except (TypeError, KeyError, AttributeError,
+ AssertionError, NotImplementedError) as error:
+ body.Append('<pre>')
+ body.Append('compiler error: %s' % error)
+ body.Append('Check server log for more details')
+ body.Append('</pre>')
+ raise
+
+ def _GetHighlighterParams(self, parsed_url):
+ """Get the highlighting parameters from a parsed url.
+ """
+ query_dict = urlparse.parse_qs(parsed_url.query)
+ return (query_dict.get('highlighter', ['pygments'])[0],
+ query_dict.get('style', ['colorful'])[0])
+
+ def _RenderNavPane(self, path):
+ """Renders an HTML nav pane.
+
+ This consists of a select element to set highlight style, and a list of all
+ files at |path| with the appropriate onclick handlers to open either
+ subdirectories or JSON files.
+ """
+ html = code.Code()
+
+ # Highlighter chooser.
+ html.Append('<select id="highlighters" onChange="updateEverything()">')
+ for name, highlighter in self.server.highlighters.items():
+ html.Append('<option value="%s">%s</option>' %
+ (name, highlighter.DisplayName()))
+ html.Append('</select>')
+
+ html.Append('<br/>')
+
+ # Style for each highlighter.
+ # The correct highlighting will be shown by Javascript.
+ for name, highlighter in self.server.highlighters.items():
+ styles = sorted(highlighter.GetStyles())
+ if not styles:
+ continue
+
+ html.Append('<select class="highlighter_styles" id="%s_styles" '
+ 'onChange="updateEverything()">' % name)
+ for style in styles:
+ html.Append('<option>%s</option>' % style)
+ html.Append('</select>')
+
+ html.Append('<br/>')
+
+ # The files, with appropriate handlers.
+ html.Append('<ul>')
+
+ # Make path point to a non-empty directory. This can happen if a URL like
+ # http://localhost:8000 is navigated to.
+ if path == '':
+ path = os.curdir
+
+ # Firstly, a .. link if this isn't the root.
+ if not os.path.samefile(os.curdir, path):
+ normpath = os.path.normpath(os.path.join(path, os.pardir))
+ html.Append('<li><a href="/%s">%s/</a>' % (normpath, os.pardir))
+
+ # Each file under path/
+ for filename in sorted(os.listdir(path)):
+ full_path = os.path.join(path, filename)
+ (file_root, file_ext) = os.path.splitext(full_path)
+ if os.path.isdir(full_path) and not full_path.endswith('.xcodeproj'):
+ html.Append('<li><a href="/%s/">%s/</a>' % (full_path, filename))
+ elif file_ext in ['.json', '.idl']:
+ # cc/h panes will automatically update via the hash change event.
+ html.Append('<li><a href="#%s">%s</a>' %
+ (filename, filename))
+
+ html.Append('</ul>')
+
+ return html.Render()
+
+class PreviewHTTPServer(HTTPServer, object):
+ def __init__(self, server_address, handler, highlighters):
+ super(PreviewHTTPServer, self).__init__(server_address, handler)
+ self.highlighters = highlighters
+
+
+if __name__ == '__main__':
+ parser = optparse.OptionParser(
+ description='Runs a server to preview the json_schema_compiler output.',
+ usage='usage: %prog [option]...')
+ parser.add_option('-p', '--port', default='8000',
+ help='port to run the server on')
+
+ (opts, argv) = parser.parse_args()
+
+ try:
+ print('Starting previewserver on port %s' % opts.port)
+ print('The extension documentation can be found at:')
+ print('')
+ print(' http://localhost:%s/chrome/common/extensions/api' % opts.port)
+ print('')
+
+ highlighters = {
+ 'hilite': hilite_me_highlighter.HiliteMeHighlighter(),
+ 'none': none_highlighter.NoneHighlighter()
+ }
+ try:
+ highlighters['pygments'] = pygments_highlighter.PygmentsHighlighter()
+ except ImportError as e:
+ pass
+
+ server = PreviewHTTPServer(('', int(opts.port)),
+ CompilerHandler,
+ highlighters)
+ server.serve_forever()
+ except KeyboardInterrupt:
+ server.socket.close()
diff --git a/chromium/tools/json_schema_compiler/schema_loader.py b/chromium/tools/json_schema_compiler/schema_loader.py
new file mode 100644
index 00000000000..c434dc16746
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/schema_loader.py
@@ -0,0 +1,53 @@
+# 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 os
+import sys
+
+import idl_schema
+import json_schema
+from model import Model
+
+class SchemaLoader(object):
+ '''Resolves a type name into the namespace the type belongs to.
+ '''
+ def __init__(self, api_path):
+ self._api_path = api_path
+
+ def ResolveType(self, full_name, default_namespace):
+ name_parts = full_name.rsplit('.', 1)
+ if len(name_parts) == 1:
+ if full_name not in default_namespace.types:
+ return None
+ return default_namespace
+ namespace_name, type_name = name_parts
+ real_name = None
+ for ext in ['json', 'idl']:
+ filename = '%s.%s' % (namespace_name, ext)
+ if os.path.exists(filename):
+ real_name = filename
+ break
+ if real_name is None:
+ return None
+ namespace = Model().AddNamespace(self.LoadSchema(real_name)[0],
+ os.path.join(self._api_path, real_name))
+ if type_name not in namespace.types:
+ return None
+ return namespace
+
+ def LoadSchema(self, schema):
+ schema_filename, schema_extension = os.path.splitext(schema)
+
+ if schema_extension == '.json':
+ api_defs = json_schema.Load(schema)
+ elif schema_extension == '.idl':
+ api_defs = idl_schema.Load(schema)
+ else:
+ sys.exit('Did not recognize file extension %s for schema %s' %
+ (schema_extension, schema))
+ if len(api_defs) != 1:
+ sys.exit('File %s has multiple schemas. Files are only allowed to contain'
+ 'a single schema.' % schema)
+
+ return api_defs
diff --git a/chromium/tools/json_schema_compiler/schema_util.py b/chromium/tools/json_schema_compiler/schema_util.py
new file mode 100644
index 00000000000..7ce399e8ed3
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/schema_util.py
@@ -0,0 +1,37 @@
+# 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.
+"""Utilies for the processing of schema python structures.
+"""
+
+import json_parse
+
+def CapitalizeFirstLetter(value):
+ return value[0].capitalize() + value[1:]
+
+def GetNamespace(ref):
+ return SplitNamespace(ref)[0]
+
+def StripNamespace(ref):
+ return SplitNamespace(ref)[1]
+
+def SplitNamespace(ref):
+ """Returns (namespace, entity) from |ref|, e.g. app.window.AppWindow ->
+ (app.window, AppWindow). If |ref| isn't qualified then returns (None, ref).
+ """
+ if '.' in ref:
+ return tuple(ref.rsplit('.', 1))
+ return (None, ref)
+
+def JsFunctionNameToClassName(namespace_name, function_name):
+ """Transform a fully qualified function name like foo.bar.baz into FooBarBaz
+
+ Also strips any leading 'Experimental' prefix."""
+ parts = []
+ full_name = namespace_name + "." + function_name
+ for part in full_name.split("."):
+ parts.append(CapitalizeFirstLetter(part))
+ if parts[0] == "Experimental":
+ del parts[0]
+ class_name = "".join(parts)
+ return class_name
diff --git a/chromium/tools/json_schema_compiler/schema_util_test.py b/chromium/tools/json_schema_compiler/schema_util_test.py
new file mode 100755
index 00000000000..154da0137ea
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/schema_util_test.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+# 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.
+
+from schema_util import JsFunctionNameToClassName
+from schema_util import StripNamespace
+import unittest
+
+class SchemaUtilTest(unittest.TestCase):
+ def testStripNamespace(self):
+ self.assertEquals('Bar', StripNamespace('foo.Bar'))
+ self.assertEquals('Baz', StripNamespace('Baz'))
+
+ def testJsFunctionNameToClassName(self):
+ self.assertEquals('FooBar', JsFunctionNameToClassName('foo', 'bar'))
+ self.assertEquals('FooBar',
+ JsFunctionNameToClassName('experimental.foo', 'bar'))
+ self.assertEquals('FooBarBaz',
+ JsFunctionNameToClassName('foo.bar', 'baz'))
+ self.assertEquals('FooBarBaz',
+ JsFunctionNameToClassName('experimental.foo.bar', 'baz'))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp b/chromium/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp
new file mode 100644
index 00000000000..27ec4385be8
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp
@@ -0,0 +1,44 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'json_schema_compiler_tests',
+ 'type': 'static_library',
+ 'variables': {
+ 'chromium_code': 1,
+ 'schema_files': [
+ 'additional_properties.json',
+ 'any.json',
+ 'arrays.json',
+ 'callbacks.json',
+ 'choices.json',
+ 'crossref.json',
+ 'enums.json',
+ 'functions_as_parameters.json',
+ 'functions_on_types.json',
+ 'idl_basics.idl',
+ 'idl_object_types.idl',
+ 'objects.json',
+ 'simple_api.json',
+ 'error_generation.json'
+ ],
+ 'cc_dir': 'tools/json_schema_compiler/test',
+ 'root_namespace': 'test::api',
+ },
+ 'inputs': [
+ '<@(schema_files)',
+ ],
+ 'sources': [
+ '<@(schema_files)',
+ 'test_util.cc',
+ 'test_util.h',
+ ],
+ 'includes': ['../../../build/json_schema_compile.gypi'],
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [4267, ],
+ },
+ ],
+}
diff --git a/chromium/tools/json_schema_compiler/util.cc b/chromium/tools/json_schema_compiler/util.cc
new file mode 100644
index 00000000000..d0e77659e6a
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/util.cc
@@ -0,0 +1,97 @@
+// 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/util.h"
+
+#include "base/values.h"
+
+namespace json_schema_compiler {
+namespace util {
+
+bool GetItemFromList(const base::ListValue& from, int index, int* out) {
+ return from.GetInteger(index, out);
+}
+
+bool GetItemFromList(const base::ListValue& from, int index, bool* out) {
+ return from.GetBoolean(index, out);
+}
+
+bool GetItemFromList(const base::ListValue& from, int index, double* out) {
+ return from.GetDouble(index, out);
+}
+
+bool GetItemFromList(const base::ListValue& from, int index, std::string* out) {
+ return from.GetString(index, out);
+}
+
+bool GetItemFromList(const base::ListValue& from,
+ int index,
+ linked_ptr<base::Value>* out) {
+ const base::Value* value = NULL;
+ if (!from.Get(index, &value))
+ return false;
+ *out = make_linked_ptr(value->DeepCopy());
+ return true;
+}
+
+bool GetItemFromList(const base::ListValue& from, int index,
+ linked_ptr<base::DictionaryValue>* out) {
+ const base::DictionaryValue* dict = NULL;
+ if (!from.GetDictionary(index, &dict))
+ return false;
+ *out = make_linked_ptr(dict->DeepCopy());
+ return true;
+}
+
+void AddItemToList(const int from, base::ListValue* out) {
+ out->Append(new base::FundamentalValue(from));
+}
+
+void AddItemToList(const bool from, base::ListValue* out) {
+ out->Append(new base::FundamentalValue(from));
+}
+
+void AddItemToList(const double from, base::ListValue* out) {
+ out->Append(new base::FundamentalValue(from));
+}
+
+void AddItemToList(const std::string& from, base::ListValue* out) {
+ out->Append(new base::StringValue(from));
+}
+
+void AddItemToList(const linked_ptr<base::Value>& from,
+ base::ListValue* out) {
+ out->Append(from->DeepCopy());
+}
+
+void AddItemToList(const linked_ptr<base::DictionaryValue>& from,
+ base::ListValue* out) {
+ out->Append(static_cast<base::Value*>(from->DeepCopy()));
+}
+
+std::string ValueTypeToString(Value::Type type) {
+ switch(type) {
+ case Value::TYPE_NULL:
+ return "null";
+ case Value::TYPE_BOOLEAN:
+ return "boolean";
+ case Value::TYPE_INTEGER:
+ return "integer";
+ case Value::TYPE_DOUBLE:
+ return "number";
+ case Value::TYPE_STRING:
+ return "string";
+ case Value::TYPE_BINARY:
+ return "binary";
+ case Value::TYPE_DICTIONARY:
+ return "dictionary";
+ case Value::TYPE_LIST:
+ return "list";
+ }
+ NOTREACHED();
+ return "";
+}
+
+} // namespace api_util
+} // namespace extensions
diff --git a/chromium/tools/json_schema_compiler/util.h b/chromium/tools/json_schema_compiler/util.h
new file mode 100644
index 00000000000..b775be7e0cc
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/util.h
@@ -0,0 +1,181 @@
+// 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_UTIL_H__
+#define TOOLS_JSON_SCHEMA_COMPILER_UTIL_H__
+
+#include <string>
+#include <vector>
+
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+namespace json_schema_compiler {
+
+namespace util {
+
+// Creates a new item at |out| from |from|[|index|]. These are used by template
+// specializations of |Get(Optional)ArrayFromList|.
+bool GetItemFromList(const base::ListValue& from, int index, int* out);
+bool GetItemFromList(const base::ListValue& from, int index, bool* out);
+bool GetItemFromList(const base::ListValue& from, int index, double* out);
+bool GetItemFromList(const base::ListValue& from, int index, std::string* out);
+bool GetItemFromList(const base::ListValue& from,
+ int index,
+ linked_ptr<base::Value>* out);
+bool GetItemFromList(const base::ListValue& from,
+ int index,
+ linked_ptr<base::DictionaryValue>* out);
+
+// This template is used for types generated by tools/json_schema_compiler.
+template<class T>
+bool GetItemFromList(const base::ListValue& from,
+ int index,
+ linked_ptr<T>* out) {
+ const base::DictionaryValue* dict;
+ if (!from.GetDictionary(index, &dict))
+ return false;
+ scoped_ptr<T> obj(new T());
+ if (!T::Populate(*dict, obj.get()))
+ return false;
+ *out = linked_ptr<T>(obj.release());
+ return true;
+}
+
+// Populates |out| with |list|. Returns false if there is no list at the
+// specified key or if the list has anything other than |T|.
+template <class T>
+bool PopulateArrayFromList(
+ const base::ListValue& list, std::vector<T>* out) {
+ out->clear();
+ T value;
+ for (size_t i = 0; i < list.GetSize(); ++i) {
+ if (!GetItemFromList(list, i, &value))
+ return false;
+ out->push_back(value);
+ }
+
+ return true;
+}
+
+// Populates |out| with |from|.|name|. Returns false if there is no list at
+// the specified key or if the list has anything other than |T|.
+template <class T>
+bool PopulateArrayFromDictionary(
+ const base::DictionaryValue& from,
+ const std::string& name,
+ std::vector<T>* out) {
+ const base::ListValue* list = NULL;
+ if (!from.GetListWithoutPathExpansion(name, &list))
+ return false;
+
+ return PopulateArrayFromList(*list, out);
+}
+
+// Creates a new vector containing |list| at |out|. Returns
+// true on success or if there is nothing at the specified key. Returns false
+// if anything other than a list of |T| is at the specified key.
+template <class T>
+bool PopulateOptionalArrayFromList(
+ const base::ListValue& list,
+ scoped_ptr<std::vector<T> >* out) {
+ out->reset(new std::vector<T>());
+ T value;
+ for (size_t i = 0; i < list.GetSize(); ++i) {
+ if (!GetItemFromList(list, i, &value)) {
+ out->reset();
+ return false;
+ }
+ (*out)->push_back(value);
+ }
+
+ return true;
+}
+
+// Creates a new vector containing |from|.|name| at |out|. Returns
+// true on success or if there is nothing at the specified key. Returns false
+// if anything other than a list of |T| is at the specified key.
+template <class T>
+bool PopulateOptionalArrayFromDictionary(
+ const base::DictionaryValue& from,
+ const std::string& name,
+ scoped_ptr<std::vector<T> >* out) {
+ const base::ListValue* list = NULL;
+ {
+ const base::Value* maybe_list = NULL;
+ // Since |name| is optional, its absence is acceptable. However, anything
+ // other than a ListValue is not.
+ if (!from.GetWithoutPathExpansion(name, &maybe_list))
+ return true;
+ if (!maybe_list->IsType(base::Value::TYPE_LIST))
+ return false;
+ list = static_cast<const base::ListValue*>(maybe_list);
+ }
+
+ return PopulateOptionalArrayFromList(*list, out);
+}
+
+// Appends a Value newly created from |from| to |out|. These used by template
+// specializations of |Set(Optional)ArrayToList|.
+void AddItemToList(const int from, base::ListValue* out);
+void AddItemToList(const bool from, base::ListValue* out);
+void AddItemToList(const double from, base::ListValue* out);
+void AddItemToList(const std::string& from, base::ListValue* out);
+void AddItemToList(const linked_ptr<base::Value>& from,
+ base::ListValue* out);
+void AddItemToList(const linked_ptr<base::DictionaryValue>& from,
+ base::ListValue* out);
+
+// This template is used for types generated by tools/json_schema_compiler.
+template<class T>
+void AddItemToList(const linked_ptr<T>& from, base::ListValue* out) {
+ out->Append(from->ToValue().release());
+}
+
+// Set |out| to the the contents of |from|. Requires GetItemFromList to be
+// implemented for |T|.
+template <class T>
+void PopulateListFromArray(
+ const std::vector<T>& from,
+ base::ListValue* out) {
+ out->Clear();
+ for (typename std::vector<T>::const_iterator it = from.begin();
+ it != from.end(); ++it) {
+ AddItemToList(*it, out);
+ }
+}
+
+// Set |out| to the the contents of |from| if |from| is non-NULL. Requires
+// GetItemFromList to be implemented for |T|.
+template <class T>
+void PopulateListFromOptionalArray(
+ const scoped_ptr<std::vector<T> >& from,
+ base::ListValue* out) {
+ if (from.get())
+ PopulateListFromArray(*from, out);
+
+}
+
+template <class T>
+scoped_ptr<base::Value> CreateValueFromArray(const std::vector<T>& from) {
+ base::ListValue* list = new base::ListValue();
+ PopulateListFromArray(from, list);
+ return scoped_ptr<base::Value>(list);
+}
+
+template <class T>
+scoped_ptr<base::Value> CreateValueFromOptionalArray(
+ const scoped_ptr<std::vector<T> >& from) {
+ if (from.get())
+ return CreateValueFromArray(*from);
+ return scoped_ptr<base::Value>();
+}
+
+std::string ValueTypeToString(Value::Type type);
+
+} // namespace util
+} // namespace json_schema_compiler
+
+#endif // TOOLS_JSON_SCHEMA_COMPILER_UTIL_H__
diff --git a/chromium/tools/json_schema_compiler/util_cc_helper.py b/chromium/tools/json_schema_compiler/util_cc_helper.py
new file mode 100644
index 00000000000..df5b6de0f50
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/util_cc_helper.py
@@ -0,0 +1,85 @@
+# 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.
+
+API_UTIL_NAMESPACE = 'json_schema_compiler::util'
+
+class UtilCCHelper(object):
+ """A util class that generates code that uses
+ tools/json_schema_compiler/util.cc.
+ """
+ def __init__(self, type_manager):
+ self._type_manager = type_manager
+
+ def PopulateArrayFromDictionary(self, array_prop, src, name, dst):
+ """Generates code to get an array from a src.name into dst.
+
+ src: DictionaryValue*
+ dst: std::vector or scoped_ptr<std::vector>
+ """
+ prop = array_prop.item_type
+ sub = {
+ 'namespace': API_UTIL_NAMESPACE,
+ 'name': name,
+ 'src': src,
+ 'dst': dst,
+ }
+
+ sub['type'] = self._type_manager.GetCppType(prop),
+ if array_prop.optional:
+ val = ('%(namespace)s::PopulateOptionalArrayFromDictionary'
+ '(*%(src)s, "%(name)s", &%(dst)s)')
+ else:
+ val = ('%(namespace)s::PopulateArrayFromDictionary'
+ '(*%(src)s, "%(name)s", &%(dst)s)')
+
+ return val % sub
+
+ def PopulateArrayFromList(self, array_prop, src, dst, optional):
+ """Generates code to get an array from src into dst.
+
+ src: ListValue*
+ dst: std::vector or scoped_ptr<std::vector>
+ """
+ prop = array_prop.item_type
+ sub = {
+ 'namespace': API_UTIL_NAMESPACE,
+ 'src': src,
+ 'dst': dst,
+ 'type': self._type_manager.GetCppType(prop),
+ }
+
+ if optional:
+ val = '%(namespace)s::PopulateOptionalArrayFromList(*%(src)s, &%(dst)s)'
+ else:
+ val = '%(namespace)s::PopulateArrayFromList(*%(src)s, &%(dst)s)'
+
+ return val % sub
+
+ def CreateValueFromArray(self, array_prop, src, optional):
+ """Generates code to create a scoped_pt<Value> from the array at src.
+
+ src: std::vector or scoped_ptr<std::vector>
+ """
+ prop = array_prop.item_type
+ sub = {
+ 'namespace': API_UTIL_NAMESPACE,
+ 'src': src,
+ 'type': self._type_manager.GetCppType(prop),
+ }
+
+ if optional:
+ val = '%(namespace)s::CreateValueFromOptionalArray(%(src)s)'
+ else:
+ val = '%(namespace)s::CreateValueFromArray(%(src)s)'
+
+ return val % sub
+
+ def GetIncludePath(self):
+ return '#include "tools/json_schema_compiler/util.h"'
+
+ def GetValueTypeString(self, value, is_ptr=False):
+ call = '.GetType()'
+ if is_ptr:
+ call = '->GetType()'
+ return 'json_schema_compiler::util::ValueTypeToString(%s%s)' % (value, call)