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 theHtmlTemplate
class; and - the
pathTo
instruction of theHtmlTemplate
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.htmlfile:///tmp/html-sink-example/blog/index.htmlfile:///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
暂无评论内容