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