diff options
author | Bryan Duxbury <bryanduxbury@apache.org> | 2009-01-29 01:51:08 +0000 |
---|---|---|
committer | Bryan Duxbury <bryanduxbury@apache.org> | 2009-01-29 01:51:08 +0000 |
commit | 986d705578f7c3189b076b43097da0ff2f5d648e (patch) | |
tree | 7aee1c12f09a9446067864ccbef6a5f108647adb | |
parent | 9a75aa51922c53d2dd010cbf962bdcd2302833aa (diff) | |
download | thrift-986d705578f7c3189b076b43097da0ff2f5d648e.tar.gz |
THRIFT-253. java: Enhance FieldMetaData
The code generator new creates a static map of field id to metadata for each field, including information like the field TType, class of embedded structs, required/optional/default, etc. Additionally, on loading, generated classes statically register their class and metadata map with the global FieldMetaData map, so you can get the metadata for any TBase-implementing class easily.
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@738708 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | compiler/cpp/src/generate/t_java_generator.cc | 93 | ||||
-rw-r--r-- | lib/java/build.xml | 2 | ||||
-rw-r--r-- | lib/java/src/org/apache/thrift/FieldMetaData.java | 8 | ||||
-rw-r--r-- | lib/java/src/org/apache/thrift/TFieldRequirementType.java | 11 | ||||
-rw-r--r-- | lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java | 50 | ||||
-rw-r--r-- | lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java | 23 | ||||
-rw-r--r-- | lib/java/src/org/apache/thrift/meta_data/ListMetaData.java | 10 | ||||
-rw-r--r-- | lib/java/src/org/apache/thrift/meta_data/MapMetaData.java | 12 | ||||
-rw-r--r-- | lib/java/src/org/apache/thrift/meta_data/SetMetaData.java | 10 | ||||
-rw-r--r-- | lib/java/src/org/apache/thrift/meta_data/StructMetaData.java | 10 | ||||
-rw-r--r-- | lib/java/test/org/apache/thrift/test/MetaDataTest.java | 60 | ||||
-rw-r--r-- | test/ThriftTest.thrift | 8 |
12 files changed, 286 insertions, 11 deletions
diff --git a/compiler/cpp/src/generate/t_java_generator.cc b/compiler/cpp/src/generate/t_java_generator.cc index 439aeba08..3e718fbe9 100644 --- a/compiler/cpp/src/generate/t_java_generator.cc +++ b/compiler/cpp/src/generate/t_java_generator.cc @@ -11,6 +11,7 @@ #include <vector> #include <sys/stat.h> +#include <stdexcept> #include "platform.h" #include "t_oop_generator.h" @@ -80,6 +81,8 @@ class t_java_generator : public t_oop_generator { void generate_java_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_java_struct_tostring(std::ofstream& out, t_struct* tstruct); void generate_java_meta_data_map(std::ofstream& out, t_struct* tstruct); + void generate_field_value_meta_data(std::ofstream& out, t_type* type); + std::string get_java_type_string(t_type* type); void generate_reflection_setters(std::ostringstream& out, t_type* type, std::string field_name, std::string cap_name); void generate_reflection_getters(std::ostringstream& out, t_type* type, std::string field_name, std::string cap_name); void generate_generic_field_getters_setters(std::ofstream& out, t_struct* tstruct); @@ -259,7 +262,8 @@ string t_java_generator::java_type_imports() { "import java.util.HashSet;\n" + "import java.util.Collections;\n" + hash_builder + - "import org.apache.thrift.*;\n\n"; + "import org.apache.thrift.*;\n" + + "import org.apache.thrift.meta_data.*;\n\n"; } /** @@ -635,6 +639,13 @@ void t_java_generator::generate_java_struct_definition(ofstream &out, } generate_java_meta_data_map(out, tstruct); + + // Static initializer to populate global class to struct metadata map + indent(out) << "static {" << endl; + indent_up(); + indent(out) << "FieldMetaData.addStructMetaDataMap(" << tstruct->get_name() << ".class, metaDataMap);" << endl; + indent_down(); + indent(out) << "}" << endl << endl; // Default constructor indent(out) << @@ -1509,12 +1520,90 @@ void t_java_generator::generate_java_meta_data_map(ofstream& out, for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { t_field* field = *f_iter; std::string field_name = field->get_name(); - indent(out) << "put(" << upcase_string(field_name) << ", new FieldMetaData(\"" << field_name << "\"));" << endl; + indent(out) << "put(" << upcase_string(field_name) << ", new FieldMetaData(\"" << field_name << "\", "; + + // Set field requirement type (required, optional, etc.) + if (field->get_req() == t_field::T_REQUIRED) { + out << "TFieldRequirementType.REQUIRED, "; + } else if (field->get_req() == t_field::T_OPTIONAL) { + out << "TFieldRequirementType.OPTIONAL, "; + } else { + out << "TFieldRequirementType.DEFAULT, "; + } + + // Create value meta data + generate_field_value_meta_data(out, field->get_type()); + out << "));" << endl; } indent_down(); indent(out) << "}});" << endl << endl; } +/** + * Returns a string with the java representation of the given thrift type + * (e.g. for the type struct it returns "TType.STRUCT") + */ +std::string t_java_generator::get_java_type_string(t_type* type) { + if (type->is_list()){ + return "TType.LIST"; + } else if (type->is_map()) { + return "TType.MAP"; + } else if (type->is_set()) { + return "TType.SET"; + } else if (type->is_struct() || type->is_xception()) { + return "TType.STRUCT"; + } else if (type->is_enum()) { + return "TType.I32"; + } else if (type->is_typedef()) { + return get_java_type_string(((t_typedef*)type)->get_type()); + } else if (type->is_base_type()) { + switch (((t_base_type*)type)->get_base()) { + case t_base_type::TYPE_VOID : return "TType.VOID"; break; + case t_base_type::TYPE_STRING : return "TType.STRING"; break; + case t_base_type::TYPE_BOOL : return "TType.BOOL"; break; + case t_base_type::TYPE_BYTE : return "TType.BYTE"; break; + case t_base_type::TYPE_I16 : return "TType.I16"; break; + case t_base_type::TYPE_I32 : return "TType.I32"; break; + case t_base_type::TYPE_I64 : return "TType.I64"; break; + case t_base_type::TYPE_DOUBLE : return "TType.DOUBLE"; break; + default : throw std::runtime_error("Unknown thrift type \"" + type->get_name() + "\" passed to t_java_generator::get_java_type_string!"); break; // This should never happen! + } + } else { + throw std::runtime_error("Unknown thrift type \"" + type->get_name() + "\" passed to t_java_generator::get_java_type_string!"); // This should never happen! + } +} + +void t_java_generator::generate_field_value_meta_data(std::ofstream& out, t_type* type){ + out << endl; + indent_up(); + indent_up(); + if (type->is_struct()){ + indent(out) << "new StructMetaData(TType.STRUCT, " << type->get_name() << ".class"; + } else if (type->is_container()){ + if (type->is_list()){ + indent(out) << "new ListMetaData(TType.LIST, "; + t_type* elem_type = ((t_list*)type)->get_elem_type(); + generate_field_value_meta_data(out, elem_type); + } else if (type->is_set()){ + indent(out) << "new SetMetaData(TType.SET, "; + t_type* elem_type = ((t_list*)type)->get_elem_type(); + generate_field_value_meta_data(out, elem_type); + } else{ // map + indent(out) << "new MapMetaData(TType.MAP, "; + t_type* key_type = ((t_map*)type)->get_key_type(); + t_type* val_type = ((t_map*)type)->get_val_type(); + generate_field_value_meta_data(out, key_type); + out << ", "; + generate_field_value_meta_data(out, val_type); + } + } else { + indent(out) << "new FieldValueMetaData(" << get_java_type_string(type); + } + out << ")"; + indent_down(); + indent_down(); +} + /** * Generates a thrift service. In C++, this comprises an entirely separate diff --git a/lib/java/build.xml b/lib/java/build.xml index 1215de123..c686c7da6 100644 --- a/lib/java/build.xml +++ b/lib/java/build.xml @@ -61,6 +61,8 @@ classpath="${cpath}:${build.test}" failonerror="true" /> <java classname="org.apache.thrift.test.DeepCopyTest" classpath="${cpath}:${build.test}" failonerror="true" /> + <java classname="org.apache.thrift.test.MetaDataTest" + classpath="${cpath}:${build.test}" failonerror="true" /> <java classname="org.apache.thrift.test.JavaBeansTest" classpath="${cpath}:${build.test}" failonerror="true" /> </target> diff --git a/lib/java/src/org/apache/thrift/FieldMetaData.java b/lib/java/src/org/apache/thrift/FieldMetaData.java index 246989233..e69de29bb 100644 --- a/lib/java/src/org/apache/thrift/FieldMetaData.java +++ b/lib/java/src/org/apache/thrift/FieldMetaData.java @@ -1,8 +0,0 @@ -package org.apache.thrift; - -public class FieldMetaData implements java.io.Serializable { - public final String fieldName; - public FieldMetaData(String fieldName){ - this.fieldName = fieldName; - } -} diff --git a/lib/java/src/org/apache/thrift/TFieldRequirementType.java b/lib/java/src/org/apache/thrift/TFieldRequirementType.java new file mode 100644 index 000000000..d816a7894 --- /dev/null +++ b/lib/java/src/org/apache/thrift/TFieldRequirementType.java @@ -0,0 +1,11 @@ +package org.apache.thrift; + +/** + * Requirement type constants. + * + */ +public final class TFieldRequirementType { + public static final byte REQUIRED = 1; + public static final byte OPTIONAL = 2; + public static final byte DEFAULT = 3; +} diff --git a/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java b/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java new file mode 100644 index 000000000..efb7630c3 --- /dev/null +++ b/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java @@ -0,0 +1,50 @@ +package org.apache.thrift.meta_data; + +import java.util.HashMap; +import java.util.Map; +import org.apache.thrift.TBase; + +/** + * This class is used to store meta data about thrift fields. Every field in a + * a struct should have a corresponding instance of this class describing it. + * + */ +public class FieldMetaData implements java.io.Serializable { + public final String fieldName; + public final byte requirementType; + public final FieldValueMetaData valueMetaData; + private static Map<Class<? extends TBase>, Map> structMap; + + static { + structMap = new HashMap<Class<? extends TBase>, Map>(); + } + + public FieldMetaData(String name, byte req, FieldValueMetaData vMetaData){ + this.fieldName = name; + this.requirementType = req; + this.valueMetaData = vMetaData; + } + + public static void addStructMetaDataMap(Class<? extends TBase> sClass, Map map){ + structMap.put(sClass, map); + } + + /** + * Returns a map with metadata (i.e. instances of FieldMetaData) that + * describe the fields of the given class. + * + * @param sClass The TBase class for which the metadata map is requested + */ + public static Map<Integer, FieldMetaData> getStructMetaDataMap(Class<? extends TBase> sClass){ + if (!structMap.containsKey(sClass)){ // Load class if it hasn't been loaded + try{ + sClass.newInstance(); + } catch (InstantiationException e){ + throw new RuntimeException("InstantiationException for TBase class: " + sClass.getName() + ", message: " + e.getMessage()); + } catch (IllegalAccessException e){ + throw new RuntimeException("IllegalAccessException for TBase class: " + sClass.getName() + ", message: " + e.getMessage()); + } + } + return structMap.get(sClass); + } +} diff --git a/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java b/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java new file mode 100644 index 000000000..83607d863 --- /dev/null +++ b/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java @@ -0,0 +1,23 @@ +package org.apache.thrift.meta_data; + +import org.apache.thrift.protocol.TType; + +/** + * FieldValueMetaData and collection of subclasses to store metadata about + * the value(s) of a field + */ +public class FieldValueMetaData implements java.io.Serializable { + public final byte type; + + public FieldValueMetaData(byte type){ + this.type = type; + } + + public boolean isStruct() { + return type == TType.STRUCT; + } + + public boolean isContainer() { + return type == TType.LIST || type == TType.MAP || type == TType.SET; + } +} diff --git a/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java b/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java new file mode 100644 index 000000000..6dcbb3475 --- /dev/null +++ b/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java @@ -0,0 +1,10 @@ +package org.apache.thrift.meta_data; + +public class ListMetaData extends FieldValueMetaData { + public final FieldValueMetaData elemMetaData; + + public ListMetaData(byte type, FieldValueMetaData eMetaData){ + super(type); + this.elemMetaData = eMetaData; + } +} diff --git a/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java b/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java new file mode 100644 index 000000000..96aecbc85 --- /dev/null +++ b/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java @@ -0,0 +1,12 @@ +package org.apache.thrift.meta_data; + +public class MapMetaData extends FieldValueMetaData { + public final FieldValueMetaData keyMetaData; + public final FieldValueMetaData valueMetaData; + + public MapMetaData(byte type, FieldValueMetaData kMetaData, FieldValueMetaData vMetaData){ + super(type); + this.keyMetaData = kMetaData; + this.valueMetaData = vMetaData; + } +} diff --git a/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java b/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java new file mode 100644 index 000000000..dcc9f07b1 --- /dev/null +++ b/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java @@ -0,0 +1,10 @@ +package org.apache.thrift.meta_data; + +public class SetMetaData extends FieldValueMetaData { + public final FieldValueMetaData elemMetaData; + + public SetMetaData(byte type, FieldValueMetaData eMetaData){ + super(type); + this.elemMetaData = eMetaData; + } +} diff --git a/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java b/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java new file mode 100644 index 000000000..8fd6ce0e3 --- /dev/null +++ b/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java @@ -0,0 +1,10 @@ +package org.apache.thrift.meta_data; + +public class StructMetaData extends FieldValueMetaData { + public final Class structClass; + + public StructMetaData(byte type, Class sClass){ + super(type); + this.structClass = sClass; + } +} diff --git a/lib/java/test/org/apache/thrift/test/MetaDataTest.java b/lib/java/test/org/apache/thrift/test/MetaDataTest.java new file mode 100644 index 000000000..daf9b44cd --- /dev/null +++ b/lib/java/test/org/apache/thrift/test/MetaDataTest.java @@ -0,0 +1,60 @@ + +package org.apache.thrift.test; + +import java.util.Map; +import org.apache.thrift.TFieldRequirementType; +import org.apache.thrift.meta_data.FieldMetaData; +import org.apache.thrift.meta_data.ListMetaData; +import org.apache.thrift.meta_data.MapMetaData; +import org.apache.thrift.meta_data.SetMetaData; +import org.apache.thrift.meta_data.StructMetaData; +import org.apache.thrift.protocol.TType; +import thrift.test.*; + +public class MetaDataTest { + + public static void main(String[] args) throws Exception { + CrazyNesting cn = new CrazyNesting(); + Insanity in = new Insanity(); + Map<Integer, FieldMetaData> mdMap = cn.metaDataMap; + + // Check for struct fields existence + if (mdMap.size() != 3) + throw new RuntimeException("metadata map contains wrong number of entries!"); + if (!mdMap.containsKey(CrazyNesting.SET_FIELD) || !mdMap.containsKey(CrazyNesting.LIST_FIELD) || !mdMap.containsKey(CrazyNesting.STRING_FIELD)) + throw new RuntimeException("metadata map doesn't contain entry for a struct field!"); + + // Check for struct fields contents + if (!mdMap.get(CrazyNesting.STRING_FIELD).fieldName.equals("string_field") || + !mdMap.get(CrazyNesting.LIST_FIELD).fieldName.equals("list_field") || + !mdMap.get(CrazyNesting.SET_FIELD).fieldName.equals("set_field")) + throw new RuntimeException("metadata map contains a wrong fieldname"); + if (mdMap.get(CrazyNesting.STRING_FIELD).requirementType != TFieldRequirementType.DEFAULT || + mdMap.get(CrazyNesting.LIST_FIELD).requirementType != TFieldRequirementType.REQUIRED || + mdMap.get(CrazyNesting.SET_FIELD).requirementType != TFieldRequirementType.OPTIONAL) + throw new RuntimeException("metadata map contains the wrong requirement type for a field"); + if (mdMap.get(CrazyNesting.STRING_FIELD).valueMetaData.type != TType.STRING || + mdMap.get(CrazyNesting.LIST_FIELD).valueMetaData.type != TType.LIST || + mdMap.get(CrazyNesting.SET_FIELD).valueMetaData.type != TType.SET) + throw new RuntimeException("metadata map contains the wrong requirement type for a field"); + + // Check nested structures + if (!mdMap.get(CrazyNesting.LIST_FIELD).valueMetaData.isContainer()) + throw new RuntimeException("value metadata for a list is stored as non-container!"); + if (mdMap.get(CrazyNesting.LIST_FIELD).valueMetaData.isStruct()) + throw new RuntimeException("value metadata for a list is stored as a struct!"); + if (((MapMetaData)((ListMetaData)((SetMetaData)((MapMetaData)((MapMetaData)((ListMetaData)mdMap.get(CrazyNesting.LIST_FIELD).valueMetaData).elemMetaData).valueMetaData).valueMetaData).elemMetaData).elemMetaData).keyMetaData.type != TType.STRUCT) + throw new RuntimeException("metadata map contains wrong type for a value in a deeply nested structure"); + if (((StructMetaData)((MapMetaData)((ListMetaData)((SetMetaData)((MapMetaData)((MapMetaData)((ListMetaData)mdMap.get(CrazyNesting.LIST_FIELD).valueMetaData).elemMetaData).valueMetaData).valueMetaData).elemMetaData).elemMetaData).keyMetaData).structClass != Insanity.class) + throw new RuntimeException("metadata map contains wrong class for a struct in a deeply nested structure"); + + // Check that FieldMetaData contains a map with metadata for all generated struct classes + if (FieldMetaData.getStructMetaDataMap(CrazyNesting.class) == null || + FieldMetaData.getStructMetaDataMap(Insanity.class) == null || + FieldMetaData.getStructMetaDataMap(Xtruct.class) == null) + throw new RuntimeException("global metadata map doesn't contain an entry for a known struct"); + if (FieldMetaData.getStructMetaDataMap(CrazyNesting.class) != cn.metaDataMap || + FieldMetaData.getStructMetaDataMap(Insanity.class) != in.metaDataMap) + throw new RuntimeException("global metadata map contains wrong entry for a loaded struct"); + } +} diff --git a/test/ThriftTest.thrift b/test/ThriftTest.thrift index 275b574f5..a042d7efd 100644 --- a/test/ThriftTest.thrift +++ b/test/ThriftTest.thrift @@ -43,6 +43,12 @@ struct Insanity 2: list<Xtruct> xtructs } +struct CrazyNesting { + 1: string string_field, + 2: optional set<Insanity> set_field, + 3: required list< map<set<i32>,map<i32,set<list<map<Insanity,string>>>>>> list_field +} + exception Xception { 1: i32 errorCode, 2: string message @@ -129,4 +135,4 @@ struct ListTypeVersioningV1 { struct ListTypeVersioningV2 { 1: list<string> strings; 2: string hello; -}
\ No newline at end of file +} |