Change-Id: I49411e6893f3adc1c41cc690aca35cd919769625tags/7.2.0.beta1
@@ -19,6 +19,8 @@ import java.io.PrintWriter; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
@@ -74,6 +76,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
private final SourceWriter target; | |||
private final String baseName; | |||
private final int splitSize; | |||
private final List<String> methodNames; | |||
// Seems to be undercounted by about 15% | |||
private int approximateChars = 0; | |||
@@ -84,6 +87,8 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
this.target = target; | |||
this.baseName = baseName; | |||
this.splitSize = splitSize; | |||
methodNames = new ArrayList<String>(); | |||
methodNames.add(baseName); | |||
} | |||
@Override | |||
@@ -169,17 +174,34 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
} | |||
public void splitIfNeeded() { | |||
splitIfNeeded(false, null); | |||
} | |||
public void splitIfNeeded(boolean isNative, String params) { | |||
if (approximateChars > splitSize) { | |||
String newMethod = baseName + wrapCount++; | |||
println("%s();", newMethod); | |||
outdent(); | |||
println("}"); | |||
println("private void %s() {", newMethod); | |||
String args = params == null ? "" : params; | |||
if (isNative) { | |||
outdent(); | |||
println("}-*/;"); | |||
println("private native void %s(%s) /*-{", newMethod, args); | |||
} else { | |||
println("%s();", newMethod); | |||
outdent(); | |||
println("}"); | |||
println("private void %s(%s) {", newMethod, args); | |||
} | |||
methodNames.add(newMethod); | |||
indent(); | |||
approximateChars = 0; | |||
} | |||
} | |||
public List<String> getMethodNames() { | |||
return Collections.unmodifiableList(methodNames); | |||
} | |||
} | |||
@Override | |||
@@ -227,6 +249,8 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
w.indent(); | |||
for (ConnectorBundle bundle : bundles) { | |||
detectBadProperties(bundle, logger); | |||
String name = bundle.getName(); | |||
boolean isEager = name | |||
.equals(ConnectorBundleLoader.EAGER_BUNDLE_NAME); | |||
@@ -275,12 +299,34 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
w.println("private void load() {"); | |||
w.indent(); | |||
printBundleData(logger, w, bundle); | |||
String loadNativeJsBundle = "loadJsBundle"; | |||
printBundleData(logger, w, bundle, loadNativeJsBundle); | |||
// Close load method | |||
w.outdent(); | |||
w.println("}"); | |||
// Separate method for loading native JS stuff (e.g. callbacks) | |||
String loadNativeJsMethodName = "loadNativeJs"; | |||
w.println("private native void %s(%s store) /*-{", | |||
loadNativeJsMethodName, TypeDataStore.class.getName()); | |||
w.indent(); | |||
List<String> jsMethodNames = printJsBundleData(logger, w, bundle, | |||
loadNativeJsMethodName); | |||
w.outdent(); | |||
w.println("}-*/;"); | |||
// Call all generated native method inside one Java method to avoid | |||
// refercences inside native methods to each other | |||
w.println("private void %s(%s store) {", loadNativeJsBundle, | |||
TypeDataStore.class.getName()); | |||
w.indent(); | |||
printLoadJsBundleData(w, loadNativeJsBundle, jsMethodNames); | |||
w.outdent(); | |||
w.println("}"); | |||
// onFailure method declaration starts | |||
w.println("public void onFailure(Throwable reason) {"); | |||
w.indent(); | |||
@@ -315,27 +361,153 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
w.commit(logger); | |||
} | |||
private void printLoadJsBundleData(SourceWriter w, String methodName, | |||
List<String> methods) { | |||
SplittingSourceWriter writer = new SplittingSourceWriter(w, methodName, | |||
30000); | |||
for (String method : methods) { | |||
writer.println("%s(store);", method); | |||
writer.splitIfNeeded(); | |||
} | |||
} | |||
private void detectBadProperties(ConnectorBundle bundle, TreeLogger logger) | |||
throws UnableToCompleteException { | |||
Map<JClassType, Set<String>> definedProperties = new HashMap<JClassType, Set<String>>(); | |||
for (Property property : bundle.getNeedsProperty()) { | |||
JClassType beanType = property.getBeanType(); | |||
Set<String> usedPropertyNames = definedProperties.get(beanType); | |||
if (usedPropertyNames == null) { | |||
usedPropertyNames = new HashSet<String>(); | |||
definedProperties.put(beanType, usedPropertyNames); | |||
} | |||
String name = property.getName(); | |||
if (!usedPropertyNames.add(name)) { | |||
logger.log(Type.ERROR, beanType.getQualifiedSourceName() | |||
+ " has multiple properties with the name " + name | |||
+ ". This can happen if there are multiple " | |||
+ "setters with identical names ignoring case."); | |||
throw new UnableToCompleteException(); | |||
} | |||
if (!property.hasAccessorMethods()) { | |||
logger.log(Type.ERROR, beanType.getQualifiedSourceName() | |||
+ " has the property '" + name | |||
+ "' without getter defined."); | |||
throw new UnableToCompleteException(); | |||
} | |||
} | |||
} | |||
private List<String> printJsBundleData(TreeLogger logger, SourceWriter w, | |||
ConnectorBundle bundle, String methodName) { | |||
SplittingSourceWriter writer = new SplittingSourceWriter(w, methodName, | |||
30000); | |||
Set<Property> needsProperty = bundle.getNeedsProperty(); | |||
for (Property property : needsProperty) { | |||
writer.println("var data = {"); | |||
writer.indent(); | |||
writer.println("setter: function(bean, value) {"); | |||
writer.indent(); | |||
property.writeSetterBody(logger, writer, "bean", "value"); | |||
writer.outdent(); | |||
writer.println("},"); | |||
writer.println("getter: function(bean) {"); | |||
writer.indent(); | |||
property.writeGetterBody(logger, writer, "bean"); | |||
writer.outdent(); | |||
writer.println("}"); | |||
writer.outdent(); | |||
writer.println("};"); | |||
// Method declaration | |||
writer.print( | |||
"store.@%s::setPropertyData(Ljava/lang/Class;Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)", | |||
TypeDataStore.class.getName()); | |||
writer.println("(@%s::class, '%s', data);", property.getBeanType() | |||
.getQualifiedSourceName(), property.getName()); | |||
writer.println(); | |||
writer.splitIfNeeded(true, | |||
String.format("%s store", TypeDataStore.class.getName())); | |||
} | |||
return writer.getMethodNames(); | |||
} | |||
private void printBundleData(TreeLogger logger, SourceWriter sourceWriter, | |||
ConnectorBundle bundle) throws UnableToCompleteException { | |||
ConnectorBundle bundle, String loadNativeJsMethodName) | |||
throws UnableToCompleteException { | |||
// Split into new load method when reaching approximately 30000 bytes | |||
SplittingSourceWriter w = new SplittingSourceWriter(sourceWriter, | |||
"load", 30000); | |||
writeSuperClasses(w, bundle); | |||
writeIdentifiers(w, bundle); | |||
writeGwtConstructors(w, bundle); | |||
writeReturnTypes(w, bundle); | |||
writeInvokers(w, bundle); | |||
writeParamTypes(w, bundle); | |||
writeProxys(w, bundle); | |||
wirteDelayedInfo(w, bundle); | |||
writeProperites(logger, w, bundle); | |||
writePropertyTypes(w, bundle); | |||
writeSetters(logger, w, bundle); | |||
writeGetters(logger, w, bundle); | |||
writeDelayedInfo(w, bundle); | |||
w.println("%s(store);", loadNativeJsMethodName); | |||
// Must use Java code to generate Type data (because of Type[]), doing | |||
// this after the JS property data has been initialized | |||
writePropertyTypes(logger, w, bundle); | |||
writeSerializers(logger, w, bundle); | |||
writeDelegateToWidget(logger, w, bundle); | |||
} | |||
private void writeSuperClasses(SplittingSourceWriter w, | |||
ConnectorBundle bundle) { | |||
List<JClassType> needsSuperclass = new ArrayList<JClassType>( | |||
bundle.getNeedsSuperclass()); | |||
// Emit in hierarchy order to ensure superclass is defined when | |||
// referenced | |||
Collections.sort(needsSuperclass, new Comparator<JClassType>() { | |||
@Override | |||
public int compare(JClassType type1, JClassType type2) { | |||
int depthDiff = getDepth(type1) - getDepth(type2); | |||
if (depthDiff != 0) { | |||
return depthDiff; | |||
} else { | |||
// Just something to get a stable compare | |||
return type1.getName().compareTo(type2.getName()); | |||
} | |||
} | |||
private int getDepth(JClassType type) { | |||
int depth = 0; | |||
while (type != null) { | |||
depth++; | |||
type = type.getSuperclass(); | |||
} | |||
return depth; | |||
} | |||
}); | |||
for (JClassType jClassType : needsSuperclass) { | |||
JClassType superclass = jClassType.getSuperclass(); | |||
while (superclass != null && !superclass.isPublic()) { | |||
superclass = superclass.getSuperclass(); | |||
} | |||
String classLiteralString; | |||
if (superclass == null) { | |||
classLiteralString = "null"; | |||
} else { | |||
classLiteralString = getClassLiteralString(superclass); | |||
} | |||
w.println("store.setSuperClass(%s, %s);", | |||
getClassLiteralString(jClassType), classLiteralString); | |||
} | |||
} | |||
private void writeDelegateToWidget(TreeLogger logger, | |||
SplittingSourceWriter w, ConnectorBundle bundle) { | |||
Set<Property> needsDelegateToWidget = bundle.getNeedsDelegateToWidget(); | |||
@@ -378,64 +550,9 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
} | |||
} | |||
private void writeGetters(TreeLogger logger, SplittingSourceWriter w, | |||
private void writePropertyTypes(TreeLogger logger, SplittingSourceWriter 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.println("\", 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("});"); | |||
w.splitIfNeeded(); | |||
} | |||
} | |||
private void writeSetters(TreeLogger logger, SplittingSourceWriter 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("});"); | |||
w.splitIfNeeded(); | |||
} | |||
} | |||
private void writePropertyTypes(SplittingSourceWriter w, | |||
ConnectorBundle bundle) { | |||
Set<Property> properties = bundle.getNeedsType(); | |||
Set<Property> properties = bundle.getNeedsProperty(); | |||
for (Property property : properties) { | |||
w.print("store.setPropertyType("); | |||
writeClassLiteral(w, property.getBeanType()); | |||
@@ -449,40 +566,7 @@ public class ConnectorBundleLoaderFactory extends Generator { | |||
} | |||
} | |||
private void writeProperites(TreeLogger logger, SplittingSourceWriter 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("});"); | |||
w.splitIfNeeded(); | |||
} | |||
} | |||
private void wirteDelayedInfo(SplittingSourceWriter w, | |||
private void writeDelayedInfo(SplittingSourceWriter w, | |||
ConnectorBundle bundle) { | |||
Map<JClassType, Set<JMethod>> needsDelayedInfo = bundle | |||
.getNeedsDelayedInfo(); |
@@ -59,7 +59,7 @@ public class ConnectorBundle { | |||
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> needsSuperClass = new HashSet<JClassType>(); | |||
private final Set<JClassType> needsGwtConstructor = new HashSet<JClassType>(); | |||
private final Set<JClassType> visitedTypes = new HashSet<JClassType>(); | |||
private final Set<JClassType> needsProxySupport = new HashSet<JClassType>(); | |||
@@ -70,9 +70,7 @@ public class ConnectorBundle { | |||
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 final Set<Property> needsProperty = new HashSet<Property>(); | |||
private final Set<Property> needsDelegateToWidget = new HashSet<Property>(); | |||
private ConnectorBundle(String name, ConnectorBundle previousBundle, | |||
@@ -246,16 +244,18 @@ public class ConnectorBundle { | |||
logger.log(Type.INFO, "Will serialize " + type + " as a bean"); | |||
setNeedsPropertyList(typeAsClass); | |||
JClassType needsSuperClass = typeAsClass; | |||
while (needsSuperClass != null) { | |||
if (needsSuperClass.isPublic()) { | |||
setNeedsSuperclass(needsSuperClass); | |||
} | |||
needsSuperClass = needsSuperClass.getSuperclass(); | |||
} | |||
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); | |||
setNeedsProperty(property); | |||
JType propertyType = property.getPropertyType(); | |||
setNeedsSerialize(propertyType); | |||
@@ -304,80 +304,42 @@ public class ConnectorBundle { | |||
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 void setNeedsSuperclass(JClassType typeAsClass) { | |||
if (!isNeedsSuperClass(typeAsClass)) { | |||
needsSuperClass.add(typeAsClass); | |||
} | |||
} | |||
private boolean isNeedsSetter(Property property) { | |||
if (needsSetter.contains(property)) { | |||
private boolean isNeedsSuperClass(JClassType typeAsClass) { | |||
if (needsSuperClass.contains(typeAsClass)) { | |||
return true; | |||
} else { | |||
return previousBundle != null | |||
&& previousBundle.isNeedsSetter(property); | |||
&& previousBundle.isNeedsSuperClass(typeAsClass); | |||
} | |||
} | |||
public Set<Property> getNeedsSetter() { | |||
return Collections.unmodifiableSet(needsSetter); | |||
public Set<JClassType> getNeedsSuperclass() { | |||
return Collections.unmodifiableSet(needsSuperClass); | |||
} | |||
private void setNeedsPropertyList(JClassType type) { | |||
if (!isNeedsPropertyList(type)) { | |||
needsPropertyList.add(type); | |||
private void setNeedsProperty(Property property) { | |||
if (!isNeedsProperty(property)) { | |||
needsProperty.add(property); | |||
} | |||
} | |||
private boolean isNeedsPropertyList(JClassType type) { | |||
if (needsPropertyList.contains(type)) { | |||
private boolean isNeedsProperty(Property property) { | |||
if (needsProperty.contains(property)) { | |||
return true; | |||
} else { | |||
return previousBundle != null | |||
&& previousBundle.isNeedsPropertyList(type); | |||
&& previousBundle.isNeedsProperty(property); | |||
} | |||
} | |||
public Set<JClassType> getNeedsPropertyListing() { | |||
return Collections.unmodifiableSet(needsPropertyList); | |||
public Set<Property> getNeedsProperty() { | |||
return Collections.unmodifiableSet(needsProperty); | |||
} | |||
public Collection<Property> getProperties(JClassType type) { | |||
@@ -623,4 +585,4 @@ public class ConnectorBundle { | |||
return Collections.unmodifiableSet(needsDelegateToWidget); | |||
} | |||
} | |||
} |
@@ -37,19 +37,26 @@ public class FieldProperty extends Property { | |||
this.field = field; | |||
} | |||
@Override | |||
public boolean hasAccessorMethods() { | |||
return true; | |||
} | |||
@Override | |||
public void writeSetterBody(TreeLogger logger, SourceWriter w, | |||
String beanVariable, String valueVariable) { | |||
w.print("((%s) %s).%s = (%s)%s;", getBeanType() | |||
.getQualifiedSourceName(), beanVariable, getName(), | |||
getUnboxedPropertyTypeName(), valueVariable); | |||
w.println("%s.@%s::%s = %s;", beanVariable, getBeanType() | |||
.getQualifiedSourceName(), getName(), unboxValue(valueVariable)); | |||
} | |||
@Override | |||
public void writeGetterBody(TreeLogger logger, SourceWriter w, | |||
String beanVariable) { | |||
w.print("return ((%s) %s).%s;", getBeanType().getQualifiedSourceName(), | |||
beanVariable, getName()); | |||
String value = String.format("%s.@%s::%s", beanVariable, getBeanType() | |||
.getQualifiedSourceName(), getName()); | |||
w.print("return "); | |||
w.print(boxValue(value)); | |||
w.println(";"); | |||
} | |||
public static Collection<FieldProperty> findProperties(JClassType type) { | |||
@@ -57,7 +64,7 @@ public class FieldProperty extends Property { | |||
List<JField> fields = getPublicFields(type); | |||
for (JField field : fields) { | |||
properties.add(new FieldProperty(type, field)); | |||
properties.add(new FieldProperty(field.getEnclosingType(), field)); | |||
} | |||
return properties; |
@@ -19,7 +19,9 @@ package com.vaadin.server.widgetsetutils.metadata; | |||
import java.lang.annotation.Annotation; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import com.google.gwt.core.ext.TreeLogger; | |||
import com.google.gwt.core.ext.typeinfo.JClassType; | |||
@@ -31,18 +33,29 @@ public class MethodProperty extends Property { | |||
private final JMethod setter; | |||
private MethodProperty(JClassType beanType, JMethod setter) { | |||
private final String getter; | |||
private MethodProperty(JClassType beanType, JMethod setter, String getter) { | |||
super(getTransportFieldName(setter), beanType, setter | |||
.getParameterTypes()[0]); | |||
this.setter = setter; | |||
this.getter = getter; | |||
} | |||
@Override | |||
public boolean hasAccessorMethods() { | |||
return getter != null; | |||
} | |||
public static Collection<MethodProperty> findProperties(JClassType type) { | |||
Collection<MethodProperty> properties = new ArrayList<MethodProperty>(); | |||
List<JMethod> setters = getSetters(type); | |||
Set<String> getters = new HashSet<String>(); | |||
List<JMethod> setters = getSetters(type, getters); | |||
for (JMethod setter : setters) { | |||
properties.add(new MethodProperty(type, setter)); | |||
String getter = findGetter(type, setter); | |||
properties.add(new MethodProperty(setter.getEnclosingType(), | |||
setter, getters.contains(getter) ? getter : null)); | |||
} | |||
return properties; | |||
@@ -53,9 +66,12 @@ public class MethodProperty extends Property { | |||
* | |||
* @param beanType | |||
* The type to check | |||
* @param getters | |||
* Set that will be filled with names of getters. | |||
* @return A list of setter methods from the class and its parents | |||
*/ | |||
private static List<JMethod> getSetters(JClassType beanType) { | |||
private static List<JMethod> getSetters(JClassType beanType, | |||
Set<String> getters) { | |||
List<JMethod> setterMethods = new ArrayList<JMethod>(); | |||
while (beanType != null | |||
@@ -63,13 +79,19 @@ public class MethodProperty extends Property { | |||
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 | |||
if (!method.isPublic() || method.isStatic()) { | |||
// Not getter/setter, skip to next method | |||
continue; | |||
} | |||
setterMethods.add(method); | |||
String methodName = method.getName(); | |||
if (methodName.startsWith("set") | |||
&& method.getParameterTypes().length == 1) { | |||
setterMethods.add(method); | |||
} else if (method.getParameterTypes().length == 0 | |||
&& methodName.startsWith("is") | |||
|| methodName.startsWith("get")) { | |||
getters.add(methodName); | |||
} | |||
} | |||
beanType = beanType.getSuperclass(); | |||
} | |||
@@ -78,34 +100,26 @@ public class MethodProperty extends Property { | |||
} | |||
@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(");"); | |||
public void writeGetterBody(TreeLogger logger, SourceWriter w, | |||
String beanVariable) { | |||
String value = String.format("%s.@%s::%s()()", beanVariable, | |||
getBeanType().getQualifiedSourceName(), getter); | |||
w.print("return "); | |||
w.print(boxValue(value)); | |||
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("();"); | |||
public void writeSetterBody(TreeLogger logger, SourceWriter w, | |||
String beanVariable, String valueVariable) { | |||
w.println("%s.@%s::%s(%s)(%s);", beanVariable, getBeanType() | |||
.getQualifiedSourceName(), setter.getName(), setter | |||
.getParameterTypes()[0].getJNISignature(), | |||
unboxValue(valueVariable)); | |||
} | |||
private String findGetter(JClassType beanType, JMethod setterMethod) { | |||
private static String findGetter(JClassType beanType, JMethod setterMethod) { | |||
JType setterParameterType = setterMethod.getParameterTypes()[0]; | |||
String fieldName = setterMethod.getName().substring(3); | |||
if (setterParameterType.getQualifiedSourceName().equals( |
@@ -53,6 +53,28 @@ public abstract class Property { | |||
} | |||
} | |||
public String boxValue(String codeSnippet) { | |||
JPrimitiveType primitive = propertyType.isPrimitive(); | |||
if (primitive == null) { | |||
return codeSnippet; | |||
} else { | |||
return String.format("@%s::valueOf(%s)(%s)", | |||
primitive.getQualifiedBoxedSourceName(), | |||
propertyType.getJNISignature(), codeSnippet); | |||
} | |||
} | |||
public String unboxValue(String codeSnippet) { | |||
JPrimitiveType primitive = propertyType.isPrimitive(); | |||
if (primitive == null) { | |||
return codeSnippet; | |||
} else { | |||
return String.format("%s.@%s::%sValue()()", codeSnippet, | |||
primitive.getQualifiedBoxedSourceName(), | |||
primitive.getSimpleSourceName()); | |||
} | |||
} | |||
public JClassType getBeanType() { | |||
return beanType; | |||
} | |||
@@ -63,6 +85,8 @@ public abstract class Property { | |||
public abstract void writeGetterBody(TreeLogger logger, SourceWriter w, | |||
String beanVariable); | |||
public abstract boolean hasAccessorMethods(); | |||
@Override | |||
public boolean equals(Object obj) { | |||
if (this == obj) { |
@@ -15,11 +15,10 @@ | |||
*/ | |||
package com.vaadin.client.metadata; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import com.google.gwt.core.shared.GWT; | |||
import com.vaadin.client.FastStringMap; | |||
import com.vaadin.client.metadata.AsyncBundleLoader.State; | |||
public abstract class ConnectorBundleLoader { | |||
@@ -28,8 +27,9 @@ public abstract class ConnectorBundleLoader { | |||
private static ConnectorBundleLoader impl; | |||
private Map<String, AsyncBundleLoader> asyncBlockLoaders = new HashMap<String, AsyncBundleLoader>(); | |||
private Map<String, String> identifierToBundle = new HashMap<String, String>(); | |||
private FastStringMap<AsyncBundleLoader> asyncBlockLoaders = FastStringMap | |||
.create(); | |||
private FastStringMap<String> identifierToBundle = FastStringMap.create(); | |||
private final TypeDataStore datStore = new TypeDataStore(); | |||
@@ -30,11 +30,11 @@ public class Property { | |||
} | |||
public Object getValue(Object bean) throws NoDataException { | |||
return TypeDataStore.getGetter(this).invoke(bean); | |||
return TypeDataStore.getValue(this, bean); | |||
} | |||
public void setValue(Object bean, Object value) throws NoDataException { | |||
TypeDataStore.getSetter(this).invoke(bean, value); | |||
TypeDataStore.setValue(this, bean, value); | |||
} | |||
public String getDelegateToWidgetMethodName() { | |||
@@ -50,6 +50,10 @@ public class Property { | |||
return TypeDataStore.getType(this); | |||
} | |||
public Type getBeanType() { | |||
return bean; | |||
} | |||
/** | |||
* The unique signature used to identify this property. The structure of the | |||
* returned string may change without notice and should not be used for any |
@@ -34,8 +34,6 @@ public class TypeDataStore { | |||
.create(); | |||
private final FastStringMap<ProxyHandler> proxyHandlers = FastStringMap | |||
.create(); | |||
private final FastStringMap<JsArrayObject<Property>> properties = FastStringMap | |||
.create(); | |||
private final FastStringMap<JsArrayString> delegateToWidgetProperties = FastStringMap | |||
.create(); | |||
@@ -46,12 +44,11 @@ public class TypeDataStore { | |||
private final FastStringMap<Invoker> invokers = FastStringMap.create(); | |||
private final FastStringMap<Type[]> paramTypes = FastStringMap.create(); | |||
private final FastStringMap<Type> propertyTypes = FastStringMap.create(); | |||
private final FastStringMap<Invoker> setters = FastStringMap.create(); | |||
private final FastStringMap<Invoker> getters = FastStringMap.create(); | |||
private final FastStringMap<String> delegateToWidget = FastStringMap | |||
.create(); | |||
private final JavaScriptObject jsTypeData = JavaScriptObject.createObject(); | |||
public static TypeDataStore get() { | |||
return ConnectorBundleLoader.get().getTypeDataStore(); | |||
} | |||
@@ -117,19 +114,10 @@ public class TypeDataStore { | |||
return invoker; | |||
} | |||
public static Invoker getGetter(Property property) throws NoDataException { | |||
Invoker getter = get().getters.get(property.getSignature()); | |||
if (getter == null) { | |||
throw new NoDataException("There is no getter for " | |||
+ property.getSignature()); | |||
} | |||
return getter; | |||
} | |||
public void setGetter(Class<?> clazz, String propertyName, Invoker invoker) { | |||
getters.put(new Property(getType(clazz), propertyName).getSignature(), | |||
invoker); | |||
public static Object getValue(Property property, Object target) | |||
throws NoDataException { | |||
return getJsPropertyValue(get().jsTypeData, property.getBeanType() | |||
.getBaseTypeName(), property.getName(), target); | |||
} | |||
public static String getDelegateToWidget(Property property) { | |||
@@ -243,51 +231,31 @@ public class TypeDataStore { | |||
public static JsArrayObject<Property> getPropertiesAsArray(Type type) | |||
throws NoDataException { | |||
JsArrayObject<Property> properties = get().properties.get(type | |||
.getSignature()); | |||
if (properties == null) { | |||
throw new NoDataException("No property list for " | |||
+ type.getSignature()); | |||
} | |||
return properties; | |||
} | |||
JsArrayString names = getJsPropertyNames(get().jsTypeData, | |||
type.getBaseTypeName()); | |||
public void setProperties(Class<?> clazz, String[] propertyNames) { | |||
// Create Property instances for each property name | |||
JsArrayObject<Property> properties = JavaScriptObject.createArray() | |||
.cast(); | |||
Type type = getType(clazz); | |||
for (String name : propertyNames) { | |||
properties.add(new Property(type, name)); | |||
for (int i = 0; i < names.length(); i++) { | |||
properties.add(new Property(type, names.get(i))); | |||
} | |||
this.properties.put(type.getSignature(), properties); | |||
} | |||
public static Type getType(Property property) throws NoDataException { | |||
Type type = get().propertyTypes.get(property.getSignature()); | |||
if (type == null) { | |||
throw new NoDataException("No return type for " | |||
+ property.getSignature()); | |||
} | |||
return type; | |||
return properties; | |||
} | |||
public void setPropertyType(Class<?> clazz, String propertName, Type type) { | |||
propertyTypes.put( | |||
new Property(getType(clazz), propertName).getSignature(), type); | |||
public static Type getType(Property property) throws NoDataException { | |||
return getJsPropertyType(get().jsTypeData, property.getBeanType() | |||
.getBaseTypeName(), property.getName()); | |||
} | |||
public static Invoker getSetter(Property property) throws NoDataException { | |||
Invoker setter = get().setters.get(property.getSignature()); | |||
if (setter == null) { | |||
throw new NoDataException("No setter for " | |||
+ property.getSignature()); | |||
} | |||
return setter; | |||
public void setPropertyType(Class<?> clazz, String propertyName, Type type) { | |||
setJsPropertyType(jsTypeData, clazz.getName(), propertyName, type); | |||
} | |||
public void setSetter(Class<?> clazz, String propertyName, Invoker setter) { | |||
setters.put(new Property(getType(clazz), propertyName).getSignature(), | |||
setter); | |||
public static void setValue(Property property, Object target, Object value) { | |||
setJsPropertyValue(get().jsTypeData, property.getBeanType() | |||
.getBaseTypeName(), property.getName(), target, value); | |||
} | |||
public void setSerializerFactory(Class<?> clazz, Invoker factory) { | |||
@@ -304,6 +272,99 @@ public class TypeDataStore { | |||
} | |||
public static boolean hasProperties(Type type) { | |||
return get().properties.containsKey(type.getSignature()); | |||
return hasJsProperties(get().jsTypeData, type.getBaseTypeName()); | |||
} | |||
public void setSuperClass(Class<?> baseClass, Class<?> superClass) { | |||
String superClassName = superClass == null ? null : superClass | |||
.getName(); | |||
setSuperClass(jsTypeData, baseClass.getName(), superClassName); | |||
} | |||
public void setPropertyData(Class<?> type, String propertyName, | |||
JavaScriptObject propertyData) { | |||
setPropertyData(jsTypeData, type.getName(), propertyName, propertyData); | |||
} | |||
private static native void setPropertyData(JavaScriptObject typeData, | |||
String className, String propertyName, JavaScriptObject propertyData) | |||
/*-{ | |||
typeData[className][propertyName] = propertyData; | |||
}-*/; | |||
/* | |||
* This method sets up prototypes chain for <code>baseClassName</code>. | |||
* Precondition is : <code>superClassName</code> had to be handled before | |||
* its child <code>baseClassName</code>. | |||
* | |||
* It makes all properties defined in the <code>superClassName</code> | |||
* available for <code>baseClassName</code> as well. | |||
*/ | |||
private static native void setSuperClass(JavaScriptObject typeData, | |||
String baseClassName, String superClassName) | |||
/*-{ | |||
var parentType = typeData[superClassName]; | |||
if (parentType !== undefined ){ | |||
var ctor = function () {}; | |||
ctor.prototype = parentType; | |||
typeData[baseClassName] = new ctor; | |||
} | |||
else { | |||
typeData[baseClassName] = {}; | |||
} | |||
}-*/; | |||
private static native boolean hasGetter(JavaScriptObject typeData, | |||
String beanName, String propertyName) | |||
/*-{ | |||
return typeData[beanName][propertyName].getter !== undefined; | |||
}-*/; | |||
private static native boolean hasSetter(JavaScriptObject typeData, | |||
String beanName, String propertyName) | |||
/*-{ | |||
return typeData[beanName][propertyName].setter !== undefined; | |||
}-*/; | |||
private static native Object getJsPropertyValue(JavaScriptObject typeData, | |||
String beanName, String propertyName, Object beanInstance) | |||
/*-{ | |||
return typeData[beanName][propertyName].getter(beanInstance); | |||
}-*/; | |||
private static native void setJsPropertyValue(JavaScriptObject typeData, | |||
String beanName, String propertyName, Object beanInstance, | |||
Object value) | |||
/*-{ | |||
typeData[beanName][propertyName].setter(beanInstance, value); | |||
}-*/; | |||
private static native Type getJsPropertyType(JavaScriptObject typeData, | |||
String beanName, String propertyName) | |||
/*-{ | |||
return typeData[beanName][propertyName].type; | |||
}-*/; | |||
private static native void setJsPropertyType(JavaScriptObject typeData, | |||
String beanName, String propertyName, Type type) | |||
/*-{ | |||
typeData[beanName][propertyName].type = type; | |||
}-*/; | |||
private static native JsArrayString getJsPropertyNames( | |||
JavaScriptObject typeData, String beanName) | |||
/*-{ | |||
var names = []; | |||
for(var name in typeData[beanName]) { | |||
names.push(name); | |||
} | |||
return names; | |||
}-*/; | |||
private static native boolean hasJsProperties(JavaScriptObject typeData, | |||
String beanName) | |||
/*-{ | |||
return typeData[beanName] !== undefined ; | |||
}-*/; | |||
} |