HtmlApiFaster is a fluent Java DSL for the HTML5.2 language. It follows the XML schema
definition, i.e. XSD, for the HTML5.2 language, which means that all the syntax rules are enforced, either being attribute
value restrictions or regarding element organization. This DSL can be used in multiple ways, since all the classes present
in this DSL implement the Visitor pattern, so it is possible to define your own Visitor implementation to manipulate the HTML language
for any purpose, for example, to writing well formed HTML to a text file, a stream, a database, etc.
All the code present in this library was automatically generated based a XSD file representing the rules of HTML5.2. In order to generate this code some additional libraries were needed such as XsdAsmFaster, XsdParser and ASM. More information of how this library was generated will be added further.
NOTICE: The HTML elements generator XsdAsmFaster is disabled in maven executions section to allow other customizations in source code such as the package
All the code present in this library was automatically generated based a XSD file representing the rules of HTML5.2. In order to generate this code some additional libraries were needed such as XsdAsmFaster, XsdParser and ASM. More information of how this library was generated will be added further.
NOTICE: The HTML elements generator XsdAsmFaster is disabled in maven executions section to allow other customizations in source code such as the package
org.xmlet.htmlapifaster.async
.
If you need new features of XsdAsmFaster you have to enable it again.
First, in order to include it to your Maven project, simply add this dependency:
<dependency>
<groupId>com.github.xmlet</groupId>
<artifactId>htmlApiFaster</artifactId>
<version>1.0.17</version>
</dependency>
Below it is presented a Java example that shows how the DSL works. It has the following HTML as base.
<html>
<head>
<meta charset="UTF-8"/>
<title>
Title
</title>
<link type="text/css" href="/assets/images/favicon.png" />
<link type="text/css" href="assets/styles/main.css" />
</head>
<body class="clear">
<div id="col-wrap">
<header id="header">
<section class="contain clear">
<div class="logo">
<img id="brand" src="./assets/images/logo.png" />
</div>
<aside>
<em>
Advertisement: <span class="number">HtmlApi is great!</span>
</em>
</aside>
</section>
</header>
</div>
</body>
</html>
public class HtmlApiExample {
public void simpleAPIUsage(){
CustomVisitor customVisitor = new CustomVisitor();
new Html<Html>(customVisitor)
.head()
.comment("This is a comment.")
.meta().attrCharset("UTF-8").__()
.title()
.text("Title").__()
.link().attrType(EnumTypeContentType.TEXT_CSS).attrHref("/assets/images/favicon.png").__()
.link().attrType(EnumTypeContentType.TEXT_CSS).attrHref("/assets/styles/main.css").__().__()
.body().attrClass("clear")
.div()
.header()
.section()
.div()
.img().attrId("brand").attrSrc("./assets/images/logo.png").__()
.aside()
.em()
.text("Advertisement")
.span()
.text("HtmlApi is great!")
.__()
.__()
.__()
.__()
.__()
.__()
.__()
.__()
.__();
String result = customVisitor.getResult();
}
}
The DSL that the HtmlApiFaster provides is pretty straightforward. After creating an Html element we can keep on
creating the HTML element tree by invoking methods of the Html class. Each class that represents an HTML element,
such as Html, Div, P, etc. has its respective methods, acording to the HTML5.2 language specification.
The naming convention of the methods has two variants:
- When adding another element - The method has the name of the element being added, i.e. calling the head() method on the root variable will add a head instance to the html children list.
- When adding another attribute - The method name has the prefix attr before the attribute name.
- The methods which add elements to the element tree return the newly created element.
- The methods which add attributes to the element attributes return the element where the attribute was added.
- To navigate to the parent element we have the __() method.
Having the Java code presented in the previous example how can we generate the respective HTML document? We need to implement
the ElementVisitor abstract class. This class has four different abstract methods:
- visitElement(Element element) - This method is called whenever a class generated based on a XSD xsd:element has its accept method called. By receiving the Element we have access to the element children and attributes.
- visitAttribute(String attributeName, String attributeValue) - This method is called when an attribute method is called. It received the attribute name and the attribute value.
- visitParent(Element element) - This method is called when the __() method is invoked, receiving the instance where the method was invoked.
- visitText(Text text) - This method is called when the text method is invoked.
- visitComment(Text comment) - This method is called when the comment method is invoked.
public class ElementVisitor {
public abstract void visitElement(Element element);
public abstract void visitAttribute(String attributeName, String attributeValue);
public abstract void visitParent(Element element);
public abstract <R> void visitText(Text<? extends Element, R> text);
public abstract <R> void visitComment(Text<? extends Element, R> comment);
public void visitOpenDynamic() { }
public void visitCloseDynamic() { }
public void visitParentHtml(Html element) {
this.visitParent(element);
}
public void visitElementHtml(Html element) {
this.visitElement(element);
}
public void visitAttributeManifest(String manifest) {
this.visitAttribute("manifest", manifest);
}
}
The HtmlApiFaster provides the definition of HTML templates as functions. For example, in the next snippet we define
a template that returns a table presenting information received as parameter.
public class BinderExample{
public String bindExample(List<String> names){
CustomVisitor visitor = new CustomVisitor();
new Html<>(visitor)
.body()
.table()
.tr()
.th()
.text("Title")
.__()
.__()
.of(table ->
names.forEach(name ->
table
.tr()
.td()
.text(name)
.__()
.__()
)
)
.__()
.__()
.__();
return visitor.getResult();
}
}
Even though the code present in this DSL is generated code we implemented some tests to assert code quality,
vulnerabilities and other various metrics. The results are available in the xmlet
Sonarcloud page.
Even though this DSL is created based on aumatically generated classes there are a few nuances. In order to provide
DSL users with source files and java documentation of the DSL, the automatically generated classes are decompiled,
using Fernflower Decompiler used by Intellij,
and then compiled regularly by the maven lifecycle. This process, apart from allowing the DSL users to have the
source and documention files also allows to verify that there are no compiler problems with the code, which is very
helpful when making changes in the way that this DSL is generated.
- New Kotlin
SuspendConsumer
withElement
as receiver of theaccept()
suspending function. - New
visitSuspending()
that deals with asuspend
function ofSuspendConsumer
. - Element extension for String unary plus to enable
+"some text"
in element builders.
- New
raw()
method inElement
to distinguish fromtext()
. ThevisitRaw()
should keep text as it is, whilevisitText()
should escape HTML.
- New attributes to globalEventAttributes
- Replaced
async()
method byawait()
, which can deal with any type of asynchronous API, ond not onlyObservable
. - New
interface AsyncElement<E extends Element> {<M> E await(AwaitConsumer<E,M> asyncAction);}
- New
interface AwaitConsumer<T,M> { void accept(T first, M model, OnCompletion third);
- Changed implementation for
dynamic()
that uses a singevisitDynamic()
instead of two visits for beginning and end. - Upgrade xsdAsmFaster to 1.0.10 to fix problem on duplicated packages in camelcase.
-
New
async()
method inElement
to support asynchronous data models, such asObservable
orPublisher
. -
New package
org.xmlet.htmlapifaster.async
-
New interfaces
Thenable
andAsyncElement
- Removed the async method and corresponding visit methods included in release 1.0.8. Theses methods have been removed from XsdAsmFaster and we intend to implement them at source code level of HtmlApiFaster.
- Moves dynamic and of method from the specific elements class to a default implementation.
- Adds a new Element, CustomElement, and a new method .custom on Element. Refer to Issue for details.
- Added two new methods to ElementVisitor, visitOpenAsync and visitCloseAsync, to allow asynchronous operations.
- Added an async method to all Elements.
- First usable version.