有没有一种方法可以在Gradle中进行源代码到源代码的Java重构?

8

我得到了一些自动生成的Java代码,我想在编译之前进行自动重构。主要是类重命名和包修改。

有没有可用于此的gradle或ant任务?


这不是Gradle的工作,Gradle只是一个构建工具。你需要一个重构工具,然后将其作为构建的一部分调用。 - Oliver Charlesworth
你知道这些工具中的任何一个吗?如果可能的话,最好它们有Java API,这样我就可以很好地将它们集成到Gradle中。 - Teocali
代码是由Gradle生成的还是作为项目源代码的一部分?您需要保存重命名的结果还是每次构建时都应该重命名它(我的意思是您的代码有许多不同的版本)? - Eugen Martynov
这是由Gradle使用XJC生成的代码,使用一些xsd文件。它会在每次构建时重新生成。结果需要保存,因为它们会在非生成代码中使用。 - Teocali
6个回答

4
我得到了一些自动生成的Java代码,我想在编译之前自动重构它。主要是类重命名和包修改。
[离题评论:你应该修复生成代码的代码。自动生成代码,然后使用另一个工具修改它似乎不是正确的方法。]
Eclipse提供重构API,可用于程序(不使用Eclipse)。原始工具是JDT(我曾使用过它),我猜新解决方案是LTK-尚未尝试。

这不完全是重构,但在生成代码后进行格式化似乎是一个合理的事情。也许某些简单的重构是类似的。 - flup
我完全同意您的离题评论,但我没有生成工具 XJC 的掌握。我会看一下 LTK。 - Teocali

3
你需要的是程序转换系统(PTS)。
PTS 读取源代码,构建程序表示(通常是抽象语法树或AST),应用转换(通常在“如果看到这个,则将其替换为那个”目标语言“表面”语法中)到树上,并可以从修改后的 AST 中重新生成有效的源文本(通常是漂亮的打印)。一个示例表面语法可能按如下文字方式编写(语法因系统而异):
    \x ** 2 ==>  \x * \x

通常需要一组转换规则协同工作,由元程序控制,以实现更复杂的结果。在PTS中,编写元程序的方式有很大差异。
通常需要配置PTS以解析/美化所选目标语言。Stratego、TXL和DMS(我的工具)都已经拥有了Java解析器/美化程序,并且都有表面语法重写。在选择PTS时,您将生成源代码,然后启动一个进程来运行该工具,使用一组转换和相应的元程序来实现您想要的特定代码更改。我相信Stratego有一个Java实现,您可能可以将其集成到您的应用程序中,避免单独的进程。
一个问题是,您想要执行的转换通常需要名称解析、类型信息或对代码中数据流的某种理解。Stratego和TXL没有内置这些信息,因此您必须通过编写其他转换来按需计算它们;这实际上有点困难,因为语言语义很复杂。我们的工具DMS已经为Java完成了这些功能,并且在数据流方面存在一些不完整性。
如果您的问题只是名称替换,并且在生成的代码中名称是唯一的,那么您可以使用类似以下的转换:
      uniquename1 ==> replacementpath1

例如。
      foo.bar ==>  baz.bar.foo

"If this is really enough, you might get away with just text string substitution rather than a PTS. Most of my career has been spent discovering that nothing is ever as simple as I had hoped."
"If这足够的话,你可以通过文本字符串替换而不是PTS来解决问题。我的大部分职业生涯都是发现事情并不像我希望的那样简单。"
"Eclipse的JDT可能是一个选择。它确实有一个解析器,但没有表面语法转换,因此它实际上不是PTS。相反,变换是通过使用JDT API在树上下行走并进行更改的Java代码编码的,因此有点痛苦。据我所知,它提供名称信息的访问,但不提供表达式类型,并且没有特定的数据流支持。我了解到很难将其与Eclipse隔离并用作模块。YMMV。"
曾经有一个名为Spoon的独立工具,它提供了Java解析、完整的名称和类型解析,但没有过程性树修改。我不知道它是否跟踪了现代的Java方言(例如,带有模板的1.5及以上版本)。

Spoon支持Java 1.5、1.6、1.7和1.8。 - Martin Monperrus

3

根据你的说法,你使用“xjc”生成代码,并且想要进行“大多数类重命名和包修改”,也许一些“xjc”选项可以实现你想要的功能:

