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
023 package org.granite.generator.javafx;
024
025 import java.lang.reflect.GenericArrayType;
026 import java.lang.reflect.ParameterizedType;
027 import java.lang.reflect.Type;
028 import java.lang.reflect.TypeVariable;
029 import java.lang.reflect.WildcardType;
030 import java.util.HashMap;
031 import java.util.HashSet;
032 import java.util.List;
033 import java.util.Map;
034 import java.util.Set;
035 import java.util.SortedMap;
036 import java.util.SortedSet;
037
038 import org.granite.generator.as3.As3TypeFactory;
039 import org.granite.generator.as3.ClientType;
040 import org.granite.generator.as3.PropertyType;
041 import org.granite.generator.util.GenericTypeUtil;
042 import org.granite.util.ClassUtil;
043
044 /**
045 * @author Franck WOLFF
046 */
047 public 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("key 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("key 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 }