001package net.gdface.codegen.generic;
002
003import java.lang.reflect.Modifier;
004import java.lang.reflect.TypeVariable;
005import java.util.ArrayList;
006import java.util.Arrays;
007import java.util.HashSet;
008import java.util.Iterator;
009import java.util.Set;
010import java.util.regex.Pattern;
011
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014
015import net.gdface.annotation.AnnotationException;
016import net.gdface.annotation.AnnotationRuntimeException;
017import net.gdface.annotation.DefaultGenericTypes;
018import net.gdface.annotation.DeriveMethod;
019import net.gdface.annotation.Generic;
020import net.gdface.annotation.GenericNameException;
021import net.gdface.annotation.GenericParam;
022import net.gdface.annotation.GenericParamException;
023import net.gdface.codegen.AnnotationUtils;
024import net.gdface.codegen.CodeGenUtils;
025import net.gdface.codegen.InvalidAnnotationDefineException;
026import net.gdface.codegen.InvalidNameException;
027import net.gdface.codegen.Method;
028import net.gdface.codegen.Method.Parameter;
029import net.gdface.codegen.NewSourceInfoAbstract;
030import net.gdface.codegen.ServiceInfo;
031import net.gdface.utils.Assert;
032
033public class GenericInterface<T> extends NewSourceInfoAbstract<T> implements GenericInterfaceConstants {
034        private static final Logger logger = LoggerFactory.getLogger(NewSourceInfoAbstract.class);
035
036        private DefaultParameterGenericTypes defaultGenericTypes;
037
038        private Class<?> shellInterface;
039
040        private ServiceInfo serviceInfo;
041
042        private boolean hasRemoteResolveType;
043
044        public GenericInterface(Class<T> interfaceClass, Class<? extends T> refClass, Class<?> shellInterface) {
045                super(interfaceClass, refClass, null);
046                Assert.notNull(shellInterface, "shellInterface");
047                this.shellInterface=shellInterface;
048                
049                if(!shellInterface.isInterface())
050                        throw new IllegalArgumentException(String.format("shellInterface must be a interface(必须是接口)"));
051                if (!interfaceClass.isAssignableFrom(shellInterface))
052                        throw new IllegalArgumentException(String.format(
053                                        "shellInterface must  extends from  [%s] (必须实现接口)", interfaceClass.getName()));
054
055        }
056
057        @Override
058        public boolean compile() {
059                boolean compileOk=false;
060                try {
061                        defaultGenericTypes = new DefaultParameterGenericTypes(interfaceClass);
062                        serviceInfo=new ServiceInfo(AnnotationUtils.getServiceAnnotation(shellInterface));
063                        super.compile();
064                        importedList.remove(refClass.getSimpleName());
065                        importedList.remove(interfaceClass.getSimpleName());
066                        // 检查是否有同名的泛型名称
067                        for (String name : defaultGenericTypes.names()) {
068                                if (hasInClassGenericTypes(name))
069                                        try {
070                                                throw new GenericNameConflictException(name, DefaultGenericTypes.class.getSimpleName(),
071                                                                defaultGenericTypes.toString());
072                                        } catch (GenericNameConflictException e) {
073                                                throw new DefaultGenericTypesExceptoin(e);
074                                        }
075                        }
076                        for (Method method : methodsNeedGenerated) {
077                                compile(method);
078                        }
079                        compileOk=true;
080                } catch (AnnotationException e) {
081                        logger.error(e.toString());
082                }catch(AnnotationRuntimeException e){
083                        logger.error(e.toString());
084                }
085                return compileOk;
086        }
087
088        private final void compile(Method method) throws GenericParamException, GenericNameException, GenericNameConflictException {
089                for (Parameter param : method.getParameters()) {
090                        try {
091                                compile(param);
092                        } catch (GenericParamRedefineException e) {
093                                logger.warn("{} is generic type({}) defined by class,can't be redefined in method:\n{}", param.name,
094                                                param.genericType.toString(), method.toGenericString());
095                        } catch (GenericNameException e) {
096                                throw new GenericNameException(String.format(
097                                                "the name of annotation %s of parameter [%s] must not have space char in method %s",
098                                                Generic.class.getSimpleName(), param.name, method.toGenericString()));
099                        }
100                }
101                DeriveMethod deriveMethodAnnotation = getDeriveMethodAnnotation(method);
102                if(deriveMethodAnnotation!=null){
103                        this.addImportedClass(deriveMethodAnnotation.localResolvedTypes());                     
104                        if(deriveMethodAnnotation.remoteResolveTypes().length>0){
105                                hasRemoteResolveType=true;
106                                this.addImportedClass(deriveMethodAnnotation.remoteResolveTypes());
107                        }
108                }
109        }
110
111        private final void compile(Parameter param) throws GenericParamRedefineException, GenericNameConflictException,
112                        GenericNameException {
113                GenericParam genericParam = param.getAnnotation(GenericParam.class);
114                if (null != genericParam && genericParam.value() && (param.genericType instanceof TypeVariable)) {
115                        throw new GenericParamRedefineException();
116                }
117                if (null != genericParam && !genericParam.name().isEmpty()) {
118                        if (!Pattern.compile("^[A-Z]\\w*$").matcher(genericParam.name()).matches())
119                                throw new GenericNameException();
120                        if (hasInClassGenericTypes(genericParam.name()))
121                                throw new GenericNameConflictException(genericParam.name(), GenericParam.class.getSimpleName(),
122                                                genericParam.toString());
123                }
124        }
125
126        @Override
127        protected void createMethodsNeedGenerated() {
128                ArrayList<java.lang.reflect.Method> interfaceMethods = new ArrayList<java.lang.reflect.Method>(
129                                Arrays.asList(interfaceClass.getMethods()));
130                Iterator<java.lang.reflect.Method> it = interfaceMethods.iterator();
131                try {
132                        Method cm;
133                        while (it.hasNext()) {
134                                java.lang.reflect.Method im = it.next();
135//                              cm = new Method(refClass.getMethod(im.getName(), im.getParameterTypes()),
136//                                              this.paramTable.getParameterNames(im.getName(), im.getParameterTypes()));                               
137                                cm = new Method(im,
138                                                this.paramTable.getParameterNames(im.getName(), im.getParameterTypes()));                               
139
140                                if (needGeneric(cm))
141                                        this.methodsNeedGenerated.add(cm);
142                        }
143                } catch (NoSuchMethodException e) {
144                        throw new RuntimeException(e);
145                }
146        }
147
148        /**
149         * @return defaultGenericTypes
150         */
151        public DefaultParameterGenericTypes getDefaultGenericTypes() {
152                return defaultGenericTypes;
153        }
154
155        private boolean hasInClassGenericTypes(String name) {
156                for (TypeVariable<Class<T>> type : interfaceClass.getTypeParameters()) {
157                        if (type.getName().equals(name))
158                                return true;
159                }
160                return false;
161        }
162
163        private final boolean hasNeedGenericParameter(Method method) {
164                for (Parameter param : method.getParameters()) {
165                        if (needGeneric(param)) {
166                                return true;
167                        }
168                }
169                return false;
170        }
171
172        public boolean needGeneric(Method method) {
173                Generic generic = method.getAnnotation(Generic.class);
174                return (null == generic || generic.value()) && hasNeedGenericParameter(method);
175        }
176
177        public final boolean needGeneric(Parameter param){
178                GenericParam genericParam = param.getAnnotation(GenericParam.class);
179                return (null == genericParam || genericParam.value()) && defaultGenericTypes.hasType(param.type);
180        }
181        public final GenericParam getGenericParam(Parameter param){
182                return  param.getAnnotation(GenericParam.class);
183        }
184        
185        public final Set<String> getGenericNames(Method method) {
186                GenericParam genericParam;
187                Set<String> names=new HashSet<String>();
188                for (Parameter param : method.getParameters()) {
189                        if (needGeneric(param)) {
190                                if(null==(genericParam = getGenericParam(param))){                                      
191                                        names.add(defaultGenericTypes.getName(param.getType()));
192                                }else
193                                        names.add(genericParam.name());
194                        }
195                }
196                return names;
197        }
198        @Override
199        public String toString() {
200                StringBuilder builder = new StringBuilder();
201                int c = 0;
202                builder.append("//classes need imported in new Class:\n");
203                for (Class<?> clazz : importedList.values()) {
204                        builder.append("//[" + (++c) + "]\nimported ").append(clazz.getName()).append(";\n");
205                }
206                builder.append("public interface NewClass{\n");
207                c = 0;
208                builder.append("//methods thad need generated in new Class:\n");
209                for (Method m : methodsNeedGenerated) {
210                        builder.append("//[" + (++c) + "]\n").append(m.toGenericString()).append(";\n");
211                }
212                builder.append("}\n");
213                return builder.toString();
214        }
215
216        /**
217         * @return shellInterface
218         */
219        public Class<?> getShellInterface() {
220                return shellInterface;
221        }
222        public DeriveMethod getDeriveMethodAnnotation(Method method) {
223                try {
224                        return AnnotationUtils.getDeriveMethodAnnotation(method, serviceInfo);
225                } catch (InvalidNameException e) {
226                        throw new RuntimeException(e);
227                } catch (InvalidAnnotationDefineException e) {
228                        throw new RuntimeException(e);
229                }
230        }
231
232        /**
233         * @return serviceInfo
234         */
235        public ServiceInfo getServiceInfo() {
236                return serviceInfo;
237        }
238
239        public boolean isTargetType(Class<?> type){
240                return serviceInfo.getTargetType()==type;
241        }
242        public boolean needDeriveParam(Method method,Parameter param){
243                DeriveMethod deriveMethod =getDeriveMethodAnnotation(method);
244                if(deriveMethod!=null){
245                        Set<String> set = CodeGenUtils.toSet(deriveMethod.genericParam());
246                        return needGeneric(param)&&(set.isEmpty()||set.contains(param.name));
247                }
248                return false;
249        }
250        public ArrayList<String> getDeriveParameterNames(Method method){
251                ArrayList<String> list = new ArrayList<String>();
252                for(Parameter parameter:method.getParameters()){
253                        if(needDeriveParam(method,parameter)){
254                                list.add(parameter.name);
255                        }
256                }
257                return list;
258        }
259        /**
260         * @return hasRemoteResolveType
261         */
262        public boolean isHasRemoteResolveType() {
263                return hasRemoteResolveType;
264        }
265
266}