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    
023    package org.granite.generator.java;
024    
025    import java.lang.reflect.GenericArrayType;
026    import java.lang.reflect.ParameterizedType;
027    import java.lang.reflect.Type;
028    import java.lang.reflect.TypeVariable;
029    import java.lang.reflect.WildcardType;
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.SortedSet;
037    
038    import org.granite.generator.as3.As3TypeFactory;
039    import org.granite.generator.as3.ClientType;
040    import org.granite.generator.as3.PropertyType;
041    import org.granite.generator.util.GenericTypeUtil;
042    import org.granite.util.ClassUtil;
043    
044    /**
045     * @author Franck WOLFF
046     */
047    public class DefaultJavaTypeFactory implements As3TypeFactory {
048    
049        ///////////////////////////////////////////////////////////////////////////
050        // Fields.
051    
052        private final Map<String, ClientType> simpleJava2JavaType = new HashMap<String, ClientType>();
053        private final Map<String, ClientType> propertyJava2JavaType = new HashMap<String, ClientType>();
054        
055        
056        ///////////////////////////////////////////////////////////////////////////
057        // Constructors.
058    
059        public DefaultJavaTypeFactory() {
060            simpleJava2JavaType.put(buildCacheKey(Boolean.TYPE), JavaType.BOOLEAN);
061            simpleJava2JavaType.put(buildCacheKey(Integer.TYPE), JavaType.INT);
062            simpleJava2JavaType.put(buildCacheKey(Long.TYPE), JavaType.LONG);
063            simpleJava2JavaType.put(buildCacheKey(Float.TYPE), JavaType.FLOAT);
064            simpleJava2JavaType.put(buildCacheKey(Double.TYPE), JavaType.DOUBLE);
065            simpleJava2JavaType.put(buildCacheKey(String.class), JavaType.STRING);
066            
067            propertyJava2JavaType.put(buildCacheKey(Boolean.TYPE), JavaType.BOOLEAN);
068            propertyJava2JavaType.put(buildCacheKey(Integer.TYPE), JavaType.INT);
069            propertyJava2JavaType.put(buildCacheKey(Long.TYPE), JavaType.LONG);
070            propertyJava2JavaType.put(buildCacheKey(Float.TYPE), JavaType.FLOAT);
071            propertyJava2JavaType.put(buildCacheKey(Double.TYPE), JavaType.DOUBLE);
072            propertyJava2JavaType.put(buildCacheKey(String.class), JavaType.STRING);
073        }
074    
075        ///////////////////////////////////////////////////////////////////////////
076        // Fields.
077    
078        @Override
079            public void configure(boolean externalizeLong, boolean externalizeBigInteger, boolean externalizeBigDecimal) {
080            }
081        
082        private static final String buildCacheKey(Type jType) {
083            return buildCacheKey(jType, null, null);
084        }
085        private static final String buildCacheKey(Type jType, Class<?> declaringClass, ParameterizedType[] declaringTypes) {
086                    String key = jType.toString();
087                    if (declaringClass != null)
088                            key += "::" + declaringClass.toString();
089                    if (declaringTypes != null) {
090                            for (ParameterizedType dt : declaringTypes)
091                                    key += "::" + dt.toString();
092                    }
093                    return key;
094        }
095    
096            @Override
097            public ClientType getClientType(Type jType, Class<?> declaringClass, ParameterizedType[] declaringTypes, PropertyType propertyType) {
098                    String key = buildCacheKey(jType, declaringClass, declaringTypes);
099                    
100                    ClientType javaType = getFromCache(key, propertyType);
101                    
102            if (javaType == null) {
103                    if (jType instanceof GenericArrayType) {
104                            Type componentType = ((GenericArrayType)jType).getGenericComponentType();
105                            javaType = getClientType(componentType, declaringClass, declaringTypes, PropertyType.SIMPLE).toArrayType();
106                    }
107                    else if (jType instanceof Class<?> && ((Class<?>)jType).isArray()) {
108                            javaType = getClientType(((Class<?>)jType).getComponentType(), declaringClass, declaringTypes, PropertyType.SIMPLE).toArrayType();
109                    }
110                    else {
111                            Set<String> imports = new HashSet<String>();
112                            Class<?> jClass = ClassUtil.classOfType(jType);
113                            String genericType = "";
114                            if (jType instanceof ParameterizedType)
115                                    genericType = buildGenericTypeName((ParameterizedType)jType, declaringClass, declaringTypes, propertyType, imports);
116                            
117                    if (propertyType.isProperty() && List.class.isAssignableFrom(jClass)) {
118                        javaType = createJavaType(jType, declaringClass, declaringTypes, "org.granite.client.persistence.collection.PersistentList" + genericType, propertyType);
119                    }
120                    else if (propertyType.isProperty() && SortedSet.class.isAssignableFrom(jClass)) {
121                        javaType = createJavaType(jType, declaringClass, declaringTypes, "org.granite.client.persistence.collection.PersistentSortedSet" + genericType, propertyType);
122                    }
123                    else if (propertyType.isProperty() && Set.class.isAssignableFrom(jClass)) {
124                        javaType = createJavaType(jType, declaringClass, declaringTypes, "org.granite.client.persistence.collection.PersistentSet" + genericType, propertyType);
125                    }
126                    else if (propertyType.isProperty() && SortedMap.class.isAssignableFrom(jClass)) {
127                        javaType = createJavaType(jType, declaringClass, declaringTypes, "org.granite.client.persistence.collection.PersistentSortedMap" + genericType, propertyType);
128                    }
129                    else if (propertyType.isProperty() && Map.class.isAssignableFrom(jClass)) {
130                        javaType = createJavaType(jType, declaringClass, declaringTypes, "org.granite.client.persistence.collection.PersistentMap" + genericType, propertyType);
131                    }
132                    else if (jClass.getName().equals("com.google.appengine.api.datastore.Key")) {
133                            javaType = JavaType.STRING;
134                        }
135                        else if (jClass.getName().equals("org.springframework.data.domain.Page")) {
136                            javaType = new JavaType("org.granite.tide.data.model", "Page" + genericType, null);
137                        }
138                        else if (jClass.getName().equals("org.springframework.data.domain.Pageable")) {
139                            javaType = JavaType.PAGE_INFO;
140                        }
141                        else if (jClass.getName().equals("org.springframework.data.domain.Sort")) {
142                            javaType = JavaType.SORT_INFO;
143                        }
144                        else {
145                            javaType = createJavaType(jType, declaringClass, declaringTypes, null, propertyType);
146                        }
147                        if (!imports.isEmpty())
148                            javaType.addImports(imports);
149                    }
150                    
151                putInCache(key, propertyType, javaType);
152            }
153            
154            return javaType;
155        }
156    
157            @Override
158            public ClientType getAs3Type(Class<?> jType) {
159                    return getClientType(jType, null, null, PropertyType.SIMPLE);
160            }
161    
162        protected JavaType createJavaType(Type jType, Class<?> declaringClass, ParameterizedType[] declaringTypes, String propertyImplTypeName, PropertyType propertyType) {
163            Set<String> imports = new HashSet<String>();
164            
165            Class<?> jClass = ClassUtil.classOfType(jType);
166            String name = jClass.getSimpleName();
167            if (jClass.isMemberClass())
168                    name = jClass.getEnclosingClass().getSimpleName() + '$' + jClass.getSimpleName();
169            else if (jType instanceof ParameterizedType) {
170                    ParameterizedType type = (ParameterizedType)jType;
171                    name += buildGenericTypeName(type, declaringClass, declaringTypes, propertyType, imports);
172            }
173            
174            JavaType javaType = new JavaType(ClassUtil.getPackageName(jClass), name, propertyImplTypeName, null);
175            javaType.addImports(imports);
176            return javaType;
177        }
178    
179        protected ClientType getFromCache(String key, PropertyType propertyType) {
180            if (key == null)
181                throw new NullPointerException("jType must be non null");
182            if (propertyType == PropertyType.PROPERTY)
183                return propertyJava2JavaType.get(key);
184            return simpleJava2JavaType.get(key);
185        }
186    
187        protected void putInCache(String key, PropertyType propertyType, ClientType javaType) {
188            if (key == null || javaType == null)
189                throw new NullPointerException("jType and JavaType must be non null");
190            if (propertyType == PropertyType.PROPERTY)
191                propertyJava2JavaType.put(key, javaType);
192            else
193                simpleJava2JavaType.put(key, javaType);
194        }
195        
196        private String buildGenericTypeName(ParameterizedType type, Class<?> declaringClass, ParameterizedType[] declaringTypes, PropertyType propertyType, Set<String> imports) {
197                    StringBuilder sb = new StringBuilder("<");
198                    boolean first = true;
199                    for (Type ata : type.getActualTypeArguments()) {
200                            if (first)
201                                    first = false;
202                            else
203                                    sb.append(", ");
204                            if (ata instanceof TypeVariable<?>) {
205                                    Type resolved = GenericTypeUtil.resolveTypeVariable(ata, declaringClass, declaringTypes);
206                                    if (resolved instanceof TypeVariable<?>)
207                                            sb.append("?");
208                                    else {
209                                            sb.append(ClassUtil.classOfType(resolved).getSimpleName());
210                                            imports.add(getClientType(resolved, declaringClass, declaringTypes, propertyType).getQualifiedName());
211                                    }
212                            }
213                            else if (ata instanceof WildcardType) {
214                                    sb.append("?");
215                                    if (((WildcardType)ata).getLowerBounds().length > 0) {
216                                            String bounds = "";
217                                    for (Type t : ((WildcardType)ata).getLowerBounds()) {
218                                            Type resolved = GenericTypeUtil.resolveTypeVariable(t, declaringClass, declaringTypes);
219                                            if (resolved instanceof TypeVariable<?>) {
220                                                    bounds = "";
221                                                    break;
222                                            }
223                                            if (bounds.length() > 0)
224                                                    bounds = bounds + ", ";
225                                            bounds = bounds + ClassUtil.classOfType(resolved).getSimpleName();
226                                            imports.add(getClientType(resolved, declaringClass, declaringTypes, propertyType).getQualifiedName());
227                                    }
228                                    if (bounds.length() > 0)
229                                            sb.append(" super ").append(bounds);
230                                    }
231                                    if (((WildcardType)ata).getUpperBounds().length > 0) {
232                                            String bounds = "";
233                                    for (Type t : ((WildcardType)ata).getUpperBounds()) {
234                                            Type resolved = GenericTypeUtil.resolveTypeVariable(t, declaringClass, declaringTypes);
235                                            if (resolved instanceof TypeVariable<?>) {
236                                                    bounds = "";
237                                                    break;
238                                            }
239                                            if (bounds.length() > 0)
240                                                    bounds = bounds + ", ";
241                                            bounds = bounds + ClassUtil.classOfType(resolved).getSimpleName();
242                                            imports.add(getClientType(resolved, declaringClass, declaringTypes, propertyType).getQualifiedName());
243                                    }
244                                    if (bounds.length() > 0)
245                                            sb.append(" extends ").append(bounds);
246                                    }
247                            }
248                            else {
249                                    sb.append(ClassUtil.classOfType(ata).getSimpleName());
250                                    imports.add(getClientType(ata, declaringClass, declaringTypes, propertyType).getQualifiedName());
251                            }
252                    }
253                    sb.append(">");
254                    return sb.toString();
255        }
256    }