001package net.gdface.codegen.webclient;
002
003import java.beans.PropertyDescriptor;
004import java.io.File;
005import java.io.IOException;
006import java.lang.reflect.Method;
007import java.util.HashMap;
008import java.util.Map;
009import java.util.Map.Entry;
010import java.util.Set;
011import java.util.regex.Matcher;
012import java.util.regex.Pattern;
013
014import org.slf4j.Logger;
015import org.slf4j.LoggerFactory;
016
017import net.gdface.codegen.CodeGenUtils;
018import net.gdface.utils.Assert;
019import net.gdface.utils.BeanPropertyUtils;
020import net.gdface.utils.Configuration;
021import net.gdface.utils.FaceUtilits;
022import net.gdface.utils.Judge;
023
024/**
025 * 用于从soapcpp2生成的gsoap stub头文件中解析读取生成C++代码所需要的数据
026 * @author guyadong
027 *
028 */
029public class GSoapHeaderHelper  implements GSoapConstants,WebClientConstants{
030        private static final Logger logger = LoggerFactory.getLogger(GSoapClient.class);
031        /**
032         * gsoap properties
033         */
034        private static final Configuration prop=GSoapProperties.getInstance();
035        /**
036         * gsoap代码中下划线符号的替换字符串
037         */
038        private static final String underscore;
039        static {
040                underscore=prop.getProperty(PROP_UNDERSCORE).trim();
041                Assert.notEmpty(underscore, "underscore");
042        }
043        /**
044         * gsoap stub头文件文本内容(xxxxStub.h)
045         */
046        private final String stubHeader;
047        /**
048         * namespace->prefix映射
049         */
050        private final Map<String, String> gsoapNamespaceMap;
051        /**
052         * 所有wsdl名称-->stub类型映射
053         */
054        private final Map<String, String> typeNameMap;
055        /**
056         * 所有引用类型的java Class-->stub类型映射
057         */
058        private final Map<Class<?>, String> referenceClassMap;
059        
060        /**
061         * 引用类型的属性映射
062         */
063        private final Map<Class<?>,Map<String, PropertyDescriptor>> refClassPropertiesMap;
064        /**
065         * stub类型的属性映射
066         */
067        private Map<String, Map<String, String>> stubClassProppertiesMap;
068        /**
069         * gsoap stub源代码文件前缀
070         */
071        private final String stubPrefix;
072
073        /**
074         * gsoapstub源代码文件夹位置
075         */
076        private final File stubFolder;
077        private final Class<?> serviceClass;
078
079        /**
080         * @param stubFolder gsoap stub代码文件夹
081         * @param serviceClass TODO
082         * @param stubPrefix gsoap stub代码文件前缀
083         */
084        public GSoapHeaderHelper(File stubFolder, Class<?> serviceClass, String stubPrefix) {
085                Assert.notNull(stubFolder, "stubFolder");
086                Assert.notNull(serviceClass, "serviceClass");
087                this.serviceClass=serviceClass;
088                this.stubFolder= stubFolder;
089                this.stubPrefix= Judge.isEmpty(stubPrefix) ?this.serviceClass.getSimpleName().toLowerCase():stubPrefix;         
090                this.stubHeader=load(new File(this.stubFolder,this.stubPrefix+"Stub.h"));
091                this.gsoapNamespaceMap=createGSoapNamespaceMap(stubHeader);
092                this.typeNameMap=createTypeNameMap(stubHeader);
093                this.referenceClassMap=createReferenceClassMap(stubHeader);
094                this.refClassPropertiesMap=createBeanClassPropertiesMap(referenceClassMap.keySet());
095                this.stubClassProppertiesMap=createStubClassPropDefMap(CodeGenUtils.toSet(referenceClassMap.values()));
096        }
097        /**
098         * @return referenceClassMap
099         */
100        public Map<Class<?>, String> getReferenceClassMap() {
101                return referenceClassMap;
102        }
103        
104        /**
105         * @return 所有Bean类型映射
106         */
107        public Map<Class<?>, String> getBeanClassMap() {
108                Map<Class<?>, String> beanMap=new HashMap<Class<?>, String>();
109                for(Entry<Class<?>, String> entry:referenceClassMap.entrySet()){
110                        if(!Exception.class.isAssignableFrom(entry.getKey())){
111                                beanMap.put(entry.getKey(), entry.getValue());
112                        }
113                }
114                return beanMap;
115        }
116        /**
117         * @param method
118         * @param prefix
119         * @param suffix
120         * @return
121         * @see #getStubPortName(String, String, String)
122         */
123        public String getStubPortName(Method method,String prefix,String suffix){
124                Assert.notNull(method, "method");
125                return getStubPortName(method.getName(),prefix,suffix);
126        }
127        /**
128         * 返回指定方法的在stub中对应的名字
129         * @param methodName 方法名
130         * @param prefix 方法名前缀
131         * @param suffix 方法名后缀
132         * @return
133         */
134        public String getStubPortName(String methodName,String prefix,String suffix){
135                Assert.notEmpty(methodName, "methodName");
136                StringBuilder sb=new StringBuilder();
137                if(!Judge.isEmpty(prefix))
138                        sb.append(prefix);              
139                sb.append(methodName);
140                if(!Judge.isEmpty(suffix))
141                        sb.append(suffix);
142                return typeNameMap.get(sb.toString());
143        }
144        /**
145         * 将header文件加载到内存
146         * @param gsoapHeader
147         * @return 返回文件所有文本内容的字符串
148         */
149        private static String load(File gsoapHeader){
150                Assert.notNull(gsoapHeader, "gsoapHeader");
151                Assert.isTrue(gsoapHeader.isFile(), "gsoapHeader.isFile()");
152                try {
153                        return new String(FaceUtilits.getBytesNotEmpty(gsoapHeader));
154                } catch (IOException e) {
155                        throw new RuntimeException(e);
156                }
157        }
158        public String getStubClassName(Class<?> clazz){
159                String prefix = getNamespacePrefix(clazz);
160                if(null!=prefix)
161                        return String.format("%s__%s",prefix,clazz.getSimpleName());                                            
162                return null;
163        }
164        public String getNamespacePrefix(Class<?> clazz){
165                Assert.notNull(clazz, "clazz");
166                String namespace = NAMESPACEGENERATOR.schemaNamespaceFromPackageName(clazz.getPackage().getName()).toString();
167                String prefix = gsoapNamespaceMap.get(namespace);
168                if(null==prefix){
169                        namespace=NAMESPACEGENERATOR.namespaceFromPackageName(clazz.getPackage().getName()).toString();
170                        prefix = gsoapNamespaceMap.get(namespace);
171                }
172                return prefix;
173        }
174        
175        /**
176         *  分析gsoap .h文件,创建实际的namespace prefix与namespace的映射表
177         * @param gsaopHeader
178         * @return 
179         */
180        protected static Map<String, String> createGSoapNamespaceMap(String header){
181                String regexpString = GSoapProperties.getInstance().getProperty(PROP_REGEXP_NAMESPACE).trim();
182                Assert.notEmpty(regexpString, "regexpString");
183                Pattern pattern = Pattern.compile(regexpString,Pattern.MULTILINE);
184                Matcher matcher = pattern.matcher(header);
185                Map<String,String>map=new HashMap<String,String>();
186                while (matcher.find()) {
187                        map.put(matcher.group(2),matcher.group(1));
188                        logger.debug("namespace {} = {}",matcher.group(1),matcher.group(2));
189            }
190                Assert.isTrue(!map.isEmpty(), "!map.isEmpty()");
191                return map;
192        }
193        /**
194         *  分析gsoap .h文件,创建实际的原始类名与gosap stub代码中的类名映射表
195         * @param gsaopHeader
196         * @return 
197         */
198        protected static Map<String, String> createTypeNameMap(String headerFile){
199                String regexpString = prop.getProperty(PROP_REGEXP_CLASSMAP).trim();
200                Assert.notEmpty(regexpString, "regexpString");
201                Pattern pattern = Pattern.compile(regexpString,Pattern.MULTILINE);
202                Matcher matcher = pattern.matcher(headerFile);
203                Map<String,String>map=new HashMap<String,String>();
204                while (matcher.find()) {
205                        String stubClassName=matcher.group(1);
206                        // 将_USCORE替换为_
207                        String cppClassName=matcher.group(3).replace(underscore, "_");                  
208                        map.put(cppClassName, stubClassName);
209                        logger.debug("type name map {} = {}",cppClassName,stubClassName);
210            }
211                Assert.isTrue(!map.isEmpty(), "!map.isEmpty()");
212                return map;
213        }
214        /**
215         *  分析gsoap .h文件,创建引用类型名与gosap stub代码中的类名映射表
216         * @param gsaopHeader
217         * @return 
218         */
219        protected Map<Class<?>, String> createReferenceClassMap(String headerFile){
220                String regexpString = prop.getProperty(PROP_REFERENCE_CLASS).trim();            
221                Assert.notEmpty(regexpString, "regexpString");
222                Pattern pattern = Pattern.compile(regexpString,Pattern.MULTILINE);
223                Matcher matcher = pattern.matcher(headerFile);
224                Map<Class<?>, String>map=new HashMap<Class<?>,String>();
225                while (matcher.find()) {
226                        String stubClassName=matcher.group(1);
227                        // 将_USCORE替换为_                 
228                        Class<?> clazz = classFromStubClassName(stubClassName.replace(underscore, "_"));
229                        map.put(clazz, stubClassName);
230                        logger.debug("reference class map {} = {}",clazz.getName(),stubClassName);
231            }
232                if(map.isEmpty())
233                        logger.info("no reference class defined");
234                return map;
235        }
236
237        /**
238         * 创建所有引用
239         * @param classSet
240         * @return
241         */
242        private static Map<Class<?>, Map<String, PropertyDescriptor>> createBeanClassPropertiesMap(
243                        Set<Class<?>> classSet) {
244                Map<Class<?>, Map<String, PropertyDescriptor>> map = new HashMap<Class<?>, Map<String, PropertyDescriptor>>();
245                for (Class<?> refClass : classSet) {
246                        map.put(refClass, BeanPropertyUtils.getProperties(refClass, 3));
247                }
248                return map;
249        }
250        
251        /**
252         * 
253         * 分析gsoap .h文件,获取所有stub类型的属性定义
254         * @param stubClassSet
255         * @return
256         */
257        private Map<String, Map<String, String>> createStubClassPropDefMap(Set<String> stubClassSet){
258                Assert.notNull(stubClassSet, "stubClassSet");
259                Map<String,Map<String,String>>map=new HashMap<String,Map<String,String>>();
260                String regClassDef = prop.getProperty(PROP_REGEXP_CLASSDEF);
261                String regPropDef = prop.getProperty(PROP_REGEXP_PROPDEF);      
262                Assert.notEmpty(regClassDef, "regClassDef");            
263                Assert.notEmpty(regPropDef, "regPropDef");
264                Pattern pattern=Pattern.compile(regPropDef, Pattern.MULTILINE);
265                for(String type:stubClassSet){
266                        logger.debug("class {}{",type);
267                        Matcher mc = Pattern.compile(regClassDef.replace("$CLASS_NAME$",type), Pattern.MULTILINE).matcher(stubHeader);
268                        if(!mc.find())
269                                throw new IllegalArgumentException(String.format("regexp not match Class Definition of  %s in header file",type));
270                        String props=mc.group(1);
271                        Matcher matcher = pattern.matcher(props);
272                        Map<String,String> propMap=new HashMap<String,String>();
273                        while(matcher.find()){
274                                propMap.put(matcher.group(2).trim(), matcher.group(1).trim());
275                                logger.debug("\t{} : {}",matcher.group(1),matcher.group(2));
276                        }
277                        logger.debug("}",type);
278                        map.put(type, propMap);
279                }
280                return map;
281        }
282
283        /**
284         * 将gsoap stub中的类名转换为java Class
285         * @param stubClassName gsoap stub中的类名
286         * @return
287         */
288        public Class<?> classFromStubClassName(String stubClassName){             
289                Pattern pattern = Pattern.compile("^\\b([a-zA-Z\\d]+\\d+)__([a-zA-Z\\d]+)?$");
290                // 正则匹配前先将_USCORE替换为_
291                Matcher matcher = pattern.matcher(stubClassName.replace(underscore, "_"));
292                if(matcher.find()){
293                        String namespace = CodeGenUtils.getKey(this.gsoapNamespaceMap,matcher.group(1));
294                        return CodeGenUtils.classFromNamespaceAndClassName(namespace,matcher.group(2));
295                }else
296                        throw new IllegalArgumentException("invalid stubClassName");
297        }
298
299        /**
300         * 将指定的变量名下划线用 {@value #underscore}替换返回stub类中的变量名
301         * @param name
302         * @return
303         */
304        public String toStubName(String name){
305                Assert.notNull(name, "name");
306                return name.replace("_", underscore);
307        }
308        /**
309         * @return typeNameMap
310         */
311        public Map<String, String> getTypeNameMap() {
312                return typeNameMap;
313        }
314
315        /**
316         * @return refClassPropertiesMap
317         */
318        public Map<Class<?>, Map<String, PropertyDescriptor>> getRefClassPropertiesMap() {
319                return refClassPropertiesMap;
320        }
321
322        boolean isPointer(String stubClassName){
323                Assert.notEmpty(stubClassName, "stubClassName");
324                return stubClassName.endsWith("*");
325        }
326        /**
327         * 返回指定属性的类型
328         * @param prop
329         * @return
330         */
331        public static Class<?> getClassOfProperty(PropertyDescriptor prop){
332                Assert.notNull(prop,"prop");
333                Method method = prop.getReadMethod();
334                if(null==method)
335                        throw new IllegalArgumentException(String.format("not found read method for %s",prop.getName()));
336                return method.getReturnType();
337        }
338        /**
339         * 获取stubClass中指定属性prop的类型描述
340         * @param stubClassName
341         * @param prop
342         * @return
343         */
344        public Entry<String, String> getStubPropertyDefine(String stubClassName,String prop){
345                Assert.notEmpty(stubClassName, "className");
346                Assert.notEmpty(prop, "prop");
347                Map<String, String> properties = stubClassProppertiesMap.get(stubClassName);
348                //String stubPropName=stubClassName.replace("_", underscore);   
349                for( Entry<String, String> entry:properties.entrySet()){
350                        if(entry.getKey().matches("^"+prop+"_?$"))
351                                return entry;
352                }
353                throw new IllegalArgumentException(String.format("not found property %s for %s",prop,stubClassName));
354        }
355        /**
356         * 返回stub类型中的属性名在引用类型(refClass)对应的{@link PropertyDescriptor}对象
357         * @param refClass 引用类型
358         * @param stubProp stub类型中的属性名
359         * @return
360         */
361        public PropertyDescriptor getPropertyDescriptor(Class<?> refClass,String stubProp){
362                Assert.notNull(refClass, "refClass");
363                Assert.notEmpty(stubProp, "stubProp");
364                Map<String, PropertyDescriptor> properties = refClassPropertiesMap.get(refClass);
365                if(null==properties)
366                        throw new IllegalArgumentException(String.format("%s not a reference class(不是引用类型)",refClass.getName()));
367                String stubPropName=stubProp.replace(underscore, "_");  
368                for( Entry<String, PropertyDescriptor> entry:properties.entrySet()){
369                        if(stubPropName.matches("^"+entry.getKey()+"_?$"))
370                                return entry.getValue();
371                }
372                throw new IllegalArgumentException(String.format("not found property %s for %s",stubProp,refClass.getName()));
373        }
374        /**
375         * java类名转为cpp的类名(包括namespace)
376         * @param className
377         * @return
378         */
379        public static String toCppFullName(String className){
380                Assert.notEmpty(className, "className");
381                return className.replaceAll("\\.", "::");
382        }
383        /**
384         * @param clazz
385         * @return
386         * @see #toCppFullName(String)
387         */
388        public static String toCppFullName(Class<?> clazz){
389                Assert.notNull(clazz, "className");
390                return toCppFullName(clazz.getName());
391        }
392        /**
393         * java基本数据类型与cpp类型的映射表
394         */
395        private static final Map<Class<?>, String> BASE_TYPE_MAP = new HashMap<Class<?>, String>() {
396                /**
397                 * 
398                 */
399                private static final long serialVersionUID = 1L;
400                {
401                        put(void.class,"void");
402                        put(int.class,"int");
403                        put(long.class,"long");
404                        put(byte.class,"char");
405                        put(double.class,"double");
406                        put(float.class,"");
407                        put(char.class,"char");
408                        put(short.class,"short");
409                        put(boolean.class,"bool");
410                        put(Integer.class,"std::shared_ptr<int>");
411                        put(Long.class,"std::shared_ptr<long>");
412                        put(Byte.class,"std::shared_ptr<char>");
413                        put(Double.class,"std::shared_ptr<double>");
414                        put(Float.class,"std::shared_ptr<float>");
415                        put(Character.class,"std::shared_ptr<<char>");
416                        put(Short.class,"std::shared_ptr<short>");
417                        put(Boolean.class,"std::shared_ptr<bool>");
418                        put(String.class,"std::string");
419                        put(byte[].class,"std::shared_ptr<std::vector<char>>");
420                }
421        };
422        /**
423         * 返回java类对应的cpp类名
424         * primitive类型void类型原样输出
425         * byte[]转为std::shared_ptr<std::vector<char>><br>
426         * String转为std::string<br>
427         * 数组类型用std::vector封装<br>
428         * 普通Object类型使用std::shared_ptr封装<br>
429         * @param clazz
430         * @param full 是否要包含namespace的cpp类名
431         * @return
432         */
433        public static String toCppType(Class<?> clazz,boolean full){
434                Assert.notNull(clazz, "clazz");
435                if(BASE_TYPE_MAP.containsKey(clazz))return BASE_TYPE_MAP.get(clazz);
436                else if(clazz.isArray())return String.format("std::vector<%s>", toCppType(clazz.getComponentType(),full));// 递归
437                else return String.format("std::shared_ptr<%s>", full?toCppFullName(clazz):clazz.getSimpleName());                
438        }
439        /**
440         * @param clazz
441         * @return
442         * @see #toCppType(Class, boolean)
443         */
444        public static String toCppType(Class<?> clazz){
445                return toCppType(clazz,false);
446        }
447        /**
448         * 返回java类型对应的cpp参数类型
449         * @param clazz
450         * @param full
451         * @return
452         * @see #toCppType(Class, boolean)
453         */
454        public static String toCppParameterType(Class<?> clazz,boolean full){
455                String typeName = toCppType(clazz,full);
456                if(typeName.startsWith("std::")){
457                        typeName="const "+typeName+"&";
458                }
459                return typeName;
460        }
461        /**
462         * @param clazz
463         * @return
464         * @see #toCppParameterType(Class, boolean)
465         */
466        public static String toCppParameterType(Class<?> clazz){
467                return toCppParameterType(clazz,false);
468        }
469        /**
470         * 返回基础缩进量
471         * @param packageName
472         * @return
473         */
474        public static String getBaseIndentTabs(String packageName){
475                if(Judge.isEmpty(packageName))return "";
476                String[] names = packageName.split("\\.");
477                byte[] ba = new byte[names.length];
478                for(int i=0;i<ba.length;++i)ba[i]='\t';
479                return new String(ba,0,ba.length-1);
480        }
481        public static String getBaseIndentTabs(Class<?> clazz){
482                Assert.notNull(clazz, "clazz");
483                return getBaseIndentTabs(clazz.getPackage().getName());
484        }
485        private static ThreadLocal<String[]> _packageName=new ThreadLocal<String[]>() ;
486        public static String namespaceBegin(String packageName){                
487                if(Judge.isEmpty(packageName))return "";
488                String[] names = packageName.split("\\.");
489                _packageName.set(names);
490                StringBuilder builder=new StringBuilder();
491                byte[] ba = new byte[names.length];
492                for(int i=0;i<ba.length;++i)ba[i]='\t';
493                for(int i=0;i<names.length;++i){
494                        String tabs = new String(ba,0,i);
495                        builder.append(String.format("%snamespace %s\n%s{\n",tabs,names[i],tabs));
496                }
497                return builder.toString();              
498        }
499        public static String namespaceBegin(Class<?> clazz){
500                Assert.notNull(clazz, "clazz");
501                return namespaceBegin(clazz.getPackage().getName());
502        }
503        
504        public static String namespaceEnd(){            
505                String[] names = _packageName.get();            
506                if(Judge.isEmpty(names))return "";
507                StringBuilder builder=new StringBuilder();
508                byte[] ba = new byte[names.length];
509                for(int i=0;i<ba.length;++i)ba[i]='\t';
510                for(int i=names.length;i>0;--i){
511                        String tabs = new String(ba,0,i-1);
512                        builder.append(String.format("%s} /* namespace %s */\n",tabs,names[i-1]));
513                }
514                _packageName.set(null);
515                return builder.toString();              
516        }       
517        
518        /**
519         * @return stubClassProppertiesMap
520         */
521        public Map<String, Map<String, String>> getStubClassProppertiesMap() {
522                return stubClassProppertiesMap;
523        }
524        public boolean isReferenceClass(Class<?>type){
525                Assert.notNull(type, "type");
526                return this.referenceClassMap.containsKey(type);
527        }
528        /**
529         * @return stubPrefix
530         */
531        public String getStubPrefix() {
532                return stubPrefix;
533        }
534        /**
535         * @return stubFolder
536         */
537        public File getStubFolder() {
538                return stubFolder;
539        }
540        public final String toStubExpectionClassName(Class<? extends Throwable> expection) {
541                Assert.notNull(expection, "expection");
542                return String.format("%s__%s%s", getNamespacePrefix(this.serviceClass),this.serviceClass.getSimpleName(),expection.getSimpleName());
543        }
544}