Commit d01d5bb3 authored by wjywbs@gmail.com's avatar wjywbs@gmail.com

Support converting referenced enum array into string.

This patch in JSON Schema Compiler first generates a vector with
strings of the enum array using the correct ToString() function
of both inline and referenced enums. Then the result is created
from the vector of strings.

R=kalman@chromium.org
BUG=371808,368368

Review URL: https://codereview.chromium.org/276603003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271244 0039d316-1c4b-4281-b951-d872f2087c98
parent 27b14935
...@@ -366,12 +366,12 @@ class _Generator(object): ...@@ -366,12 +366,12 @@ class _Generator(object):
# ANY is a base::Value which is abstract and cannot be a direct member, so # ANY is a base::Value which is abstract and cannot be a direct member, so
# it will always be a pointer. # it will always be a pointer.
is_ptr = prop.optional or prop.type_.property_type == PropertyType.ANY is_ptr = prop.optional or prop.type_.property_type == PropertyType.ANY
c.Append('value->SetWithoutPathExpansion("%s", %s);' % ( c.Cblock(self._CreateValueFromType(
'value->SetWithoutPathExpansion("%s", %%s);' % prop.name,
prop.name, prop.name,
self._CreateValueFromType(cpp_namespace, prop.type_,
prop.type_, prop_var,
prop_var, is_ptr=is_ptr))
is_ptr=is_ptr)))
if prop.optional: if prop.optional:
c.Eblock('}') c.Eblock('}')
...@@ -390,11 +390,11 @@ class _Generator(object): ...@@ -390,11 +390,11 @@ class _Generator(object):
cpp_util.PadForGenerics(cpp_type)) cpp_util.PadForGenerics(cpp_type))
.Append(' additional_properties.begin();') .Append(' additional_properties.begin();')
.Append(' it != additional_properties.end(); ++it) {') .Append(' it != additional_properties.end(); ++it) {')
.Append('value->SetWithoutPathExpansion(it->first, %s);' % .Cblock(self._CreateValueFromType(
self._CreateValueFromType( 'value->SetWithoutPathExpansion(it->first, %s);',
cpp_namespace, type_.additional_properties.name,
type_.additional_properties, type_.additional_properties,
'%sit->second' % ('*' if needs_unwrap else ''))) '%sit->second' % ('*' if needs_unwrap else '')))
.Eblock('}') .Eblock('}')
) )
...@@ -414,10 +414,10 @@ class _Generator(object): ...@@ -414,10 +414,10 @@ class _Generator(object):
(c.Sblock('if (%s) {' % choice_var) (c.Sblock('if (%s) {' % choice_var)
.Append('DCHECK(!result) << "Cannot set multiple choices for %s";' % .Append('DCHECK(!result) << "Cannot set multiple choices for %s";' %
type_.unix_name) type_.unix_name)
.Append('result.reset(%s);' % self._CreateValueFromType( .Cblock(self._CreateValueFromType('result.reset(%s);',
cpp_namespace, choice.name,
choice, choice,
'*%s' % choice_var)) '*%s' % choice_var))
.Eblock('}') .Eblock('}')
) )
(c.Append('DCHECK(result) << "Must set at least one choice for %s";' % (c.Append('DCHECK(result) << "Must set at least one choice for %s";' %
...@@ -474,7 +474,7 @@ class _Generator(object): ...@@ -474,7 +474,7 @@ class _Generator(object):
) )
return c return c
def _CreateValueFromType(self, cpp_namespace, type_, var, is_ptr=False): def _CreateValueFromType(self, code, prop_name, type_, var, is_ptr=False):
"""Creates a base::Value given a type. Generated code passes ownership """Creates a base::Value given a type. Generated code passes ownership
to caller. to caller.
...@@ -482,6 +482,52 @@ class _Generator(object): ...@@ -482,6 +482,52 @@ class _Generator(object):
E.g for std::string, generate new base::StringValue(var) E.g for std::string, generate new base::StringValue(var)
""" """
c = Code()
underlying_type = self._type_helper.FollowRef(type_)
if underlying_type.property_type == PropertyType.ARRAY:
# Enums are treated specially because C++ templating thinks that they're
# ints, but really they're strings. So we create a vector of strings and
# populate it with the names of the enum in the array. The |ToString|
# function of the enum can be in another namespace when the enum is
# referenced. Templates can not be used here because C++ templating does
# not support passing a namespace as an argument.
item_type = self._type_helper.FollowRef(underlying_type.item_type)
if item_type.property_type == PropertyType.ENUM:
vardot = '(%s)%s' % (var, '->' if is_ptr else '.')
maybe_namespace = ''
if type_.item_type.property_type == PropertyType.REF:
maybe_namespace = '%s::' % item_type.namespace.unix_name
enum_list_var = '%s_list' % prop_name
# Scope the std::vector variable declaration inside braces.
(c.Sblock('{')
.Append('std::vector<std::string> %s;' % enum_list_var)
.Append('for (std::vector<%s>::const_iterator it = %sbegin();'
% (self._type_helper.GetCppType(item_type), vardot))
.Sblock(' it != %send(); ++it) {' % vardot)
.Append('%s.push_back(%sToString(*it));' % (enum_list_var,
maybe_namespace))
.Eblock('}'))
# Because the std::vector above is always created for both required and
# optional enum arrays, |is_ptr| is set to false and uses the
# std::vector to create the values.
(c.Append(code %
self._GenerateCreateValueFromType(type_, enum_list_var, False))
.Eblock('}'))
return c
c.Append(code % self._GenerateCreateValueFromType(type_, var, is_ptr))
return c
def _GenerateCreateValueFromType(self, type_, var, is_ptr):
"""Generates the statement to create a base::Value given a type.
type_: The type of the values being converted.
var: The name of the variable.
is_ptr: Whether |type_| is optional.
"""
underlying_type = self._type_helper.FollowRef(type_) underlying_type = self._type_helper.FollowRef(type_)
if (underlying_type.property_type == PropertyType.CHOICES or if (underlying_type.property_type == PropertyType.CHOICES or
underlying_type.property_type == PropertyType.OBJECT): underlying_type.property_type == PropertyType.OBJECT):
...@@ -510,8 +556,6 @@ class _Generator(object): ...@@ -510,8 +556,6 @@ class _Generator(object):
(vardot, vardot)) (vardot, vardot))
elif underlying_type.property_type == PropertyType.ARRAY: elif underlying_type.property_type == PropertyType.ARRAY:
return '%s.release()' % self._util_cc_helper.CreateValueFromArray( return '%s.release()' % self._util_cc_helper.CreateValueFromArray(
cpp_namespace,
underlying_type,
var, var,
is_ptr) is_ptr)
elif underlying_type.property_type.is_fundamental: elif underlying_type.property_type.is_fundamental:
...@@ -959,10 +1003,10 @@ class _Generator(object): ...@@ -959,10 +1003,10 @@ class _Generator(object):
for param in params: for param in params:
declaration_list.append(cpp_util.GetParameterDeclaration( declaration_list.append(cpp_util.GetParameterDeclaration(
param, self._type_helper.GetCppType(param.type_))) param, self._type_helper.GetCppType(param.type_)))
c.Append('create_results->Append(%s);' % self._CreateValueFromType( c.Cblock(self._CreateValueFromType('create_results->Append(%s);',
cpp_namespace, param.name,
param.type_, param.type_,
param.unix_name)) param.unix_name))
c.Append('return create_results.Pass();') c.Append('return create_results.Pass();')
c.Eblock('}') c.Eblock('}')
c.Substitute({ c.Substitute({
......
...@@ -16,6 +16,48 @@ ...@@ -16,6 +16,48 @@
} }
} }
}, },
{
"id": "Enumeration",
"type": "string",
"enum": ["one", "two", "three"]
},
{
"id": "EnumArrayReference",
"type": "object",
"properties": {
"types": {
"type": "array",
"items": {
"$ref": "Enumeration"
}
}
}
},
{
"id": "EnumArrayMixed",
"type": "object",
"properties": {
"inline_enums": {
"type": "array",
"items": {
"type": "string",
"enum": ["one", "two", "three"]
}
},
"infile_enums": {
"type": "array",
"items": {
"$ref": "Enumeration"
}
},
"external_enums": {
"type": "array",
"items": {
"$ref": "enums.Enumeration"
}
}
}
},
{ {
"id": "OptionalEnumArrayType", "id": "OptionalEnumArrayType",
"type": "object", "type": "object",
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "tools/json_schema_compiler/test/arrays.h" #include "tools/json_schema_compiler/test/arrays.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "tools/json_schema_compiler/test/enums.h"
using namespace test::api::arrays; using namespace test::api::arrays;
...@@ -80,6 +81,87 @@ TEST(JsonSchemaCompilerArrayTest, EnumArrayType) { ...@@ -80,6 +81,87 @@ TEST(JsonSchemaCompilerArrayTest, EnumArrayType) {
EXPECT_TRUE(value.Equals(as_value.get())) << value << " != " << *as_value; EXPECT_TRUE(value.Equals(as_value.get())) << value << " != " << *as_value;
} }
TEST(JsonSchemaCompilerArrayTest, EnumArrayReference) {
// { "types": ["one", "two", "three"] }
base::ListValue* types = new base::ListValue();
types->AppendString("one");
types->AppendString("two");
types->AppendString("three");
base::DictionaryValue value;
value.Set("types", types);
EnumArrayReference enum_array_reference;
// Test Populate.
ASSERT_TRUE(EnumArrayReference::Populate(value, &enum_array_reference));
Enumeration expected_types[] = {ENUMERATION_ONE, ENUMERATION_TWO,
ENUMERATION_THREE};
EXPECT_EQ(std::vector<Enumeration>(
expected_types, expected_types + arraysize(expected_types)),
enum_array_reference.types);
// Test ToValue.
scoped_ptr<base::Value> as_value(enum_array_reference.ToValue());
EXPECT_TRUE(value.Equals(as_value.get())) << value << " != " << *as_value;
}
TEST(JsonSchemaCompilerArrayTest, EnumArrayMixed) {
// { "types": ["one", "two", "three"] }
base::ListValue* inline_enums = new base::ListValue();
inline_enums->AppendString("one");
inline_enums->AppendString("two");
inline_enums->AppendString("three");
base::ListValue* infile_enums = new base::ListValue();
infile_enums->AppendString("one");
infile_enums->AppendString("two");
infile_enums->AppendString("three");
base::ListValue* external_enums = new base::ListValue();
external_enums->AppendString("one");
external_enums->AppendString("two");
external_enums->AppendString("three");
base::DictionaryValue value;
value.Set("inline_enums", inline_enums);
value.Set("infile_enums", infile_enums);
value.Set("external_enums", external_enums);
EnumArrayMixed enum_array_mixed;
// Test Populate.
ASSERT_TRUE(EnumArrayMixed::Populate(value, &enum_array_mixed));
EnumArrayMixed::Inline_enumsType expected_inline_types[] = {
EnumArrayMixed::INLINE_ENUMS_TYPE_ONE,
EnumArrayMixed::INLINE_ENUMS_TYPE_TWO,
EnumArrayMixed::INLINE_ENUMS_TYPE_THREE};
EXPECT_EQ(std::vector<EnumArrayMixed::Inline_enumsType>(
expected_inline_types,
expected_inline_types + arraysize(expected_inline_types)),
enum_array_mixed.inline_enums);
Enumeration expected_infile_types[] = {ENUMERATION_ONE, ENUMERATION_TWO,
ENUMERATION_THREE};
EXPECT_EQ(std::vector<Enumeration>(
expected_infile_types,
expected_infile_types + arraysize(expected_infile_types)),
enum_array_mixed.infile_enums);
test::api::enums::Enumeration expected_external_types[] = {
test::api::enums::ENUMERATION_ONE, test::api::enums::ENUMERATION_TWO,
test::api::enums::ENUMERATION_THREE};
EXPECT_EQ(std::vector<test::api::enums::Enumeration>(
expected_external_types,
expected_external_types + arraysize(expected_external_types)),
enum_array_mixed.external_enums);
// Test ToValue.
scoped_ptr<base::Value> as_value(enum_array_mixed.ToValue());
EXPECT_TRUE(value.Equals(as_value.get())) << value << " != " << *as_value;
}
TEST(JsonSchemaCompilerArrayTest, OptionalEnumArrayType) { TEST(JsonSchemaCompilerArrayTest, OptionalEnumArrayType) {
{ {
std::vector<OptionalEnumArrayType::TypesType> enums; std::vector<OptionalEnumArrayType::TypesType> enums;
......
...@@ -173,24 +173,6 @@ scoped_ptr<base::Value> CreateValueFromOptionalArray( ...@@ -173,24 +173,6 @@ scoped_ptr<base::Value> CreateValueFromOptionalArray(
return scoped_ptr<base::Value>(); return scoped_ptr<base::Value>();
} }
template <class TParent, class T>
scoped_ptr<base::Value> CreateValueFromEnumArray(const std::vector<T>& from) {
base::ListValue* list = new base::ListValue();
for (typename std::vector<T>::const_iterator it = from.begin();
it != from.end(); ++it) {
list->AppendString(TParent::ToString(*it));
}
return scoped_ptr<base::Value>(list);
}
template <class TParent, class T>
scoped_ptr<base::Value> CreateValueFromOptionalEnumArray(
const scoped_ptr<std::vector<T> >& from) {
if (from.get())
return CreateValueFromEnumArray<TParent>(*from);
return scoped_ptr<base::Value>();
}
std::string ValueTypeToString(base::Value::Type type); std::string ValueTypeToString(base::Value::Type type);
} // namespace util } // namespace util
......
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
from model import PropertyType
_API_UTIL_NAMESPACE = 'json_schema_compiler::util' _API_UTIL_NAMESPACE = 'json_schema_compiler::util'
...@@ -55,30 +52,17 @@ class UtilCCHelper(object): ...@@ -55,30 +52,17 @@ class UtilCCHelper(object):
'dst': dst 'dst': dst
} }
def CreateValueFromArray(self, cpp_namespace, type_, src, optional): def CreateValueFromArray(self, src, optional):
"""Generates code to create a scoped_pt<Value> from the array at src. """Generates code to create a scoped_pt<Value> from the array at src.
|cpp_namespace| The namespace which contains |type_|. This is needed for
enum conversions, where the ToString method is on the containing
namespace.
|type_| The type of the values being converted. This is needed for enum
conversions, to know whether to use the Enum form of conversion.
|src| The variable to convert, either a vector or scoped_ptr<vector>. |src| The variable to convert, either a vector or scoped_ptr<vector>.
|optional| Whether |type_| was optional. Optional types are pointers so |optional| Whether |type_| was optional. Optional types are pointers so
must be treated differently. must be treated differently.
""" """
if type_.item_type.property_type == PropertyType.ENUM: if optional:
# Enums are treated specially because C++ templating thinks that they're name = 'CreateValueFromOptionalArray'
# ints, but really they're strings.
if optional:
name = 'CreateValueFromOptionalEnumArray<%s>' % cpp_namespace
else:
name = 'CreateValueFromEnumArray<%s>' % cpp_namespace
else: else:
if optional: name = 'CreateValueFromArray'
name = 'CreateValueFromOptionalArray'
else:
name = 'CreateValueFromArray'
return '%s::%s(%s)' % (_API_UTIL_NAMESPACE, name, src) return '%s::%s(%s)' % (_API_UTIL_NAMESPACE, name, src)
def GetIncludePath(self): def GetIncludePath(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment