Overview

Every Programm talks about something. A Webshop will talk about Products, Customers, Orders and Payment. Programms can also talk about other Programms in the same way (Metaprogramming). The Livecycle from writing code to it being executed is long and there are many different opportunities for metaprogramming. Each having different tradeoffs depending on the context.

The code written with this Library runs in the java compiler. It takes in source files and outputs class files. Derivandi can do two things:

Annotations

Writing CustomerEntitiy or @Entity Customer represents the same information. The structured way of expressing metadata as Annotations is just easier to process by tools. Annotations don’t do anything on their own.

The most relevant properties of Annotations for compile time meta programming are:

  1. @Retention configures how long an Annotations should be kept in the livecycle.

    1. SOURCE Annotations are discarded at the end of the compilation

    2. CLASS Annotations are written in the class files, but not read by the java runtime

    3. RUNTIME Annotations are always present

  2. @Target configures where an Annotation can be used

  3. @Inherited configures inheritance for an Annotation

Derivandi

Derivandi core features are

Code Model

It is possible to query the code model from modules down to method parameters.

D.Class string = context.getClassOrThrow("java.lang.String");
for (D.Property property : string.getProperties()) {
   D.Method getter = property.getGetter();
   for (D.Parameter parameter : getter.getParameters()) {
      System.out.println(parameter.isFinal());
   }
}

JavaDsl

The JavaDsl makes it easy to generate source files. The Code Model can be used with the JavaDsl. Modules down to method parameters are typesafe. Everything has a flexible String overload.

JavaDsl.class_().package_("org.example")
             .public_().name("MyClass")
             .field(JavaDsl.field().private_().type("String").name("foo"))
             .method(JavaDsl.method().public_().resultType("void").name("bar"))
             .renderDeclaration(RenderingContext.createRenderingContext());

File Generation

There are tree kind of files that can be generated. Class files, resource files and source files. Generated source files will be complied like any other. Generating them should be preferred over class files. The JavaDsl can be used.

context.writeClassFile();
context.writeResource();
context.writeAndCompileSourceFile(JavaDsl.class_().package_("org.example")
                                         .public_().name("MyClass")
                                         .constructor(JavaDsl.constructor().surroundingType()));

Logging

Logging can be done on three levels. Error, Warning or Info. Errors will interrupt the compilation. It is possible to log with a reference to a specific place in the code.

context.logAndRaiseError("broken");

D.Class string = context.getClassOrThrow("java.lang.String");
context.logWarningAt(string, "let me warn you about Strings!");

context.logInfo("i was here");

Compiler

Overview

Derivandi runs in the complier. Compilation happens in five steps.

  1. Parse
    The source files are parsed

  2. Enter
    The Parser Result is entered in the internal datastructures

  3. Annotation Processing
    All Annotation processors, like Derivandi, are executed. This may parts in new source files. If new source files are generated the steps 1-3 will be run for again for just these.

  4. Analyse
    Analyses and Validates the code.

    This means that "normal" code can reference code that will only be generated at compile time.
  5. Generate
    Class files are generated

Compilation Units

Derivandi code is normal java code, that gets executed at compile time. Therefore, it needs to be already compiled. Making multiple compilation Units necessary. Meaning multiple maven modules or equivalent in other build tools.

Examples