当前位置: 首页 > 知识库问答 >
问题:

用spoon修改java方法体

闻深
2023-03-14

我正在尝试重构旧的SimpleFormController。我想用实际的成功视图和表单视图字符串替换getSuccessView()和gerFormView()调用

public class SimpleFormControllerReplaceViewCall extends AbstractProcessor<CtMethod> {
    MetaData meta;
    String successView= "successView";
    String formView = "formView";
    
    public SimpleFormControllerReplaceViewCall(MetaData meta)  {
        this.meta = meta;
    }
    
    @Override
    public boolean isToBeProcessed(CtMethod candidate) {
        if(candidate.getBody() == null) { //Ignore abstract methods
            return false;
        }
        
        String sourceCode;
        try {
            sourceCode = candidate.getBody()
            .getOriginalSourceFragment()
            .getSourceCode();
        } catch (Exception e) {
            return false;
        }
        
        return sourceCode.contains(getViewFunctionName(successView)) 
                ||  sourceCode.contains(getViewFunctionName(formView));
    }
    
    @Override
    public void process(CtMethod method) {
        Node beanNode = getBeanNode(method);
        
        CtBlock<Object> body = getFactory().createBlock();

        method.getBody().getStatements()
            .stream()
            .map(s -> {
                Optional<String> sourceCode = getStatementSourceCode(s);
                if(!sourceCode.isPresent()) {
                    return s.clone(); // Clone required to handle runtime error for trying attach a node to two parents
                } else {
                    System.out.println("Modifying: " + method.getSignature());
                    String code = sourceCode.get();
                    code = replaceViewCalls(beanNode, code, successView);
                    code = replaceViewCalls(beanNode, code, formView);
                    return getFactory().createCodeSnippetStatement(code);
                }
            }).forEach(body::addStatement);
        
        
        
        method.setBody(body);
    }
    
    private Optional<String> getStatementSourceCode(CtStatement s) {
        String sourceCode = null;
        
        try {
            sourceCode = s.getOriginalSourceFragment()
            .getSourceCode();
        } catch (Exception e) {}
        
        System.out.println(sourceCode);
        if (sourceCode != null && 
                (sourceCode.contains(getViewFunctionName(successView)) 
                        || sourceCode.contains(getViewFunctionName(formView)))) {
            sourceCode = sourceCode.trim();
            if(sourceCode.endsWith(";"))
                sourceCode = sourceCode.substring(0, sourceCode.length()-1);
            return Optional.of(sourceCode);
        } else {
            return Optional.empty();
        }
    }
    
    public String replaceViewCalls(Node beanNode, String code, String viewType) {
        String getViewFunctionName = getViewFunctionName(viewType);
        if (!code.contains(getViewFunctionName)) {
            return code;
        }
        String view = AppUtil.getSpringBeanPropertyValue(beanNode, viewType);
        return code.replaceAll(getViewFunctionName + "\\(\\)", String.format("\"%s\"", view));
    }
    
    public Node getBeanNode(CtMethod method) {
        String qualifiedName = method.getParent(CtClass.class).getQualifiedName();
        return meta.getFullyQualifiedNameToNodeMap().get(qualifiedName);
    }
    
    private String getViewFunctionName(String viewType) {
        return "get" + viewType.substring(0, 1).toUpperCase() + viewType.substring(1);
    }
}

我尝试的另一种方法是直接操纵身体作为一个整体。

@Override
public void process(CtMethod method) {
    String code = method.getBody()
            .getOriginalSourceFragment()
            .getSourceCode();
    
    Node beanNode = getBeanNode(method);
    
    code = replaceViewCalls(beanNode, code, successView);
    code = replaceViewCalls(beanNode, code, formView);
    
    CtCodeSnippetStatement codeStatement = getFactory().createCodeSnippetStatement(code);
    method.setBody(codeStatement);
}

这和第一个还有同样的汽车进口问题。除此之外,它添加了冗余的花括号,例如

void method() { x=y;} 

会变成

void method() { {x=y;} }

因为它是简单的替换,最简单的方法是如下所示

//Walk over all files and execute
Files.lines(Paths.get("/path/to/java/file"))
        .map(l -> l.replaceAll("getSuccessView\\(\\)", "actualViewNameWithEscapedQuotes"))
        .map(l -> l.replaceAll("getFormView\\(\\)", "actualViewNameWithEscapedQuotes"))
        .forEach(l -> {
            //write to file
        });

由于我可以在修改修饰符、注释、方法名、注释等方面避免使用spoon的文本操作,所以我希望有更好的方法来修改方法体。

共有1个答案

羿宏硕
2023-03-14

您应该将处理器输入视为抽象语法树而不是字符串:

public class SimpleFormControllerReplaceViewCall extends AbstractProcessor<CtMethod<?>> {

    @Override
    public boolean isToBeProcessed(CtMethod candidate) {
        if(candidate.isAbstract()) { //Ignore abstract methods
            return false;
        }
        return !candidate.filterChildren((CtInvocation i)->
                i.getExecutable().getSimpleName().equals("getSuccessView")
                        || i.getExecutable().getSimpleName().equals("getFormView")).list().isEmpty();
    }

    @Override
    public void process(CtMethod<?> ctMethod) {
        Launcher launcher = new Launcher();
        CodeFactory factory = launcher.createFactory().Code();
        List<CtInvocation> invocations = ctMethod.filterChildren((CtInvocation i)->
                i.getExecutable().getSimpleName().equals("getSuccessView")
        || i.getExecutable().getSimpleName().equals("getFormView")).list();
        for(CtInvocation i : invocations) {
            if(i.getExecutable().getSimpleName().equals("getSuccessView")) {
                i.replace(factory.createLiteral("successView"));
            } else {
                i.replace(factory.createLiteral("formView"));
            }
        }
    }
}

这里,遍历CtMethod AST以搜索具有指定属性的CtInvocation元素。然后将找到的元素替换为新的字符串文本元素。

 类似资料:
  • 打开UserModule, 将login方法修改为 @At @Filters // 覆盖UserModule类的@Filter设置,因为登陆可不能要求是个已经登陆的Session public Object login(@Param("username")String name, @Param("password")String password,

  • 求你了,我需要你的帮助。每当我试图运行下面的程序时,它会说不兼容的类型,字符串不能转换为整数。

  • Spoon 是一个 Android 平台的分布式仪表 (Instrumentation)测试项目。

  • 问题内容: 我正在尝试读取HTML文件,并添加指向某些文本的链接: 例如:我想添加链接到“ Campaign0”文本。: 要添加的链接: 我需要一个Java程序来修改html,以在“ Campaign0 ” 上添加超链接。 我如何用Jsoup做到这一点? 我用JSoup尝试了这个: 这样对吗 ??它不起作用:( 简而言之 :是否有类似-> 在Java代码中使用JSoup或任何技术? 问题答案: 您

  • 我试图阅读一个超文本标记语言文件,并添加一些文本的链接: 例如:我想添加“活动0”文本的链接: 要添加的链接: 我需要一个JAVA程序,修改html添加超链接超过“0”。 我如何使用Jsoup实现这一点? 我用JSoup试过这个: 这是正确的吗??它不起作用:( 简言之:有类似的吗-- 在JAVA代码中使用JSoup或任何技术??

  • Spoon 是一个用来对 Java 源码进行语法分析的库,可将 Java 源码文件解析成 AST 语法树结构,并提供强大的分析和转化的 API,支持最新的 Java 11, 12, 13, 14。Spoon 是 Inria 官方的开源项目,同时也是  OW2 开源组织成员。 学术用法: CtClass l = Launcher.parseClass("class A { void m() { Sy