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
diff --git a/lib/java/build.xml b/lib/java/build.xml
index 1215de1..c686c7d 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 2469892..e69de29 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 0000000..d816a78
--- /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 0000000..efb7630
--- /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 0000000..83607d8
--- /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 0000000..6dcbb34
--- /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 0000000..96aecbc
--- /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 0000000..dcc9f07
--- /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 0000000..8fd6ce0
--- /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 0000000..daf9b44
--- /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");
+ }
+}