001package net.gdface.codegen.thrift;
002
003import static com.google.common.base.Preconditions.*;
004
005import com.google.common.base.MoreObjects;
006import com.google.common.base.Supplier;
007import com.google.common.base.Suppliers;
008
009/**
010 * 定义thrift协议cxx类型
011 * @author guyadong
012 *
013 */
014public class CxxTypeMeta {
015
016        enum ThriftProtocolType {
017            TYPE_VOID(true,false,false),
018            TYPE_STRING(true,false,false),
019            TYPE_BOOL(true,false,false),
020            TYPE_I8(true,false,true),
021            TYPE_I16(true,false,true),
022            TYPE_I32(true,false,true),
023            TYPE_I64(true,false,true),
024            TYPE_DOUBLE(true,false,true),
025            TYPE_BINARY(true,false,false),
026            TYPE_STRUCT(false,false,false),
027            TYPE_MAP(false,true,false),
028            TYPE_SET(false,true,false),
029            TYPE_LIST(false,true,false),
030            TYPE_ENUM(false,false,false);
031            public final boolean isBaseType,isContainer,isNumber;
032                private ThriftProtocolType(boolean isBaseType, boolean isContainer,boolean isNumber) {
033                        this.isBaseType = isBaseType;
034                        this.isContainer = isContainer;
035                        this.isNumber = isNumber;
036                }
037                private Supplier<String> getTypeSupplier(CxxTypeMeta meta){
038                        switch(this){
039                    case TYPE_MAP:
040                        return meta.mapSupplier;
041                    case TYPE_SET:
042                        return meta.setSupplier;
043                    case TYPE_LIST:
044                        return meta.listSupplier;
045                    default:
046                        return meta.type;
047                        }
048                }
049        };
050    public static final CxxTypeMeta BOOL = new CxxTypeMeta(ThriftProtocolType.TYPE_BOOL, "bool");
051    public static final CxxTypeMeta BYTE = new CxxTypeMeta(ThriftProtocolType.TYPE_I8, "int8_t");
052    public static final CxxTypeMeta DOUBLE = new CxxTypeMeta(ThriftProtocolType.TYPE_DOUBLE, "double");
053    public static final CxxTypeMeta I16 = new CxxTypeMeta(ThriftProtocolType.TYPE_I16, "int16_t");
054    public static final CxxTypeMeta I32 = new CxxTypeMeta(ThriftProtocolType.TYPE_I32, "int32_t");
055    public static final CxxTypeMeta I64 = new CxxTypeMeta(ThriftProtocolType.TYPE_I64, "int64_t");
056    public static final CxxTypeMeta STRING = new CxxTypeMeta(ThriftProtocolType.TYPE_STRING, "std::string");
057    public static final CxxTypeMeta BINARY = new CxxTypeMeta(ThriftProtocolType.TYPE_BINARY, "std::string");
058    public static final CxxTypeMeta VOID = new CxxTypeMeta(ThriftProtocolType.TYPE_VOID, "void");
059        private Supplier<String> arraySupplier=new Supplier<String>(){      
060        @Override
061        public String get() {
062                StringBuilder sb = new StringBuilder();
063                sb.append("std::vector<")
064                        .append(valueType.getType()).append(">");
065                return  sb.toString();
066        }};
067        private Supplier<String> listSupplier=new Supplier<String>(){
068
069                @Override
070                public String get() {
071                        StringBuilder sb = new StringBuilder();
072                        sb.append("std::vector<")
073                                .append(valueType.getType()).append(">");
074                        return  sb.toString();  
075                }};
076        private Supplier<String> setSupplier=new Supplier<String>(){
077
078                @Override
079                public String get() {
080                        StringBuilder sb = new StringBuilder();
081                        sb.append("std::set<")
082                                .append(valueType.getType()).append(">");
083                        return  sb.toString();  
084                }};
085        private Supplier<String> mapSupplier = new Supplier<String>(){
086
087                @Override
088                public String get() {
089                StringBuilder sb = new StringBuilder();
090                        sb.append("std::map<")
091                                .append(keyType.getType()).append(",")
092                                .append(valueType.getType()).append(">");
093                        return sb.toString();
094                }};
095        private final ThriftProtocolType protocolType;
096        private final CxxTypeMeta keyType;
097        private final CxxTypeMeta valueType;
098        private final Supplier<String> type;
099        private final boolean isArray;
100        private static class NameSupplier implements Supplier<String>{
101                private String cxxType;
102
103                public NameSupplier(String cxxType) {
104                        super();
105                        checkArgument(null !=cxxType, "cxxType is null");
106                        this.cxxType = cxxType;
107                }
108
109                @Override
110                public String get() {
111                        return CxxHelper.getTypeName(cxxType);
112                }
113        }
114        static CxxTypeMeta struct(final String cxxType)
115    {           
116        Supplier<String> supplier = new NameSupplier(cxxType);
117        return new CxxTypeMeta(ThriftProtocolType.TYPE_STRUCT,supplier);
118    }
119
120    static CxxTypeMeta enumType(final String cxxType)
121        {
122        Supplier<String> supplier = new NameSupplier(cxxType);
123            return new CxxTypeMeta(ThriftProtocolType.TYPE_ENUM,supplier);
124        }
125
126        static <K, V> CxxTypeMeta map(CxxTypeMeta keyType, CxxTypeMeta valueType)
127    {
128        if(null == valueType || null == valueType){
129                return null;
130        }
131
132        return new CxxTypeMeta(ThriftProtocolType.TYPE_MAP, keyType, valueType, false);
133    }
134        static <E> CxxTypeMeta set(CxxTypeMeta valueType)
135    {
136                 if(null == valueType){
137                        return null;
138             }
139
140        return new CxxTypeMeta(ThriftProtocolType.TYPE_SET, null, valueType, false);
141    }
142
143    static <E> CxxTypeMeta list(CxxTypeMeta valueType)
144    {
145         if(null == valueType){
146                return null;
147         }
148
149        return new CxxTypeMeta(ThriftProtocolType.TYPE_LIST, null, valueType, false);
150    }
151    static CxxTypeMeta array(CxxTypeMeta valueType)
152    {
153        if(null == valueType){
154                return null;
155        }
156        return new CxxTypeMeta(ThriftProtocolType.TYPE_LIST, null, valueType, true);
157    }
158    /**
159     * 容器类型构造方法<br>
160     * {@code isAray}为{@code true}且{@code valueType}为BYTE时,也允许{@code protocolType}为BINARY
161     * @param protocolType
162     * @param keyType
163     * @param valueType
164     * @param isArray 
165     */
166    private CxxTypeMeta(ThriftProtocolType protocolType, CxxTypeMeta keyType, CxxTypeMeta valueType, boolean isArray)
167    {
168        checkArgument(null !=protocolType, "protocolType is null");        
169        checkArgument(protocolType.equals(ThriftProtocolType.TYPE_BINARY) || protocolType.isContainer, "protocolType must be SET,MAP,LIST,BINARY");
170        if(isArray){
171                checkArgument(protocolType.equals(ThriftProtocolType.TYPE_LIST) || protocolType.equals(ThriftProtocolType.TYPE_BINARY),"protocolType must be LIST or BINARY,if isArray");
172                if(protocolType.equals(ThriftProtocolType.TYPE_BINARY)){
173                        checkArgument(BYTE.equals(valueType),"valueType must be BYTE,if isArray is true and protocolType is TYPE_LIST");
174                }
175        }
176        this.protocolType = protocolType;
177        this.isArray=isArray;
178        this.type = isArray? arraySupplier : protocolType.getTypeSupplier(this);
179        this.keyType = keyType;
180        this.valueType = checkNotNull(valueType,"valueType is null");
181    }
182    /**
183     * 简单类型构造方法
184     * @param protocolType
185     * @param cxxType
186     */
187    private CxxTypeMeta(ThriftProtocolType protocolType, String cxxType){
188        this(protocolType,Suppliers.ofInstance(checkNotNull(cxxType, "cxxType is null")));
189        }
190    /**
191     * 非容器类型构造方法
192     * @param protocolType
193     * @param cxxType
194     */
195    private CxxTypeMeta(ThriftProtocolType protocolType, Supplier<String> cxxType){
196        checkNotNull(protocolType, "protocolType is null"); 
197        checkArgument(!protocolType.isContainer, "protocolType must not be SET,MAP,LIST");        
198        checkNotNull(cxxType, "cxxType is null");        
199        this.protocolType = protocolType;
200        this.isArray = false;
201        this.type = cxxType;
202        this.keyType = null;
203        this.valueType = null;
204        }
205        public String getType(){
206                return type.get();
207        }
208        public String getSampleType(){
209                if(protocolType==ThriftProtocolType.TYPE_STRUCT || protocolType==ThriftProtocolType.TYPE_ENUM){
210                        return CxxHelper.simpleName(type.get());
211                }
212                return type.get();
213        }
214        public String getFullType(){
215                if(type instanceof NameSupplier){
216                        return ((NameSupplier)type).cxxType;
217                }else{
218                        return type.get();
219                }
220        }
221        public String getNamespace(){
222                if(type instanceof NameSupplier){
223                        return CxxHelper.getCxxNamespace(((NameSupplier)type).cxxType);
224                }else{
225                        return CxxHelper.getCxxNamespace(type.get());
226                }
227        }
228        public CxxTypeMeta getKeyType() {
229        checkState(keyType != null, "%s does not have a key", protocolType);
230                return keyType;
231        }
232        public CxxTypeMeta getValueType() {
233                checkState(valueType != null, "%s does not have a value", protocolType);
234                return valueType;
235        }
236
237        public ThriftProtocolType getProtocolType() {
238                return protocolType;
239        }
240        public boolean isBool(){
241                return ThriftProtocolType.TYPE_BOOL.equals(getProtocolType());
242        }
243        public boolean isVoid(){
244                return ThriftProtocolType.TYPE_VOID.equals(getProtocolType());
245        }
246        public boolean isString(){
247                return ThriftProtocolType.TYPE_STRING.equals(getProtocolType());
248        }
249        public boolean isBinary(){
250                return ThriftProtocolType.TYPE_BINARY.equals(getProtocolType());
251        }
252        public boolean isMap() {
253                return ThriftProtocolType.TYPE_MAP.equals(getProtocolType());
254        }
255        public boolean isSet() {
256                return ThriftProtocolType.TYPE_SET.equals(getProtocolType());
257        }
258        public boolean isList() {
259                return ThriftProtocolType.TYPE_LIST.equals(getProtocolType());
260        }
261        public boolean isEnum() {
262                return ThriftProtocolType.TYPE_ENUM.equals(getProtocolType());
263        }
264        public boolean isStruct() {
265                return ThriftProtocolType.TYPE_STRUCT.equals(getProtocolType());
266        }
267        public boolean isContainer(){
268                return this.getProtocolType().isContainer;
269        }
270        public boolean isBaseType(){
271                return this.getProtocolType().isBaseType;
272        }
273        
274        public boolean isNumber(){
275                return getProtocolType().isNumber;
276        }
277        public boolean isArray() {
278                return isArray;
279        }
280
281        public boolean isByValue(){
282                return isBool() || isNumber() || isEnum();
283        }
284        public boolean isByReference(){
285                return !isByValue() && !isVoid();
286        }
287        public boolean isCanMove(){
288                return isBinary() || isString() || isContainer() || isStruct();
289        }
290        public boolean isRaii(){
291                return isBinary() || isString() || isContainer();
292        }
293        /**
294         * 类型namespace转换
295         * @param ns 新的名字空间
296         * @param suffix 后缀
297         * @return
298         */
299        public CxxTypeMeta castNamespace(String ns, String suffix){
300                checkArgument(null !=ns, "ns is null");
301                if(!ns.isEmpty() && !ns.endsWith("::")){
302                        ns = ns+"::";   
303                }
304                if(isContainer()){
305                        CxxTypeMeta k = (null != keyType)?keyType.castNamespace(ns, suffix) : null;
306                        CxxTypeMeta v = (null != valueType)?valueType.castNamespace(ns, suffix) : null;
307                        return cast(k,v,isArray);
308                }else{
309                        if(isEnum() || isStruct()){
310                                String name = CxxHelper.simpleName(getType());
311                                return new CxxTypeMeta(protocolType,ns + name + MoreObjects.firstNonNull(suffix, ""));
312                        }else{
313                                return this;
314                        }
315                }
316        }
317        /**
318         * 类名转换,不含namespace
319         * @param name
320         * @return
321         */
322        public CxxTypeMeta castName(String name){
323                checkArgument(null !=name, "ns is null");
324                checkArgument(isEnum() || isStruct(),"only ENUM or STRUCT can be cast namespace");
325        return new CxxTypeMeta(protocolType,getNamespace() + "::" + name);
326        }
327        /**
328         * 类型转换
329         * @param cxxType
330         * @return
331         */
332        public CxxTypeMeta cast(String cxxType){
333                checkArgument(null !=cxxType, "cxxType is null");
334        return new CxxTypeMeta(protocolType,cxxType);
335        }
336        /**
337         * 类型转换
338         * @param keyType
339         * @param valueType
340         * @param isArray
341         * @return
342         */
343        public CxxTypeMeta cast(CxxTypeMeta keyType, CxxTypeMeta valueType, boolean isArray){
344                return new CxxTypeMeta(protocolType,keyType,valueType,isArray);
345        }
346@Override
347        public String toString() {
348                StringBuilder builder = new StringBuilder();
349                builder.append("CxxTypeMeta [protocolType=");
350                builder.append(protocolType);
351                builder.append(", type=");
352                builder.append(type.get());
353                builder.append(", isArray=");
354                builder.append(isArray);
355                builder.append("]");
356                return builder.toString();
357        }
358        @Override
359        public int hashCode() {
360                final int prime = 31;
361                int result = 1;
362                result = prime * result + ((protocolType == null) ? 0 : protocolType.hashCode());
363                result = prime * result + ((type == null) ? 0 : type.get().hashCode());
364                return result;
365        }
366        @Override
367        public boolean equals(Object obj) {
368                if (this == obj)
369                        return true;
370                if (obj == null)
371                        return false;
372                if (getClass() != obj.getClass())
373                        return false;
374                CxxTypeMeta other = (CxxTypeMeta) obj;
375                if (protocolType != other.protocolType)
376                        return false;
377                if (type == null) {
378                        if (other.type != null)
379                                return false;
380                } else if (!type.get().equals(other.type.get()))
381                        return false;
382                return true;
383        }
384
385}