@@ -9,6 +9,7 @@ import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Map.Entry; | |||
@@ -43,6 +44,8 @@ import com.vaadin.terminal.gwt.client.metadata.TypeDataStore; | |||
import com.vaadin.terminal.gwt.widgetsetutils.metadata.ClientRpcVisitor; | |||
import com.vaadin.terminal.gwt.widgetsetutils.metadata.ConnectorBundle; | |||
import com.vaadin.terminal.gwt.widgetsetutils.metadata.ConnectorInitVisitor; | |||
import com.vaadin.terminal.gwt.widgetsetutils.metadata.GeneratedSerializer; | |||
import com.vaadin.terminal.gwt.widgetsetutils.metadata.Property; | |||
import com.vaadin.terminal.gwt.widgetsetutils.metadata.ServerRpcVisitor; | |||
import com.vaadin.terminal.gwt.widgetsetutils.metadata.StateInitVisitor; | |||
import com.vaadin.terminal.gwt.widgetsetutils.metadata.TypeVisitor; | |||
@@ -134,7 +137,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
w.println("public void load() {"); | |||
w.indent(); | |||
printBundleData(w, bundle); | |||
printBundleData(logger, w, bundle); | |||
// Close load method | |||
w.outdent(); | |||
@@ -165,7 +168,8 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
w.commit(logger); | |||
} | |||
private void printBundleData(SourceWriter w, ConnectorBundle bundle) { | |||
private void printBundleData(TreeLogger logger, SourceWriter w, | |||
ConnectorBundle bundle) throws UnableToCompleteException { | |||
writeIdentifiers(w, bundle); | |||
writeGwtConstructors(w, bundle); | |||
writeReturnTypes(w, bundle); | |||
@@ -173,6 +177,133 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
writeParamTypes(w, bundle); | |||
writeProxys(w, bundle); | |||
wirteDelayedInfo(w, bundle); | |||
writeProperites(logger, w, bundle); | |||
writePropertyTypes(w, bundle); | |||
writeSetters(logger, w, bundle); | |||
writeGetters(logger, w, bundle); | |||
writeSerializers(logger, w, bundle); | |||
} | |||
private void writeSerializers(TreeLogger logger, SourceWriter w, | |||
ConnectorBundle bundle) throws UnableToCompleteException { | |||
Map<JType, GeneratedSerializer> serializers = bundle.getSerializers(); | |||
for (Entry<JType, GeneratedSerializer> entry : serializers.entrySet()) { | |||
JType type = entry.getKey(); | |||
GeneratedSerializer serializer = entry.getValue(); | |||
w.print("store.setSerializerFactory("); | |||
writeClassLiteral(w, type); | |||
w.print(", "); | |||
w.println("new Invoker() {"); | |||
w.indent(); | |||
w.println("public Object invoke(Object target, Object[] params) {"); | |||
w.indent(); | |||
serializer.writeSerializerInstantiator(logger, w); | |||
w.outdent(); | |||
w.println("}"); | |||
w.outdent(); | |||
w.print("}"); | |||
w.println(");"); | |||
} | |||
} | |||
private void writeGetters(TreeLogger logger, SourceWriter w, | |||
ConnectorBundle bundle) { | |||
Set<Property> properties = bundle.getNeedsSetter(); | |||
for (Property property : properties) { | |||
w.print("store.setGetter("); | |||
writeClassLiteral(w, property.getBeanType()); | |||
w.print(", \""); | |||
w.print(escape(property.getName())); | |||
w.print("\", new Invoker() {"); | |||
w.indent(); | |||
w.println("public Object invoke(Object bean, Object[] params) {"); | |||
w.indent(); | |||
property.writeGetterBody(logger, w, "bean"); | |||
w.println(); | |||
w.outdent(); | |||
w.println("}"); | |||
w.outdent(); | |||
w.println("});"); | |||
} | |||
} | |||
private void writeSetters(TreeLogger logger, SourceWriter w, | |||
ConnectorBundle bundle) { | |||
Set<Property> properties = bundle.getNeedsSetter(); | |||
for (Property property : properties) { | |||
w.print("store.setSetter("); | |||
writeClassLiteral(w, property.getBeanType()); | |||
w.print(", \""); | |||
w.print(escape(property.getName())); | |||
w.println("\", new Invoker() {"); | |||
w.indent(); | |||
w.println("public Object invoke(Object bean, Object[] params) {"); | |||
w.indent(); | |||
property.writeSetterBody(logger, w, "bean", "params[0]"); | |||
w.println("return null;"); | |||
w.outdent(); | |||
w.println("}"); | |||
w.outdent(); | |||
w.println("});"); | |||
} | |||
} | |||
private void writePropertyTypes(SourceWriter w, ConnectorBundle bundle) { | |||
Set<Property> properties = bundle.getNeedsType(); | |||
for (Property property : properties) { | |||
w.print("store.setPropertyType("); | |||
writeClassLiteral(w, property.getBeanType()); | |||
w.print(", \""); | |||
w.print(escape(property.getName())); | |||
w.print("\", "); | |||
writeTypeCreator(w, property.getPropertyType()); | |||
w.println(");"); | |||
} | |||
} | |||
private void writeProperites(TreeLogger logger, SourceWriter w, | |||
ConnectorBundle bundle) throws UnableToCompleteException { | |||
Set<JClassType> needsPropertyListing = bundle.getNeedsPropertyListing(); | |||
for (JClassType type : needsPropertyListing) { | |||
w.print("store.setProperties("); | |||
writeClassLiteral(w, type); | |||
w.print(", new String[] {"); | |||
Set<String> usedPropertyNames = new HashSet<String>(); | |||
Collection<Property> properties = bundle.getProperties(type); | |||
for (Property property : properties) { | |||
String name = property.getName(); | |||
if (!usedPropertyNames.add(name)) { | |||
logger.log( | |||
Type.ERROR, | |||
type.getQualifiedSourceName() | |||
+ " has multiple properties with the name " | |||
+ name | |||
+ ". This can happen if there are multiple setters with identical names exect casing."); | |||
throw new UnableToCompleteException(); | |||
} | |||
w.print("\""); | |||
w.print(name); | |||
w.print("\", "); | |||
} | |||
w.println("});"); | |||
} | |||
} | |||
private void wirteDelayedInfo(SourceWriter w, ConnectorBundle bundle) { | |||
@@ -187,14 +318,14 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
Delayed annotation = method.getAnnotation(Delayed.class); | |||
if (annotation != null) { | |||
w.print("store.setDelayed("); | |||
printClassLiteral(w, type); | |||
writeClassLiteral(w, type); | |||
w.print(", \""); | |||
w.print(escape(method.getName())); | |||
w.println("\");"); | |||
if (annotation.lastonly()) { | |||
w.print("store.setLastonly("); | |||
printClassLiteral(w, type); | |||
writeClassLiteral(w, type); | |||
w.print(", \""); | |||
w.print(escape(method.getName())); | |||
w.println("\");"); | |||
@@ -208,7 +339,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
Set<JClassType> needsProxySupport = bundle.getNeedsProxySupport(); | |||
for (JClassType type : needsProxySupport) { | |||
w.print("store.setProxyHandler("); | |||
printClassLiteral(w, type); | |||
writeClassLiteral(w, type); | |||
w.print(", new "); | |||
w.print(ProxyHandler.class.getCanonicalName()); | |||
w.println("() {"); | |||
@@ -253,7 +384,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
w.print("handler.invoke(this, "); | |||
w.print(TypeData.class.getCanonicalName()); | |||
w.print(".getType("); | |||
printClassLiteral(w, type); | |||
writeClassLiteral(w, type); | |||
w.print(").getMethod(\""); | |||
w.print(escape(method.getName())); | |||
w.print("\"), new Object [] {"); | |||
@@ -288,7 +419,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
Set<JMethod> methods = entry.getValue(); | |||
for (JMethod method : methods) { | |||
w.print("store.setParamTypes("); | |||
printClassLiteral(w, type); | |||
writeClassLiteral(w, type); | |||
w.print(", \""); | |||
w.print(escape(method.getName())); | |||
w.print("\", new Type[] {"); | |||
@@ -312,7 +443,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
Set<JMethod> methods = entry.getValue(); | |||
for (JMethod method : methods) { | |||
w.print("store.setInvoker("); | |||
printClassLiteral(w, type); | |||
writeClassLiteral(w, type); | |||
w.print(", \""); | |||
w.print(escape(method.getName())); | |||
w.println("\", new Invoker() {"); | |||
@@ -368,7 +499,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
// setReturnType(Class<?> type, String methodName, Type | |||
// returnType) | |||
w.print("store.setReturnType("); | |||
printClassLiteral(w, type); | |||
writeClassLiteral(w, type); | |||
w.print(", \""); | |||
w.print(escape(method.getName())); | |||
w.print("\", "); | |||
@@ -382,7 +513,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
Set<JClassType> constructors = bundle.getGwtConstructors(); | |||
for (JClassType type : constructors) { | |||
w.print("store.setConstructor("); | |||
printClassLiteral(w, type); | |||
writeClassLiteral(w, type); | |||
w.println(", new Invoker() {"); | |||
w.indent(); | |||
@@ -392,7 +523,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
w.print("return "); | |||
w.print(GWT.class.getName()); | |||
w.print(".create("); | |||
printClassLiteral(w, type); | |||
writeClassLiteral(w, type); | |||
w.println(");"); | |||
w.outdent(); | |||
@@ -403,7 +534,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
} | |||
} | |||
private void printClassLiteral(SourceWriter w, JClassType type) { | |||
public static void writeClassLiteral(SourceWriter w, JType type) { | |||
w.print(type.getQualifiedSourceName()); | |||
w.print(".class"); | |||
} | |||
@@ -417,7 +548,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
w.print("store.setClass(\""); | |||
w.print(escape(id)); | |||
w.print("\", "); | |||
printClassLiteral(w, type); | |||
writeClassLiteral(w, type); | |||
w.println(");"); | |||
} | |||
} | |||
@@ -450,7 +581,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
Collection<TypeVisitor> visitors = getVisitors(typeOracle); | |||
ConnectorBundle eagerBundle = new ConnectorBundle( | |||
ConnectorBundleLoader.EAGER_BUNDLE_NAME, visitors); | |||
ConnectorBundleLoader.EAGER_BUNDLE_NAME, visitors, typeOracle); | |||
TreeLogger eagerLogger = logger.branch(Type.TRACE, | |||
"Populating eager bundle"); | |||
@@ -515,9 +646,9 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
public static void writeTypeCreator(SourceWriter sourceWriter, JType type) { | |||
String typeName = ConnectorBundleLoaderFactory.getBoxedTypeName(type); | |||
sourceWriter.print("new Type(\"" + typeName + "\", "); | |||
JParameterizedType parameterized = type.isParameterized(); | |||
if (parameterized != null) { | |||
sourceWriter.print("new Type(\"" + typeName + "\", "); | |||
sourceWriter.print("new Type[] {"); | |||
JClassType[] typeArgs = parameterized.getTypeArgs(); | |||
for (JClassType jClassType : typeArgs) { | |||
@@ -526,7 +657,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
} | |||
sourceWriter.print("}"); | |||
} else { | |||
sourceWriter.print("null"); | |||
sourceWriter.print("new Type(" + typeName + ".class"); | |||
} | |||
sourceWriter.print(")"); | |||
} |
@@ -1,484 +0,0 @@ | |||
/* | |||
* Copyright 2011 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils; | |||
import java.io.PrintWriter; | |||
import java.util.ArrayList; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import com.google.gwt.core.client.GWT; | |||
import com.google.gwt.core.ext.Generator; | |||
import com.google.gwt.core.ext.GeneratorContext; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.TreeLogger.Type; | |||
import com.google.gwt.core.ext.UnableToCompleteException; | |||
import com.google.gwt.core.ext.typeinfo.JArrayType; | |||
import com.google.gwt.core.ext.typeinfo.JClassType; | |||
import com.google.gwt.core.ext.typeinfo.JEnumConstant; | |||
import com.google.gwt.core.ext.typeinfo.JEnumType; | |||
import com.google.gwt.core.ext.typeinfo.JMethod; | |||
import com.google.gwt.core.ext.typeinfo.JPackage; | |||
import com.google.gwt.core.ext.typeinfo.JPrimitiveType; | |||
import com.google.gwt.core.ext.typeinfo.JType; | |||
import com.google.gwt.core.ext.typeinfo.TypeOracleException; | |||
import com.google.gwt.json.client.JSONArray; | |||
import com.google.gwt.json.client.JSONObject; | |||
import com.google.gwt.json.client.JSONString; | |||
import com.google.gwt.json.client.JSONValue; | |||
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; | |||
import com.google.gwt.user.rebind.SourceWriter; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.communication.DiffJSONSerializer; | |||
import com.vaadin.terminal.gwt.client.communication.JSONSerializer; | |||
import com.vaadin.terminal.gwt.client.communication.JsonDecoder; | |||
import com.vaadin.terminal.gwt.client.communication.JsonEncoder; | |||
import com.vaadin.terminal.gwt.client.communication.SerializerMap; | |||
/** | |||
* GWT generator for creating serializer classes for custom classes sent from | |||
* server to client. | |||
* | |||
* Only fields with a correspondingly named setter are deserialized. | |||
* | |||
* @since 7.0 | |||
*/ | |||
public class SerializerGenerator extends Generator { | |||
private static final String SUBTYPE_SEPARATOR = "___"; | |||
@Override | |||
public String generate(TreeLogger logger, GeneratorContext context, | |||
String typeName) throws UnableToCompleteException { | |||
JClassType type; | |||
try { | |||
type = (JClassType) context.getTypeOracle().parse(typeName); | |||
} catch (TypeOracleException e1) { | |||
logger.log(Type.ERROR, "Could not find type " + typeName, e1); | |||
throw new UnableToCompleteException(); | |||
} | |||
String serializerClassName = getSerializerSimpleClassName(type); | |||
try { | |||
// Generate class source code | |||
generateClass(logger, context, type, | |||
getSerializerPackageName(type), serializerClassName); | |||
} catch (Exception e) { | |||
logger.log(TreeLogger.ERROR, "SerializerGenerator failed for " | |||
+ type.getQualifiedSourceName(), e); | |||
throw new UnableToCompleteException(); | |||
} | |||
// return the fully qualifed name of the class generated | |||
return getFullyQualifiedSerializerClassName(type); | |||
} | |||
/** | |||
* Generate source code for a VaadinSerializer implementation. | |||
* | |||
* @param logger | |||
* Logger object | |||
* @param context | |||
* Generator context | |||
* @param type | |||
* @param beanTypeName | |||
* bean type for which the serializer is to be generated | |||
* @param beanSerializerTypeName | |||
* name of the serializer class to generate | |||
* @throws UnableToCompleteException | |||
*/ | |||
private void generateClass(TreeLogger logger, GeneratorContext context, | |||
JClassType type, String serializerPackageName, | |||
String serializerClassName) throws UnableToCompleteException { | |||
// get print writer that receives the source code | |||
PrintWriter printWriter = null; | |||
printWriter = context.tryCreate(logger, serializerPackageName, | |||
serializerClassName); | |||
// print writer if null, source code has ALREADY been generated | |||
if (printWriter == null) { | |||
return; | |||
} | |||
boolean isEnum = (type.isEnum() != null); | |||
boolean isArray = (type.isArray() != null); | |||
String qualifiedSourceName = type.getQualifiedSourceName(); | |||
logger.log(Type.DEBUG, "Processing serializable type " | |||
+ qualifiedSourceName + "..."); | |||
// init composer, set class properties, create source writer | |||
ClassSourceFileComposerFactory composer = null; | |||
composer = new ClassSourceFileComposerFactory(serializerPackageName, | |||
serializerClassName); | |||
composer.addImport(GWT.class.getName()); | |||
composer.addImport(JSONValue.class.getName()); | |||
composer.addImport(com.vaadin.terminal.gwt.client.metadata.Type.class | |||
.getName()); | |||
// composer.addImport(JSONObject.class.getName()); | |||
// composer.addImport(VPaintableMap.class.getName()); | |||
composer.addImport(JsonDecoder.class.getName()); | |||
// composer.addImport(VaadinSerializer.class.getName()); | |||
if (isEnum || isArray) { | |||
composer.addImplementedInterface(JSONSerializer.class.getName() | |||
+ "<" + qualifiedSourceName + ">"); | |||
} else { | |||
composer.addImplementedInterface(DiffJSONSerializer.class.getName() | |||
+ "<" + qualifiedSourceName + ">"); | |||
} | |||
SourceWriter sourceWriter = composer.createSourceWriter(context, | |||
printWriter); | |||
sourceWriter.indent(); | |||
// Serializer | |||
// public JSONValue serialize(Object value, | |||
// ApplicationConnection connection) { | |||
sourceWriter.println("public " + JSONValue.class.getName() | |||
+ " serialize(" + qualifiedSourceName + " value, " | |||
+ ApplicationConnection.class.getName() + " connection) {"); | |||
sourceWriter.indent(); | |||
// MouseEventDetails castedValue = (MouseEventDetails) value; | |||
sourceWriter.println(qualifiedSourceName + " castedValue = (" | |||
+ qualifiedSourceName + ") value;"); | |||
if (isEnum) { | |||
writeEnumSerializer(logger, sourceWriter, type); | |||
} else if (isArray) { | |||
writeArraySerializer(logger, sourceWriter, type.isArray()); | |||
} else { | |||
writeBeanSerializer(logger, sourceWriter, type); | |||
} | |||
// } | |||
sourceWriter.outdent(); | |||
sourceWriter.println("}"); | |||
sourceWriter.println(); | |||
// Updater | |||
// public void update(T target, Type type, JSONValue jsonValue, | |||
// ApplicationConnection connection); | |||
if (!isEnum && !isArray) { | |||
sourceWriter.println("public void update(" + qualifiedSourceName | |||
+ " target, Type type, " + JSONValue.class.getName() | |||
+ " jsonValue, " + ApplicationConnection.class.getName() | |||
+ " connection) {"); | |||
sourceWriter.indent(); | |||
writeBeanDeserializer(logger, sourceWriter, type); | |||
sourceWriter.outdent(); | |||
sourceWriter.println("}"); | |||
} | |||
// Deserializer | |||
// T deserialize(Type type, JSONValue jsonValue, ApplicationConnection | |||
// connection); | |||
sourceWriter.println("public " + qualifiedSourceName | |||
+ " deserialize(Type type, " + JSONValue.class.getName() | |||
+ " jsonValue, " + ApplicationConnection.class.getName() | |||
+ " connection) {"); | |||
sourceWriter.indent(); | |||
if (isEnum) { | |||
writeEnumDeserializer(logger, sourceWriter, type.isEnum()); | |||
} else if (isArray) { | |||
writeArrayDeserializer(logger, sourceWriter, type.isArray()); | |||
} else { | |||
sourceWriter.println(qualifiedSourceName + " target = GWT.create(" | |||
+ qualifiedSourceName + ".class);"); | |||
sourceWriter | |||
.println("update(target, type, jsonValue, connection);"); | |||
// return target; | |||
sourceWriter.println("return target;"); | |||
} | |||
sourceWriter.outdent(); | |||
sourceWriter.println("}"); | |||
// End of class | |||
sourceWriter.outdent(); | |||
sourceWriter.println("}"); | |||
// commit generated class | |||
context.commit(logger, printWriter); | |||
logger.log(TreeLogger.INFO, "Generated Serializer class " | |||
+ getFullyQualifiedSerializerClassName(type)); | |||
} | |||
private void writeEnumDeserializer(TreeLogger logger, | |||
SourceWriter sourceWriter, JEnumType enumType) { | |||
sourceWriter.println("String enumIdentifier = ((" | |||
+ JSONString.class.getName() + ")jsonValue).stringValue();"); | |||
for (JEnumConstant e : enumType.getEnumConstants()) { | |||
sourceWriter.println("if (\"" + e.getName() | |||
+ "\".equals(enumIdentifier)) {"); | |||
sourceWriter.indent(); | |||
sourceWriter.println("return " + enumType.getQualifiedSourceName() | |||
+ "." + e.getName() + ";"); | |||
sourceWriter.outdent(); | |||
sourceWriter.println("}"); | |||
} | |||
sourceWriter.println("return null;"); | |||
} | |||
private void writeArrayDeserializer(TreeLogger logger, | |||
SourceWriter sourceWriter, JArrayType type) { | |||
JType leafType = type.getLeafType(); | |||
int rank = type.getRank(); | |||
sourceWriter.println(JSONArray.class.getName() | |||
+ " jsonArray = jsonValue.isArray();"); | |||
// Type value = new Type[jsonArray.size()][][]; | |||
sourceWriter.print(type.getQualifiedSourceName() + " value = new " | |||
+ leafType.getQualifiedSourceName() + "[jsonArray.size()]"); | |||
for (int i = 1; i < rank; i++) { | |||
sourceWriter.print("[]"); | |||
} | |||
sourceWriter.println(";"); | |||
sourceWriter.println("for(int i = 0 ; i < value.length; i++) {"); | |||
sourceWriter.indent(); | |||
JType componentType = type.getComponentType(); | |||
sourceWriter.print("value[i] = (" | |||
+ ConnectorBundleLoaderFactory | |||
.getBoxedTypeName(componentType) + ") " | |||
+ JsonDecoder.class.getName() + ".decodeValue("); | |||
ConnectorBundleLoaderFactory.writeTypeCreator(sourceWriter, | |||
componentType); | |||
sourceWriter.print(", jsonArray.get(i), null, connection)"); | |||
sourceWriter.println(";"); | |||
sourceWriter.outdent(); | |||
sourceWriter.println("}"); | |||
sourceWriter.println("return value;"); | |||
} | |||
private void writeBeanDeserializer(TreeLogger logger, | |||
SourceWriter sourceWriter, JClassType beanType) { | |||
String beanQualifiedSourceName = beanType.getQualifiedSourceName(); | |||
// JSONOBject json = (JSONObject)jsonValue; | |||
sourceWriter.println(JSONObject.class.getName() + " json = (" | |||
+ JSONObject.class.getName() + ")jsonValue;"); | |||
for (JMethod method : getSetters(beanType)) { | |||
String setterName = method.getName(); | |||
String baseName = setterName.substring(3); | |||
String fieldName = getTransportFieldName(baseName); // setZIndex() | |||
// -> zIndex | |||
JType setterParameterType = method.getParameterTypes()[0]; | |||
logger.log(Type.DEBUG, "* Processing field " + fieldName + " in " | |||
+ beanQualifiedSourceName + " (" + beanType.getName() + ")"); | |||
// if (json.containsKey("height")) { | |||
sourceWriter.println("if (json.containsKey(\"" + fieldName | |||
+ "\")) {"); | |||
sourceWriter.indent(); | |||
String jsonFieldName = "json_" + fieldName; | |||
// JSONValue json_Height = json.get("height"); | |||
sourceWriter.println("JSONValue " + jsonFieldName | |||
+ " = json.get(\"" + fieldName + "\");"); | |||
String fieldType; | |||
String getterName = "get" + baseName; | |||
JPrimitiveType primitiveType = setterParameterType.isPrimitive(); | |||
if (primitiveType != null) { | |||
// This is a primitive type -> must used the boxed type | |||
fieldType = primitiveType.getQualifiedBoxedSourceName(); | |||
if (primitiveType == JPrimitiveType.BOOLEAN) { | |||
getterName = "is" + baseName; | |||
} | |||
} else { | |||
fieldType = setterParameterType.getQualifiedSourceName(); | |||
} | |||
// String referenceValue = target.getHeight(); | |||
sourceWriter.println(fieldType + " referenceValue = target." | |||
+ getterName + "();"); | |||
// target.setHeight((String) | |||
// JsonDecoder.decodeValue(jsonFieldValue,referenceValue, idMapper, | |||
// connection)); | |||
sourceWriter.print("target." + setterName + "((" + fieldType + ") " | |||
+ JsonDecoder.class.getName() + ".decodeValue("); | |||
ConnectorBundleLoaderFactory.writeTypeCreator(sourceWriter, | |||
setterParameterType); | |||
sourceWriter.println(", " + jsonFieldName | |||
+ ", referenceValue, connection));"); | |||
// } ... end of if contains | |||
sourceWriter.outdent(); | |||
sourceWriter.println("}"); | |||
} | |||
} | |||
private void writeEnumSerializer(TreeLogger logger, | |||
SourceWriter sourceWriter, JClassType beanType) { | |||
// return new JSONString(castedValue.name()); | |||
sourceWriter.println("return new " + JSONString.class.getName() | |||
+ "(castedValue.name());"); | |||
} | |||
private void writeArraySerializer(TreeLogger logger, | |||
SourceWriter sourceWriter, JArrayType array) { | |||
sourceWriter.println(JSONArray.class.getName() + " values = new " | |||
+ JSONArray.class.getName() + "();"); | |||
JType componentType = array.getComponentType(); | |||
// JPrimitiveType primitive = componentType.isPrimitive(); | |||
sourceWriter.println("for (int i = 0; i < castedValue.length; i++) {"); | |||
sourceWriter.indent(); | |||
sourceWriter.print("values.set(i, "); | |||
sourceWriter.print(JsonEncoder.class.getName() | |||
+ ".encode(castedValue[i], false, connection)"); | |||
sourceWriter.println(");"); | |||
sourceWriter.outdent(); | |||
sourceWriter.println("}"); | |||
sourceWriter.println("return values;"); | |||
} | |||
private void writeBeanSerializer(TreeLogger logger, | |||
SourceWriter sourceWriter, JClassType beanType) | |||
throws UnableToCompleteException { | |||
// JSONObject json = new JSONObject(); | |||
sourceWriter.println(JSONObject.class.getName() + " json = new " | |||
+ JSONObject.class.getName() + "();"); | |||
HashSet<String> usedFieldNames = new HashSet<String>(); | |||
for (JMethod setterMethod : getSetters(beanType)) { | |||
String setterName = setterMethod.getName(); | |||
String fieldName = getTransportFieldName(setterName.substring(3)); // setZIndex() | |||
// -> zIndex | |||
if (!usedFieldNames.add(fieldName)) { | |||
logger.log( | |||
TreeLogger.ERROR, | |||
"Can't encode " | |||
+ beanType.getQualifiedSourceName() | |||
+ " as it has multiple fields with the name " | |||
+ fieldName.toLowerCase() | |||
+ ". This can happen if only casing distinguishes one property name from another."); | |||
throw new UnableToCompleteException(); | |||
} | |||
String getterName = findGetter(beanType, setterMethod); | |||
if (getterName == null) { | |||
logger.log(TreeLogger.ERROR, "No getter found for " + fieldName | |||
+ ". Serialization will likely fail"); | |||
} | |||
// json.put("button", | |||
// JsonEncoder.encode(castedValue.getButton(), false, idMapper, | |||
// connection)); | |||
sourceWriter.println("json.put(\"" + fieldName + "\", " | |||
+ JsonEncoder.class.getName() + ".encode(castedValue." | |||
+ getterName + "(), false, connection));"); | |||
} | |||
// return json; | |||
sourceWriter.println("return json;"); | |||
} | |||
private static String getTransportFieldName(String baseName) { | |||
return Character.toLowerCase(baseName.charAt(0)) | |||
+ baseName.substring(1); | |||
} | |||
private String findGetter(JClassType beanType, JMethod setterMethod) { | |||
JType setterParameterType = setterMethod.getParameterTypes()[0]; | |||
String fieldName = setterMethod.getName().substring(3); | |||
if (setterParameterType.getQualifiedSourceName().equals( | |||
boolean.class.getName())) { | |||
return "is" + fieldName; | |||
} else { | |||
return "get" + fieldName; | |||
} | |||
} | |||
/** | |||
* Returns a list of all setters found in the beanType or its parent class | |||
* | |||
* @param beanType | |||
* The type to check | |||
* @return A list of setter methods from the class and its parents | |||
*/ | |||
protected static List<JMethod> getSetters(JClassType beanType) { | |||
List<JMethod> setterMethods = new ArrayList<JMethod>(); | |||
while (beanType != null | |||
&& !beanType.getQualifiedSourceName().equals( | |||
Object.class.getName())) { | |||
for (JMethod method : beanType.getMethods()) { | |||
// Process all setters that have corresponding fields | |||
if (!method.isPublic() || method.isStatic() | |||
|| !method.getName().startsWith("set") | |||
|| method.getParameterTypes().length != 1) { | |||
// Not setter, skip to next method | |||
continue; | |||
} | |||
setterMethods.add(method); | |||
} | |||
beanType = beanType.getSuperclass(); | |||
} | |||
return setterMethods; | |||
} | |||
private static String getSerializerSimpleClassName(JClassType beanType) { | |||
return getSimpleClassName(beanType) + "_Serializer"; | |||
} | |||
private static String getSimpleClassName(JType type) { | |||
JArrayType arrayType = type.isArray(); | |||
if (arrayType != null) { | |||
return "Array" + getSimpleClassName(arrayType.getComponentType()); | |||
} | |||
JClassType classType = type.isClass(); | |||
if (classType != null && classType.isMemberType()) { | |||
// Assumed to be static sub class | |||
String baseName = getSimpleClassName(classType.getEnclosingType()); | |||
String name = baseName + SUBTYPE_SEPARATOR | |||
+ type.getSimpleSourceName(); | |||
return name; | |||
} | |||
return type.getSimpleSourceName(); | |||
} | |||
public static String getFullyQualifiedSerializerClassName(JClassType type) { | |||
return getSerializerPackageName(type) + "." | |||
+ getSerializerSimpleClassName(type); | |||
} | |||
private static String getSerializerPackageName(JClassType type) { | |||
JPackage typePackage = type.getPackage(); | |||
if (typePackage == null) { | |||
return SerializerMap.class.getPackage().getName(); | |||
} else { | |||
String packageName = typePackage.getName(); | |||
// Dev mode classloader gets unhappy for some java packages | |||
if (packageName.startsWith("java.")) { | |||
packageName = "com.vaadin." + packageName; | |||
} | |||
return packageName; | |||
} | |||
} | |||
} |
@@ -1,377 +0,0 @@ | |||
/* | |||
* Copyright 2011 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils; | |||
import java.io.PrintWriter; | |||
import java.io.Serializable; | |||
import java.util.Date; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import com.google.gwt.core.ext.Generator; | |||
import com.google.gwt.core.ext.GeneratorContext; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.TreeLogger.Type; | |||
import com.google.gwt.core.ext.UnableToCompleteException; | |||
import com.google.gwt.core.ext.typeinfo.JArrayType; | |||
import com.google.gwt.core.ext.typeinfo.JClassType; | |||
import com.google.gwt.core.ext.typeinfo.JMethod; | |||
import com.google.gwt.core.ext.typeinfo.JParameterizedType; | |||
import com.google.gwt.core.ext.typeinfo.JType; | |||
import com.google.gwt.core.ext.typeinfo.NotFoundException; | |||
import com.google.gwt.core.ext.typeinfo.TypeOracle; | |||
import com.google.gwt.json.client.JSONValue; | |||
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; | |||
import com.google.gwt.user.rebind.SourceWriter; | |||
import com.vaadin.shared.communication.ClientRpc; | |||
import com.vaadin.shared.communication.ServerRpc; | |||
import com.vaadin.shared.communication.SharedState; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.communication.JSONSerializer; | |||
import com.vaadin.terminal.gwt.client.communication.SerializerMap; | |||
/** | |||
* GWT generator that creates a {@link SerializerMap} implementation (mapper | |||
* from type string to serializer instance) and serializer classes for all | |||
* subclasses of {@link SharedState}. | |||
* | |||
* @since 7.0 | |||
*/ | |||
public class SerializerMapGenerator extends Generator { | |||
private static final String FAIL_IF_NOT_SERIALIZABLE = "vFailIfNotSerializable"; | |||
private String packageName; | |||
private String className; | |||
@Override | |||
public String generate(TreeLogger logger, GeneratorContext context, | |||
String typeName) throws UnableToCompleteException { | |||
try { | |||
TypeOracle typeOracle = context.getTypeOracle(); | |||
Set<JClassType> typesNeedingSerializers = findTypesNeedingSerializers( | |||
typeOracle, logger); | |||
checkForUnserializableTypes(typesNeedingSerializers, typeOracle, | |||
logger); | |||
Set<JClassType> typesWithExistingSerializers = findTypesWithExistingSerializers( | |||
typeOracle, logger); | |||
Set<JClassType> serializerMappings = new HashSet<JClassType>(); | |||
serializerMappings.addAll(typesNeedingSerializers); | |||
serializerMappings.addAll(typesWithExistingSerializers); | |||
// get classType and save instance variables | |||
JClassType classType = typeOracle.getType(typeName); | |||
packageName = classType.getPackage().getName(); | |||
className = classType.getSimpleSourceName() + "Impl"; | |||
// Generate class source code for SerializerMapImpl | |||
generateSerializerMap(serializerMappings, logger, context); | |||
SerializerGenerator sg = new SerializerGenerator(); | |||
for (JClassType type : typesNeedingSerializers) { | |||
sg.generate(logger, context, type.getQualifiedSourceName()); | |||
} | |||
} catch (Exception e) { | |||
logger.log(TreeLogger.ERROR, | |||
"SerializerMapGenerator creation failed", e); | |||
throw new UnableToCompleteException(); | |||
} | |||
// return the fully qualifed name of the class generated | |||
return packageName + "." + className; | |||
} | |||
/** | |||
* Emits a warning for all classes that are used in communication but do not | |||
* implement java.io.Serializable. Implementing java.io.Serializable is not | |||
* needed for communication but for the server side Application to be | |||
* serializable i.e. work in GAE for instance. | |||
* | |||
* @param typesNeedingSerializers | |||
* @param typeOracle | |||
* @param logger | |||
* @throws UnableToCompleteException | |||
*/ | |||
private void checkForUnserializableTypes( | |||
Set<JClassType> typesNeedingSerializers, TypeOracle typeOracle, | |||
TreeLogger logger) throws UnableToCompleteException { | |||
JClassType javaSerializable = typeOracle.findType(Serializable.class | |||
.getName()); | |||
for (JClassType type : typesNeedingSerializers) { | |||
if (type.isArray() != null) { | |||
// Don't check for arrays | |||
continue; | |||
} | |||
boolean serializable = type.isAssignableTo(javaSerializable); | |||
if (!serializable) { | |||
boolean abortCompile = "true".equals(System | |||
.getProperty(FAIL_IF_NOT_SERIALIZABLE)); | |||
logger.log( | |||
abortCompile ? Type.ERROR : Type.WARN, | |||
type | |||
+ " is used in RPC or shared state but does not implement " | |||
+ Serializable.class.getName() | |||
+ ". Communication will work but the Application on server side cannot be serialized if it refers to objects of this type. " | |||
+ "If the system property " | |||
+ FAIL_IF_NOT_SERIALIZABLE | |||
+ " is set to \"true\", this causes the compilation to fail instead of just emitting a warning."); | |||
if (abortCompile) { | |||
throw new UnableToCompleteException(); | |||
} | |||
} | |||
} | |||
} | |||
private Set<JClassType> findTypesWithExistingSerializers( | |||
TypeOracle typeOracle, TreeLogger logger) | |||
throws UnableToCompleteException { | |||
JClassType serializerInterface = typeOracle | |||
.findType(JSONSerializer.class.getName()); | |||
JType[] deserializeParamTypes = new JType[] { | |||
typeOracle | |||
.findType(com.vaadin.terminal.gwt.client.metadata.Type.class | |||
.getName()), | |||
typeOracle.findType(JSONValue.class.getName()), | |||
typeOracle.findType(ApplicationConnection.class.getName()) }; | |||
String deserializeMethodName = "deserialize"; | |||
try { | |||
serializerInterface.getMethod(deserializeMethodName, | |||
deserializeParamTypes); | |||
} catch (NotFoundException e) { | |||
logger.log(Type.ERROR, "Could not find " + deserializeMethodName | |||
+ " in " + serializerInterface); | |||
throw new UnableToCompleteException(); | |||
} | |||
Set<JClassType> types = new HashSet<JClassType>(); | |||
for (JClassType serializer : serializerInterface.getSubtypes()) { | |||
JMethod deserializeMethod = serializer.findMethod( | |||
deserializeMethodName, deserializeParamTypes); | |||
if (deserializeMethod == null) { | |||
logger.log(Type.DEBUG, "Could not find " | |||
+ deserializeMethodName + " in " + serializer); | |||
continue; | |||
} | |||
JType returnType = deserializeMethod.getReturnType(); | |||
logger.log(Type.DEBUG, "Found " + deserializeMethodName | |||
+ " with return type " + returnType + " in " + serializer); | |||
types.add(returnType.isClass()); | |||
} | |||
return types; | |||
} | |||
/** | |||
* Generate source code for SerializerMapImpl | |||
* | |||
* @param typesNeedingSerializers | |||
* | |||
* @param logger | |||
* Logger object | |||
* @param context | |||
* Generator context | |||
*/ | |||
private void generateSerializerMap(Set<JClassType> typesNeedingSerializers, | |||
TreeLogger logger, GeneratorContext context) { | |||
// get print writer that receives the source code | |||
PrintWriter printWriter = null; | |||
printWriter = context.tryCreate(logger, packageName, className); | |||
// print writer if null, source code has ALREADY been generated | |||
if (printWriter == null) { | |||
return; | |||
} | |||
Date date = new Date(); | |||
TypeOracle typeOracle = context.getTypeOracle(); | |||
// init composer, set class properties, create source writer | |||
ClassSourceFileComposerFactory composer = null; | |||
composer = new ClassSourceFileComposerFactory(packageName, className); | |||
composer.addImport("com.google.gwt.core.client.GWT"); | |||
composer.addImplementedInterface(SerializerMap.class.getName()); | |||
SourceWriter sourceWriter = composer.createSourceWriter(context, | |||
printWriter); | |||
sourceWriter.indent(); | |||
sourceWriter.println("public " + JSONSerializer.class.getName() | |||
+ " getSerializer(String type) {"); | |||
sourceWriter.indent(); | |||
// TODO cache serializer instances in a map | |||
for (JClassType type : typesNeedingSerializers) { | |||
sourceWriter.print("if (type.equals(\"" | |||
+ type.getQualifiedSourceName() + "\")"); | |||
if (type instanceof JArrayType) { | |||
// Also add binary name to support encoding based on | |||
// object.getClass().getName() | |||
sourceWriter.print("||type.equals(\"" + type.getJNISignature() | |||
+ "\")"); | |||
} | |||
sourceWriter.println(") {"); | |||
sourceWriter.indent(); | |||
String serializerName = SerializerGenerator | |||
.getFullyQualifiedSerializerClassName(type); | |||
sourceWriter.println("return GWT.create(" + serializerName | |||
+ ".class);"); | |||
sourceWriter.outdent(); | |||
sourceWriter.println("}"); | |||
logger.log(Type.INFO, "Configured serializer (" + serializerName | |||
+ ") for " + type.getName()); | |||
} | |||
sourceWriter | |||
.println("throw new RuntimeException(\"No serializer found for class \"+type);"); | |||
sourceWriter.outdent(); | |||
sourceWriter.println("}"); | |||
// close generated class | |||
sourceWriter.outdent(); | |||
sourceWriter.println("}"); | |||
// commit generated class | |||
context.commit(logger, printWriter); | |||
logger.log(Type.INFO, | |||
"Done. (" + (new Date().getTime() - date.getTime()) / 1000 | |||
+ "seconds)"); | |||
} | |||
public Set<JClassType> findTypesNeedingSerializers(TypeOracle typeOracle, | |||
TreeLogger logger) { | |||
logger.log(Type.DEBUG, "Detecting serializable data types..."); | |||
HashSet<JClassType> types = new HashSet<JClassType>(); | |||
// Generate serializer classes for each subclass of SharedState | |||
JClassType serializerType = typeOracle.findType(SharedState.class | |||
.getName()); | |||
types.add(serializerType); | |||
JClassType[] serializerSubtypes = serializerType.getSubtypes(); | |||
for (JClassType type : serializerSubtypes) { | |||
types.add(type); | |||
} | |||
// Serializer classes might also be needed for RPC methods | |||
for (Class<?> cls : new Class[] { ServerRpc.class, ClientRpc.class }) { | |||
JClassType rpcType = typeOracle.findType(cls.getName()); | |||
JClassType[] serverRpcSubtypes = rpcType.getSubtypes(); | |||
for (JClassType type : serverRpcSubtypes) { | |||
addMethodParameterTypes(type, types, logger); | |||
} | |||
} | |||
// Add all types used from/in the types | |||
for (Object t : types.toArray()) { | |||
findSubTypesNeedingSerializers((JClassType) t, types); | |||
} | |||
logger.log(Type.DEBUG, "Serializable data types: " + types.toString()); | |||
return types; | |||
} | |||
private void addMethodParameterTypes(JClassType classContainingMethods, | |||
Set<JClassType> types, TreeLogger logger) { | |||
for (JMethod method : classContainingMethods.getMethods()) { | |||
if (method.getName().equals("initRpc")) { | |||
continue; | |||
} | |||
for (JType type : method.getParameterTypes()) { | |||
addTypeIfNeeded(types, type); | |||
} | |||
} | |||
} | |||
public void findSubTypesNeedingSerializers(JClassType type, | |||
Set<JClassType> serializableTypes) { | |||
// Find all setters and look at their parameter type to determine if a | |||
// new serializer is needed | |||
for (JMethod setterMethod : SerializerGenerator.getSetters(type)) { | |||
// The one and only parameter for the setter | |||
JType setterType = setterMethod.getParameterTypes()[0]; | |||
addTypeIfNeeded(serializableTypes, setterType); | |||
} | |||
} | |||
private void addTypeIfNeeded(Set<JClassType> serializableTypes, JType type) { | |||
if (serializableTypes.contains(type)) { | |||
return; | |||
} | |||
JParameterizedType parametrized = type.isParameterized(); | |||
if (parametrized != null) { | |||
for (JClassType parameterType : parametrized.getTypeArgs()) { | |||
addTypeIfNeeded(serializableTypes, parameterType); | |||
} | |||
} | |||
if (serializationHandledByFramework(type)) { | |||
return; | |||
} | |||
if (serializableTypes.contains(type)) { | |||
return; | |||
} | |||
JClassType typeClass = type.isClass(); | |||
if (typeClass != null) { | |||
// setterTypeClass is null at least for List<String>. It is | |||
// possible that we need to handle the cases somehow, for | |||
// instance for List<MyObject>. | |||
serializableTypes.add(typeClass); | |||
findSubTypesNeedingSerializers(typeClass, serializableTypes); | |||
} | |||
// Generate (n-1)-dimensional array serializer for n-dimensional array | |||
JArrayType arrayType = type.isArray(); | |||
if (arrayType != null) { | |||
serializableTypes.add(arrayType); | |||
addTypeIfNeeded(serializableTypes, arrayType.getComponentType()); | |||
} | |||
} | |||
Set<Class<?>> frameworkHandledTypes = new HashSet<Class<?>>(); | |||
{ | |||
frameworkHandledTypes.add(String.class); | |||
frameworkHandledTypes.add(Boolean.class); | |||
frameworkHandledTypes.add(Integer.class); | |||
frameworkHandledTypes.add(Float.class); | |||
frameworkHandledTypes.add(Double.class); | |||
frameworkHandledTypes.add(Long.class); | |||
frameworkHandledTypes.add(Enum.class); | |||
frameworkHandledTypes.add(String[].class); | |||
frameworkHandledTypes.add(Object[].class); | |||
frameworkHandledTypes.add(Map.class); | |||
frameworkHandledTypes.add(List.class); | |||
frameworkHandledTypes.add(Set.class); | |||
frameworkHandledTypes.add(Byte.class); | |||
frameworkHandledTypes.add(Character.class); | |||
} | |||
private boolean serializationHandledByFramework(JType setterType) { | |||
// Some types are handled by the framework at the moment. See #8449 | |||
// This method should be removed at some point. | |||
if (setterType.isPrimitive() != null) { | |||
return true; | |||
} | |||
String qualifiedName = setterType.getQualifiedSourceName(); | |||
for (Class<?> cls : frameworkHandledTypes) { | |||
if (qualifiedName.equals(cls.getName())) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
} |
@@ -0,0 +1,90 @@ | |||
/* | |||
* Copyright 2011 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils.metadata; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.typeinfo.JArrayType; | |||
import com.google.gwt.core.ext.typeinfo.JType; | |||
import com.google.gwt.json.client.JSONArray; | |||
import com.google.gwt.user.rebind.SourceWriter; | |||
import com.vaadin.terminal.gwt.client.communication.JsonDecoder; | |||
import com.vaadin.terminal.gwt.client.communication.JsonEncoder; | |||
import com.vaadin.terminal.gwt.widgetsetutils.ConnectorBundleLoaderFactory; | |||
public class ArraySerializer extends JsonSerializer { | |||
private final JArrayType arrayType; | |||
public ArraySerializer(JArrayType arrayType) { | |||
super(arrayType); | |||
this.arrayType = arrayType; | |||
} | |||
@Override | |||
protected void printDeserializerBody(TreeLogger logger, SourceWriter w, | |||
String type, String jsonValue, String connection) { | |||
JType leafType = arrayType.getLeafType(); | |||
int rank = arrayType.getRank(); | |||
w.println(JSONArray.class.getName() + " jsonArray = " + jsonValue | |||
+ ".isArray();"); | |||
// Type value = new Type[jsonArray.size()][][]; | |||
w.print(arrayType.getQualifiedSourceName() + " value = new " | |||
+ leafType.getQualifiedSourceName() + "[jsonArray.size()]"); | |||
for (int i = 1; i < rank; i++) { | |||
w.print("[]"); | |||
} | |||
w.println(";"); | |||
w.println("for(int i = 0 ; i < value.length; i++) {"); | |||
w.indent(); | |||
JType componentType = arrayType.getComponentType(); | |||
w.print("value[i] = (" | |||
+ ConnectorBundleLoaderFactory.getBoxedTypeName(componentType) | |||
+ ") " + JsonDecoder.class.getName() + ".decodeValue("); | |||
ConnectorBundleLoaderFactory.writeTypeCreator(w, componentType); | |||
w.print(", jsonArray.get(i), null, " + connection + ")"); | |||
w.println(";"); | |||
w.outdent(); | |||
w.println("}"); | |||
w.println("return value;"); | |||
} | |||
@Override | |||
protected void printSerializerBody(TreeLogger logger, SourceWriter w, | |||
String value, String applicationConnection) { | |||
w.println(JSONArray.class.getName() + " values = new " | |||
+ JSONArray.class.getName() + "();"); | |||
// JPrimitiveType primitive = componentType.isPrimitive(); | |||
w.println("for (int i = 0; i < " + value + ".length; i++) {"); | |||
w.indent(); | |||
w.print("values.set(i, "); | |||
w.print(JsonEncoder.class.getName() + ".encode(" + value | |||
+ "[i], false, " + applicationConnection + ")"); | |||
w.println(");"); | |||
w.outdent(); | |||
w.println("}"); | |||
w.println("return values;"); | |||
} | |||
} |
@@ -21,6 +21,7 @@ import java.util.Set; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.typeinfo.JClassType; | |||
import com.google.gwt.core.ext.typeinfo.JMethod; | |||
import com.google.gwt.core.ext.typeinfo.JType; | |||
public class ClientRpcVisitor extends TypeVisitor { | |||
@Override | |||
@@ -33,6 +34,11 @@ public class ClientRpcVisitor extends TypeVisitor { | |||
for (JMethod method : methods) { | |||
bundle.setNeedsInvoker(type, method); | |||
bundle.setNeedsParamTypes(type, method); | |||
JType[] parameterTypes = method.getParameterTypes(); | |||
for (JType paramType : parameterTypes) { | |||
bundle.setNeedsSerialize(paramType); | |||
} | |||
} | |||
} | |||
} |
@@ -4,62 +4,114 @@ | |||
package com.vaadin.terminal.gwt.widgetsetutils.metadata; | |||
import java.io.Serializable; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.TreeLogger.Type; | |||
import com.google.gwt.core.ext.UnableToCompleteException; | |||
import com.google.gwt.core.ext.typeinfo.JArrayType; | |||
import com.google.gwt.core.ext.typeinfo.JClassType; | |||
import com.google.gwt.core.ext.typeinfo.JEnumType; | |||
import com.google.gwt.core.ext.typeinfo.JMethod; | |||
import com.google.gwt.core.ext.typeinfo.JParameterizedType; | |||
import com.google.gwt.core.ext.typeinfo.JType; | |||
import com.google.gwt.core.ext.typeinfo.NotFoundException; | |||
import com.google.gwt.core.ext.typeinfo.TypeOracle; | |||
import com.google.gwt.json.client.JSONValue; | |||
import com.vaadin.shared.communication.ClientRpc; | |||
import com.vaadin.shared.communication.ServerRpc; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.ComponentConnector; | |||
import com.vaadin.terminal.gwt.client.ServerConnector; | |||
import com.vaadin.terminal.gwt.client.communication.JSONSerializer; | |||
public class ConnectorBundle { | |||
private static final String FAIL_IF_NOT_SERIALIZABLE = "vFailIfNotSerializable"; | |||
private final String name; | |||
private final ConnectorBundle previousBundle; | |||
private final Collection<TypeVisitor> visitors; | |||
private final Map<JType, JClassType> customSerializers; | |||
private final Set<JType> hasSerializeSupport = new HashSet<JType>(); | |||
private final Set<JType> needsSerializeSupport = new HashSet<JType>(); | |||
private final Map<JType, GeneratedSerializer> serializers = new HashMap<JType, GeneratedSerializer>(); | |||
private final Set<JClassType> needsPropertyList = new HashSet<JClassType>(); | |||
private final Set<JClassType> needsGwtConstructor = new HashSet<JClassType>(); | |||
private final Map<JClassType, Set<String>> identifiers = new HashMap<JClassType, Set<String>>(); | |||
private final Set<JClassType> visitedTypes = new HashSet<JClassType>(); | |||
private final Set<JClassType> visitQueue = new HashSet<JClassType>(); | |||
private final Map<JClassType, Set<JMethod>> needsReturnType = new HashMap<JClassType, Set<JMethod>>(); | |||
private final Collection<TypeVisitor> visitors; | |||
private final Set<JClassType> needsProxySupport = new HashSet<JClassType>(); | |||
private final Map<JClassType, Set<String>> identifiers = new HashMap<JClassType, Set<String>>(); | |||
private final Map<JClassType, Set<JMethod>> needsReturnType = new HashMap<JClassType, Set<JMethod>>(); | |||
private final Map<JClassType, Set<JMethod>> needsInvoker = new HashMap<JClassType, Set<JMethod>>(); | |||
private final Map<JClassType, Set<JMethod>> needsParamTypes = new HashMap<JClassType, Set<JMethod>>(); | |||
private final Map<JClassType, Set<JMethod>> needsDelayedInfo = new HashMap<JClassType, Set<JMethod>>(); | |||
private final Set<Property> needsSetter = new HashSet<Property>(); | |||
private final Set<Property> needsType = new HashSet<Property>(); | |||
private final Set<Property> needsGetter = new HashSet<Property>(); | |||
private ConnectorBundle(String name, ConnectorBundle previousBundle, | |||
Collection<TypeVisitor> visitors) { | |||
Collection<TypeVisitor> visitors, | |||
Map<JType, JClassType> customSerializers) { | |||
this.name = name; | |||
this.previousBundle = previousBundle; | |||
this.visitors = visitors; | |||
this.customSerializers = customSerializers; | |||
} | |||
public ConnectorBundle(String name, ConnectorBundle previousBundle) { | |||
this(name, previousBundle, previousBundle.visitors); | |||
} | |||
this(name, previousBundle, previousBundle.visitors, | |||
previousBundle.customSerializers); | |||
} | |||
public ConnectorBundle(String name, Collection<TypeVisitor> visitors, | |||
TypeOracle oracle) throws NotFoundException { | |||
this(name, null, visitors, findCustomSerializers(oracle)); | |||
} | |||
private static Map<JType, JClassType> findCustomSerializers( | |||
TypeOracle oracle) throws NotFoundException { | |||
Map<JType, JClassType> serializers = new HashMap<JType, JClassType>(); | |||
JClassType serializerInterface = oracle.findType(JSONSerializer.class | |||
.getName()); | |||
JType[] deserializeParamTypes = new JType[] { | |||
oracle.findType(com.vaadin.terminal.gwt.client.metadata.Type.class | |||
.getName()), | |||
oracle.findType(JSONValue.class.getName()), | |||
oracle.findType(ApplicationConnection.class.getName()) }; | |||
String deserializeMethodName = "deserialize"; | |||
// Just test that the method exists | |||
serializerInterface.getMethod(deserializeMethodName, | |||
deserializeParamTypes); | |||
for (JClassType serializer : serializerInterface.getSubtypes()) { | |||
JMethod deserializeMethod = serializer.findMethod( | |||
deserializeMethodName, deserializeParamTypes); | |||
if (deserializeMethod == null) { | |||
continue; | |||
} | |||
JType returnType = deserializeMethod.getReturnType(); | |||
public ConnectorBundle(String name, Collection<TypeVisitor> visitors) { | |||
this(name, null, visitors); | |||
serializers.put(returnType, serializer); | |||
} | |||
return serializers; | |||
} | |||
public void setNeedsGwtConstructor(JClassType type) { | |||
if (!needsGwtConstructor(type)) { | |||
ensureVisited(type); | |||
needsGwtConstructor.add(type); | |||
} | |||
} | |||
@@ -75,7 +127,6 @@ public class ConnectorBundle { | |||
public void setIdentifier(JClassType type, String identifier) { | |||
if (!hasIdentifier(type, identifier)) { | |||
ensureVisited(type); | |||
addMapping(identifiers, type, identifier); | |||
} | |||
} | |||
@@ -114,8 +165,13 @@ public class ConnectorBundle { | |||
public void processType(TreeLogger logger, JClassType type) | |||
throws UnableToCompleteException { | |||
ensureVisited(type); | |||
purgeQueue(logger); | |||
if (!isTypeVisited(type)) { | |||
for (TypeVisitor typeVisitor : visitors) { | |||
invokeVisitor(logger, type, typeVisitor); | |||
} | |||
visitedTypes.add(type); | |||
purgeSerializeSupportQueue(logger); | |||
} | |||
} | |||
private boolean isTypeVisited(JClassType type) { | |||
@@ -126,31 +182,200 @@ public class ConnectorBundle { | |||
} | |||
} | |||
private void ensureVisited(JClassType type) { | |||
if (!isTypeVisited(type)) { | |||
visitQueue.add(type); | |||
private void purgeSerializeSupportQueue(TreeLogger logger) | |||
throws UnableToCompleteException { | |||
while (!needsSerializeSupport.isEmpty()) { | |||
Iterator<JType> iterator = needsSerializeSupport.iterator(); | |||
JType type = iterator.next(); | |||
iterator.remove(); | |||
if (hasSserializeSupport(type)) { | |||
continue; | |||
} | |||
addSerializeSupport(logger, type); | |||
} | |||
} | |||
private void purgeQueue(TreeLogger logger) throws UnableToCompleteException { | |||
while (!visitQueue.isEmpty()) { | |||
Iterator<JClassType> iterator = visitQueue.iterator(); | |||
JClassType type = iterator.next(); | |||
iterator.remove(); | |||
private void addSerializeSupport(TreeLogger logger, JType type) | |||
throws UnableToCompleteException { | |||
hasSerializeSupport.add(type); | |||
if (isTypeVisited(type)) { | |||
continue; | |||
JParameterizedType parametrized = type.isParameterized(); | |||
if (parametrized != null) { | |||
for (JClassType parameterType : parametrized.getTypeArgs()) { | |||
setNeedsSerialize(parameterType); | |||
} | |||
} | |||
// Mark as visited before visiting to avoid adding to queue again | |||
visitedTypes.add(type); | |||
if (serializationHandledByFramework(type)) { | |||
return; | |||
} | |||
for (TypeVisitor typeVisitor : visitors) { | |||
invokeVisitor(logger, type, typeVisitor); | |||
JClassType customSerializer = customSerializers.get(type); | |||
JClassType typeAsClass = type.isClass(); | |||
JEnumType enumType = type.isEnum(); | |||
JArrayType arrayType = type.isArray(); | |||
if (customSerializer != null) { | |||
logger.log(Type.INFO, "Will serialize " + type + " using " | |||
+ customSerializer.getName()); | |||
setSerializer(type, new CustomSerializer(customSerializer)); | |||
} else if (arrayType != null) { | |||
logger.log(Type.INFO, "Will serialize " + type + " as an array"); | |||
setSerializer(type, new ArraySerializer(arrayType)); | |||
setNeedsSerialize(arrayType.getComponentType()); | |||
} else if (enumType != null) { | |||
logger.log(Type.INFO, "Will serialize " + type + " as an enum"); | |||
setSerializer(type, new EnumSerializer(enumType)); | |||
} else if (typeAsClass != null) { | |||
// Bean | |||
checkSerializable(logger, typeAsClass); | |||
logger.log(Type.INFO, "Will serialize " + type + " as a bean"); | |||
setNeedsPropertyList(typeAsClass); | |||
for (Property property : getProperties(typeAsClass)) { | |||
setNeedsGwtConstructor(property.getBeanType()); | |||
setNeedsSetter(property); | |||
// Getters needed for reading previous value that should be | |||
// passed to sub encoder | |||
setNeedsGetter(property); | |||
setNeedsType(property); | |||
JType propertyType = property.getPropertyType(); | |||
setNeedsSerialize(propertyType); | |||
} | |||
} | |||
} | |||
private void checkSerializable(TreeLogger logger, JClassType type) | |||
throws UnableToCompleteException { | |||
JClassType javaSerializable = type.getOracle().findType( | |||
Serializable.class.getName()); | |||
boolean serializable = type.isAssignableTo(javaSerializable); | |||
if (!serializable) { | |||
boolean abortCompile = "true".equals(System | |||
.getProperty(FAIL_IF_NOT_SERIALIZABLE)); | |||
logger.log( | |||
abortCompile ? Type.ERROR : Type.WARN, | |||
type | |||
+ " is used in RPC or shared state but does not implement " | |||
+ Serializable.class.getName() | |||
+ ". Communication will work but the Application on server side cannot be serialized if it refers to objects of this type. " | |||
+ "If the system property " | |||
+ FAIL_IF_NOT_SERIALIZABLE | |||
+ " is set to \"true\", this causes the compilation to fail instead of just emitting a warning."); | |||
if (abortCompile) { | |||
throw new UnableToCompleteException(); | |||
} | |||
} | |||
} | |||
private void setSerializer(JType type, GeneratedSerializer serializer) { | |||
if (!hasSerializer(type)) { | |||
serializers.put(type, serializer); | |||
} | |||
} | |||
private boolean hasSerializer(JType type) { | |||
if (serializers.containsKey(type)) { | |||
return true; | |||
} else { | |||
return previousBundle != null && previousBundle.hasSerializer(type); | |||
} | |||
} | |||
public Map<JType, GeneratedSerializer> getSerializers() { | |||
return Collections.unmodifiableMap(serializers); | |||
} | |||
private void setNeedsGetter(Property property) { | |||
if (!isNeedsGetter(property)) { | |||
needsGetter.add(property); | |||
} | |||
} | |||
private boolean isNeedsGetter(Property property) { | |||
if (needsGetter.contains(property)) { | |||
return true; | |||
} else { | |||
return previousBundle != null | |||
&& previousBundle.isNeedsGetter(property); | |||
} | |||
} | |||
public Set<Property> getNeedsGetter() { | |||
return Collections.unmodifiableSet(needsGetter); | |||
} | |||
private void setNeedsType(Property property) { | |||
if (!isNeedsType(property)) { | |||
needsType.add(property); | |||
} | |||
} | |||
public Set<Property> getNeedsType() { | |||
return Collections.unmodifiableSet(needsType); | |||
} | |||
private boolean isNeedsType(Property property) { | |||
if (needsType.contains(property)) { | |||
return true; | |||
} else { | |||
return previousBundle != null | |||
&& previousBundle.isNeedsType(property); | |||
} | |||
} | |||
public void setNeedsSetter(Property property) { | |||
if (!isNeedsSetter(property)) { | |||
needsSetter.add(property); | |||
} | |||
} | |||
private boolean isNeedsSetter(Property property) { | |||
if (needsSetter.contains(property)) { | |||
return true; | |||
} else { | |||
return previousBundle != null | |||
&& previousBundle.isNeedsSetter(property); | |||
} | |||
} | |||
public Set<Property> getNeedsSetter() { | |||
return Collections.unmodifiableSet(needsSetter); | |||
} | |||
private void setNeedsPropertyList(JClassType type) { | |||
if (!isNeedsPropertyList(type)) { | |||
needsPropertyList.add(type); | |||
} | |||
} | |||
private boolean isNeedsPropertyList(JClassType type) { | |||
if (needsPropertyList.contains(type)) { | |||
return true; | |||
} else { | |||
return previousBundle != null | |||
&& previousBundle.isNeedsPropertyList(type); | |||
} | |||
} | |||
public Set<JClassType> getNeedsPropertyListing() { | |||
return Collections.unmodifiableSet(needsPropertyList); | |||
} | |||
public Collection<Property> getProperties(JClassType type) { | |||
HashSet<Property> properties = new HashSet<Property>(); | |||
properties.addAll(MethodProperty.findProperties(type)); | |||
return properties; | |||
} | |||
private void invokeVisitor(TreeLogger logger, JClassType type, | |||
TypeVisitor typeVisitor) throws UnableToCompleteException { | |||
TreeLogger subLogger = logger.branch(Type.TRACE, | |||
@@ -158,9 +383,11 @@ public class ConnectorBundle { | |||
+ typeVisitor.getClass().getSimpleName()); | |||
if (isConnectedConnector(type)) { | |||
typeVisitor.visitConnector(subLogger, type, this); | |||
} else if (isClientRpc(type)) { | |||
} | |||
if (isClientRpc(type)) { | |||
typeVisitor.visitClientRpc(subLogger, type, this); | |||
} else if (isServerRpc(type)) { | |||
} | |||
if (isServerRpc(type)) { | |||
typeVisitor.visitServerRpc(subLogger, type, this); | |||
} | |||
} | |||
@@ -172,7 +399,6 @@ public class ConnectorBundle { | |||
public void setNeedsReturnType(JClassType type, JMethod method) { | |||
if (!isNeedsReturnType(type, method)) { | |||
ensureVisited(type); | |||
addMapping(needsReturnType, type, method); | |||
} | |||
} | |||
@@ -221,7 +447,6 @@ public class ConnectorBundle { | |||
public void setNeedsInvoker(JClassType type, JMethod method) { | |||
if (!isNeedsInvoker(type, method)) { | |||
ensureVisited(type); | |||
addMapping(needsInvoker, type, method); | |||
} | |||
} | |||
@@ -254,7 +479,6 @@ public class ConnectorBundle { | |||
public void setNeedsParamTypes(JClassType type, JMethod method) { | |||
if (!isNeedsParamTypes(type, method)) { | |||
ensureVisited(type); | |||
addMapping(needsParamTypes, type, method); | |||
} | |||
} | |||
@@ -274,7 +498,6 @@ public class ConnectorBundle { | |||
public void setNeedsProxySupport(JClassType type) { | |||
if (!isNeedsProxySupport(type)) { | |||
ensureVisited(type); | |||
needsProxySupport.add(type); | |||
} | |||
} | |||
@@ -294,7 +517,6 @@ public class ConnectorBundle { | |||
public void setNeedsDelayedInfo(JClassType type, JMethod method) { | |||
if (!isNeedsDelayedInfo(type, method)) { | |||
ensureVisited(type); | |||
addMapping(needsDelayedInfo, type, method); | |||
} | |||
} | |||
@@ -311,4 +533,55 @@ public class ConnectorBundle { | |||
public Map<JClassType, Set<JMethod>> getNeedsDelayedInfo() { | |||
return Collections.unmodifiableMap(needsDelayedInfo); | |||
} | |||
public void setNeedsSerialize(JType type) { | |||
if (!hasSserializeSupport(type)) { | |||
needsSerializeSupport.add(type); | |||
} | |||
} | |||
private static Set<Class<?>> frameworkHandledTypes = new HashSet<Class<?>>(); | |||
{ | |||
frameworkHandledTypes.add(String.class); | |||
frameworkHandledTypes.add(Boolean.class); | |||
frameworkHandledTypes.add(Integer.class); | |||
frameworkHandledTypes.add(Float.class); | |||
frameworkHandledTypes.add(Double.class); | |||
frameworkHandledTypes.add(Long.class); | |||
frameworkHandledTypes.add(Enum.class); | |||
frameworkHandledTypes.add(String[].class); | |||
frameworkHandledTypes.add(Object[].class); | |||
frameworkHandledTypes.add(Map.class); | |||
frameworkHandledTypes.add(List.class); | |||
frameworkHandledTypes.add(Set.class); | |||
frameworkHandledTypes.add(Byte.class); | |||
frameworkHandledTypes.add(Character.class); | |||
} | |||
private boolean serializationHandledByFramework(JType setterType) { | |||
// Some types are handled by the framework at the moment. See #8449 | |||
// This method should be removed at some point. | |||
if (setterType.isPrimitive() != null) { | |||
return true; | |||
} | |||
String qualifiedName = setterType.getQualifiedSourceName(); | |||
for (Class<?> cls : frameworkHandledTypes) { | |||
if (qualifiedName.equals(cls.getName())) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
private boolean hasSserializeSupport(JType type) { | |||
if (hasSerializeSupport.contains(type)) { | |||
return true; | |||
} else { | |||
return previousBundle != null | |||
&& previousBundle.hasSserializeSupport(type); | |||
} | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
* Copyright 2011 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils.metadata; | |||
import com.google.gwt.core.client.GWT; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.UnableToCompleteException; | |||
import com.google.gwt.core.ext.typeinfo.JClassType; | |||
import com.google.gwt.user.rebind.SourceWriter; | |||
import com.vaadin.terminal.gwt.widgetsetutils.ConnectorBundleLoaderFactory; | |||
public class CustomSerializer implements GeneratedSerializer { | |||
private final JClassType serializerType; | |||
public CustomSerializer(JClassType serializerType) { | |||
this.serializerType = serializerType; | |||
} | |||
@Override | |||
public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w) | |||
throws UnableToCompleteException { | |||
w.print("return "); | |||
w.print(GWT.class.getCanonicalName()); | |||
w.print(".create("); | |||
ConnectorBundleLoaderFactory.writeClassLiteral(w, serializerType); | |||
w.println(");"); | |||
} | |||
} |
@@ -0,0 +1,58 @@ | |||
/* | |||
* Copyright 2011 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils.metadata; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.typeinfo.JEnumConstant; | |||
import com.google.gwt.core.ext.typeinfo.JEnumType; | |||
import com.google.gwt.json.client.JSONString; | |||
import com.google.gwt.user.rebind.SourceWriter; | |||
public class EnumSerializer extends JsonSerializer { | |||
private final JEnumType enumType; | |||
public EnumSerializer(JEnumType type) { | |||
super(type); | |||
enumType = type; | |||
} | |||
@Override | |||
protected void printDeserializerBody(TreeLogger logger, SourceWriter w, | |||
String type, String jsonValue, String connection) { | |||
w.println("String enumIdentifier = ((" + JSONString.class.getName() | |||
+ ")" + jsonValue + ").stringValue();"); | |||
for (JEnumConstant e : enumType.getEnumConstants()) { | |||
w.println("if (\"" + e.getName() + "\".equals(enumIdentifier)) {"); | |||
w.indent(); | |||
w.println("return " + enumType.getQualifiedSourceName() + "." | |||
+ e.getName() + ";"); | |||
w.outdent(); | |||
w.println("}"); | |||
} | |||
w.println("return null;"); | |||
} | |||
@Override | |||
protected void printSerializerBody(TreeLogger logger, SourceWriter w, | |||
String value, String applicationConnection) { | |||
// return new JSONString(castedValue.name()); | |||
w.println("return new " + JSONString.class.getName() + "(" + value | |||
+ ".name());"); | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
/* | |||
* Copyright 2011 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils.metadata; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.UnableToCompleteException; | |||
import com.google.gwt.user.rebind.SourceWriter; | |||
public interface GeneratedSerializer { | |||
public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w) | |||
throws UnableToCompleteException; | |||
} |
@@ -0,0 +1,88 @@ | |||
/* | |||
* Copyright 2011 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils.metadata; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.UnableToCompleteException; | |||
import com.google.gwt.core.ext.typeinfo.JType; | |||
import com.google.gwt.json.client.JSONValue; | |||
import com.google.gwt.user.rebind.SourceWriter; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.communication.JSONSerializer; | |||
public abstract class JsonSerializer implements GeneratedSerializer { | |||
private final JType type; | |||
public JsonSerializer(JType type) { | |||
this.type = type; | |||
} | |||
@Override | |||
public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w) | |||
throws UnableToCompleteException { | |||
w.print("return new "); | |||
w.print(JSONSerializer.class.getCanonicalName()); | |||
w.print("<"); | |||
w.print(type.getQualifiedSourceName()); | |||
w.println(">() {"); | |||
w.indent(); | |||
writeSerializerBody(logger, w); | |||
w.outdent(); | |||
w.println("};"); | |||
} | |||
protected void writeSerializerBody(TreeLogger logger, SourceWriter w) { | |||
String qualifiedSourceName = type.getQualifiedSourceName(); | |||
w.println("public " + JSONValue.class.getName() + " serialize(" | |||
+ qualifiedSourceName + " value, " | |||
+ ApplicationConnection.class.getName() + " connection) {"); | |||
w.indent(); | |||
// MouseEventDetails castedValue = (MouseEventDetails) value; | |||
w.println(qualifiedSourceName + " castedValue = (" | |||
+ qualifiedSourceName + ") value;"); | |||
printSerializerBody(logger, w, "castedValue", "connection"); | |||
// End of serializer method | |||
w.outdent(); | |||
w.println("}"); | |||
// Deserializer | |||
// T deserialize(Type type, JSONValue jsonValue, ApplicationConnection | |||
// connection); | |||
w.println("public " + qualifiedSourceName + " deserialize(Type type, " | |||
+ JSONValue.class.getName() + " jsonValue, " | |||
+ ApplicationConnection.class.getName() + " connection) {"); | |||
w.indent(); | |||
printDeserializerBody(logger, w, "type", "jsonValue", "connection"); | |||
w.outdent(); | |||
w.println("}"); | |||
} | |||
protected abstract void printDeserializerBody(TreeLogger logger, | |||
SourceWriter w, String type, String jsonValue, String connection); | |||
protected abstract void printSerializerBody(TreeLogger logger, | |||
SourceWriter w, String value, String applicationConnection); | |||
} |
@@ -0,0 +1,124 @@ | |||
/* | |||
* Copyright 2011 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils.metadata; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.typeinfo.JClassType; | |||
import com.google.gwt.core.ext.typeinfo.JMethod; | |||
import com.google.gwt.core.ext.typeinfo.JType; | |||
import com.google.gwt.user.rebind.SourceWriter; | |||
public class MethodProperty extends Property { | |||
private final JMethod setter; | |||
private MethodProperty(JClassType beanType, JMethod setter) { | |||
super(getTransportFieldName(setter), beanType, setter | |||
.getParameterTypes()[0]); | |||
this.setter = setter; | |||
} | |||
public static Collection<MethodProperty> findProperties(JClassType type) { | |||
Collection<MethodProperty> properties = new ArrayList<MethodProperty>(); | |||
List<JMethod> setters = getSetters(type); | |||
for (JMethod setter : setters) { | |||
properties.add(new MethodProperty(type, setter)); | |||
} | |||
return properties; | |||
} | |||
/** | |||
* Returns a list of all setters found in the beanType or its parent class | |||
* | |||
* @param beanType | |||
* The type to check | |||
* @return A list of setter methods from the class and its parents | |||
*/ | |||
private static List<JMethod> getSetters(JClassType beanType) { | |||
List<JMethod> setterMethods = new ArrayList<JMethod>(); | |||
while (beanType != null | |||
&& !beanType.getQualifiedSourceName().equals( | |||
Object.class.getName())) { | |||
for (JMethod method : beanType.getMethods()) { | |||
// Process all setters that have corresponding fields | |||
if (!method.isPublic() || method.isStatic() | |||
|| !method.getName().startsWith("set") | |||
|| method.getParameterTypes().length != 1) { | |||
// Not setter, skip to next method | |||
continue; | |||
} | |||
setterMethods.add(method); | |||
} | |||
beanType = beanType.getSuperclass(); | |||
} | |||
return setterMethods; | |||
} | |||
@Override | |||
public void writeSetterBody(TreeLogger logger, SourceWriter w, | |||
String beanVariable, String valueVariable) { | |||
w.print("(("); | |||
w.print(getBeanType().getQualifiedSourceName()); | |||
w.print(") "); | |||
w.print(beanVariable); | |||
w.print(")."); | |||
w.print(setter.getName()); | |||
w.print("(("); | |||
w.print(getUnboxedPropertyTypeName()); | |||
w.print(") "); | |||
w.print(valueVariable); | |||
w.println(");"); | |||
} | |||
@Override | |||
public void writeGetterBody(TreeLogger logger, SourceWriter w, | |||
String beanVariable) { | |||
w.print("return (("); | |||
w.print(getBeanType().getQualifiedSourceName()); | |||
w.print(") "); | |||
w.print(beanVariable); | |||
w.print(")."); | |||
w.print(findGetter(getBeanType(), setter)); | |||
w.print("();"); | |||
} | |||
private String findGetter(JClassType beanType, JMethod setterMethod) { | |||
JType setterParameterType = setterMethod.getParameterTypes()[0]; | |||
String fieldName = setterMethod.getName().substring(3); | |||
if (setterParameterType.getQualifiedSourceName().equals( | |||
boolean.class.getName())) { | |||
return "is" + fieldName; | |||
} else { | |||
return "get" + fieldName; | |||
} | |||
} | |||
private static String getTransportFieldName(JMethod setter) { | |||
String baseName = setter.getName().substring(3); | |||
return Character.toLowerCase(baseName.charAt(0)) | |||
+ baseName.substring(1); | |||
} | |||
} |
@@ -0,0 +1,84 @@ | |||
/* | |||
* Copyright 2011 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils.metadata; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.typeinfo.JClassType; | |||
import com.google.gwt.core.ext.typeinfo.JPrimitiveType; | |||
import com.google.gwt.core.ext.typeinfo.JType; | |||
import com.google.gwt.user.rebind.SourceWriter; | |||
public abstract class Property { | |||
private final String name; | |||
private final JClassType beanType; | |||
private final JType propertyType; | |||
protected Property(String name, JClassType beanType, JType propertyType) { | |||
this.name = name; | |||
this.beanType = beanType; | |||
this.propertyType = propertyType; | |||
} | |||
public String getName() { | |||
return name; | |||
} | |||
public JType getPropertyType() { | |||
return propertyType; | |||
} | |||
public String getUnboxedPropertyTypeName() { | |||
JType propertyType = getPropertyType(); | |||
JPrimitiveType primitive = propertyType.isPrimitive(); | |||
if (primitive != null) { | |||
return primitive.getQualifiedBoxedSourceName(); | |||
} else { | |||
return propertyType.getQualifiedSourceName(); | |||
} | |||
} | |||
public JClassType getBeanType() { | |||
return beanType; | |||
} | |||
public abstract void writeSetterBody(TreeLogger logger, SourceWriter w, | |||
String beanVariable, String valueVariable); | |||
public abstract void writeGetterBody(TreeLogger logger, SourceWriter w, | |||
String beanVariable); | |||
@Override | |||
public boolean equals(Object obj) { | |||
if (this == obj) { | |||
return true; | |||
} else if (obj instanceof Property) { | |||
Property other = (Property) obj; | |||
return other.getClass() == getClass() | |||
&& other.getBeanType().equals(getBeanType()) | |||
&& other.getName().equals(getName()); | |||
} else { | |||
return false; | |||
} | |||
} | |||
@Override | |||
public int hashCode() { | |||
return getClass().hashCode() * 31 ^ 2 + getBeanType().hashCode() * 31 | |||
+ getName().hashCode(); | |||
} | |||
} |
@@ -21,6 +21,7 @@ import java.util.Set; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.typeinfo.JClassType; | |||
import com.google.gwt.core.ext.typeinfo.JMethod; | |||
import com.google.gwt.core.ext.typeinfo.JType; | |||
public class ServerRpcVisitor extends TypeVisitor { | |||
@Override | |||
@@ -35,6 +36,11 @@ public class ServerRpcVisitor extends TypeVisitor { | |||
JMethod[] methods = subType.getMethods(); | |||
for (JMethod method : methods) { | |||
bundle.setNeedsDelayedInfo(type, method); | |||
JType[] parameterTypes = method.getParameterTypes(); | |||
for (JType paramType : parameterTypes) { | |||
bundle.setNeedsSerialize(paramType); | |||
} | |||
} | |||
} | |||
} |
@@ -16,6 +16,8 @@ public class StateInitVisitor extends TypeVisitor { | |||
JMethod getState = findInheritedMethod(type, "getState"); | |||
bundle.setNeedsReturnType(type, getState); | |||
bundle.setNeedsSerialize(getState.getReturnType()); | |||
JType stateType = getState.getReturnType(); | |||
bundle.setNeedsGwtConstructor(stateType.isClass()); | |||
} |
@@ -23,14 +23,6 @@ | |||
<when-type-is class="com.google.gwt.core.client.impl.SchedulerImpl" /> | |||
</replace-with> | |||
<!-- Generators for serializators for classes used in communication between | |||
server and client --> | |||
<generate-with | |||
class="com.vaadin.terminal.gwt.widgetsetutils.SerializerMapGenerator"> | |||
<when-type-is | |||
class="com.vaadin.terminal.gwt.client.communication.SerializerMap" /> | |||
</generate-with> | |||
<replace-with class="com.vaadin.terminal.gwt.client.VDebugConsole"> | |||
<when-type-is class="com.vaadin.terminal.gwt.client.Console" /> | |||
</replace-with> |
@@ -65,7 +65,6 @@ import com.vaadin.terminal.gwt.client.communication.HasJavaScriptConnectorHelper | |||
import com.vaadin.terminal.gwt.client.communication.JsonDecoder; | |||
import com.vaadin.terminal.gwt.client.communication.JsonEncoder; | |||
import com.vaadin.terminal.gwt.client.communication.RpcManager; | |||
import com.vaadin.terminal.gwt.client.communication.SerializerMap; | |||
import com.vaadin.terminal.gwt.client.communication.StateChangeEvent; | |||
import com.vaadin.terminal.gwt.client.extensions.AbstractExtensionConnector; | |||
import com.vaadin.terminal.gwt.client.metadata.ConnectorBundleLoader; | |||
@@ -106,8 +105,6 @@ public class ApplicationConnection { | |||
public static final char VAR_ESCAPE_CHARACTER = '\u001b'; | |||
private static SerializerMap serializerMap; | |||
/** | |||
* A string that, if found in a non-JSON response to a UIDL request, will | |||
* cause the browser to refresh the page. If followed by a colon, optional | |||
@@ -215,7 +212,6 @@ public class ApplicationConnection { | |||
rpcManager = GWT.create(RpcManager.class); | |||
layoutManager = GWT.create(LayoutManager.class); | |||
layoutManager.setConnection(this); | |||
serializerMap = GWT.create(SerializerMap.class); | |||
} | |||
public void init(WidgetSet widgetSet, ApplicationConfiguration cnf) { | |||
@@ -2577,8 +2573,4 @@ public class ApplicationConnection { | |||
LayoutManager getLayoutManager() { | |||
return layoutManager; | |||
} | |||
public SerializerMap getSerializerMap() { | |||
return serializerMap; | |||
} | |||
} |
@@ -31,6 +31,8 @@ import com.google.gwt.json.client.JSONValue; | |||
import com.vaadin.shared.Connector; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.ConnectorMap; | |||
import com.vaadin.terminal.gwt.client.metadata.NoDataException; | |||
import com.vaadin.terminal.gwt.client.metadata.Property; | |||
import com.vaadin.terminal.gwt.client.metadata.Type; | |||
/** | |||
@@ -106,18 +108,42 @@ public class JsonDecoder { | |||
private static Object decodeObject(Type type, JSONValue jsonValue, | |||
Object target, ApplicationConnection connection) { | |||
JSONSerializer<Object> serializer = connection.getSerializerMap() | |||
.getSerializer(type.getBaseTypeName()); | |||
// TODO handle case with no serializer found | |||
// Currently getSerializer throws exception if not found | |||
if (target != null && serializer instanceof DiffJSONSerializer<?>) { | |||
DiffJSONSerializer<Object> diffSerializer = (DiffJSONSerializer<Object>) serializer; | |||
diffSerializer.update(target, type, jsonValue, connection); | |||
return target; | |||
JSONSerializer<Object> serializer = (JSONSerializer<Object>) type | |||
.findSerializer(); | |||
if (serializer != null) { | |||
if (target != null && serializer instanceof DiffJSONSerializer<?>) { | |||
DiffJSONSerializer<Object> diffSerializer = (DiffJSONSerializer<Object>) serializer; | |||
diffSerializer.update(target, type, jsonValue, connection); | |||
return target; | |||
} else { | |||
Object object = serializer.deserialize(type, jsonValue, | |||
connection); | |||
return object; | |||
} | |||
} else { | |||
Object object = serializer.deserialize(type, jsonValue, connection); | |||
return object; | |||
try { | |||
Collection<Property> properties = type.getProperties(); | |||
if (target == null) { | |||
target = type.createInstance(); | |||
} | |||
JSONObject jsonObject = jsonValue.isObject(); | |||
for (Property property : properties) { | |||
JSONValue encodedPropertyValue = jsonObject.get(property | |||
.getName()); | |||
if (encodedPropertyValue == null) { | |||
continue; | |||
} | |||
Object propertyReference = property.getValue(target); | |||
Object decodedValue = decodeValue(property.getType(), | |||
encodedPropertyValue, propertyReference, connection); | |||
property.setValue(target, decodedValue); | |||
} | |||
return target; | |||
} catch (NoDataException e) { | |||
throw new RuntimeException("Can not deserialize " | |||
+ type.getSignature(), e); | |||
} | |||
} | |||
} | |||
@@ -33,6 +33,9 @@ import com.vaadin.shared.Connector; | |||
import com.vaadin.shared.JsonConstants; | |||
import com.vaadin.shared.communication.UidlValue; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.metadata.NoDataException; | |||
import com.vaadin.terminal.gwt.client.metadata.Property; | |||
import com.vaadin.terminal.gwt.client.metadata.Type; | |||
/** | |||
* Encoder for converting RPC parameters and other values to JSON for transfer | |||
@@ -99,12 +102,33 @@ public class JsonEncoder { | |||
} else { | |||
// Try to find a generated serializer object, class name is the | |||
// type | |||
transportType = value.getClass().getName(); | |||
JSONSerializer serializer = connection.getSerializerMap() | |||
.getSerializer(transportType); | |||
Type type = new Type(value.getClass()); | |||
JSONSerializer<Object> serializer = (JSONSerializer<Object>) type | |||
.findSerializer(); | |||
if (serializer != null) { | |||
return serializer.serialize(value, connection); | |||
} else { | |||
try { | |||
Collection<Property> properties = type.getProperties(); | |||
JSONObject jsonObject = new JSONObject(); | |||
for (Property property : properties) { | |||
Object propertyValue = property.getValue(value); | |||
JSONValue encodedPropertyValue = encode( | |||
propertyValue, restrictToInternalTypes, | |||
connection); | |||
jsonObject.put(property.getName(), | |||
encodedPropertyValue); | |||
} | |||
return jsonObject; | |||
} catch (NoDataException e) { | |||
throw new RuntimeException("Can not encode " | |||
+ type.getSignature(), e); | |||
} | |||
} | |||
// TODO handle case with no serializer found | |||
return serializer.serialize(value, connection); | |||
} | |||
} | |||
} |
@@ -1,44 +0,0 @@ | |||
/* | |||
* Copyright 2011 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.terminal.gwt.client.communication; | |||
/** | |||
* Provide a mapping from a type (communicated between the server and the | |||
* client) and a {@link JSONSerializer} instance. | |||
* | |||
* An implementation of this class is created at GWT compilation time by | |||
* SerializerMapGenerator, so this interface can be instantiated with | |||
* GWT.create(). | |||
* | |||
* @since 7.0 | |||
*/ | |||
public interface SerializerMap { | |||
/** | |||
* Returns a serializer instance for a given type. | |||
* | |||
* @param type | |||
* type communicated on between the server and the client | |||
* (currently fully qualified class name) | |||
* @return serializer instance, not null | |||
* @throws RuntimeException | |||
* if no serializer is found | |||
*/ | |||
// TODO better error handling in javadoc and in generator | |||
public JSONSerializer getSerializer(String type); | |||
} |
@@ -1,52 +0,0 @@ | |||
/* | |||
* Copyright 2011 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.terminal.gwt.client.communication; | |||
public class Type { | |||
private final String baseTypeName; | |||
private final Type[] parameterTypes; | |||
public Type(String baseTypeName, Type[] parameterTypes) { | |||
this.baseTypeName = baseTypeName; | |||
this.parameterTypes = parameterTypes; | |||
} | |||
public String getBaseTypeName() { | |||
return baseTypeName; | |||
} | |||
public Type[] getParameterTypes() { | |||
return parameterTypes; | |||
} | |||
@Override | |||
public String toString() { | |||
String string = baseTypeName; | |||
if (parameterTypes != null) { | |||
string += '<'; | |||
for (int i = 0; i < parameterTypes.length; i++) { | |||
if (i != 0) { | |||
string += ','; | |||
} | |||
string += parameterTypes[i].toString(); | |||
} | |||
string += '>'; | |||
} | |||
return string; | |||
} | |||
} |
@@ -5,5 +5,5 @@ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
public interface Invoker { | |||
public Object invoke(Object target, Object[] params); | |||
public Object invoke(Object target, Object... params); | |||
} |
@@ -5,16 +5,20 @@ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
public class Property { | |||
private final Type type; | |||
private final Type bean; | |||
private final String name; | |||
public Property(Type type, String name) { | |||
this.type = type; | |||
public Property(Type bean, String name) { | |||
this.bean = bean; | |||
this.name = name; | |||
} | |||
public Object getValue(Object bean) throws NoDataException { | |||
return TypeDataStore.getGetter(this).invoke(bean, null); | |||
return TypeDataStore.getGetter(this).invoke(bean); | |||
} | |||
public void setValue(Object bean, Object value) throws NoDataException { | |||
TypeDataStore.getSetter(this).invoke(bean, value); | |||
} | |||
public String getDelegateToWidgetMethod() { | |||
@@ -29,8 +33,12 @@ public class Property { | |||
} | |||
} | |||
public Type getType() throws NoDataException { | |||
return TypeDataStore.getType(this); | |||
} | |||
public String getSignature() { | |||
return type.toString() + "." + name; | |||
return bean.toString() + "." + name; | |||
} | |||
@Override | |||
@@ -50,4 +58,13 @@ public class Property { | |||
return getSignature().hashCode(); | |||
} | |||
public String getName() { | |||
return name; | |||
} | |||
@Override | |||
public String toString() { | |||
return getSignature(); | |||
} | |||
} |
@@ -3,6 +3,10 @@ | |||
*/ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
import java.util.Collection; | |||
import com.vaadin.terminal.gwt.client.communication.JSONSerializer; | |||
public class Type { | |||
private final String name; | |||
private final Type[] parameterTypes; | |||
@@ -27,13 +31,17 @@ public class Type { | |||
public Object createInstance() throws NoDataException { | |||
Invoker invoker = TypeDataStore.getConstructor(this); | |||
return invoker.invoke(null, null); | |||
return invoker.invoke(null); | |||
} | |||
public Method getMethod(String name) { | |||
return new Method(this, name); | |||
} | |||
public Collection<Property> getProperties() throws NoDataException { | |||
return TypeDataStore.getProperties(this); | |||
} | |||
public Property getProperty(String propertyName) { | |||
return new Property(this, propertyName); | |||
} | |||
@@ -82,4 +90,8 @@ public class Type { | |||
.createProxy(invokationHandler); | |||
} | |||
public JSONSerializer<?> findSerializer() { | |||
return TypeDataStore.findSerializer(this); | |||
} | |||
} |
@@ -4,17 +4,23 @@ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import com.vaadin.terminal.gwt.client.communication.JSONSerializer; | |||
public class TypeDataStore { | |||
private static final String CONSTRUCTOR_NAME = "!new"; | |||
private final Map<String, Class<?>> identifiers = new HashMap<String, Class<?>>(); | |||
private final Map<Type, Invoker> serializerFactories = new HashMap<Type, Invoker>(); | |||
private final Map<Type, ProxyHandler> proxyHandlers = new HashMap<Type, ProxyHandler>(); | |||
private final Map<Type, Collection<Property>> properties = new HashMap<Type, Collection<Property>>(); | |||
private final Set<Method> delayedMethods = new HashSet<Method>(); | |||
private final Set<Method> lastonlyMethods = new HashSet<Method>(); | |||
@@ -23,6 +29,8 @@ public class TypeDataStore { | |||
private final Map<Method, Invoker> invokers = new HashMap<Method, Invoker>(); | |||
private final Map<Method, Type[]> paramTypes = new HashMap<Method, Type[]>(); | |||
private final Map<Property, Type> propertyTypes = new HashMap<Property, Type>(); | |||
private final Map<Property, Invoker> setters = new HashMap<Property, Invoker>(); | |||
private final Map<Property, Invoker> getters = new HashMap<Property, Invoker>(); | |||
private final Map<Property, String> delegateToWidget = new HashMap<Property, String>(); | |||
@@ -85,6 +93,10 @@ public class TypeDataStore { | |||
return getter; | |||
} | |||
public void setGetter(Class<?> clazz, String propertyName, Invoker invoker) { | |||
getters.put(new Property(getType(clazz), propertyName), invoker); | |||
} | |||
public static String getDelegateToWidget(Property property) { | |||
return get().delegateToWidget.get(property); | |||
} | |||
@@ -148,4 +160,61 @@ public class TypeDataStore { | |||
public void setLastonly(Class<?> clazz, String methodName) { | |||
lastonlyMethods.add(getType(clazz).getMethod(methodName)); | |||
} | |||
public static Collection<Property> getProperties(Type type) | |||
throws NoDataException { | |||
Collection<Property> properties = get().properties.get(type); | |||
if (properties == null) { | |||
throw new NoDataException("No property list for " | |||
+ type.getSignature()); | |||
} | |||
return properties; | |||
} | |||
public void setProperties(Class<?> clazz, String[] propertyNames) { | |||
Set<Property> properties = new HashSet<Property>(); | |||
Type type = getType(clazz); | |||
for (String name : propertyNames) { | |||
properties.add(new Property(type, name)); | |||
} | |||
this.properties.put(type, Collections.unmodifiableSet(properties)); | |||
} | |||
public static Type getType(Property property) throws NoDataException { | |||
Type type = get().propertyTypes.get(property); | |||
if (type == null) { | |||
throw new NoDataException("No return type for " | |||
+ property.getSignature()); | |||
} | |||
return type; | |||
} | |||
public void setPropertyType(Class<?> clazz, String propertName, Type type) { | |||
propertyTypes.put(new Property(getType(clazz), propertName), type); | |||
} | |||
public static Invoker getSetter(Property property) throws NoDataException { | |||
Invoker setter = get().setters.get(property); | |||
if (setter == null) { | |||
throw new NoDataException("No setter for " | |||
+ property.getSignature()); | |||
} | |||
return setter; | |||
} | |||
public void setSetter(Class<?> clazz, String propertyName, Invoker setter) { | |||
setters.put(new Property(getType(clazz), propertyName), setter); | |||
} | |||
public void setSerializerFactory(Class<?> clazz, Invoker factory) { | |||
serializerFactories.put(getType(clazz), factory); | |||
} | |||
public static JSONSerializer<?> findSerializer(Type type) { | |||
Invoker factoryCreator = get().serializerFactories.get(type); | |||
if (factoryCreator == null) { | |||
return null; | |||
} | |||
return (JSONSerializer<?>) factoryCreator.invoke(null); | |||
} | |||
} |