diff options
author | cduvall@chromium.org <cduvall@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-25 01:00:41 +0000 |
---|---|---|
committer | cduvall@chromium.org <cduvall@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-25 01:00:41 +0000 |
commit | 0ba7b4f2a2295978d3747573ef0332f7e410889b (patch) | |
tree | 93769dd77f84c9463e1a8ad8a6dff5be850f895e /tools/json_schema_compiler | |
parent | dd5d7cc6a98df018e0018cd23b72a1b6287be0c4 (diff) | |
download | chromium_src-0ba7b4f2a2295978d3747573ef0332f7e410889b.zip chromium_src-0ba7b4f2a2295978d3747573ef0332f7e410889b.tar.gz chromium_src-0ba7b4f2a2295978d3747573ef0332f7e410889b.tar.bz2 |
Extensions Docs Server: Enum values do not show up if enum is a type
If an enumerated string is a type, the possible values were not shown.
BUG=146301
Review URL: https://chromiumcodereview.appspot.com/10907151
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@158475 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/json_schema_compiler')
-rw-r--r-- | tools/json_schema_compiler/cc_generator.py | 190 | ||||
-rw-r--r-- | tools/json_schema_compiler/cpp_type_generator.py | 17 | ||||
-rwxr-xr-x | tools/json_schema_compiler/cpp_type_generator_test.py | 5 | ||||
-rw-r--r-- | tools/json_schema_compiler/h_generator.py | 20 | ||||
-rw-r--r-- | tools/json_schema_compiler/model.py | 5 | ||||
-rw-r--r-- | tools/json_schema_compiler/test/dependency_tester.json | 2 | ||||
-rw-r--r-- | tools/json_schema_compiler/test/enums.json | 2 | ||||
-rw-r--r-- | tools/json_schema_compiler/test/enums_unittest.cc | 4 | ||||
-rw-r--r-- | tools/json_schema_compiler/test/font_settings.json | 17 | ||||
-rw-r--r-- | tools/json_schema_compiler/util.h | 11 |
10 files changed, 168 insertions, 105 deletions
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py index f01479a..2e42998 100644 --- a/tools/json_schema_compiler/cc_generator.py +++ b/tools/json_schema_compiler/cc_generator.py @@ -109,27 +109,27 @@ class CCGenerator(object): self._GenerateFunction( cpp_namespace + '::' + cpp_util.Classname(function.name), function)) - .Append() - ) + .Append()) elif type_.type_ == PropertyType.OBJECT: (c.Concat(self._GeneratePropertyFunctions( cpp_namespace, type_.properties.values())) .Sblock('%(namespace)s::%(classname)s()') .Concat(self._GenerateInitializersAndBody(type_)) .Eblock('%(namespace)s::~%(classname)s() {}') - .Append() - ) + .Append()) if type_.from_json: (c.Concat(self._GenerateTypePopulate(cpp_namespace, type_)) - .Append() - ) + .Append()) if type_.from_client: (c.Concat(self._GenerateTypeToValue(cpp_namespace, type_)) - .Append() - ) + .Append()) elif self._cpp_type_generator.IsEnumOrEnumRef(type_): - c.Concat(self._GenerateCreateEnumTypeValue(cpp_namespace, type_)) - c.Append() + (c.Concat(self._GenerateCreateEnumTypeValue(cpp_namespace, type_)) + .Append() + .Concat(self._GenerateEnumFromString(cpp_namespace, type_)) + .Append() + .Concat(self._GenerateEnumToString(cpp_namespace, type_)) + .Append()) c.Substitute({'classname': classname, 'namespace': cpp_namespace}) return c @@ -220,12 +220,14 @@ class CCGenerator(object): c.Append('const base::Value* %(value_var)s = NULL;') if prop.optional: (c.Sblock( - 'if (%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s)) {' - ) + 'if (%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s)) {') .Concat(self._GeneratePopulatePropertyFromValue( - prop, value_var, dst, 'false')) - .Eblock('}') - ) + prop, value_var, dst, 'false'))) + if self._cpp_type_generator.IsEnumOrEnumRef(prop): + (c.Append('} else {') + .Append('%%(dst)s->%%(name)s = %s;' % + self._cpp_type_generator.GetEnumNoneValue(prop))) + c.Eblock('}') else: (c.Append( 'if (!%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s))') @@ -234,7 +236,13 @@ class CCGenerator(object): prop, value_var, dst, 'false')) ) c.Append() - c.Substitute({'value_var': value_var, 'key': prop.name, 'src': src}) + c.Substitute({ + 'value_var': value_var, + 'key': prop.name, + 'src': src, + 'dst': dst, + 'name': prop.unix_name + }) return c def _GenerateTypeToValue(self, cpp_namespace, type_): @@ -343,7 +351,7 @@ class CCGenerator(object): vardot = var + '.' return '%sDeepCopy()' % vardot elif self._cpp_type_generator.IsEnumOrEnumRef(prop): - return 'CreateEnumValue(%s).release()' % var + return 'base::Value::CreateStringValue(ToString(%s))' % var elif prop.type_ == PropertyType.BINARY: if prop.optional: vardot = var + '->' @@ -648,11 +656,7 @@ class CCGenerator(object): c.Append('%(dst)s->%(name)s' + accessor + 'push_back(enum_temp);') c.Eblock('}') - def _GenerateStringToEnumConversion(self, - c, - prop, - value_var, - enum_temp): + def _GenerateStringToEnumConversion(self, c, prop, value_var, enum_temp): """Appends code that converts a string to an enum. Leaves failure_value unsubstituted. @@ -661,23 +665,16 @@ class CCGenerator(object): value_var: the string value that is being converted. enum_temp: the name used to store the temporary enum value. """ - (c.Append('%s %s;' % (self._cpp_type_generator.GetCompiledType(prop), - enum_temp)) - .Append('std::string enum_as_string;') + (c.Append('std::string enum_as_string;') .Append('if (!%s->GetAsString(&enum_as_string))' % value_var) .Append(' return %(failure_value)s;') - ) - for i, enum_value in enumerate( - self._cpp_type_generator.GetReferencedProperty(prop).enum_values): - (c.Append( - ('if' if i == 0 else 'else if') + - '(enum_as_string == "%s")' % enum_value) - .Append(' ' + enum_temp + ' = %s;' % ( - self._cpp_type_generator.GetEnumValue(prop, enum_value))) - ) - (c.Append('else') - .Append(' return %(failure_value)s;') - ) + .Append('%(type)s %(enum)s = From%(type)sString(enum_as_string);' % { + 'type': self._cpp_type_generator.GetCompiledType(prop), + 'enum': enum_temp + }) + .Append('if (%s == %s)' % + (enum_temp, self._cpp_type_generator.GetEnumNoneValue(prop))) + .Append(' return %(failure_value)s;')) def _GeneratePropertyFunctions(self, param_namespace, params): """Generate the functions for structures generated by a property such as @@ -699,8 +696,16 @@ class CCGenerator(object): if param.from_client: c.Concat(self._GenerateGetChoiceValue(param_namespace, param)) elif param.type_ == PropertyType.ENUM: - c.Concat(self._GenerateCreateEnumValue(param_namespace, param)) - c.Append() + (c.Concat(self._GenerateCreateEnumValue(param_namespace, param)) + .Append() + .Concat(self._GenerateEnumFromString(param_namespace, + param, + use_namespace=True)) + .Append() + .Concat(self._GenerateEnumToString(param_namespace, + param, + use_namespace=True)) + .Append()) return c def _GenerateGetChoiceValue(self, cpp_namespace, prop): @@ -711,18 +716,15 @@ class CCGenerator(object): (c.Sblock('scoped_ptr<base::Value> ' '%(cpp_namespace)s::Get%(choice)sChoiceValue() const {') .Sblock('switch (%s_type) {' % prop.unix_name) - ) - if prop.optional: - c.Concat(self._GenerateReturnCase( + .Concat(self._GenerateReturnCase( self._cpp_type_generator.GetEnumNoneValue(prop), - 'scoped_ptr<base::Value>()')) + 'scoped_ptr<base::Value>()'))) for choice in self._cpp_type_generator.ExpandParams([prop]): c.Concat(self._GenerateReturnCase( self._cpp_type_generator.GetEnumValue(prop, choice.type_.name), 'make_scoped_ptr<base::Value>(%s)' % self._CreateValueFromProperty(choice, choice.unix_name))) (c.Eblock('}') - .Append('NOTREACHED();') .Append('return scoped_ptr<base::Value>();') .Eblock('}') .Append() @@ -739,21 +741,76 @@ class CCGenerator(object): """ c = Code() classname = cpp_util.Classname(schema_util.StripSchemaNamespace(prop.name)) - c.Sblock('scoped_ptr<base::Value> CreateEnumValue(%s %s) {' % ( - classname, classname.lower())) - c.Sblock('switch (%s) {' % classname.lower()) + (c.Sblock('scoped_ptr<base::Value> CreateEnumValue(%s %s) {' % + (classname, classname.lower())) + .Append('std::string enum_temp = ToString(%s);' % classname.lower()) + .Append('if (enum_temp.empty())') + .Append(' return scoped_ptr<base::Value>();') + .Append('return scoped_ptr<base::Value>(' + 'base::Value::CreateStringValue(enum_temp));') + .Eblock('}')) + return c + + def _GenerateEnumToString(self, cpp_namespace, prop, use_namespace=False): + """Generates ToString() which gets the string representation of an enum. + """ + c = Code() + classname = cpp_util.Classname(schema_util.StripSchemaNamespace(prop.name)) + if use_namespace: + namespace = '%s::' % cpp_namespace + else: + namespace = '' + (c.Append('// static') + .Sblock('std::string %(namespace)sToString(%(class)s enum_param) {')) enum_prop = self._cpp_type_generator.GetReferencedProperty(prop) + c.Sblock('switch (enum_param) {') for enum_value in enum_prop.enum_values: c.Concat(self._GenerateReturnCase( - '%s_%s' % (classname.upper(), enum_value.upper()), - 'scoped_ptr<base::Value>(base::Value::CreateStringValue("%s"))' % - enum_value)) - (c.Eblock('}') - .Append('NOTREACHED();') - .Append('return scoped_ptr<base::Value>();') + self._cpp_type_generator.GetEnumValue(prop, enum_value), + '"%s"' % enum_value)) + (c.Append('case %s:' % self._cpp_type_generator.GetEnumNoneValue(prop)) + .Append(' return "";') .Eblock('}') - ) + .Append('return "";') + .Eblock('}') + .Substitute({ + 'namespace': namespace, + 'class': classname + })) + return c + + def _GenerateEnumFromString(self, cpp_namespace, prop, use_namespace=False): + """Generates FromClassNameString() which gets an enum from its string + representation. + """ + c = Code() + classname = cpp_util.Classname(schema_util.StripSchemaNamespace(prop.name)) + if use_namespace: + namespace = '%s::' % cpp_namespace + else: + namespace = '' + + (c.Append('// static') + .Sblock('%(namespace)s%(class)s' + ' %(namespace)sFrom%(class)sString(' + 'const std::string& enum_string) {')) + enum_prop = self._cpp_type_generator.GetReferencedProperty(prop) + for i, enum_value in enumerate( + self._cpp_type_generator.GetReferencedProperty(prop).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._cpp_type_generator.GetEnumValue(prop, enum_value))) + (c.Append('return %s;' % + self._cpp_type_generator.GetEnumNoneValue(prop)) + .Eblock('}') + .Substitute({ + 'namespace': namespace, + 'class': classname + })) return c # TODO(chebert): This is basically the same as GenerateCreateEnumTypeValue(). @@ -764,29 +821,20 @@ class CCGenerator(object): representation of an enum. """ c = Code() - c.Append('// static') - c.Sblock('scoped_ptr<base::Value> %(cpp_namespace)s::CreateEnumValue(' + (c.Append('// static') + .Sblock('scoped_ptr<base::Value> %(cpp_namespace)s::CreateEnumValue(' '%(arg)s) {') - c.Sblock('switch (%s) {' % prop.unix_name) - if prop.optional: - c.Concat(self._GenerateReturnCase( - self._cpp_type_generator.GetEnumNoneValue(prop), - 'scoped_ptr<base::Value>()')) - for enum_value in prop.enum_values: - c.Concat(self._GenerateReturnCase( - self._cpp_type_generator.GetEnumValue(prop, enum_value), - 'scoped_ptr<base::Value>(base::Value::CreateStringValue("%s"))' % - enum_value)) - (c.Eblock('}') - .Append('NOTREACHED();') - .Append('return scoped_ptr<base::Value>();') + .Append('std::string enum_temp = ToString(%s);' % prop.unix_name) + .Append('if (enum_temp.empty())') + .Append(' return scoped_ptr<base::Value>();') + .Append('return scoped_ptr<base::Value>(' + 'base::Value::CreateStringValue(enum_temp));') .Eblock('}') .Substitute({ 'cpp_namespace': cpp_namespace, 'arg': cpp_util.GetParameterDeclaration( prop, self._cpp_type_generator.GetType(prop)) - }) - ) + })) return c def _GenerateReturnCase(self, case_value, return_value): diff --git a/tools/json_schema_compiler/cpp_type_generator.py b/tools/json_schema_compiler/cpp_type_generator.py index 95329c3..deade36 100644 --- a/tools/json_schema_compiler/cpp_type_generator.py +++ b/tools/json_schema_compiler/cpp_type_generator.py @@ -6,6 +6,7 @@ from code import Code from model import PropertyType import any_helper import cpp_util +import operator import schema_util class CppTypeGenerator(object): @@ -98,20 +99,15 @@ class CppTypeGenerator(object): """Gets the enum value in the given model.Property indicating no value has been set. """ - prop_name = prop.unix_name - if (self.IsEnumRef(prop)): - prop_name = self.GetCompiledType(prop).upper() - return '%s_NONE' % prop_name.upper() + return '%s_NONE' % self.GetReferencedProperty(prop).unix_name.upper() def GetEnumValue(self, prop, enum_value): """Gets the enum value of the given model.Property of the given type. e.g VAR_STRING """ - prop_name = prop.unix_name.upper() - if (self.IsEnumRef(prop)): - prop_name = self.GetCompiledType(prop).upper() - return '%s_%s' % (prop_name, cpp_util.Classname(enum_value.upper())) + return '%s_%s' % (self.GetReferencedProperty(prop).unix_name.upper(), + cpp_util.Classname(enum_value.upper())) def GetChoicesEnumType(self, prop): """Gets the type of the enum for the given model.Property. @@ -209,7 +205,7 @@ class CppTypeGenerator(object): c = Code() namespace_type_dependencies = self._NamespaceTypeDependencies() for namespace in sorted(namespace_type_dependencies.keys(), - key=lambda ns: ns.name): + key=operator.attrgetter('name')): c.Append('namespace %s {' % namespace.name) for type_ in sorted(namespace_type_dependencies[namespace], key=schema_util.StripSchemaNamespace): @@ -222,7 +218,8 @@ class CppTypeGenerator(object): 'name': type_name, 'item_type': self.GetType(namespace.types[type_].item_type, wrap_optional=True)}) - else: + # Enums cannot be forward declared. + elif namespace.types[type_].type_ != PropertyType.ENUM: c.Append('struct %s;' % type_name) c.Append('}') c.Concat(self.GetNamespaceStart()) diff --git a/tools/json_schema_compiler/cpp_type_generator_test.py b/tools/json_schema_compiler/cpp_type_generator_test.py index decf1e9..4bfa507 100755 --- a/tools/json_schema_compiler/cpp_type_generator_test.py +++ b/tools/json_schema_compiler/cpp_type_generator_test.py @@ -120,7 +120,7 @@ class CppTypeGeneratorTest(unittest.TestCase): 'typedef std::vector<int> ColorArray;\n' '}\n' 'namespace fontSettings {\n' - 'typedef std::string ScriptCode;\n' + 'typedef std::string FakeStringType;\n' '}\n' 'namespace dependency_tester {\n' '} // dependency_tester', @@ -154,7 +154,8 @@ class CppTypeGeneratorTest(unittest.TestCase): self.font_settings.unix_name) self.assertEquals( 'std::string', - manager.GetType(self.font_settings.types['fontSettings.ScriptCode'])) + manager.GetType( + self.font_settings.types['fontSettings.FakeStringType'])) def testArrayAsType(self): manager = CppTypeGenerator('', self.browser_action, diff --git a/tools/json_schema_compiler/h_generator.py b/tools/json_schema_compiler/h_generator.py index c1df7ba..05a0241 100644 --- a/tools/json_schema_compiler/h_generator.py +++ b/tools/json_schema_compiler/h_generator.py @@ -135,8 +135,7 @@ class HGenerator(object): """ c = Code() c.Sblock('enum %s {' % enum_name) - if prop.optional: - c.Append(self._cpp_type_generator.GetEnumNoneValue(prop) + ',') + c.Append(self._cpp_type_generator.GetEnumNoneValue(prop) + ',') for value in values: c.Append(self._cpp_type_generator.GetEnumValue(prop, value) + ',') (c.Eblock('};') @@ -193,12 +192,16 @@ class HGenerator(object): if type_.description: c.Comment(type_.description) c.Sblock('enum %(classname)s {') + c.Append('%s,' % self._cpp_type_generator.GetEnumNoneValue(type_)) for value in type_.enum_values: - c.Append('%s_%s,' % (classname.upper(), value.upper())) + c.Append('%s,' % self._cpp_type_generator.GetEnumValue(type_, value)) (c.Eblock('};') .Append() .Append('scoped_ptr<base::Value> CreateEnumValue(%s %s);' % (classname, classname.lower())) + .Append('std::string ToString(%s enum_param);' % classname) + .Append('%s From%sString(const std::string& enum_string);' % + (classname, classname)) ) else: if type_.description: @@ -312,12 +315,19 @@ class HGenerator(object): prop.enum_values)) create_enum_value = ('scoped_ptr<base::Value> CreateEnumValue(%s %s);' % (enum_name, prop.unix_name)) + enum_to_string = 'std::string ToString(%s enum_param);' % enum_name + enum_from_string = ('%s From%sString(const std::string& enum_string);' % + (enum_name, enum_name)) # If the property is from the UI then we're in a struct so this function # should be static. If it's from the client, then we're just in a # namespace so we can't have the static keyword. if prop.from_json: - create_enum_value = 'static ' + create_enum_value - c.Append(create_enum_value) + create_enum_value = 'static %s' % create_enum_value + enum_to_string = 'static %s' % enum_to_string + enum_from_string = 'static %s' % enum_from_string + (c.Append(create_enum_value) + .Append(enum_to_string) + .Append(enum_from_string)) return c def _GeneratePrivatePropertyStructures(self, props): diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py index 29c54a0..38a70ac 100644 --- a/tools/json_schema_compiler/model.py +++ b/tools/json_schema_compiler/model.py @@ -79,13 +79,13 @@ class Type(object): self.item_type = Property(self, name + "Element", json['items'], from_json=True, from_client=True) - elif json.get('type') == 'string': - self.type_ = PropertyType.STRING elif 'enum' in json: self.enum_values = [] for value in json['enum']: self.enum_values.append(value) self.type_ = PropertyType.ENUM + elif json.get('type') == 'string': + self.type_ = PropertyType.STRING else: if not ( 'properties' in json or @@ -95,6 +95,7 @@ class Type(object): raise ParseException(self, name + " has no properties or functions") self.type_ = PropertyType.OBJECT self.name = name + self.unix_name = UnixName(self.name) self.description = json.get('description') self.from_json = True self.from_client = True diff --git a/tools/json_schema_compiler/test/dependency_tester.json b/tools/json_schema_compiler/test/dependency_tester.json index aec4c15..15ff4ac 100644 --- a/tools/json_schema_compiler/test/dependency_tester.json +++ b/tools/json_schema_compiler/test/dependency_tester.json @@ -21,7 +21,7 @@ "$ref": "browserAction.ColorArray" }, "scriptCode": { - "$ref": "fontSettings.ScriptCode" + "$ref": "fontSettings.FakeStringType" } } } diff --git a/tools/json_schema_compiler/test/enums.json b/tools/json_schema_compiler/test/enums.json index 48c2c980..a0bea46 100644 --- a/tools/json_schema_compiler/test/enums.json +++ b/tools/json_schema_compiler/test/enums.json @@ -4,7 +4,7 @@ "types": [ { "id": "Enumeration", - "enum": ["none", "one", "two", "three"] + "enum": ["one", "two", "three"] }, { "id": "EnumType", diff --git a/tools/json_schema_compiler/test/enums_unittest.cc b/tools/json_schema_compiler/test/enums_unittest.cc index b9fec70..4604694 100644 --- a/tools/json_schema_compiler/test/enums_unittest.cc +++ b/tools/json_schema_compiler/test/enums_unittest.cc @@ -33,10 +33,10 @@ TEST(JsonSchemaCompilerEnumsTest, EnumsAsTypes) { scoped_ptr<TakesEnumAsType::Params> params( TakesEnumAsType::Params::Create(args)); ASSERT_TRUE(params.get()); - EXPECT_EQ(ENUMERATION_ONE, params->enumeration); + EXPECT_EQ(ENUMS_ENUMERATION_ONE, params->enumeration); EXPECT_TRUE(args.Equals(ReturnsEnumAsType::Results::Create( - ENUMERATION_ONE).get())); + ENUMS_ENUMERATION_ONE).get())); } { HasEnumeration enumeration; diff --git a/tools/json_schema_compiler/test/font_settings.json b/tools/json_schema_compiler/test/font_settings.json index be68083..0326aca 100644 --- a/tools/json_schema_compiler/test/font_settings.json +++ b/tools/json_schema_compiler/test/font_settings.json @@ -22,14 +22,9 @@ } }, { - "id": "ScriptCode", + "id": "FakeStringType", "type": "string", - "enum": [ "Arab", "Armn", "Beng", "Cans", "Cher", "Cyrl", "Deva", "Ethi", "Geor", - "Grek", "Gujr", "Guru", "Hang", "Hans", "Hant", "Hebr", "Hrkt", "Knda", - "Khmr", "Laoo", "Mlym", "Mong", "Mymr", "Orya", "Sinh", "Taml", "Telu", - "Thaa", "Thai", "Tibt", "Yiii" - ], - "description": "An ISO 15924 script code." + "description": "Used to test a string type." }, { "id": "GenericFamily", @@ -54,7 +49,7 @@ "type": "object", "properties": { "script": { - "$ref": "ScriptCode", + "$ref": "FakeStringType", "description": "The script for which the font should be cleared. If omitted, the global script font setting is cleared.", "optional": true }, @@ -81,7 +76,7 @@ "type": "object", "properties": { "script": { - "$ref": "ScriptCode", + "$ref": "FakeStringType", "description": "The script for which the font should be retrieved. If omitted, the font for the global script is retrieved.", "optional": true }, @@ -119,7 +114,7 @@ "type": "object", "properties": { "script": { - "$ref": "ScriptCode", + "$ref": "FakeStringType", "description": "The script code which the font should be set. If omitted, the font for the global script is set.", "optional": true }, @@ -454,7 +449,7 @@ "properties": { "fontName": { "type": "string" }, "script": { - "$ref": "ScriptCode", + "$ref": "FakeStringType", "description": "The script code for which the font setting has changed. If omitted, the global script font setting has changed.", "optional": true }, diff --git a/tools/json_schema_compiler/util.h b/tools/json_schema_compiler/util.h index 509b0dc..6943590 100644 --- a/tools/json_schema_compiler/util.h +++ b/tools/json_schema_compiler/util.h @@ -44,6 +44,17 @@ bool GetItemFromList(const ListValue& from, int index, linked_ptr<T>* out) { return true; } +// This is used for getting an enum out of a ListValue, which will happen if an +// array of enums is a parameter to a function. +template<class T> +bool GetItemFromList(const ListValue& from, int index, T* out) { + int value; + if (!from.GetInteger(index, &value)) + return false; + *out = static_cast<T>(value); + 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> |