PDA

View Full Version : [1.2.x] Improvement : add a type parameter to BeanModel



sdc
18 Feb 2009, 6:52 AM
It would be really nice to parameterize the BeanModel : add a type parameter which would be the wrapped bean type (and remove the ugly X method parameter on getBean()). Then it would avoid a lot of casts on the getBean() call and it would be more type safe.

It would look like this :


public class BeanModel<B> extends BaseModel {

protected B bean;
protected Map<String, BeanModel> nestedModels = new HashMap<String, BeanModel>();
protected List<String> beanProperties = new ArrayList<String>();

protected BeanModel() {

}

public B getBean() {
return bean;
}

@Override
public Collection<String> getPropertyNames() {
Collection c = super.getPropertyNames();
for (String s : beanProperties) {
c.add(s);
}
return c;
}

@Override
public String toString() {
return getBean().toString();
}

protected Object processValue(Object value) {
return value;
}

protected void setBean(B bean) {
this.bean = bean;
}

}

sdc
18 Feb 2009, 7:03 AM
Of course, that would also lead to add type parameters to BeanModelFactory :

public abstract class BeanModelFactory<B> {

protected abstract BeanModel<B> newInstance();

public BeanModel<B> createModel(B bean) {
BeanModel<B> model = newInstance();
model.setBean(bean);
return model;
}

public List<BeanModel<B>> createModel(Collection<B> beans) {
List<BeanModel<B>> models = new ArrayList<BeanModel<B>>();
for (B obj : beans) {
models.add(createModel(obj));
}
return models;
}

}
and BeanModelLookup :

public abstract class BeanModelLookup {

private static BeanModelLookup instance = (BeanModelLookup) GWT.create(BeanModelLookup.class);

public static BeanModelLookup get() {
return instance;
}

public abstract <B> BeanModelFactory<B> getFactory(Class<B> bean);

}

sdc
26 Mar 2009, 11:54 PM
@Ext team : what do you think of it ? Is it feasible ? Can we expect it for 2.0 release ?

zaccret
1 Apr 2009, 8:43 AM
+1 (and basically +1 for all generics fixes)

zaccret
8 Apr 2009, 4:09 AM
In the BeanModelGenerator, the only thing required would be to add the parameters and some casts. I've tested the following code and sdc suggestion, it is working.


