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 }