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
021 package org.granite.generator.as3.reflect;
022
023 import java.beans.PropertyDescriptor;
024 import java.lang.annotation.Annotation;
025 import java.lang.reflect.Field;
026 import java.lang.reflect.Modifier;
027 import java.net.URL;
028 import java.util.Collection;
029 import java.util.Collections;
030 import java.util.HashMap;
031 import java.util.HashSet;
032 import java.util.List;
033 import java.util.Map;
034 import java.util.Set;
035 import java.util.SortedMap;
036 import java.util.TreeMap;
037
038 import org.granite.generator.as3.As3Type;
039 import org.granite.generator.as3.reflect.JavaMethod.MethodType;
040 import org.granite.messaging.amf.io.util.externalizer.annotation.ExternalizedProperty;
041 import org.granite.messaging.amf.io.util.externalizer.annotation.IgnoredProperty;
042 import org.granite.tide.annotations.TideEvent;
043
044 /**
045 * @author Franck WOLFF
046 */
047 public class JavaBean extends JavaAbstractType {
048
049 ///////////////////////////////////////////////////////////////////////////
050 // Fields.
051
052 protected final Set<JavaImport> imports = new HashSet<JavaImport>();
053
054 protected final JavaType superclass;
055 protected final As3Type as3Superclass;
056
057 protected final List<JavaInterface> interfaces;
058 protected final List<JavaProperty> interfacesProperties;
059
060 protected final SortedMap<String, JavaProperty> properties;
061 protected final JavaProperty uid;
062
063 ///////////////////////////////////////////////////////////////////////////
064 // Constructor.
065
066 public JavaBean(JavaTypeFactory provider, Class<?> type, URL url) {
067 super(provider, type, url);
068
069 // Find superclass (controller filtered).
070 this.superclass = provider.getJavaTypeSuperclass(type);
071 if (this.superclass == null && type.isAnnotationPresent(TideEvent.class))
072 as3Superclass = new As3Type("org.granite.tide.events", "AbstractTideEvent");
073 else
074 as3Superclass = null;
075
076 // Find implemented interfaces (filtered by the current transformer).
077 this.interfaces = Collections.unmodifiableList(provider.getJavaTypeInterfaces(type));
078
079 // Collect bean properties.
080 this.properties = Collections.unmodifiableSortedMap(initProperties());
081
082 // Collect properties from implemented interfaces (ignore all implemented properties).
083 Map<String, JavaProperty> allProperties = new HashMap<String, JavaProperty>(this.properties);
084 for (JavaType supertype = this.superclass; supertype instanceof JavaBean; supertype = ((JavaBean)supertype).superclass)
085 allProperties.putAll(((JavaBean)supertype).properties);
086 Map<String, JavaProperty> iPropertyMap = new HashMap<String, JavaProperty>();
087 for (JavaInterface interfaze : interfaces) {
088 for (JavaProperty property : interfaze.getProperties()) {
089 String name = property.getName();
090 if (!iPropertyMap.containsKey(name) && !allProperties.containsKey(name))
091 iPropertyMap.put(name, property);
092 }
093 }
094 this.interfacesProperties = getSortedUnmodifiableList(iPropertyMap.values());
095
096 // Find uid (if any).
097 JavaProperty tmpUid = null;
098 for (JavaProperty property : properties.values()) {
099 if (provider.isUid(property)) {
100 tmpUid = property;
101 break;
102 }
103 }
104 this.uid = tmpUid;
105
106 // Collect imports.
107 if (superclass != null)
108 addToImports(provider.getJavaImport(superclass.getType()));
109 for (JavaInterface interfaze : interfaces)
110 addToImports(provider.getJavaImport(interfaze.getType()));
111 for (JavaProperty property : properties.values())
112 addToImports(provider.getJavaImport(property.getType()));
113 }
114
115 ///////////////////////////////////////////////////////////////////////////
116 // Properties.
117
118 public Set<JavaImport> getImports() {
119 return imports;
120 }
121 protected void addToImports(JavaImport javaImport) {
122 if (javaImport != null)
123 imports.add(javaImport);
124 }
125
126 public boolean hasSuperclass() {
127 return superclass != null;
128 }
129 public JavaType getSuperclass() {
130 return superclass;
131 }
132 public As3Type getAs3Superclass() {
133 return as3Superclass;
134 }
135
136 public boolean hasInterfaces() {
137 return interfaces != null && !interfaces.isEmpty();
138 }
139 public List<JavaInterface> getInterfaces() {
140 return interfaces;
141 }
142
143 public boolean hasInterfacesProperties() {
144 return interfacesProperties != null && !interfacesProperties.isEmpty();
145 }
146 public List<JavaProperty> getInterfacesProperties() {
147 return interfacesProperties;
148 }
149
150 public Collection<JavaProperty> getProperties() {
151 return properties.values();
152 }
153
154 public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
155 return type.isAnnotationPresent(annotation);
156 }
157
158 public boolean hasUid() {
159 return uid != null;
160 }
161 public JavaProperty getUid() {
162 return uid;
163 }
164
165 public boolean hasEnumProperty() {
166 for (JavaProperty property : properties.values()) {
167 if (property.isEnum())
168 return true;
169 }
170 return false;
171 }
172
173 ///////////////////////////////////////////////////////////////////////////
174 // Utilities.
175
176 protected SortedMap<String, JavaProperty> initProperties() {
177 PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(type);
178 SortedMap<String, JavaProperty> propertyMap = new TreeMap<String, JavaProperty>();
179
180 // Standard declared fields.
181 for (Field field : type.getDeclaredFields()) {
182 if (!Modifier.isStatic(field.getModifiers()) &&
183 !Modifier.isTransient(field.getModifiers()) &&
184 !"jdoDetachedState".equals(field.getName()) && // Specific for JDO statically enhanced classes
185 !field.isAnnotationPresent(IgnoredProperty.class)) {
186
187 String name = field.getName();
188 JavaMethod readMethod = null;
189 JavaMethod writeMethod = null;
190
191 if (field.getType().isMemberClass() && !field.getType().isEnum())
192 throw new UnsupportedOperationException("Inner classes are not supported (except enums): " + field.getType());
193
194 if (propertyDescriptors != null) {
195 for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
196 if (name.equals(propertyDescriptor.getName())) {
197 if (propertyDescriptor.getReadMethod() != null)
198 readMethod = new JavaMethod(propertyDescriptor.getReadMethod(), MethodType.GETTER);
199 if (propertyDescriptor.getWriteMethod() != null)
200 writeMethod = new JavaMethod(propertyDescriptor.getWriteMethod(), MethodType.SETTER);
201 break;
202 }
203 }
204 }
205
206 JavaFieldProperty property = new JavaFieldProperty(provider, field, readMethod, writeMethod);
207 propertyMap.put(name, property);
208 }
209 }
210
211 // Getter annotated by @ExternalizedProperty.
212 if (propertyDescriptors != null) {
213 for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
214 if (propertyDescriptor.getReadMethod() != null &&
215 propertyDescriptor.getReadMethod().getDeclaringClass().equals(type) &&
216 propertyDescriptor.getReadMethod().isAnnotationPresent(ExternalizedProperty.class) &&
217 !propertyMap.containsKey(propertyDescriptor.getName())) {
218
219 JavaMethod readMethod = new JavaMethod(propertyDescriptor.getReadMethod(), MethodType.GETTER);
220 JavaMethodProperty property = new JavaMethodProperty(provider, propertyDescriptor.getName(), readMethod, null);
221 propertyMap.put(propertyDescriptor.getName(), property);
222 }
223 }
224 }
225
226 return propertyMap;
227 }
228 }