001package net.gdface.codegen.thrift; 002 003import net.gdface.annotation.CodegenDefaultvalue; 004import net.gdface.annotation.CodegenInvalidValue; 005import net.gdface.annotation.CodegenLength; 006import net.gdface.annotation.CodegenRequired; 007import net.gdface.codegen.AbstractSchema; 008import net.gdface.codegen.generator.GeneratorUtils; 009import net.gdface.codegen.thrift.ThriftServiceDecoratorConfiguration.LanguageType; 010import net.gdface.thrift.ThriftDecorator; 011import net.gdface.thrift.ThriftUtils; 012import net.gdface.thrift.TypeTransformer; 013import net.gdface.thrift.exception.BaseServiceException; 014import net.gdface.utils.NameStringUtils; 015 016import static com.google.common.base.Preconditions.*; 017 018import java.beans.PropertyDescriptor; 019import java.lang.reflect.Method; 020import java.lang.reflect.Type; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.Comparator; 024import java.util.List; 025import java.util.Map; 026import java.util.Map.Entry; 027 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031import com.facebook.swift.codec.ThriftField; 032import com.facebook.swift.codec.ThriftField.Requiredness; 033import com.facebook.swift.codec.metadata.ThriftCatalogWithTransformer; 034import com.facebook.swift.codec.metadata.ThriftFieldMetadata; 035import com.facebook.swift.codec.metadata.ThriftStructMetadata; 036import com.google.common.base.Function; 037import com.google.common.base.Predicate; 038import com.google.common.base.Predicates; 039import com.google.common.base.Strings; 040import com.google.common.base.Throwables; 041import com.google.common.collect.ImmutableList; 042import com.google.common.collect.ImmutableMap; 043import com.google.common.collect.ImmutableMap.Builder; 044import com.google.common.collect.Iterables; 045import com.google.common.collect.Lists; 046import com.google.common.collect.Maps; 047import com.google.common.primitives.Primitives; 048 049/** 050 * 装饰类信息 051 * @author guyadong 052 * 053 */ 054public class ThriftStructDecorator extends AbstractSchema implements ThriftConstants,Comparator<ThriftStructDecorator>,Comparable<ThriftStructDecorator>{ 055 private static final Logger logger = LoggerFactory.getLogger(ThriftStructDecorator.class); 056 057 private final Map<String, PropertyDescriptor> fields; 058 private final String decoratorPackage; 059 private final String decoratorClassName; 060 private final TypeHelper typeHelper = new TypeHelper(this); 061 062 private final Map<String, CxxType> cxxFields; 063 064 private final boolean hasOptional; 065 private final boolean hasCanMove; 066 067 private final CxxType cxxType; 068 069 private final TraverseTypeForTryFind findString= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){ 070 @Override 071 public boolean apply(CxxTypeMeta input) { 072 return null != input && (input.isBinary() || input.isString()); 073 }}); 074 private final TraverseTypeForTryFind findBinary= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){ 075 @Override 076 public boolean apply(CxxTypeMeta input) { 077 return null != input && (input.isBinary()); 078 }}); 079 private final TraverseTypeForTryFind findMap= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){ 080 @Override 081 public boolean apply(CxxTypeMeta input) { 082 return null != input && input.isMap(); 083 }}); 084 private final TraverseTypeForTryFind findSet= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){ 085 @Override 086 public boolean apply(CxxTypeMeta input) { 087 return null != input && input.isSet(); 088 }}); 089 private final TraverseTypeForTryFind findArray= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){ 090 @Override 091 public boolean apply(CxxTypeMeta input) { 092 return null != input && input.isList(); 093 }}); 094 private final Predicate<PropertyDescriptor> codegenInvalidValueFilter = new Predicate<PropertyDescriptor>() { 095 096 @Override 097 public boolean apply(PropertyDescriptor input) { 098 if(input != null){ 099 CodegenInvalidValue ann = GeneratorUtils.extractFieldAnnotation(input, CodegenInvalidValue.class); 100 if(ann != null){ 101 if(GeneratorUtils.isString(input.getPropertyType()) || GeneratorUtils.isBinary(input.getPropertyType())){ 102 // 对于String,二进制类型不判断空字符串, 103 // 空字符串可以视为缺省值 104 return true; 105 } 106 return ann.value().length()>0 || ann.exp().length() > 0; 107 } 108 } 109 return false; 110 } 111 }; 112 private final Predicate<PropertyDescriptor> codegenDefaultvalueFilter = new Predicate<PropertyDescriptor>() { 113 114 @Override 115 public boolean apply(PropertyDescriptor input) { 116 if(input != null){ 117 CodegenDefaultvalue ann = GeneratorUtils.extractFieldAnnotation(input, CodegenDefaultvalue.class); 118 if(ann != null){ 119 if(GeneratorUtils.isString(input.getPropertyType()) || GeneratorUtils.isBinary(input.getPropertyType())){ 120 // 对于String,二进制类型不判断空字符串, 121 // 空字符串可以视为缺省值 122 return true; 123 } 124 return ann.value().length()>0; 125 } 126 } 127 return false; 128 } 129 }; 130 private final Predicate<PropertyDescriptor> codegenLengthFilter = new Predicate<PropertyDescriptor>() { 131 132 @Override 133 public boolean apply(PropertyDescriptor input) { 134 if(input != null && !input.getPropertyType().isPrimitive() && !Primitives.unwrap(input.getPropertyType()).isPrimitive()){ 135 CodegenLength ann = GeneratorUtils.extractFieldAnnotation(input, CodegenLength.class); 136 return ann != null && (ann.value()> 0 || ann.max()> 0); 137 } 138 return false; 139 } 140 }; 141 private static final String FIELD_NEW_ = "_new"; 142 private static final String FIELD_NEW = "new"; 143 private static final String FIELD_MODIFIED = "modified"; 144 private static final String FIELD_INITIALIZED = "initialized"; 145 static String sql2javaBeanPkgPrefix = null; 146 /** sql2java生成的JavaBean 类验证器 */ 147 static final Predicate<ThriftStructDecorator> SQL2JAVA_BEAN_FILTER = new Predicate<ThriftStructDecorator>(){ 148 private boolean checkField(ThriftStructDecorator input,String name,Class<?> type){ 149 try{ 150 PropertyDescriptor field = input.getField(name); 151 return (field.getPropertyType().equals(type)) 152 && field.getReadMethod() != null 153 && field.getWriteMethod() != null; 154 }catch (Exception e) { 155 return false; 156 } 157 } 158 @Override 159 public boolean apply(ThriftStructDecorator input) { 160 try{ 161 boolean m1 = checkField(input,FIELD_NEW,boolean.class) 162 && checkField(input,FIELD_MODIFIED,int.class) 163 && checkField(input,FIELD_INITIALIZED,int.class); 164 boolean m2 = checkField(input,FIELD_NEW,boolean.class) 165 && checkField(input,FIELD_MODIFIED,int[].class) 166 && checkField(input,FIELD_INITIALIZED,int[].class); 167 boolean m = m1 || m2; 168 if(!Strings.isNullOrEmpty(ThriftStructDecorator.sql2javaBeanPkgPrefix)){ 169 m = m && input.getBaseClass().getPackage().getName().startsWith(ThriftStructDecorator.sql2javaBeanPkgPrefix); 170 } 171 return m; 172 }catch (Exception e) { 173 return false; 174 } 175 }}; 176 /** 177 * 对{@code baseClass}生成装饰类 178 * @param baseClass 179 */ 180 public ThriftStructDecorator(Class<? > baseClass) { 181 this.baseClass = checkNotNull(baseClass); 182 TypeHelper.checkNotGeneric(baseClass); 183 if(baseClass.isInterface()){ 184 throw new IllegalArgumentException("baseClass must not be interface"); 185 } 186 if(LanguageType.CPP.equals(LanguageType.getCurrent()) 187 && ThriftUtils.isException(baseClass) 188 && !ThriftUtils.isThriftStruct(baseClass)){ 189 // 所有非thrift struct的服务异常会被生成对应的装饰类(以BaseServiceException为父类), 190 // 所以生成C++代码时要包含 BaseServiceException 的字段 191 Builder<String,PropertyDescriptor> builder = ImmutableMap.builder(); 192 builder.putAll(typeHelper.getFields(BaseServiceException.class,Predicates.<PropertyDescriptor>alwaysTrue(),true)); 193 builder.putAll(typeHelper.getFields(baseClass,Predicates.<PropertyDescriptor>alwaysTrue(),true)); 194 fields = builder.build(); 195 }else{ 196 fields = ImmutableMap.copyOf(typeHelper.getFields(baseClass,Predicates.<PropertyDescriptor>alwaysTrue(),true)); 197 } 198 hasOptional = Iterables.tryFind(fields.values(), new Predicate<PropertyDescriptor>(){ 199 @Override 200 public boolean apply(PropertyDescriptor input) { 201 return !input.getReadMethod().getReturnType().isPrimitive(); 202 }}).isPresent(); 203 204 cxxFields=LanguageType.CPP.equals(LanguageType.getCurrent()) 205 ? getThriftTypes(fields) 206 : Collections.<String, CxxType>emptyMap(); 207 cxxType = CxxType.getThriftType(getBaseClass()); 208 hasCanMove = Iterables.tryFind(cxxFields.values(), new Predicate<CxxType>(){ 209 @Override 210 public boolean apply(CxxType input) { 211 return input.getStubType().isCanMove(); 212 }}).isPresent(); 213 StringBuffer buffer = new StringBuffer(baseClass.getPackage().getName()); 214 buffer.append( "." + ThriftUtils.DECORATOR_PKG_SUFFIX); 215 switch(ThriftServiceDecoratorConfiguration.INSTANCE.getTaskType()){ 216 case CLIENT: 217 buffer.append(".").append(ThriftUtils.CLIENT_SUFFIX);break; 218 case CLIENT_THRIFTY: 219 buffer.append(".").append(ThriftUtils.CLIENT_SUFFIX);break; 220 default: 221 break; 222 } 223 decoratorPackage = buffer.toString(); 224 decoratorClassName = decoratorPackage + "." + this.baseClass.getSimpleName(); 225 } 226 @Override 227 public boolean compile() { 228 // 循环扫描检查所有的引用类型 229 for(Entry<String, PropertyDescriptor> entry:fields.entrySet()){ 230 PropertyDescriptor descriptor = entry.getValue(); 231 Type returnType = descriptor.getReadMethod().getGenericReturnType(); 232 typeHelper.checkType(returnType); 233 typeHelper.addReferTypes(returnType); 234 if(NameStringUtils.isThriftReserved( descriptor.getName())){ 235 logger.warn("field name '{}' of {} is thrift IDL reserved word", descriptor.getName(),this.getBaseClass().getName()); 236 } 237 if(NameStringUtils.isJavaReserved( descriptor.getName())){ 238 logger.warn("field name '{}' of {} is java reserved word", descriptor.getName(),this.getBaseClass().getName()); 239 } 240 } 241 if(typeHelper.needTransformer()){ 242 addImportedClass(TypeTransformer.class); 243 } 244 addImportedClass(ThriftDecorator.class); 245 246 return true; 247 } 248 public Method getGetMethod(String field){ 249 PropertyDescriptor descriptor = getField(field); 250 return descriptor.getReadMethod(); 251 } 252 public Method getSetMethod(String field){ 253 PropertyDescriptor descriptor = getField(field); 254 return descriptor.getWriteMethod(); 255 } 256 257 public Type getFieldType(String field){ 258 PropertyDescriptor descriptor = getField(field); 259 return descriptor.getReadMethod().getGenericReturnType(); 260 } 261 262 public PropertyDescriptor getField(String field){ 263 PropertyDescriptor descriptor = fields.get(field); 264 if(null == descriptor){ 265 throw new IllegalArgumentException(String.format("INVALID field %s in %s", field,this.baseClass.getSimpleName())); 266 } 267 return descriptor; 268 } 269 /** 270 * 返回数据结构的所有字段名,如果是sql2java生成的Thrift Struct 对象,则忽略'new','initialized','modified'字段 271 * @return 字段名列表 272 */ 273 public List<String> getFields(){ 274 if(isThriftStruct()){ 275 ThriftStructMetadata thriftMetadata = ThriftCatalogWithTransformer.CATALOG.getThriftStructMetadata(getBaseClass()); 276 if(isSql2javaBean()){ 277 List<String> list = Lists.newLinkedList(); 278 // 与thrift struct中定义的字段顺序保持一致 279 for(ThriftFieldMetadata field:thriftMetadata.getFields()){ 280 String name = field.getName(); 281 if(!name.equals(FIELD_NEW_) && !name.equals(FIELD_INITIALIZED) && !name.equals(FIELD_MODIFIED)){ 282 list.add(name); 283 } 284 } 285 return list; 286 }else{ 287 List<String> list = Lists.newLinkedList(); 288 // 与thrift struct中定义的字段顺序保持一致 289 for(ThriftFieldMetadata field:thriftMetadata.getFields()){ 290 list.add(field.getName()); 291 } 292 return list; 293 } 294 } 295 ArrayList<String> list = new ArrayList<>(this.fields.keySet()); 296 // 字段名排序,确保输出的代码顺序稳定 297 Collections.sort(list); 298 return list; 299 } 300 /** 301 * 返回数据结构的非primitive类型所有字段名 302 * 303 * @return 字段名列表 304 */ 305 public List<String> getRefFields(){ 306 return Lists.newArrayList(Iterables.filter(getFields(), new Predicate<String>() { 307 308 @Override 309 public boolean apply(String input) { 310 return !getField(input).getPropertyType().isPrimitive(); 311 } 312 })); 313 } 314 /** 315 * @return 返回枚举类型的所有对象 316 */ 317 public Object getEnums(){ 318 if(isEnum()){ 319 try { 320 return baseClass.getMethod("values").invoke(null); 321 } catch (Exception e) { 322 Throwables.throwIfUnchecked(e); 323 throw new RuntimeException(e); 324 } 325 } 326 return Collections.emptyList(); 327 } 328 public List<PropertyDescriptor> getFieldDescriptors(){ 329 return ImmutableList.copyOf(this.fields.values()); 330 } 331 public String getDecoratorPackage() { 332 return decoratorPackage; 333 } 334 335 public String getDecoratorClassName() { 336 return decoratorClassName; 337 } 338 339 public List<ThriftStructDecorator> getDecorateTypes() { 340 return typeHelper.getDecorateTypes(); 341 } 342 public List<ThriftStructDecorator> getThriftTypes() { 343 return typeHelper.getThriftTypes(); 344 } 345 public boolean isException(){ 346 return Exception.class.isAssignableFrom(getBaseClass()); 347 } 348 public boolean isThriftStruct(){ 349 return ThriftUtils.isThriftStruct(getBaseClass()); 350 } 351 public boolean isEnum(){ 352 return getBaseClass().isEnum(); 353 } 354 public boolean isBean(){ 355 return !isException() && !isEnum(); 356 } 357 public boolean isSql2javaBean(){ 358 return isBean() && SQL2JAVA_BEAN_FILTER.apply(this); 359 } 360 public boolean hasStringConstructor(){ 361 return ThriftUtils.hasConstructor(getBaseClass(), String.class); 362 } 363 public String toThriftType(Type type) { 364 return typeHelper.toThriftType(type); 365 } 366 367 public String toClientThriftType(Type type) { 368 return typeHelper.toClientThriftType(type); 369 } 370 public String toClientThriftyType(Type type) { 371 return typeHelper.toClientThriftyType(type); 372 } 373 374 public String toThriftyDecoratorType(Type type) { 375 return typeHelper.toThriftyDecoratorType(type); 376 } 377 @Override 378 public int hashCode() { 379 final int prime = 31; 380 int result = 1; 381 result = prime * result + ((baseClass == null) ? 0 : baseClass.hashCode()); 382 return result; 383 } 384 385 @Override 386 public boolean equals(Object obj) { 387 if (this == obj) 388 return true; 389 if (obj == null) 390 return false; 391 if (!(obj instanceof ThriftStructDecorator)) 392 return false; 393 ThriftStructDecorator other = (ThriftStructDecorator) obj; 394 if (baseClass == null) { 395 if (other.baseClass != null) 396 return false; 397 } else if (!baseClass.equals(other.baseClass)) 398 return false; 399 return true; 400 } 401 402 @Override 403 public String toString() { 404 StringBuilder builder = new StringBuilder(); 405 builder.append("ThriftStructDecorator [baseClass="); 406 builder.append(baseClass); 407 builder.append("]"); 408 return builder.toString(); 409 } 410 public void removeDecorateTypesFromImports(){ 411 // 排除枚举类型 412 Iterable<Class<?>> it = Iterables.filter(typeHelper.getTypesWithDecorator(),new Predicate<Class<?>>() { 413 @Override 414 public boolean apply(Class<?> input) { 415 return !input.isEnum(); 416 } 417 }); 418 ArrayList<Class<?>> types = Lists.newArrayList(it); 419 this.removeClassFromImports(types); 420 } 421 422 @Override 423 public int compare(ThriftStructDecorator o1, ThriftStructDecorator o2) { 424 return o1.getBaseClass().getSimpleName().compareTo(o2.getBaseClass().getSimpleName()); 425 } 426 427 @Override 428 public int compareTo(ThriftStructDecorator o) { 429 return getBaseClass().getSimpleName().compareTo(o.getBaseClass().getSimpleName()); 430 } 431 432 private static ImmutableMap<String, CxxType> getThriftTypes(Map<String, PropertyDescriptor> fields){ 433 Map<String, CxxType> m = Maps.transformValues(checkNotNull(fields,"fields is null"), 434 new Function<PropertyDescriptor,CxxType>(){ 435 @Override 436 public CxxType apply(PropertyDescriptor input) { 437 return CxxType.getThriftType(input.getReadMethod().getGenericReturnType()); 438 } 439 }); 440 return ImmutableMap.copyOf(m); 441 } 442 public ImmutableMap<String, CxxType> getCxxFieldsAsMap(){ 443 return ImmutableMap.copyOf(this.cxxFields); 444 } 445 public CxxType getCxxField(String name){ 446 return cxxFields.get(checkNotNull(name,"name is null")); 447 } 448 /** 449 * 是否有optional字段 450 * @return 451 */ 452 public boolean isHasOptionalField(){ 453 return hasOptional; 454 } 455 456 /** 457 * 是否有需要移动的字段 458 * @return 459 */ 460 public boolean isHasCanMoveField() { 461 return hasCanMove; 462 } 463 public CxxType getCxxType() { 464 return cxxType; 465 } 466 467 private static class TraverseTypeForTryFind implements Predicate<CxxType>{ 468 private Predicate<CxxTypeMeta> finder; 469 TraverseTypeForTryFind(Predicate<CxxTypeMeta> finder){ 470 this.finder = finder; 471 } 472 @Override 473 public boolean apply(CxxType input) { 474 if(null == input){ 475 return false; 476 } 477 CxxTypeMeta type = input.getUiType(); 478 if(finder.apply(type)){ 479 return true; 480 } 481 if(type.isContainer()){ 482 CxxTypeMeta keyType = type.getKeyType(); 483 CxxTypeMeta valueType = type.getValueType(); 484 if(null != keyType && finder.apply(keyType)){ 485 return true; 486 } 487 if(null != valueType && finder.apply(valueType)){ 488 return true; 489 } 490 } 491 return false; 492 }} 493 public boolean isUseString(){ 494 return Iterables.tryFind(cxxFields.values(), findString).isPresent(); 495 } 496 public boolean isUseBinary(){ 497 return Iterables.tryFind(cxxFields.values(), findBinary).isPresent(); 498 } 499 public boolean isUseMap(){ 500 return Iterables.tryFind(cxxFields.values(), findMap).isPresent(); 501 } 502 public boolean isUseSet(){ 503 return Iterables.tryFind(cxxFields.values(), findSet).isPresent(); 504 } 505 public boolean isUseVector(){ 506 return Iterables.tryFind(cxxFields.values(), findArray).isPresent(); 507 } 508 public boolean isUseCodegenInvalidValue(){ 509 return Iterables.tryFind(fields.values(), codegenInvalidValueFilter).isPresent(); 510 } 511 public boolean isUseCodegenDefaultvalue(){ 512 return Iterables.tryFind(fields.values(), codegenDefaultvalueFilter).isPresent(); 513 } 514 public boolean isUseCodegenLength(){ 515 return Iterables.tryFind(fields.values(), codegenLengthFilter).isPresent(); 516 } 517 public boolean needRenderCodegen(PropertyDescriptor input){ 518 return codegenInvalidValueFilter.apply(input) || codegenDefaultvalueFilter.apply(input) || codegenLengthFilter.apply(input); 519 } 520 public String renderCodegenInvalidValue(PropertyDescriptor input){ 521 if(codegenInvalidValueFilter.apply(input)){ 522 CodegenInvalidValue ann = GeneratorUtils.extractFieldAnnotation(input, CodegenInvalidValue.class); 523 if(ann.value().length() >0){ 524 return String.format("@CodegenInvalidValue(\"%s\")", ann.value()); 525 }else{ 526 return String.format("@CodegenInvalidValue(exp=\"%s\")", ann.exp()); 527 } 528 } 529 return null; 530 } 531 public String renderCodegenDefaultValue(PropertyDescriptor input){ 532 if(codegenDefaultvalueFilter.apply(input)){ 533 CodegenDefaultvalue ann = GeneratorUtils.extractFieldAnnotation(input, CodegenDefaultvalue.class); 534 return String.format("@CodegenDefaultvalue(\"%s\")", ann.value()); 535 } 536 return null; 537 } 538 public String renderCodegenLength(PropertyDescriptor input){ 539 if(codegenLengthFilter.apply(input)){ 540 CodegenLength ann = GeneratorUtils.extractFieldAnnotation(input, CodegenLength.class); 541 if(ann.value() >0){ 542 return String.format("@CodegenLength(\"%d\")", ann.value()); 543 }else{ 544 return String.format("@CodegenLength(max=\"%d\")", ann.max()); 545 } 546 } 547 return null; 548 } 549 public Integer lengthLimitOf(PropertyDescriptor input){ 550 if(codegenLengthFilter.apply(input)){ 551 CodegenLength ann = GeneratorUtils.extractFieldAnnotation(input, CodegenLength.class); 552 int limit; 553 if(ann.value() >0){ 554 limit = ann.value(); 555 }else{ 556 limit = ann.max(); 557 } 558 if(String.class.equals(input.getPropertyType())){ 559 limit++; 560 } 561 return limit; 562 } 563 return null; 564 } 565 public String defaultValueOf(PropertyDescriptor input){ 566 if(codegenDefaultvalueFilter.apply(input)){ 567 CodegenDefaultvalue ann = GeneratorUtils.extractFieldAnnotation(input, CodegenDefaultvalue.class); 568 return ann.value(); 569 } 570 return null; 571 } 572 public String invalidValueOf(PropertyDescriptor input){ 573 if(codegenInvalidValueFilter.apply(input)){ 574 CodegenInvalidValue ann = GeneratorUtils.extractFieldAnnotation(input, CodegenInvalidValue.class); 575 if(ann.value().length() >0){ 576 return ann.value(); 577 }else{ 578 return ann.exp(); 579 } 580 } 581 return null; 582 } 583 public String initValueOf(PropertyDescriptor input){ 584 String initValue = invalidValueOf(input); 585 return initValue == null ? defaultValueOf(input) : initValue; 586 } 587 public boolean isRequired(PropertyDescriptor input){ 588 if(input != null){ 589 CodegenRequired ann = GeneratorUtils.extractFieldAnnotation(input, CodegenRequired.class); 590 if(ann != null){ 591 return ann.value(); 592 } 593 ThriftField thriftField=GeneratorUtils.extractFieldAnnotation(input, ThriftField.class); 594 if(thriftField != null){ 595 return Requiredness.REQUIRED.equals(thriftField.requiredness()); 596 } 597 } 598 return false; 599 } 600 public boolean isRequired(String name){ 601 return isRequired(fields.get(name)); 602 } 603}