Created
March 18, 2014 17:38
-
-
Save siviae/9625165 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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