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            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         * @return the CxxType for the specified java type; never null
191         * @throws IllegalArgumentException if the Java Type can not be coerced to a CxxType
192         */
193        public static CxxType getThriftType(Type javaType)
194                throws IllegalArgumentException
195        {
196                checkArgument(javaType instanceof Class<?>|| javaType instanceof ParameterizedType,
197                                "invalid javaType"  + javaType);
198            Class<?> rawType = TypeToken.of(javaType).getRawType();
199            CxxType manualType = manualTypes.get(rawType);
200            if (manualType != null) {
201                return manualType;
202            }
203            CxxType buildin = THRIFT_BUILTIN_CXX_TYPES.get(rawType);
204            if(null != buildin){
205                return buildin;
206            }
207            CxxType uiType = THRIFT_CAST_CXX_TYPES.get(rawType);
208            if(null != uiType){
209                return uiType;
210            }
211            
212            if (Enum.class.isAssignableFrom(rawType)) {
213                return enumType(rawType);
214            }
215            if (rawType.isArray()) {
216                Class<?> elementType = rawType.getComponentType();
217                return array(getThriftType(elementType));
218            }
219            if (Map.class.isAssignableFrom(rawType)) {
220                Type mapKeyType = getMapKeyType(javaType);
221                Type mapValueType = getMapValueType(javaType);
222                return map(getThriftType(mapKeyType), getThriftType(mapValueType));
223            }
224            if (Set.class.isAssignableFrom(rawType)) {
225                Type elementType = getIterableType(javaType);
226                return set(getThriftType(elementType));
227            }
228            if (Iterable.class.isAssignableFrom(rawType)) {
229                Type elementType = getIterableType(javaType);
230                return list(getThriftType(elementType));
231            }
232            if(Exception.class.isAssignableFrom(rawType)){
233                return exception(rawType);
234            }
235                checkArgument(Object.class != rawType,"unsupport not type Object.class");
236                String pkg = rawType.getPackage().getName();
237                checkArgument( !pkg.startsWith("java.") && !pkg.startsWith("javax."),"not allow type %s",javaType);
238            return struct(rawType);
239        }
240        public static void addThriftType(CxxType thriftType)
241        {
242            manualTypes.put(TypeToken.of(thriftType.getJavaType()).getRawType(), thriftType);
243        }
244        private CxxType(Type javaType, CxxTypeMeta thriftCxxType, CxxTypeMeta uiType){
245                this.javaType = checkNotNull(javaType, "javaType is null");
246                this.stubType=checkNotNull(thriftCxxType, "thriftCxxType is null");
247                this.uiType = uiType;
248        }
249        public CxxTypeMeta getUiType(){
250                return MoreObjects.firstNonNull(uiType, stubType);
251        }
252        public CxxTypeMeta getStubType(){
253                return stubType;
254        }
255        public ThriftProtocolType getProtocolType() {
256                return getStubType().getProtocolType();
257        }
258        public Type getJavaType() {
259                return javaType;
260        }
261    public boolean isException() {
262                Class<?> rawType = TypeToken.of(javaType).getRawType();
263                return Exception.class.isAssignableFrom(rawType);
264        }
265        public boolean isPrimitive(){
266                Type type = getJavaType();
267                if(type instanceof Class<?>){
268                        Class<?> clazz = (Class<?>)type;
269                        return clazz.isPrimitive();
270                }
271                return false;
272        }
273        @Override
274        public String toString() {
275                StringBuilder builder = new StringBuilder();
276                builder.append("CxxType [javaType=");
277                builder.append(javaType);
278                builder.append(", uiType=");
279                builder.append(uiType);
280                builder.append(", thriftCxxType=");
281                builder.append(stubType);
282                builder.append("]");
283                return builder.toString();
284        }
285        @Override
286        public int hashCode() {
287                final int prime = 31;
288                int result = 1;
289                result = prime * result + ((uiType == null) ? 0 : uiType.hashCode());
290                result = prime * result + ((javaType == null) ? 0 : javaType.hashCode());
291                result = prime * result + ((stubType == null) ? 0 : stubType.hashCode());
292                return result;
293        }
294        @Override
295        public boolean equals(Object obj) {
296                if (this == obj)
297                        return true;
298                if (obj == null)
299                        return false;
300                if (getClass() != obj.getClass())
301                        return false;
302                CxxType other = (CxxType) obj;
303                if (uiType == null) {
304                        if (other.uiType != null)
305                                return false;
306                } else if (!uiType.equals(other.uiType))
307                        return false;
308                if (javaType == null) {
309                        if (other.javaType != null)
310                                return false;
311                } else if (!javaType.equals(other.javaType))
312                        return false;
313                if (stubType == null) {
314                        if (other.stubType != null)
315                                return false;
316                } else if (!stubType.equals(other.stubType))
317                        return false;
318                return true;
319        }
320        
321}