BAEL-2100 Custom Lombok Annotation (#5918)
This commit is contained in:
parent
bd32693a46
commit
ad03c69e01
|
@ -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>
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.baeldung.singleton;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface Singleton {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
5
pom.xml
5
pom.xml
|
@ -7,7 +7,10 @@
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-modules</artifactId>
|
<artifactId>parent-modules</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
<name>parent-modules</name>
|
<modules>
|
||||||
|
<module>lombok-custom</module>
|
||||||
|
</modules>
|
||||||
|
<name>parent-modules</name>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
Loading…
Reference in New Issue