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