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}