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