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}