简体   繁体   English

批量重命名Java个类

[英]Bulk rename of Java classes

Background背景

Hundreds of class files need to be renamed with a prefix.数百个 class 文件需要用前缀重命名。 For example, rename these:例如,重命名这些:

  • com.domain.project.Mary com.domain.project.Mary
  • com.domain.project.Jones com.domain.project.Jones
  • com.domain.project.package.Locket com.domain.project.package.Locket
  • com.domain.project.package.Washington com.domain.project.package.Washington

to these:对这些:

  • com.domain.project.PrefixMary com.domain.project.PrefixMary
  • com.domain.project.PrefixJones com.domain.project.PrefixJones
  • com.domain.project.package.PrefixLocket com.domain.project.package.PrefixLocket
  • com.domain.project.package.PrefixWashington com.domain.project.package.PrefixWashington

Such a rename could use a regular expression on the corresponding .java filename, such as:这样的重命名可以在相应的.java文件名上使用正则表达式,例如:

(.+)\.(.+) -> Prefix$1\.$2

Problem问题

Most solutions describe renaming files.大多数解决方案都描述了重命名文件。 Renaming files won't work because it leaves the class references unchanged, resulting in a broken build.重命名文件将不起作用,因为它会使 class 引用保持不变,从而导致构建失败。

Question问题

How would you rename Java source files en mass (in bulk) so that all references are also updated, without performing hundreds of actions manually (ie, one per file)?您将如何重命名 Java 个源文件(批量)以便所有引用也得到更新,而无需手动执行数百个操作(即每个文件一个)?

Ideas想法

  • refactobot is inscrutable, but looks promising. refactobot难以理解,但看起来很有前途。
  • Spoon 's Refactoring API is too buggy and introduced broken code. Spoon的 Refactoring API 漏洞太多,引入了损坏的代码。
  • JavaParser doesn't appear to have a concept of related references. JavaParser似乎没有相关引用的概念。 Renaming the class resulted in only the class name being changed, but not its constructor, much less other references.重命名 class 只会导致 class 名称被更改,但不会更改其构造函数,更不用说其他引用了。 This would require a visitor pattern, but even so the output from JavaParser loses formatting and may introduce other issues.这将需要访问者模式,但即使如此,来自 JavaParser 的 output 也会丢失格式并可能引入其他问题。
  • CodART could help refactor the class names. CodART可以帮助重构 class 名称。
  • Rename the files using a regular expression and the find command, then use an IDE to fix all the problems.使用正则表达式和find命令重命名文件,然后使用 IDE 修复所有问题。 I couldn't see a way for IntelliJ IDEA to correct errors in bulk, which means fixing hundreds of issues one at a time.我看不到 IntelliJ IDEA 批量更正错误的方法,这意味着一次修复数百个问题。
  • Use IntelliJ IDEA's "replace structurally" functionality.使用 IntelliJ IDEA 的“结构替换”功能。 This doesn't perform refactoring, resulting in a broken build.这不会执行重构,导致构建失败。 Also, there's no easy way to distinguish between files that have already been renamed and files that haven't: you have to rename by selecting classes in batches.此外,没有简单的方法来区分已重命名的文件和未重命名的文件:您必须通过分批选择类来重命名。
  • Use IntelliJ's RenameProcessor API to perform renaming.使用 IntelliJ 的RenameProcessor API 进行重命名。 There doesn't appear to be a fine-grained separation of packages that can be pulled from a central repository.似乎没有可以从中央存储库中提取的包的细粒度分离。
  • Use an IDEA plug-in, such as RenameFilesRefactorBatch .使用 IDEA 插件,例如RenameFilesRefactorBatch The plug-in has been updated to support regular expressions, making it the most promising candidate.该插件已更新为支持正则表达式,使其成为最有希望的候选者。

IDEA主意

The main stumbling block with using IDEA is that although it can detect the problems as "Project Errors" when renaming the files, it offers no way to resolve the all the errors at once:使用 IDEA 的主要障碍是,虽然它可以在重命名文件时将问题检测为“项目错误”,但它无法立即解决所有错误:

IDEA-01

The screenshot shows Glue and Num having been renamed to KtGlue and KtNum , respectively.屏幕截图显示GlueNum已分别重命名为KtGlueKtNum There's no way to select multiple items, and the context menu does not have an option to automatically fix the problems.没有办法 select 多个项目,上下文菜单没有自动修复问题的选项。

As for CodART , the following code is used to rename a specified class:对于CodART ,以下代码用于重命名指定的 class:

    from codart.refactorings.rename_class2 import main
    project_path_ = r"/JSON/"  # Project source files root
    package_name_ = r"org.json"  # Class package name
    class_identifier_ = r"CDL"  # Old class name
    new_class_name_ = r"CDL_Renamed"  # New class name
    output_dir_ = r"JSON_Refactored"  # Refactored project source files root
    main(project_path_, package_name_, class_identifier_, new_class_name_, output_dir_)

A few solutions courtesy of HackerNews. HackerNews 提供的一些解决方案。


A shell script :一个shell 脚本

#!/usr/bin/env bash

javas=$(find . -regex '.*\.java$')
sed -i -E "$(printf 's/\\<(%s)\\>/Kt\\1/g;' $(grep -hrPo '\b(class|interface|record|enum) (?!Kt)(?!List\b)(?!Entry\b)\K[A-Z]\w+'))" $(echo $javas); 
perl-rename 's;\b(?!Kt)(\w+[.]java)$;Kt$1;' $(echo $javas)

