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      
021      SLSB: This class and all the modifications to use it are marked with the 'SLSB' tag.
022     */
023    
024    package org.granite.generator.as3.reflect;
025    
026    import java.beans.Introspector;
027    import java.lang.annotation.Annotation;
028    import java.lang.reflect.Method;
029    import java.lang.reflect.Modifier;
030    import java.net.URL;
031    import java.util.ArrayList;
032    import java.util.Collection;
033    import java.util.Collections;
034    import java.util.HashSet;
035    import java.util.LinkedHashMap;
036    import java.util.List;
037    import java.util.Map;
038    import java.util.Set;
039    
040    import org.granite.generator.as3.reflect.JavaMethod.MethodType;
041    import org.granite.messaging.service.annotations.IgnoredMethod;
042    import org.granite.messaging.service.annotations.RemoteDestination;
043    
044    /**
045     * @author Franck WOLFF
046     */
047    public class JavaRemoteDestination extends JavaAbstractType {
048    
049            // /////////////////////////////////////////////////////////////////////////
050            // Fields.
051    
052            protected final Set<JavaImport> imports = new HashSet<JavaImport>();
053            protected final JavaType superclass;
054            protected final List<JavaMethod> methods;
055        protected final Map<String, JavaMethodProperty> properties;
056            protected final String destinationName;
057            protected final String channelId;
058            
059            // /////////////////////////////////////////////////////////////////////////
060            // Constructor.
061    
062            public JavaRemoteDestination(JavaTypeFactory provider, Class<?> type, URL url) {
063                    super(provider, type, url);
064    
065                    // Find superclass (controller filtered).
066                    this.superclass = provider.getJavaTypeSuperclass(type);
067    
068                    // Collect methods.
069                    this.methods = Collections.unmodifiableList(initMethods());
070    
071            // Collect bean properties.
072            this.properties = Collections.unmodifiableMap(initProperties());
073    
074                    // Collect imports.
075                    if (superclass != null)
076                            addToImports(provider.getJavaImport(superclass.getType()));
077                    
078                    RemoteDestination rd = type.getAnnotation(RemoteDestination.class);
079                    if (rd != null) {
080                            destinationName = rd.id();
081                            channelId = rd.channel();
082                    }
083                    else {
084                            destinationName = null;
085                            channelId = null;
086                    }
087            }
088    
089            // /////////////////////////////////////////////////////////////////////////
090            // Properties.
091    
092            public Set<JavaImport> getImports() {
093                    return imports;
094            }
095    
096            protected void addToImports(JavaImport javaImport) {
097                    if (javaImport != null)
098                            imports.add(javaImport);
099            }
100    
101            public boolean hasSuperclass() {
102                    return superclass != null;
103            }
104    
105            public JavaType getSuperclass() {
106                    return superclass;
107            }
108            
109        public Collection<JavaMethod> getMethods() {
110            return methods;
111        }
112            
113        public Collection<JavaMethodProperty> getProperties() {
114            return properties.values();
115        }
116    
117            public String getDestinationName() {
118                    return destinationName;
119            }
120    
121            public String getChannelId() {
122                    return channelId;
123            }
124            
125            public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
126                    return type.isAnnotationPresent(annotation);
127            }
128    
129            // /////////////////////////////////////////////////////////////////////////
130            // Utilities.
131    
132            protected List<JavaMethod> initMethods() {
133                    List<JavaMethod> methodMap = new ArrayList<JavaMethod>();
134    
135                    // Get all methods for interfaces: normally, even if it is possible in Java
136                    // to override a method into a inherited interface, there is no meaning
137                    // to do so (we just ignore potential compilation issues with generated AS3
138                    // classes for this case since it is always possible to remove the method
139                    // re-declaration in the child interface).
140                    Method[] methods = null;
141                    if (type.isInterface())
142                            methods = type.getMethods();
143                    else
144                            methods = type.getDeclaredMethods();
145                    
146                    for (Method method : methods) {
147                            if (shouldGenerateMethod(method)) {
148                                    for (Class<?> clazz : method.getParameterTypes()) {
149                                            if (clazz.isMemberClass() && !clazz.isEnum()) {
150                                                    throw new UnsupportedOperationException(
151                                                            "Inner classes are not supported (except enums): " + clazz
152                                                    );
153                                            }
154                                            addToImports(provider.getJavaImport(clazz));
155                                    }
156    
157                                    methodMap.add(new JavaMethod(method, MethodType.OTHER, this.provider));
158                            }
159                    }
160    
161                    return methodMap;
162            }
163            
164            protected boolean shouldGenerateMethod(Method method) {
165                    return 
166                            Modifier.isPublic(method.getModifiers())
167                            && !Modifier.isStatic(method.getModifiers())
168                            && !method.isAnnotationPresent(IgnoredMethod.class);
169            }       
170    
171            protected Map<String, JavaMethodProperty> initProperties() {
172            Map<String, JavaMethodProperty> propertyMap = new LinkedHashMap<String, JavaMethodProperty>();
173    
174                    // Get all methods for interfaces: normally, even if it is possible in Java
175                    // to override a method into a inherited interface, there is no meaning
176                    // to do so (we just ignore potential compilation issues with generated AS3
177                    // classes for this case since it is always possible to remove the method
178                    // re-declaration in the child interface).
179                    Method[] methods = null;
180                    if (type.isInterface())
181                            methods = type.getMethods();
182                    else
183                            methods = type.getDeclaredMethods();
184                    
185                    List<Object[]> tmp = new ArrayList<Object[]>();
186                    
187                    for (Method method : methods) {
188                            if (shouldGenerateProperty(method)) {                           
189                                    for (Class<?> clazz : method.getParameterTypes())
190                                            addToImports(provider.getJavaImport(clazz));
191                                    addToImports(provider.getJavaImport(method.getReturnType()));
192                                    
193                                    String propertyName = Introspector.decapitalize(method.getName().startsWith("is") ? method.getName().substring(2) : method.getName().substring(3));
194    
195                                    Object[] property = null;
196                                    for (Object[] mp : tmp) {
197                                            if (mp[0].equals(propertyName)) {
198                                                    property = mp;
199                                                    break;
200                                            }
201                                    }
202                                    if (property == null) {
203                                            property = new Object[] { propertyName, null, null };
204                                            tmp.add(property);
205                                    }
206                                    if (method.getName().startsWith("set"))
207                                            property[2] = method;
208                                    else
209                                            property[1] = method;
210                            }
211                    }
212                    
213                    for (Object[] property : tmp) {
214                            JavaMethod readMethod = property[1] != null ? new JavaMethod((Method)property[1], MethodType.GETTER) : null;
215                            JavaMethod writeMethod = property[2] != null ? new JavaMethod((Method)property[2], MethodType.SETTER) : null;
216                            propertyMap.put((String)property[0], new JavaMethodProperty(provider, (String)property[0], readMethod, writeMethod));
217                    }
218                    
219                    return propertyMap;
220            }
221            
222            protected boolean shouldGenerateProperty(Method method) {
223                    return false;
224            }       
225    
226            
227            public JavaInterface convertToJavaInterface() {
228                    return new JavaInterface(getProvider(), getType(), getUrl());
229            }
230    }