001package net.gdface.codegen.thrift;
002
003import org.apache.commons.cli.CommandLine;
004import org.apache.commons.cli.Options;
005import org.apache.commons.cli.ParseException;
006import org.apache.commons.configuration2.Configuration;
007import org.apache.commons.configuration2.PropertiesConfiguration;
008import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
009import org.apache.commons.configuration2.io.FileHandler;
010
011import static com.google.common.base.Preconditions.*;
012
013import com.google.common.base.Throwables;
014import com.google.common.collect.Lists;
015import com.google.common.collect.Maps;
016
017import net.gdface.codegen.generator.CodeWriter;
018import net.gdface.codegen.generator.CxxCodeWriter;
019import net.gdface.codegen.generator.GeneratorConfiguration;
020import net.gdface.codegen.generator.JavaCodeWriter;
021
022import java.io.File;
023import java.util.List;
024import java.util.Map;
025
026/**
027 * decorator生成器参数对象
028 * @author guyadong
029 *
030 */
031public class ThriftServiceDecoratorConfiguration extends GeneratorConfiguration 
032                                        implements ThriftConstants{
033        private static final String ENCODING = "UTF-8";
034        /**
035         * 任务类型定义
036         * @author guyadong
037         *
038         */
039        public static enum TaskType{            
040                /** 生成thrift service代码 */SERVICE("service"),
041                /** 生成thrift/swift client代码 */CLIENT("client"),
042                /** 生成基于Microsoft/thrifty client代码 */CLIENT_THRIFTY("client_thrifty");
043                public final String folder;
044                private TaskType(String folder){
045                        this.folder = folder;
046                }
047        };
048        /**
049         * 生成代码语言类型定义
050         * @author guyadong
051         *
052         */
053        public static enum LanguageType{
054                /** 生成thrift java代码 */JAVA(""),
055                /** 生成thrift c++代码 */CPP("_cpp"),
056                /** 生成thrift c++代码 */C_GLIB("_c_glib")
057                ;
058                private static LanguageType current = null;
059                /** 模板根文件夹名结尾 */
060                public final String postfix;
061                private LanguageType(String postfix){
062                        this.postfix = postfix;
063                }
064                /** 返回对应的 {@link CodeWriter}实例 */
065                public CodeWriter getCodeWriter(File outputFolder){
066                        switch(this){
067                        case JAVA:
068                                return new JavaCodeWriter(outputFolder);
069                        case CPP:
070                                return new CxxCodeWriter(outputFolder);
071                        case C_GLIB:
072                                return new CxxCodeWriter(outputFolder);
073                        default:
074                                        throw new UnsupportedOperationException("unsupported language type:" + this.name());
075                        }
076                }
077                public static LanguageType getCurrent() {
078                        return current;
079                }
080                public synchronized static void setCurrent(LanguageType current) {
081                        checkState(null == LanguageType.current 
082                                        || LanguageType.current.equals(current),
083                                        "LanguageType.current can be initialized only onece");
084                        LanguageType.current = current;
085                }
086        };
087        public static final String DEFAULT_TEMPLATE_FOLDER = "thrift";
088        public static final String DEFAULT_LANGUAGE = "JAVA";
089        /** refClass 的默认值 */
090        public static final Class<?> DEF_REF_CLASS = Object.class;
091        private static final String NO_REF_CLASS = "";
092        private Map<Class<?>, Class<?>> interfaceClasses;
093        private TaskType taskType;
094        private LanguageType languageType;
095        private String thriftClientPackage;
096        private final PropertiesConfiguration config = new PropertiesConfiguration();
097        public static final ThriftServiceDecoratorConfiguration INSTANCE = new ThriftServiceDecoratorConfiguration();
098        private ThriftServiceDecoratorConfiguration() {
099                super();
100                // 指定模板文件夹
101                this.defaultValue.setProperty(TEMPLATE_FOLDER_OPTION_LONG, DEFAULT_TEMPLATE_FOLDER);
102                // 指定refClass的默认值,避免默认值为{@code null}
103                this.defaultValue.setProperty(REFERENCE_CLASS_OPTION_LONG,NO_REF_CLASS);
104                // 指定refClass的默认值,避免默认值为{@code null}
105                this.defaultValue.setProperty(THRIFT_CLIENT_PKG_OPTION_LONG,"");
106                // 指定refClass的默认值,避免默认值为{@code null}
107                this.defaultValue.setProperty(LANGUAGE_OPTION_LONG,DEFAULT_LANGUAGE);
108                // 指定CONFIG的默认值,避免默认值为{@code null}
109                this.defaultValue.setProperty(CONFIG_OPTION_LONG,null);
110        }
111
112        @Override
113        public void loadConfig(Options options, CommandLine cmd) throws ParseException {
114                super.loadConfig(options, cmd);
115                try {
116                        List<Class<?>> interfaceList = toClassArray((String) getProperty(INTERFACE_CLASS_OPTION_LONG));
117                        List<Class<?>> refList = toClassArray((String) getProperty(REFERENCE_CLASS_OPTION_LONG));
118                        this.thriftClientPackage = (String)getProperty(THRIFT_CLIENT_PKG_OPTION_LONG);
119                        if(refList.size()>0 && refList.size() != interfaceList.size()){
120                                throw new ParseException("mismatch number interface class and reference class");
121                        }
122                        interfaceClasses = Maps.newLinkedHashMap();
123                        for(int i = 0 ;i < interfaceList.size() ; ++i){
124                                Class<?> key = interfaceList.get(i);
125                                if(interfaceList.get(i)!=DEF_REF_CLASS){
126                                        try{
127                                                Class<?> value = refList.get(i);
128                                                interfaceClasses.put(key, value);
129                                        }catch(IndexOutOfBoundsException e){
130                                                interfaceClasses.put(key, DEF_REF_CLASS);
131                                        }
132                                }
133                        }
134                        if(interfaceList.isEmpty()){
135                                throw new ParseException("NOT FOUND VALID interface class define");
136                        }
137                } catch (ClassNotFoundException e) {
138                        throw new ParseException("ClassNotFoundException:"+e.getMessage());
139                }
140                try{
141                        taskType = TaskType.valueOf((String) getProperty(TASK_TYPE_OPTION_LONG));
142                        if((taskType == TaskType.CLIENT || taskType == TaskType.CLIENT_THRIFTY) && this.thriftClientPackage.isEmpty()){
143                                throw new IllegalArgumentException(String.format("must set param :%s",THRIFT_CLIENT_PKG_OPTION_LONG));
144                        }
145                }catch(IllegalArgumentException e){
146                        throw new ParseException(e.getMessage());
147                }
148                try{
149                        languageType = LanguageType.valueOf((String) getProperty(LANGUAGE_OPTION_LONG));
150                        LanguageType.setCurrent(languageType);
151                }catch(IllegalArgumentException e){
152                        throw new ParseException(e.getMessage());
153                }
154                if(hasProperty(CONFIG_OPTION_LONG)){
155                        File configFile = new File((String) getProperty(CONFIG_OPTION_LONG));
156                        checkArgument(configFile.isFile() && configFile.getName().endsWith(".properties"),
157                                        "%s must be a .properties file",CONFIG_OPTION_LONG);
158                        try {
159                                // 指定文件编码方式,否则properties文件读取中文会是乱码,要求文件编码是UTF-8
160                            FileBasedConfigurationBuilder.setDefaultEncoding(PropertiesConfiguration.class, ENCODING);
161                            new FileHandler(config).load(configFile);
162                        } catch (Exception e) {
163                                Throwables.throwIfUnchecked(e);
164                                throw new RuntimeException(e);
165                        }
166                }
167        }
168        
169        private List<Class<?>> toClassArray(String input) throws ClassNotFoundException{
170                String[] classNames = input.split(",");
171                List<Class<?>>result = Lists.newArrayList(); 
172                for(String name:classNames){
173                        result.add(name.isEmpty()? DEF_REF_CLASS : Class.forName(name));
174                }
175                return result;
176        }
177
178        public Map<Class<?>, Class<?>> getInterfaceClasses() {
179                return interfaceClasses;
180        }
181
182        public TaskType getTaskType() {
183                return taskType;
184        }
185
186        public LanguageType getLanguageType() {
187                return languageType;
188        }
189
190        @Override
191        public String getTemplateFolder() {
192                String folder = super.getTemplateFolder();
193                StringBuilder sb = new StringBuilder(folder);           
194                if(DEFAULT_TEMPLATE_FOLDER.equals(folder)){
195                        sb.append(getLanguageType().postfix);   
196                }               
197                sb.append("/").append(getTaskType().folder);
198                return sb.toString();
199        }
200
201        public String getThriftClientPackage() {
202                return thriftClientPackage;
203        }
204        public CodeWriter getCodeWriter(){
205                return this.getLanguageType().getCodeWriter(getOutputLocation());
206        }
207
208        /**
209         * @return config
210         */
211        public Configuration getConfig() {
212                return config;
213        }
214}