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 package org.granite.generator.as3.reflect;
022
023 import java.beans.PropertyDescriptor;
024 import java.lang.annotation.Annotation;
025 import java.lang.reflect.Field;
026 import java.lang.reflect.Method;
027 import java.lang.reflect.Modifier;
028 import java.lang.reflect.ParameterizedType;
029 import java.lang.reflect.Type;
030 import java.net.URL;
031 import java.util.ArrayList;
032 import java.util.Collection;
033 import java.util.Collections;
034 import java.util.HashMap;
035 import java.util.HashSet;
036 import java.util.LinkedHashMap;
037 import java.util.List;
038 import java.util.Map;
039 import java.util.Set;
040 import java.util.SortedMap;
041 import java.util.TreeMap;
042
043 import org.granite.generator.as3.As3Type;
044 import org.granite.generator.as3.ClientType;
045 import org.granite.generator.as3.reflect.JavaMethod.MethodType;
046 import org.granite.messaging.amf.io.util.externalizer.annotation.ExternalizedProperty;
047 import org.granite.messaging.amf.io.util.externalizer.annotation.IgnoredProperty;
048 import org.granite.tide.annotations.TideEvent;
049 import org.granite.util.ClassUtil;
050 import org.granite.util.ClassUtil.DeclaredAnnotation;
051
052 /**
053 * @author Franck WOLFF
054 */
055 public class JavaBean extends JavaAbstractType {
056
057 ///////////////////////////////////////////////////////////////////////////
058 // Fields.
059
060 protected final Set<JavaImport> imports = new HashSet<JavaImport>();
061
062 protected final JavaType superclass;
063 protected final ClientType clientSuperclass;
064
065 protected final List<JavaInterface> interfaces;
066 protected final List<JavaProperty> interfacesProperties;
067
068 protected final Map<String, JavaProperty> properties;
069 protected final JavaProperty uid;
070
071 ///////////////////////////////////////////////////////////////////////////
072 // Constructor.
073
074 public JavaBean(JavaTypeFactory provider, Class<?> type, URL url) {
075 super(provider, type, url);
076
077 // Find superclass (controller filtered).
078 this.superclass = provider.getJavaTypeSuperclass(type);
079 if (this.superclass == null && type.isAnnotationPresent(TideEvent.class))
080 clientSuperclass = new As3Type("org.granite.tide.events", "AbstractTideEvent");
081 else
082 clientSuperclass = null;
083
084 // Find implemented interfaces (filtered by the current transformer).
085 this.interfaces = Collections.unmodifiableList(provider.getJavaTypeInterfaces(type));
086
087 // Collect bean properties.
088 Map<String, JavaProperty> properties = new LinkedHashMap<String, JavaProperty>();
089
090 if (this.superclass == null) {
091 // Collect bean properties of non generated superclasses if necessary
092 // Example: com.myapp.AbstractEntity extends org.springframework.data.jpa.model.AbstractPersistable<T>
093 // gsup collects the superclass parameterized type so the type parameter can be translated to an actual type later
094 List<Class<?>> superclasses = new ArrayList<Class<?>>();
095 Type gsup = type.getGenericSuperclass();
096 Class<?> c = type.getSuperclass();
097 while (c.getGenericSuperclass() != null && !c.getName().equals(Object.class.getName())) {
098 superclasses.add(0, c);
099 c = c.getSuperclass();
100 }
101
102 for (Class<?> sc : superclasses)
103 properties.putAll(initProperties(sc, gsup instanceof ParameterizedType ? (ParameterizedType)gsup : null));
104 }
105
106 properties.putAll(initProperties());
107
108 this.properties = Collections.unmodifiableMap(properties);
109
110 // Collect properties from superclasses.
111 Map<String, JavaProperty> allProperties = new HashMap<String, JavaProperty>(this.properties);
112 for (JavaType supertype = this.superclass; supertype instanceof JavaBean; supertype = ((JavaBean)supertype).superclass)
113 allProperties.putAll(((JavaBean)supertype).properties);
114
115 // Collect properties from interfaces.
116 Map<String, JavaProperty> iPropertyMap = new HashMap<String, JavaProperty>();
117 addImplementedInterfacesProperties(interfaces, iPropertyMap, allProperties);
118 this.interfacesProperties = getSortedUnmodifiableList(iPropertyMap.values());
119
120 // Find uid (if any).
121 JavaProperty tmpUid = null;
122 for (JavaProperty property : properties.values()) {
123 if (provider.isUid(property)) {
124 tmpUid = property;
125 break;
126 }
127 }
128 this.uid = tmpUid;
129
130 // Collect imports.
131 if (superclass != null)
132 addToImports(provider.getJavaImport(superclass.getType()));
133 for (JavaInterface interfaze : interfaces)
134 addToImports(provider.getJavaImport(interfaze.getType()));
135 for (JavaProperty property : properties.values())
136 addToImports(provider.getJavaImports(property.getClientType(), true));
137 }
138
139 private void addImplementedInterfacesProperties(List<JavaInterface> interfaces, Map<String, JavaProperty> iPropertyMap, Map<String, JavaProperty> allProperties) {
140 for (JavaInterface interfaze : interfaces) {
141 for (JavaProperty property : interfaze.getProperties()) {
142 String name = property.getName();
143 if (!iPropertyMap.containsKey(name) && !allProperties.containsKey(name))
144 iPropertyMap.put(name, property);
145 }
146 addImplementedInterfacesProperties(interfaze.interfaces, iPropertyMap, allProperties);
147 }
148 }
149
150 ///////////////////////////////////////////////////////////////////////////
151 // Properties.
152
153 public Set<JavaImport> getImports() {
154 return imports;
155 }
156 protected void addToImports(JavaImport javaImport) {
157 if (javaImport != null)
158 imports.add(javaImport);
159 }
160 protected void addToImports(Set<JavaImport> javaImports) {
161 if (javaImports != null)
162 imports.addAll(javaImports);
163 }
164
165 public boolean hasSuperclass() {
166 return superclass != null;
167 }
168 public JavaType getSuperclass() {
169 return superclass;
170 }
171 public ClientType getAs3Superclass() {
172 return clientSuperclass;
173 }
174 public ClientType getClientSuperclass() {
175 return clientSuperclass;
176 }
177
178 public boolean hasInterfaces() {
179 return interfaces != null && !interfaces.isEmpty();
180 }
181 public List<JavaInterface> getInterfaces() {
182 return interfaces;
183 }
184
185 public boolean hasInterfacesProperties() {
186 return interfacesProperties != null && !interfacesProperties.isEmpty();
187 }
188 public List<JavaProperty> getInterfacesProperties() {
189 return interfacesProperties;
190 }
191
192 public Collection<JavaProperty> getProperties() {
193 return properties.values();
194 }
195
196 public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
197 return type.isAnnotationPresent(annotation);
198 }
199
200 public boolean hasUid() {
201 return uid != null;
202 }
203 public JavaProperty getUid() {
204 return uid;
205 }
206
207 public boolean hasEnumProperty() {
208 for (JavaProperty property : properties.values()) {
209 if (property.isEnum())
210 return true;
211 }
212 return false;
213 }
214
215 ///////////////////////////////////////////////////////////////////////////
216 // Utilities.
217
218 protected SortedMap<String, JavaProperty> initProperties() {
219 return initProperties(type, null);
220 }
221
222 protected SortedMap<String, JavaProperty> initProperties(Class<?> type, ParameterizedType parentGenericType) {
223 PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(type);
224 SortedMap<String, JavaProperty> propertyMap = new TreeMap<String, JavaProperty>();
225
226 // Standard declared fields.
227 for (Field field : type.getDeclaredFields()) {
228 if (!Modifier.isStatic(field.getModifiers()) &&
229 !Modifier.isTransient(field.getModifiers()) &&
230 !"jdoDetachedState".equals(field.getName()) && // Specific for JDO statically enhanced classes
231 !field.isAnnotationPresent(IgnoredProperty.class)) {
232
233 String name = field.getName();
234 JavaMethod readMethod = null;
235 JavaMethod writeMethod = null;
236
237 if (field.getType().isMemberClass() && !field.getType().isEnum())
238 throw new UnsupportedOperationException("Inner classes are not supported (except enums): " + field.getType());
239
240 if (propertyDescriptors != null) {
241 for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
242 if (name.equals(propertyDescriptor.getName())) {
243 if (propertyDescriptor.getReadMethod() != null)
244 readMethod = new JavaMethod(propertyDescriptor.getReadMethod(), MethodType.GETTER);
245 if (propertyDescriptor.getWriteMethod() != null)
246 writeMethod = new JavaMethod(propertyDescriptor.getWriteMethod(), MethodType.SETTER);
247 break;
248 }
249 }
250 }
251
252 JavaFieldProperty property = new JavaFieldProperty(provider, field, readMethod, writeMethod, parentGenericType);
253 propertyMap.put(name, property);
254 }
255 }
256
257 // Getter annotated by @ExternalizedProperty.
258 if (propertyDescriptors != null) {
259 for (PropertyDescriptor property : propertyDescriptors) {
260 Method getter = property.getReadMethod();
261 if (getter != null &&
262 getter.getDeclaringClass().equals(type) &&
263 !propertyMap.containsKey(property.getName())) {
264
265 DeclaredAnnotation<ExternalizedProperty> annotation = ClassUtil.getAnnotation(getter, ExternalizedProperty.class);
266 if (annotation == null || (annotation.declaringClass != type && !annotation.declaringClass.isInterface()))
267 continue;
268
269 JavaMethod readMethod = new JavaMethod(getter, MethodType.GETTER);
270 JavaMethodProperty methodProperty = new JavaMethodProperty(provider, property.getName(), readMethod, null, parentGenericType);
271 propertyMap.put(property.getName(), methodProperty);
272 }
273 }
274 }
275
276 return propertyMap;
277 }
278 }