Giter Club home page Giter Club logo

draft.java's Introduction

draft.java

draft.java is a developer-friendly tool to build, access, change, add, remove, and analyze .java source code; then optionally compile, load and use/run the code from within a program. it's a "code generator", and much MUCH more. (it's like having a DOM for Java source code). To get up to speed quickly with simple code generation, here is JavaPoet examples done in the draft.java API for you to compare and contrast

requirements

draft.java runs on JDK 8 or later.*(runtime compilation requires the JDK rather than the JRE) draft.java depends only on the (fantastic) JavaParser core (3.13.2 or later) library for transforming Java source code into ASTs. draft.java also integrates well with the JShell environment for "live" code generation / modification and scripting. draft.java can generate source code for ANY version of Java (Java 1.0 - 12).

<dependency>
   <groupId>draft</groupId>
   <artifactId>draft.java</artifactId>
   <version>1.0</version>   
</dependency>
<dependency>
    <groupId>com.github.javaparser</groupId>
    <artifactId>javaparser-core</artifactId>
    <version>3.13.2</version>
</dependency>

how to...

the most convenient way to build a DOM-like _type is by pass in an existing Class to the _type.of(Class) method (the .java source of the Class is modeled)

public class Point { @Deprecated int x, y; }
_class _c = _class.of(Point.class); //_class _c represents the source of Point.java

public interface Drawable{ public void draw(); }
_interface _i = _interface.of(Drawable.class); //_interface _i represents the source of Drawable.java

public enum State{ STABLE, REDRAW; }
_enum _e = _enum.of(State.class); //_enum _e represents the source of State.java

public @interface Refresh{ int value() default 0; }
 _annotation _a = _annotation.of(Refresh.class); //_annotation _a represents the source of Refresh.java

you can "manually" build a _type (_class, _enum, _interface, _annotation) via the simple API

//verify that building _types via Class is equivalent to building via component / Strings
assertEquals(_c, _class.of("Point").fields("@Deprecated int x,y;"));
assertEquals(_i, _interface.of("Drawable").method("public void draw();"));
assertEquals(_e, _enum.of("State").constants("STABLE", "REDRAW"));
assertEquals(_a, _annotation.of("Refresh").element("int value() default 0;"));

accessing members of _types

each _type gives access to individual members via .getXXX(...)
_field _x = _c.getField("x");
_method _m = _i.getMethod("draw");
_enum._constant _ec = _e.getConstant("STABLE");
_annotation._element _ae = _a.getElement("value");

accessing lists of like members of each _type with .listXXX()

List<_field> _fs = _c.listFields();                  //list all fields on _c
List<_method> _ms = _i.listMethods();                //list all methods on _i 
List<_enum._constant> _ecs = _e.listConstants();     //list all constants on _e
List<_annotation._element> _aes = _a.listElements(); //list all elements on _a

each _type can selectively list members based on a lambda with .listXXX(Predicate)

_fs = _c.listFields(f -> f.isPrivate());        //list all private fields on _c   
_ms = _i.listMethods(m -> m.isDefault());       //list all default methods on _i
_ecs = _e.listConstants(c -> c.hasArguments()); //list all constants with constructor arguments on _e
_aes = _a.listElements(e -> e.hasDefault());    //list all elements with defaults on _a

we can also list all (generic) members with .listMembers(Predicate)

List<_model._member>_mems = _c.listMembers(m -> m.hasAnnos()); //list all annotated members 

change _types & members

changes can be applied to the _type or members are reflected in the source of the _class

//apply changes to top level _type
_c.implements(Serializable.class); //add Serializable import & implement Serializable to _c
_i.packageName("graphics.draw");   //set Package Name on _i

_a.setTargetRuntime();  //add the @Target(ElementType.TYPE) annotation
  
//apply changes to individual members
_c.getField("x").init(0); //initialize value of field x to be 0
_c.getField("y").init(0); //initialize value of field y to be 0

_types allow lambdas to simplify iteration over members with forXXX(Consumer)

