Creating a static site using pure Java. Objectos 0.5.1 released

Welcome to Objectos Weekly issue #018.

I have released Objectos 0.5.1! Most of the work done on this release was internal. In other words, it contains just a few of user-facing changes. I expect the next few releases to follow suit.

In any case, I will show what’s new in Objectos 0.5.1.

Let’s begin.

Before we begin

I use Objectos in production. This very page was generated using Objectos HTML (and a few other Objectos libraries).

Having said that, please know that Objectos is alpha software. In particular:

  • it is far from being stable: deviate slightly from the shown use-case and it will probably fail;
  • API might change substantially between minor/patch releases; and
  • documentation is a work in progress.

Creating a static site using pure Java

The following were introduced to Objectos HTML in version 0.5.1:

  • the HtmlSink class;
  • the HtmlSink::toDirectory(HtmTemplate, Path) method;
  • the pathName instruction of the HtmlTemplate class; and
  • the pathTo instruction of the HtmlTemplate class.

They are all related. Let’s look at an example to see how they work together.

Our template

Suppose the following Objectos HTML template:

<span>public</span> <span>class</span> <span>MyTemplate</span> <span>extends</span> <span>HtmlTemplate</span> <span>{</span>
<span>String</span> <span>pathName</span><span>;</span>
<span>@Override</span>
<span>protected</span> <span>final</span> <span>void</span> <span>definition</span><span>()</span> <span>{</span>
<span>pathName</span><span>(</span><span>pathName</span><span>);</span>
<span>doctype</span><span>();</span>
<span>html</span><span>(</span>
<span>head</span><span>(</span>
<span>title</span><span>(</span><span>"Objectos HTML example"</span><span>)</span>
<span>),</span>
<span>body</span><span>(</span>
<span>p</span><span>(</span>
<span>a</span><span>(</span><span>pathTo</span><span>(</span><span>"/index.html"</span><span>),</span> <span>t</span><span>(</span><span>"Home"</span><span>))</span>
<span>),</span>
<span>dl</span><span>(</span>
<span>dt</span><span>(</span><span>"Path name:"</span><span>),</span>
<span>dd</span><span>(</span><span>pathName</span><span>)</span>
<span>)</span>
<span>)</span>
<span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>MyTemplate</span> <span>extends</span> <span>HtmlTemplate</span> <span>{</span>
  <span>String</span> <span>pathName</span><span>;</span>

  <span>@Override</span>
  <span>protected</span> <span>final</span> <span>void</span> <span>definition</span><span>()</span> <span>{</span>
    <span>pathName</span><span>(</span><span>pathName</span><span>);</span>

    <span>doctype</span><span>();</span>
    <span>html</span><span>(</span>
      <span>head</span><span>(</span>
        <span>title</span><span>(</span><span>"Objectos HTML example"</span><span>)</span>
      <span>),</span>
      <span>body</span><span>(</span>
        <span>p</span><span>(</span>
          <span>a</span><span>(</span><span>pathTo</span><span>(</span><span>"/index.html"</span><span>),</span> <span>t</span><span>(</span><span>"Home"</span><span>))</span>
        <span>),</span>
        <span>dl</span><span>(</span>
          <span>dt</span><span>(</span><span>"Path name:"</span><span>),</span>
          <span>dd</span><span>(</span><span>pathName</span><span>)</span>
        <span>)</span>
      <span>)</span>
    <span>);</span>
  <span>}</span>
<span>}</span>
public class MyTemplate extends HtmlTemplate { String pathName; @Override protected final void definition() { pathName(pathName); doctype(); html( head( title("Objectos HTML example") ), body( p( a(pathTo("/index.html"), t("Home")) ), dl( dt("Path name:"), dd(pathName) ) ) ); } }

Enter fullscreen mode Exit fullscreen mode

Notice the pathName instruction at the top of the definition method:

<span>@Override</span>
<span>protected</span> <span>final</span> <span>void</span> <span>definition</span><span>()</span> <span>{</span>
<span>pathName</span><span>(</span><span>pathName</span><span>);</span>
<span>...</span>
<span>}</span>
<span>@Override</span>
<span>protected</span> <span>final</span> <span>void</span> <span>definition</span><span>()</span> <span>{</span>
  <span>pathName</span><span>(</span><span>pathName</span><span>);</span>

  <span>...</span>
<span>}</span>
@Override protected final void definition() { pathName(pathName); ... }

Enter fullscreen mode Exit fullscreen mode

It represents the path of generated HTML file.

The pathTo instruction was used inside the anchor in the template’s body:

<span>a</span><span>(</span><span>pathTo</span><span>(</span><span>"/index.html"</span><span>),</span> <span>t</span><span>(</span><span>"Home"</span><span>))</span>
<span>a</span><span>(</span><span>pathTo</span><span>(</span><span>"/index.html"</span><span>),</span> <span>t</span><span>(</span><span>"Home"</span><span>))</span>
a(pathTo("/index.html"), t("Home"))

Enter fullscreen mode Exit fullscreen mode

It is a link that takes you back to the site’s home. The pathTo instruction generates a href attribute. But it uses the pathName information to generate a relative link.

Generating our static site

We will use the HtmlSink class to generate our static site to a pre-defined directory.

The following code does that:

<span>var</span> <span>tmpdir</span> <span>=</span> <span>System</span><span>.</span><span>getProperty</span><span>(</span><span>"java.io.tmpdir"</span><span>);</span>
<span>var</span> <span>target</span> <span>=</span> <span>Path</span><span>.</span><span>of</span><span>(</span><span>tmpdir</span><span>,</span> <span>"objectos-html-example"</span><span>);</span>
<span>var</span> <span>pathNames</span> <span>=</span> <span>List</span><span>.</span><span>of</span><span>(</span>
<span>"/index.html"</span><span>,</span>
<span>"/blog/index.html"</span><span>,</span>
<span>"/blog/2023/03/17/objectos-html-is-cool.html"</span>
<span>);</span>
<span>var</span> <span>htmlSink</span> <span>=</span> <span>new</span> <span>HtmlSink</span><span>();</span>
<span>var</span> <span>template</span> <span>=</span> <span>new</span> <span>MyTemplate</span><span>();</span>
<span>for</span> <span>(</span><span>var</span> <span>pathName</span> <span>:</span> <span>pathNames</span><span>)</span> <span>{</span>
<span>template</span><span>.</span><span>pathName</span> <span>=</span> <span>pathName</span><span>;</span>
<span>htmlSink</span><span>.</span><span>toDirectory</span><span>(</span><span>template</span><span>,</span> <span>target</span><span>);</span>
<span>}</span>
<span>var</span> <span>tmpdir</span> <span>=</span> <span>System</span><span>.</span><span>getProperty</span><span>(</span><span>"java.io.tmpdir"</span><span>);</span>

<span>var</span> <span>target</span> <span>=</span> <span>Path</span><span>.</span><span>of</span><span>(</span><span>tmpdir</span><span>,</span> <span>"objectos-html-example"</span><span>);</span>

<span>var</span> <span>pathNames</span> <span>=</span> <span>List</span><span>.</span><span>of</span><span>(</span>
  <span>"/index.html"</span><span>,</span>
  <span>"/blog/index.html"</span><span>,</span>
  <span>"/blog/2023/03/17/objectos-html-is-cool.html"</span>
<span>);</span>

<span>var</span> <span>htmlSink</span> <span>=</span> <span>new</span> <span>HtmlSink</span><span>();</span>

<span>var</span> <span>template</span> <span>=</span> <span>new</span> <span>MyTemplate</span><span>();</span>

<span>for</span> <span>(</span><span>var</span> <span>pathName</span> <span>:</span> <span>pathNames</span><span>)</span> <span>{</span>
  <span>template</span><span>.</span><span>pathName</span> <span>=</span> <span>pathName</span><span>;</span>

  <span>htmlSink</span><span>.</span><span>toDirectory</span><span>(</span><span>template</span><span>,</span> <span>target</span><span>);</span>
<span>}</span>
var tmpdir = System.getProperty("java.io.tmpdir"); var target = Path.of(tmpdir, "objectos-html-example"); var pathNames = List.of( "/index.html", "/blog/index.html", "/blog/2023/03/17/objectos-html-is-cool.html" ); var htmlSink = new HtmlSink(); var template = new MyTemplate(); for (var pathName : pathNames) { template.pathName = pathName; htmlSink.toDirectory(template, target); }

Enter fullscreen mode Exit fullscreen mode

We start by defining our target directory:

<span>var</span> <span>tmpdir</span> <span>=</span> <span>System</span><span>.</span><span>getProperty</span><span>(</span><span>"java.io.tmpdir"</span><span>);</span>
<span>var</span> <span>target</span> <span>=</span> <span>Path</span><span>.</span><span>of</span><span>(</span><span>tmpdir</span><span>,</span> <span>"objectos-html-example"</span><span>);</span>
<span>var</span> <span>tmpdir</span> <span>=</span> <span>System</span><span>.</span><span>getProperty</span><span>(</span><span>"java.io.tmpdir"</span><span>);</span>

<span>var</span> <span>target</span> <span>=</span> <span>Path</span><span>.</span><span>of</span><span>(</span><span>tmpdir</span><span>,</span> <span>"objectos-html-example"</span><span>);</span>
var tmpdir = System.getProperty("java.io.tmpdir"); var target = Path.of(tmpdir, "objectos-html-example");

Enter fullscreen mode Exit fullscreen mode

I am running Linux, so I expect the target directory to be /tmp/objectos-html-example

Next, we define the structure of our static site:

<span>var</span> <span>pathNames</span> <span>=</span> <span>List</span><span>.</span><span>of</span><span>(</span>
<span>"/index.html"</span><span>,</span>
<span>"/blog/index.html"</span><span>,</span>
<span>"/blog/2023/03/17/objectos-html-is-cool.html"</span>
<span>);</span>
<span>var</span> <span>pathNames</span> <span>=</span> <span>List</span><span>.</span><span>of</span><span>(</span>
  <span>"/index.html"</span><span>,</span>
  <span>"/blog/index.html"</span><span>,</span>
  <span>"/blog/2023/03/17/objectos-html-is-cool.html"</span>
<span>);</span>
var pathNames = List.of( "/index.html", "/blog/index.html", "/blog/2023/03/17/objectos-html-is-cool.html" );

Enter fullscreen mode Exit fullscreen mode

Note that the pathnames must start with the ‘/’ slash character. Our hypothetical site contains three files. In a real site the structure could come e.g. from your AsciiDoc or Markdown files.

Next, we generate our site using HtmlSink and our MyTemplate class from before:

<span>var</span> <span>htmlSink</span> <span>=</span> <span>new</span> <span>HtmlSink</span><span>();</span>
<span>var</span> <span>template</span> <span>=</span> <span>new</span> <span>MyTemplate</span><span>();</span>
<span>for</span> <span>(</span><span>var</span> <span>pathName</span> <span>:</span> <span>pathNames</span><span>)</span> <span>{</span>
<span>template</span><span>.</span><span>pathName</span> <span>=</span> <span>pathName</span><span>;</span>
<span>htmlSink</span><span>.</span><span>toDirectory</span><span>(</span><span>template</span><span>,</span> <span>target</span><span>);</span>
<span>}</span>
<span>var</span> <span>htmlSink</span> <span>=</span> <span>new</span> <span>HtmlSink</span><span>();</span>

<span>var</span> <span>template</span> <span>=</span> <span>new</span> <span>MyTemplate</span><span>();</span>

<span>for</span> <span>(</span><span>var</span> <span>pathName</span> <span>:</span> <span>pathNames</span><span>)</span> <span>{</span>
  <span>template</span><span>.</span><span>pathName</span> <span>=</span> <span>pathName</span><span>;</span>

  <span>htmlSink</span><span>.</span><span>toDirectory</span><span>(</span><span>template</span><span>,</span> <span>target</span><span>);</span>
<span>}</span>
var htmlSink = new HtmlSink(); var template = new MyTemplate(); for (var pathName : pathNames) { template.pathName = pathName; htmlSink.toDirectory(template, target); }

Enter fullscreen mode Exit fullscreen mode

So, for each pathName defined, we set the pathName instance variable of our template.

Then, we invoke the HtmlSink::toDirectory method. Please note that it throws an IOException.

Running our example

After running our example, here’s the structure generated:

$ find /tmp/objectos-html-example -type f
/tmp/objectos-html-example/blog/2023/03/17/objectos-html-is-cool.html
/tmp/objectos-html-example/blog/index.html
/tmp/objectos-html-example/index.html
$ find /tmp/objectos-html-example -type f
/tmp/objectos-html-example/blog/2023/03/17/objectos-html-is-cool.html
/tmp/objectos-html-example/blog/index.html
/tmp/objectos-html-example/index.html
$ find /tmp/objectos-html-example -type f /tmp/objectos-html-example/blog/2023/03/17/objectos-html-is-cool.html /tmp/objectos-html-example/blog/index.html /tmp/objectos-html-example/index.html

Enter fullscreen mode Exit fullscreen mode

So the HtmlSink class used the pathName to generate the directory hierarchy automatically.

Let’s look at generate HTML of our home page:

<span><!doctype html></span>
<span><html></span>
<span><head></span>
<span><title></span>Objectos HTML example<span></title></span>
<span></head></span>
<span><body></span>
<span><p><a</span> <span>href=</span><span>"index.html"</span><span>></span>Home<span></a></p></span>
<span><dl><dt></span>Path name:<span></dt><dd></span>/index.html<span></dd></dl></body></span>
<span></html></span>
<span><!doctype html></span>
<span><html></span>
<span><head></span>
<span><title></span>Objectos HTML example<span></title></span>
<span></head></span>
<span><body></span>
<span><p><a</span> <span>href=</span><span>"index.html"</span><span>></span>Home<span></a></p></span>
<span><dl><dt></span>Path name:<span></dt><dd></span>/index.html<span></dd></dl></body></span>
<span></html></span>
<!doctype html> <html> <head> <title>Objectos HTML example</title> </head> <body> <p><a href="index.html">Home</a></p> <dl><dt>Path name:</dt><dd>/index.html</dd></dl></body> </html>

Enter fullscreen mode Exit fullscreen mode

Notice the href in the anchor: it is not absolute.

The reason becomes clearer in the blog index page:

<span><!doctype html></span>
<span><html></span>
<span><head></span>
<span><title></span>Objectos HTML example<span></title></span>
<span></head></span>
<span><body></span>
<span><p><a</span> <span>href=</span><span>"../index.html"</span><span>></span>Home<span></a></p></span>
<span><dl><dt></span>Path name:<span></dt><dd></span>/blog/index.html<span></dd></dl></body></span>
<span></html></span>
<span><!doctype html></span>
<span><html></span>
<span><head></span>
<span><title></span>Objectos HTML example<span></title></span>
<span></head></span>
<span><body></span>
<span><p><a</span> <span>href=</span><span>"../index.html"</span><span>></span>Home<span></a></p></span>
<span><dl><dt></span>Path name:<span></dt><dd></span>/blog/index.html<span></dd></dl></body></span>
<span></html></span>
<!doctype html> <html> <head> <title>Objectos HTML example</title> </head> <body> <p><a href="../index.html">Home</a></p> <dl><dt>Path name:</dt><dd>/blog/index.html</dd></dl></body> </html>

Enter fullscreen mode Exit fullscreen mode

Remember we used the pathTo instruction in the anchor (instead of a href).

The pathTo instruction uses the template’s pathName value to generate a relative link.

For completeness, let’s look at the generated HTML of the blog article:

<span><!doctype html></span>
<span><html></span>
<span><head></span>
<span><title></span>Objectos HTML example<span></title></span>
<span></head></span>
<span><body></span>
<span><p><a</span> <span>href=</span><span>"../../../../index.html"</span><span>></span>Home<span></a></p></span>
<span><dl><dt></span>Path name:<span></dt><dd></span>/blog/2023/03/17/objectos-html-is-cool.html<span></dd></dl></body></span>
<span></html></span>
<span><!doctype html></span>
<span><html></span>
<span><head></span>
<span><title></span>Objectos HTML example<span></title></span>
<span></head></span>
<span><body></span>
<span><p><a</span> <span>href=</span><span>"../../../../index.html"</span><span>></span>Home<span></a></p></span>
<span><dl><dt></span>Path name:<span></dt><dd></span>/blog/2023/03/17/objectos-html-is-cool.html<span></dd></dl></body></span>
<span></html></span>
<!doctype html> <html> <head> <title>Objectos HTML example</title> </head> <body> <p><a href="../../../../index.html">Home</a></p> <dl><dt>Path name:</dt><dd>/blog/2023/03/17/objectos-html-is-cool.html</dd></dl></body> </html>

Enter fullscreen mode Exit fullscreen mode

So, during development, there’s no need for a web server. In other words, you can point your browser to:

file:///tmp/html-sink-example/blog/index.html
file:///tmp/html-sink-example/blog/index.html
file:///tmp/html-sink-example/blog/index.html

Enter fullscreen mode Exit fullscreen mode

And links should work fine.

Objectos Code: allow annotations in field declarations

I continue to work on the Objectos Code documentation.

While documenting field declarations I realized that Objectos Code did not allow for fields to be annotated. So I implemented that. And also wrote the documentation for it.

So the following Objectos Code:

<span>import</span> <span>objectos.code.ClassTypeName</span><span>;</span>
<span>import</span> <span>objectos.code.JavaTemplate</span><span>;</span>
<span>public</span> <span>class</span> <span>FieldAnnotations</span> <span>extends</span> <span>JavaTemplate</span> <span>{</span>
<span>static</span> <span>final</span> <span>ClassTypeName</span> <span>ANNO_A</span>
<span>=</span> <span>ClassTypeName</span><span>.</span><span>of</span><span>(</span><span>"com.example.annotations"</span><span>,</span> <span>"AnnotationA"</span><span>);</span>
<span>static</span> <span>final</span> <span>ClassTypeName</span> <span>ANNO_B</span>
<span>=</span> <span>ClassTypeName</span><span>.</span><span>of</span><span>(</span><span>"com.example.annotations"</span><span>,</span> <span>"AnnotationB"</span><span>);</span>
<span>static</span> <span>final</span> <span>ClassTypeName</span> <span>ANNO_C</span>
<span>=</span> <span>ClassTypeName</span><span>.</span><span>of</span><span>(</span><span>"com.example.annotations"</span><span>,</span> <span>"AnnotationC"</span><span>);</span>
<span>@Override</span>
<span>protected</span> <span>final</span> <span>void</span> <span>definition</span><span>()</span> <span>{</span>
<span>autoImports</span><span>();</span>
<span>classDeclaration</span><span>(</span>
<span>name</span><span>(</span><span>"FieldAnnotations"</span><span>),</span>
<span>field</span><span>(</span>
<span>annotation</span><span>(</span><span>ANNO_A</span><span>),</span>
<span>annotation</span><span>(</span><span>ANNO_B</span><span>),</span>
<span>annotation</span><span>(</span><span>ANNO_C</span><span>),</span>
<span>PUBLIC</span><span>,</span> <span>INT</span><span>,</span> <span>name</span><span>(</span><span>"multiple"</span><span>)</span>
<span>)</span>
<span>);</span>
<span>}</span>
<span>}</span>
<span>import</span> <span>objectos.code.ClassTypeName</span><span>;</span>
<span>import</span> <span>objectos.code.JavaTemplate</span><span>;</span>

<span>public</span> <span>class</span> <span>FieldAnnotations</span> <span>extends</span> <span>JavaTemplate</span> <span>{</span>
  <span>static</span> <span>final</span> <span>ClassTypeName</span> <span>ANNO_A</span>
      <span>=</span> <span>ClassTypeName</span><span>.</span><span>of</span><span>(</span><span>"com.example.annotations"</span><span>,</span> <span>"AnnotationA"</span><span>);</span>
  <span>static</span> <span>final</span> <span>ClassTypeName</span> <span>ANNO_B</span>
      <span>=</span> <span>ClassTypeName</span><span>.</span><span>of</span><span>(</span><span>"com.example.annotations"</span><span>,</span> <span>"AnnotationB"</span><span>);</span>
  <span>static</span> <span>final</span> <span>ClassTypeName</span> <span>ANNO_C</span>
      <span>=</span> <span>ClassTypeName</span><span>.</span><span>of</span><span>(</span><span>"com.example.annotations"</span><span>,</span> <span>"AnnotationC"</span><span>);</span>

  <span>@Override</span>
  <span>protected</span> <span>final</span> <span>void</span> <span>definition</span><span>()</span> <span>{</span>
    <span>autoImports</span><span>();</span>

    <span>classDeclaration</span><span>(</span>
      <span>name</span><span>(</span><span>"FieldAnnotations"</span><span>),</span>

      <span>field</span><span>(</span>
        <span>annotation</span><span>(</span><span>ANNO_A</span><span>),</span>
        <span>annotation</span><span>(</span><span>ANNO_B</span><span>),</span>
        <span>annotation</span><span>(</span><span>ANNO_C</span><span>),</span>
        <span>PUBLIC</span><span>,</span> <span>INT</span><span>,</span> <span>name</span><span>(</span><span>"multiple"</span><span>)</span>
      <span>)</span>
    <span>);</span>
  <span>}</span>
<span>}</span>
import objectos.code.ClassTypeName; import objectos.code.JavaTemplate; public class FieldAnnotations extends JavaTemplate { static final ClassTypeName ANNO_A = ClassTypeName.of("com.example.annotations", "AnnotationA"); static final ClassTypeName ANNO_B = ClassTypeName.of("com.example.annotations", "AnnotationB"); static final ClassTypeName ANNO_C = ClassTypeName.of("com.example.annotations", "AnnotationC"); @Override protected final void definition() { autoImports(); classDeclaration( name("FieldAnnotations"), field( annotation(ANNO_A), annotation(ANNO_B), annotation(ANNO_C), PUBLIC, INT, name("multiple") ) ); } }

Enter fullscreen mode Exit fullscreen mode

Generates the following Java code:

<span>import</span> <span>com.example.annotations.AnnotationA</span><span>;</span>
<span>import</span> <span>com.example.annotations.AnnotationB</span><span>;</span>
<span>import</span> <span>com.example.annotations.AnnotationC</span><span>;</span>
<span>class</span> <span>FieldAnnotations</span> <span>{</span>
<span>@AnnotationA</span>
<span>@AnnotationB</span>
<span>@AnnotationC</span>
<span>public</span> <span>int</span> <span>multiple</span><span>;</span>
<span>}</span>
<span>import</span> <span>com.example.annotations.AnnotationA</span><span>;</span>
<span>import</span> <span>com.example.annotations.AnnotationB</span><span>;</span>
<span>import</span> <span>com.example.annotations.AnnotationC</span><span>;</span>

<span>class</span> <span>FieldAnnotations</span> <span>{</span>
  <span>@AnnotationA</span>
  <span>@AnnotationB</span>
  <span>@AnnotationC</span>
  <span>public</span> <span>int</span> <span>multiple</span><span>;</span>
<span>}</span>
import com.example.annotations.AnnotationA; import com.example.annotations.AnnotationB; import com.example.annotations.AnnotationC; class FieldAnnotations { @AnnotationA @AnnotationB @AnnotationC public int multiple; }

Enter fullscreen mode Exit fullscreen mode

Until the next issue of Objectos Weekly

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

The source code of all of the examples are in this GitHub repository.

原文链接:Creating a static site using pure Java. Objectos 0.5.1 released

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
The best things in life are free.
生活中最美好的事都是免费的
评论 抢沙发

请登录后发表评论

    暂无评论内容