Skip to content

Instantly share code, notes, and snippets.

@siviae
Created March 18, 2014 17:38
Show Gist options
  • Save siviae/9625165 to your computer and use it in GitHub Desktop.
Save siviae/9625165 to your computer and use it in GitHub Desktop.
package ru.ifmo.ctddev.isaev;
import info.kgeorgiy.java.advanced.implementor.Impler;
import info.kgeorgiy.java.advanced.implementor.ImplerException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
*
*/
public class Implementor implements Impler {
String parentClassName;
String className;
public static final String LS = System.getProperty("line.separator");
public static final String FS = System.getProperty("file.separator");
private static final String PS = System.getProperty("path.separator");
StringBuilder sb;
boolean isInterface;
boolean implementedDefaultConstructor;
private Constructor<?>[] constructors;
private Set<Package> packages = new HashSet<>();
Map<String, Method> allMethods = new HashMap<>();
Map<String, Method> methods = new HashMap<>();
private String modifiers(int m) {
return Modifier.toString(m);
}
/**
*
* @return
* @throws ImplerException
*/
private StringBuilder generateDefaultConstructor() throws ImplerException {
StringBuilder sb = new StringBuilder();
sb.append("public ").append(className).append("() ");
if (constructors.length > 0) {
sb.append(getThrowsPart(constructors[0].getExceptionTypes()));
}
sb.append(" {").append(LS);
sb.append(callSuperConstructor()).append("}").append(LS + LS);
return sb;
}
/**
*
* @return
* @throws ImplerException
*/
private StringBuilder callSuperConstructor() throws ImplerException {
StringBuilder sb = new StringBuilder();
if (constructors.length > 0) {
boolean f = false;
Constructor<?> parentCons = null;
for (Constructor<?> cons : constructors) {
if (!Modifier.isPrivate(cons.getModifiers())) {
parentCons = cons;
f = true;
break;
}
}
if (!f) {
throw new ImplerException("Cannot call super constructor");
}
Class<?>[] consParams = parentCons.getParameterTypes();
sb.append("super(");
for (int i = 0; i < consParams.length; i++) {
sb.append("(").append(getValidType(consParams[i])).append(") ").append(getDefaultValue(consParams[i]));
if (i != consParams.length - 1) {
sb.append(", ");
}
}
sb.append(");");
}
return sb;
}
/**
*
* @param constructor
* @return
* @throws ImplerException
*/
private StringBuilder implementConstructor(Constructor<?> constructor) throws ImplerException {
StringBuilder sb = new StringBuilder();
sb.append(modifiers(constructor.getModifiers()).replace("transient", " ")).append(" ");
sb.append(className).append("(");
Class<?>[] params = constructor.getParameterTypes();
for (int i = 0; i < params.length; i++) {
Class<?> param = params[i];
sb.append(getValidType(param)).append(" ").append("p").append(i);
if (i != params.length - 1) {
sb.append(", ");
}
}
sb.append(") ");
sb.append(getThrowsPart(constructor.getExceptionTypes()));
sb.append(" {").append(LS);
sb.append(callSuperConstructor());
sb.append(LS).append("}").append(LS + LS);
return sb;
}
/**
*
* @param p
* @return
*/
private String getValidPackageString(Package p) {
return (p == null ? "" : p.getName() + ".");
}
/**
*
* @param param
* @return
*/
private String getValidType(Class<?> param) {
String p = getValidPackageString(param.getPackage());
String pack;
pack = p;/* p.isEmpty() ? p2 : p;*/
String name = pack + param.getSimpleName();
/* if (param.isArray()) {
name += "[]";
}*/
return name;
}
/**
*
* @param param
* @return
*/
private String getDefaultValue(Class<?> param) {
switch (param.getSimpleName()) {
case "byte":
return " 0 ";
case "short":
return " 0 ";
case "int":
return " 0 ";
case "long":
return " 0L ";
case "float":
return " 0.0f ";
case "double":
return " 0.0d ";
case "char":
return " \u0000 ";
case "boolean":
return " false ";
case "void":
return " ";
default:
return " null ";
}
}
/**
*
* @param exceptions
* @return
*/
private StringBuilder getThrowsPart(Class<?>[] exceptions) {
StringBuilder sb = new StringBuilder();
if (exceptions.length != 0) {
sb.append(" throws ");
for (int i = 0; i < exceptions.length; i++) {
Class<?> e = exceptions[i];
sb.append(e.getName());
if (i != exceptions.length - 1) {
sb.append(", ");
}
}
}
return sb;
}
/**
*
* @param method
* @return
*/
private StringBuilder implementMethod(Method method) {
StringBuilder sb = new StringBuilder();
/*if (isInterface) {*/
sb.append("@Override").append(LS);
// }
sb.append(modifiers(method.getModifiers()).replace("abstract", "").replace("transient", "")).append(" ");
sb.append(getValidType(method.getReturnType())).append(" ");
sb.append(method.getName()).append("(");
Class<?>[] params = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
Class<?> param = params[i];
sb.append(getValidType(param)).append(" ").append("p").append(i);
if (i != params.length - 1) {
sb.append(", ");
}
}
sb.append(")");
sb.append(getThrowsPart(method.getExceptionTypes()));
sb.append("{ ").append(LS).append(getReturnSection(method.getReturnType())).append(LS);
sb.append("}").append(LS);
return sb;
}
/**
*
* @param returnType
* @return
*/
private StringBuilder getReturnSection(Class<?> returnType) {
StringBuilder sb = new StringBuilder();
String s = getDefaultValue(returnType);
// sb.append(returnType.getSimpleName() + "*/");
if (!s.equals(" ")) {
sb.append("return ").append(s).append(";");
}
return sb;
}
/**
*
* @param name
* @throws ClassNotFoundException
*/
void run(String name) throws ClassNotFoundException {
Class impl = Class.forName(name);
File file = new File(".");
try {
implement(impl, file);
} catch (ImplerException e) {
System.out.println("Cannot generate default implementation for class " + className);
}
}
/**
*
* @param args
*/
public static void main(String[] args) {
if (args.length == 1) {
try {
new Implementor().run(args[0]);
} catch (ClassNotFoundException e) {
System.out.print("Cannot find class with name " + args[0] + " ");
}
} else {
System.out.println("Invalid arguments. Please, set one parameter - class full name");
}
}
/**
* Returns signature of given {@link java.lang.reflect.Method}
* @param method
* @return
*/
private String getMethodSignature(Method method) {
StringBuilder sb = new StringBuilder();
sb.append(method.getName()).append("(");
Class<?>[] params = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
Class<?> param = params[i];
sb.append(param.getName());
if (i != params.length - 1) {
sb.append(", ");
}
}
sb.append(")");
return sb.toString();
}
/**
* Recursive method, visit class hierarchy from starting class to {@link java.lang.Object} exclusive,
* collecting all methods, that must be implemented.
* @param clazz
*/
private void getMethods(Class<?> clazz) {
if (clazz == Object.class)
return;
for (Method method : clazz.getDeclaredMethods()) {
int mod = method.getModifiers();
if ((Modifier.isPublic(mod) || Modifier.isProtected(mod))) {
if (Modifier.isAbstract(mod)) {
Method m = allMethods.get(method.getName());
if ((m == null || !Modifier.isAbstract(mod)) && !Modifier.isFinal(mod)) {
String s = getMethodSignature(method);
if (!methods.containsKey(s)) {
methods.put(s, method);
}
}
} else {
allMethods.put(method.getName(), method);
}
}
}
if (clazz.getSuperclass() != null)
getMethods(clazz.getSuperclass());
for (Class<?> cl : clazz.getInterfaces()) {
getMethods(cl);
}
}
/**
* Initializes all fields required to correct work
*/
private void init() {
sb = new StringBuilder();
methods.clear();
allMethods.clear();
packages.clear();
constructors = null;
isInterface = false;
implementedDefaultConstructor = false;
}
@Override
public void implement(Class<?> clazz, File root) throws ImplerException {
init();
if (Modifier.isFinal(clazz.getModifiers())) {
throw new ImplerException("Cannot implement final class!");
}
String pack = clazz.getPackage() == null ? "" : clazz.getPackage().getName();
File targetFile = new File(root.getPath() + FS + pack.replace(".", FS) + FS + clazz.getSimpleName() + "Impl.java");
File parent = targetFile.getParentFile();
if (!parent.exists() && !parent.mkdirs()) {
throw new ImplerException("Couldn't create dir: " + parent);
}
try (PrintWriter out = new PrintWriter(targetFile)) {
parentClassName = clazz.getSimpleName();
className = parentClassName + "Impl";
if (!pack.isEmpty()) {
sb.append("package ").append(pack).append(PS).append(LS);
}
constructors = clazz.getDeclaredConstructors();
getMethods(clazz);
for (Method method : methods.values()) {
Class<?>[] params = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
packages.add(params[i].getPackage());
}
}
for (Constructor<?> constructor : constructors) {
Class<?>[] params = constructor.getParameterTypes();
for (int i = 0; i < params.length; i++) {
packages.add(params[i].getPackage());
}
}
for (Package p : packages) {
if (p != null) {
sb.append("import " + p.getName() + ".*;" + LS);
}
}
sb.append("public class ");
isInterface = clazz.isInterface();
sb.append(className).append(" ");
sb.append(isInterface ? "implements " : "extends ").append(clazz.getName()).append(" {").append(LS).append(LS);
for (Constructor<?> constructor : constructors) {
if (constructor.getParameterTypes().length == 0) {
implementedDefaultConstructor = true;
}
sb.append(implementConstructor(constructor));
}
if (!implementedDefaultConstructor) {
sb.append(generateDefaultConstructor());
}
for (Method m : methods.values()) {
sb.append(implementMethod(m));
}
sb.append(LS).append("}");
out.print(sb);
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment