javaparser / javaparser

Java 1-21 Parser and Abstract Syntax Tree for Java with advanced analysis functionalities.
https://javaparser.org
Other
5.39k stars 1.14k forks source link

could i using it to build a project like lombok? #4484

Closed aSingleDragon closed 2 months ago

aSingleDragon commented 3 months ago

Within a Maven-managed project A, I have an annotation defined as follows: @Target({ElementType.TYPE}) @Retention(RetentionPolicy.SOURCE) public @interface ConstantClass { } I intend to make project B dependent on project A, thereby enabling the usage of this ConstantClass annotation. Classes annotated with ConstantClass should automatically have public static final modifiers added to all their instance variables, resembling the functionality and usage of Lombok's @UtilityClass annotation. ConstantClass processor as fellows: `package pers.hll.processor;

import com.github.javaparser.StaticJavaParser; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Modifier; import com.github.javaparser.ast.body.FieldDeclaration; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; import pers.hll.annotation.ConstantClass;

import javax.annotation.processing.AbstractProcessor; 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.tools.Diagnostic; import javax.tools.JavaFileObject; import java.io.Writer; import java.lang.reflect.Method; import java.util.Set;

@SupportedAnnotationTypes("pers.hll.annotation.*") @SupportedSourceVersion(SourceVersion.RELEASE_17) public class ConstantClassProcessor1 extends AbstractProcessor {

private Trees trees;

@Override
public synchronized void init(javax.annotation.processing.ProcessingEnvironment processingEnv) {
    super.init(jbUnwrap(javax.annotation.processing.ProcessingEnvironment.class, processingEnv));
    trees = Trees.instance(this.processingEnv);
}

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    for (Element element : roundEnv.getElementsAnnotatedWith(ConstantClass.class)) {
        TypeElement typeElement = (TypeElement) element;
        TreePath path = trees.getPath(typeElement);
        CompilationUnitTree compilationUnitTree = path.getCompilationUnit();
        String sourceCode = compilationUnitTree.toString();
        try {
            CompilationUnit cu = StaticJavaParser.parse(sourceCode);
            LexicalPreservingPrinter.setup(cu);
            new ConstantClassVisitor().visit(cu, null);
            String modifiedCode = LexicalPreservingPrinter.print(cu);
            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, modifiedCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return true;
}

private static class ConstantClassVisitor extends VoidVisitorAdapter<Void> {
    @Override
    public void visit(FieldDeclaration fd, Void arg) {
        super.visit(fd, arg);
        fd.setModifiers(Modifier.Keyword.PUBLIC, Modifier.Keyword.STATIC, Modifier.Keyword.FINAL);
    }
}

private static <T> T jbUnwrap(Class<? extends T> iface, T wrapper) {
    T unwrapped = null;
    try {
        final Class<?> apiWrappers = wrapper.getClass().getClassLoader().loadClass("org.jetbrains.jps.javac.APIWrappers");
        final Method unwrapMethod = apiWrappers.getDeclaredMethod("unwrap", Class.class, Object.class);
        unwrapped = iface.cast(unwrapMethod.invoke(null, iface, wrapper));
    } catch (Throwable ignored) {}
    return unwrapped != null ? unwrapped : wrapper;
}

}`

but it doesn't work

jlerbsc commented 3 months ago

I don't see any issues with the current version of JP. Here's the simplified test case I created using your example.

@Test
void issue4484() {
    String code =
                "class foo {\n" +
                "    String a  = null;\n" +
                "}";
        CompilationUnit cu = StaticJavaParser.parse(code);

        LexicalPreservingPrinter.setup(cu);
        new ConstantClassVisitor().visit(cu, null);
        String modifiedCode = LexicalPreservingPrinter.print(cu);
        System.out.println(modifiedCode);
}

private static class ConstantClassVisitor extends VoidVisitorAdapter<Void> {
    @Override
    public void visit(FieldDeclaration fd, Void arg) {
        super.visit(fd, arg);
        fd.setModifiers(Modifier.Keyword.PUBLIC, Modifier.Keyword.STATIC, Modifier.Keyword.FINAL);
    }
}
aSingleDragon commented 3 months ago

Thank you very much for your reply. This indeed doesn't pose as a conventional issue. I am attempting to leverage javaparser to achieve the effects of Lombok in my project. Here is the link to lombok-ext project , and this is the test project. I have discovered that it is capable of generating the correct modifiedCode, however, it appears that this output is not .class file and ultimately isn't being written into the .class file. How can I direct this output into the .class file? My desired outcome is for, mvn compile the test project, the .class file of the Student class, when decompiled, matches my expected result as follow: public final class Student { private Student() { } public static final String name; }

aSingleDragon commented 3 months ago
image
aSingleDragon commented 3 months ago

I don't see any issues with the current version of JP. Here's the simplified test case I created using your example.

@Test
void issue4484() {
  String code =
                "class foo {\n" +
                "    String a  = null;\n" +
                "}";
        CompilationUnit cu = StaticJavaParser.parse(code);

        LexicalPreservingPrinter.setup(cu);
        new ConstantClassVisitor().visit(cu, null);
        String modifiedCode = LexicalPreservingPrinter.print(cu);
        System.out.println(modifiedCode);
}

private static class ConstantClassVisitor extends VoidVisitorAdapter<Void> {
    @Override
    public void visit(FieldDeclaration fd, Void arg) {
        super.visit(fd, arg);
        fd.setModifiers(Modifier.Keyword.PUBLIC, Modifier.Keyword.STATIC, Modifier.Keyword.FINAL);
    }
}

The modifiedCode has been generated as expected, but I wish to write it into bytecode. Are there any methods to achieve this? For specific code details, please refer to my previous reply. Many thanks in advance!

jlerbsc commented 3 months ago

No, that's not what Javaparser was designed for. Perhaps you could try compiling the code generated by Javaparser.

jlerbsc commented 2 months ago

I'm closing this exit because we've given an answer and there's no further discussion.