BAEL-2100 Custom Lombok Annotation (#5918)

This commit is contained in:
dev-chirag 2018-12-17 01:20:17 +05:30 committed by maibin
parent bd32693a46
commit ad03c69e01
5 changed files with 304 additions and 1 deletions

58
lombok-custom/pom.xml Normal file
View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent-modules</artifactId>
<groupId>com.baeldung</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>lombok-custom</artifactId>
<version>0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>core</artifactId>
<version>3.3.0-v_771</version>
</dependency>
</dependencies>
<profiles>
<profile>
<id>default-tools.jar</id>
<activation>
<property>
<name>java.vendor</name>
<value>Oracle Corporation</value>
</property>
</activation>
<dependencies>
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>${java.version}</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
</dependencies>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,10 @@
package com.baeldung.singleton;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
public @interface Singleton {
}

View File

@ -0,0 +1,124 @@
package com.baeldung.singleton.handlers;
import com.baeldung.singleton.Singleton;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.EclipseHandlerUtil;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.kohsuke.MetaInfServices;
import static lombok.eclipse.Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccFinal;
import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccPrivate;
import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccStatic;
@MetaInfServices(EclipseAnnotationHandler.class)
public class SingletonEclipseHandler extends EclipseAnnotationHandler<Singleton> {
@Override
public void handle(AnnotationValues<Singleton> annotation, Annotation ast, EclipseNode annotationNode) {
//remove annotation
EclipseHandlerUtil.unboxAndRemoveAnnotationParameter(ast, "onType", "@Singleton(onType=", annotationNode);
//add private constructor
EclipseNode singletonClass = annotationNode.up();
TypeDeclaration singletonClassType = (TypeDeclaration) singletonClass.get();
ConstructorDeclaration constructor = addConstructor(singletonClass, singletonClassType);
TypeReference singletonTypeRef = EclipseHandlerUtil.cloneSelfType(singletonClass, singletonClassType);
//add inner class
//add instance field
StringBuilder sb = new StringBuilder();
sb.append(singletonClass.getName());
sb.append("Holder");
String innerClassName = sb.toString();
TypeDeclaration innerClass = new TypeDeclaration(singletonClassType.compilationResult);
innerClass.modifiers = AccPrivate | AccStatic;
innerClass.name = innerClassName.toCharArray();
FieldDeclaration instanceVar = addInstanceVar(constructor, singletonTypeRef, innerClass);
FieldDeclaration[] declarations = new FieldDeclaration[]{instanceVar};
innerClass.fields = declarations;
EclipseHandlerUtil.injectType(singletonClass, innerClass);
addFactoryMethod(singletonClass, singletonClassType, singletonTypeRef, innerClass, instanceVar);
}
private void addFactoryMethod(EclipseNode singletonClass, TypeDeclaration astNode, TypeReference typeReference, TypeDeclaration innerClass, FieldDeclaration field) {
MethodDeclaration factoryMethod = new MethodDeclaration(astNode.compilationResult);
factoryMethod.modifiers = AccStatic | ClassFileConstants.AccPublic;
factoryMethod.returnType = typeReference;
factoryMethod.sourceStart = astNode.sourceStart;
factoryMethod.sourceEnd = astNode.sourceEnd;
factoryMethod.selector = "getInstance".toCharArray();
factoryMethod.bits = ECLIPSE_DO_NOT_TOUCH_FLAG;
long pS = factoryMethod.sourceStart;
long pE = factoryMethod.sourceEnd;
long p = (long) pS << 32 | pE;
FieldReference ref = new FieldReference(field.name, p);
ref.receiver = new SingleNameReference(innerClass.name, p);
ReturnStatement statement = new ReturnStatement(ref, astNode.sourceStart, astNode.sourceEnd);
factoryMethod.statements = new Statement[]{statement};
EclipseHandlerUtil.injectMethod(singletonClass, factoryMethod);
}
private FieldDeclaration addInstanceVar(ConstructorDeclaration constructor, TypeReference typeReference, TypeDeclaration innerClass) {
FieldDeclaration field = new FieldDeclaration();
field.modifiers = AccPrivate | AccStatic | AccFinal;
field.name = "INSTANCE".toCharArray();
field.type = typeReference;
AllocationExpression exp = new AllocationExpression();
exp.type = typeReference;
exp.binding = constructor.binding;
exp.sourceStart = innerClass.sourceStart;
exp.sourceEnd = innerClass.sourceEnd;
field.initialization = exp;
return field;
}
private ConstructorDeclaration addConstructor(EclipseNode singletonClass, TypeDeclaration astNode) {
ConstructorDeclaration constructor = new ConstructorDeclaration(astNode.compilationResult);
constructor.modifiers = AccPrivate;
constructor.selector = astNode.name;
constructor.sourceStart = astNode.sourceStart;
constructor.sourceEnd = astNode.sourceEnd;
constructor.thrownExceptions = null;
constructor.typeParameters = null;
constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = astNode.sourceStart;
constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = astNode.sourceEnd;
constructor.arguments = null;
EclipseHandlerUtil.injectMethod(singletonClass, constructor);
return constructor;
}
}

View File

@ -0,0 +1,108 @@
package com.baeldung.singleton.handlers;
import com.baeldung.singleton.Singleton;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import lombok.core.AnnotationValues;
import lombok.javac.Javac8BasedLombokOptions;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.handlers.JavacHandlerUtil;
import org.kohsuke.MetaInfServices;
import static lombok.javac.handlers.JavacHandlerUtil.deleteAnnotationIfNeccessary;
import static lombok.javac.handlers.JavacHandlerUtil.deleteImportFromCompilationUnit;
@MetaInfServices(JavacAnnotationHandler.class)
public class SingletonJavacHandler extends JavacAnnotationHandler<Singleton> {
@Override
public void handle(AnnotationValues<Singleton> annotation, JCTree.JCAnnotation ast, JavacNode annotationNode) {
Context context = annotationNode.getContext();
Javac8BasedLombokOptions options = Javac8BasedLombokOptions.replaceWithDelombokOptions(context);
options.deleteLombokAnnotations();
//remove annotation
deleteAnnotationIfNeccessary(annotationNode, Singleton.class);
//remove import
deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
//private constructor
JavacNode singletonClass = annotationNode.up();
JavacTreeMaker singletonClassTreeMaker = singletonClass.getTreeMaker();
addPrivateConstructor(singletonClass, singletonClassTreeMaker);
//singleton holder
JavacNode holderInnerClass = addInnerClass(singletonClass, singletonClassTreeMaker);
//inject static field to this
addInstanceVar(singletonClass, singletonClassTreeMaker, holderInnerClass);
//add factory method
addFactoryMethod(singletonClass, singletonClassTreeMaker, holderInnerClass);
}
private void addFactoryMethod(JavacNode singletonClass, JavacTreeMaker singletonClassTreeMaker, JavacNode holderInnerClass) {
JCTree.JCModifiers modifiers = singletonClassTreeMaker.Modifiers(Flags.PUBLIC | Flags.STATIC);
JCTree.JCClassDecl singletonClassDecl = (JCTree.JCClassDecl) singletonClass.get();
JCTree.JCIdent singletonClassType = singletonClassTreeMaker.Ident(singletonClassDecl.name);
JCTree.JCBlock block = addReturnBlock(singletonClassTreeMaker, holderInnerClass);
JCTree.JCMethodDecl factoryMethod = singletonClassTreeMaker.MethodDef(modifiers, singletonClass.toName("getInstance"), singletonClassType, List.nil(), List.nil(), List.nil(), block, null);
JavacHandlerUtil.injectMethod(singletonClass, factoryMethod);
}
private JCTree.JCBlock addReturnBlock(JavacTreeMaker singletonClassTreeMaker, JavacNode holderInnerClass) {
JCTree.JCClassDecl holderInnerClassDecl = (JCTree.JCClassDecl) holderInnerClass.get();
JavacTreeMaker holderInnerClassTreeMaker = holderInnerClass.getTreeMaker();
JCTree.JCIdent holderInnerClassType = holderInnerClassTreeMaker.Ident(holderInnerClassDecl.name);
JCTree.JCFieldAccess instanceVarAccess = holderInnerClassTreeMaker.Select(holderInnerClassType, holderInnerClass.toName("INSTANCE"));
JCTree.JCReturn returnValue = singletonClassTreeMaker.Return(instanceVarAccess);
ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
statements.append(returnValue);
return singletonClassTreeMaker.Block(0L, statements.toList());
}
private void addInstanceVar(JavacNode singletonClass, JavacTreeMaker singletonClassTM, JavacNode holderClass) {
JCTree.JCModifiers fieldMod = singletonClassTM.Modifiers(Flags.PRIVATE | Flags.STATIC | Flags.FINAL);
JCTree.JCClassDecl singletonClassDecl = (JCTree.JCClassDecl) singletonClass.get();
JCTree.JCIdent singletonClassType = singletonClassTM.Ident(singletonClassDecl.name);
JCTree.JCNewClass newKeyword = singletonClassTM.NewClass(null, List.nil(), singletonClassType, List.nil(), null);
JCTree.JCVariableDecl instanceVar = singletonClassTM.VarDef(fieldMod, singletonClass.toName("INSTANCE"), singletonClassType, newKeyword);
JavacHandlerUtil.injectField(holderClass, instanceVar);
}
private JavacNode addInnerClass(JavacNode singletonClass, JavacTreeMaker singletonTM) {
JCTree.JCModifiers modifiers = singletonTM.Modifiers(Flags.PRIVATE | Flags.STATIC);
String innerClassName = singletonClass.getName() + "Holder";
JCTree.JCClassDecl innerClassDecl = singletonTM.ClassDef(modifiers, singletonClass.toName(innerClassName), List.nil(), null, List.nil(), List.nil());
return JavacHandlerUtil.injectType(singletonClass, innerClassDecl);
}
private void addPrivateConstructor(JavacNode singletonClass, JavacTreeMaker singletonTM) {
JCTree.JCModifiers modifiers = singletonTM.Modifiers(Flags.PRIVATE);
JCTree.JCBlock block = singletonTM.Block(0L, List.nil());
JCTree.JCMethodDecl constructor = singletonTM.MethodDef(modifiers, singletonClass.toName("<init>"), null, List.nil(), List.nil(), List.nil(), block, null);
JavacHandlerUtil.injectMethod(singletonClass, constructor);
}
}

View File

@ -7,7 +7,10 @@
<groupId>com.baeldung</groupId>
<artifactId>parent-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>parent-modules</name>
<modules>
<module>lombok-custom</module>
</modules>
<name>parent-modules</name>
<packaging>pom</packaging>
<dependencies>