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.Collections;
015import java.util.Comparator;
016import java.util.List;
017import java.util.Map;
018import java.util.Map.Entry;
019
020import org.slf4j.Logger;
021import org.slf4j.LoggerFactory;
022
023import com.google.common.base.Function;
024import com.google.common.base.Predicate;
025import com.google.common.base.Predicates;
026import com.google.common.collect.ImmutableList;
027import com.google.common.collect.ImmutableMap;
028import com.google.common.collect.Iterables;
029import com.google.common.collect.Maps;
030
031/**
032 * 装饰类信息
033 * @author guyadong
034 *
035 */
036public class ThriftStructDecorator extends AbstractSchema implements ThriftConstants,Comparator<ThriftStructDecorator>,Comparable<ThriftStructDecorator>{
037        private static final Logger logger = LoggerFactory.getLogger(ThriftStructDecorator.class);
038
039        private final Map<String, PropertyDescriptor> fields;
040        private final String decoratorPackage;
041        private final String decoratorClassName;
042        private final TypeHelper typeHelper = new TypeHelper(this);
043
044        private final Map<String, CxxType> cxxFields;
045
046        private final boolean hasOptional;
047        private final boolean hasCanMove;
048
049        private final CxxType cxxType;
050
051        private final TraverseTypeForTryFind findString= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){
052                @Override
053                public boolean apply(CxxTypeMeta input) {
054                        return null != input && (input.isBinary() || input.isString());
055                }});
056        private final TraverseTypeForTryFind findMap= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){
057                @Override
058                public boolean apply(CxxTypeMeta input) {
059                        return null != input && input.isMap();
060                }});
061        private final TraverseTypeForTryFind findSet= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){
062                @Override
063                public boolean apply(CxxTypeMeta input) {
064                        return null != input && input.isSet();
065                }});
066        private final TraverseTypeForTryFind findArray= new TraverseTypeForTryFind(new Predicate<CxxTypeMeta>(){
067                @Override
068                public boolean apply(CxxTypeMeta input) {
069                        return null != input && input.isList();
070                }});
071        /**
072         * 对{@code baseClass}生成装饰类
073         * @param baseClass 
074         */
075        public ThriftStructDecorator(Class<? > baseClass) {
076                this.baseClass = checkNotNull(baseClass);
077                TypeHelper.checkNotGeneric(baseClass);
078                if(baseClass.isInterface()){
079                        throw new IllegalArgumentException("baseClass must not be interface");
080                }
081                fields = ImmutableMap.copyOf(typeHelper.getFields(baseClass,Predicates.<PropertyDescriptor>alwaysTrue(),true));
082                hasOptional = Iterables.tryFind(fields.values(), new Predicate<PropertyDescriptor>(){
083                        @Override
084                        public boolean apply(PropertyDescriptor input) {
085                                return !input.getReadMethod().getReturnType().isPrimitive();
086                        }}).isPresent();
087
088                cxxFields=LanguageType.CPP.equals(LanguageType.getCurrent()) 
089                                ? getThriftTypes(fields) 
090                                : Collections.<String, CxxType>emptyMap();
091                cxxType = CxxType.getThriftType(getBaseClass());
092                hasCanMove = Iterables.tryFind(cxxFields.values(), new Predicate<CxxType>(){
093                        @Override
094                        public boolean apply(CxxType input) {
095                                return input.getStubType().isCanMove();
096                        }}).isPresent();
097                StringBuffer buffer = new StringBuffer(baseClass.getPackage().getName());
098                if(LanguageType.JAVA.equals(LanguageType.getCurrent())){
099                        buffer.append( "." + ThriftUtils.DECORATOR_PKG_SUFFIX);
100                        switch(ThriftServiceDecoratorConfiguration.INSTANCE.getTaskType()){
101                        case CLIENT:
102                                buffer.append(".").append(ThriftUtils.CLIENT_SUFFIX);break;
103                        case CLIENT_THRIFTY:
104                                buffer.append(".").append(ThriftUtils.CLIENT_SUFFIX);break;
105                        default:
106                                        break;
107                        }
108                }
109                decoratorPackage = buffer.toString();
110                decoratorClassName = decoratorPackage + "." + this.baseClass.getSimpleName();           
111        }
112        @Override
113        public boolean compile() {
114                // 循环扫描检查所有的引用类型
115                for(Entry<String, PropertyDescriptor> entry:fields.entrySet()){
116                        PropertyDescriptor descriptor = entry.getValue();
117                        Type returnType = descriptor.getReadMethod().getGenericReturnType();
118                        typeHelper.checkType(returnType);
119                        typeHelper.addReferTypes(returnType);   
120                        if(NameStringUtils.isThriftReserved( descriptor.getName())){
121                                logger.warn("field name '{}' of {} is thrift IDL reserved word", descriptor.getName(),this.getBaseClass().getName());
122                        }
123                        if(NameStringUtils.isJavaReserved( descriptor.getName())){
124                                logger.warn("field name '{}' of {} is java reserved word", descriptor.getName(),this.getBaseClass().getName());
125                        }
126                }
127                if(typeHelper.needTransformer()){
128                        addImportedClass(TypeTransformer.class);
129                }
130                addImportedClass(ThriftDecorator.class);
131                
132                return true;
133        }
134        public String getGetMethodName(String field){
135                PropertyDescriptor descriptor = getField(field);
136                return descriptor.getReadMethod().getName();            
137        }
138        public String getSetMethodName(String field){
139                PropertyDescriptor descriptor = getField(field);
140                return descriptor.getWriteMethod().getName();           
141        }
142
143        public Type getFieldType(String field){
144                PropertyDescriptor descriptor = getField(field);
145                return descriptor.getReadMethod().getGenericReturnType();
146        }
147        
148        public PropertyDescriptor getField(String field){
149                PropertyDescriptor descriptor = fields.get(field);
150                if(null == descriptor){
151                        throw new IllegalArgumentException(String.format("INVALID field %s in %s", field,this.baseClass.getSimpleName()));
152                }
153                return descriptor;
154        }
155        
156        public List<String> getFields(){
157                return ImmutableList.copyOf(this.fields.keySet());
158        }
159        public List<PropertyDescriptor> getFieldDescriptors(){
160                return ImmutableList.copyOf(this.fields.values());
161        }
162        public String getDecoratorPackage() {
163                return decoratorPackage;
164        }
165
166        public String getDecoratorClassName() {
167                return decoratorClassName;
168        }
169
170        public List<ThriftStructDecorator> getDecorateTypes() {
171                return typeHelper.getDecorateTypes();
172        }
173
174        public boolean isDecoratorType(Type type) {
175                return typeHelper.isDecoratorType(type);
176        }
177        
178        public boolean isException(){
179                return Exception.class.isAssignableFrom(getBaseClass());
180        }
181        public boolean isEnum(){
182                return Enum.class.isAssignableFrom(getBaseClass());
183        }
184        public boolean isBean(){
185                return !isException() && !isEnum(); 
186        }
187        public boolean hasStringConstructor(){
188                return ThriftUtils.hasConstructor(getBaseClass(), String.class);
189        }
190        public String toThriftType(Type type) {
191                return typeHelper.toThriftType(type);
192        }
193
194        public String toClientThriftType(Type type) {
195                return typeHelper.toClientThriftType(type);
196        }
197        public String toClientThriftyType(Type type) {
198                return typeHelper.toClientThriftyType(type);
199        }
200
201        public String toDecoratorType(Type type) {
202                return typeHelper.toDecoratorType(type);
203        }
204
205        public String toThriftyDecoratorType(Type type) {
206                return typeHelper.toThriftyDecoratorType(type);
207        }
208        @Override
209        public int hashCode() {
210                final int prime = 31;
211                int result = 1;
212                result = prime * result + ((baseClass == null) ? 0 : baseClass.hashCode());
213                return result;
214        }
215
216        @Override
217        public boolean equals(Object obj) {
218                if (this == obj)
219                        return true;
220                if (obj == null)
221                        return false;
222                if (!(obj instanceof ThriftStructDecorator))
223                        return false;
224                ThriftStructDecorator other = (ThriftStructDecorator) obj;
225                if (baseClass == null) {
226                        if (other.baseClass != null)
227                                return false;
228                } else if (!baseClass.equals(other.baseClass))
229                        return false;
230                return true;
231        }
232
233        @Override
234        public String toString() {
235                StringBuilder builder = new StringBuilder();
236                builder.append("ThriftStructDecorator [baseClass=");
237                builder.append(baseClass);
238                builder.append("]");
239                return builder.toString();
240        }
241        public void removeDecorateTypesFromImports(){
242                this.removeClassFromImports(typeHelper.getTypesWithDecorator());
243        }
244
245        @Override
246        public int compare(ThriftStructDecorator o1, ThriftStructDecorator o2) {
247                return o1.getBaseClass().getSimpleName().compareTo(o2.getBaseClass().getSimpleName());
248        }
249
250        @Override
251        public int compareTo(ThriftStructDecorator o) {
252                return getBaseClass().getSimpleName().compareTo(o.getBaseClass().getSimpleName());
253        }
254
255        private static ImmutableMap<String, CxxType> getThriftTypes(Map<String, PropertyDescriptor> fields){                
256                Map<String, CxxType> m = Maps.transformValues(checkNotNull(fields,"fields is null"), 
257                                new Function<PropertyDescriptor,CxxType>(){
258                                        @Override
259                                        public CxxType apply(PropertyDescriptor input) {
260                                                return CxxType.getThriftType(input.getReadMethod().getGenericReturnType());
261                                        }                       
262                });
263                return ImmutableMap.copyOf(m);
264        }
265        public ImmutableMap<String, CxxType> getCxxFieldsAsMap(){
266                return ImmutableMap.copyOf(this.cxxFields);
267        }
268        public CxxType getCxxField(String name){                
269                return cxxFields.get(checkNotNull(name,"name is null"));
270        }
271        /**
272         * 是否有optional字段
273         * @return
274         */
275        public boolean isHasOptionalField(){
276                return hasOptional;
277        }
278        
279        /**
280         * 是否有需要移动的字段
281         * @return
282         */
283        public boolean isHasCanMoveField() {
284                return hasCanMove;
285        }
286        public CxxType getCxxType() {
287                return cxxType;
288        }
289        
290        private static class TraverseTypeForTryFind implements Predicate<CxxType>{
291                private Predicate<CxxTypeMeta> finder;
292                TraverseTypeForTryFind(Predicate<CxxTypeMeta> finder){
293                        this.finder = finder;
294                }
295                @Override
296                public boolean apply(CxxType input) {
297                        if(null == input){
298                                return false;
299                        }
300                        CxxTypeMeta type = input.getUiType();
301                        if(finder.apply(type)){
302                                return true;
303                        }
304                        if(type.isContainer()){
305                                CxxTypeMeta keyType = type.getKeyType();
306                                CxxTypeMeta valueType = type.getValueType();
307                                if(null != keyType && finder.apply(keyType)){
308                                        return true;
309                                }
310                                if(null != valueType && finder.apply(valueType)){
311                                        return true;
312                                }
313                        }
314                        return false;
315                }}
316        public boolean isUseString(){
317                return Iterables.tryFind(cxxFields.values(), findString).isPresent();
318        }
319        public boolean isUseMap(){
320                return Iterables.tryFind(cxxFields.values(), findMap).isPresent();
321        }
322        public boolean isUseSet(){
323                return Iterables.tryFind(cxxFields.values(), findSet).isPresent();
324        }
325        public boolean isUseVector(){
326                return Iterables.tryFind(cxxFields.values(), findArray).isPresent();
327        }
328}