001package net.gdface.codegen.webclient;
002
003import java.lang.reflect.Modifier;
004import java.util.ArrayList;
005import java.util.Arrays;
006import java.util.HashMap;
007import java.util.Iterator;
008import java.util.Map;
009
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013import net.gdface.annotation.AnnotationException;
014import net.gdface.annotation.AnnotationRuntimeException;
015import net.gdface.annotation.Remote;
016import net.gdface.annotation.Service;
017import net.gdface.annotation.ServicePort;
018import net.gdface.codegen.AnnotationUtils;
019import net.gdface.codegen.NewSourceInfoAbstract;
020import net.gdface.codegen.ServiceInfo;
021import net.gdface.codegen.Method;
022import net.gdface.codegen.MethodException;
023import net.gdface.exception.ServiceRuntime;
024import net.gdface.utils.Assert;
025import net.gdface.utils.ParameterNames;
026
027public class WebClient<T> extends NewSourceInfoAbstract<T> {
028        private static final Logger logger = LoggerFactory.getLogger(WebClient.class);
029        protected ServiceInfo serviceInfo;
030        protected final Class<?> serviceClass;
031        public final ParameterNames serviceParamTable;
032
033        protected Map<Method, Method> method2PortMap = new HashMap<Method, Method>();
034        private final Map<String, java.lang.reflect.Method> servicePorts;
035        @SuppressWarnings("unchecked")
036        public WebClient(Class<T> interfaceClass, Class<? extends T> refClass, Class<?> serviceClass) {
037                super(interfaceClass, refClass, (Class<? extends T>) refClass.getSuperclass());
038                Assert.notNull(serviceClass, "serviceClass");
039                this.serviceClass = serviceClass;
040                try {
041                        servicePorts=getServicePorts(this.serviceClass);
042                } catch (DuplicatedNameMethodException e) {
043                        throw new ExceptionInInitializerError(e);
044                }
045                serviceParamTable = new ParameterNames(serviceClass);
046                getImportedList().put("ServiceRuntime", net.gdface.exception.ServiceRuntime.class);
047        }
048
049        @Override
050        protected void createMethodsNeedGenerated() {
051                ArrayList<java.lang.reflect.Method> interfaceMethods = new ArrayList<java.lang.reflect.Method>(
052                                Arrays.asList(interfaceClass.getMethods()));
053                java.lang.reflect.Method[] baseMethods = null != baseClass ? baseClass.getMethods()
054                                : new java.lang.reflect.Method[0];
055                try {
056                        for (Iterator<java.lang.reflect.Method> it = interfaceMethods.iterator(); it.hasNext();) {
057                                java.lang.reflect.Method im = it.next();
058                                if(needImpl(im)||!isImplemented(baseMethods, im))
059                                //if(!isImplemented(baseMethods, im))
060                                        this.methodsNeedGenerated.add(new Method(refClass.getMethod(im.getName(), im.getParameterTypes()),
061                                                        this.paramTable.getParameterNames(im.getName(), im.getParameterTypes())));
062                        }
063                } catch (NoSuchMethodException e) {
064                        throw new RuntimeException(e);
065                }
066        }
067
068        @Override
069        public String toString() {
070                StringBuilder builder = new StringBuilder();
071                int c = 0;
072                builder.append("//classes need imported in new Class:\n");
073                for (Class<?> clazz : importedList.values()) {
074                        builder.append("//[" + (++c) + "]\nimported ").append(clazz.getName()).append(";\n");
075                }
076                builder.append("public class NewClass");
077                if (null != this.baseClass)
078                        builder.append("extends " + this.baseClass.getSimpleName());
079                else
080                        builder.append(" implements " + this.interfaceClass.getSimpleName());
081                builder.append("{\n");
082                c = 0;
083                builder.append("//methods thad need generated in new Class:\n");
084                for (Method m : methodsNeedGenerated) {
085                        builder.append("//[" + (++c) + "]\n").append(m.toGenericString()).append("{}\n");
086                }
087                builder.append("}\n");
088                return builder.toString();
089        }
090
091        @Override
092        public boolean compile() {
093                boolean compileOk = false;
094                try {
095                        if (super.compile()) {
096                                Service service = AnnotationUtils.getServiceAnnotation(interfaceClass);
097//                              if (null != service)
098//                                      addImportedClass(service.genericTypeClasses());
099                                addImportedClass(interfaceClass);
100                                serviceInfo = new ServiceInfo(service);
101//                              method2PortMap = createMap();
102                                compileOk = true;
103                        }
104                } catch (AnnotationException e) {
105                        logger.error(e.toString());
106                } catch (AnnotationRuntimeException e) {
107                        logger.error(e.toString());
108                } /*catch (DuplicatedNameMethodException e) {
109                        logger.error(e.toString());
110                } catch (NoSuchMethodException e) {
111                        logger.error(e.toString());
112                }*/
113                return compileOk;
114        }
115
116        protected Map<Method, Method> createMethod2PortMapMap() throws NoSuchMethodException {
117                ServicePort portDesc;
118                String portName;
119                java.lang.reflect.Method portMethod;
120                Map<Method, Method> method2PortMap = new HashMap<Method, Method>();
121                String portPrefix = serviceInfo.getPortPrefix();
122                Method m;
123                StringBuilder builder;
124                for (Method method : methodsNeedGenerated) {
125                        builder=new StringBuilder(portPrefix);
126                        try {
127                                if (needImpl(method)) {
128                                        Remote remoteAnnotation = getRemoteAnnotation(method);
129                                        m = getPrimitiveMethod(refClass, method);
130                                        portDesc = m.getAnnotation(ServicePort.class);
131                                        if (portDesc != null){
132                                                String suffix = method.getName().substring(remoteAnnotation.primtiveName().length());
133                                                builder.append(remoteAnnotation.primtiveName()).append(portDesc.suffix()).append(suffix);
134                                        }
135                                        else 
136                                                builder.append(method.getName());
137                                }else{
138                                        portDesc = method.getAnnotation(ServicePort.class);
139                                        builder.append(method.getName());
140                                        if (portDesc != null)
141                                                builder.append(portDesc.suffix());
142                                }
143                        } catch (MethodException e) {
144                                throw new RuntimeException(e);
145                        }                       
146                        portName = builder.toString();
147                        if ((portMethod = servicePorts.get(portName)) == null)
148                                throw new NoSuchMethodException(String.format("not found webservice port %s for method %s ", portName,
149                                                method.getDocSignature()));
150                        method2PortMap.put(method, new Method(portMethod, getPortParameterNames(portMethod)));
151                }
152                return method2PortMap;
153        }
154        
155        /**
156         * 
157         * @param method 接口中的方法
158         * @return Servcie Class中对应的方法
159         */
160        protected java.lang.reflect.Method getServicePort(Method method) {
161                ServicePort portDesc;
162                String portName;
163                String portPrefix = serviceInfo.getPortPrefix();
164                StringBuilder builder;
165                builder=new StringBuilder(portPrefix);
166                try {
167                        if (needImpl(method)) {
168                                Remote remoteAnnotation = getRemoteAnnotation(method);
169                                Method m = getPrimitiveMethod(refClass, method);
170                                portDesc = m.getAnnotation(ServicePort.class);
171                                if (portDesc != null){
172                                        String suffix = method.getName().substring(remoteAnnotation.primtiveName().length());
173                                        builder.append(remoteAnnotation.primtiveName()).append(portDesc.suffix()).append(suffix);
174                                }
175                                else 
176                                        builder.append(method.getName());
177                        }else{
178                                portDesc = method.getAnnotation(ServicePort.class);
179                                builder.append(method.getName());
180                                if (portDesc != null)
181                                        builder.append(portDesc.suffix());
182                        }
183                } catch (MethodException e) {
184                        throw new RuntimeException(e);
185                }                       
186                portName = builder.toString();
187                return servicePorts.get(portName);
188        }
189        
190        private final String[] getPortParameterNames(java.lang.reflect.Method portMethod) throws NoSuchMethodException {
191                return serviceParamTable.getParameterNames(portMethod.getName(), portMethod.getParameterTypes());
192        }
193        protected final Map<String, java.lang.reflect.Method> getServicePorts(Class<?> serviceClass)
194                        throws DuplicatedNameMethodException {
195                java.lang.reflect.Method[] methods = serviceClass.getDeclaredMethods();
196                Map<String, java.lang.reflect.Method> servicePorts = new HashMap<String, java.lang.reflect.Method>(
197                                methods.length << 1);
198                int modifier ;
199                for (java.lang.reflect.Method method : methods) {
200                        modifier=method.getModifiers();
201                        if(Modifier.isPublic(modifier)){
202                                if (servicePorts.keySet().contains(method.getName()))
203                                        throw new DuplicatedNameMethodException(serviceClass, method.getName());
204                                servicePorts.put(method.getName(), method);
205                        }
206                }
207                return servicePorts;
208        }
209
210        /**
211         * @return serviceInfo
212         */
213        public ServiceInfo getServiceInfo() {
214                return serviceInfo;
215        }
216
217        /**
218         * @return method2PortMap
219         */
220        public Map<Method, Method> getMethod2PortMap() {
221                return method2PortMap;
222        }
223
224        /**
225         * @return serviceClass
226         */
227        public Class<?> getServiceClass() {
228                return serviceClass;
229        }
230        public boolean isServiceRuntime(Class<?> exp){
231                return ServiceRuntime.class.isAssignableFrom(exp);
232        }
233        public boolean needImpl(Method method){
234                return getRemoteAnnotation(method)!=null;               
235        }
236        public boolean needImpl(java.lang.reflect.Method method){
237                return getRemoteAnnotation(method)!=null;               
238        }
239        public Remote getRemoteAnnotation(Method method){
240                return method.getAnnotation(Remote.class);              
241        }
242        public Remote getRemoteAnnotation(java.lang.reflect.Method method){
243                return method.getAnnotation(Remote.class);              
244        }
245
246        public boolean isGenericTypeClass(Method method, Class<?> parameterType) {
247                Remote remoteAnnotation = getRemoteAnnotation(method);
248                return serviceInfo.getBridgeType() == String.class 
249                                && remoteAnnotation != null 
250                                && remoteAnnotation.value()
251                                && remoteAnnotation.genericTypeClass() == parameterType;
252        }
253
254        public Method getPrimitiveMethod(Class<?> from,Method deriveMethod) throws MethodException {
255                Remote remoteAnnotation = getRemoteAnnotation(deriveMethod);
256                return getMatchedGenericMethod(from, deriveMethod, serviceInfo.getTargetType(), remoteAnnotation.genericParam(),
257                                remoteAnnotation.genericTypeClass(),remoteAnnotation.primtiveName());
258        }
259}