以编程方式将java代码添加到现有java文件的任何可能方法?

问题描述

我有一个使用我创建的自定义 Maven 插件自动生成的接口和类文件。该插件将从 JSON 文件中读取必要的数据,并使用 Jenesis4Java(下面提供的 Mojo 代码)为我创建一个 Java 文件

要求 - 我必须遍历已生成文件并在该文件添加方法代码。有没有办法从 Mojo 实现这一目标?看看下面生成代码,所以我必须给它添加一个新的抽象方法

我只能从头开始重新生成相同的文件,但不能添加到现有代码中。

生成以下代码-

   /**
 * Customer360 interface.
 */

import java.io.Serializable;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

import javax.ws.rs.core.*;

import reactor.core.publisher.Mono;


@Path(value = "/")
public interface Customer360 {
    @GET
    @Path(value = "")
    @Produces(value = "application/json")
    Mono<Response> casecreation(@Context
    HttpHeaders httpHeaders,@Context
    UriInfo uriInfo);

    @GET
    @Path(value = "")
    @Produces(value = "application/json")
    Mono<Response> getCustomerDetails(@Context
    HttpHeaders httpHeaders,@Context
    UriInfo uriInfo);

    @GET
    @Path(value = "")
    @Produces(value = "application/json")
    Mono<Response> prefetch(@Context
    HttpHeaders httpHeaders,@Context
    UriInfo uriInfo);
}

在 mojo 文件中,我编写了生成这个和另一个文件的逻辑。 (mojo指的是maven插件创建过程中的java文件

Mojo 文件供参考-

import net.sf.json.JSONSerializer;
import net.sourceforge.jenesis4java.*;
import net.sourceforge.jenesis4java.impl.MCodeWriter;
import net.sourceforge.jenesis4java.jaloppy.JenesisjalopyEncoder;
import org.apache.commons.io.IoUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;

import java.awt.*;
import java.io.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

//import reactor.core.publisher.Mono;


import java.util.Iterator;
import java.util.Map;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.*;

@Mojo(name = "generate-code",defaultPhase = LifecyclePhase.COMPILE)
public class GenerateApiResource extends AbstractMojo {

    @Parameter(defaultValue = "${project}",required = true,readonly = true)
    MavenProject project;

    @Parameter(defaultValue = "src/main/java",required = true)
    protected File outputJavaDirectory;

    @Parameter(defaultValue = "src/main/java",required = true)
    protected File outputJavaDirectory2;

    @Parameter
    protected String[] endpoints;

    private String apiName;


    private AbstractMethod mtr;
    private PackageClass cls ;

    @Override
    public void execute() throws MojoExecutionException,MojoFailureException {

        this.apiName = endpoints[0];
        System.out.println("API TO USE  1= >> "+apiName);
        this.apiName = endpoints[1];
        System.out.println("API TO USE  2= >> "+apiName);

        this.apiName = "Customer360";
        if (this.project != null) {
            this.project.addCompileSourceRoot(this.outputJavaDirectory.getAbsolutePath());
            this.project.addCompileSourceRoot(this.outputJavaDirectory2.getAbsolutePath());
        }

        /*if (!this.outputJavaDirectory.mkdirs()) {
            getLog().error("Could not create source directory!");
        } else {

        }*/
        try {
            generateJavaCode();
        } catch (IOException e) {
            throw new MojoExecutionException("Could not generate Java source code!",e);
        }

        /*if (!this.outputJavaDirectory2.mkdirs()) {
            getLog().error("Could not create source directory!");
        } else {

        } */

        try {
            generateJavaCode2();
        } catch (IOException e) {
            throw new MojoExecutionException("Could not generate Java source code!",e);
        }


    }

    private void generateJavaCode2() throws IOException {
        System.setProperty("jenesis.encoder",JenesisjalopyEncoder.class.getName());
        // Get the VirtualMachine implementation.
        VirtualMachine vm = VirtualMachine.getVirtualMachine();

        // Instantiate a new compilationunit. The argument to the
        // compilation unit is the "codebase" or directory where the
        // compilation unit should be written.

        // Make a new compilation unit rooted to the given sourcepath.
        compilationunit unit = vm.newcompilationunit(this.outputJavaDirectory2.getAbsolutePath());

        // Set the package namespace.
        unit.setNamespace("com.cs.frontline.apiimplementations");

        unit.addImport("javax.inject.Inject");
        unit.addImport("javax.ws.rs.core.Context");
        unit.addImport("javax.ws.rs.core.HttpHeaders");
        unit.addImport("javax.ws.rs.core.Response");
        unit.addImport("javax.ws.rs.core.UriInfo");
        unit.addImport("org.springframework.context.annotation.Scope");
        unit.addImport("org.springframework.stereotype.Component");
        unit.addImport(String.format("com.cs.frontoffice.api.%s",apiName));
        unit.addImport("com.cs.frontoffice.dataorchestrationengine.EndPointHandler");
        unit.addImport("reactor.core.publisher.Mono");

        PackageClass cls = unit.newPublicclass(String.format("%sImpl",apiName));
        cls.addImplements(String.format("%s",apiName));
        unit.setComment(Comment.D,"The API Implementation class.");
        cls.newField(vm.newType("EndPointHandler"),"endPointHandler").addAnnotation("Inject");

        //READ FROM JSON FILE
        JSONParser parser = new JSONParser();
        JSONObject jsonObject;
        JSONArray jsonArray;
        try{
            // parsing file
            File file = new File(String.format("src/main/resources/%s.json",apiName));
            jsonArray = (JSONArray) parser.parse(new FileReader(file));

            for(Object obj: jsonArray){
                JSONObject apiObj = (JSONObject) obj;

                String operationId = (String) apiObj.get("operationId");
                String method = (String) apiObj.get("method");
                String endPointFunction = (String) apiObj.get("endPointFunction");

                ClassMethod mtr = cls.newMethod(vm.newType("Mono<Response>"),operationId);
                mtr.setAccess(Access.PUBLIC);

                if(method == "POST" || method == "PUT"){
                    mtr.addParameter(vm.newType("String"),"requestBodyStr");
                }

                Classtype clsType = vm.newType("@Context HttpHeaders");
                Classtype clsType2 = vm.newType("@Context UriInfo");

                mtr.addParameter(clsType,"httpHeaders");
                mtr.addParameter(clsType2,"uriInfo");

                mtr.addAnnotation("Override");

                Try tr = mtr.newTry();
                tr.newCatch(vm.newType("Exception"),"e");

                Let letx = tr.newLet(vm.newType("Mono<Response>"));

                if(method.equals("GET")){
                    letx.addAssign("responseMap",vm.newInvoke("endPointHandler",String.format("%s",endPointFunction))
                            .addArg(vm.newVar("new Object() {}.getClass().getEnclosingMethod().getName()"))
                            .addArg(vm.newNull())
                            .addVarriableArg("uriInfo")
                            .addVarriableArg("httpHeaders"));

                } else {

                    letx.addAssign("responseMap","getEndpointResponse")
                            .addArg(vm.newVar("new Object() {}.getClass().getEnclosingMethod().getName()"))
                            .addArg(vm.newVar("new JSONObject(requestBodyStr)"))
                            .addVarriableArg("uriInfo")
                            .addVarriableArg("httpHeaders"));

                }

                tr.newReturn().setExpression(vm.newVar("responseMap"));

                mtr.newReturn().setExpression(vm.newNull());



            }
        } catch (Exception e){
            System.out.println("File Failed ======");
            System.out.println(e);
        }



        unit.encode();



    }

    private void generateJavaCode() throws IOException {
        System.setProperty("jenesis.encoder",JenesisjalopyEncoder.class.getName());

        // Get the VirtualMachine implementation.
        VirtualMachine vm = VirtualMachine.getVirtualMachine();

        // Instantiate a new compilationunit. The argument to the
        // compilation unit is the "codebase" or directory where the
        // compilation unit should be written.
        //
        // Make a new compilation unit rooted to the given sourcepath.
        compilationunit unit = vm.newcompilationunit(this.outputJavaDirectory.getAbsolutePath());

        // Set the package namespace.
        unit.setNamespace("com.cs.frontoffice.api");

        // Add an import statement for fun.
        unit.addImport("java.io.Serializable");
        unit.addImport("javax.ws.rs.GET");
        unit.addImport("javax.ws.rs.Path");
        unit.addImport("javax.ws.rs.Produces");
        unit.addImport("javax.ws.rs.core.*");
        unit.addImport("reactor.core.publisher.Mono");

        // Comment the package with a javadoc (DocumentationComment).
        unit.setComment(Comment.D,"Auto-Generated using the Jenesis Syntax API");


        // Make a new interface.
        Interface itr = unit.newPublicInterface(String.format("%s",apiName));
        itr.addAnnotation("Path").addAnntationAttribute("value").setValue(vm.newString("/"));

        // Comment the class with a javadoc (DocumentationComment).
        unit.setComment(Comment.D,String.format("%s interface.",apiName));
        Classtype t = vm.newType("Mono<Response>");


        //READ FROM JSON FILE
        JSONParser parser = new JSONParser();
        JSONObject jsonObject;
        JSONArray jsonArray;
        try{
            // parsing file
            File file = new File(String.format("src/main/resources/%s.json",apiName));
            jsonArray = (JSONArray) parser.parse(new FileReader(file));

            for(Object obj: jsonArray){
                JSONObject apiObj = (JSONObject) obj;

                String operationId = (String) apiObj.get("operationId");
                String path = (String) apiObj.get("path");
                String method = (String) apiObj.get("method");

                AbstractMethod mtr = itr.newMethod(vm.newType("Mono<Response>"),operationId);
                mtr.addAnnotation(String.format("%s",method));
                mtr.addAnnotation("Path").addAnntationAttribute("value").setValue(vm.newString(String.format("%s",path)));
                mtr.addAnnotation("Produces").addAnntationAttribute("value").setValue(vm.newString("application/json"));

                if(method.equals("POST") || method.equals("PUT")){
                    mtr.addParameter(vm.newType("@RequestBody String"),"requestBodyStr");
                }
                Classtype clsType = vm.newType("@Context HttpHeaders");
                System.out.println(clsType.getName());


                Classtype clsType2 = vm.newType("@Context UriInfo");
                System.out.println(clsType.getName());

                mtr.addParameter(clsType,"uriInfo");

                //Print interface
                System.out.println(mtr);

            }
        } catch (Exception e){
            System.out.println("File Failed ======");
            System.out.println(e);
        }


        // Write the java file.
        unit.encode();

    }
}

解决方法

如果您可以将 Mojo 中生成的代码表示为字符串,则可以使用 Spoon 轻松编辑它。假设您生成的代码在字符串 generatedCode 中,那么下面将向其添加一个抽象方法:

// create a Spoon model of the generated code
Launcher launcher = new Launcher();
VirtualFile virtualFile = new VirtualFile(generatedCode);
launcher.addInputResource(virtualFile);
launcher.buildModel();
CtInterface ctInterface = launcher.getModel().getRootPackage().getType("Customer360");

// create an abstract method
String codeWithAbstractMethod = "package pkg; abstract class AbstractClass { abstract void abstractMethod();} ";
CtClass<?> classWithAbstractMethod = Launcher.parseClass(codeWithAbstractMethod);
CtMethod abstractMethod = classWithAbstractMethod.getMethodsByName("abstractMethod").get(0);

// add abstract method to the generated code
ctInterface.addMethod(abstractMethod);
String prettyPrint = ctInterface.toString();

prettyPrint 将保存转换后生成代码的字符串表示。这里的抽象方法只是一个例子,Spoon 有很多有用的工具来创建复杂的代码元素,你可以在 documentation 中找到。