001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2014 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 */
022package org.granite.generator.as3.reflect;
023
024import java.lang.annotation.Annotation;
025import java.lang.reflect.Method;
026import java.lang.reflect.ParameterizedType;
027import java.lang.reflect.Type;
028import java.util.HashSet;
029import java.util.Map;
030import java.util.Set;
031
032import org.granite.generator.as3.ClientType;
033import org.granite.generator.as3.PropertyType;
034import org.granite.generator.util.GenericTypeUtil;
035import org.granite.messaging.service.annotations.Param;
036import org.granite.tide.data.Lazy;
037import org.granite.util.ClassUtil;
038
039/**
040 * @author Franck WOLFF
041 */
042public class JavaMethod extends JavaMember<Method> {
043
044    public enum MethodType {
045        GETTER,
046        SETTER,
047        OTHER
048    }
049
050    private final String name;
051    private final boolean override;
052    private final MethodType type;
053    private final String options;
054    private final Class<?> returnType;
055    private final Class<?>[] parameterTypes;
056    private final ClientType clientReturnType;
057    private final ClientType[] clientParameterTypes;
058    private final String[] clientParameterNames;
059    private final String[] clientParameterOptions;
060    private final Set<ClientType> clientAnnotationTypes = new HashSet<ClientType>();
061    
062    public JavaMethod(Method method, MethodType type) {
063        this(method, type, null, null);
064    }
065    
066    public JavaMethod(Method method, MethodType type, JavaTypeFactory provider) {
067        this(method, type, provider, null);
068    }
069    
070    public JavaMethod(Method method, MethodType type, JavaTypeFactory provider, ParameterizedType[] declaringTypes) {
071        super(method);
072
073        Class<?> objectClass = Object.class;
074        try {
075                objectClass = method.getDeclaringClass().getClassLoader().loadClass(Object.class.getCanonicalName());
076        }
077        catch (Exception e) {
078        }
079        
080        this.name = method.getName();
081
082        // This part figure out if an ActionScript3 accessor should be marked as override.
083        Class<?> superclass = method.getDeclaringClass().getSuperclass();
084        boolean override = false;
085        if (superclass != null && superclass != objectClass) {
086            try {
087                Method superMethod = superclass.getMethod(method.getName(), method.getParameterTypes());
088
089                // if the super method is declared by an interface, check if we have a superclass that
090                // implements this interface.
091                if (superMethod.getDeclaringClass().isInterface())
092                        override = superMethod.getDeclaringClass().isAssignableFrom(superclass);
093                // if the super method is declared by a class, check if its declaring class implements,
094                // directly or not, an interface with a method with the same signature.
095                else {
096                        for (Class<?> sc = superMethod.getDeclaringClass(); sc != null; sc = sc.getSuperclass()) {
097                                for (Class<?> interfaze : sc.getInterfaces()) {
098                                                try {
099                                                        interfaze.getMethod(method.getName(), method.getParameterTypes());
100                                                        override = true;
101                                                        break;
102                                                }
103                                                catch (NoSuchMethodException e) {
104                                                        // continue...
105                                                }
106                                        }
107                                if (override)
108                                        break;
109                        }
110                }
111            } catch (NoSuchMethodException e) {
112                // continue...
113            }
114        }
115        this.override = override;
116
117        this.type = type;
118        
119        if (method.isAnnotationPresent(Lazy.class)) {
120                this.options = "Lazy";
121                this.clientAnnotationTypes.add(provider.getClientType(Lazy.class, null, null, null));
122        }
123        else
124                this.options = null;
125        
126                if (type == MethodType.OTHER && provider != null) {
127                        if (method.getReturnType() == void.class) {
128                                this.returnType = Void.class;
129                                this.clientReturnType = provider.getClientType(Void.class, null, null, PropertyType.SIMPLE);
130                        }
131                        else {
132                                Type genericType = GenericTypeUtil.resolveTypeVariable(method.getGenericReturnType(), method.getDeclaringClass(), declaringTypes);
133                                genericType = GenericTypeUtil.primitiveToWrapperType(genericType);
134                        this.returnType = ClassUtil.classOfType(genericType);
135                        
136                                ClientType returnType = provider.getClientType(genericType, method.getDeclaringClass(), declaringTypes, PropertyType.SIMPLE);
137                                if (returnType == null)
138                                        returnType = provider.getAs3Type(this.returnType);
139                                clientReturnType = returnType;
140                        }
141
142                        this.parameterTypes = method.getParameterTypes();                       
143                        this.clientParameterTypes = new ClientType[this.parameterTypes.length];
144                        this.clientParameterNames = new String[this.parameterTypes.length];
145                        this.clientParameterOptions = new String[this.parameterTypes.length];
146                        for (int i = 0; i < this.parameterTypes.length; i++) {
147                                clientParameterNames[i] = getParamName(method, i);
148                                if (Map.class.isAssignableFrom(parameterTypes[i]))
149                                        clientParameterTypes[i] = provider.getClientType(Object.class, null, null, PropertyType.SIMPLE);
150                                else {
151                                        Type genericType = GenericTypeUtil.resolveTypeVariable(method.getGenericParameterTypes()[i], method.getDeclaringClass(), declaringTypes);
152                                parameterTypes[i] = ClassUtil.classOfType(genericType);
153                                
154                                        ClientType paramType = provider.getClientType(genericType, method.getDeclaringClass(), declaringTypes, PropertyType.SIMPLE);
155                                        if (paramType == null)
156                                                paramType = provider.getAs3Type(parameterTypes[i]);
157                                        clientParameterTypes[i] = paramType;
158                                }
159                                
160                                Annotation[] annotations = method.getParameterAnnotations()[i];
161                                for (Annotation annotation : annotations) {
162                                        if (annotation.annotationType().equals(Lazy.class)) {
163                                                clientParameterOptions[i] = "Lazy";
164                                        this.clientAnnotationTypes.add(provider.getClientType(Lazy.class, null, null, null));
165                                                break;
166                                        }
167                                }                               
168                        }
169                } 
170                else {
171                        this.returnType = null;
172                        this.parameterTypes = null;
173                        this.clientReturnType = null;
174                        this.clientParameterTypes = null;
175                        this.clientParameterNames = null;
176                        this.clientParameterOptions = null;
177                }
178    }
179    
180    private String getParamName(Method method, int paramIndex) {
181        Annotation[][] annotations = method.getParameterAnnotations();
182        if (annotations != null && annotations.length > paramIndex && annotations[paramIndex] != null) {
183                for (Annotation annotation : annotations[paramIndex]) {
184                        if (annotation.annotationType().equals(Param.class))
185                                return ((Param)annotation).value();
186                }
187        }
188        return "arg" + paramIndex;
189    }
190
191    public boolean isOverride() {
192        return override;
193    }
194
195    public MethodType getType() {
196        return type;
197    }
198    
199    public String getOptions() {
200        return options;
201    }
202
203    public String getTypeName() {
204        return type.name();
205    }
206    
207    @Override
208        public String getName() {
209        return name;
210    }
211    
212    public Class<?> getReturnType() {
213        return returnType;
214    }
215   
216    public Class<?>[] getParameterTypes() {
217        return parameterTypes;
218    }
219
220        public ClientType[] getAs3ParameterTypes() {
221                return clientParameterTypes;
222        }
223
224        public String[] getAs3ParameterNames() {
225                return clientParameterNames;
226        }
227
228        public String[] getAs3ParameterOptions() {
229                return clientParameterOptions;
230        }
231        
232        public ClientType getClientReturnType() {
233                return clientReturnType;
234        }
235
236        public ClientType[] getClientParameterTypes() {
237                return clientParameterTypes;
238        }
239
240        public String[] getClientParameterNames() {
241                return clientParameterNames;
242        }
243
244        public String[] getClientParameterOptions() {
245                return clientParameterOptions;
246        }
247        
248        public Set<ClientType> getClientAnnotationTypes() {
249                return clientAnnotationTypes;
250        }
251}