This is a little overzealous, but rolling back some of the changes was quick and painless.这有点过分热心,但回滚一些更改是快速而轻松的。 Also, Arch Linux doesn't have perl-rename installed by default, so that's needed.此外,Arch Linux 没有默认安装 perl-rename,所以这是必需的。


Another solution is to create a Kotlin IDEA plug-in :另一种解决方案是创建一个Kotlin IDEA 插件

  1. Install, run, then import the project into IDEA .安装、运行,然后将项目导入IDEA
  2. Install the Kotlin plug-in for IDEA.安装IDEA的Kotlin插件。
  3. Press Ctrl+Shift+A to open the Script Engine menu.Ctrl+Shift+A打开脚本引擎菜单。
  4. Select Kotlin . Select Kotlin
  5. Paste the script (given below).粘贴脚本(如下所示)。
  6. Press Ctrl+A to select the script.Ctrl+A到 select 脚本。
  7. Press Ctrl+Enter to integrate the script into the IDE.Ctrl+Enter将脚本集成到 IDE 中。
  8. Open the Project window.打开项目window。
  9. Select a single package directory (ie, a root-level package). Select 单个 package 目录(即根级包)。
  10. Click Navigate >> Search Everywhere .单击导航 >> 到处搜索
  11. Click the Actions tab.单击操作选项卡。
  12. Search for: Bulk搜索: Bulk
  13. Select Bulk refactor . Select批量重构

The classes are renamed.类已重命名。 Note: There may be prompts for shadowing class names and other trivial issues to resolve.注意:可能会提示隐藏 class 名称和其他需要解决的琐碎问题。

Script脚本

@file:Suppress("NAME_SHADOWING")

  import com.intellij.notification.Notification
  import com.intellij.notification.NotificationType
  import com.intellij.notification.Notifications
  import com.intellij.openapi.actionSystem.*
  import com.intellij.openapi.keymap.KeymapManager
  import com.intellij.openapi.command.WriteCommandAction
  import com.intellij.psi.*
  import com.intellij.psi.search.*
  import com.intellij.refactoring.rename.RenameProcessor
  import com.intellij.util.ThrowableConsumer
  import java.io.PrintWriter
  import java.io.StringWriter
  import javax.swing.KeyStroke

  // Usage: In IDEA: Tools -> IDE Scripting Console -> Kotlin
  // Ctrl+A, Ctrl+Enter to run the script
  // Select folder containing target classes, Ctrl+Shift+A to open action menu, search for Bulk refactor

  //<editor-fold desc="Boilerplate">
  val b = bindings as Map<*, *>
  val IDE = b["IDE"] as com.intellij.ide.script.IDE

  fun registerAction(
    name: String,
    keyBind: String? = null,
    consumer: ThrowableConsumer<AnActionEvent, Throwable>
  ) {
    registerAction(name, keyBind, object : AnAction() {
      override fun actionPerformed(event: AnActionEvent) {
        try {
          consumer.consume(event);
        } catch (t: Throwable) {
          val sw = StringWriter()
          t.printStackTrace(PrintWriter(sw))
          log("Exception in action $name: $t\n\n\n$sw", NotificationType.ERROR)
          throw t
        }
      }
    });
  }

  fun registerAction(name: String, keyBind: String? = null, action: AnAction) {
    action.templatePresentation.text = name;
    action.templatePresentation.description = name;

    KeymapManager.getInstance().activeKeymap.removeAllActionShortcuts(name);
    ActionManager.getInstance().unregisterAction(name);
    ActionManager.getInstance().registerAction(name, action);

    if (keyBind != null) {
      KeymapManager.getInstance().activeKeymap.addShortcut(
        name,
        KeyboardShortcut(KeyStroke.getKeyStroke(keyBind), null)
      );
    }
  }

  fun log(msg: String, notificationType: NotificationType = NotificationType.INFORMATION) {
    log("Scripted Action", msg, notificationType)
  }

  fun log(
    title: String,
    msg: String,
    notificationType: NotificationType = NotificationType.INFORMATION
  ) {
    Notifications.Bus.notify(
      Notification(
        "scriptedAction",
        title,
        msg,
        notificationType
      )
    )
  }
  //</editor-fold>

  registerAction("Bulk refactor") lambda@{ event ->
    val project = event.project ?: return@lambda;
    val psiElement = event.getData(LangDataKeys.PSI_ELEMENT) ?: return@lambda

    log("Bulk refactor for: $psiElement")

    WriteCommandAction.writeCommandAction(event.project).withGlobalUndo().run<Throwable> {
      psiElement.accept(object : PsiRecursiveElementWalkingVisitor() {
        override fun visitElement(element: PsiElement) {
          super.visitElement(element);
          if (element !is PsiClass) {
            return
          }

          if(element.name?.startsWith("Renamed") == false) {
            log("Renaming $element")

            // arg4 = isSearchInComments
            // arg5 = isSearchTextOccurrences
            val processor = object : RenameProcessor(project, element, "Renamed" + element.name, false, false) {
              override fun isPreviewUsages(usages: Array<out UsageInfo>): Boolean {
                return false
              }
            }
  
            processor.run()
          }
        }
      })
    }
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM