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