PDA

View Full Version : BeanModelGenerator for plain AutoBeans



mwelt
26 Aug 2011, 9:43 AM
Hi,

the generator posted at

http://www.sencha.com/forum/showthread.php?122282-BeanModel-generation-with-AutoBean-support

d (http://www.sencha.com/forum/showthread.php?122282-BeanModel-generation-with-AutoBean-support)oes only support RF. I needed something which works with plain AutoBeans. A little messy but working generator following up:



package gwt.bindery;


import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


import com.extjs.gxt.ui.client.core.FastMap;
import com.extjs.gxt.ui.client.data.BeanModelFactory;
import com.extjs.gxt.ui.client.data.BeanModelLookup;
import com.extjs.gxt.ui.client.data.BeanModelMarker;
import com.extjs.gxt.ui.client.data.BeanModelTag;
import com.extjs.gxt.ui.rebind.core.BeanModelGenerator;
import com.google.gwt.core.ext.GeneratorContext;
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.core.ext.typeinfo.JMethod;
import com.google.gwt.dev.util.Name;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.web.bindery.autobean.shared.impl.AbstractAutoBean;


/**
* BeanModel generator for plain AutoBeans.
* @todo no setters supported yet
* @author Michael Welt inspired by Stig Runar Vangen
*/
public class AutoBeanBeanModelGenerator extends BeanModelGenerator {


/**
* the generator
*/
@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(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.getCanonicalName());
composer.addImport(BeanModelFactory.class.getName());
composer.addImport(Map.class.getName());
composer.addImport(FastMap.class.getName());


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


if (pw != null) {
SourceWriter sw = composer.createSourceWriter(context, pw);


sw.println("public BeanModelFactory getFactory(Class b) {");
sw.indent();


sw.println("String n = b.getName();");

for (int i = 0; i < beans.size(); i++) {
JClassType bean = beans.get(i);

JClassType originBeanType = bean;

/*
* exchange the bean to the underlying interface of the AutoBean
*/
bean = this.getUnderlyingInterface(bean);

String name = createBean(bean, logger, context);
String factory = createFactory(bean, name, logger, context);

/*
* AutoBean runtime implementationclasses start with the myBeanAutoBean and
* have some $1 added to them.
*/
sw.println("if (b.getName().startsWith(\"%s\")){", originBeanType.getQualifiedSourceName());
sw.indent();
sw.println("return new %s();", factory);
sw.outdent();
sw.println("}");
}

sw.outdent();
sw.println("return null;");
sw.println("}");

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


}




/**
* only accept classes derived from AbstractAutobean and with underlying
* interface. see getUnderlyingInterface
*/
@Override
protected boolean isBean(JClassType type) {
if (isAutoBean(type)) {
return true;
}
return false;
}


/**
* if it's an autobean this class takes the getters of the underlying
* AutoBeanInterface instead of direct methods of the AutoBeanImpl
*/
@Override
protected final void addGetters(final JClassType cls,
final List<JMethod> methods) {


for (JMethod m : cls.getMethods()) {
if (m.isPublic() || m.isProtected()) {
String name = m.getName();
if ((name.matches("get.*") || name.matches("is.*"))
&& m.getParameters().length == 0) {
methods.add(m);
}
}
}
}




/**
* Test if JClassType is derived from AbstractAutoBean
* and if it has an underlying interface.
*/
private boolean isAutoBean(final JClassType cls) {
if (cls != null
&& cls.getSuperclass() != null
&& cls.getSuperclass().getQualifiedSourceName().equals(Name.getSourceNameForClass(AbstractAutoBean.class))
&& getUnderlyingInterface(cls) != null)
return true;
return false;
}


/**
* this is the messy part of the whole implementation.
* AutoBeans have at compiletime no link to the interface which is going to be implemented
* so we have to guess. But the current namingconvention of AutoBeans does the following:
*
* path.to.your.package.InterfaceToImplement -> path.to.your.package.InterfaceToImplementAutoBean
*
* this in mind we can extract the interface which is going to be implemented later on. At
* runtime, the AutoBean has this interface implemented.
*
* See AutoBean documentation for more infos.
*
* @param cls
* @return
*/
private JClassType getUnderlyingInterface(final JClassType cls){
if(cls.getQualifiedSourceName().indexOf("AutoBean") == -1)
return null;
JClassType interfaceType = oracle.findType(cls.getQualifiedSourceName().substring(0, cls.getQualifiedSourceName().indexOf("AutoBean")));
return interfaceType;
}
}



put the following snipplet to your gwt.xml



<generate-with class="gwt.bindery.AutoBeanBeanModelGenerator">
<when-type-assignable class="com.extjs.gxt.ui.client.data.BeanModelLookup" />
</generate-with>


usage example:



AutoBean<MyDtoClass> autoBeanWrapper = AutoBeanCodex.decode(myAutoBeanFactory,MyDtoClass.class, jsonString);

MyDtoClass bean = autoBeanWrapper.as();

BeanModelReader reader = new BeanModelReader();
someStore.add(reader.read(null, bean).getData());


This is built to only support BeanModels out of AutoBeans no BeanModelTag
or BeanModelMarker is needed on the interfaces. So every declared AutoBean
in your AutoBeanFactory becomes a BeanModel. And with GXT3.0 it sure becomes
very deprecated :)

-- Michael