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