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}