/** * GRANITE DATA SERVICES * Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S. * * This file is part of the Granite Data Services Platform. * * Granite Data Services is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * Granite Data Services is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA, or see <http://www.gnu.org/licenses/>. */ package org.granite.generator.as3.reflect; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.granite.generator.as3.As3Type; import org.granite.generator.as3.ClientType; import org.granite.generator.as3.reflect.JavaMethod.MethodType; import org.granite.messaging.annotations.Exclude; import org.granite.messaging.annotations.Include; import org.granite.tide.annotations.TideEvent; import org.granite.util.ClassUtil; import org.granite.util.ClassUtil.DeclaredAnnotation; import org.granite.util.PropertyDescriptor; /** * @author Franck WOLFF */ public class JavaBean extends JavaAbstractType { /////////////////////////////////////////////////////////////////////////// // Fields. protected final Set<JavaImport> imports = new HashSet<JavaImport>(); protected final JavaType superclass; protected final ClientType clientSuperclass; protected final List<JavaInterface> interfaces; protected final List<JavaProperty> interfacesProperties; protected final Map<String, JavaProperty> properties; protected final JavaProperty uid; protected final List<JavaProperty> lazyProperties; /////////////////////////////////////////////////////////////////////////// // Constructor. public JavaBean(JavaTypeFactory provider, Class<?> type, URL url) { super(provider, type, url); // Find superclass (controller filtered). this.superclass = provider.getJavaTypeSuperclass(type); if (this.superclass == null && type.isAnnotationPresent(TideEvent.class)) clientSuperclass = new As3Type("org.granite.tide.events", "AbstractTideEvent"); else clientSuperclass = null; // Find implemented interfaces (filtered by the current transformer). this.interfaces = Collections.unmodifiableList(provider.getJavaTypeInterfaces(type)); // Collect bean properties. Map<String, JavaProperty> properties = new LinkedHashMap<String, JavaProperty>(); if (this.superclass == null) { // Collect bean properties of non generated superclasses if necessary // Example: com.myapp.AbstractEntity extends org.springframework.data.jpa.model.AbstractPersistable<T> // gsup collects the superclass parameterized type so the type parameter can be translated to an actual type later List<Class<?>> superclasses = new ArrayList<Class<?>>(); Type gsup = type.getGenericSuperclass(); Class<?> c = type.getSuperclass(); while (c.getGenericSuperclass() != null && !c.getName().equals(Object.class.getName())) { superclasses.add(0, c); c = c.getSuperclass(); } for (Class<?> sc : superclasses) properties.putAll(initProperties(sc, gsup instanceof ParameterizedType ? (ParameterizedType)gsup : null)); } properties.putAll(initProperties()); this.properties = Collections.unmodifiableMap(properties); List<JavaProperty> tmpLazyProperties = new ArrayList<JavaProperty>(); for (JavaProperty property : properties.values()) { if (provider.isLazy(property)) tmpLazyProperties.add(property); } this.lazyProperties = (tmpLazyProperties.isEmpty() ? null : Collections.unmodifiableList(tmpLazyProperties)); // Collect properties from superclasses. Map<String, JavaProperty> allProperties = new HashMap<String, JavaProperty>(this.properties); for (JavaType supertype = this.superclass; supertype instanceof JavaBean; supertype = ((JavaBean)supertype).superclass) allProperties.putAll(((JavaBean)supertype).properties); // Collect properties from interfaces. Map<String, JavaProperty> iPropertyMap = new HashMap<String, JavaProperty>(); addImplementedInterfacesProperties(interfaces, iPropertyMap, allProperties); this.interfacesProperties = getSortedUnmodifiableList(iPropertyMap.values()); // Find uid (if any). JavaProperty tmpUid = null; for (JavaProperty property : properties.values()) { if (provider.isUid(property)) { tmpUid = property; break; } } this.uid = tmpUid; // Collect imports. if (superclass != null) addToImports(provider.getJavaImport(superclass.getType())); for (JavaInterface interfaze : interfaces) addToImports(provider.getJavaImport(interfaze.getType())); for (JavaProperty property : properties.values()) addToImports(provider.getJavaImports(property.getClientType(), true)); } private void addImplementedInterfacesProperties(List<JavaInterface> interfaces, Map<String, JavaProperty> iPropertyMap, Map<String, JavaProperty> allProperties) { for (JavaInterface interfaze : interfaces) { for (JavaProperty property : interfaze.getProperties()) { String name = property.getName(); if (!iPropertyMap.containsKey(name) && !allProperties.containsKey(name)) iPropertyMap.put(name, property); } addImplementedInterfacesProperties(interfaze.interfaces, iPropertyMap, allProperties); } } /////////////////////////////////////////////////////////////////////////// // Properties. public Set<JavaImport> getImports() { return imports; } protected void addToImports(JavaImport javaImport) { if (javaImport != null) imports.add(javaImport); } protected void addToImports(Set<JavaImport> javaImports) { if (javaImports != null) imports.addAll(javaImports); } public boolean hasSuperclass() { return superclass != null; } public JavaType getSuperclass() { return superclass; } public ClientType getAs3Superclass() { return clientSuperclass; } public ClientType getClientSuperclass() { return clientSuperclass; } public boolean hasInterfaces() { return interfaces != null && !interfaces.isEmpty(); } public List<JavaInterface> getInterfaces() { return interfaces; } public boolean hasInterfacesProperties() { return interfacesProperties != null && !interfacesProperties.isEmpty(); } public List<JavaProperty> getInterfacesProperties() { return interfacesProperties; } public Collection<JavaProperty> getProperties() { return properties.values(); } public boolean isAnnotationPresent(Class<? extends Annotation> annotation) { return type.isAnnotationPresent(annotation); } public boolean hasUid() { return uid != null; } public JavaProperty getUid() { return uid; } public boolean isLazy(JavaProperty property) { return lazyProperties != null && lazyProperties.contains(property); } public boolean hasEnumProperty() { for (JavaProperty property : properties.values()) { if (property.isEnum()) return true; } return false; } /////////////////////////////////////////////////////////////////////////// // Utilities. protected SortedMap<String, JavaProperty> initProperties() { return initProperties(type, null); } protected SortedMap<String, JavaProperty> initProperties(Class<?> type, ParameterizedType parentGenericType) { PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(type); SortedMap<String, JavaProperty> propertyMap = new TreeMap<String, JavaProperty>(); // Standard declared fields. for (Field field : type.getDeclaredFields()) { if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isTransient(field.getModifiers()) && !"jdoDetachedState".equals(field.getName()) && // Specific for JDO statically enhanced classes !field.isAnnotationPresent(Exclude.class)) { String name = field.getName(); JavaMethod readMethod = null; JavaMethod writeMethod = null; if (field.getType().isMemberClass() && !field.getType().isEnum()) throw new UnsupportedOperationException("Inner classes are not supported (except enums): " + field.getType()); if (propertyDescriptors != null) { for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { if (name.equals(propertyDescriptor.getName())) { if (propertyDescriptor.getReadMethod() != null) readMethod = new JavaMethod(propertyDescriptor.getReadMethod(), MethodType.GETTER); if (propertyDescriptor.getWriteMethod() != null) writeMethod = new JavaMethod(propertyDescriptor.getWriteMethod(), MethodType.SETTER); break; } } } JavaFieldProperty property = new JavaFieldProperty(provider, field, readMethod, writeMethod, parentGenericType); propertyMap.put(name, property); } } // Getter annotated by @Include. if (propertyDescriptors != null) { for (PropertyDescriptor property : propertyDescriptors) { Method getter = property.getReadMethod(); if (getter != null && getter.getDeclaringClass().equals(type) && !propertyMap.containsKey(property.getName())) { DeclaredAnnotation<Include> annotation = ClassUtil.getAnnotation(getter, Include.class); if (annotation == null || (annotation.declaringClass != type && !annotation.declaringClass.isInterface())) continue; JavaMethod readMethod = new JavaMethod(getter, MethodType.GETTER); JavaMethodProperty methodProperty = new JavaMethodProperty(provider, property.getName(), readMethod, null, parentGenericType); propertyMap.put(property.getName(), methodProperty); } } } return propertyMap; } }