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