/* * Ext GWT - Ext for GWT * Copyright(c) 2007-2009, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ package com.extjs.gxt.ui.rebind.core; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import com.extjs.gxt.ui.client.data.BeanModel; 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.client.data.NestedModelUtil; import com.extjs.gxt.ui.client.data.BeanModelMarker.BEAN; 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.UnableToCompleteException; 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.NotFoundException; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; import com.google.gwt.user.rebind.SourceWriter; 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 = "com.extjs.gxt.ui.client.data"; final String genClassName = "BeanModelLookupImpl"; ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(genPackageName, genClassName); composer.setSuperclass(BeanModelLookup.class.getCanonicalName()); 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 = "com.extjs.gxt.ui.client.data"; final String genClassName = "BeanModel_" + bean.getQualifiedSourceName().replace(".", "_") + "_Factory"; ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(genPackageName, genClassName); composer.setSuperclass(BeanModelFactory.class.getCanonicalName()); PrintWriter pw = context.tryCreate(logger, genPackageName, genClassName); if (pw != null) { SourceWriter sw = composer.createSourceWriter(context, pw); sw.println("public BeanModel 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.getCanonicalName()); composer.addImport(BeanModel.class.getName()); composer.addImport(NestedModelUtil.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().getCanonicalName()); } 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.getCanonicalName() + ".get().getFactory(" + params[0].getQualifiedSourceName() + ".class).createModel(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.getCanonicalName() + ".get().getFactory(" + returnType.getQualifiedSourceName() + ".class).createModel(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()) { if(m.isPublic() || m.isProtected()) { 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()) { if(m.isPublic() || m.isProtected()) { String name = m.getName(); if (name.matches("set.*") && m.getParameters().length == 1) { methods.add(m); } } } } }