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}