_c.forFields(f->f.setPrivate()); //set ALL _fields to be private on _c
_c.forFields(f->f.removeAnnos(Deprecated.class)); //remove Deprecated annotation from ALL fields of _c
_e.forConstants(c->c.addArgument(100)); //add constructor argument 100 for ALL constants of _e

_i.forMembers(m -> m.annotate(Deprecated.class)); //apply @Deprecated to all members (fields, methods) of _i 

_types can also selectively change members easily with forXXX(Predicate, Consumer)

_c.forFields(f->f.isStatic() && f.hasInit(), f->f.setFinal()); //set all static initialized fields as final
_i.forMethods(m->m.isStatic(), m->m.setPublic()); //select all static methods & make them public

adding members to _types

each _type provides methods for adding members appropriate for the underlying _type

_c.field("public static final int ID = 1023;");
_c.method("public int getX(){ return this.x; }");

_e.field("final int count;"); //add a field to _enum _e

_i.field("public static final int VERSION = 12;");
_e.constant("INVALID(100);"); //add a new constant to _e
_a.element("String name() default \"\";"); //add a new annotation element to _a

_c.field("/** temp field */ public String temp;");
_c.method("public String getTemp() { return temp; }");

_types provide the ability to "pass code around" via lambda bodies and anonymous objects. (this technique allows source code to be parsed / checked and presented/colorized in real time by the IDE, and not treated like escaped plain text... much easier to read, debug, and modify.)

/* this method body is defined by a lambda body */
_c.method("public int getY()", (Integer y)->{ return y; });

/* the riseRun method is defined on an anonymous object & added to the _class */
_c.method( new Object(){ int x, y; //these exist to avoid compiler errors
   public double riseRun(){
       return y * 1.0d / x * 1.0d;
   }
});

removing members

remove members to the _type with removeXXX(...)

_c.removeField("temp");                             //remove field named "temp" from _c
_c.removeMethods(m->m.getName().equals("getTemp")); //remove all methods with name "getTemp" from _c
_e.removeConstant("INVALID");                       //remove constant named "INVALID" from _e
_i.removeFields(f-> f.isStatic());                  //remove all static fields on _i

automate with macros

macros can automate repetitive manual coding tasks when building _types. use the built in ones -or- easily build your own

_c.apply(_autoSet.$,_autoEquals.$,_autoHashCode.$); //adds set methods & equals, hashCode methods
_e.apply(_autoConstructor.$); //generate the appropriate constructor for the enum based on fields

the common _type interface

to uniformly collect or operate on _types, we use the _type interface.

List<_type> _ts = new ArrayList<>();
_ts.add(_c);
_ts.add(_i);
_ts.add(_e);
_ts.add(_a);

//here we can operate on all _types (_class _c, _enum _e, _interface _i, and _annotation _a) 
_ts.forEach( t-> t.packageName("graphics.draw") ); //change the package name for ALL _types

print the .java source

for testing and debugging support, you can always use toString() to get the .java source code for a _type

System.out.println(_c);

//PRINTS:
package graphics.draw;

import java.io.Serializable;

public class Point implements Serializable{

    private int x = 0, y = 0;

    public int getX() {
        return this.x;
    }

    public int getY() {
        return y;
    }

