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);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue