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")
);
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:
- parse the `srcCode` String to build a syntax tree (AST)
- manipulate AST field `type` nodes in the tree to change to `float`
- convert the tree back to a String
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);
- Metaprogramming
- Code Generation
- Code Inspection
- Code Querying
- Code Evolution
- Eclipse JDT
- Google Auto Value
- Google Error Prone
- IntelliJ PSI
- JavaParser
- JavaPoet
- Manifold
- RascalMPL
- RoslynSyntax
- Semmle
- Spoon
jdraft requires (2) things to compile/build/run:
- a JDK 1.8+ (not a
JRE) - a current version of javaparser-core
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
<version>3.18.0</version>
</dependency>
- 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);
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; } }
- build a jdraft model from source of an existing class (
_class.of(Point.class)
)
(NOTE: this more preferred mechanism to using Strings above, and it works for Top level Classes, Nested Classes, and Local Classes) :
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);
- 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;
}
});
- build
_draft
models with @macros (@_get
&@_set
auto generategetX()
,getY()
,setX()
&setY()
methods)
_class _c = _class.of("graph.Point",
new @_get @_set Object(){ public double x,y;});
// 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);