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