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