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 com.facebook.swift.codec.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         * @param toDecorator 为true时要求获取decorator
272         * @return
273         */
274        private String toClientThriftType0(Type type,boolean toDecorator){
275                checkArgument(null != type,"type is null");
276                if(ThriftUtils.THRIFT_BUILTIN_KNOWNTYPES.contains(type) || type == Void.class ){
277                        return parent.getTypeName(type);
278                }
279                if(ThriftUtils.CAST_TYPES.containsKey(type)){
280                        return parent.getTypeName(ThriftUtils.CAST_TYPES.get(type));
281                }
282                if(toDecorator && this.isDecoratorType(type) && !Enum.class.isAssignableFrom((Class<?>) type)){
283                        return toDecoratorType(type);
284                }
285                if( type instanceof ParameterizedType){
286                        ParameterizedType paramType = (ParameterizedType)type;
287                        Type rawType = paramType.getRawType();
288                        Type[] typeArgs = paramType.getActualTypeArguments();
289                        if(rawType == Map.class){
290                                return String.format("Map<%s,%s>",toClientThriftType0(typeArgs[0],toDecorator), toClientThriftType0(typeArgs[1],toDecorator));
291                        }else if (rawType == List.class){
292                                return String.format("List<%s>",toClientThriftType0(typeArgs[0],toDecorator));
293                        }else if(rawType == Set.class){
294                                return String.format("Set<%s>",toClientThriftType0(typeArgs[0],toDecorator));
295                        }else{
296                                throw new IllegalArgumentException(String.format("not allow parameterized type %s", type.toString()));
297                        }
298                }else if(type instanceof Class){
299                        Class<?> clazz = (Class<?>)type;
300                        if(clazz.isPrimitive() || Primitives.isWrapperType(clazz)){
301                                throw new IllegalArgumentException(String.format("not allow type %s", clazz.toString()));
302                        }
303                        if(isThriftStruct(clazz) || Enum.class.isAssignableFrom(clazz) || this.isDecoratorType(clazz)){
304                                return ThriftServiceDecoratorConfiguration.INSTANCE.getThriftClientPackage() + "." + clazz.getSimpleName();
305                        }
306                        // 控制只递归一层
307                        if(clazz.isArray() ){
308                                Class<?> conmponentType = clazz.getComponentType();
309                                if(!conmponentType.isArray()){
310                                        return String.format("java.util.List<%s>",toClientThriftType0(Primitives.wrap(conmponentType),toDecorator));
311                                }else{
312                                        throw new IllegalArgumentException("unsupported type multi dimension array");
313                                }
314                        }
315                }
316                throw new IllegalArgumentException(String.format("not allow type %s", type.toString()));
317        }
318        /**
319         * 将指定的类型转为thrift支持的类型
320         * @param type
321         * @return
322         */
323        public String toClientThriftType(Type type){
324                checkArgument(null != type,"type is null");
325                if(type == ByteBuffer.class || type == byte[].class){
326                        return parent.getTypeName(byte[].class);
327                }
328                
329                return toClientThriftType0(type, false);
330        }
331        /**
332         * 将指定的类型转为Microsoft/thrifty支持的类型
333         * @param type
334         * @return
335         */
336        public String toClientThriftyType(Type type){
337                checkArgument(null != type,"type is null");
338                if(type == ByteBuffer.class || type == byte[].class){
339                        return parent.getTypeName(ByteString.class);
340                }
341                
342                return toClientThriftType0(type, false);
343        }
344        public boolean isClientThriftType(Type type){
345                checkArgument(null != type,"type is null");
346                if(type == ByteBuffer.class){
347                        return false;
348                }               
349                if(ThriftUtils.THRIFT_BUILTIN_KNOWNTYPES.contains(type) || type == byte[].class || type == Void.class ){
350                        return true;
351                }
352                if(ThriftUtils.CAST_TYPES.containsKey(type)){
353                        return false;
354                }
355                if( type instanceof ParameterizedType){
356                        ParameterizedType paramType = (ParameterizedType)type;
357                        Type rawType = paramType.getRawType();
358                        Type[] typeArgs = paramType.getActualTypeArguments();
359                        if(rawType == Map.class){
360                                return isClientThriftType(typeArgs[0]) && isClientThriftType(typeArgs[1]);
361                        }else if (rawType == List.class){
362                                return isClientThriftType(typeArgs[0]);
363                        }else if(rawType == Set.class){
364                                return isClientThriftType(typeArgs[0]);
365                        }else{
366                                throw new IllegalArgumentException(String.format("not allow parameterized type %s", type.toString()));
367                        }
368                }else if(type instanceof Class){
369                        return false;
370                }
371                throw new IllegalArgumentException(String.format("not allow type %s", type.toString()));
372        }
373        private String toDecoratorType(Type type){
374                if(this.isDecoratorType(type)){
375                        ThriftStructDecorator decorator = decorateTypes.get(type);
376                        if(decorator.getDecoratorPackage().equals(getParentPackage())
377                                        && !parent.getImportedList().containsValue(type)){
378                                return ((Class<?>)type).getSimpleName();
379                        }else{
380                                return decorator.getDecoratorClassName();
381                        }
382                }
383                return parent.getTypeName(type);
384        }
385        /**
386         * 返回指定类型在thrifty client对应的装饰类名,如果非decorator,则调用{@link #toClientThriftType0(Type,boolean)}
387         * @param type
388         * @return
389         */
390        public String toThriftyDecoratorType(Type type){
391                if(type == ByteBuffer.class || type == byte[].class){
392                        return parent.getTypeName(byte[].class);
393                }               
394                return toClientThriftType0(type,true);
395        }
396        public void checkType(Type type){
397                if(!verifyType(type)){
398                        throw new IllegalArgumentException(String.format("UNSUPPORTED TYPE %s",
399                                        type.toString()
400                                        ));
401                }
402        }
403        /**
404         * 确保方法的参数和返回类型符合要求
405         * @param method
406         */
407        public void checkType(Method method){
408                checkParameter(method);
409                checkReturnType(method);
410                checkThrows(method);
411        }
412        
413        /**
414         * 验证参数类型
415         * @param method
416         */
417        public void checkParameter(Method method){
418                for (Parameter param : method.getParameters()) {
419                        Type genericType = param.getGenericType();
420                        if(!verifyType(genericType)){
421                                throw new IllegalArgumentException(String.format("UNSUPPORTED TYPE %s of parameter %s in %s",
422                                                genericType.toString(),
423                                                param.name,
424                                                method.getName()
425                                                ));
426                        }
427                }
428        }
429        /**
430         * 验证返回类型
431         * @param method
432         */
433        public void checkReturnType(Method method){
434                Type returnType = method.getGenericReturnType();
435                if(!verifyType(returnType)){
436                        throw new IllegalArgumentException(String.format("UNSUPPORTED TYPE %s of return type for %s",
437                                        returnType.toString(),
438                                        method.getName()                                
439                                        ));
440                }
441        }
442        public void checkThrows(Method method){
443                for(Type exp:method.getGenericExceptionTypes()){
444                        if(!verifyType(exp)){
445                                throw new IllegalArgumentException(String.format("UNSUPPORTED EXCEPTION TYPE %s in %s",
446                                                exp.toString(),
447                                                method.getName()
448                                                ));
449                        }
450                };
451                
452        }
453        
454        /**
455         * 确保{@code clazz}不是泛型类
456         * @param clazz
457         */
458        public static void checkNotGeneric(Class<?> clazz){
459                if(clazz.getTypeParameters().length > 0){
460                        throw new IllegalArgumentException(String.format("%s must not be a generic class", clazz.getName()));
461                }
462        }
463        /**
464         * 确保{@code method}不是泛型方法
465         * @param method
466         */
467        public static void checkNotGeneric(java.lang.reflect.Method method){
468                if(method.getTypeParameters().length > 0){
469                        throw new IllegalArgumentException(String.format("%s must not be a generic method", method.getName()));
470                }
471        }
472        /**
473         * 确保{@code method}不是泛型方法
474         * @param method
475         */
476        public static void checkNotGeneric(Method method){
477                if(method.getTypeParameters().length > 0){
478                        throw new IllegalArgumentException(String.format("%s must not be a generic method", method.getName()));
479                }
480        }
481
482        private String getParentPackage(){
483                if(parent instanceof ThriftStructDecorator){
484                        return ((ThriftStructDecorator)parent).getDecoratorPackage();
485                }else if(parent instanceof ThriftServiceDecorator){
486                        return ((ThriftServiceDecorator<?>)parent).getGeneratePackage();
487                }
488                return null;
489        }
490        public void addReferTypes(Type type){
491                traverseTypes(type,new Action(){
492                        @Override
493                        public void doClass(Class<?> clazz) {
494                                referTypes.add(clazz);
495                                parent.addImportedClass(clazz);
496                                if(ThriftServiceDecoratorConfiguration.INSTANCE.getTaskType().castReferType
497                                                && CAST_TYPES.containsKey(clazz)){
498                                        parent.addImportedClass(CAST_TYPES.get(clazz));
499                                        referTypes.add(CAST_TYPES.get(clazz));
500                                }
501                        }});
502        }
503        public void addReferTypes(Method method){
504                for (Parameter param : method.getParameters()) {
505                        addReferTypes(param.getGenericType());
506                }
507                addReferTypes(method.getGenericReturnType());
508                for(Type exp:method.getGenericExceptionTypes()){
509                        addReferTypes(exp);
510                }
511        }
512        public boolean needTransformer(){
513                return Iterators.tryFind(referTypes.iterator(), new Predicate<Class<?>>(){
514                        @Override
515                        public boolean apply(Class<?> input) {
516                                return ThriftUtils.needTransformer(input);
517                        }}).isPresent();
518        }
519        public List<Class<?>> getTypesWithDecorator(){
520                return Lists.transform(getDecorateTypes(),new Function<ThriftStructDecorator,Class<?>>(){
521                        @Override
522                        public Class<?> apply(ThriftStructDecorator input) {
523                                return input.getBaseClass();
524                        }});
525        }
526        public List<Class<?>> getReferExceptions(){
527                Iterable<Class<?>> iterable = Iterables.filter(referTypes, new Predicate<Class<?>>(){
528                        @Override
529                        public boolean apply(Class<?> input) {
530                                return Exception.class.isAssignableFrom(input);
531                        }});
532                return Lists.newArrayList(iterable);
533        }
534        /**
535         * 创建{@link ThriftStructDecorator}实例<br>
536         * 如果{@code structType}有循环引用则抛出异常
537         * @param structType
538         * @return
539         */
540        private ThriftStructDecorator makeThriftStructDecorator(Class<?> structType){
541        checkNotNull(structType, "structType is null");
542        Deque<Class<?>> stack = this.stack.get();
543        if (stack.contains(structType)) {
544            String path = Joiner.on("->").join(Iterables.transform(Iterables.concat(stack, ImmutableList.of(structType)), new Function<Class<?>, Object>()
545            {
546                @Override
547                public Object apply(Class<?> input)
548                {
549                    return TypeToken.of(input).getRawType().getName();
550                }
551            }));
552            throw new IllegalArgumentException("Circular references are not allowed: " + path);
553        }
554        stack.push(structType);
555        try {
556            return new ThriftStructDecorator(structType);
557        }
558        finally {
559                Class<?> top = stack.pop();
560            checkState(structType.equals(top),
561                    "ThriftCatalog circularity detection stack is corrupt: expected %s, but got %s",
562                    structType,
563                    top);
564        }
565        }
566}