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