package org.andork.codegen.model; import static com.schwab.codegen.NameUtils.cap; import japa.parser.ast.CompilationUnit; import japa.parser.ast.body.BodyDeclaration; import japa.parser.ast.body.FieldDeclaration; import japa.parser.ast.body.MethodDeclaration; import japa.parser.ast.body.TypeDeclaration; import java.io.File; import java.io.FileInputStream; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.List; import java.util.Set; import com.schwab.att.core.event.HierarchicalBasicPropertyChangeListener; import com.schwab.att.core.event.HierarchicalBasicPropertyChangePropagator; import com.schwab.att.core.event.HierarchicalBasicPropertyChangeSupport; import com.schwab.att.core.type.CompactTypeFormatter; import com.schwab.att.core.type.ReflectionUtils; import com.schwab.att.core.type.TypeFormatter; import com.schwab.att.core.util.FileFinder; import com.schwab.codegen.CodeBuilder2; import com.schwab.codegen.CodeBuilder2.DeclBlock; import com.schwab.codegen.CodeBuilder2.JavaBlock; import com.schwab.codegen.Generated; import com.schwab.codegen.LineUtils; import com.schwab.codegen.Model; import com.schwab.codegen.ModifiableCompilationUnit; import com.schwab.codegen.ModifiableCompilationUnit.Modifier; import com.schwab.codegen.NameUtils; import com.schwab.codegen.builder.BuilderElementName; import com.schwab.codegen.builder.BuilderIgnore; import com.schwab.japa.JavaParserUtils; public class ModelGenerator2 { public static void main(String[] args) throws Exception { Class<?> cls = TestTemplate2.class; File file = FileFinder.findFile(cls.getSimpleName() + ".java", new File("applet/src"), 10); List<String> lines = LineUtils.readLines(new FileInputStream(file)); ModifiableCompilationUnit compUnit = new ModifiableCompilationUnit(lines); new ModelGenerator2().generate(cls, compUnit); System.out.println(compUnit.getCompilationUnit()); } private final TypeFormatter typeFormatter = new CompactTypeFormatter(); private String equalsMethod = "Common.equals"; public void generate(Class<?> cls, ModifiableCompilationUnit unit) throws Exception { CompilationUnit u = unit.getCompilationUnit(); Modifier modifier = unit.modify(); if (isModelType(cls)) { insertLineIfMissing(unit, modifier, "import " + HierarchicalBasicPropertyChangeListener.class.getName() + ";", 1); insertLineIfMissing(unit, modifier, "import " + HierarchicalBasicPropertyChangePropagator.class.getName() + ";", 1); insertLineIfMissing(unit, modifier, "import " + HierarchicalBasicPropertyChangeSupport.class.getName() + ";", 1); } generate(cls, unit, modifier, (TypeDeclaration) JavaParserUtils.getDeclaration(u, cls)); modifier.commit(); } private void insertLineIfMissing(ModifiableCompilationUnit unit, Modifier modifier, String line, int index) { if (!unit.getLines().contains(line)) { modifier.insert(1, 0, line); } } private void generate(Class<?> cls, ModifiableCompilationUnit unit, Modifier modifier, TypeDeclaration typeDecl) throws SecurityException, NoSuchFieldException { String property = "Property"; if (cls.getEnclosingClass() != null && !java.lang.reflect.Modifier.isStatic(cls.getModifiers())) { property = cls.getSimpleName() + "Property"; } DeclBlock propertiesBlock = CodeBuilder2.newDeclBlock("public static enum " + property); propertiesBlock.addAnnotation("@" + Generated.class.getName()); if (isModelType(cls)) { String newDecl; FieldDeclaration childChangeListenerDecl = (FieldDeclaration) JavaParserUtils.getNode(typeDecl, "childChangeListener"); newDecl = "@" + Generated.class.getName() + "\nprivate HierarchicalBasicPropertyChangeListener childChangeListener = new HierarchicalBasicPropertyChangePropagator(this, propertyChangeSupport);"; insertOrReplace(modifier, typeDecl, childChangeListenerDecl, newDecl); FieldDeclaration propertyChangeSupportDecl = (FieldDeclaration) JavaParserUtils.getNode(typeDecl, "propertyChangeSupport"); newDecl = "@" + Generated.class.getName() + "\nprivate HierarchicalBasicPropertyChangeSupport propertyChangeSupport = new HierarchicalBasicPropertyChangeSupport();"; insertOrReplace(modifier, typeDecl, propertyChangeSupportDecl, newDecl); MethodDeclaration changeSupportDecl = (MethodDeclaration) JavaParserUtils.getNode(typeDecl, "changeSupport()"); newDecl = "@" + Generated.class.getName() + "\npublic HierarchicalBasicPropertyChangeSupport.External changeSupport() { return propertyChangeSupport.external(); }"; insertOrReplace(modifier, typeDecl, changeSupportDecl, newDecl); } Field lastField = null; for (Field field : cls.getDeclaredFields()) { if (ignoreField(field)) { continue; } String name = field.getName(); DeclBlock getterBlock = createGetter(field); MethodDeclaration getterDecl = (MethodDeclaration) JavaParserUtils.getNode(typeDecl, "get" + NameUtils.cap(name) + "()"); insertOrReplace(modifier, typeDecl, getterDecl, getterBlock.toString()); DeclBlock setterBlock = createSetter(field, property); MethodDeclaration setterDecl = (MethodDeclaration) JavaParserUtils.getNode(typeDecl, "set" + NameUtils.cap(name) + "(java.util.Collection)"); insertOrReplace(modifier, typeDecl, setterDecl, setterBlock.toString()); if (List.class.isAssignableFrom(field.getType()) || Set.class.isAssignableFrom(field.getType())) { Type elemType = ReflectionUtils.getTypeParameter(field.getGenericType(), 0); if (elemType == null) { elemType = Object.class; } String elemNameSingular = NameUtils.singularize(field.getName()); BuilderElementName elemName = field.getAnnotation(BuilderElementName.class); if (elemName != null) { elemNameSingular = elemName.singular(); } DeclBlock addBlock = createAdder(field); MethodDeclaration adderDecl = (MethodDeclaration) JavaParserUtils.getNode(typeDecl, "add" + NameUtils.cap(elemNameSingular) + "(" + JavaParserUtils.formatParameterType(ReflectionUtils.getRawType(elemType)) + ")"); insertOrReplace(modifier, typeDecl, adderDecl, addBlock.toString()); DeclBlock removeBlock = createRemover(field); MethodDeclaration removerDecl = (MethodDeclaration) JavaParserUtils.getNode(typeDecl, "remove" + NameUtils.cap(elemNameSingular) + "(" + JavaParserUtils.formatParameterType(ReflectionUtils.getRawType(elemType)) + ")"); insertOrReplace(modifier, typeDecl, removerDecl, removeBlock.toString()); } if (lastField != null) { propertiesBlock.addLine(NameUtils.constantify(lastField.getName()) + ","); } lastField = field; } if (lastField != null) { propertiesBlock.addLine(NameUtils.constantify(lastField.getName()) + ";"); } TypeDeclaration propertiesDecl = null; if (cls.getEnclosingClass() != null && !java.lang.reflect.Modifier.isStatic(cls.getModifiers())) { TypeDeclaration enclosingDecl = JavaParserUtils.getDeclaration(unit.getCompilationUnit(), cls.getEnclosingClass()); if (enclosingDecl != null) { propertiesDecl = (TypeDeclaration) JavaParserUtils.getNode(enclosingDecl, property); insertOrReplace(modifier, enclosingDecl, propertiesDecl, propertiesBlock.toString()); } } else { propertiesDecl = (TypeDeclaration) JavaParserUtils.getNode(typeDecl, property); insertOrReplace(modifier, typeDecl, propertiesDecl, propertiesBlock.toString()); } } protected final void insertOrReplace(Modifier modifier, TypeDeclaration parentDecl, BodyDeclaration bodyDecl, String code) { if (bodyDecl != null) { modifier.replace(LineUtils.fullRegion(bodyDecl), code); } else { modifier.insert(parentDecl.getEndLine() - 1, parentDecl.getEndColumn() - 1, code); } } protected DeclBlock createGetter(Field field) { String name = field.getName(); String ftype = typeFormatter.format(field.getType()); DeclBlock getterBlock; if (List.class.isAssignableFrom(field.getType()) || Set.class.isAssignableFrom(field.getType())) { Type elemType = ReflectionUtils.getTypeParameter(field.getGenericType(), 0); if (elemType == null) { elemType = Object.class; } String elemTypeStr = typeFormatter.format(elemType); String ifaceTypeStr; if (List.class.isAssignableFrom(field.getType())) { ifaceTypeStr = "List"; } else { ifaceTypeStr = "Set"; } String fqIfaceTypeStr = "java.util." + ifaceTypeStr; getterBlock = CodeBuilder2.newDeclBlock("public " + fqIfaceTypeStr + "<" + elemTypeStr + "> get" + cap(name) + "()"); getterBlock.addLine("return Collections.<" + elemTypeStr + ">unmodifiable" + ifaceTypeStr + "(").append(name).append(");"); } else { getterBlock = CodeBuilder2.newDeclBlock("public " + ftype + " get" + cap(name) + "()"); getterBlock.addLine("return " + name + ";"); } getterBlock.addAnnotation("@" + Generated.class.getName()); return getterBlock; } protected DeclBlock createSetter(Field field, String property) { String name = field.getName(); String ftype = typeFormatter.format(field.getType()); DeclBlock setterBlock = null; if (List.class.isAssignableFrom(field.getType()) || Set.class.isAssignableFrom(field.getType())) { Type elemType = ReflectionUtils.getTypeParameter(field.getGenericType(), 0); if (elemType == null) { elemType = Object.class; } String elemTypeStr = typeFormatter.format(elemType); String ifaceTypeStr; if (List.class.isAssignableFrom(field.getType())) { ifaceTypeStr = "List"; } else { ifaceTypeStr = "Set"; } setterBlock = CodeBuilder2.newDeclBlock("public void set" + cap(name) + "(Collection<? extends " + elemTypeStr + "> " + name + ")"); if (isModelType(field.getDeclaringClass())) { if (isModelType(elemType)) { JavaBlock removeLoop = setterBlock.newJavaBlock("for (" + elemTypeStr + " a : this." + field.getName() + ")"); removeLoop.addLine("a.changeSupport().removePropertyChangeListener(childChangeListener);"); } else if (couldBeModelType(elemType)) { JavaBlock removeLoop = setterBlock.newJavaBlock("for (" + elemTypeStr + " a : this." + field.getName() + ")"); JavaBlock ifModelBlock = removeLoop.newJavaBlock("if (a instanceof Model)"); ifModelBlock.addLine("((Model) a).changeSupport().removePropertyChangeListener(childChangeListener);"); } } setterBlock.addLine("this.").append(field.getName()).append(".clear();"); setterBlock.addLine("this.").append(field.getName()).append(".addAll(").append(field.getName()).append(");"); if (isModelType(field.getDeclaringClass())) { if (isModelType(elemType)) { JavaBlock addLoop = setterBlock.newJavaBlock("for (" + elemTypeStr + " a : " + field.getName() + ")"); addLoop.addLine("a.changeSupport().addPropertyChangeListener(childChangeListener);"); } else if (couldBeModelType(elemType)) { JavaBlock addLoop = setterBlock.newJavaBlock("for (" + elemTypeStr + " a : " + field.getName() + ")"); JavaBlock ifModelBlock = addLoop.newJavaBlock("if (a instanceof Model)"); ifModelBlock.addLine("((Model) a).changeSupport().addPropertyChangeListener(childChangeListener);"); } setterBlock.addLine("propertyChangeSupport.fireChildrenChanged(this);"); } } else if (field.getType().isArray()) { } else { setterBlock = CodeBuilder2.newDeclBlock("public void set" + cap(name) + "(" + ftype + " " + name + ")"); StringBuffer ifStmt = new StringBuffer(); ifStmt.append("if (!").append(equalsMethod).append("(this.").append(field.getName()).append(", ").append(field.getName()).append("))"); JavaBlock equalsBlock = setterBlock.newJavaBlock(ifStmt.toString()); if (isModelType(field.getDeclaringClass()) && isModelType(field.getType())) { JavaBlock removeListenerBlock = equalsBlock.newJavaBlock("if (this." + field.getName() + " != null)"); removeListenerBlock.addLine("this.").append(field.getName()).append(".changeSupport().removePropertyChangeListener(childChangeListener);"); } equalsBlock.addLine(ftype).append(" old = this.").append(field.getName()).append(";"); equalsBlock.addLine("this.").append(field.getName()).append(" = ").append(field.getName()).append(";"); if (isModelType(field.getDeclaringClass())) { if (isModelType(field.getType())) { JavaBlock addListenerBlock = equalsBlock.newJavaBlock("if (" + field.getName() + " != null)"); addListenerBlock.addLine(field.getName()).append(".changeSupport().addPropertyChangeListener(childChangeListener);"); } equalsBlock.addLine("propertyChangeSupport.firePropertyChange(this, ").append(property).append(".").append(NameUtils.constantify(field.getName())).append(", old, ").append(field.getName()).append(");"); } } setterBlock.addAnnotation("@" + Generated.class.getName()); return setterBlock; } protected DeclBlock createAdder(Field field) { Type elemType = ReflectionUtils.getTypeParameter(field.getGenericType(), 0); if (elemType == null) { elemType = Object.class; } String elemTypeStr = typeFormatter.format(elemType); String elemNameSingular = NameUtils.singularize(field.getName()); BuilderElementName elemName = field.getAnnotation(BuilderElementName.class); if (elemName != null) { elemNameSingular = elemName.singular(); } DeclBlock addBlock = CodeBuilder2.newDeclBlock("public void add" + cap(elemNameSingular) + "(" + elemTypeStr + " " + elemNameSingular + ")"); if (isModelType(field.getDeclaringClass())) { JavaBlock ifAddedBlock = addBlock.newJavaBlock("if (this." + field.getName() + ".add(" + elemNameSingular + "))"); if (isModelType(elemType)) { ifAddedBlock.addLine(elemNameSingular).append(".changeSupport().addPropertyChangeListener(childChangeListener);"); } else if (couldBeModelType(elemType)) { JavaBlock ifModelBlock = ifAddedBlock.newJavaBlock("if (" + elemNameSingular + " instanceof Model)"); ifModelBlock.addLine("((Model) ").append(elemNameSingular).append(").changeSupport().addPropertyChangeListener(childChangeListener);"); } ifAddedBlock.addLine("propertyChangeSupport.fireChildAdded(this, ").append(elemNameSingular).append(");"); } else { addBlock.addLine("this.").append(field.getName()).append(".add(").append(elemNameSingular).append(");"); } addBlock.addAnnotation("@" + Generated.class.getName()); return addBlock; } protected DeclBlock createRemover(Field field) { Type elemType = ReflectionUtils.getTypeParameter(field.getGenericType(), 0); if (elemType == null) { elemType = Object.class; } String elemTypeStr = typeFormatter.format(elemType); String elemNameSingular = NameUtils.singularize(field.getName()); BuilderElementName elemName = field.getAnnotation(BuilderElementName.class); if (elemName != null) { elemNameSingular = elemName.singular(); } DeclBlock removeBlock = CodeBuilder2.newDeclBlock("public void remove" + cap(elemNameSingular) + "(" + elemTypeStr + " " + elemNameSingular + ")"); if (isModelType(field.getDeclaringClass())) { JavaBlock ifRemovedBlock = removeBlock.newJavaBlock("if (this." + field.getName() + ".remove(" + elemNameSingular + "))"); if (isModelType(elemType)) { ifRemovedBlock.addLine(elemNameSingular).append(".changeSupport().removePropertyChangeListener(childChangeListener);"); } else if (couldBeModelType(elemType)) { JavaBlock ifModelBlock = ifRemovedBlock.newJavaBlock("if (" + elemNameSingular + " instanceof Model)"); ifModelBlock.addLine("((Model) ").append(elemNameSingular).append(").changeSupport().removePropertyChangeListener(childChangeListener);"); } ifRemovedBlock.addLine("propertyChangeSupport.fireChildRemoved(this, ").append(elemNameSingular).append(");"); } else { removeBlock.addLine("this.").append(field.getName()).append(".remove(").append(elemNameSingular).append(");"); } removeBlock.addAnnotation("@" + Generated.class.getName()); return removeBlock; } protected boolean ignoreField(Field field) { return field.getAnnotation(Generated.class) != null || field.getAnnotation(BuilderIgnore.class) != null || java.lang.reflect.Modifier.isStatic(field.getModifiers()) || field.getName().startsWith("this$"); } protected boolean isModelType(Type type) { return Model.class.isAssignableFrom(ReflectionUtils.getRawType(type)); } protected boolean couldBeModelType(Type type) { return !ReflectionUtils.getRawType(type).isEnum() && !ReflectionUtils.getRawType(type).getName().startsWith("java"); } }