INRIA / spoon

Spoon is a metaprogramming library to analyze and transform Java source code. :spoon: is made with :heart:, :beers: and :sparkles:. It parses source files to build a well-designed AST with powerful analysis and transformation API.
http://spoon.gforge.inria.fr/
Other
1.75k stars 351 forks source link

How to update existing class #5003

Open esfomeado opened 1 year ago

esfomeado commented 1 year ago

Describe the bug

I just discovered this library and I'm trying to see if it's possible to do something that I require.

I have the following code:

@MyAnnotation
public class Book
{
    private String name;
}

And I want this to get generated:

public class Book
{
    private String name;

    boolean isValid()
    {
        return name != null;
    }
}

I'm already able to generate this code but the output file is created in a generated folder with the same name.

I changed the output folder to be the same as the generated code but it's replacing the existing class.

It's possible to just generate the file in memory and then compile it?

I don't want to generate a new file or change the source code of the existing one.

I can't use other libraries such as Byte-Buddy because I need this new file available during compilation.

Any help is welcome.

Thanks

Spoon Version

10.2.0

JVM Version

11

What operating system are you using?

Windows 10

I-Al-Istannen commented 1 year ago

It's possible to just generate the file in memory and then compile it?

That depends on what exactly you want to do. An example might be

    @Test
    void foo() throws ClassNotFoundException {
        Launcher launcher = new Launcher();
        launcher.addInputResource(new VirtualFile(
            "import java.util.UUID;\n" +
            "public class Foo { private UUID name; }"
        ));
        launcher.getEnvironment().setAutoImports(true);
        launcher.buildModel();
        Factory factory = launcher.getFactory();
        CtType<?> foo = factory.Type().get("Foo");
        CtMethod<?> validMethod = factory.Method().create(
            foo,
            Set.of(ModifierKind.PUBLIC),
            factory.Type().BOOLEAN_PRIMITIVE,
            "isValid",
            List.of(),
            Set.of()
        );
        validMethod.setBody(factory.createBlock());

        CtFieldReference fieldRef = factory.Field().createReference(foo.getField("name"));
        validMethod.getBody().addStatement(
            factory.createReturn()
                .setReturnedExpression(factory.createBinaryOperator(
                    factory.createFieldRead().setVariable(fieldRef),
                    factory.createLiteral(null),
                    BinaryOperatorKind.EQ
                ))
        );
        foo.addMethod(validMethod);

        CompilationUnit unit = factory.CompilationUnit().getOrCreate(foo);
        String asString = launcher.createPrettyPrinter().printCompilationUnit(unit);
        System.out.println(asString);

        ClassLoader loader = JavacFacade.compileFiles(Map.of("Foo.java", asString), List.of());
        System.out.println(loader.loadClass("Foo"));
    }

Instead of passing it to our test-only javac facade you can also ask Spoon to compile it using JDT by creating a compiler or write it to a file, etc. You can also ask spoon to pretty-print everything to another directory and then copy the files you need.

What to do depends on your needs. I don't quite understand your problem, so I would need some more information about that.