Reputation: 11
I have created a Java Custom Annotation Processor with RetentionPolicy as SOURCE for generating personalized code for me just like Lombok's Getter & Setter.
Unfortunately it didn't worked because Lombok provides this feature by communicating with the Java compiler and modifying the way it works and also has some modification to do specific to each IDEs.
It seems the only way it would work is to contribute to Lombok project and also them to accept the code.
I have tried the following things to fix this issue:
Enabling annotation processing in both Eclipse and intellij IDEs.
Using the same JDK version.
Configuring annotationProcessorPaths in pom.xml.
public void clickGoogleSearchBtn() {
googleSearchBtn.click();
}
package com.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//Annotation to generate click method for WebElement fields
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface ClickElement {
}
package com.annotations.processors;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;
import com.annotations.ClickElement;
import com.google.auto.service.AutoService;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
@SupportedAnnotationTypes("com.automationdemo1.annotations.ClickElement")
@SupportedSourceVersion(SourceVersion.RELEASE_21)
@AutoService(Processor.class)
public class ClickMethodProcessor extends AbstractProcessor {
private Messager messager;
private JavacTrees javacTrees;
private TreeMaker treeMaker;
private Names names;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
messager.printMessage(Diagnostic.Kind.NOTE, "Hello");
// Pass the wrapped ProcessingEnvironment object to the super method.
super.init(processingEnv);
// If you need access to the original ProcessingEnvironment for JavacTrees
if (processingEnv instanceof JavacProcessingEnvironment) {
JavacProcessingEnvironment javacProcessingEnv = (JavacProcessingEnvironment) processingEnv;
this.javacTrees = JavacTrees.instance(javacProcessingEnv);
} else {
// Fallback for non-Javac environments
this.javacTrees = JavacTrees.instance(processingEnv);
}
Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
this.treeMaker = TreeMaker.instance(context);
this.names = Names.instance(context);
this.messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE, "Annotation processor initialized with JavacTrees.");
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(ClickElement.class)) {
if (element instanceof VariableElement && isWebElement(element)) {
generateClickMethod((VariableElement) element);
}
}
return true;
}
private boolean isWebElement(Element element) {
return element.asType().toString().equals("org.openqa.selenium.WebElement");
}
private void generateClickMethod(VariableElement field) {
TypeElement enclosingClass = (TypeElement) field.getEnclosingElement();
String fieldName = field.getSimpleName().toString();
Name methodName = names.fromString("click" + capitalize(fieldName));
JCTree.JCClassDecl classTree = (JCTree.JCClassDecl) javacTrees.getTree(enclosingClass);
JCTree.JCMethodDecl clickMethod = createClickMethod(methodName, fieldName);
classTree.defs = classTree.defs.prepend(clickMethod);
messager.printMessage(Diagnostic.Kind.NOTE, "Generated click method: " + methodName);
}
private JCTree.JCMethodDecl createClickMethod(Name methodName, String fieldName) {
JCTree.JCExpression returnType = treeMaker.TypeIdent(TypeTag.VOID);
JCTree.JCExpressionStatement clickStatement = treeMaker.Exec(
treeMaker.Apply(
List.nil(),
treeMaker.Select(
treeMaker.Ident(names.fromString(fieldName)),
names.fromString("click")
),
List.nil()
)
);
JCTree.JCBlock methodBody = treeMaker.Block(0, List.of(clickStatement));
return treeMaker.MethodDef(
treeMaker.Modifiers(Flags.PUBLIC),
methodName,
returnType,
List.nil(),
List.nil(),
List.nil(),
methodBody,
null
);
}
private String capitalize(String str) {
return Character.toUpperCase(str.charAt(0)) + str.substring(1);
}
}
package com.annotations.pages;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import com.annotations.ClickElement;
import com.annotations.base.BasePage;
public class DemoPage extends BasePage {
@ClickElement
@FindBy(xpath="(//input[@name='btnK'])[2]")
public WebElement googleSearchBtn;
}
Upvotes: 1
Views: 45
Reputation: 4044
Short answer: NO!
Longer answer: Currently, the Java JDK does provide an API to allow an Annotation Processor to modify the processed source code. As you said correctly, Lombok is doing some weird things with the byte code produced by the compiler, using ASM or a similar tool.
But without these hacks, an Annotation Processor can create only a new source file that is compiled to a *.class
file in the next cycle.
Of course, what the Lombok developers had done could be done by you as well – there are several other tools that are working along the same lines.
And there is a new API in the pipeline that should add the capability to manipulate the byte code of a *.class
file to the JDK. Enhancing the API for the Annotation Processors with that capability would be the logical next step.
Upvotes: 0