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}