package io.trygvis.container.compiler.model; import io.trygvis.container.compiler.Utils; import org.apache.commons.lang.StringUtils; import javax.annotation.Generated; import javax.lang.model.type.TypeMirror; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.TreeSet; import static io.trygvis.container.compiler.Utils.toJavaString; import static java.lang.reflect.Modifier.*; import static java.util.Arrays.asList; import static org.apache.commons.lang.StringUtils.join; import static org.springframework.util.StringUtils.collectionToDelimitedString; public class ClassG { public final int modifiers; public final TypeRef type; private TypeRef extendsType; private List implementsTypes = new ArrayList<>(); private final Set imports = new TreeSet<>(); private final Set fields = new TreeSet<>(); private final List methods = new ArrayList<>(); private final List constructors = new ArrayList<>(); private final List innerClasses = new ArrayList<>(); public static class InnerClassG extends ClassG { public final ClassG parent; public InnerClassG(ClassG parent, int modifiers, TypeRef type) { super(modifiers, type); this.parent = parent; } } public ClassG(int modifiers, TypeRef type) { if ((Modifier.classModifiers() & modifiers) != modifiers) { throw new RuntimeException("Invalid modifiers for class: " + Modifier.toString(modifiers)); } this.modifiers = modifiers; this.type = type; } public ClassG extendsType(TypeRef extendsType) { this.extendsType = extendsType; return this; } public ClassG implementsType(TypeRef... implementsTypes) { this.implementsTypes.addAll(asList(implementsTypes)); return this; } public TypeRef addImport(final TypeMirror type) { return addImport(new TypeRef(type)); } public TypeRef addImport(Class c) { return addImport(new TypeRef(c)); } public TypeRef addImport(TypeRef klass) { String fqName = klass.fqName; String name = klass.toString(); for (TypeRef i : imports) { if (i.fqName.equals(fqName)) { return i; } // If we've already found an import with the same name, use the fq version if (i.plainName.equals(name)) { name = fqName; break; } } TypeRef ref = new TypeRef(fqName, name); imports.add(ref); return ref; } public FieldRef addField(TypeMirror klass, String name) { TypeRef type = addImport(klass); FieldRef ref = new FieldRef(PRIVATE | FINAL, type, name); fields.add(ref); return ref; } public FieldRef addField(int modifiers, TypeRef type, String name) { TypeRef t = addImport(type); FieldRef ref = new FieldRef(modifiers, t, name); fields.add(ref); return ref; } public FieldRef addField(TypeRef type, String name) { return addField(PRIVATE | FINAL, type, name); } public FieldRef addPublicFinalField(TypeRef type, String name) { return addField(PUBLIC | FINAL, type, name); } public FieldRef addPublicStaticFinalField(TypeRef type, String name) { return addField(PUBLIC | STATIC | FINAL, type, name); } public Constructor addConstructor(Parameters parameters, List body) { Constructor constructor = new Constructor(this, parameters, body); constructors.add(constructor); return constructor; } public MethodRef addMethod(List body, TypeRef returnType, String name, Parameters parameters) { MethodRef ref = new MethodRef(PUBLIC, returnType, name, parameters, body); methods.add(ref); return ref; } public MethodRef addStaticMethod(List body, TypeRef returnType, String name, Parameters parameters) { MethodRef ref = new MethodRef(PUBLIC | STATIC, returnType, name, parameters, body); methods.add(ref); return ref; } public InnerClassG addInnerClass(int modifiers, TypeRef type) { InnerClassG inner = new InnerClassG(this, modifiers, type); innerClasses.add(inner); return inner; } public final List generate() { TypeRef generatedType = addImport(Generated.class); List body = new ArrayList<>(); boolean isInner = this instanceof InnerClassG; if (!type.inUnnamedPackage() && !isInner) { body.add("package " + type.packageName() + ";"); body.add(""); } if (!isInner) { // TODO: Add imports from inner classes for (TypeRef i : getImports()) { if (i.isPrimitive() || i.inUnnamedPackage()) { continue; } body.add("import " + i.fqName + ";"); } if (!imports.isEmpty()) { body.add(""); } } String extendsString = extendsType == null ? "" : " extends " + extendsType; if (!implementsTypes.isEmpty()) { extendsString += " implements " + join(implementsTypes, ", "); } body.add("@" + generatedType + "(" + toJavaString("SQL Persistence, yay!!") + ")"); body.add(Modifier.toString(modifiers) + " class " + type.className + extendsString + " {"); for (FieldRef field : fields) { body.add(""); body.add(" " + field.toJava() + ";"); } for (InnerClassG innerClass : innerClasses) { body.add(""); addAll(1, body, innerClass.generate()); } for (Constructor constructor : constructors) { body.add(""); addAll(1, body, constructor.write()); } for (MethodRef method : methods) { body.add(""); addAll(1, body, write(method)); } body.add("}"); return body; } public static void addAll(int indent, List body, List innerBody) { String prefix = StringUtils.leftPad("", indent * 4); for (String s : innerBody) { body.add(prefix + s); } } protected Collection getImports() { List imports = new ArrayList<>(this.imports); for (InnerClassG c : innerClasses) { imports.addAll(c.getImports()); } return imports; } private List write(MethodRef method) { List body = new ArrayList<>(); String returnString; if (method.returnType == TypeRef.VOID) { returnString = "void"; } else { returnString = method.returnType.toString(); } List parameters = new ArrayList<>(); for (Parameters.ParameterRef p : method.parameters) { parameters.add("final " + p.klass + " " + p.name); } String m = Modifier.toString(method.modifiers) + " " + returnString + " " + method.name + "(" + collectionToDelimitedString(parameters, ", ") + ")"; if (method.exceptions.isEmpty()) { body.add(m + " {"); } else { ArrayList typeRefs = new ArrayList<>(method.exceptions); String end = typeRefs.size() == 1 ? " {" : ","; body.add(m + " throws " + typeRefs.get(0).toString() + end); for (int i = 1; i < typeRefs.size(); i++) { TypeRef e = typeRefs.get(i); String s = " " + e; if (i < typeRefs.size() - 1) { s += ","; } else { s += " {"; } body.add(s); } } addAll(1, body, method.body); body.add("}"); return body; } }