summaryrefslogtreecommitdiff
path: root/lib/java/src/org/apache/thrift/TDeserializer.java
diff options
context:
space:
mode:
Diffstat (limited to 'lib/java/src/org/apache/thrift/TDeserializer.java')
-rw-r--r--lib/java/src/org/apache/thrift/TDeserializer.java388
1 files changed, 379 insertions, 9 deletions
diff --git a/lib/java/src/org/apache/thrift/TDeserializer.java b/lib/java/src/org/apache/thrift/TDeserializer.java
index fc8cb8332..1433f6240 100644
--- a/lib/java/src/org/apache/thrift/TDeserializer.java
+++ b/lib/java/src/org/apache/thrift/TDeserializer.java
@@ -19,18 +19,29 @@
package org.apache.thrift;
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
-
+import org.apache.thrift.meta_data.EnumMetaData;
+import org.apache.thrift.meta_data.StructMetaData;
+import org.apache.thrift.partial.TFieldData;
+import org.apache.thrift.partial.ThriftFieldValueProcessor;
+import org.apache.thrift.partial.ThriftMetadata;
+import org.apache.thrift.partial.ThriftStructProcessor;
+import org.apache.thrift.partial.Validate;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TField;
+import org.apache.thrift.protocol.TList;
+import org.apache.thrift.protocol.TMap;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.protocol.TProtocolUtil;
+import org.apache.thrift.protocol.TSet;
import org.apache.thrift.protocol.TType;
import org.apache.thrift.transport.TMemoryInputTransport;
import org.apache.thrift.transport.TTransportException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.util.Collection;
+
/**
* Generic utility for easily deserializing objects from a byte array or Java
* String.
@@ -40,6 +51,12 @@ public class TDeserializer {
private final TProtocol protocol_;
private final TMemoryInputTransport trans_;
+ // Metadata that describes fields to deserialize during partial deserialization.
+ private ThriftMetadata.ThriftStruct metadata_ = null;
+
+ // Processor that handles deserialized field values during partial deserialization.
+ private ThriftFieldValueProcessor processor_ = null;
+
/**
* Create a new TDeserializer that uses the TBinaryProtocol by default.
*
@@ -62,6 +79,54 @@ public class TDeserializer {
}
/**
+ * Construct a new TDeserializer that supports partial deserialization
+ * that outputs instances of type controlled by the given {@code processor}.
+ *
+ * @param thriftClass a TBase derived class.
+ * @param fieldNames list of fields to deserialize.
+ * @param processor the Processor that handles deserialized field values.
+ * @param protocolFactory the Factory to create a protocol.
+ */
+ public TDeserializer(
+ Class<? extends TBase> thriftClass,
+ Collection<String> fieldNames,
+ ThriftFieldValueProcessor processor,
+ TProtocolFactory protocolFactory) throws TTransportException {
+ this(protocolFactory);
+
+ Validate.checkNotNull(thriftClass, "thriftClass");
+ Validate.checkNotNull(fieldNames, "fieldNames");
+ Validate.checkNotNull(processor, "processor");
+
+ metadata_ = ThriftMetadata.ThriftStruct.fromFieldNames(thriftClass, fieldNames);
+ processor_ = processor;
+ }
+
+ /**
+ * Construct a new TDeserializer that supports partial deserialization
+ * that outputs {@code TBase} instances.
+ *
+ * @param thriftClass a TBase derived class.
+ * @param fieldNames list of fields to deserialize.
+ * @param protocolFactory the Factory to create a protocol.
+ */
+ public TDeserializer(
+ Class<? extends TBase> thriftClass,
+ Collection<String> fieldNames,
+ TProtocolFactory protocolFactory) throws TTransportException {
+ this(thriftClass, fieldNames, new ThriftStructProcessor(), protocolFactory);
+ }
+
+ /**
+ * Gets the metadata used for partial deserialization.
+ *
+ * @return the metadata used for partial deserialization.
+ */
+ public ThriftMetadata.ThriftStruct getMetadata() {
+ return metadata_;
+ }
+
+ /**
* Deserialize the Thrift object from a byte array.
*
* @param base The object to read into
@@ -82,12 +147,16 @@ public class TDeserializer {
* @throws TException if an error is encountered during deserialization.
*/
public void deserialize(TBase base, byte[] bytes, int offset, int length) throws TException {
- try {
- trans_.reset(bytes, offset, length);
- base.read(protocol_);
- } finally {
- trans_.clear();
- protocol_.reset();
+ if (this.isPartialDeserializationMode()) {
+ this.partialDeserializeThriftObject(base, bytes, offset, length);
+ } else {
+ try {
+ trans_.reset(bytes, offset, length);
+ base.read(protocol_);
+ } finally {
+ trans_.clear();
+ protocol_.reset();
+ }
}
}
@@ -353,4 +422,305 @@ public class TDeserializer {
public void fromString(TBase base, String data) throws TException {
deserialize(base, data.getBytes());
}
+
+ // ----------------------------------------------------------------------
+ // Methods related to partial deserialization.
+
+ /**
+ * Partially deserializes the given serialized blob.
+ *
+ * @param bytes the serialized blob.
+ * @return deserialized instance.
+ * @throws TException if an error is encountered during deserialization.
+ */
+ public Object partialDeserializeObject(byte[] bytes) throws TException {
+ return this.partialDeserializeObject(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Partially deserializes the given serialized blob into the given {@code TBase} instance.
+ *
+ * @param base the instance into which the given blob is deserialized.
+ * @param bytes the serialized blob.
+ * @param offset the blob is read starting at this offset.
+ * @param length the size of blob read (in number of bytes).
+ * @return deserialized instance.
+ * @throws TException if an error is encountered during deserialization.
+ */
+ public Object partialDeserializeThriftObject(TBase base, byte[] bytes, int offset, int length)
+ throws TException {
+ ensurePartialThriftDeserializationMode();
+
+ return this.partialDeserializeObject(base, bytes, offset, length);
+ }
+
+ /**
+ * Partially deserializes the given serialized blob.
+ *
+ * @param bytes the serialized blob.
+ * @param offset the blob is read starting at this offset.
+ * @param length the size of blob read (in number of bytes).
+ * @return deserialized instance.
+ * @throws TException if an error is encountered during deserialization.
+ */
+ public Object partialDeserializeObject(byte[] bytes, int offset, int length) throws TException {
+ ensurePartialDeserializationMode();
+
+ return this.partialDeserializeObject(null, bytes, offset, length);
+ }
+
+ /**
+ * Partially deserializes the given serialized blob.
+ *
+ * @param instance the instance into which the given blob is deserialized.
+ * @param bytes the serialized blob.
+ * @param offset the blob is read starting at this offset.
+ * @param length the size of blob read (in number of bytes).
+ * @return deserialized instance.
+ * @throws TException if an error is encountered during deserialization.
+ */
+ private Object partialDeserializeObject(Object instance, byte[] bytes, int offset, int length)
+ throws TException {
+ ensurePartialDeserializationMode();
+
+ this.trans_.reset(bytes, offset, length);
+ this.protocol_.reset();
+ return this.deserializeStruct(instance, this.metadata_);
+ }
+
+ private Object deserialize(ThriftMetadata.ThriftObject data) throws TException {
+
+ Object value;
+ byte fieldType = data.data.valueMetaData.type;
+ switch (fieldType) {
+ case TType.STRUCT:
+ return this.deserializeStruct(null, (ThriftMetadata.ThriftStruct) data);
+
+ case TType.LIST:
+ return this.deserializeList((ThriftMetadata.ThriftList) data);
+
+ case TType.MAP:
+ return this.deserializeMap((ThriftMetadata.ThriftMap) data);
+
+ case TType.SET:
+ return this.deserializeSet((ThriftMetadata.ThriftSet) data);
+
+ case TType.ENUM:
+ return this.deserializeEnum((ThriftMetadata.ThriftEnum) data);
+
+ case TType.BOOL:
+ return this.protocol_.readBool();
+
+ case TType.BYTE:
+ return this.protocol_.readByte();
+
+ case TType.I16:
+ return this.protocol_.readI16();
+
+ case TType.I32:
+ return this.protocol_.readI32();
+
+ case TType.I64:
+ return this.protocol_.readI64();
+
+ case TType.DOUBLE:
+ return this.protocol_.readDouble();
+
+ case TType.STRING:
+ if (((ThriftMetadata.ThriftPrimitive) data).isBinary()) {
+ return this.processor_.prepareBinary(this.protocol_.readBinary());
+ } else {
+ return this.processor_.prepareString(this.protocol_.readBinary());
+ }
+
+ default:
+ throw unsupportedFieldTypeException(fieldType);
+ }
+ }
+
+ private Object deserializeStruct(Object instance, ThriftMetadata.ThriftStruct data)
+ throws TException {
+
+ if (instance == null) {
+ instance = this.processor_.createNewStruct(data);
+ }
+
+ this.protocol_.readStructBegin();
+ while (true) {
+ int tfieldData = this.protocol_.readFieldBeginData();
+ byte tfieldType = TFieldData.getType(tfieldData);
+ if (tfieldType == TType.STOP) {
+ break;
+ }
+
+ Integer id = (int) TFieldData.getId(tfieldData);
+ ThriftMetadata.ThriftObject field = (ThriftMetadata.ThriftObject) data.fields.get(id);
+
+ if (field != null) {
+ this.deserializeStructField(instance, field.fieldId, field);
+ } else {
+ this.protocol_.skip(tfieldType);
+ }
+ this.protocol_.readFieldEnd();
+ }
+ this.protocol_.readStructEnd();
+
+ return this.processor_.prepareStruct(instance);
+ }
+
+ private void deserializeStructField(
+ Object instance,
+ TFieldIdEnum fieldId,
+ ThriftMetadata.ThriftObject data) throws TException {
+
+ byte fieldType = data.data.valueMetaData.type;
+ Object value;
+
+ switch (fieldType) {
+ case TType.BOOL:
+ this.processor_.setBool(instance, fieldId, this.protocol_.readBool());
+ break;
+
+ case TType.BYTE:
+ this.processor_.setByte(instance, fieldId, this.protocol_.readByte());
+ break;
+
+ case TType.I16:
+ this.processor_.setInt16(instance, fieldId, this.protocol_.readI16());
+ break;
+
+ case TType.I32:
+ this.processor_.setInt32(instance, fieldId, this.protocol_.readI32());
+ break;
+
+ case TType.I64:
+ this.processor_.setInt64(instance, fieldId, this.protocol_.readI64());
+ break;
+
+ case TType.DOUBLE:
+ this.processor_.setDouble(instance, fieldId, this.protocol_.readDouble());
+ break;
+
+ case TType.STRING:
+ if (((ThriftMetadata.ThriftPrimitive) data).isBinary()) {
+ this.processor_.setBinary(instance, fieldId, this.protocol_.readBinary());
+ } else {
+ this.processor_.setString(instance, fieldId, this.protocol_.readBinary());
+ }
+ break;
+
+ case TType.STRUCT:
+ value = this.deserializeStruct(null, (ThriftMetadata.ThriftStruct) data);
+ this.processor_.setStructField(instance, fieldId, value);
+ break;
+
+ case TType.LIST:
+ value = this.deserializeList((ThriftMetadata.ThriftList) data);
+ this.processor_.setListField(instance, fieldId, value);
+ break;
+
+ case TType.MAP:
+ value = this.deserializeMap((ThriftMetadata.ThriftMap) data);
+ this.processor_.setMapField(instance, fieldId, value);
+ break;
+
+ case TType.SET:
+ value = this.deserializeSet((ThriftMetadata.ThriftSet) data);
+ this.processor_.setSetField(instance, fieldId, value);
+ break;
+
+ case TType.ENUM:
+ value = this.deserializeEnum((ThriftMetadata.ThriftEnum) data);
+ this.processor_.setEnumField(instance, fieldId, value);
+ break;
+
+ default:
+ throw new RuntimeException("Unsupported field type: " + fieldId.toString());
+ }
+ }
+
+ private Object deserializeList(ThriftMetadata.ThriftList data) throws TException {
+
+ TList tlist = this.protocol_.readListBegin();
+ Object instance = this.processor_.createNewList(tlist.size);
+ for (int i = 0; i < tlist.size; i++) {
+ Object value = this.deserialize(data.elementData);
+ this.processor_.setListElement(instance, i, value);
+ }
+ this.protocol_.readListEnd();
+ return this.processor_.prepareList(instance);
+ }
+
+ private Object deserializeMap(ThriftMetadata.ThriftMap data) throws TException {
+ TMap tmap = this.protocol_.readMapBegin();
+ Object instance = this.processor_.createNewMap(tmap.size);
+ for (int i = 0; i < tmap.size; i++) {
+ Object key = this.deserialize(data.keyData);
+ Object val = this.deserialize(data.valueData);
+ this.processor_.setMapElement(instance, i, key, val);
+ }
+ this.protocol_.readMapEnd();
+ return this.processor_.prepareMap(instance);
+ }
+
+ private Object deserializeSet(ThriftMetadata.ThriftSet data) throws TException {
+ TSet tset = this.protocol_.readSetBegin();
+ Object instance = this.processor_.createNewSet(tset.size);
+ for (int i = 0; i < tset.size; i++) {
+ Object eltValue = this.deserialize(data.elementData);
+ this.processor_.setSetElement(instance, i, eltValue);
+ }
+ this.protocol_.readSetEnd();
+ return this.processor_.prepareSet(instance);
+ }
+
+ private Object deserializeEnum(ThriftMetadata.ThriftEnum data) throws TException {
+ int ordinal = this.protocol_.readI32();
+ Class<? extends TEnum> enumClass = ((EnumMetaData) data.data.valueMetaData).enumClass;
+ return this.processor_.prepareEnum(enumClass, ordinal);
+ }
+
+ private <T extends TBase> T createNewStruct(ThriftMetadata.ThriftStruct data) {
+ T instance = null;
+
+ try {
+ instance = (T) this.getStructClass(data).newInstance();
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+
+ return instance;
+ }
+
+ private <T extends TBase> Class<T> getStructClass(ThriftMetadata.ThriftStruct data) {
+ return (Class<T>) ((StructMetaData) data.data.valueMetaData).structClass;
+ }
+
+ private static UnsupportedOperationException unsupportedFieldTypeException(byte fieldType) {
+ return new UnsupportedOperationException("field type not supported: " + fieldType);
+ }
+
+ private boolean isPartialDeserializationMode() {
+ return (this.metadata_ != null) && (this.processor_ != null);
+ }
+
+ private void ensurePartialDeserializationMode() throws IllegalStateException {
+ if (!this.isPartialDeserializationMode()) {
+ throw new IllegalStateException(
+ "Members metadata and processor must be correctly initialized in order to use this method"
+ );
+ }
+ }
+
+ private void ensurePartialThriftDeserializationMode() throws IllegalStateException {
+ this.ensurePartialDeserializationMode();
+
+ if (!(this.processor_ instanceof ThriftStructProcessor)) {
+ throw new IllegalStateException(
+ "processor must be an instance of ThriftStructProcessor to use this method"
+ );
+ }
+ }
}