8

I got some automatically generated Java code. I am willing to refactor automatically before compiling. It is mostly class rename and package modification.

Are there any gradle or ant tasks available for this?

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
Teocali
  • 2,725
  • 2
  • 23
  • 39
  • This is not a job for Gradle, Gradle is simply a build tool. You need a refactoring tool, which you would then invoke as part of your build. – Oliver Charlesworth Jan 23 '14 at 09:07
  • Do you know any of this tool ? If possible, it would be nice if they have a java API, as I could integrate them nicely into gradle. – Teocali Jan 23 '14 at 09:22
  • Is code generated by gradle or you have it as part of your project in sources? Do you need to save result of your renaming or you should rename it every build (I mean do you have many flavours of your code)? – Eugen Martynov Jan 23 '14 at 10:56
  • It is code generated by gradle using XJC, using some xsd files. It is regenerated at every build. The result need to be saved as there are used in code non generated. – Teocali Jan 23 '14 at 11:06

6 Answers6

4

I got some automatically generated java code I would like to automaticaly refactor before compiling it. It is mostly class rename and package modification.

[off topic comment: You should fix the code that generate the code. Generating code automatically and then modify it using another tool does not look like correct approach.]

Eclipse provides refactoring API that can be used in programs(without eclipse). Original tool was JDT(I had used it), I guess the new solution is LTK - not tried.

Jayan
  • 18,003
  • 15
  • 89
  • 143
  • It's not exactly refactoring, but formatting the generated code seems like a reasonable thing to do after generating. Perhaps certain simple refactorings are similar. – flup Feb 01 '14 at 00:08
  • I totally agree with your your off topic comment, but I don't have the hand on the generating tool, which is XJC. I will take a look on LTK – Teocali Feb 03 '14 at 12:46
3

What you want is a program transformation system (PTS).

A PTS reads source code, builds a program representation (usually an abstract syntax tree or AST), applies transformations (often stated in "if you see this, replace it by that" target langauge "surface" syntax) to the tree, and can regenerate valid source text (often prettyprinted) from the modified AST. An example surface syntax might be written literally as follows (syntax varies per system):

    \x ** 2 ==>  \x * \x

Usually a set of transformation rules working in cooperation, controlled by a metaprogram, are needed to achieve a more complex result. How metaprograms are written vary radically across the PTS.

You generally have to configure the PTS to parse/prettyprint the target language of choice. Stratego, TXL, and DMS (my tool) all have Java parsers/prettyprinters already and all have surface syntax rewrites. Given the choice of PTS, you would generate your source code, then launch a process to run the tool, using a set of transformations and corresponding metaprogram that you provide to achieve the specific code changes you want. Stratego, I believe, has a Java implementation, and you might be able to integrate it into your application, avoiding the separate process.

A complication is that often the transformations you want to do, require name resolution, type information, or some understanding of dataflow in the code. Stratego and TXL do not have this information built in, so you have to compute it on demand by writing additional transformations; that's actually a little hard because the language semantics are complex. Our tool, DMS, has these facilities already complete for Java with some incompleteness on the data flow.

If your problem is really just name substitution, and the names are unique in your generated code, then you might get away with transformations like:

      uniquename1 ==> replacementpath1

e.g.

      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).

Eclipse's JDT might be an alternative. It certainly has a parser, but no surface syntax transformations so it isn't really a PTS). Instead transformations are coded in Java by walking up and down the tree and making changes using JDT APIs, so it is a bit painful. AFAIK, it provides access to name information but not expression types if I understand it, and has no specific support for data flow. I understand it isn't easy to isolate it from Eclipse for use as a module; YMMV.

There used to be a standalone tool called Spoon, that provided Java parsing, full name and type resolution, but has on procedural tree modifications. I don't know if has tracked modern dialects of Java (e.g., 1.5 and up with templates).

Martin Monperrus
  • 1,845
  • 2
  • 19
  • 28
Ira Baxter
  • 93,541
  • 22
  • 172
  • 341
3

as you said you use "xjc" to generate the code and you want "mostly class rename and package modification" maybe some of the "xjc" options would do what you want:

-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

With "-p" you can define the target package to which the generated code should belong. For using the bindings option have a look here for further information http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.html

edit Another approach could be: to move the generated files in the directory you want and then using the replace plugin to replace the package specification in the copied source files

<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>
SubOptimal
  • 22,518
  • 3
  • 53
  • 69
  • Yes, I know about these option, but it is not enough, as there is no way to specify the package of a given class. And it seems that there is no plugin for that. – Teocali Feb 04 '14 at 13:47
3

If you're trying to control the package and class names generated by xjc you can provide an external binding file.

You would say something like:

<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>

And for each class name you want to customize you would have:

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

Save this up into a bindings.xjb file, for example, and run xjc like so:

xjc your.xsd -b bindings.xjb
Edward Samson
  • 2,395
  • 2
  • 26
  • 39
  • Yep I know that... but the choosen package is local to the schema. If, for example, I got a xsd generating two class A et B, if if I want to have A in a package pA et B in a package pB, it's not possible with the bindings file, is it ? – Teocali Feb 07 '14 at 06:38
  • Yeah, not possible if they belong to the same namespace. – Edward Samson Feb 07 '14 at 06:56
  • Now that I think about it, instead of trying to refactor the generated Java sources, why not "refactor" the xsd files instead? There are a lot of XML API's readily available and, personally, I think it would be easier to transform an xsd to be more amenable to JAXB binding customizations. – Edward Samson Feb 07 '14 at 14:38
1

There is a tools named Spoon. This project is open source and enables you to transform and analyze Java source code with an approach source-to-source. It provides a complete and fine-grained Java metamodel where any program element (classes, methods, fields, statements, expressions...) can be accessed both for reading and modification.

You can have more information about it on its website: http://spoon.gforge.inria.fr/.

To run spoon with Gradle, there is plugin that you can insert on your project and apply Spoon on it. To use it, clone this project (https://github.com/SpoonLabs/spoon-gradle-plugin) and install the plugin on your local repository (./gradlew install). After, you can insert it on your project how is explained in the documentation of the plugin (https://github.com/SpoonLabs/spoon-gradle-plugin#basic-usage).

I hope this can help any new developer interested in the java transformation source to source! :)

  • Hum, I will tae a look. Don't remember the full context of the problem. (Two contract, a wedding and a kid since seems to have an impact on my memory) but Spoon is a promising tool. – Teocali Oct 09 '15 at 12:01
0

You can do it after compiling with proguard. There is a gradle task for proguard.

Horcrux7
  • 23,758
  • 21
  • 98
  • 156
  • Please explain how proguard (an obfuscator) enables OP to modify source code. This answer sounds like nonsense. – Ira Baxter Feb 03 '14 at 00:34
  • Not the source code. The compiles classes. It look for me that the OP does not want refactor it original sources. Else he want distribute it with different packages. In this case he can do it after compiling. – Horcrux7 Feb 03 '14 at 09:09
  • I specifically said I wanted to do source-to-source refactoring. Is there a way to do the following chain of operation with proguard : Code generation -> Compilation -> Proguard refactoring -> Code regeneration from the modified code ? – Teocali Feb 03 '14 at 12:40
  • @Teocali Why you want explicit refactor the source code. The result is the same after compiling. The refactoring of source code is many more error-prone. In fact every good source code refactoring need some type of parsing/compiling. – Horcrux7 Feb 04 '14 at 12:19
  • Long response short : because of the project context. I would no go into detail, as it is mostly irrelevant. Suffice to say that I need to integrate the generation of the code and refactoring of it into an existing build process. – Teocali Feb 04 '14 at 13:49