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