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, STRING)
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        String cxxType = CxxHelper.cxxClassName((Class<?>)javaType,true);
093        CxxType type = new CxxType(javaType,CxxTypeMeta.struct(cxxType),null);
094        return type;
095    }
096    public static CxxType exception(Type javaType)
097    {           
098            checkNotNull(javaType, "javaType is null");
099            checkArgument(javaType instanceof Class<?>,"javaType must be a Class");
100        String cxxType = CxxHelper.cxxClassName((Class<?>)javaType,true);
101        return new CxxType(javaType,CxxTypeMeta.struct(cxxType),null);
102    }
103    public static CxxType enumType(Type javaType)
104        {
105            checkNotNull(javaType, "javaType is null");
106            checkArgument(javaType instanceof Class<?>,"javaType must be a Class");
107            String cxxType = CxxHelper.cxxClassName((Class<?>)javaType,true);
108            return new CxxType(javaType,CxxTypeMeta.enumType(cxxType),null);
109        }
110
111        public static <K, V> CxxType map(CxxType keyType, CxxType valueType)
112    {
113                checkArgument(null != keyType, "keyType is null");
114                checkArgument(null != valueType, "valueType is null");
115
116        @SuppressWarnings({ "serial", "unchecked" })
117        Type javaType = new TypeToken<Map<K, V>>(){}
118                .where(new TypeParameter<K>(){}, (TypeToken<K>) TypeToken.of(keyType.getJavaType()))
119                .where(new TypeParameter<V>(){}, (TypeToken<V>) TypeToken.of(valueType.getJavaType()))
120                .getType();
121            CxxTypeMeta uiType = null;
122            if(null != keyType.uiType){
123                uiType = CxxTypeMeta.map(keyType.uiType, MoreObjects.firstNonNull(valueType.uiType,valueType.stubType));
124            }else if(null != valueType.uiType){
125                uiType = CxxTypeMeta.map(MoreObjects.firstNonNull(keyType.uiType,keyType.stubType),valueType.uiType);
126            }
127            CxxTypeMeta thriftCxxType = CxxTypeMeta.map(keyType.stubType,valueType.stubType);
128        return new CxxType(javaType, thriftCxxType, uiType);
129    }
130        public static CxxType container(ParameterizedType type )
131    {
132        checkNotNull(type, "type is null");
133        Class<?> rawType = TypeToken.of(type).getRawType();
134        Type[] actTypes = type.getActualTypeArguments();
135        if(Map.class.isAssignableFrom(rawType)){
136            return map(getThriftType(actTypes[0]), getThriftType(actTypes[1]));
137        }else if(List.class.isAssignableFrom(rawType)){
138            return list(getThriftType(actTypes[0]));
139        }else if(Set.class.isAssignableFrom(rawType)){
140                return set(getThriftType(actTypes[0]));
141        }
142        throw new IllegalArgumentException("unsupported type, type must be one of Map,List,Set:" + type);
143    }
144    public static <E> CxxType set(CxxType valueType)
145    {
146        checkArgument(null != valueType, "valueType is null");
147
148        @SuppressWarnings({ "serial", "unchecked" })
149        Type javaType = new TypeToken<Set<E>>(){}
150                .where(new TypeParameter<E>(){}, (TypeToken<E>) TypeToken.of(valueType.getJavaType()))
151                .getType();
152        CxxTypeMeta uiType = CxxTypeMeta.set(valueType.uiType);
153        CxxTypeMeta thriftCxxType = CxxTypeMeta.set(valueType.stubType);
154        return new CxxType(javaType, thriftCxxType, uiType);
155    }
156
157    public static <E> CxxType list(CxxType valueType)
158    {
159        checkArgument(null != valueType, "valueType is null");
160
161        @SuppressWarnings({ "serial", "unchecked" })
162        Type javaType = new TypeToken<List<E>>(){}
163                .where(new TypeParameter<E>(){}, (TypeToken<E>) TypeToken.of(valueType.getJavaType()))
164                .getType();
165        CxxTypeMeta uiType = CxxTypeMeta.list(valueType.uiType);
166        CxxTypeMeta thriftCxxType = CxxTypeMeta.list(valueType.stubType);
167        return new CxxType(javaType, thriftCxxType, uiType);
168    }
169
170    public static CxxType array(CxxType valueType)
171    {
172        checkArgument(null != valueType, "valueType is null");
173        Class<?> javaType = ReflectionHelper.getArrayOfType(valueType.getJavaType());
174        CxxTypeMeta thriftCxxType = CxxTypeMeta.list(valueType.stubType);
175        // array类型与list不同,必有转换类型
176        CxxTypeMeta uiType = CxxTypeMeta.array(MoreObjects.firstNonNull(valueType.uiType, valueType.stubType));
177        return new CxxType(javaType, thriftCxxType, uiType);
178    }
179
180        /**
181         * Gets the CxxType for the specified Java type.  The native Thrift type for the Java type will
182         * be inferred from the Java type, and if necessary type coercions will be applied.
183         *
184         * @return the CxxType for the specified java type; never null
185         * @throws IllegalArgumentException if the Java Type can not be coerced to a CxxType
186         */
187        public static CxxType getThriftType(Type javaType)
188                throws IllegalArgumentException
189        {
190                checkArgument(javaType instanceof Class<?>|| javaType instanceof ParameterizedType,
191                                "invalid javaType"  + javaType);
192            Class<?> rawType = TypeToken.of(javaType).getRawType();
193            CxxType manualType = manualTypes.get(rawType);
194            if (manualType != null) {
195                return manualType;
196            }
197            CxxType buildin = THRIFT_BUILTIN_CXX_TYPES.get(rawType);
198            if(null != buildin){
199                return buildin;
200            }
201            CxxType uiType = THRIFT_CAST_CXX_TYPES.get(rawType);
202            if(null != uiType){
203                return uiType;
204            }
205            
206            if (Enum.class.isAssignableFrom(rawType)) {
207                return enumType(rawType);
208            }
209            if (rawType.isArray()) {
210                Class<?> elementType = rawType.getComponentType();
211                return array(getThriftType(elementType));
212            }
213            if (Map.class.isAssignableFrom(rawType)) {
214                Type mapKeyType = getMapKeyType(javaType);
215                Type mapValueType = getMapValueType(javaType);
216                return map(getThriftType(mapKeyType), getThriftType(mapValueType));
217            }
218            if (Set.class.isAssignableFrom(rawType)) {
219                Type elementType = getIterableType(javaType);
220                return set(getThriftType(elementType));
221            }
222            if (Iterable.class.isAssignableFrom(rawType)) {
223                Type elementType = getIterableType(javaType);
224                return list(getThriftType(elementType));
225            }
226            if(Exception.class.isAssignableFrom(rawType)){
227                return exception(rawType);
228            }
229                checkArgument(Object.class != rawType,"unsupport not type Object.class");
230                String pkg = rawType.getPackage().getName();
231                checkArgument( !pkg.startsWith("java.") && !pkg.startsWith("javax."),"not allow type %s",javaType);
232            return struct(rawType);
233        }
234        public static void addThriftType(CxxType thriftType)
235        {
236            manualTypes.put(TypeToken.of(thriftType.getJavaType()).getRawType(), thriftType);
237        }
238        private CxxType(Type javaType, CxxTypeMeta thriftCxxType, CxxTypeMeta uiType){
239                this.javaType = checkNotNull(javaType, "javaType is null");
240                this.stubType=checkNotNull(thriftCxxType, "thriftCxxType is null");
241                this.uiType = uiType;
242        }
243        public CxxTypeMeta getUiType(){
244                return MoreObjects.firstNonNull(uiType, stubType);
245        }
246        public CxxTypeMeta getStubType(){
247                return stubType;
248        }
249        public ThriftProtocolType getProtocolType() {
250                return getStubType().getProtocolType();
251        }
252        public Type getJavaType() {
253                return javaType;
254        }
255    public boolean isException() {
256                Class<?> rawType = TypeToken.of(javaType).getRawType();
257                return Exception.class.isAssignableFrom(rawType);
258        }
259        public boolean isPrimitive(){
260                Type type = getJavaType();
261                if(type instanceof Class<?>){
262                        Class<?> clazz = (Class<?>)type;
263                        return clazz.isPrimitive();
264                }
265                return false;
266        }
267        @Override
268        public String toString() {
269                StringBuilder builder = new StringBuilder();
270                builder.append("CxxType [javaType=");
271                builder.append(javaType);
272                builder.append(", uiType=");
273                builder.append(uiType);
274                builder.append(", thriftCxxType=");
275                builder.append(stubType);
276                builder.append("]");
277                return builder.toString();
278        }
279        @Override
280        public int hashCode() {
281                final int prime = 31;
282                int result = 1;
283                result = prime * result + ((uiType == null) ? 0 : uiType.hashCode());
284                result = prime * result + ((javaType == null) ? 0 : javaType.hashCode());
285                result = prime * result + ((stubType == null) ? 0 : stubType.hashCode());
286                return result;
287        }
288        @Override
289        public boolean equals(Object obj) {
290                if (this == obj)
291                        return true;
292                if (obj == null)
293                        return false;
294                if (getClass() != obj.getClass())
295                        return false;
296                CxxType other = (CxxType) obj;
297                if (uiType == null) {
298                        if (other.uiType != null)
299                                return false;
300                } else if (!uiType.equals(other.uiType))
301                        return false;
302                if (javaType == null) {
303                        if (other.javaType != null)
304                                return false;
305                } else if (!javaType.equals(other.javaType))
306                        return false;
307                if (stubType == null) {
308                        if (other.stubType != null)
309                                return false;
310                } else if (!stubType.equals(other.stubType))
311                        return false;
312                return true;
313        }
314        
315}