public class BeanModelGenerator extends Generator {

private TypeOracle oracle;
private JClassType beanModelMarkerType;
private JClassType beanModelTagType;
private List<JClassType> beans;

@Override
public String generate(TreeLogger logger, GeneratorContext context, String typeName)
throws UnableToCompleteException {
oracle = context.getTypeOracle();
beanModelMarkerType = oracle.findType(BeanModelMarker.class.getName());
beanModelTagType = oracle.findType(BeanModelTag.class.getName());

try {
// final all beans and bean markers
beans = new ArrayList<JClassType>();
JClassType[] types = oracle.getTypes();
for (JClassType type : types) {
if (isBeanMarker(type)) {
beans.add(getMarkerBean(type));
} else if (isBean(type)) {
beans.add(type);
}
}

final String genPackageName = BeanModelLookup.class.getPackage().getName();
final String genClassName = "BeanModelLookupImpl";

ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(genPackageName,
genClassName);
composer.setSuperclass(BeanModelLookup.class.getName());
composer.addImport("java.util.Map");
composer.addImport("java.util.HashMap");
composer.addImport(BeanModelFactory.class.getName());

PrintWriter pw = context.tryCreate(logger, genPackageName, genClassName);

if (pw != null) {
SourceWriter sw = composer.createSourceWriter(context, pw);
sw.println("private Map<Class, BeanModelFactory> map = new HashMap<Class, BeanModelFactory>();");

sw.println("BeanModelLookupImpl() {");

for (JClassType bean : beans) {
String name = createBean(bean, logger, context);
String factory = createFactory(bean, name, logger, context);

sw.println("map.put(" + bean.getQualifiedSourceName() + ".class, new " + factory + "());");
}

sw.println("}");
sw.println("public BeanModelFactory getFactory(Class beanClass) {");
sw.println("return map.get(beanClass);");
sw.println("}");
sw.commit(logger);
}

return composer.getCreatedClassName();

} catch (Exception e) {
logger.log(TreeLogger.ERROR, "Class " + typeName + " not found.", e);
throw new UnableToCompleteException();
}

}

private String createFactory(JClassType bean, String beanModelName, TreeLogger logger,
GeneratorContext context) throws Exception {
final String genPackageName = BeanModelFactory.class.getPackage().getName();
final String genClassName = "BeanModel_" + bean.getQualifiedSourceName().replace(".", "_")
+ "_Factory";

ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(genPackageName,
genClassName);
composer.setSuperclass(BeanModelFactory.class.getName()+"<"+bean.getQualifiedSourceName()+">");
PrintWriter pw = context.tryCreate(logger, genPackageName, genClassName);

if (pw != null) {
SourceWriter sw = composer.createSourceWriter(context, pw);
sw.println("public BeanModel<"+bean.getQualifiedSourceName()+"> newInstance() {");
sw.println("return new " + beanModelName + "();");
sw.println("}");
sw.commit(logger);
}
return composer.getCreatedClassName();
}

private String createBean(JClassType bean, TreeLogger logger, GeneratorContext context)
throws Exception {
final String genPackageName = bean.getPackage().getName();
final String genClassName = "BeanModel_" + bean.getQualifiedSourceName().replace(".", "_");

ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(genPackageName,
genClassName);
composer.setSuperclass(BeanModel.class.getName()+"<"+bean.getQualifiedSourceName()+">");
composer.addImport(NestedModelUtil.class.getName());
composer.addImport(BeanModel.class.getName());
PrintWriter pw = context.tryCreate(logger, genPackageName, genClassName);

if (pw != null) {
List<JMethod> getters = findGetters(bean);
List<JMethod> setters = findSetters(bean);
SourceWriter sw = composer.createSourceWriter(context, pw);

sw.println("public " + genClassName + "(){");
for (JMethod method : getters) {
String s = method.getName();
String p = lowerFirst(s.substring(s.startsWith("g") ? 3 : 2)); // get or
// is
sw.println("beanProperties.add(\"" + p + "\");");
}
sw.println("}");

createGetMethods(getters, sw, bean.getQualifiedSourceName());
createSetMethods(setters, sw, bean.getQualifiedSourceName());

// delegate equals to bean
sw.println("public boolean equals(Object obj) {");
sw.println(" if (obj instanceof " + "BeanModel" + ") {");
sw.println(" obj = ((BeanModel)obj).getBean();");
sw.println(" }");
sw.println(" return bean.equals(obj);");
sw.println("}");

// delegate hashCode to bean
sw.println("public int hashCode(){");
sw.println(" return bean.hashCode();");
sw.println("}");

sw.commit(logger);
}
return composer.getCreatedClassName();
}

private JClassType getMarkerBean(JClassType type) throws NotFoundException {
BEAN pojo = type.getAnnotation(BEAN.class);
return oracle.getType(pojo.value().getName());
}

private boolean isBean(JClassType type) {
return !type.equals(beanModelTagType) && type.isAssignableTo(beanModelTagType);
}

private boolean isBeanMarker(JClassType type) {
return !type.equals(beanModelMarkerType) && type.isAssignableTo(beanModelMarkerType);
}

private void createGetMethods(List<JMethod> getters, SourceWriter sw, String typeName) {
sw.println("public <X> X get(String s) {");

sw.println("if (allowNestedValues && NestedModelUtil.isNestedProperty(s)) {");
sw.println(" return (X)NestedModelUtil.getNestedValue(this, s);");
sw.println("}");

for (JMethod method : getters) {
JClassType returnType = method.getReturnType().isClassOrInterface();
String s = method.getName();
String p = lowerFirst(s.substring(s.startsWith("g") ? 3 : 2)); // get or

sw.println("if (s.equals(\"" + p + "\")) {");
sw.println("Object value = ((" + typeName + ")bean)." + s + "();");

try {
if (returnType != null) {
if (returnType.isAssignableTo(oracle.getType("java.util.List"))) {
if (returnType.isParameterized() != null) {
JParameterizedType type = returnType.isParameterized();
JClassType[] params = type.getTypeArgs();
if (params[0].isAssignableTo(beanModelTagType)) {
sw.println("if (value != null) {");
sw.println(" java.util.List list = (java.util.List)value;");
sw.println(" java.util.List temp = new java.util.ArrayList();");
sw.println(" for (Object obj : list) {");
sw.println(" temp.add(");
sw.println(BeanModelLookup.class.getName() + ".get().getFactory("
+ params[0].getQualifiedSourceName() + ".class).createModel(("+params[0].getQualifiedSourceName()+")obj));");
sw.println(" }");
sw.println(" return (X)temp;");
sw.println("}");
}
}
}
}
// swap returnType as generic types were not matching
// (beans.contains(returnType))
if (returnType != null) {
String t = returnType.getQualifiedSourceName();
if (t.indexOf("extends") == -1) {
returnType = oracle.getType(t);
}
}
if (beans.contains(returnType)) {
sw.println("if (value != null) {");
sw.println(" BeanModel nestedModel = nestedModels.get(s);");
sw.println(" if (nestedModel != null) {");
sw.println(" Object bean = nestedModel.getBean();");
sw.println(" if (!bean.equals(value)){");
sw.println(" nestedModel = null;");
sw.println(" }");
sw.println(" }");
sw.println(" if (nestedModel == null) {");
sw.println(" nestedModel = " + BeanModelLookup.class.getName()
+ ".get().getFactory(" + returnType.getQualifiedSourceName()
+ ".class).createModel(("+returnType.getQualifiedSourceName()+")value);");
sw.println(" nestedModels.put(s, nestedModel);");
sw.println(" }");
sw.println(" return (X)processValue(nestedModel);");
sw.println("}");
}
} catch (Exception e) {
e.printStackTrace();
}

sw.println("return (X)processValue(value);");
sw.println("}");
}
sw.println("return super.get(s);");
sw.println("}");
}

private String lowerFirst(String propName) {
if (propName.length() == 0) {
return propName;
} else if (propName.length() == 1) {
return propName.toLowerCase();
} else {
return propName.substring(0, 1).toLowerCase() + propName.substring(1);
}
}

private void createSetMethods(List<JMethod> properties, SourceWriter sw, String typeName) {
sw.println("public <X> X set(String s, X val) {");
sw.indent();
sw.println("Object obj = val;");
sw.println("if (allowNestedValues && val instanceof BeanModel) {");
sw.println(" obj = ((BeanModel)val).getBean();");
sw.println(" if (nestedModels.containsKey(s)) {");
sw.println(" nestedModels.put(s, (BeanModel)val);");
sw.println(" }");
sw.println("}");

sw.println("if (allowNestedValues && NestedModelUtil.isNestedProperty(s)) {");
sw.println(" return (X)NestedModelUtil.setNestedValue(this, s, val);");
sw.println("}");

for (JMethod method : properties) {
String s = method.getName();
String p = lowerFirst(s.substring(3));
String type = method.getParameters()[0].getType().getQualifiedSourceName();

if (type.indexOf("extends") != -1) {
type = "java.lang.Object";
}

if (type.equals("byte")) {
type = "Byte";
} else if (type.equals("char")) {
type = "Character";
} else if (type.equals("short")) {
type = "Short";
} else if (type.equals("int")) {
type = "Integer";
} else if (type.equals("long")) {
type = "Long";
} else if (type.equals("float")) {
type = "Float";
} else if (type.equals("double")) {
type = "Double";
} else if (type.equals("boolean")) {
type = "Boolean";
}

sw.println("if (s.equals(\"" + p + "\")) {");
sw.println("Object old = get(s);");
sw.println("((" + typeName + ")bean)." + s + "((" + type + ")obj);");
sw.println("notifyPropertyChanged(s, val, old);");
sw.println("return (X)old;");
sw.println("}");
}
sw.println("return super.set(s, val);");
sw.outdent();
sw.println("}");
}

private List<JMethod> findGetters(JClassType cls) {
List<JMethod> methods = new ArrayList<JMethod>();
addGetters(cls, methods);
return methods;
}

private void addGetters(JClassType cls, List<JMethod> methods) {

// ignore methods of Object
if (cls.getSuperclass() != null) {
addGetters(cls.getSuperclass(), methods);
for (JMethod m : cls.getMethods()) {
String name = m.getName();
if ((name.matches("get.*") || name.matches("is.*")) && m.getParameters().length == 0) {
methods.add(m);
}
}
}

}

private List<JMethod> findSetters(JClassType cls) {
List<JMethod> methods = new ArrayList<JMethod>();
addSetters(cls, methods);
return methods;
}

private void addSetters(JClassType cls, List<JMethod> methods) {
if (cls.getSuperclass() != null) {
addSetters(cls.getSuperclass(), methods);
}
for (JMethod m : cls.getMethods()) {
String name = m.getName();
if (name.matches("set.*") && m.getParameters().length == 1) {
methods.add(m);
}
}
}

}

sdc
4 May 2009, 1:12 AM
This is smoothly working with 1.2.3 !!!! Great !!!!
@GXT team : it would be really appreciated if you could include this in GXT !!!

sdc
20 May 2009, 12:29 AM
Something wrong ? :(