From 8e2c1ba4000cf4e25afbfb9fb826612989b88a88 Mon Sep 17 00:00:00 2001 From: Chirag Dewan Date: Sat, 15 Dec 2018 16:05:16 +0530 Subject: [PATCH] BAEL-2100 Custom Lombok Annotation --- lombok-custom/pom.xml | 58 ++++++++ .../com/baeldung/singleton/Singleton.java | 10 ++ .../handlers/SingletonEclipseHandler.java | 124 ++++++++++++++++++ .../handlers/SingletonJavacHandler.java | 108 +++++++++++++++ pom.xml | 5 +- 5 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 lombok-custom/pom.xml create mode 100644 lombok-custom/src/main/java/com/baeldung/singleton/Singleton.java create mode 100644 lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonEclipseHandler.java create mode 100644 lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonJavacHandler.java diff --git a/lombok-custom/pom.xml b/lombok-custom/pom.xml new file mode 100644 index 0000000000..41bd042a5e --- /dev/null +++ b/lombok-custom/pom.xml @@ -0,0 +1,58 @@ + + + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + + + 4.0.0 + lombok-custom + 0.1-SNAPSHOT + + + + org.projectlombok + lombok + 1.14.8 + provided + + + + org.kohsuke.metainf-services + metainf-services + 1.8 + + + + org.eclipse.jdt + core + 3.3.0-v_771 + + + + + + default-tools.jar + + + java.vendor + Oracle Corporation + + + + + com.sun + tools + ${java.version} + system + ${java.home}/../lib/tools.jar + + + + + + + \ No newline at end of file diff --git a/lombok-custom/src/main/java/com/baeldung/singleton/Singleton.java b/lombok-custom/src/main/java/com/baeldung/singleton/Singleton.java new file mode 100644 index 0000000000..2d2fd0ffb9 --- /dev/null +++ b/lombok-custom/src/main/java/com/baeldung/singleton/Singleton.java @@ -0,0 +1,10 @@ +package com.baeldung.singleton; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +public @interface Singleton { + + +} diff --git a/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonEclipseHandler.java b/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonEclipseHandler.java new file mode 100644 index 0000000000..03cdd707a4 --- /dev/null +++ b/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonEclipseHandler.java @@ -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 { + + + @Override + public void handle(AnnotationValues 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; + } +} diff --git a/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonJavacHandler.java b/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonJavacHandler.java new file mode 100644 index 0000000000..1f4cf0ea58 --- /dev/null +++ b/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonJavacHandler.java @@ -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 { + + @Override + public void handle(AnnotationValues 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 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(""), null, List.nil(), List.nil(), List.nil(), block, null); + + JavacHandlerUtil.injectMethod(singletonClass, constructor); + } + + +} diff --git a/pom.xml b/pom.xml index 99dae9decb..03a66080c2 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,10 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - parent-modules + + lombok-custom + + parent-modules pom