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    
023    package org.granite.generator.as3.reflect;
024    
025    import java.lang.annotation.Annotation;
026    import java.lang.reflect.Method;
027    import java.lang.reflect.ParameterizedType;
028    import java.lang.reflect.Type;
029    import java.util.Map;
030    
031    import org.granite.generator.as3.ClientType;
032    import org.granite.generator.as3.PropertyType;
033    import org.granite.generator.util.GenericTypeUtil;
034    import org.granite.messaging.service.annotations.Param;
035    import org.granite.tide.data.Lazy;
036    import org.granite.util.ClassUtil;
037    
038    /**
039     * @author Franck WOLFF
040     */
041    public 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    }