001package net.gdface.codegen.thrift;
002
003import static com.google.common.base.Preconditions.*;
004import static net.gdface.thrift.ThriftUtils.*;
005
006import java.beans.PropertyDescriptor;
007import java.lang.reflect.Constructor;
008import java.lang.reflect.Modifier;
009import java.lang.reflect.ParameterizedType;
010import java.lang.reflect.Type;
011import java.nio.ByteBuffer;
012import java.util.ArrayDeque;
013import java.util.Deque;
014import java.util.List;
015import java.util.Map;
016import java.util.Set;
017
018import org.slf4j.Logger;
019import org.slf4j.LoggerFactory;
020
021import com.google.common.base.Function;
022import com.google.common.base.Joiner;
023import com.google.common.base.Predicate;
024import com.google.common.base.Predicates;
025import com.google.common.collect.ImmutableList;
026import com.google.common.collect.Iterables;
027import com.google.common.collect.Iterators;
028import com.google.common.collect.Lists;
029import com.google.common.collect.Maps;
030import com.google.common.collect.Sets;
031import com.google.common.primitives.Primitives;
032import com.google.common.reflect.TypeToken;
033
034import net.gdface.codegen.AbstractSchema;
035import net.gdface.codegen.Method;
036import net.gdface.codegen.Method.Parameter;
037import net.gdface.thrift.ThriftUtils;
038import net.gdface.utils.BeanPropertyUtils;
039import okio.ByteString;
040
041/**
042 * @author guyadong
043 *
044 */
045public class TypeHelper implements ThriftConstants {
046        private static final Logger logger = LoggerFactory.getLogger(TypeHelper.class);
047        private final Set<Class<?>> knownTypes = Sets.newHashSet();
048        private final Set<Class<?>> referTypes = Sets.newHashSet();
049        private final Map<Class<?>,ThriftStructDecorator> decorateTypes = Maps.newHashMap();
050        private final List<ThriftStructDecorator> thriftTypes = Lists.newLinkedList();
051        private final AbstractSchema parent;
052    private final ThreadLocal<Deque<Class<?>>> stack = new ThreadLocal<Deque<Class<?>>>()
053    {
054        @Override
055        protected Deque<Class<?>> initialValue()
056        {
057            return new ArrayDeque<>();
058        }
059    };
060    /**
061     * 用于外部监控所有的类型
062     */
063    public static final ThreadLocal<Predicate<Type>> VERIFYTYPE_MONITOR = new ThreadLocal<Predicate<Type>>();
064        public TypeHelper(AbstractSchema parent) {
065                this.parent = checkNotNull(parent,"parent is null");
066                knownTypes.addAll(ThriftUtils.THRIFT_BUILTIN_KNOWNTYPES);
067                knownTypes.addAll(ThriftUtils.CAST_TYPES.keySet());
068        }
069        public final boolean verifyType(Type type){
070                if(null != VERIFYTYPE_MONITOR.get()){
071                        VERIFYTYPE_MONITOR.get().apply(type);
072                }
073                if (knownTypes.contains(type)) {
074                        return true;
075                }
076                if( type instanceof ParameterizedType){
077                        ParameterizedType paramType = (ParameterizedType)type;
078                        Type rawType = paramType.getRawType();
079                        Type[] typeArgs = paramType.getActualTypeArguments();
080                        if(rawType == Map.class){
081                                return verifyType(typeArgs[0]) && verifyType(typeArgs[1]);
082                        }else if (rawType == List.class){
083                                return verifyType(typeArgs[0]);
084                        }else if(rawType == Set.class){
085                                return verifyType(typeArgs[0]);
086                        }else{
087                                throw new IllegalArgumentException(String.format("not allow parameterized type %s", type.toString()));
088                        }
089                }else if(type instanceof Class){                
090                        Class<?> clazz = (Class<?>)type;
091                        if(null !=clazz.getDeclaringClass() && !Modifier.isStatic(clazz.getModifiers())){
092                                // 不允许为非静态成员类
093                                logger.error("unsupport not static member class {}",clazz);
094                                return false;
095                        }
096                        if(clazz.isPrimitive() || Primitives.isWrapperType(clazz)){
097                                logger.error("unsupport primitive type {}",clazz);
098                                return false;
099                        }
100                        if(isThriftStruct(clazz)){
101                                knownTypes.add(clazz);
102                                thriftTypes.add(makeThriftStructDecorator(clazz));
103                                return true;
104                        }
105                        // 枚举类型
106                        if(Enum.class.isAssignableFrom(clazz)){
107                                knownTypes.add(clazz);
108                                decorateTypes.put(clazz,makeThriftStructDecorator(clazz));
109                                return true;
110                        }
111                        // 控制只递归一层
112                        if(clazz.isArray()){
113                                if(!clazz.getComponentType().isArray()){
114                                        return verifyType(clazz.getComponentType());
115                                }else{
116                                        logger.error("unsupport multi dimension array {}",clazz.toString());
117                                        return false;
118                                }
119                        }
120                        if(ThriftUtils.isException(clazz)){
121                                return verifyException(clazz);
122                        }
123                        if(Object.class == clazz){
124                                logger.error("unsupport not type Object.class");
125                                return false;
126                        }
127                        String pkg = clazz.getPackage().getName();
128                        if(pkg.startsWith("java.") || pkg.startsWith("javax.")){
129                                logger.error("not allow type {}",type);
130                                return false;
131                        }
132                        return verifyStruct(clazz);
133                }
134                throw new IllegalArgumentException(String.format("not allow type %s", type.toString()));
135        }
136        private final Predicate<PropertyDescriptor> expFieldfilter= new Predicate<PropertyDescriptor>(){
137                @Override
138                public boolean apply(PropertyDescriptor input) {
139                        return input.getReadMethod().getDeclaringClass()!=Throwable.class;
140                }};
141        private boolean verifyException(Class<?> clazz){
142                if(!ThriftUtils.isException(clazz)){
143                        return false;
144                }
145                if(!verifyFields(clazz)){
146                        return false;
147                }
148                if(null == ThriftUtils.getConstructor(clazz) 
149                        && null == ThriftUtils.getConstructor(clazz,String.class)){
150                        // 没有默认构造方法也没有String参数构造方法返回false
151                        logger.error("not found default constructor or consturctor with String.class argument for {}",clazz.getName());
152                        return false;
153                }
154                knownTypes.add(clazz);
155                decorateTypes.put(clazz,makeThriftStructDecorator(clazz));
156                return true;
157        }
158        private boolean verifyStruct(Class<?> clazz){
159                if(clazz.getTypeParameters().length > 0){
160                        logger.error("unsupport generic class {}",clazz.getName());
161                        return false;
162                }
163                try {
164                        @SuppressWarnings("unused")
165                        Constructor<?> ctor = clazz.getConstructor();
166                } catch (NoSuchMethodException e) {
167                        // 没有默认构造方法返回false
168                        logger.error("not found default consturctor for {}",clazz.getName());
169                        return false;
170                } 
171                if(!verifyFields(clazz)){
172                        return false;
173                }
174                knownTypes.add(clazz);
175                decorateTypes.put(clazz,makeThriftStructDecorator(clazz));
176                return true;            
177        }
178        private boolean verifyFields(Class<?> clazz){
179                Map<String, PropertyDescriptor> fields = getFields(clazz);
180                for(PropertyDescriptor descriptor:fields.values()){
181                        if(!verifyType(descriptor.getReadMethod().getGenericReturnType())){
182                                logger.warn("invalid type for {} in {}",descriptor.getName(),clazz.getName());
183                                return false;
184                        }
185                }
186                return true;
187        }
188        public Map<String, PropertyDescriptor> getFields(Class<?> clazz,Predicate<PropertyDescriptor>filter,boolean lenient){
189                Map<String, PropertyDescriptor> fields = BeanPropertyUtils.getProperties(clazz, 3,lenient);
190                Predicate<PropertyDescriptor> f = checkNotNull(filter,"filter is null");
191                if(ThriftUtils.isException(clazz) && (f != expFieldfilter)){
192                        f = Predicates.and(f, expFieldfilter);
193                }
194                return Maps.filterValues(fields, f);
195        }
196        public Map<String, PropertyDescriptor> getFields(Class<?> clazz,Predicate<PropertyDescriptor>filter){
197                return getFields(clazz, filter, false);
198        }
199        public Map<String, PropertyDescriptor> getFields(Class<?> clazz){
200                return getFields(clazz,Predicates.<PropertyDescriptor>alwaysTrue());
201        }
202        private boolean isDecoratorType(Type type){
203                return this.decorateTypes.containsKey(type);
204        }
205        public List<ThriftStructDecorator> getDecorateTypes() {
206                return ImmutableList.copyOf(decorateTypes.values());
207        }
208        /**
209         * @return 返回所有有 {@link ThriftStruct} 注释的类型
210         */
211        public List<ThriftStructDecorator> getThriftTypes() {
212                return ImmutableList.copyOf(thriftTypes);
213        }
214        /**
215         * 将指定的类型转为thrift支持的类型
216         * @param type
217         * @return
218         */
219        public String toThriftType(Type type){
220                checkArgument(null != type,"type is null");
221                if(ThriftUtils.THRIFT_BUILTIN_KNOWNTYPES.contains(type)){
222                        return parent.getTypeName(type);
223                }
224                if(ThriftUtils.CAST_TYPES.containsKey(type)){
225                        return parent.getTypeName(ThriftUtils.CAST_TYPES.get(type));
226                }
227                if(this.isDecoratorType(type)){
228                        // 枚举类型
229                        if(Enum.class.isAssignableFrom((Class<?>) type)){
230                                return parent.getTypeName(type);
231                        }
232                        return toDecoratorType(type);
233                }
234                if( type instanceof ParameterizedType){
235                        ParameterizedType paramType = (ParameterizedType)type;
236                        Type rawType = paramType.getRawType();
237                        Type[] typeArgs = paramType.getActualTypeArguments();
238                        if(rawType == Map.class){
239                                return String.format("Map<%s,%s>",toThriftType(typeArgs[0]), toThriftType(typeArgs[1]));
240                        }else if (rawType == List.class){
241                                return String.format("List<%s>",toThriftType(typeArgs[0]));
242                        }else if(rawType == Set.class){
243                                return String.format("Set<%s>",toThriftType(typeArgs[0]));
244                        }else{
245                                throw new IllegalArgumentException(String.format("not allow parameterized type %s", type.toString()));
246                        }
247                }else if(type instanceof Class){
248                        Class<?> clazz = (Class<?>)type;
249                        if(clazz.isPrimitive() || Primitives.isWrapperType(clazz)){
250                                throw new IllegalArgumentException(String.format("not allow type %s", clazz.toString()));
251                        }
252                        if(isThriftStruct(clazz)){
253                                return parent.getTypeName(clazz);
254                        }
255
256                        // 控制只递归一层
257                        if(clazz.isArray() ){
258                                Class<?> conmponentType = clazz.getComponentType();
259                                if(!conmponentType.isArray()){
260                                        return String.format("java.util.List<%s>",toThriftType(Primitives.wrap(conmponentType)));
261                                }else{
262                                        throw new IllegalArgumentException("unsupported type multi dimension array");
263                                }
264                        }
265                }
266                throw new IllegalArgumentException(String.format("not allow type %s", type.toString()));
267        }
268        /**
269         * 将指定的类型转为thrift支持的类型(递归)
270         * @param type
271         * @return
272         */
273        private String toClientThriftType0(Type type){
274                checkArgument(null != type,"type is null");
275                if(ThriftUtils.THRIFT_BUILTIN_KNOWNTYPES.contains(type) || type == Void.class ){
276                        return parent.getTypeName(type);
277                }
278                if(ThriftUtils.CAST_TYPES.containsKey(type)){
279                        return parent.getTypeName(ThriftUtils.CAST_TYPES.get(type));
280                }
281                if( type instanceof ParameterizedType){
282                        ParameterizedType paramType = (ParameterizedType)type;
283                        Type rawType = paramType.getRawType();
284                        Type[] typeArgs = paramType.getActualTypeArguments();
285                        if(rawType == Map.class){
286                                return String.format("Map<%s,%s>",toClientThriftType0(typeArgs[0]), toClientThriftType0(typeArgs[1]));
287                        }else if (rawType == List.class){
288                                return String.format("List<%s>",toClientThriftType0(typeArgs[0]));
289                        }else if(rawType == Set.class){
290                                return String.format("Set<%s>",toClientThriftType0(typeArgs[0]));
291                        }else{
292                                throw new IllegalArgumentException(String.format("not allow parameterized type %s", type.toString()));
293                        }
294                }else if(type instanceof Class){
295                        Class<?> clazz = (Class<?>)type;
296                        if(clazz.isPrimitive() || Primitives.isWrapperType(clazz)){
297                                throw new IllegalArgumentException(String.format("not allow type %s", clazz.toString()));
298                        }
299                        if(isThriftStruct(clazz) || Enum.class.isAssignableFrom(clazz) || this.isDecoratorType(clazz)){
300                                return ThriftServiceDecoratorConfiguration.INSTANCE.getThriftClientPackage() + "." + clazz.getSimpleName();
301                        }
302                        // 控制只递归一层
303                        if(clazz.isArray() ){
304                                Class<?> conmponentType = clazz.getComponentType();
305                                if(!conmponentType.isArray()){
306                                        return String.format("java.util.List<%s>",toClientThriftType0(Primitives.wrap(conmponentType)));
307                                }else{
308                                        throw new IllegalArgumentException("unsupported type multi dimension array");
309                                }
310                        }
311                }
312                throw new IllegalArgumentException(String.format("not allow type %s", type.toString()));
313        }
314        /**
315         * 将指定的类型转为thrift支持的类型
316         * @param type
317         * @return
318         */
319        public String toClientThriftType(Type type){
320                checkArgument(null != type,"type is null");
321                if(type == ByteBuffer.class || type == byte[].class){
322                        return parent.getTypeName(byte[].class);
323                }
324                
325                return toClientThriftType0(type);
326        }
327        /**
328         * 将指定的类型转为Microsoft/thrifty支持的类型
329         * @param type
330         * @return
331         */
332        public String toClientThriftyType(Type type){
333                checkArgument(null != type,"type is null");
334                if(type == ByteBuffer.class || type == byte[].class){
335                        return parent.getTypeName(ByteString.class);
336                }
337                
338                return toClientThriftType0(type);
339        }
340        public boolean isClientThriftType(Type type){
341                checkArgument(null != type,"type is null");
342                if(type == ByteBuffer.class){
343                        return false;
344                }               
345                if(ThriftUtils.THRIFT_BUILTIN_KNOWNTYPES.contains(type) || type == byte[].class || type == Void.class ){
346                        return true;
347                }
348                if(ThriftUtils.CAST_TYPES.containsKey(type)){
349                        return false;
350                }
351                if( type instanceof ParameterizedType){
352                        ParameterizedType paramType = (ParameterizedType)type;
353                        Type rawType = paramType.getRawType();
354                        Type[] typeArgs = paramType.getActualTypeArguments();
355                        if(rawType == Map.class){
356                                return isClientThriftType(typeArgs[0]) && isClientThriftType(typeArgs[1]);
357                        }else if (rawType == List.class){
358                                return isClientThriftType(typeArgs[0]);
359                        }else if(rawType == Set.class){
360                                return isClientThriftType(typeArgs[0]);
361                        }else{
362                                throw new IllegalArgumentException(String.format("not allow parameterized type %s", type.toString()));
363                        }
364                }else if(type instanceof Class){
365                        return false;
366                }
367                throw new IllegalArgumentException(String.format("not allow type %s", type.toString()));
368        }
369        private String toDecoratorType(Type type){
370                if(this.isDecoratorType(type)){
371                        ThriftStructDecorator decorator = decorateTypes.get(type);
372                        if(decorator.getDecoratorPackage().equals(getParentPackage())
373                                        && !parent.getImportedList().containsValue(type)){
374                                return ((Class<?>)type).getSimpleName();
375                        }else{
376                                return decorator.getDecoratorClassName();
377                        }
378                }
379                return parent.getTypeName(type);
380        }
381        /**
382         * 返回指定类型在thrifty client对应的装饰类名,如果非decorator,则调用{@link #toClientThriftType0(Type)}
383         * @param type
384         * @return
385         */
386        public String toThriftyDecoratorType(Type type){
387                if(this.isDecoratorType(type) && !Enum.class.isAssignableFrom((Class<?>) type)){
388                        return toDecoratorType(type);
389                }
390                if(type == ByteBuffer.class || type == byte[].class){
391                        return parent.getTypeName(byte[].class);
392                }               
393                return toClientThriftType0(type);
394        }
395        public void checkType(Type type){
396                if(!verifyType(type)){
397                        throw new IllegalArgumentException(String.format("UNSUPPORTED TYPE %s",
398                                        type.toString()
399                                        ));
400                }
401        }
402        /**
403         * 确保方法的参数和返回类型符合要求
404         * @param method
405         */
406        public void checkType(Method method){
407                checkParameter(method);
408                checkReturnType(method);
409                checkThrows(method);
410        }
411        
412        /**
413         * 验证参数类型
414         * @param method
415         */
416        public void checkParameter(Method method){
417                for (Parameter param : method.getParameters()) {
418                        Type genericType = param.getGenericType();
419                        if(!verifyType(genericType)){
420                                throw new IllegalArgumentException(String.format("UNSUPPORTED TYPE %s of parameter %s in %s",
421                                                genericType.toString(),
422                                                param.name,
423                                                method.getName()
424                                                ));
425                        }
426                }
427        }
428        /**
429         * 验证返回类型
430         * @param method
431         */
432        public void checkReturnType(Method method){
433                Type returnType = method.getGenericReturnType();
434                if(!verifyType(returnType)){
435                        throw new IllegalArgumentException(String.format("UNSUPPORTED TYPE %s of return type for %s",
436                                        returnType.toString(),
437                                        method.getName()                                
438                                        ));
439                }
440        }
441        public void checkThrows(Method method){
442                for(Type exp:method.getGenericExceptionTypes()){
443                        if(!verifyType(exp)){
444                                throw new IllegalArgumentException(String.format("UNSUPPORTED EXCEPTION TYPE %s in %s",
445                                                exp.toString(),
446                                                method.getName()
447                                                ));
448                        }
449                };
450                
451        }
452        
453        /**
454         * 确保{@code clazz}不是泛型类
455         * @param clazz
456         */
457        public static void checkNotGeneric(Class<?> clazz){
458                if(clazz.getTypeParameters().length > 0){
459                        throw new IllegalArgumentException(String.format("%s must not be a generic class", clazz.getName()));
460                }
461        }
462        /**
463         * 确保{@code method}不是泛型方法
464         * @param method
465         */
466        public static void checkNotGeneric(java.lang.reflect.Method method){
467                if(method.getTypeParameters().length > 0){
468                        throw new IllegalArgumentException(String.format("%s must not be a generic method", method.getName()));
469                }
470        }
471        /**
472         * 确保{@code method}不是泛型方法
473         * @param method
474         */
475        public static void checkNotGeneric(Method method){
476                if(method.getTypeParameters().length > 0){
477                        throw new IllegalArgumentException(String.format("%s must not be a generic method", method.getName()));
478                }
479        }
480
481        private String getParentPackage(){
482                if(parent instanceof ThriftStructDecorator){
483                        return ((ThriftStructDecorator)parent).getDecoratorPackage();
484                }else if(parent instanceof ThriftServiceDecorator){
485                        return ((ThriftServiceDecorator<?>)parent).getGeneratePackage();
486                }
487                return null;
488        }
489        public void addReferTypes(Type type){
490                traverseTypes(type,new Action(){
491                        @Override
492                        public void doClass(Class<?> clazz) {
493                                referTypes.add(clazz);
494                                parent.addImportedClass(clazz);
495                                if(CAST_TYPES.containsKey(clazz)){
496                                        parent.addImportedClass(CAST_TYPES.get(clazz));
497                                        referTypes.add(CAST_TYPES.get(clazz));
498                                }
499                        }});
500        }
501        public void addReferTypes(Method method){
502                for (Parameter param : method.getParameters()) {
503                        addReferTypes(param.getGenericType());
504                }
505                addReferTypes(method.getGenericReturnType());
506                for(Type exp:method.getGenericExceptionTypes()){
507                        addReferTypes(exp);
508                }
509        }
510        public boolean needTransformer(){
511                return Iterators.tryFind(referTypes.iterator(), new Predicate<Class<?>>(){
512                        @Override
513                        public boolean apply(Class<?> input) {
514                                return ThriftUtils.needTransformer(input);
515                        }}).isPresent();
516        }
517        public List<Class<?>> getTypesWithDecorator(){
518                return Lists.transform(getDecorateTypes(),new Function<ThriftStructDecorator,Class<?>>(){
519                        @Override
520                        public Class<?> apply(ThriftStructDecorator input) {
521                                return input.getBaseClass();
522                        }});
523        }
524        public List<Class<?>> getReferExceptions(){
525                Iterable<Class<?>> iterable = Iterables.filter(referTypes, new Predicate<Class<?>>(){
526                        @Override
527                        public boolean apply(Class<?> input) {
528                                return Exception.class.isAssignableFrom(input);
529                        }});
530                return Lists.newArrayList(iterable);
531        }
532        /**
533         * 创建{@link ThriftStructDecorator}实例<br>
534         * 如果{@code structType}有循环引用则抛出异常
535         * @param structType
536         * @return
537         */
538        private ThriftStructDecorator makeThriftStructDecorator(Class<?> structType){
539        checkNotNull(structType, "structType is null");
540        Deque<Class<?>> stack = this.stack.get();
541        if (stack.contains(structType)) {
542            String path = Joiner.on("->").join(Iterables.transform(Iterables.concat(stack, ImmutableList.of(structType)), new Function<Class<?>, Object>()
543            {
544                @Override
545                public Object apply(Class<?> input)
546                {
547                    return TypeToken.of(input).getRawType().getName();
548                }
549            }));
550            throw new IllegalArgumentException("Circular references are not allowed: " + path);
551        }
552        stack.push(structType);
553        try {
554            return new ThriftStructDecorator(structType);
555        }
556        finally {
557                Class<?> top = stack.pop();
558            checkState(structType.equals(top),
559                    "ThriftCatalog circularity detection stack is corrupt: expected %s, but got %s",
560                    structType,
561                    top);
562        }
563        }
564}