Java源重构7000个引用

时间:2010-10-11 12:34:29

标签: java ide automated-refactoring

我需要更改整个代码库中使用的方法的签名。

具体来说,方法void log(String)将需要两个额外的参数(Class c, String methodName),这些参数需要由调用者提供,具体取决于调用它的方法。我不能简单地通过null或类似的。

为了了解范围,Eclipse发现了7000个对该方法的引用,因此如果我更改它,整个项目将会失效。我需要几周时间才能手动修复它。

据我所知,Eclipse的Eclipse重构插件不能完成任务,但我真的想让它自动化。
那么,我该如何完成工作呢?

12 个答案:

答案 0 :(得分:22)

很好,我可以复制a previous answer of mine,我只需要编辑一点点:


我认为您需要做的是使用像javaparser这样的源代码解析器来执行此操作。

对于每个java源文件,将其解析为CompilationUnit,create a Visitor,可能使用ModifierVisitorAdapter作为基类,并覆盖(至少)visit(MethodCallExpr, arg)。然后将更改的CompilationUnit写入新文件并在之后执行diff。

我建议不要更改原始源文件,但是创建一个影子文件树可能是个好主意(例如旧文件:src/main/java/com/mycompany/MyClass.java,新文件src/main/refactored/com/mycompany/MyClass.java,这样你可以将整个文件区分开来目录)。

答案 1 :(得分:6)

Eclipse能够使用 Refactor - >做到这一点更改方法签名并为新参数提供默认值。

对于class参数,defaultValue应该是this.getClass(),但是你的注释正好我不知道如何处理方法名参数。

答案 2 :(得分:3)

IntelliJ IDEA不应该有任何麻烦。

我不是Java专家,但这样的事情可行。这不是一个完美的解决方案(它甚至可能是一个非常糟糕的解决方案),但它可以让你开始:

使用IntelliJ的重构工具更改方法签名,并为2个新参数指定默认值:

c: self.getClass()
methodName: Thread.currentThread().getStackTrace()[1].getMethodName()

或者更好的是,只需指定 null 作为默认值。

答案 3 :(得分:2)

我认为有几个步骤可以解决这个问题,因为它不仅仅是一个技术问题,而是一个“情况”:

  1. 由于风险,拒绝在短期内完成。
  2. 指出不使用标准框架但重新发明轮子所引起的问题(如Paul所说)。
  3. 如果进行更改,请坚持使用Log4j或同等产品。
  4. 在合理的块中使用Eclipse重构来进行更改并处理不同的默认值。
  5. 我已经使用Eclipse重构来修复旧的臭代码 - 现在它非常强大。

答案 4 :(得分:2)

也许我很天真,但为什么你不能只重载方法名?

void thing(paramA) {
    thing(paramA, THE_DEFAULT_B, THE_DEFAULT_C)
}

void thing(paramA, paramB, paramC) {
    // new method
}

答案 5 :(得分:2)

您真的需要更改调用代码和方法签名吗?我得到的是看起来添加的参数是为了给你提供添加到日志数据的调用类和方法。如果唯一的要求只是将调用类/方法添加到日志数据中,那么Thread.currentThread()。getStackTrace()应该可以工作。获得StackTraceElement []后,您可以获取调用者的类名和方法名。

答案 6 :(得分:1)

如果您需要替换的行属于少数类别,那么您需要的是Perl:

find -name '*.java' | xargs perl -pi -e 's/log\(([^,)]*?)\)/log(\1, "foo", "bar")/g'

我猜测将一个将classname(从文件名派生)作为第二个参数放入脚本并不太难。将方法名称作为第三个参数保留为读者的练习。

答案 7 :(得分:1)

使用intellij尝试重构。它有一个名为SSR(结构搜索和替换)的功能。您可以为上下文引用类,方法名称等。 (seanizer的答案更有希望,我赞成它)

答案 8 :(得分:1)

我同意Seanizer的回答,你想要一个可以解析Java的工具。这是必要但不充分的;你真正想要的是一个可以进行可靠的质量变化的工具。

要做到这一点,您需要一个可以解析Java的工具,可以对解析的代码进行模式匹配,安装替换调用,并在不破坏其余源代码的情况下吐出答案。

我们的DMS Software Reengineering Toolkit可以针对各种语言(包括Java)执行所有这些操作。它解析完整 java系统的源代码,构建抽象语法树(对于整个代码集)。

DMS可以应用模式导向的源到源转换来实现所需的更改。

为了达到OP的效果,他将应用以下program transformation

 rule replace_legacy_log(s:STRING): expression -> expression
    " log(\s) " -> " log( \s, \class\(\), \method\(\) ) "

这个规则说的是,找到一个对具有单个字符串参数的log的调用,并用对辅助函数确定的另外两个参数的log调用替换它 class 方法

这些函数确定包含方法名称并包含规则找到匹配项的AST节点根目录的类名。

规则以“源格式”编写,但实际上与AST匹配,并用修改后的AST替换找到的AST。

要获取修改后的源代码,您可以让DMS简单地进行漂亮打印(制作漂亮的布局)或保真打印(如果您希望保留旧代码的布局)。 DMS保留注释,数字基数等。

如果exisitng应用程序具有多个“log”功能的定义,则需要添加限定符:

... if IsDesiredLog().

其中 IsDesiredLog 使用DMS的符号表和继承信息来确定特定日志是否引用了感兴趣的定义。

答案 9 :(得分:0)

事实上你的问题不是使用一个click'n'play引擎,它可以让你替换所有出现的

log("some weird message");

通过

log(this.getClass(), new Exception().getStackTrace()[1].getMethodName());

因为几乎没有机会处理各种情况(比如静态方法,例如)。

我倾向于建议你看看spoon。该工具允许源代码解析和转换,允许您以基于代码的方式实现操作 - 缓慢但受控制的操作。

但是,您可以考虑使用一个探索堆栈跟踪来转换实际方法以获取信息,或者更好的是,在内部使用log4j和显示正确信息的日志格式化程序。

答案 10 :(得分:0)

我会使用log(

搜索并替换log(@class, @methodname,

然后用任何语言(甚至是java)编写一个小脚本来查找类名和方法名,并替换@class和@method标记......

祝你好运

答案 11 :(得分:0)

如果“此日志来自何处?”需要类和方法名称?键入数据,然后另一个选项是在日志方法中打印出堆栈跟踪。 E.g。

public void log(String text)
{
   StringWriter sw = new StringWriter();
   PrintWriter pw = new PrintWriter(sw, true);
   new Throwable.printStackTrace(pw);
   pw.flush();
   sw.flush();
   String stackTraceAsLog = sw.toString();
   //do something with text and stackTraceAsLog
}