    public double riseRun() {
        return y * 1.0d / x * 1.0d;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public boolean equals(Object o) {
        if (o == null) {
            return false;
        }
        if (this == o) {
            return true;
        }
        if (getClass() != o.getClass()) {
            return false;
        }
        Point test = (Point) o;
        boolean eq = true;
        eq = eq && this.x == test.x;
        eq = eq && this.y == test.y;
        return eq;
    }

    public int hashCode() {
        int hash = 5;
        int prime = 3;
        hash = hash * prime + x;
        hash = hash * prime + y;
        return hash;
    }
}

instant feedback

draft.java doesn't stop at just building or modifying source code, it also provides "instant feedback" by compiling & loading the dynamically built code, and allowing the code to be loaded and used. (This gives developers a tight feedback loop, to generate build, compile and use or test code in a single program without a restart)

compiling _types with _javac

to compile the .java code represented by one or more _type (_class, _enum, _interface, _annotation) use _javac.of(...):

//we can verify the generated code is valid Java, call the javac compiler
_classFiles _cfs = _javac.of(_c); //compile & return the _classFiles (bytecode) for _c _class
_classFiles _allCfs = _javac.of(_c, _i, _a, _e); //compile & return _classFiles for all _types

we can pass in options to the javac compiler via a builder _javac.options().XXX

_classFiles _allCfs = _javac.of( 
    _javac.options()
        //https://stackoverflow.com/questions/44067477/drawbacks-of-javac-parameters-flag
        .parameterNamesStoredForRuntimeReflection() 
        .terminateOnWarning(), //strict compile: fail if any warning is found
    _c, _i, _a, _e); //compile & return the _classFiles (bytecode) for the (_c, _i, _a, and _e) _types

compile & load in one step via _project

to compile and load (in a new ClassLoader) the .java code represented by one or more _class, _enum, _interface... _types, use _project:

// we can create a _project of a single _type
_project _proj = _project.of(_c);

List<Class> loadedClasses = _proj.listClasses(); //list all loaded classes
// _project constructor optionally accepts _javac options
_proj = _project.of( _javac.options().terminateOnWarning(), _c, _a, _i, _e);

_project gives you rudimentary access to individual Classes

assertEquals(1023, _proj.get(_c, "ID")); //get the value of a static field value from the Point.class
Object aPoint = _proj._new(_c); //create a new instance of the Point.class

using a _proxy to interact with dynamic instances

a _proxy simplifies the using a _class instances.

_proxy _p = _proj._proxy(_c ); //build a proxy from the Point.class created by _class _c

field or property values on an instance can be retrieved with .get(...).

/* _proxy simplifies accessing fields or getters on the proxied instance */
assertEquals(1023,_p.get("ID")); //get static field value
assertEquals(0,_p.get("x")); //call get method (note: x is private field)

field or property values on an instance can be updated with .set(...).

_p.set("x",100).set("y",200); //set(...) will call set methods since x, y are private

static or instance methods can be invoked with .call(...)

assertEquals(200.0d / 100.0d, _p.call("riseRun"));

the object instance wrapped by a _proxy is available via the .instance field

assertEquals("Point", _p.instance.getClass().getCanonicalName());

a new instance of the _class can be created with _new(...) on the proxy, or we can create from the _project._proxy(...)

_proxy _p2 = _p._new().set("x",100).set("y",200);
assertEquals(_p2, _proj._proxy(_c).set("x",100).set("y",200));

we can use these (2) _proxy instances (_p, _p2) to test the macro generated equals() and hashcode() methods

assertEquals(_p.instance, _p2.instance); //instance equality check
assertEquals(_p.instance.hashCode(),_p2.instance.hashCode()); //instance hashCode equality check

/* _proxy instances try to operate as "transparently" as possible */
assertEquals(_p, _p2); // _proxy delegates equals() method to the .instance method
assertEquals(_p.hashCode(), _p2.hashCode()); //_proxy delegates the hashCode() method to instance hashCode() method

when we are satisfied with the .java code and .class that was generated, we can export it

/* export Point.java to a file & verify where it was written to */
assertTrue(_io.out("C:\\temp\\",_c).contains("C:\\temp\\Point.java"));

/* export Point.class to a file & verify where it was written to */
assertTrue(_io.out("C:\\temp\\",_proj.classFiles()).contains("C:\\temp\\Point.class"));

/* export Point.java & Point.class to files & verify where both files were written to */
assertTrue(_io.out("C:\\temp\\",_proj).containsAll("C:\\temp\\Point.class", "C:\\temp\\Point.java"));

draft.java's People

Contributors

edefazio avatar

Stargazers

Mahmoud Rusty Abdelkader avatar Simon avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.