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 }