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