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.javafx; 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 DefaultJavaFXTypeFactory implements As3TypeFactory { 046 047 /////////////////////////////////////////////////////////////////////////// 048 // Fields. 049 050 private final Map<String, ClientType> simpleJava2JavaFXType = new HashMap<String, ClientType>(); 051 private final Map<String, ClientType> propertyJava2JavaFXType = new HashMap<String, ClientType>(); 052 private final Map<String, ClientType> readOnlyPropertyJava2JavaFXType = new HashMap<String, ClientType>(); 053 054 055 /////////////////////////////////////////////////////////////////////////// 056 // Constructors. 057 058 public DefaultJavaFXTypeFactory() { 059 simpleJava2JavaFXType.put(buildCacheKey(Boolean.TYPE), JavaFXType.BOOLEAN); 060 simpleJava2JavaFXType.put(buildCacheKey(Integer.TYPE), JavaFXType.INT); 061 simpleJava2JavaFXType.put(buildCacheKey(Long.TYPE), JavaFXType.LONG); 062 simpleJava2JavaFXType.put(buildCacheKey(Float.TYPE), JavaFXType.FLOAT); 063 simpleJava2JavaFXType.put(buildCacheKey(Double.TYPE), JavaFXType.DOUBLE); 064 simpleJava2JavaFXType.put(buildCacheKey(String.class), JavaFXType.STRING); 065 066 propertyJava2JavaFXType.put(buildCacheKey(Boolean.TYPE), JavaFXType.BOOLEAN_PROPERTY); 067 propertyJava2JavaFXType.put(buildCacheKey(Double.TYPE), JavaFXType.DOUBLE_PROPERTY); 068 propertyJava2JavaFXType.put(buildCacheKey(Float.TYPE), JavaFXType.FLOAT_PROPERTY); 069 propertyJava2JavaFXType.put(buildCacheKey(Long.TYPE), JavaFXType.LONG_PROPERTY); 070 propertyJava2JavaFXType.put(buildCacheKey(Integer.TYPE), JavaFXType.INT_PROPERTY); 071 propertyJava2JavaFXType.put(buildCacheKey(String.class), JavaFXType.STRING_PROPERTY); 072 073 readOnlyPropertyJava2JavaFXType.put(buildCacheKey(Boolean.TYPE), JavaFXType.BOOLEAN_READONLY_PROPERTY); 074 readOnlyPropertyJava2JavaFXType.put(buildCacheKey(Double.TYPE), JavaFXType.DOUBLE_READONLY_PROPERTY); 075 readOnlyPropertyJava2JavaFXType.put(buildCacheKey(Float.TYPE), JavaFXType.FLOAT_READONLY_PROPERTY); 076 readOnlyPropertyJava2JavaFXType.put(buildCacheKey(Long.TYPE), JavaFXType.LONG_READONLY_PROPERTY); 077 readOnlyPropertyJava2JavaFXType.put(buildCacheKey(Integer.TYPE), JavaFXType.INT_READONLY_PROPERTY); 078 readOnlyPropertyJava2JavaFXType.put(buildCacheKey(String.class), JavaFXType.STRING_READONLY_PROPERTY); 079 } 080 081 /////////////////////////////////////////////////////////////////////////// 082 // Fields. 083 084 @Override 085 public void configure(boolean externalizeLong, boolean externalizeBigInteger, boolean externalizeBigDecimal) { 086 } 087 088 private static final String buildCacheKey(Type jType) { 089 return buildCacheKey(jType, null, null); 090 } 091 private static final String buildCacheKey(Type jType, Class<?> declaringClass, ParameterizedType[] declaringTypes) { 092 String key = jType.toString(); 093 if (declaringClass != null) 094 key += "::" + declaringClass.toString(); 095 if (declaringTypes != null) { 096 for (ParameterizedType dt : declaringTypes) 097 key += "::" + dt.toString(); 098 } 099 return key; 100 } 101 102 @Override 103 public ClientType getClientType(Type jType, Class<?> declaringClass, ParameterizedType[] declaringTypes, PropertyType propertyType) { 104 String key = buildCacheKey(jType, declaringClass, declaringTypes); 105 106 ClientType javafxType = getFromCache(key, propertyType); 107 108 if (javafxType == null) { 109 if (jType instanceof GenericArrayType) { 110 Type componentType = ((GenericArrayType)jType).getGenericComponentType(); 111 javafxType = getClientType(componentType, declaringClass, declaringTypes, PropertyType.SIMPLE).toArrayType(); 112 } 113 else if (jType instanceof Class<?> && ((Class<?>)jType).isArray()) { 114 javafxType = getClientType(((Class<?>)jType).getComponentType(), declaringClass, declaringTypes, PropertyType.SIMPLE).toArrayType(); 115 } 116 else { 117 Set<String> imports = new HashSet<String>(); 118 Class<?> jClass = ClassUtil.classOfType(jType); 119 String genericType = ""; 120 if (jType instanceof ParameterizedType) 121 genericType = buildGenericTypeName((ParameterizedType)jType, declaringClass, declaringTypes, propertyType, imports); 122 123 if (propertyType.isProperty() && List.class.isAssignableFrom(jClass)) { 124 imports.add("org.granite.client.persistence.collection.javafx.FXPersistentCollections"); 125 javafxType = new JavaFXType("javafx.collections", "ObservableList" + genericType, "javafx.beans.property.ReadOnlyListProperty" + genericType, "javafx.beans.property.ReadOnlyListWrapper" + genericType, "FXPersistentCollections.readOnlyObservablePersistentList", null, true); 126 } 127 else if (propertyType.isProperty() && SortedSet.class.isAssignableFrom(jClass)) { 128 imports.add("org.granite.client.persistence.collection.javafx.FXPersistentCollections"); 129 javafxType = new JavaFXType("javafx.collections", "ObservableSet" + genericType, "javafx.beans.property.ReadOnlySetProperty" + genericType, "javafx.beans.property.ReadOnlySetWrapper" + genericType, "FXPersistentCollections.readOnlyObservablePersistentSortedSet", null, true); 130 } 131 else if (propertyType.isProperty() && Set.class.isAssignableFrom(jClass)) { 132 imports.add("org.granite.client.persistence.collection.javafx.FXPersistentCollections"); 133 javafxType = new JavaFXType("javafx.collections", "ObservableSet" + genericType, "javafx.beans.property.ReadOnlySetProperty" + genericType, "javafx.beans.property.ReadOnlySetWrapper" + genericType, "FXPersistentCollections.readOnlyObservablePersistentSet", null, true); 134 } 135 else if (propertyType.isProperty() && SortedMap.class.isAssignableFrom(jClass)) { 136 imports.add("org.granite.client.persistence.collection.javafx.FXPersistentCollections"); 137 javafxType = new JavaFXType("javafx.collections", "ObservableMap" + genericType, "javafx.beans.property.ReadOnlyMapProperty" + genericType, "javafx.beans.property.ReadOnlyMapWrapper" + genericType, "FXPersistentCollections.readOnlyObservablePersistentSortedMap", null, true); 138 } 139 else if (propertyType.isProperty() && Map.class.isAssignableFrom(jClass)) { 140 imports.add("org.granite.client.persistence.collection.javafx.FXPersistentCollections"); 141 javafxType = new JavaFXType("javafx.collections", "ObservableMap" + genericType, "javafx.beans.property.ReadOnlyMapProperty" + genericType, "javafx.beans.property.ReadOnlyMapWrapper" + genericType, "FXPersistentCollections.readOnlyObservablePersistentMap", null, true); 142 } 143 else if (jClass.getName().equals("com.google.appengine.api.datastore.Key")) { 144 javafxType = JavaFXType.STRING; 145 } 146 else if (jClass.getName().equals("org.springframework.data.domain.Page")) { 147 javafxType = new JavaFXType("org.granite.tide.data.model", "Page" + genericType, null); 148 } 149 else if (jClass.getName().equals("org.springframework.data.domain.Pageable")) { 150 javafxType = JavaFXType.PAGE_INFO; 151 } 152 else if (jClass.getName().equals("org.springframework.data.domain.Sort")) { 153 javafxType = JavaFXType.SORT_INFO; 154 } 155 else { 156 javafxType = createJavaFXType(jType, declaringClass, declaringTypes, propertyType); 157 } 158 if (!imports.isEmpty()) 159 javafxType.addImports(imports); 160 } 161 162 putInCache(key, propertyType, javafxType); 163 } 164 165 return javafxType; 166 } 167 168 @Override 169 public ClientType getAs3Type(Class<?> jType) { 170 return getClientType(jType, null, null, PropertyType.SIMPLE); 171 } 172 173 protected JavaFXType createJavaFXType(Type jType, Class<?> declaringClass, ParameterizedType[] declaringTypes, PropertyType propertyType) { 174 Set<String> imports = new HashSet<String>(); 175 176 Class<?> jClass = ClassUtil.classOfType(jType); 177 String name = jClass.getSimpleName(); 178 if (jClass.isMemberClass()) 179 name = jClass.getEnclosingClass().getSimpleName() + '$' + jClass.getSimpleName(); 180 else if (jType instanceof ParameterizedType) { 181 ParameterizedType type = (ParameterizedType)jType; 182 name += buildGenericTypeName(type, declaringClass, declaringTypes, propertyType, imports); 183 } 184 185 JavaFXType javaFXType = null; 186 if (propertyType == PropertyType.PROPERTY) { 187 javaFXType = new JavaFXType(ClassUtil.getPackageName(jClass), name, 188 "javafx.beans.property.ObjectProperty<" + name + ">", "javafx.beans.property.SimpleObjectProperty<" + name + ">", null); 189 } 190 else if (propertyType == PropertyType.READONLY_PROPERTY) { 191 javaFXType = new JavaFXType(ClassUtil.getPackageName(jClass), name, 192 "javafx.beans.property.ReadOnlyObjectProperty<" + name + ">", "javafx.beans.property.ReadOnlyObjectWrapper<" + name + ">", null, null, true); 193 } 194 else 195 javaFXType = new JavaFXType(ClassUtil.getPackageName(jClass), name, null); 196 javaFXType.addImports(imports); 197 return javaFXType; 198 } 199 200 protected ClientType getFromCache(String key, PropertyType propertyType) { 201 if (key == null) 202 throw new NullPointerException("jType must be non null"); 203 if (propertyType == PropertyType.PROPERTY) 204 return propertyJava2JavaFXType.get(key); 205 else if (propertyType == PropertyType.READONLY_PROPERTY) 206 return readOnlyPropertyJava2JavaFXType.get(key); 207 return simpleJava2JavaFXType.get(key); 208 } 209 210 protected void putInCache(String key, PropertyType propertyType, ClientType javafxType) { 211 if (key == null || javafxType == null) 212 throw new NullPointerException("jType and JavaFXType must be non null"); 213 if (propertyType == PropertyType.PROPERTY) 214 propertyJava2JavaFXType.put(key, javafxType); 215 else if (propertyType == PropertyType.READONLY_PROPERTY) 216 readOnlyPropertyJava2JavaFXType.put(key, javafxType); 217 else 218 simpleJava2JavaFXType.put(key, javafxType); 219 } 220 221 private String buildGenericTypeName(ParameterizedType type, Class<?> declaringClass, ParameterizedType[] declaringTypes, PropertyType propertyType, Set<String> imports) { 222 StringBuilder sb = new StringBuilder("<"); 223 boolean first = true; 224 for (Type ata : type.getActualTypeArguments()) { 225 if (first) 226 first = false; 227 else 228 sb.append(", "); 229 if (ata instanceof TypeVariable<?>) { 230 Type resolved = GenericTypeUtil.resolveTypeVariable(ata, declaringClass, declaringTypes); 231 if (resolved instanceof TypeVariable<?>) 232 sb.append("?"); 233 else { 234 sb.append(ClassUtil.classOfType(resolved).getSimpleName()); 235 imports.add(getClientType(resolved, declaringClass, declaringTypes, propertyType).getQualifiedName()); 236 } 237 } 238 else if (ata instanceof WildcardType) { 239 sb.append("?"); 240 if (((WildcardType)ata).getLowerBounds().length > 0) { 241 String bounds = ""; 242 for (Type t : ((WildcardType)ata).getLowerBounds()) { 243 Type resolved = GenericTypeUtil.resolveTypeVariable(t, declaringClass, declaringTypes); 244 if (resolved instanceof TypeVariable<?>) { 245 bounds = ""; 246 break; 247 } 248 if (bounds.length() > 0) 249 bounds = bounds + ", "; 250 bounds = bounds + ClassUtil.classOfType(resolved).getSimpleName(); 251 imports.add(getClientType(resolved, declaringClass, declaringTypes, propertyType).getQualifiedName()); 252 } 253 if (bounds.length() > 0) 254 sb.append(" super ").append(bounds); 255 } 256 if (((WildcardType)ata).getUpperBounds().length > 0) { 257 String bounds = ""; 258 for (Type t : ((WildcardType)ata).getUpperBounds()) { 259 Type resolved = GenericTypeUtil.resolveTypeVariable(t, declaringClass, declaringTypes); 260 if (resolved instanceof TypeVariable<?>) { 261 bounds = ""; 262 break; 263 } 264 if (bounds.length() > 0) 265 bounds = bounds + ", "; 266 bounds = bounds + ClassUtil.classOfType(resolved).getSimpleName(); 267 imports.add(getClientType(resolved, declaringClass, declaringTypes, propertyType).getQualifiedName()); 268 } 269 if (bounds.length() > 0) 270 sb.append(" extends ").append(bounds); 271 } 272 } 273 else { 274 sb.append(ClassUtil.classOfType(ata).getSimpleName()); 275 imports.add(getClientType(ata, declaringClass, declaringTypes, propertyType).getQualifiedName()); 276 } 277 } 278 sb.append(">"); 279 return sb.toString(); 280 } 281}