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 023package org.granite.generator.as3.reflect; 024 025import java.lang.annotation.Annotation; 026import java.lang.reflect.Field; 027import java.lang.reflect.Method; 028import java.lang.reflect.Modifier; 029import java.lang.reflect.ParameterizedType; 030import java.lang.reflect.Type; 031import java.net.URL; 032import java.util.ArrayList; 033import java.util.Collection; 034import java.util.Collections; 035import java.util.HashMap; 036import java.util.HashSet; 037import java.util.LinkedHashMap; 038import java.util.List; 039import java.util.Map; 040import java.util.Set; 041import java.util.SortedMap; 042import java.util.TreeMap; 043 044import org.granite.generator.as3.As3Type; 045import org.granite.generator.as3.ClientType; 046import org.granite.generator.as3.reflect.JavaMethod.MethodType; 047import org.granite.messaging.annotations.Exclude; 048import org.granite.messaging.annotations.Include; 049import org.granite.tide.annotations.TideEvent; 050import org.granite.util.ClassUtil; 051import org.granite.util.ClassUtil.DeclaredAnnotation; 052import org.granite.util.PropertyDescriptor; 053 054/** 055 * @author Franck WOLFF 056 */ 057public 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}