001package net.gdface.codegen.thrift; 002 003import net.gdface.codegen.AbstractSchema; 004import net.gdface.codegen.thrift.ThriftServiceDecoratorConfiguration.LanguageType; 005import net.gdface.thrift.ThriftDecorator; 006import net.gdface.thrift.ThriftUtils; 007import net.gdface.thrift.TypeTransformer; 008import net.gdface.utils.NameStringUtils; 009 010import static com.google.common.base.Preconditions.*; 011 012import java.beans.PropertyDescriptor; 013import java.lang.reflect.Type; 014import java.util.ArrayList; 015import java.util.Collections; 016import java.util.Comparator; 017import java.util.List; 018import java.util.Map; 019import java.util.Map.Entry; 020 021import org.slf4j.Logger; 022import org.slf4j.LoggerFactory; 023 024import com.google.common.base.Function; 025import com.google.common.base.Predicate; 026import com.google.common.base.Predicates; 027import com.google.common.base.Strings; 028import com.google.common.base.Throwables; 029import com.google.common.collect.ImmutableList; 030import com.google.common.collect.ImmutableMap; 031import com.google.common.collect.Iterables; 032import com.google.common.collect.Lists; 033import com.google.common.collect.Maps; 034 035/** 036 * 装饰类信息 037 * @author guyadong 038 * 039 */ 040public class ThriftStructDecorator extends AbstractSchema implements ThriftConstants,Comparator<ThriftStructDecorator>,Comparable<ThriftStructDecorator>{ 041 private static final Logger logger = LoggerFactory.getLogger(ThriftStructDecorator.class); 042 043 private final Map<String, PropertyDescriptor> fields; 044 private final String decoratorPackage; 045 private final String decoratorClassName; 046 private final TypeHelper typeHelper = new TypeHelper(this); 047 048 private final Map<String, CxxType> cxxFields; 049 050 private final boolean hasOptional; 051 private final boolean hasCanMove; 052 053 private final CxxType cxxType; 054 055 private final TraverseTypeForTryFind findString= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){ 056 @Override 057 public boolean apply(CxxTypeMeta input) { 058 return null != input && (input.isBinary() || input.isString()); 059 }}); 060 private final TraverseTypeForTryFind findMap= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){ 061 @Override 062 public boolean apply(CxxTypeMeta input) { 063 return null != input && input.isMap(); 064 }}); 065 private final TraverseTypeForTryFind findSet= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){ 066 @Override 067 public boolean apply(CxxTypeMeta input) { 068 return null != input && input.isSet(); 069 }}); 070 private final TraverseTypeForTryFind findArray= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){ 071 @Override 072 public boolean apply(CxxTypeMeta input) { 073 return null != input && input.isList(); 074 }}); 075 076 static final String FIELD_NEW = "new"; 077 static final String FIELD_MODIFIED = "modified"; 078 static final String FIELD_INITIALIZED = "initialized"; 079 static String sql2javaBeanPkgPrefix = null; 080 /** sql2java生成的JavaBean 类验证器 */ 081 static final Predicate<ThriftStructDecorator> SQL2JAVA_BEAN_FILTER = new Predicate<ThriftStructDecorator>(){ 082 private boolean checkField(ThriftStructDecorator input,String name,Class<?> type){ 083 try{ 084 PropertyDescriptor field = input.getField(name); 085 return (field.getPropertyType().equals(type)) 086 && field.getReadMethod() != null 087 && field.getWriteMethod() != null; 088 }catch (Exception e) { 089 return false; 090 } 091 } 092 @Override 093 public boolean apply(ThriftStructDecorator input) { 094 try{ 095 boolean m1 = checkField(input,FIELD_NEW,boolean.class) 096 && checkField(input,FIELD_MODIFIED,int.class) 097 && checkField(input,FIELD_INITIALIZED,int.class); 098 boolean m2 = checkField(input,FIELD_NEW,boolean.class) 099 && checkField(input,FIELD_MODIFIED,int[].class) 100 && checkField(input,FIELD_INITIALIZED,int[].class); 101 boolean m = m1 || m2; 102 if(!Strings.isNullOrEmpty(ThriftStructDecorator.sql2javaBeanPkgPrefix)){ 103 m = m && input.getBaseClass().getPackage().getName().startsWith(ThriftStructDecorator.sql2javaBeanPkgPrefix); 104 } 105 return m; 106 }catch (Exception e) { 107 return false; 108 } 109 }}; 110 /** sql2java生成的JavaBean 类的数据库字段名验证器 */ 111 static final Predicate<String> SQL2JAVA_COLUMN_FILTER = new Predicate<String>(){ 112 113 @Override 114 public boolean apply(String input) { 115 return ! (FIELD_NEW.equals(input) || FIELD_MODIFIED.equals(input) || FIELD_INITIALIZED.equals(input)); 116 }}; 117 /** 118 * 对{@code baseClass}生成装饰类 119 * @param baseClass 120 */ 121 public ThriftStructDecorator(Class<? > baseClass) { 122 this.baseClass = checkNotNull(baseClass); 123 TypeHelper.checkNotGeneric(baseClass); 124 if(baseClass.isInterface()){ 125 throw new IllegalArgumentException("baseClass must not be interface"); 126 } 127 fields = ImmutableMap.copyOf(typeHelper.getFields(baseClass,Predicates.<PropertyDescriptor>alwaysTrue(),true)); 128 hasOptional = Iterables.tryFind(fields.values(), new Predicate<PropertyDescriptor>(){ 129 @Override 130 public boolean apply(PropertyDescriptor input) { 131 return !input.getReadMethod().getReturnType().isPrimitive(); 132 }}).isPresent(); 133 134 cxxFields=LanguageType.CPP.equals(LanguageType.getCurrent()) 135 ? getThriftTypes(fields) 136 : Collections.<String, CxxType>emptyMap(); 137 cxxType = CxxType.getThriftType(getBaseClass()); 138 hasCanMove = Iterables.tryFind(cxxFields.values(), new Predicate<CxxType>(){ 139 @Override 140 public boolean apply(CxxType input) { 141 return input.getStubType().isCanMove(); 142 }}).isPresent(); 143 StringBuffer buffer = new StringBuffer(baseClass.getPackage().getName()); 144 if(LanguageType.JAVA.equals(LanguageType.getCurrent())){ 145 buffer.append( "." + ThriftUtils.DECORATOR_PKG_SUFFIX); 146 switch(ThriftServiceDecoratorConfiguration.INSTANCE.getTaskType()){ 147 case CLIENT: 148 buffer.append(".").append(ThriftUtils.CLIENT_SUFFIX);break; 149 case CLIENT_THRIFTY: 150 buffer.append(".").append(ThriftUtils.CLIENT_SUFFIX);break; 151 default: 152 break; 153 } 154 } 155 decoratorPackage = buffer.toString(); 156 decoratorClassName = decoratorPackage + "." + this.baseClass.getSimpleName(); 157 } 158 @Override 159 public boolean compile() { 160 // 循环扫描检查所有的引用类型 161 for(Entry<String, PropertyDescriptor> entry:fields.entrySet()){ 162 PropertyDescriptor descriptor = entry.getValue(); 163 Type returnType = descriptor.getReadMethod().getGenericReturnType(); 164 typeHelper.checkType(returnType); 165 typeHelper.addReferTypes(returnType); 166 if(NameStringUtils.isThriftReserved( descriptor.getName())){ 167 logger.warn("field name '{}' of {} is thrift IDL reserved word", descriptor.getName(),this.getBaseClass().getName()); 168 } 169 if(NameStringUtils.isJavaReserved( descriptor.getName())){ 170 logger.warn("field name '{}' of {} is java reserved word", descriptor.getName(),this.getBaseClass().getName()); 171 } 172 } 173 if(typeHelper.needTransformer()){ 174 addImportedClass(TypeTransformer.class); 175 } 176 addImportedClass(ThriftDecorator.class); 177 178 return true; 179 } 180 public String getGetMethodName(String field){ 181 PropertyDescriptor descriptor = getField(field); 182 return descriptor.getReadMethod().getName(); 183 } 184 public String getSetMethodName(String field){ 185 PropertyDescriptor descriptor = getField(field); 186 return descriptor.getWriteMethod().getName(); 187 } 188 189 public Type getFieldType(String field){ 190 PropertyDescriptor descriptor = getField(field); 191 return descriptor.getReadMethod().getGenericReturnType(); 192 } 193 194 public PropertyDescriptor getField(String field){ 195 PropertyDescriptor descriptor = fields.get(field); 196 if(null == descriptor){ 197 throw new IllegalArgumentException(String.format("INVALID field %s in %s", field,this.baseClass.getSimpleName())); 198 } 199 return descriptor; 200 } 201 public List<String> getFields(){ 202 Predicate<String> filter = isSql2javaBean() ? SQL2JAVA_COLUMN_FILTER : Predicates.<String>alwaysTrue(); 203 ArrayList<String> list = new ArrayList<>(Maps.filterKeys(this.fields,filter).keySet()); 204 // 字段名排序,确保输出的代码顺序稳定 205 Collections.sort(list); 206 return list; 207 } 208 /** 209 * @return 返回枚举类型的所有对象 210 */ 211 public Object getEnums(){ 212 if(isEnum()){ 213 try { 214 return baseClass.getMethod("values").invoke(null); 215 } catch (Exception e) { 216 Throwables.throwIfUnchecked(e); 217 throw new RuntimeException(e); 218 } 219 } 220 return Collections.emptyList(); 221 } 222 public List<PropertyDescriptor> getFieldDescriptors(){ 223 return ImmutableList.copyOf(this.fields.values()); 224 } 225 public String getDecoratorPackage() { 226 return decoratorPackage; 227 } 228 229 public String getDecoratorClassName() { 230 return decoratorClassName; 231 } 232 233 public List<ThriftStructDecorator> getDecorateTypes() { 234 return typeHelper.getDecorateTypes(); 235 } 236 public List<ThriftStructDecorator> getThriftTypes() { 237 return typeHelper.getThriftTypes(); 238 } 239 public boolean isException(){ 240 return Exception.class.isAssignableFrom(getBaseClass()); 241 } 242 public boolean isEnum(){ 243 return Enum.class.isAssignableFrom(getBaseClass()); 244 } 245 public boolean isBean(){ 246 return !isException() && !isEnum(); 247 } 248 public boolean isSql2javaBean(){ 249 return isBean() && SQL2JAVA_BEAN_FILTER.apply(this); 250 } 251 public boolean hasStringConstructor(){ 252 return ThriftUtils.hasConstructor(getBaseClass(), String.class); 253 } 254 public String toThriftType(Type type) { 255 return typeHelper.toThriftType(type); 256 } 257 258 public String toClientThriftType(Type type) { 259 return typeHelper.toClientThriftType(type); 260 } 261 public String toClientThriftyType(Type type) { 262 return typeHelper.toClientThriftyType(type); 263 } 264 265 public String toThriftyDecoratorType(Type type) { 266 return typeHelper.toThriftyDecoratorType(type); 267 } 268 @Override 269 public int hashCode() { 270 final int prime = 31; 271 int result = 1; 272 result = prime * result + ((baseClass == null) ? 0 : baseClass.hashCode()); 273 return result; 274 } 275 276 @Override 277 public boolean equals(Object obj) { 278 if (this == obj) 279 return true; 280 if (obj == null) 281 return false; 282 if (!(obj instanceof ThriftStructDecorator)) 283 return false; 284 ThriftStructDecorator other = (ThriftStructDecorator) obj; 285 if (baseClass == null) { 286 if (other.baseClass != null) 287 return false; 288 } else if (!baseClass.equals(other.baseClass)) 289 return false; 290 return true; 291 } 292 293 @Override 294 public String toString() { 295 StringBuilder builder = new StringBuilder(); 296 builder.append("ThriftStructDecorator [baseClass="); 297 builder.append(baseClass); 298 builder.append("]"); 299 return builder.toString(); 300 } 301 public void removeDecorateTypesFromImports(){ 302 // 排除枚举类型 303 Iterable<Class<?>> it = Iterables.filter(typeHelper.getTypesWithDecorator(),new Predicate<Class<?>>() { 304 @Override 305 public boolean apply(Class<?> input) { 306 return !input.isEnum(); 307 } 308 }); 309 ArrayList<Class<?>> types = Lists.newArrayList(it); 310 this.removeClassFromImports(types); 311 } 312 313 @Override 314 public int compare(ThriftStructDecorator o1, ThriftStructDecorator o2) { 315 return o1.getBaseClass().getSimpleName().compareTo(o2.getBaseClass().getSimpleName()); 316 } 317 318 @Override 319 public int compareTo(ThriftStructDecorator o) { 320 return getBaseClass().getSimpleName().compareTo(o.getBaseClass().getSimpleName()); 321 } 322 323 private static ImmutableMap<String, CxxType> getThriftTypes(Map<String, PropertyDescriptor> fields){ 324 Map<String, CxxType> m = Maps.transformValues(checkNotNull(fields,"fields is null"), 325 new Function<PropertyDescriptor,CxxType>(){ 326 @Override 327 public CxxType apply(PropertyDescriptor input) { 328 return CxxType.getThriftType(input.getReadMethod().getGenericReturnType()); 329 } 330 }); 331 return ImmutableMap.copyOf(m); 332 } 333 public ImmutableMap<String, CxxType> getCxxFieldsAsMap(){ 334 return ImmutableMap.copyOf(this.cxxFields); 335 } 336 public CxxType getCxxField(String name){ 337 return cxxFields.get(checkNotNull(name,"name is null")); 338 } 339 /** 340 * 是否有optional字段 341 * @return 342 */ 343 public boolean isHasOptionalField(){ 344 return hasOptional; 345 } 346 347 /** 348 * 是否有需要移动的字段 349 * @return 350 */ 351 public boolean isHasCanMoveField() { 352 return hasCanMove; 353 } 354 public CxxType getCxxType() { 355 return cxxType; 356 } 357 358 private static class TraverseTypeForTryFind implements Predicate<CxxType>{ 359 private Predicate<CxxTypeMeta> finder; 360 TraverseTypeForTryFind(Predicate<CxxTypeMeta> finder){ 361 this.finder = finder; 362 } 363 @Override 364 public boolean apply(CxxType input) { 365 if(null == input){ 366 return false; 367 } 368 CxxTypeMeta type = input.getUiType(); 369 if(finder.apply(type)){ 370 return true; 371 } 372 if(type.isContainer()){ 373 CxxTypeMeta keyType = type.getKeyType(); 374 CxxTypeMeta valueType = type.getValueType(); 375 if(null != keyType && finder.apply(keyType)){ 376 return true; 377 } 378 if(null != valueType && finder.apply(valueType)){ 379 return true; 380 } 381 } 382 return false; 383 }} 384 public boolean isUseString(){ 385 return Iterables.tryFind(cxxFields.values(), findString).isPresent(); 386 } 387 public boolean isUseMap(){ 388 return Iterables.tryFind(cxxFields.values(), findMap).isPresent(); 389 } 390 public boolean isUseSet(){ 391 return Iterables.tryFind(cxxFields.values(), findSet).isPresent(); 392 } 393 public boolean isUseVector(){ 394 return Iterables.tryFind(cxxFields.values(), findArray).isPresent(); 395 } 396}