LWO #001: Objectos Code API revamp, and a use of Java's sealed interfaces
Tags: java, news
Published: 2022-11-18 11:29:24

Hi, welcome to the first edition of "Last Week in Objectos" (LWO), my new writing project.

I enjoy writing. But the deep technical writings of the blog are very time consuming. Time spent in the writing itself, of course, but also spent on figuring out what to write about.

This is an attempt to solve this; spend less time on writing related tasks while still providing value to readers.

I will write about what happened the previous week in Objectos. The idea is to share the things I learned, the problems I faced and how I solve them. Or how I didn't solve them...

Also, the idea is for this to become a newsletter. Once I sort out the software, that is. It will have a different name though.

All right, enough of this introduction. Let's begin.

## Objectos Code API revamp

Objectos Code is a Java library for generating Java source code. Later, it will also provide utilities for writing annotation processors.

I am currently working on revamping its API.

### The new API

My idea is to provide a "template engine" for generating Java source code. However, unlike traditional template engines, the template itself is not string based. Templates in Objectos Code are written in pure Java instead.

You create a template by extending the `JavaTemplate` class. The following is a sort-of hello world example:

```java
import objectos.code.JavaTemplate;

class HelloTemplate extends JavaTemplate {
  String pkgName = "com.example";
  
  String name = "HelloWorld";
  
  String message = "Hello world!";

  @Override
  protected final void definition() {
    _package(pkgName);
  
    _class(
      _public(), id(name),
      
      _method(
        _public(), _void(), id("sayHello"),
        invoke(n(System.class, "out"), "println", s(message))
      )
    );
  }
}
```

The template can be used like the following:

```java
var hello = new HelloTemplate();
hello.pkgName = "objectos.example";
System.out.println(hello);
```

Which prints:

```java
package objectos.example;

public class HelloWorld {
  public void sayHello() {
    System.out.println("Hello world!");
  }
}
```

The new API is a work in progress. It does not support array types yet, nor can you declare method parameters. This is the reason why the example is not a "proper" hello world.

### Consistent API across Objectos' libraries

So, why am I creating a new API? Was there something wrong with the old one?

There are a few things I dislike about the old API. But the main reason for the new API is to have a consistent API throughout all of the Objectos' libraries.

For example, Objectos HTML is an unreleased Java library to generate HTML using pure Java:

```java
class MyTemplate extends HtmlTemplate {
  @Override
  protected final void definition() {
    doctype();
    html(
      lang("en"),
      head(
    	meta(charset("utf-8")),
    	meta(httpEquiv("x-ua-compatible"), content("ie=edge")),
	    meta(
	      name("viewport"),
	      content("width=device-width, initial-scale=1, shrink-to-fit=no")
	    )
      ),
      body(
        p("Hello world!")
      )
    );
  }
}
```

As you can see, it uses the same idea of "a template in pure Java".

Objectos CSS, used to generate CSS style sheets, also uses the same idea.

## Work on Objectos Code

Now that I've given an introduction to Objectos Code, here's what I've actually work on this past week.

### Initial enum declaration support

I've started adding the enum declaration support to Objectos Code.

You can generate a simple enum like the following:

```java
public enum Suit {
  CLUBS,
   
  DIAMONDS,
   
  HEARTS,
   
  SPADES
}
```

Using this code:

```java
_enum(
  _public(), id("Suit"),
  enunConstant(id("CLUBS")),
  enunConstant(id("DIAMONDS")),
  enunConstant(id("HEARTS")),
  enunConstant(id("SPADES"))
);
```

### Initial field declaration support

I also started migrating field declarations to the new API.

The following class:

```java
class Fields {
  Integer a;
  
  final String b = "b";
  
  private static final String C = "C";
  
  String d, e = b; 
}
```

Can be generated with:

```java
_class(
  id("Fields"),
  
  field(t(Integer.class), id("a")),
  
  field(_final(), t(String.class), id("b"), s("b")),
  
  field(_private(), _static(), _final(), t(String.class), id("C"), s("C")),
  
  field(t(String.class), id("d"), id("e"), n("b"))
);
```

You should note that:

- the `id` method, which is short for _identifier_, is for naming declarations, i.e., the class name, the enum class name, the field name, etc.
- the `t` method is for types
- the `s` method is for string literals
- the `n` method is for expression names

## A (somewhat) type-safe API

The examples might give the impression that the Objectos Code API is free-form. But it is not.

As an example, the following does not compile:

```java
// does not compile!!
_class(
  enumConstant(id("FAIL"))
);
```

As the `_class` method does not accept as argument the return value of the `enumConstant` method. Similarly, the following does not compile either:

```java
// does not compile!!!
_enum(
  _public(), _final(), id("Fail")
);
```

As the `_enum` method does not accept as argument the return value of the `_final()` method.

## Using Java's sealed interfaces

To provide the type-safety of the Objectos Code API I am using Java's sealed interfaces.

The `_class` method is an instance method of the `JavaTemplate` class. The signature of the method is the following:

```java
protected final ClassDeclaration _class(
    ClassDeclarationElement... elements);
```

So it only accepts instances of the `ClassDeclarationElement` interface. But it cannot accept any implementation of said interface. In fact, it must _only_ accept implementations provided by Objectos Code itself.

So there are two conflicting problems:

- the `ClassDeclarationElement` interface must be accessible to subclasses of the `JavaTemplate`; and
- the `_class` method cannot accept arbitrary implementations of the `ClassDeclarationElement` interface.

The way I solved it (I think) is by using sealed interfaces. Here's a simplified version of the `InternalApi` class. The `InternalApi` class declares all of the sealed hierarchy:

```java
public final class InternalApi {
  public sealed interface ClassDeclaration {}

  public sealed interface ClassDeclarationElement {}

  public sealed interface Identifier
      extends ClassDeclarationElement {}

  public sealed interface PublicModifier
      extends ClassDeclarationElement {}
      
  public sealed interface VoidInvocation {}    

  private static final class Ref 
      implements
      ClassDeclaration,
      Identifier,
      PublicModifier,
      VoidInvocation {
    private Ref() {}      
  }
  
  public static final Ref REF = new Ref();  
}
```

And, in the `JavaTemplate` class, we write:

```java
protected final ClassDeclaration _class(
    ClassDeclarationElement... elements) {
  // actual impl...  
  return InternalApi.REF;
}

protected final PublicModifier _public() {
  // actual impl...  
  return InternalApi.REF;
}

protected final VoidInvocation _void() {
  // actual impl...  
  return InternalApi.REF;
}

protected final Identifier id(String value) {
  // actual impl...  
  return InternalApi.REF;
}
```

So it means that the following invocation is valid:

```java
_class(_public(), id("Foo"));
```

While the following is not:

```java
_class(_void(), id("Foo"));
```

## Until the next edition

So that's it for today. I hope you've enjoyed reading.

You can subscribe to the blog via [RSS](https://www.objectos.com.br/atom.xml).

If you liked this content you can follow me on [Twitter](https://twitter.com/marcioendo).