Reputation: 31
I'm writing a custom Lombok annotation, named @PrintedValue that generates method printing Hiii. For that, I created the annotation class as below:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface PrintValue {
}
Then, I've created two handlers (javac and eclipse handlers). Here is my code:
@ProviderFor(EclipseAnnotationHandler.class) public class HandlePrintValue extends EclipseAnnotationHandler<PrintValue> {
@Override public void handle(AnnotationValues<PrintValue> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.PRINT_VALUE_FLAG_USAGE, "@PrintValue");
EclipseHandlerUtil.unboxAndRemoveAnnotationParameter(ast, "onType", "@PrintValue(onType=", annotationNode);
EclipseNode typeNode = annotationNode.up();
MethodDeclaration printValMethod = createPrintVal(typeNode, annotationNode, annotationNode.get(), ast);
injectMethod(typeNode, printValMethod);
return;
}
private MethodDeclaration createPrintVal(EclipseNode typeNode, EclipseNode errorNode, ASTNode astNode, Annotation source) {
TypeDeclaration typeDecl = (TypeDeclaration) typeNode.get();
MethodDeclaration method = new MethodDeclaration(typeDecl.compilationResult);
setGeneratedBy(method, astNode);
method.annotations = null;
method.modifiers = toEclipseModifier(AccessLevel.PUBLIC);
method.typeParameters = null;
method.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0);
method.selector = "printVal".toCharArray();
method.arguments = null;
method.binding = null;
method.thrownExceptions = null;
method.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
NameReference systemOutReference = createNameReference("System.out", source);
Expression[] printlnArguments = new Expression[] {new StringLiteral("Hiiii".toCharArray(), astNode.sourceStart, astNode.sourceEnd, 0)};
MessageSend printlnInvocation = new MessageSend();
printlnInvocation.arguments = printlnArguments;
printlnInvocation.receiver = systemOutReference;
printlnInvocation.selector = "println".toCharArray();
setGeneratedBy(printlnInvocation, source);
method.bodyStart = method.declarationSourceStart = method.sourceStart = astNode.sourceStart;
method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = astNode.sourceEnd;
method.statements = new Statement[] {printlnInvocation};
return method;
}
}
@ProviderFor(JavacAnnotationHandler.class)
public class HandlePrintValue extends JavacAnnotationHandler<PrintValue> {
@Override
public void handle(AnnotationValues<PrintValue> annotation, JCAnnotation ast, JavacNode annotationNode) {
// JavacHandlerUtil.markAnnotationAsProcessed(annotationNode,
// HelloWorld.class);
handleFlagUsage(annotationNode, ConfigurationKeys.PRINT_VALUE_FLAG_USAGE, "@PrintValue");
Context context = annotationNode.getContext();
Javac8BasedLombokOptions options = Javac8BasedLombokOptions.replaceWithDelombokOptions(context);
options.deleteLombokAnnotations();
JavacHandlerUtil.deleteAnnotationIfNeccessary(annotationNode, PrintValue.class);
JavacHandlerUtil.deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
JavacNode typeNode = annotationNode.up();
JCMethodDecl printValMethod = createPrintVal(typeNode);
JavacHandlerUtil.injectMethod(typeNode, printValMethod);
return;
}
private JCMethodDecl createPrintVal(JavacNode type) {
JavacTreeMaker treeMaker = type.getTreeMaker();
JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);
List<JCTypeParameter> methodGenericTypes = List.<JCTypeParameter>nil();
JCExpression methodType = treeMaker.Type(Javac.createVoidType(treeMaker, CTC_VOID));
Name methodName = type.toName("printVal");
List<JCVariableDecl> methodParameters = List.<JCVariableDecl>nil();
List<JCExpression> methodThrows = List.<JCExpression>nil();
JCExpression printlnMethod = JavacHandlerUtil.chainDots(type, "System", "out", "println");
List<JCExpression> printlnArgs = List.<JCExpression>of(treeMaker.Literal("val"));
JCMethodInvocation printlnInvocation = treeMaker.Apply(List.<JCExpression>nil(), printlnMethod, printlnArgs);
JCBlock methodBody = treeMaker.Block(0, List.<JCStatement>of(treeMaker.Exec(printlnInvocation)));
JCExpression defaultValue = null;
return treeMaker.MethodDef(modifiers, methodName, methodType, methodGenericTypes, methodParameters, methodThrows, methodBody, defaultValue);
}
}
After building my project and adding the generated lombok jar to my project, I can call @PrintValue annotation but the printVal method is undefined for Student class. import lombok.PrintValue;
public @PrintValue class Student {
private int age;
private String name;
}
I tried enabling the annotation processing, but still having the same problem. if anyone can help me, I'll be thankfull.
jdk:8 eclipse 2018-09 lombok 1.16.8
Upvotes: 1
Views: 1414
Reputation: 102903
The only way to add new lombok annotations is to fork lombok, and for your annotation typename (@PrintValue
) and the handlers to be in a package that starts with 'lombok.'. The reasons for this are hard to explain in a succint answer: It's a combination of:
When I say 'fork lombok', I mean:
.SCL.lombok
and not .class
.The easiest way to do this, by far, is to just get the lombok sources (clone the git repo), and create your annotation type in the same place lombok has them (/src/core/lombok/PrintValue.java
), and create your handlers in the same place lombok has them (/src/core/lombok/eclipse/handlers/HandlePrintValue.java
for example).
Then just ant dist
and then java -jar dist/lombok.jar install /path/to/your/eclipse
.
If you want to create your own sub-package of lombok for these, do a search for lombok.extern
and lombok/extern
- particularly in the build files (in the buildScripts
subdir); if you want your annotation's full name to be e.g. lombok.houria.PrintValue
, you need to tell lombok's build process that lombok.houria
must not get the .SCL.lombok
treatment, just like lombok.extern
, lombok
, and lombok.experimental
don't.
Upvotes: 3