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, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.thrift;
19 
20 import java.nio.ByteBuffer;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 
28 import org.apache.thrift.protocol.TField;
29 import org.apache.thrift.protocol.TProtocol;
30 import org.apache.thrift.protocol.TProtocolException;
31 import org.apache.thrift.protocol.TStruct;
32 import org.apache.thrift.scheme.IScheme;
33 import org.apache.thrift.scheme.SchemeFactory;
34 import org.apache.thrift.scheme.StandardScheme;
35 import org.apache.thrift.scheme.TupleScheme;
36 
37 public abstract class TUnion<T extends TUnion<T,F>, F extends TFieldIdEnum> implements TBase<T, F> {
38 
39   protected Object value_;
40   protected F setField_;
41 
TUnion()42   protected TUnion() {
43     setField_ = null;
44     value_ = null;
45   }
46 
47   private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
48   static {
schemes.put(StandardScheme.class, new TUnionStandardSchemeFactory())49     schemes.put(StandardScheme.class, new TUnionStandardSchemeFactory());
schemes.put(TupleScheme.class, new TUnionTupleSchemeFactory())50     schemes.put(TupleScheme.class, new TUnionTupleSchemeFactory());
51   }
52 
TUnion(F setField, Object value)53   protected TUnion(F setField, Object value) {
54     setFieldValue(setField, value);
55   }
56 
TUnion(TUnion<T, F> other)57   protected TUnion(TUnion<T, F> other) {
58     if (!other.getClass().equals(this.getClass())) {
59       throw new ClassCastException();
60     }
61     setField_ = other.setField_;
62     value_ = deepCopyObject(other.value_);
63   }
64 
deepCopyObject(Object o)65   private static Object deepCopyObject(Object o) {
66     if (o instanceof TBase) {
67       return ((TBase)o).deepCopy();
68     } else if (o instanceof ByteBuffer) {
69       return TBaseHelper.copyBinary((ByteBuffer)o);
70     } else if (o instanceof List) {
71       return deepCopyList((List)o);
72     } else if (o instanceof Set) {
73       return deepCopySet((Set)o);
74     } else if (o instanceof Map) {
75       return deepCopyMap((Map)o);
76     } else {
77       return o;
78     }
79   }
80 
deepCopyMap(Map<Object, Object> map)81   private static Map deepCopyMap(Map<Object, Object> map) {
82     Map copy = new HashMap(map.size());
83     for (Map.Entry<Object, Object> entry : map.entrySet()) {
84       copy.put(deepCopyObject(entry.getKey()), deepCopyObject(entry.getValue()));
85     }
86     return copy;
87   }
88 
deepCopySet(Set set)89   private static Set deepCopySet(Set set) {
90     Set copy = new HashSet(set.size());
91     for (Object o : set) {
92       copy.add(deepCopyObject(o));
93     }
94     return copy;
95   }
96 
deepCopyList(List list)97   private static List deepCopyList(List list) {
98     List copy = new ArrayList(list.size());
99     for (Object o : list) {
100       copy.add(deepCopyObject(o));
101     }
102     return copy;
103   }
104 
getSetField()105   public F getSetField() {
106     return setField_;
107   }
108 
getFieldValue()109   public Object getFieldValue() {
110     return value_;
111   }
112 
getFieldValue(F fieldId)113   public Object getFieldValue(F fieldId) {
114     if (fieldId != setField_) {
115       throw new IllegalArgumentException("Cannot get the value of field " + fieldId + " because union's set field is " + setField_);
116     }
117 
118     return getFieldValue();
119   }
120 
getFieldValue(int fieldId)121   public Object getFieldValue(int fieldId) {
122     return getFieldValue(enumForId((short)fieldId));
123   }
124 
isSet()125   public boolean isSet() {
126     return setField_ != null;
127   }
128 
isSet(F fieldId)129   public boolean isSet(F fieldId) {
130     return setField_ == fieldId;
131   }
132 
isSet(int fieldId)133   public boolean isSet(int fieldId) {
134     return isSet(enumForId((short)fieldId));
135   }
136 
read(TProtocol iprot)137   public void read(TProtocol iprot) throws TException {
138     schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
139   }
140 
setFieldValue(F fieldId, Object value)141   public void setFieldValue(F fieldId, Object value) {
142     checkType(fieldId, value);
143     setField_ = fieldId;
144     value_ = value;
145   }
146 
setFieldValue(int fieldId, Object value)147   public void setFieldValue(int fieldId, Object value) {
148     setFieldValue(enumForId((short)fieldId), value);
149   }
150 
write(TProtocol oprot)151   public void write(TProtocol oprot) throws TException {
152     schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
153   }
154 
155   /**
156    * Implementation should be generated so that we can efficiently type check
157    * various values.
158    * @param setField
159    * @param value
160    */
checkType(F setField, Object value)161   protected abstract void checkType(F setField, Object value) throws ClassCastException;
162 
163   /**
164    * Implementation should be generated to read the right stuff from the wire
165    * based on the field header.
166    * @param field
167    * @return read Object based on the field header, as specified by the argument.
168    */
standardSchemeReadValue(TProtocol iprot, TField field)169   protected abstract Object standardSchemeReadValue(TProtocol iprot, TField field) throws TException;
standardSchemeWriteValue(TProtocol oprot)170   protected abstract void standardSchemeWriteValue(TProtocol oprot) throws TException;
171 
tupleSchemeReadValue(TProtocol iprot, short fieldID)172   protected abstract Object tupleSchemeReadValue(TProtocol iprot, short fieldID) throws TException;
tupleSchemeWriteValue(TProtocol oprot)173   protected abstract void tupleSchemeWriteValue(TProtocol oprot) throws TException;
174 
getStructDesc()175   protected abstract TStruct getStructDesc();
176 
getFieldDesc(F setField)177   protected abstract TField getFieldDesc(F setField);
178 
enumForId(short id)179   protected abstract F enumForId(short id);
180 
181   @Override
toString()182   public String toString() {
183     StringBuilder sb = new StringBuilder();
184     sb.append("<");
185     sb.append(this.getClass().getSimpleName());
186     sb.append(" ");
187 
188     if (getSetField() != null) {
189       Object v = getFieldValue();
190       sb.append(getFieldDesc(getSetField()).name);
191       sb.append(":");
192       if(v instanceof ByteBuffer) {
193         TBaseHelper.toString((ByteBuffer)v, sb);
194       } else {
195         sb.append(v.toString());
196       }
197     }
198     sb.append(">");
199     return sb.toString();
200   }
201 
clear()202   public final void clear() {
203     this.setField_ = null;
204     this.value_ = null;
205   }
206 
207   private static class TUnionStandardSchemeFactory implements SchemeFactory {
getScheme()208     public TUnionStandardScheme getScheme() {
209       return new TUnionStandardScheme();
210     }
211   }
212 
213   private static class TUnionStandardScheme extends StandardScheme<TUnion> {
214 
215     @Override
read(TProtocol iprot, TUnion struct)216     public void read(TProtocol iprot, TUnion struct) throws TException {
217       struct.setField_ = null;
218       struct.value_ = null;
219 
220       iprot.readStructBegin();
221 
222       TField field = iprot.readFieldBegin();
223 
224       struct.value_ = struct.standardSchemeReadValue(iprot, field);
225       if (struct.value_ != null) {
226         struct.setField_ = struct.enumForId(field.id);
227       }
228 
229       iprot.readFieldEnd();
230       // this is so that we will eat the stop byte. we could put a check here to
231       // make sure that it actually *is* the stop byte, but it's faster to do it
232       // this way.
233       iprot.readFieldBegin();
234       iprot.readStructEnd();
235     }
236 
237     @Override
write(TProtocol oprot, TUnion struct)238     public void write(TProtocol oprot, TUnion struct) throws TException {
239       if (struct.getSetField() == null || struct.getFieldValue() == null) {
240         throw new TProtocolException("Cannot write a TUnion with no set value!");
241       }
242       oprot.writeStructBegin(struct.getStructDesc());
243       oprot.writeFieldBegin(struct.getFieldDesc(struct.setField_));
244       struct.standardSchemeWriteValue(oprot);
245       oprot.writeFieldEnd();
246       oprot.writeFieldStop();
247       oprot.writeStructEnd();
248     }
249   }
250 
251   private static class TUnionTupleSchemeFactory implements SchemeFactory {
getScheme()252     public TUnionTupleScheme getScheme() {
253       return new TUnionTupleScheme();
254     }
255   }
256 
257   private static class TUnionTupleScheme extends TupleScheme<TUnion> {
258 
259     @Override
read(TProtocol iprot, TUnion struct)260     public void read(TProtocol iprot, TUnion struct) throws TException {
261       struct.setField_ = null;
262       struct.value_ = null;
263       short fieldID = iprot.readI16();
264       struct.value_ = struct.tupleSchemeReadValue(iprot, fieldID);
265       if (struct.value_ != null) {
266         struct.setField_ = struct.enumForId(fieldID);
267       }
268     }
269 
270     @Override
write(TProtocol oprot, TUnion struct)271     public void write(TProtocol oprot, TUnion struct) throws TException {
272       if (struct.getSetField() == null || struct.getFieldValue() == null) {
273         throw new TProtocolException("Cannot write a TUnion with no set value!");
274       }
275       oprot.writeI16(struct.setField_.getThriftFieldId());
276       struct.tupleSchemeWriteValue(oprot);
277     }
278   }
279 }
280