|Most Shared

MyLombok tech coding java
03 Jul 2014at Atlanta

Writing my implementation of Project Lombok because Intellij is going to take its own sweet time to deliver the plugin for JDK8. But some good came out of this delay, I am merrier churning out my own boilerplate reducing meta enhancers with javassist. Seems good till now, some samples below.

Enhancer

Just a contract..

public interface Enhancer {
    void run(String packageName) throws Exception;
}
    

BeanEnhancer

To make a JavaBean with getters/setters and no-args constructor

/** Meta-tag */
@Target(ElementType.TYPE)
public @interface Bean { }

/** Implementation */
public class BeanEnhancer implements Enhancer{
    private static Logger log = LogManager.getLogger(BeanEnhancer.class);

    public void run(String packageName) throws Exception {
        ClassPool pool = ClassPool.getDefault();

        for (Class<?> oldClass : new Reflections(packageName).getTypesAnnotatedWith(Bean.class)) {
            String className = oldClass.getName();
            CtClass newClass = pool.get(className);
            if (newClass.isFrozen())
                newClass.defrost();
            for (Field field : oldClass.getDeclaredFields()) {
                String fieldName = field.getName();
                CtClass fieldClass = pool.get(field.getGenericType().getTypeName());
                getter(newClass, fieldName, fieldClass);
                setter(newClass, fieldName, fieldClass);
            }
            boolean hasNoArgsConstructor = false;
            for (CtConstructor constructor : newClass.getConstructors())
                if(constructor.getParameterTypes().length == 0) {
                hasNoArgsConstructor = true;
                break;
            }
            if(!hasNoArgsConstructor)
                newClass.addConstructor(new CtConstructor(new CtClass[]{},newClass));
            newClass.writeFile(oldClass.getResource("/").getFile());
            log.info("|+|" + className);
        }
    }
    private void setter(CtClass newClass, String fieldName, CtClass fieldType) throws CannotCompileException {
        CtMethod setter  = new CtMethod( CtClass.voidType, "set"+ capitalize(fieldName), new CtClass[]{fieldType}, newClass);
        setter.setBody("this." + fieldName + " = $1;");
        newClass.addMethod(setter);
    }
    private void getter(CtClass newClass, String fieldName, CtClass fieldType) throws CannotCompileException {
        CtMethod getter  = new CtMethod(fieldType,"get"+ capitalize(fieldName), null, newClass);
        getter.setBody("return " + fieldName + ";");
        newClass.addMethod(getter);
    }
}
    

ToStringEnhancer

Add a toString method based on fields

/** Meta-tag */
@Target(ElementType.TYPE)
public @interface ToString { }

/** Implementation */
public class ToStringEnhancer implements Enhancer {
    private static Logger log = LogManager.getLogger(ToStringEnhancer.class);

    @Override
    public void run(String packageName) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        for (Class<?> : new Reflections(packageName).getTypesAnnotatedWith(ToString.class)) {
            String className = oldClass.getName();
            CtClass newClass = pool.get(className);
            if (newClass.isFrozen())
                newClass.defrost();
            CtMethod oldMethod = null;
            try {
                oldMethod = newClass.getDeclaredMethod("toString");
            } catch (NotFoundException e) {
                //do nothing
            }
            CtMethod newMethod = oldMethod != null? oldMethod:
            new CtMethod(pool.get("java.lang.String"),"toString",new CtClass[]{},newClass);

            StringBuilder body = new StringBuilder("return \""+oldClass.getSimpleName()+"[\"+");
            for (Field field : oldClass.getDeclaredFields()) {
                String fieldName = field.getName();
                body.append("\"")
                .append(fieldName)
                .append("\"")
                .append("+\"=\"+this.")
                .append(fieldName)
                .append("+\"|\"+");
            }
            body.append("\"]\";");
            newMethod.setBody(body.toString());
            if(oldMethod == null)
                newClass.addMethod(newMethod);
            log.info("|+|" + className);
            newClass.writeFile(oldClass.getResource("/").getFile());
        }
    }
}
    

Usage

@Bean
@ToString
public class Employee {
    private Long id;
    private String name;
    private String designation;
    private Date joiningDate;
}
    

AllEnhancers

Entry program to run the repository of enhancers against a given package

public class AllEnhancers {
    private static Logger log = LogManager.getLogger(AllEnhancers.class);
    private List<Enhancer> enhancers = new ArrayList<>();

    public AllEnhancers() throws Exception {
        Reflections reflections = new Reflections("com.vijayrc.enhancer");
        for (Class<? extends Enhancer> aClass : reflections.getSubTypesOf(Enhancer.class))
            enhancers.add(aClass.newInstance());
    }
    public void run(String packageName) throws Exception{
        for (Enhancer enhancer : enhancers)
            enhancer.run(packageName);
    }
    public static void main(String[] args) throws Exception {
        try {
            new AllEnhancers().run(args[0]);
        } catch (Exception e) {
            log.error(e);
        }
    }
}
    

Gradle Task

Will scan given package classes and enhance

task ( enhance, type: JavaExec ) {
    print "start: enhancing classes.."
    main = 'com.vijayrc.repo.AllEnhancers'
    args("com.vijayrc.tasker") //you package name
    classpath = sourceSets.main.runtimeClasspath
    print "end: enhancing classes"
}
    
comments powered by Disqus

All content except noted photos and videos copyright © Vijayaraj Chakravarthy. All rights reserved. *Any images or videos not listed as mine are copyright to their respective owners and were used under creative common license or fair use standards. If a photo or video is your material and you do not wish it to be on the site, please email me vijayrc@outlook.com and I will remove it immediately.