org-jdraft / jdraft

Meta Representation for building Java programs to analyze, generate, refactor & run Java source code
8 stars 1 forks source link
code-generation concrete-syntax-trees java meta-representation metaprogramming refactoring source-code-analysis

jdraft

Java 8+ Apache License 2

What is it?

a tool to compose, inspect, query & mutate Java source code.

_null.of();              // a null literal
_int.of(1);              // a literal int 1
_expression.of("1 + 2"); // binary expression "1 + 2"
_statement.of("System.out.println(\"x=\"+x);");
_method.of("public void getX(){ return this.x; }");
_class.of("public class Point{ int x, y; }");

//model all of the java sources within a jar file
_archive.of("C:\\Users\\Eric\\Downloads\\guava-28.1-jre-sources.jar");

//model all of the java sources from a github project (head) & maven central sources
_project.of(
    _githubProject.of("https://github.com/org-jdraft/jdraft"),
    _mavenCentral.of("com.github.javaparser", "javaparser-core", "3.15.21")
);

Why?

Normally, Java source code is a String:

String srcCode = "public class Point{ double x; double y = 1.0; }"   

...but passing around Java source code as a String (to be manipulated by a program) is cumbersome.

Simple example: Normally, to write a program that accepts srcCode above as input to modify the types of fields (x & y) from double to float; this program has to:

jdraft makes this type of metaprogramming task easy to write AND read:

// convert srcCode to _class, set all fields as float & return the code as a String 
srcCode = _class.of(srcCode).forFields(f-> f.setType(float.class)).toString();

jdraft is a simple to learn, read & use, and it allows you to build powerful tools when operating on codebases large or small; simple metaprograms can modify code you own & are familiar with or even code or don't "own":

// read in & model the jdraft github project sources 
// update the parameters on all methods for all types to be final
_project modified = _project.of(_githubProject.of("https://github.com/org-jdraft/jdraft"))
                            .forMethods(m-> m.forParameters(p-> p.setFinal()));

// compile the resulting source code and return the _classFiles (bytecode)
List<_classFile> _cfs = _runtime.compile( modified);

//write out the modified .java source code to 
_io.out( "C:\\modified\\src\\", _project );

//write out the compiled .class files
_io.out( "C:\\modified\\classes\\", _cfs);
  1. Metaprogramming
  2. Code Generation
  3. Code Inspection
  4. Code Querying
  5. Code Evolution

"more improv, less batch job"

Comparison tests for related tools:

How to setup and use jdraft

jdraft requires (2) things to compile/build/run:

  1. a JDK 1.8+ (not a JRE)
  2. a current version of javaparser-core
    <dependency>
    <groupId>com.github.javaparser</groupId>
    <artifactId>javaparser-core</artifactId>
    <version>3.18.0</version>
    </dependency>

How to build jdraft models

  1. build individual jdraft models _class(_c), _field(_x, _y), _method(_getX, _getY, _setX, _setY) from Strings & compose them together:
    
    _class _c = _class.of("package graph;","public class Point{}");
    _field _x = _field.of("public double x;");
    _field _y = _field.of("public double y;");
    _method _getX = _method.of("public double getX(){ return x; }");
    _method _setX = _method.of("public void setX(double x){ this.x = x;}");
    _method _getY = _method.of("public double getY(){ return y; }");
    _method _setY = _method.of("public void setY(double y){ this.y = y;}");

// _draft models compose..add fields and methods to _c: _c.fields(_x, _y).methods(_getX, _getY, _setX, _setY );

// toString() will return the source code System.out.println(_c);

><PRE>
>package graph;
>
>public class Point {
>    public double x;
>    public double y;
>    public double getX() { return this.x; }
>    public void setX(double x) { this.x = x; }
>    public double getY() { return y; }
>    public void setY(double y) {this.y = y; }
>}</PRE>   
2. build a jdraft model from source of an existing class (`_class.of(Point.class)`)<BR/> 
<I>(NOTE: this more preferred mechanism to using Strings above, and it works for Top level Classes,
Nested Classes, and Local Classes)</I> :
```java
class Point{
    public double x;
    public double y;
    public double getX() { return this.x; }
    public double getY() { return this.y; }
    public void setX(double x){ this.x = x; }
    public void setY(double y){ this.y = y; }
} 
_class _c = _class.of(Point.class);
  1. build _draft models from the source of an anonymous Object:
    _class _c = _class.of("graph.Point", new Object(){
    public double x;
    public double y;
    public double getX(){
        return x;
    }  
    public void setX(double x){
        this.x = x;
    }
    public double getY(){
        return y;
    }  
    public void setY(double y){
        this.y = y;
    }
    });
  2. build _draft models with @macros (@_get & @_set auto generate getX(),getY(),setX() & setY() methods)
    _class _c = _class.of("graph.Point", 
    new @_get @_set Object(){ public double x,y;});

    How to run (compile, load, eval) the _draft models/source at runtime

    
    // the @_dto @macro will generate getX(),getY(),setX(),setY(), equals(), hashCode() & toString()
    // methods as well as a no-arg constructor in the `_draft` model
    _class _c = _class.of("graph.Point", new @_dto Object(){
     public double x,y;
     });
    // add the distance method to `_point`
    _point.method(new Object(){
    public double distanceTo( double x, double y ){
        return Math.sqrt((this.y - y) * (this.y - y) + (this.x - x) * (this.x - x));
    }
    @_remove double x, y;
    });

//compile, load and use classes at runtime: _runtime _r = _runtime.of(_c);


#### **_Query_** Java source code