001package net.gdface.thrift; 002 003import static com.google.common.base.Preconditions.checkArgument; 004import static com.google.common.base.Preconditions.checkNotNull; 005import static net.gdface.thrift.TypeTransformer.getInstance; 006 007import java.lang.reflect.Field; 008import java.lang.reflect.InvocationTargetException; 009import java.lang.reflect.Method; 010import java.lang.reflect.Modifier; 011import java.util.Map; 012import java.util.Map.Entry; 013 014import javax.annotation.concurrent.Immutable; 015 016import com.google.common.base.Throwables; 017import com.google.common.cache.CacheBuilder; 018import com.google.common.cache.CacheLoader; 019import com.google.common.cache.LoadingCache; 020import com.google.common.collect.ImmutableMap; 021import com.microsoft.thrifty.Struct; 022import com.microsoft.thrifty.StructBuilder; 023 024@Immutable 025public class ThriftyStructMetadata { 026 public static final LoadingCache<Class<?>,ThriftyStructMetadata> 027 STRUCTS_CACHE = 028 CacheBuilder.newBuilder().build( 029 new CacheLoader<Class<?>,ThriftyStructMetadata>(){ 030 @Override 031 public ThriftyStructMetadata load(Class<?> key) throws Exception { 032 return new ThriftyStructMetadata(key); 033 }}); 034 private final Class<?> structType; 035 /** å—æ®µIDå¯¹åº”çš„å—æ®µå¯¹è±¡ */ 036 private final ImmutableMap<String, Field> fields; 037 /** builderå¯¹è±¡æ‰€æœ‰å—æ®µçš„set方法 */ 038 private final ImmutableMap<String, Method> buildSetters; 039 private final Class<? extends StructBuilder<?>> builderClass; 040 private final Method buildMethod; 041 @SuppressWarnings("unchecked") 042 private ThriftyStructMetadata(Class<?> structType ) { 043 this.structType = checkNotNull(structType,"struct is null"); 044 checkArgument(Struct.class.isAssignableFrom(structType), 045 "structType %s not immplement the %s",structType.getName(),Struct.class.getName()); 046 try { 047 String className = structType.getName() + "$Builder"; 048 builderClass = (Class<? extends StructBuilder<?>>) Class.forName(className); 049 } catch (Exception e) { 050 Throwables.throwIfUnchecked(e); 051 throw new RuntimeException(e); 052 } 053 ImmutableMap.Builder<String, Field> fieldBuilder = ImmutableMap.builder(); 054 ImmutableMap.Builder<String, Method> methodBuilder = ImmutableMap.builder(); 055 056 for(Field field:structType.getDeclaredFields()){ 057 if((field.getModifiers() & Modifier.STATIC)==0){ 058 fieldBuilder.put(field.getName(), field); 059 try { 060 Method method = builderClass.getMethod(field.getName(), field.getType()); 061 methodBuilder.put(field.getName(), method); 062 } catch (NoSuchMethodException e) { 063 throw new RuntimeException(e); 064 } 065 } 066 } 067 fields = fieldBuilder.build(); 068 buildSetters = methodBuilder.build(); 069 070 try { 071 buildMethod = builderClass.getDeclaredMethod("build"); 072 } catch (Exception e) { 073 Throwables.throwIfUnchecked(e); 074 throw new RuntimeException(e); 075 } 076 } 077 public ImmutableMap<String, Field> getFields() { 078 return fields; 079 } 080 public Class<?> getStructType() { 081 return structType; 082 } 083 @SuppressWarnings("unchecked") 084 public <L,R>void setValue(Object instance,short id,L value){ 085 checkNotNull(instance,"instance is null"); 086 checkArgument(structType.isInstance(instance),"invalid value type,required %s",structType.getName()); 087 Field field = fields.get(id); 088 checkNotNull(field,"invalid field id=%s for %s",Short.toString(id),structType.getName()); 089 Class<?> fieldType = field.getType(); 090 try { 091 field.setAccessible(true); 092 if(value == null){ 093 checkArgument(!fieldType.isPrimitive(), 094 "primitive field %s of %s required not null value",field.getName(),structType.getName()); 095 field.set(instance,null); 096 } 097 else{ 098 field.set(instance, getInstance().to(value, (Class<L>) value.getClass(), fieldType)); 099 } 100 } catch (Exception e) { 101 Throwables.throwIfUnchecked(e); 102 throw new RuntimeException(e); 103 } 104 } 105 @SuppressWarnings("unchecked") 106 private <V>V getValue(Object instance,String name){ 107 checkArgument(structType.isInstance(instance),"invalid value type,required %s",structType.getName()); 108 109 Field field = fields.get(name); 110 checkNotNull(field,"invalid field name=%s for %s",name,structType.getName()); 111 try { 112 return (V) field.get(instance); 113 } catch (Exception e) { 114 Throwables.throwIfUnchecked(e); 115 throw new RuntimeException(e); 116 } 117 } 118 119 /** 120 * æ ¹æ®å—æ®µå€¼æž„é€ å®žä¾‹ 121 * @param fieldValues 122 * @return 123 */ 124 @SuppressWarnings("unchecked") 125 public <T>T constructStruct(Map<String, TypeValue> fieldValues){ 126 checkNotNull(fieldValues,"fieldValues is null"); 127 try { 128 // new Builder() 129 StructBuilder<T> builder = (StructBuilder<T>) builderClass.newInstance(); 130 for(Entry<String, TypeValue> entry : fieldValues.entrySet()){ 131 Method method = buildSetters.get(entry.getKey()); 132 checkNotNull(method,"method is null"); 133 TypeValue typeValue= entry.getValue(); 134 // 调用Builderçš„è®¾ç½®æ–¹æ³•è®¾ç½®å—æ®µå€¼ 135 method.invoke(builder, getInstance().cast( 136 typeValue.value, 137 typeValue.type, 138 fields.get(entry.getKey()).getGenericType())); 139 } 140 // build() 141 return (T) buildMethod.invoke(builder); 142 } catch (InvocationTargetException e){ 143 Throwables.throwIfUnchecked(e.getCause()); 144 throw new RuntimeException(e.getCause()); 145 }catch (Exception e) { 146 Throwables.throwIfUnchecked(e); 147 throw new RuntimeException(e); 148 } 149 } 150 /** 151 * 从 {@link com.microsoft.thrifty.Struct} 实例ä¸è¿”å›žæ‰€æœ‰å—æ®µå€¼ 152 * @param instance 153 * @return 154 */ 155 Map<String, TypeValue> getFieldValues(Object instance){ 156 checkNotNull(instance,"instance is null"); 157 checkArgument(structType.isInstance(instance),"invalid value type,required %s",structType.getName()); 158 ImmutableMap.Builder<String, TypeValue> builder = ImmutableMap.builder(); 159 for(Entry<String, Field> entry:fields.entrySet()){ 160 String name = entry.getKey(); 161 Field field = entry.getValue(); 162 builder.put(name, new TypeValue(field.getType(), getValue(instance, name))); 163 } 164 return builder.build(); 165 } 166}