summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan Duxbury <bryanduxbury@apache.org>2009-01-29 01:51:08 +0000
committerBryan Duxbury <bryanduxbury@apache.org>2009-01-29 01:51:08 +0000
commit986d705578f7c3189b076b43097da0ff2f5d648e (patch)
tree7aee1c12f09a9446067864ccbef6a5f108647adb
parent9a75aa51922c53d2dd010cbf962bdcd2302833aa (diff)
downloadthrift-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.cc93
-rw-r--r--lib/java/build.xml2
-rw-r--r--lib/java/src/org/apache/thrift/FieldMetaData.java8
-rw-r--r--lib/java/src/org/apache/thrift/TFieldRequirementType.java11
-rw-r--r--lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java50
-rw-r--r--lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java23
-rw-r--r--lib/java/src/org/apache/thrift/meta_data/ListMetaData.java10
-rw-r--r--lib/java/src/org/apache/thrift/meta_data/MapMetaData.java12
-rw-r--r--lib/java/src/org/apache/thrift/meta_data/SetMetaData.java10
-rw-r--r--lib/java/src/org/apache/thrift/meta_data/StructMetaData.java10
-rw-r--r--lib/java/test/org/apache/thrift/test/MetaDataTest.java60
-rw-r--r--test/ThriftTest.thrift8
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
+}