1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 package org.apache.thrift;
21 
22 import java.io.UnsupportedEncodingException;
23 import java.nio.ByteBuffer;
24 
25 import org.apache.thrift.protocol.TBinaryProtocol;
26 import org.apache.thrift.protocol.TField;
27 import org.apache.thrift.protocol.TProtocol;
28 import org.apache.thrift.protocol.TProtocolFactory;
29 import org.apache.thrift.protocol.TProtocolUtil;
30 import org.apache.thrift.protocol.TType;
31 import org.apache.thrift.transport.TMemoryInputTransport;
32 import org.apache.thrift.transport.TTransportException;
33 
34 /**
35  * Generic utility for easily deserializing objects from a byte array or Java
36  * String.
37  *
38  */
39 public class TDeserializer {
40   private final TProtocol protocol_;
41   private final TMemoryInputTransport trans_;
42 
43   /**
44    * Create a new TDeserializer that uses the TBinaryProtocol by default.
45    */
TDeserializer()46   public TDeserializer() throws TTransportException {
47     this(new TBinaryProtocol.Factory());
48   }
49 
50   /**
51    * Create a new TDeserializer. It will use the TProtocol specified by the
52    * factory that is passed in.
53    *
54    * @param protocolFactory Factory to create a protocol
55    */
TDeserializer(TProtocolFactory protocolFactory)56   public TDeserializer(TProtocolFactory protocolFactory) throws TTransportException {
57     trans_ = new TMemoryInputTransport(new TConfiguration());
58     protocol_ = protocolFactory.getProtocol(trans_);
59   }
60 
61   /**
62    * Deserialize the Thrift object from a byte array.
63    *
64    * @param base The object to read into
65    * @param bytes The array to read from
66    */
deserialize(TBase base, byte[] bytes)67   public void deserialize(TBase base, byte[] bytes) throws TException {
68       deserialize(base, bytes, 0, bytes.length);
69   }
70 
71   /**
72    * Deserialize the Thrift object from a byte array.
73    *
74    * @param base The object to read into
75    * @param bytes The array to read from
76    * @param offset The offset into {@code bytes}
77    * @param length The length to read from {@code bytes}
78    */
deserialize(TBase base, byte[] bytes, int offset, int length)79   public void deserialize(TBase base, byte[] bytes, int offset, int length) throws TException {
80     try {
81       trans_.reset(bytes, offset, length);
82       base.read(protocol_);
83     } finally {
84       trans_.clear();
85       protocol_.reset();
86     }
87   }
88 
89   /**
90    * Deserialize the Thrift object from a Java string, using a specified
91    * character set for decoding.
92    *
93    * @param base The object to read into
94    * @param data The string to read from
95    * @param charset Valid JVM charset
96    */
deserialize(TBase base, String data, String charset)97   public void deserialize(TBase base, String data, String charset) throws TException {
98     try {
99       deserialize(base, data.getBytes(charset));
100     } catch (UnsupportedEncodingException uex) {
101       throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset);
102     } finally {
103       protocol_.reset();
104     }
105   }
106 
107   /**
108    * Deserialize only a single Thrift object (addressed by recursively using field id)
109    * from a byte record.
110    * @param tb The object to read into
111    * @param bytes The serialized object to read from
112    * @param fieldIdPathFirst First of the FieldId's that define a path tb
113    * @param fieldIdPathRest The rest FieldId's that define a path tb
114    * @throws TException
115    */
partialDeserialize(TBase tb, byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)116   public void partialDeserialize(TBase tb, byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
117     try {
118       if (locateField(bytes, fieldIdPathFirst, fieldIdPathRest) != null) {
119         // if this line is reached, iprot will be positioned at the start of tb.
120         tb.read(protocol_);
121       }
122     } catch (Exception e) {
123       throw new TException(e);
124     } finally {
125       trans_.clear();
126       protocol_.reset();
127     }
128   }
129 
130   /**
131    * Deserialize only a boolean field (addressed by recursively using field id)
132    * from a byte record.
133    * @param bytes The serialized object to read from
134    * @param fieldIdPathFirst First of the FieldId's that define a path to a boolean field
135    * @param fieldIdPathRest The rest FieldId's that define a path to a boolean field
136    * @throws TException
137    */
partialDeserializeBool(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)138   public Boolean partialDeserializeBool(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
139     return (Boolean) partialDeserializeField(TType.BOOL, bytes, fieldIdPathFirst, fieldIdPathRest);
140   }
141 
142   /**
143    * Deserialize only a byte field (addressed by recursively using field id)
144    * from a byte record.
145    * @param bytes The serialized object to read from
146    * @param fieldIdPathFirst First of the FieldId's that define a path to a byte field
147    * @param fieldIdPathRest The rest FieldId's that define a path to a byte field
148    * @throws TException
149    */
partialDeserializeByte(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)150   public Byte partialDeserializeByte(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
151     return (Byte) partialDeserializeField(TType.BYTE, bytes, fieldIdPathFirst, fieldIdPathRest);
152   }
153 
154   /**
155    * Deserialize only a double field (addressed by recursively using field id)
156    * from a byte record.
157    * @param bytes The serialized object to read from
158    * @param fieldIdPathFirst First of the FieldId's that define a path to a double field
159    * @param fieldIdPathRest The rest FieldId's that define a path to a double field
160    * @throws TException
161    */
partialDeserializeDouble(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)162   public Double partialDeserializeDouble(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
163     return (Double) partialDeserializeField(TType.DOUBLE, bytes, fieldIdPathFirst, fieldIdPathRest);
164   }
165 
166   /**
167    * Deserialize only an i16 field (addressed by recursively using field id)
168    * from a byte record.
169    * @param bytes The serialized object to read from
170    * @param fieldIdPathFirst First of the FieldId's that define a path to an i16 field
171    * @param fieldIdPathRest The rest FieldId's that define a path to an i16 field
172    * @throws TException
173    */
partialDeserializeI16(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)174   public Short partialDeserializeI16(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
175     return (Short) partialDeserializeField(TType.I16, bytes, fieldIdPathFirst, fieldIdPathRest);
176   }
177 
178   /**
179    * Deserialize only an i32 field (addressed by recursively using field id)
180    * from a byte record.
181    * @param bytes The serialized object to read from
182    * @param fieldIdPathFirst First of the FieldId's that define a path to an i32 field
183    * @param fieldIdPathRest The rest FieldId's that define a path to an i32 field
184    * @throws TException
185    */
partialDeserializeI32(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)186   public Integer partialDeserializeI32(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
187     return (Integer) partialDeserializeField(TType.I32, bytes, fieldIdPathFirst, fieldIdPathRest);
188   }
189 
190   /**
191    * Deserialize only an i64 field (addressed by recursively using field id)
192    * from a byte record.
193    * @param bytes The serialized object to read from
194    * @param fieldIdPathFirst First of the FieldId's that define a path to an i64 field
195    * @param fieldIdPathRest The rest FieldId's that define a path to an i64 field
196    * @throws TException
197    */
partialDeserializeI64(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)198   public Long partialDeserializeI64(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
199     return (Long) partialDeserializeField(TType.I64, bytes, fieldIdPathFirst, fieldIdPathRest);
200   }
201 
202   /**
203    * Deserialize only a string field (addressed by recursively using field id)
204    * from a byte record.
205    * @param bytes The serialized object to read from
206    * @param fieldIdPathFirst First of the FieldId's that define a path to a string field
207    * @param fieldIdPathRest The rest FieldId's that define a path to a string field
208    * @throws TException
209    */
partialDeserializeString(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)210   public String partialDeserializeString(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
211     return (String) partialDeserializeField(TType.STRING, bytes, fieldIdPathFirst, fieldIdPathRest);
212   }
213 
214   /**
215    * Deserialize only a binary field (addressed by recursively using field id)
216    * from a byte record.
217    * @param bytes The serialized object to read from
218    * @param fieldIdPathFirst First of the FieldId's that define a path to a binary field
219    * @param fieldIdPathRest The rest FieldId's that define a path to a binary field
220    * @throws TException
221    */
partialDeserializeByteArray(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)222   public ByteBuffer partialDeserializeByteArray(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
223     // TType does not have binary, so we use the arbitrary num 100
224     return (ByteBuffer) partialDeserializeField((byte)100, bytes, fieldIdPathFirst, fieldIdPathRest);
225   }
226 
227   /**
228    * Deserialize only the id of the field set in a TUnion (addressed by recursively using field id)
229    * from a byte record.
230    * @param bytes The serialized object to read from
231    * @param fieldIdPathFirst First of the FieldId's that define a path to a TUnion
232    * @param fieldIdPathRest The rest FieldId's that define a path to a TUnion
233    * @throws TException
234    */
partialDeserializeSetFieldIdInUnion(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)235   public Short partialDeserializeSetFieldIdInUnion(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)  throws TException {
236     try {
237       TField field = locateField(bytes, fieldIdPathFirst, fieldIdPathRest);
238       if (field != null){
239         protocol_.readStructBegin(); // The Union
240         return protocol_.readFieldBegin().id; // The field set in the union
241       }
242       return null;
243     } catch (Exception e) {
244       throw new TException(e);
245     } finally {
246       trans_.clear();
247       protocol_.reset();
248     }
249   }
250 
partialDeserializeField(byte ttype, byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)251   private Object partialDeserializeField(byte ttype, byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
252     try {
253       TField field = locateField(bytes, fieldIdPathFirst, fieldIdPathRest);
254       if (field != null) {
255         if (ttype == field.type) {
256           // if this point is reached, iprot will be positioned at the start of
257           // the field
258           switch (ttype) {
259           case TType.BOOL:
260             return protocol_.readBool();
261           case TType.BYTE:
262             return protocol_.readByte();
263           case TType.DOUBLE:
264             return protocol_.readDouble();
265           case TType.I16:
266             return protocol_.readI16();
267           case TType.I32:
268             return protocol_.readI32();
269           case TType.I64:
270             return protocol_.readI64();
271           case TType.STRING:
272             return protocol_.readString();
273           default:
274             return null;
275           }
276         }
277         // hack to differentiate between string and binary
278         if (ttype == 100 && field.type == TType.STRING) {
279           return protocol_.readBinary();
280         }
281       }
282       return null;
283     } catch (Exception e) {
284       throw new TException(e);
285     } finally {
286       trans_.clear();
287       protocol_.reset();
288     }
289   }
290 
locateField(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest)291   private TField locateField(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
292     trans_.reset(bytes);
293 
294     TFieldIdEnum[] fieldIdPath = new TFieldIdEnum[fieldIdPathRest.length + 1];
295     fieldIdPath[0] = fieldIdPathFirst;
296     System.arraycopy(fieldIdPathRest, 0, fieldIdPath, 1, fieldIdPathRest.length);
297 
298     // index into field ID path being currently searched for
299     int curPathIndex = 0;
300 
301     // this will be the located field, or null if it is not located
302     TField field = null;
303 
304     protocol_.readStructBegin();
305 
306     while (curPathIndex < fieldIdPath.length) {
307       field = protocol_.readFieldBegin();
308       // we can stop searching if we either see a stop or we go past the field
309       // id we're looking for (since fields should now be serialized in asc
310       // order).
311       if (field.type == TType.STOP || field.id > fieldIdPath[curPathIndex].getThriftFieldId()) {
312         return null;
313       }
314 
315       if (field.id != fieldIdPath[curPathIndex].getThriftFieldId()) {
316         // Not the field we're looking for. Skip field.
317         TProtocolUtil.skip(protocol_, field.type);
318         protocol_.readFieldEnd();
319       } else {
320         // This field is the next step in the path. Step into field.
321         curPathIndex++;
322         if (curPathIndex < fieldIdPath.length) {
323           protocol_.readStructBegin();
324         }
325       }
326     }
327     return field;
328   }
329 
330   /**
331    * Deserialize the Thrift object from a Java string, using the default JVM
332    * charset encoding.
333    *
334    * @param base The object to read into
335    * @param data The string to read from
336    */
fromString(TBase base, String data)337   public void fromString(TBase base, String data) throws TException {
338     deserialize(base, data.getBytes());
339   }
340 }
341