001/*
002  GRANITE DATA SERVICES
003  Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005  This file is part of Granite Data Services.
006
007  Granite Data Services is free software; you can redistribute it and/or modify
008  it under the terms of the GNU Library General Public License as published by
009  the Free Software Foundation; either version 2 of the License, or (at your
010  option) any later version.
011
012  Granite Data Services is distributed in the hope that it will be useful, but
013  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015  for more details.
016
017  You should have received a copy of the GNU Library General Public License
018  along with this library; if not, see <http://www.gnu.org/licenses/>.
019*/
020
021package org.granite.generator.as3.reflect;
022
023import java.lang.annotation.Annotation;
024import java.lang.reflect.Method;
025import java.lang.reflect.ParameterizedType;
026import java.lang.reflect.Type;
027import java.util.Map;
028
029import org.granite.generator.as3.ClientType;
030import org.granite.generator.as3.PropertyType;
031import org.granite.generator.util.GenericTypeUtil;
032import org.granite.messaging.service.annotations.Param;
033import org.granite.tide.data.Lazy;
034import org.granite.util.ClassUtil;
035
036/**
037 * @author Franck WOLFF
038 */
039public class JavaMethod extends JavaMember<Method> {
040
041    public enum MethodType {
042        GETTER,
043        SETTER,
044        OTHER
045    }
046
047    private final String name;
048    private final boolean override;
049    private final MethodType type;
050    private final String options;
051    private final Class<?> returnType;
052    private final Class<?>[] parameterTypes;
053    private final ClientType clientReturnType;
054    private final ClientType[] clientParameterTypes;
055    private final String[] clientParameterNames;
056    private final String[] clientParameterOptions;
057    
058    public JavaMethod(Method method, MethodType type) {
059        this(method, type, null, null);
060    }
061    
062    public JavaMethod(Method method, MethodType type, JavaTypeFactory provider) {
063        this(method, type, provider, null);
064    }
065    
066    public JavaMethod(Method method, MethodType type, JavaTypeFactory provider, ParameterizedType[] declaringTypes) {
067        super(method);
068
069        Class<?> objectClass = Object.class;
070        try {
071                objectClass = method.getDeclaringClass().getClassLoader().loadClass(Object.class.getCanonicalName());
072        }
073        catch (Exception e) {
074        }
075        
076        this.name = method.getName();
077
078        // This part figure out if an ActionScript3 accessor should be marked as override.
079        Class<?> superclass = method.getDeclaringClass().getSuperclass();
080        boolean override = false;
081        if (superclass != null && superclass != objectClass) {
082            try {
083                Method superMethod = superclass.getMethod(method.getName(), method.getParameterTypes());
084
085                // if the super method is declared by an interface, check if we have a superclass that
086                // implements this interface.
087                if (superMethod.getDeclaringClass().isInterface())
088                        override = superMethod.getDeclaringClass().isAssignableFrom(superclass);
089                // if the super method is declared by a class, check if its declaring class implements,
090                // directly or not, an interface with a method with the same signature.
091                else {
092                        for (Class<?> sc = superMethod.getDeclaringClass(); sc != null; sc = sc.getSuperclass()) {
093                                for (Class<?> interfaze : sc.getInterfaces()) {
094                                                try {
095                                                        interfaze.getMethod(method.getName(), method.getParameterTypes());
096                                                        override = true;
097                                                        break;
098                                                }
099                                                catch (NoSuchMethodException e) {
100                                                        // continue...
101                                                }
102                                        }
103                                if (override)
104                                        break;
105                        }
106                }
107            } catch (NoSuchMethodException e) {
108                // continue...
109            }
110        }
111        this.override = override;
112
113        this.type = type;
114        
115        if (method.isAnnotationPresent(Lazy.class))
116                this.options = "Lazy";
117        else
118                this.options = null;
119        
120                if (type == MethodType.OTHER && provider != null) {
121                        if (method.getReturnType() == void.class) {
122                                this.returnType = Void.class;
123                                this.clientReturnType = provider.getClientType(Void.class, null, null, PropertyType.SIMPLE);
124                        }
125                        else {
126                                Type genericType = GenericTypeUtil.resolveTypeVariable(method.getGenericReturnType(), method.getDeclaringClass(), declaringTypes);
127                                genericType = GenericTypeUtil.primitiveToWrapperType(genericType);
128                        this.returnType = ClassUtil.classOfType(genericType);
129                        
130                                ClientType returnType = provider.getClientType(genericType, method.getDeclaringClass(), declaringTypes, PropertyType.SIMPLE);
131                                if (returnType == null)
132                                        returnType = provider.getAs3Type(this.returnType);
133                                clientReturnType = returnType;
134                        }
135
136                        this.parameterTypes = method.getParameterTypes();                       
137                        this.clientParameterTypes = new ClientType[this.parameterTypes.length];
138                        this.clientParameterNames = new String[this.parameterTypes.length];
139                        this.clientParameterOptions = new String[this.parameterTypes.length];
140                        for (int i = 0; i < this.parameterTypes.length; i++) {
141                                clientParameterNames[i] = getParamName(method, i);
142                                if (Map.class.isAssignableFrom(parameterTypes[i]))
143                                        clientParameterTypes[i] = provider.getClientType(Object.class, null, null, PropertyType.SIMPLE);
144                                else {
145                                        Type genericType = GenericTypeUtil.resolveTypeVariable(method.getGenericParameterTypes()[i], method.getDeclaringClass(), declaringTypes);
146                                parameterTypes[i] = ClassUtil.classOfType(genericType);
147                                
148                                        ClientType paramType = provider.getClientType(genericType, method.getDeclaringClass(), declaringTypes, PropertyType.SIMPLE);
149                                        if (paramType == null)
150                                                paramType = provider.getAs3Type(parameterTypes[i]);
151                                        clientParameterTypes[i] = paramType;
152                                }
153                                
154                                Annotation[] annotations = method.getParameterAnnotations()[i];
155                                for (Annotation annotation : annotations) {
156                                        if (annotation.annotationType().equals(Lazy.class)) {
157                                                clientParameterOptions[i] = "Lazy";
158                                                break;
159                                        }
160                                }                               
161                        }
162                } 
163                else {
164                        this.returnType = null;
165                        this.parameterTypes = null;
166                        this.clientReturnType = null;
167                        this.clientParameterTypes = null;
168                        this.clientParameterNames = null;
169                        this.clientParameterOptions = null;
170                }
171    }
172    
173    private String getParamName(Method method, int paramIndex) {
174        Annotation[][] annotations = method.getParameterAnnotations();
175        if (annotations != null && annotations.length > paramIndex && annotations[paramIndex] != null) {
176                for (Annotation annotation : annotations[paramIndex]) {
177                        if (annotation.annotationType().equals(Param.class))
178                                return ((Param)annotation).value();
179                }
180        }
181        return "arg" + paramIndex;
182    }
183
184    public boolean isOverride() {
185        return override;
186    }
187
188    public MethodType getType() {
189        return type;
190    }
191    
192    public String getOptions() {
193        return options;
194    }
195
196    public String getTypeName() {
197        return type.name();
198    }
199    
200    @Override
201        public String getName() {
202        return name;
203    }
204    
205    public Class<?> getReturnType() {
206        return returnType;
207    }
208   
209    public Class<?>[] getParameterTypes() {
210        return parameterTypes;
211    }
212
213        public ClientType[] getAs3ParameterTypes() {
214                return clientParameterTypes;
215        }
216
217        public String[] getAs3ParameterNames() {
218                return clientParameterNames;
219        }
220
221        public String[] getAs3ParameterOptions() {
222                return clientParameterOptions;
223        }
224        
225        public ClientType getClientReturnType() {
226                return clientReturnType;
227        }
228
229        public ClientType[] getClientParameterTypes() {
230                return clientParameterTypes;
231        }
232
233        public String[] getClientParameterNames() {
234                return clientParameterNames;
235        }
236
237        public String[] getClientParameterOptions() {
238                return clientParameterOptions;
239        }
240}