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 023package org.granite.generator.java; 024 025import java.lang.reflect.GenericArrayType; 026import java.lang.reflect.ParameterizedType; 027import java.lang.reflect.Type; 028import java.lang.reflect.TypeVariable; 029import java.lang.reflect.WildcardType; 030import java.util.HashMap; 031import java.util.HashSet; 032import java.util.List; 033import java.util.Map; 034import java.util.Set; 035import java.util.SortedMap; 036import java.util.SortedSet; 037 038import org.granite.generator.as3.As3TypeFactory; 039import org.granite.generator.as3.ClientType; 040import org.granite.generator.as3.PropertyType; 041import org.granite.generator.util.GenericTypeUtil; 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 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}