-p <pkg>           :  specifies the target package
-b <file/dir>      :  specify external bindings files (each <file> must have its own -b)
                      If a directory is given, **/*.xjb is searched

使用“-p”参数,您可以定义生成的代码所属的目标包。有关使用绑定选项的更多信息,请参阅此处:http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.html编辑 另一种方法是将生成的文件移动到所需的目录,然后使用替换插件在复制的源文件中替换包规范。
<plugin>
   <artifactId>maven-resources-plugin</artifactId>
   <executions>
      <execution>
         <id>copy-resources</id>
         <phase>process-resources</phase>
         <goals>
            <goal>copy-resources</goal>
         </goals>
         <configuration>
            <outputDirectory>the.directory.for.the.classes.to.move.to</outputDirectory>
            <resources>
               <resource>
                  <directory>the.directory.of.ajc.classes</directory>
                  ... do here the appropriate settings ...
               </resource>
            </resources>
         </configuration>
      </execution>
   </executions>
</plugin>

<plugin>
    <groupId>com.google.code.maven-replacer-plugin</groupId>
    <artifactId>replacer</artifactId>
    <version>1.5.2</version>
    <executions>
        <execution>
            <phase>process-sources</phase>
            <goals>
                <goal>replace</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <includes>
            <include>**/src/main/gen/**/**.java</include>
        </includes>
        <token>^package the.one.you.want.to.replace</token>
        <value>package you.want.the.classes.to.be</value>
        <regexFlags>
            <regexFlag>MULTILINE</regexFlag>
        </regexFlags>
    </configuration>
</plugin>

是的,我知道这些选项,但这还不够,因为没有办法指定给定类的包。而且似乎也没有相应的插件。 - Teocali

3
如果您想控制xjc生成的包和类名,可以提供一个外部绑定文件
您可以这样说:
<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <jxb:bindings schemaLocation="your.xsd" node="/xs:schema">
    <jxb:schemaBindings>
      <jxb:package name="the.package.name.you.want" />
    </jxb:schemaBindings>
  </jxb:bindings>
</jxb:bindings>

对于您想要自定义的每个类名,您需要:

<jxb:bindings node="//xs:complexType[@name='UglyTypeName']">
  <jxb:class name="MyNiceClass"/>
</jxb:bindings>

将此内容保存到bindings.xjb文件中,例如,并像下面这样运行xjc

xjc your.xsd -b bindings.xjb

是的,我知道... 但所选的包是特定于模式的本地包。例如,如果我有一个生成两个类A和B的xsd文件,如果我想要将A放在pA包中,将B放在pB包中,那么使用绑定文件就不可能了,对吗? - Teocali
是的,如果它们属于同一个命名空间,则不可能。 - Edward Samson
现在我想起来了,与其试图重构生成的Java源代码,为什么不改进xsd文件呢?有很多XML API可以使用,而且我个人认为将xsd转换为更适合JAXB绑定自定义的形式会更容易。 - Edward Samson

1
有一个名为Spoon的工具。该项目是开源的,可以使用源到源的方法转换和分析Java源代码。它提供了完整和细粒度的Java元模型,其中任何程序元素(类、方法、字段、语句、表达式等)都可以被访问并进行读取和修改。
您可以在其网站上获取更多信息:http://spoon.gforge.inria.fr/
要在Gradle中运行Spoon,有一个插件可以将其插入到您的项目中并应用于Spoon。要使用它,请克隆此项目(https://github.com/SpoonLabs/spoon-gradle-plugin),并将插件安装在本地存储库中(./gradlew install)。之后,您可以按照插件文档(https://github.com/SpoonLabs/spoon-gradle-plugin#basic-usage)中的说明将其插入到您的项目中。
希望这可以帮助任何对Java源到源转换感兴趣的新开发人员! :)

嗯,我会看一下。不记得问题的全部上下文了(两个合同、一场婚礼和一个孩子似乎对我的记忆有影响),但Spoon是一个很有前途的工具。 - Teocali

0

您可以在使用 proguard 进行编译后执行此操作。Gradle 任务可用于 proguard。


请解释一下Proguard(一种混淆器)是如何使OP能够修改源代码的。这个答案听起来像胡说八道。 - Ira Baxter
编译后的类文件。看起来OP并不想重构原始源代码。否则他想使用不同的包(different packages)发布它。在这种情况下,他可以在编译后这样做。 - Horcrux7
我明确表示我想要进行源代码到源代码的重构。是否有办法用Proguard实现以下一系列操作:代码生成->编译->Proguard重构->从修改后的代码重新生成代码? - Teocali
@Teocali 为什么你想要显式地重构源代码呢?编译后结果是一样的。源代码重构会更容易出错。实际上,每个好的源代码重构都需要某种类型的解析/编译。 - Horcrux7
简而言之,由于项目背景的原因,我不会详细说明,因为这大多数是无关紧要的。只需说我需要将代码生成和重构集成到现有的构建过程中。 - Teocali

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接