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