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    }