001package net.gdface.codegen.thrift;
002
003import java.lang.reflect.ParameterizedType;
004import java.lang.reflect.Type;
005import java.net.URI;
006import java.net.URL;
007import java.nio.ByteBuffer;
008import java.util.Date;
009import java.util.List;
010import java.util.Map;
011import java.util.Set;
012import java.util.concurrent.ConcurrentHashMap;
013import java.util.concurrent.ConcurrentMap;
014
015import com.facebook.swift.codec.metadata.ReflectionHelper;
016import com.google.common.base.MoreObjects;
017import com.google.common.collect.ImmutableMap;
018import com.google.common.reflect.TypeParameter;
019import com.google.common.reflect.TypeToken;
020
021import net.gdface.codegen.thrift.CxxTypeMeta.ThriftProtocolType;
022
023import static com.facebook.swift.codec.metadata.ReflectionHelper.getIterableType;
024import static com.facebook.swift.codec.metadata.ReflectionHelper.getMapKeyType;
025import static com.facebook.swift.codec.metadata.ReflectionHelper.getMapValueType;
026import static com.google.common.base.Preconditions.*;
027
028/**
029 * 定义java转cxx的类型
030 * @author guyadong
031 *
032 */
033public class CxxType {
034        public static final CxxType BOOL = new CxxType(boolean.class, CxxTypeMeta.BOOL, null);
035    public static final CxxType BYTE = new CxxType(byte.class, CxxTypeMeta.BYTE, null);
036    public static final CxxType DOUBLE = new CxxType(double.class, CxxTypeMeta.DOUBLE, null);
037    public static final CxxType I16 = new CxxType(short.class, CxxTypeMeta.I16, null);
038    public static final CxxType I32 = new CxxType(int.class, CxxTypeMeta.I32, null);
039    public static final CxxType I64 = new CxxType(long.class, CxxTypeMeta.I64, null);
040    public static final CxxType STRING = new CxxType(String.class, CxxTypeMeta.STRING, null);
041    public static final CxxType BINARY = new CxxType(ByteBuffer.class, CxxTypeMeta.BINARY, null);
042    public static final CxxType VOID = new CxxType(void.class, CxxTypeMeta.VOID, null);
043    
044    private static final ConcurrentMap<Class<?>, CxxType> manualTypes = new ConcurrentHashMap<>();
045        public static final ImmutableMap<Class<?>, CxxType> THRIFT_BUILTIN_CXX_TYPES = ImmutableMap.<Class<?>, CxxType>builder()
046                        .put(boolean.class, BOOL)
047                        .put(byte.class, BYTE)
048                        .put(double.class, DOUBLE)
049                        .put(short.class, I16)
050                        .put(int.class, I32)
051                        .put(long.class, I64)
052                        .put(String.class, STRING)
053                        .put(ByteBuffer.class, BINARY)
054                        .put(void.class, VOID)
055                        .put(Boolean.class, new CxxType(Boolean.class, CxxTypeMeta.BOOL, null))
056                        .put(Byte.class, new CxxType(Byte.class, CxxTypeMeta.BYTE, null))
057                        .put(Double.class, new CxxType(Double.class, CxxTypeMeta.DOUBLE, null))
058                        .put(Short.class, new CxxType(Short.class, CxxTypeMeta.I16, null))
059                        .put(Integer.class, new CxxType(Integer.class, CxxTypeMeta.I32, null))
060                        .put(Long.class, new CxxType(Long.class, CxxTypeMeta.I64, null))
061                        .put(Void.class, new CxxType(Void.class, CxxTypeMeta.VOID, null))
062                        .build();
063        private static final CxxTypeMeta CAST_VECTOR= CxxTypeMeta.BINARY.cast(null,CxxTypeMeta.BYTE,true);
064        private static final CxxTypeMeta CAST_DATE= CxxTypeMeta.I64.cast("std::tm");
065        private static final CxxTypeMeta CAST_FLOAT= CxxTypeMeta.DOUBLE.cast("float");
066        public static final ImmutableMap<Class<?>, CxxType> THRIFT_CAST_CXX_TYPES = ImmutableMap.<Class<?>, CxxType>builder()
067                        .put(byte[].class,new CxxType(byte[].class,CxxTypeMeta.BINARY,null))
068                        .put(Date.class,new CxxType(Date.class, CxxTypeMeta.I64,CxxType.CAST_DATE))
069                        .put(java.sql.Date.class,new CxxType(java.sql.Date.class, CxxTypeMeta.I64,CxxType.CAST_DATE))
070                        .put(java.sql.Time.class,new CxxType(java.sql.Time.class, CxxTypeMeta.I64,CxxType.CAST_DATE))
071                        .put(float.class,new CxxType(float.class, CxxTypeMeta.DOUBLE,CxxType.CAST_FLOAT))
072                        .put(Float.class,new CxxType(Float.class, CxxTypeMeta.DOUBLE,CxxType.CAST_FLOAT))
073                        .put(char.class,new CxxType(char.class, CxxTypeMeta.I16,null))
074                        .put(Character.class,new CxxType(Character.class, CxxTypeMeta.I16,null))
075                        .put(URI.class,new CxxType(URI.class, CxxTypeMeta.STRING,null))
076                        .put(URL.class,new CxxType(URL.class, CxxTypeMeta.STRING,null))
077                        .build();       
078
079        private final Type javaType;
080        /**
081         * 内部类型
082         */
083        private final CxxTypeMeta stubType;
084        /**
085         * 用户接口类型,为null时即为{@link #stubType}
086         */
087        private final CxxTypeMeta uiType;
088        public static CxxType struct(Type javaType)
089    {           
090            checkNotNull(javaType, "javaType is null");
091            checkArgument(javaType instanceof Class<?>,"javaType must be a Class");         
092            Class<?> type = (Class<?>)javaType;
093            String stubType = "::"+CxxHelper.cxxNamespace(ThriftServiceDecoratorConfiguration.INSTANCE.getThriftClientPackage(),true) + "::" + type.getSimpleName();
094            String uiType = CxxHelper.cxxClassName((Class<?>)javaType,true);
095        return new CxxType(javaType,CxxTypeMeta.struct(stubType),CxxTypeMeta.struct(uiType));
096    }
097    public static CxxType exception(Type javaType)
098    {           
099            checkNotNull(javaType, "javaType is null");
100            checkArgument(javaType instanceof Class<?>,"javaType must be a Class");
101            Class<?> type = (Class<?>)javaType;
102            String stubType = "::"+CxxHelper.cxxNamespace(ThriftServiceDecoratorConfiguration.INSTANCE.getThriftClientPackage(),true) + "::" + type.getSimpleName();
103            String uiType = CxxHelper.cxxClassName((Class<?>)javaType,true);
104        return new CxxType(javaType,CxxTypeMeta.struct(stubType),CxxTypeMeta.struct(uiType));
105    }
106    public static CxxType enumType(Type javaType)
107        {
108            checkNotNull(javaType, "javaType is null");
109            checkArgument(javaType instanceof Class<?> && Enum.class.isAssignableFrom((Class<?>) javaType),
110                        "javaType (%s) must be a enum Class",javaType);
111            Class<?> type = (Class<?>)javaType;
112            String stubType = "::"+CxxHelper.cxxNamespace(ThriftServiceDecoratorConfiguration.INSTANCE.getThriftClientPackage(),true) + "::" + type.getSimpleName() + "::type";
113            String uiType = CxxHelper.cxxClassName((Class<?>)javaType,true);
114            return new CxxType(javaType,CxxTypeMeta.enumType(stubType),CxxTypeMeta.enumType(uiType));
115        }
116
117        public static <K, V> CxxType map(CxxType keyType, CxxType valueType)
118    {
119                checkArgument(null != keyType, "keyType is null");
120                checkArgument(null != valueType, "valueType is null");
121
122        @SuppressWarnings({ "serial", "unchecked" })
123        Type javaType = new TypeToken<Map<K, V>>(){}
124                .where(new TypeParameter<K>(){}, (TypeToken<K>) TypeToken.of(keyType.getJavaType()))
125                .where(new TypeParameter<V>(){}, (TypeToken<V>) TypeToken.of(valueType.getJavaType()))
126                .getType();
127            CxxTypeMeta uiType = null;
128            if(null != keyType.uiType){
129                uiType = CxxTypeMeta.map(keyType.uiType, MoreObjects.firstNonNull(valueType.uiType,valueType.stubType));
130            }else if(null != valueType.uiType){
131                uiType = CxxTypeMeta.map(MoreObjects.firstNonNull(keyType.uiType,keyType.stubType),valueType.uiType);
132            }
133            CxxTypeMeta thriftCxxType = CxxTypeMeta.map(keyType.stubType,valueType.stubType);
134        return new CxxType(javaType, thriftCxxType, uiType);
135    }
136        public static CxxType container(ParameterizedType type )
137    {
138        checkNotNull(type, "type is null");
139        Class<?> rawType = TypeToken.of(type).getRawType();
140        Type[] actTypes = type.getActualTypeArguments();
141        if(Map.class.isAssignableFrom(rawType)){
142            return map(getThriftType(actTypes[0]), getThriftType(actTypes[1]));
143        }else if(List.class.isAssignableFrom(rawType)){
144            return list(getThriftType(actTypes[0]));
145        }else if(Set.class.isAssignableFrom(rawType)){
146                return set(getThriftType(actTypes[0]));
147        }
148        throw new IllegalArgumentException("unsupported type, type must be one of Map,List,Set:" + type);
149    }
150    public static <E> CxxType set(CxxType valueType)
151    {
152        checkArgument(null != valueType, "valueType is null");
153
154        @SuppressWarnings({ "serial", "unchecked" })
155        Type javaType = new TypeToken<Set<E>>(){}
156                .where(new TypeParameter<E>(){}, (TypeToken<E>) TypeToken.of(valueType.getJavaType()))
157                .getType();
158        CxxTypeMeta uiType = CxxTypeMeta.set(valueType.uiType);
159        CxxTypeMeta thriftCxxType = CxxTypeMeta.set(valueType.stubType);
160        return new CxxType(javaType, thriftCxxType, uiType);
161    }
162
163    public static <E> CxxType list(CxxType valueType)
164    {
165        checkArgument(null != valueType, "valueType is null");
166
167        @SuppressWarnings({ "serial", "unchecked" })
168        Type javaType = new TypeToken<List<E>>(){}
169                .where(new TypeParameter<E>(){}, (TypeToken<E>) TypeToken.of(valueType.getJavaType()))
170                .getType();
171        CxxTypeMeta uiType = CxxTypeMeta.list(valueType.uiType);
172        CxxTypeMeta thriftCxxType = CxxTypeMeta.list(valueType.stubType);
173        return new CxxType(javaType, thriftCxxType, uiType);
174    }
175
176    public static CxxType array(CxxType valueType)
177    {
178        checkArgument(null != valueType, "valueType is null");
179        Class<?> javaType = ReflectionHelper.getArrayOfType(valueType.getJavaType());
180        CxxTypeMeta thriftCxxType = CxxTypeMeta.list(valueType.stubType);
181        // array类型与list不同,必有转换类型
182        CxxTypeMeta uiType = CxxTypeMeta.array(MoreObjects.firstNonNull(valueType.uiType, valueType.stubType));
183        return new CxxType(javaType, thriftCxxType, uiType);
184    }
185
186        /**
187         * Gets the CxxType for the specified Java type.  The native Thrift type for the Java type will
188         * be inferred from the Java type, and if necessary type coercions will be applied.
189         *
190         * @param javaType
191         * @return the CxxType for the specified java type; never null
192         * @throws IllegalArgumentException if the Java Type can not be coerced to a CxxType
193         */
194        public static CxxType getThriftType(Type javaType)
195                throws IllegalArgumentException
196        {
197                checkArgument(javaType instanceof Class<?>|| javaType instanceof ParameterizedType,
198                                "invalid javaType:"  + javaType);
199            Class<?> rawType = TypeToken.of(javaType).getRawType();
200            CxxType manualType = manualTypes.get(rawType);
201            if (manualType != null) {
202                return manualType;
203            }
204            CxxType buildin = THRIFT_BUILTIN_CXX_TYPES.get(rawType);
205            if(null != buildin){
206                return buildin;
207            }
208            CxxType uiType = THRIFT_CAST_CXX_TYPES.get(rawType);
209            if(null != uiType){
210                return uiType;
211            }
212            
213            if (Enum.class.isAssignableFrom(rawType)) {
214                return enumType(rawType);
215            }
216            if (rawType.isArray()) {
217                Class<?> elementType = rawType.getComponentType();
218                return array(getThriftType(elementType));
219            }
220            if (Map.class.isAssignableFrom(rawType)) {
221                Type mapKeyType = getMapKeyType(javaType);
222                Type mapValueType = getMapValueType(javaType);
223                return map(getThriftType(mapKeyType), getThriftType(mapValueType));
224            }
225            if (Set.class.isAssignableFrom(rawType)) {
226                Type elementType = getIterableType(javaType);
227                return set(getThriftType(elementType));
228            }
229            if (Iterable.class.isAssignableFrom(rawType)) {
230                Type elementType = getIterableType(javaType);
231                return list(getThriftType(elementType));
232            }
233            if(Exception.class.isAssignableFrom(rawType)){
234                return exception(rawType);
235            }
236                checkArgument(Object.class != rawType,"unsupport not type Object.class");
237                String pkg = rawType.getPackage().getName();
238                checkArgument( !pkg.startsWith("java.") && !pkg.startsWith("javax."),"not allow type %s",javaType);
239            return struct(rawType);
240        }
241        public static void addThriftType(CxxType thriftType)
242        {
243            manualTypes.put(TypeToken.of(thriftType.getJavaType()).getRawType(), thriftType);
244        }
245        private CxxType(Type javaType, CxxTypeMeta thriftCxxType, CxxTypeMeta uiType){
246                this.javaType = checkNotNull(javaType, "javaType is null");
247                this.stubType=checkNotNull(thriftCxxType, "thriftCxxType is null");
248                this.uiType = uiType;
249        }
250        public CxxTypeMeta getUiType(){
251                return MoreObjects.firstNonNull(uiType, stubType);
252        }
253        public CxxTypeMeta getStubType(){
254                return stubType;
255        }
256        public ThriftProtocolType getProtocolType() {
257                return getStubType().getProtocolType();
258        }
259        public Type getJavaType() {
260                return javaType;
261        }
262    public boolean isException() {
263                Class<?> rawType = TypeToken.of(javaType).getRawType();
264                return Exception.class.isAssignableFrom(rawType);
265        }
266        public boolean isPrimitive(){
267                return TypeToken.of(getJavaType()).isPrimitive();
268        }
269        public boolean isEnum(){
270                return TypeToken.of(getJavaType()).getRawType().isEnum();
271        }
272        public boolean isDate(){
273                Type type = getJavaType();
274                return type instanceof Class<?> && Date.class.isAssignableFrom((Class<?>) type);
275        }
276        /**
277         * 判断类型是否需要类型转换
278         * @return
279         */
280        public boolean isNeedCast(){
281                return !getUiType().equals(getStubType());
282        }
283        @Override
284        public String toString() {
285                StringBuilder builder = new StringBuilder();
286                builder.append("CxxType [javaType=");
287                builder.append(javaType);
288                builder.append(", uiType=");
289                builder.append(uiType);
290                builder.append(", thriftCxxType=");
291                builder.append(stubType);
292                builder.append("]");
293                return builder.toString();
294        }
295        @Override
296        public int hashCode() {
297                final int prime = 31;
298                int result = 1;
299                result = prime * result + ((uiType == null) ? 0 : uiType.hashCode());
300                result = prime * result + ((javaType == null) ? 0 : javaType.hashCode());
301                result = prime * result + ((stubType == null) ? 0 : stubType.hashCode());
302                return result;
303        }
304        @Override
305        public boolean equals(Object obj) {
306                if (this == obj)
307                        return true;
308                if (obj == null)
309                        return false;
310                if (getClass() != obj.getClass())
311                        return false;
312                CxxType other = (CxxType) obj;
313                if (uiType == null) {
314                        if (other.uiType != null)
315                                return false;
316                } else if (!uiType.equals(other.uiType))
317                        return false;
318                if (javaType == null) {
319                        if (other.javaType != null)
320                                return false;
321                } else if (!javaType.equals(other.javaType))
322                        return false;
323                if (stubType == null) {
324                        if (other.stubType != null)
325                                return false;
326                } else if (!stubType.equals(other.stubType))
327                        return false;
328                return true;
329        }
330        
331}